/**
* =====================================================================
* Uno_Config_Bridge - VL53L1X modulio konfigūravimas per Arduino UNO
* =====================================================================
* Uno tampa konfigūravimo įrankiu: Serial Monitor (115200) <-> modulio
* SIGNAL laidas (9600, pusiau dvipusis). Papildomų detalių nereikia.
*
* Uno valdo SIGNAL kaip "open-drain": 0 = traukia į LOW, 1 = atleidžia
* (HIGH pakelia modulio R11). Todėl niekada nekovoja su moduliu.
*
* JUNGIMAS:
* Modulis SIGNAL (1) ── Uno D2
* Modulis GND (2) ── Uno GND
* Modulis +5V (3) ── POWER_FROM_PINS = 1: Uno D3 IR D4 (abu kartu)
* POWER_FROM_PINS = 0: Uno 5V (jungiama paraginus)
* D12 rezervuotas (SoftwareSerial TX, nenaudojamas).
*
* Serial Monitor: 115200 baud, "Newline".
* Modulio komandos (?, PRESET MINI, DIST 650, ...) persiunčiamos moduliui.
* Tilto komandos prasideda '!':
* !CONFIG - (iš naujo) įeiti į konfigūracijos režimą
* !TEST - paleisti modulį normaliu režimu ir rodyti jo išėjimą
* !HELP - šis sąrašas
* Po RESTART tiltas automatiškai pereina į !TEST.
* =====================================================================
*/
#include <SoftwareSerial.h>
#include <util/delay.h>
#define POWER_FROM_PINS 1 // 1 = modulio +5V iš D3+D4 (automatiškai), 0 = iš Uno 5V (rankiniu būdu)
const long PC_BAUD = 115200;
const long SENSOR_BAUD = 9600;
const uint8_t SIG_PIN = 2; // PD2
const uint8_t UNUSED_TX_PIN = 12;
#define SIG_BIT _BV(2)
#define PWR_BITS (_BV(3) | _BV(4)) // D3 + D4 kartu
#define BIT_US (1000000.0 / 9600)
const uint16_t CONFIG_HOLD_MS = 1000; // modulis reikalauja >= 0.5 s LOW įjungiant
const uint16_t POWER_OFF_MS = 500;
const uint16_t TEST_PRINT_MS = 300;
// Modulio tekstas atitraukiamas, kad skirtųsi nuo tilto pranešimų ir įvesties.
const char SENSOR_INDENT[] = ">\t";
SoftwareSerial sensor(SIG_PIN, UNUSED_TX_PIN);
enum BridgeMode : uint8_t { MODE_IDLE, MODE_CONFIG, MODE_TEST };
BridgeMode mode = MODE_IDLE;
long range_mm = 0; // iš modulio "RANGE ..." eilutės
bool output_pwm = false; // iš modulio "OUTPUT ..." eilutės
char pc_line[48];
uint8_t pc_len = 0;
char rx_line[64];
uint8_t rx_len = 0;
// ─── SIGNAL laidas ───────────────────────────────────────────────────
static inline void sig_low() {
PORTD &= ~SIG_BIT;
DDRD |= SIG_BIT;
}
static inline void sig_release() {
DDRD &= ~SIG_BIT;
PORTD |= SIG_BIT; // silpnas pull-up; pagrindinis - modulio R11
}
// Test režimui: įėjimas be pull-up (HIGH duoda modulio R11; išjungus modulį
// pull-up neturi maitinti jo per signalo išvadą).
static inline void sig_input_no_pull() {
DDRD &= ~SIG_BIT;
PORTD &= ~SIG_BIT;
}
static void power(bool on) {
#if POWER_FROM_PINS
if (on) PORTD |= PWR_BITS; else PORTD &= ~PWR_BITS; // abu išvadai vienu metu
DDRD |= PWR_BITS;
#else
(void)on;
#endif
}
static void send_byte(uint8_t b) {
uint8_t sreg = SREG;
cli();
sig_low();
_delay_us(BIT_US);
for (uint8_t i = 0; i < 8; i++) {
if (b & 1) sig_release(); else sig_low();
b >>= 1;
_delay_us(BIT_US);
}
sig_release();
_delay_us(BIT_US);
SREG = sreg;
}
static void send_line(const char *s) {
sensor.stopListening(); // negirdėti savęs
while (*s) send_byte((uint8_t)*s++);
send_byte('\n');
sensor.listen();
}
// ─── Pagalbinės ──────────────────────────────────────────────────────
#if !POWER_FROM_PINS
static void wait_enter() {
while (true) {
if (Serial.available()) {
char c = Serial.read();
if (c == '\n' || c == '\r') {
delay(20);
while (Serial.available()) Serial.read();
return;
}
}
}
}
#endif
static void print_help() {
Serial.println(F("Sensor commands: ? HELP PRESET MICRO|MINI|MEGA|CUSTOM OUTPUT DIGITAL|PWM"));
Serial.println(F(" DIST <mm> HYST <mm> RANGE <mm> READ DEFAULT RESTART"));
Serial.println(F("Bridge commands: !CONFIG enter configuration mode"));
Serial.println(F(" !TEST run the sensor normally and show its output"));
Serial.println(F(" !HELP this list"));
}
static void enter_config() {
sensor.stopListening();
sig_low();
#if POWER_FROM_PINS
Serial.println(F("Power-cycling the sensor with SIGNAL held LOW..."));
power(false);
delay(POWER_OFF_MS);
power(true);
delay(CONFIG_HOLD_MS);
#else
Serial.println(F("1) Disconnect the sensor's +5V wire."));
Serial.println(F("2) Connect it to the Uno 5V pin. The sensor LED lights steadily after ~0.5 s."));
Serial.println(F("3) Press Enter."));
wait_enter();
#endif
sig_release();
rx_len = 0;
sensor.listen();
mode = MODE_CONFIG;
Serial.println(F("Releasing SIGNAL. If no CONFIG MODE text appears within 2 s, repeat !CONFIG."));
}
static void enter_test() {
sensor.stopListening();
sig_input_no_pull();
#if POWER_FROM_PINS
power(false);
delay(POWER_OFF_MS);
power(true);
#else
Serial.println(F("If the sensor is still in configuration mode, type !CONFIG then RESTART,"));
Serial.println(F("or unplug and reconnect its +5V wire."));
#endif
mode = MODE_TEST;
Serial.println(F("Sensor running normally. Showing its output (type !CONFIG to configure)."));
}
// Modulio atsakymų eilutės: persiunčiamos į PC, iš jų paimami RANGE/OUTPUT.
static void handle_sensor_char(char c) {
if (c == '\r') return;
if (c == '\n') {
rx_line[rx_len] = 0;
if (strncmp(rx_line, "RANGE ", 6) == 0) range_mm = atol(rx_line + 6);
if (strncmp(rx_line, "OUTPUT ", 7) == 0) output_pwm = (strcmp(rx_line + 7, "PWM") == 0);
rx_len = 0;
Serial.println();
return;
}
if (c < 0x20 || c > 0x7E) return; // šiukšlės (pvz. normalaus režimo signalas)
if (rx_len == 0) Serial.print(SENSOR_INDENT);
if (rx_len < sizeof(rx_line) - 1) rx_line[rx_len++] = c;
Serial.write(c);
}
static void handle_pc_line(char *line) {
if (line[0] == 0) return;
Serial.print(F("> "));
Serial.println(line);
if (line[0] == '!') {
for (char *p = line; *p; p++) *p = toupper(*p);
if (strcmp(line, "!CONFIG") == 0) enter_config();
else if (strcmp(line, "!TEST") == 0) enter_test();
else if (strcmp(line, "!HELP") == 0) print_help();
else Serial.println(F("Unknown bridge command, type !HELP"));
return;
}
if (mode != MODE_CONFIG) {
Serial.println(F("Not in configuration mode. Type !CONFIG first."));
return;
}
send_line(line);
// Po RESTART modulis persikrauna į normalų režimą - pereinam į stebėjimą.
char upper[8];
uint8_t i = 0;
for (; i < sizeof(upper) - 1 && line[i]; i++) upper[i] = toupper(line[i]);
upper[i] = 0;
if (strcmp(upper, "RESTART") == 0 && line[i] == 0) {
uint32_t start = millis();
while (millis() - start < 300) {
while (sensor.available()) handle_sensor_char((char)sensor.read());
}
sensor.stopListening();
sig_input_no_pull();
mode = MODE_TEST;
Serial.println(F("Sensor restarted in normal mode. Showing its output (!CONFIG to configure)."));
}
}
static void test_monitor() {
static uint32_t last_ms = 0;
if (millis() - last_ms < TEST_PRINT_MS) return;
last_ms = millis();
unsigned long t = pulseIn(SIG_PIN, HIGH, 3000);
Serial.print(SENSOR_INDENT);
if (t > 0) {
Serial.print(F("PWM pulse "));
Serial.print(t);
Serial.print(F(" us"));
if (range_mm > 0) {
long mm = ((long)t - 50) * range_mm / 900;
if (mm < 0) mm = 0;
Serial.print(F(" -> "));
Serial.print(mm);
Serial.print(F(" mm"));
}
Serial.println();
} else {
bool high = digitalRead(SIG_PIN);
Serial.print(F("OUTPUT "));
Serial.print(high ? F("HIGH") : F("LOW"));
if (output_pwm) Serial.println(high ? F(" (sensor fault)") : F(" (nothing within RANGE)"));
else Serial.println(high ? F(" (object detected)") : F(" (nothing detected)"));
}
}
void setup() {
Serial.begin(PC_BAUD);
sensor.begin(SENSOR_BAUD);
sensor.stopListening();
power(false);
sig_low();
Serial.println(F("\nVL53L1X sensor - Uno configuration bridge"));
#if POWER_FROM_PINS
Serial.println(F("Power: sensor +5V from D3+D4 (automatic)."));
#else
Serial.println(F("Power: sensor +5V from the Uno 5V pin (manual)."));
#endif
print_help();
Serial.println();
enter_config();
}
void loop() {
if (mode == MODE_CONFIG) {
while (sensor.available()) handle_sensor_char((char)sensor.read());
} else if (mode == MODE_TEST) {
test_monitor();
}
while (Serial.available()) {
char c = Serial.read();
if (c == '\r' || c == '\n') {
pc_line[pc_len] = 0;
handle_pc_line(pc_line);
pc_len = 0;
} else if (pc_len < sizeof(pc_line) - 1) {
pc_line[pc_len++] = c;
}
}
}