diff --git a/src/main.cpp b/src/main.cpp index 7075efc..d862c9c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include "target.h" using namespace Adafruit_LittleFS_Namespace; @@ -49,39 +50,73 @@ static const unsigned long ALERT_COOLDOWN_MS = 600000; static unsigned long g_boot_time = 0; static bool g_startup_msg_sent = false; -struct SecurityConfig { uint32_t magic; uint8_t armed; uint8_t relay2; }; -static const uint32_t CFG_MAGIC = 0x53454355; - -struct ChannelConfig { uint32_t magic; char name[32]; char password[32]; }; -static const uint32_t CHAN_MAGIC = 0x4348414E; - -static char g_channel_name[32] = ""; static char g_channel_pass[32] = ""; -bool saveChannelConfig() { - ChannelConfig cfg; - memset(&cfg, 0, sizeof(cfg)); - cfg.magic = CHAN_MAGIC; - strncpy(cfg.name, g_channel_name, sizeof(cfg.name) - 1); - strncpy(cfg.password, g_channel_pass, sizeof(cfg.password) - 1); +bool saveChannelPass(const char* pass) { File f = InternalFS.open(CHANNEL_FILE, FILE_O_WRITE); if (!f) return false; - bool ok = f.write((const uint8_t*)&cfg, sizeof(cfg)) == sizeof(cfg); + size_t len = strlen(pass); + if (len > 31) len = 31; + bool ok = f.write((const uint8_t*)pass, len) == len; f.close(); return ok; } -bool loadChannelConfig() { +bool loadChannelPass() { File f = InternalFS.open(CHANNEL_FILE, FILE_O_READ); if (!f) return false; - ChannelConfig cfg; - if (f.read(&cfg, sizeof(cfg)) != sizeof(cfg)) { f.close(); return false; } + int len = f.read((uint8_t*)g_channel_pass, sizeof(g_channel_pass) - 1); f.close(); - if (cfg.magic != CHAN_MAGIC) return false; - strncpy(g_channel_name, cfg.name, sizeof(g_channel_name) - 1); - strncpy(g_channel_pass, cfg.password, sizeof(g_channel_pass) - 1); - return true; + if (len > 0) { g_channel_pass[len] = 0; return true; } + return false; } +BLEUart bleuart; + +void ble_send(const char* text) { + bleuart.println(text); +} + +void setup_ble() { + Bluefruit.begin(1, 0); + Bluefruit.setName("Security-Mesh"); + Bluefruit.setTxPower(4); + bleuart.begin(); + Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE); + Bluefruit.Advertising.addTxPower(); + Bluefruit.Advertising.addService(bleuart); + Bluefruit.ScanResponse.addName(); + Bluefruit.Advertising.restartOnDisconnect(true); + Bluefruit.Advertising.setInterval(32, 244); + Bluefruit.Advertising.start(0); +} + +void process_ble_line(const char* line) { + if (strncmp(line, "channel ", 8) == 0) { + const char* pass = line + 8; + int len = strlen(pass); + if (len < 1 || len > 31) { ble_send("bad password length"); return; } + int digits = 0; + for (int i = 0; pass[i]; i++) if (pass[i] >= '0' && pass[i] <= '9') digits++; + if (digits != len) { ble_send("use digits only"); return; } + strncpy(g_channel_pass, pass, sizeof(g_channel_pass) - 1); + saveChannelPass(pass); + char r[64]; snprintf(r, sizeof(r), "channel %s saved, rebooting...", pass); + ble_send(r); + delay(100); + NVIC_SystemReset(); + } else if (strcmp(line, "status") == 0) { + char r[64]; snprintf(r, sizeof(r), "channel: %s", g_channel_pass[0] ? g_channel_pass : "1234"); + ble_send(r); + } else if (strcmp(line, "help") == 0) { + ble_send("commands: channel , status, help"); + } else { + ble_send("unknown. try help"); + } +} + +struct SecurityConfig { uint32_t magic; uint8_t armed; uint8_t relay2; }; +static const uint32_t CFG_MAGIC = 0x53454355; + bool saveSecurityConfig() { SecurityConfig cfg = { CFG_MAGIC, g_armed ? 1 : 0, g_relay2_state ? 1 : 0 }; File f = InternalFS.open(CONFIG_FILE, FILE_O_WRITE); @@ -179,7 +214,7 @@ public: while (*cmd == ' ') cmd++; if (strcmp(cmd, "help") == 0) { - defer_response("help:ohrana on,ohrana off,relay1 on,relay1 off,relay2 on,relay2 off,relay,status,ver,scan,canal"); + defer_response("help:ohrana on,ohrana off,relay1 on,relay1 off,relay2 on,relay2 off,relay,status,ver,scan"); } else if (strcmp(cmd, "ohrana on") == 0) { g_armed = true; digitalWrite(PIN_RELAY_1, LOW); saveSecurityConfig(); defer_response("ohrana on ok"); } else if (strcmp(cmd, "ohrana off") == 0) { @@ -211,22 +246,6 @@ public: float sum = 0; for (int j = 0; j < 20; j++) { sum += radio_driver.getCurrentRSSI(); delay(50); } char r[32]; snprintf(r, sizeof(r), "noise:%.0fdBm", sum / 20); defer_response(r); - } else if (strncmp(cmd, "canal", 5) == 0) { - char buf[128]; strncpy(buf, cmd, sizeof(buf) - 1); buf[sizeof(buf) - 1] = 0; - char* rest = buf + 5; while (*rest == ' ') rest++; - char* cname = rest; - char* space = strchr(cname, ' '); - if (!space) { defer_response("need name and password"); return; } - *space = 0; - char* cpass = space + 1; - if (*cpass == 0) { defer_response("need name and password"); return; } - strncpy(g_channel_name, cname, sizeof(g_channel_name) - 1); - strncpy(g_channel_pass, cpass, sizeof(g_channel_pass) - 1); - saveChannelConfig(); - char r[64]; snprintf(r, sizeof(r), "canal %s %s ok", cname, cpass); - send_text(r); - for (int i = 0; i < 5; i++) { digitalWrite(PIN_LED_STATUS, HIGH); delay(100); digitalWrite(PIN_LED_STATUS, LOW); delay(100); } - NVIC_SystemReset(); } } }; @@ -264,10 +283,12 @@ void setup() { InternalFS.begin(); loadSecurityConfig(); - loadChannelConfig(); + loadChannelPass(); if (g_armed) digitalWrite(PIN_RELAY_1, LOW); else digitalWrite(PIN_RELAY_1, HIGH); digitalWrite(PIN_RELAY_2, g_relay2_state ? LOW : HIGH); + setup_ble(); + if (!radio_init()) { Serial.println("RADIO FAIL"); while (1) { digitalWrite(PIN_LED_STATUS, HIGH); delay(100); digitalWrite(PIN_LED_STATUS, LOW); delay(100); } @@ -287,6 +308,19 @@ void loop() { the_mesh.flush_response(); rtc_clock.tick(); + static char ble_line[64]; + static int ble_pos = 0; + while (bleuart.available()) { + char c = bleuart.read(); + if (c == '\n' || c == '\r') { + ble_line[ble_pos] = 0; + if (ble_pos > 0) process_ble_line(ble_line); + ble_pos = 0; + } else if (ble_pos < (int)sizeof(ble_line) - 1) { + ble_line[ble_pos++] = c; + } + } + if (!g_startup_msg_sent && millis() - g_boot_time >= 10000) { g_startup_msg_sent = true; char m[80]; snprintf(m, sizeof(m), "poweron %s %s", __DATE__, __TIME__);