From 6368eca7aa9c4293ee95d5a807620f847e2f8ee2 Mon Sep 17 00:00:00 2001 From: Sassa NF Date: Tue, 31 Dec 2024 11:00:48 +0000 Subject: [PATCH 1/8] Default lora config --- lib/config/config.cpp | 53 +++++++++++++++++++------------------------ lib/config/config.h | 30 ++++++++++++++++++++---- platformio.ini | 3 +++ src/main.cpp | 4 ++-- 4 files changed, 54 insertions(+), 36 deletions(-) diff --git a/lib/config/config.cpp b/lib/config/config.cpp index 2b2ec69..07ae40a 100644 --- a/lib/config/config.cpp +++ b/lib/config/config.cpp @@ -107,23 +107,26 @@ Config Config::init() return c; } +#define UPDATE_BOOL(val, key, value) \ + if (key.equalsIgnoreCase(#val)) \ + { \ + String v = value; \ + bool p = v.equalsIgnoreCase("true"); \ + if (!p && !v.equalsIgnoreCase("false")) \ + { \ + Serial.printf("Expected bool for '%s', found '%s' - ignoring\n", \ + key.c_str(), value.c_str()); \ + } \ + else \ + { \ + val = p; \ + } \ + return true; \ + } + bool Config::updateConfig(String key, String value) { - if (key.equalsIgnoreCase("print_profile_time")) - { - String v = value; - bool p = v.equalsIgnoreCase("true"); - if (!p && !v.equalsIgnoreCase("false")) - { - Serial.printf("Expected bool for '%s', found '%s' - ignoring\n", key.c_str(), - value.c_str()); - } - else - { - print_profile_time = p; - } - return true; - } + UPDATE_BOOL(print_profile_time, key, value); if (key.equalsIgnoreCase("log_data_json_interval")) { @@ -167,21 +170,9 @@ bool Config::updateConfig(String key, String value) return true; } - if (key.equalsIgnoreCase("is_host")) - { - String v = value; - bool p = v.equalsIgnoreCase("true"); - if (!p && !v.equalsIgnoreCase("false")) - { - Serial.printf("Expected bool for '%s', found '%s' - ignoring\n", key.c_str(), - value.c_str()); - } - else - { - is_host = p; - } - return true; - } + UPDATE_BOOL(is_host, key, value); + + UPDATE_BOOL(lora_enabled, key, value); return false; } @@ -512,6 +503,8 @@ bool Config::write_config(const char *path) f.println("tx_lora = " + getConfig("tx_lora")); f.println("is_host = " + getConfig("is_host")); + f.println("lora_enabled = " + getConfig("lora_enabled")); + f.close(); return true; } diff --git a/lib/config/config.h b/lib/config/config.h index 87136e8..29ce03e 100644 --- a/lib/config/config.h +++ b/lib/config/config.h @@ -25,6 +25,28 @@ struct LoRaConfig LoRaConfig *configureLora(String cfg); +#ifndef FREQ_RX +#define FREQ_RX 866 +#endif + +#ifndef FREQ_TX +#define FREQ_TX (FREQ_RX + 2) +#endif + +#define DEFAULT_RX String("freq:") + String(FREQ_RX) +#define DEFAULT_TX String("freq:") + String(FREQ_TX) + +#ifndef DEFAULT_TX_SCAN_ON_BOOT +#define DEFAULT_TX_SCAN_ON_BOOT false +#endif + +#ifndef DEFAULT_IS_LORA_HOST +#define DEFAULT_IS_LORA_HOST false +#endif + +#ifndef DEFAULT_LORA_ENABLED +#define DEFAULT_LORA_ENABLED false +#endif #define CREATE_MISSING_CONFIG true struct Config { @@ -42,16 +64,16 @@ struct Config LoRaConfig *tx_lora; bool is_host; + bool lora_enabled; Config() : create_missing_config(CREATE_MISSING_CONFIG), print_profile_time(false), detection_strategy(String("RSSI")), samples(0), scan_ranges_sz(0), scan_ranges(NULL), log_data_json_interval(1000), listen_on_serial0(String("none")), listen_on_serial1(String("readline")), - listen_on_usb(String("readline")), rx_lora(NULL), tx_lora(NULL), - // Enable Lora Send: - // rx_lora(configureLora("freq:920")),tx_lora(configureLora("freq:916")) - is_host(false) {}; + listen_on_usb(String("readline")), rx_lora(configureLora(DEFAULT_RX)), + tx_lora(configureLora(DEFAULT_TX)), is_host(DEFAULT_IS_LORA_HOST), + lora_enabled(DEFAULT_LORA_ENABLED) {}; bool write_config(const char *path); diff --git a/platformio.ini b/platformio.ini index 7225048..370060d 100644 --- a/platformio.ini +++ b/platformio.ini @@ -70,6 +70,7 @@ build_flags = -DHELTEC -DFREQ_BEGIN=130 -DFREQ_END=180 + -DFREQ_RX=133 -DRADIOLIB_CHECK_PARAMS=0 [env:lilygo-T3S3-v1-2-sx1262] @@ -122,6 +123,7 @@ build_flags = -DUSING_LR1121 -DFREQ_BEGIN=2400 -DFREQ_END=2500 + -DFREQ_RX=2440 -DARDUINO_ARCH_ESP32 -DARDUINO_USB_CDC_ON_BOOT=1 -DARDUINO_LILYGO_T3_S3_V1_X @@ -150,6 +152,7 @@ build_flags = -DUSING_SX1280PA -DFREQ_BEGIN=2400 -DFREQ_END=2500 + -DFREQ_RX=2440 -DARDUINO_ARCH_ESP32 -DARDUINO_USB_CDC_ON_BOOT=1 -DARDUINO_LILYGO_T3_S3_V1_X diff --git a/src/main.cpp b/src/main.cpp index aa63139..6b24e25 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1521,9 +1521,9 @@ void loop(void) else { doScan(); - if (TxComms != NULL) + if (TxComms != NULL && config.lora_enabled) reportScan(*TxComms); - if (RxComms != NULL) + if (RxComms != NULL && config.lora_enabled) checkRadio(*RxComms); } } From 3d7ab81f6f0ea4dff991a0cd7466c3dd85988158 Mon Sep 17 00:00:00 2001 From: Sassa NF Date: Tue, 31 Dec 2024 11:01:34 +0000 Subject: [PATCH 2/8] Rework radio message processing --- lib/comms/comms.cpp | 17 ++- lib/comms/comms.h | 11 +- lib/comms/radio_comms.cpp | 224 ++++++++++++++++++++++++++++++-------- lib/config/config.h | 10 ++ src/main.cpp | 126 +++++++++++++++------ 5 files changed, 298 insertions(+), 90 deletions(-) diff --git a/lib/comms/comms.cpp b/lib/comms/comms.cpp index 53781b3..b68bcde 100644 --- a/lib/comms/comms.cpp +++ b/lib/comms/comms.cpp @@ -265,12 +265,18 @@ bool ReadlineComms::send(Message &m) p = _scan_result_str(m.payload.dump); break; case MessageType::CONFIG_TASK: - p = m.payload.config.is_set ? "SET " : "GET "; + ConfigTaskType ctt = m.payload.config.task_type; + p = ctt == GET ? "GET " + : ctt == SET ? "SET " + : ctt == GETSET_SUCCESS ? "Success: " + : "Failed to set: "; p += *m.payload.config.key; - if (m.payload.config.is_set) + if (ctt == SET || ctt == GETSET_SUCCESS) { p += " " + *m.payload.config.value; } + + p += "\n"; break; } @@ -368,7 +374,7 @@ Message *_parsePacket(String p) { Message *m = new Message(); m->type = MessageType::CONFIG_TASK; - m->payload.config.is_set = false; + m->payload.config.task_type = GET; m->payload.config.key = new String(_stringParam(p, "")); m->payload.config.value = NULL; return m; @@ -378,7 +384,7 @@ Message *_parsePacket(String p) { Message *m = new Message(); m->type = MessageType::CONFIG_TASK; - m->payload.config.is_set = true; + m->payload.config.task_type = SET; m->payload.config.key = new String(_stringParam(p, "")); m->payload.config.value = new String(_stringParam(p, "")); @@ -430,7 +436,8 @@ Message::~Message() { delete payload.config.key; - if (payload.config.is_set) + ConfigTaskType ctt = payload.config.task_type; + if (ctt == GETSET_SUCCESS || ctt == SET) { delete payload.config.value; } diff --git a/lib/comms/comms.h b/lib/comms/comms.h index b852a06..990babb 100644 --- a/lib/comms/comms.h +++ b/lib/comms/comms.h @@ -22,6 +22,15 @@ enum MessageType _MAX_MESSAGE_TYPE = CONFIG_TASK }; +enum ConfigTaskType +{ + GET = 0, + SET, + GETSET_SUCCESS, + SET_FAIL, + _MAX_CONFIG_TASK_TYPE = SET_FAIL +}; + struct Wrapper { int32_t length; @@ -45,7 +54,7 @@ struct ConfigTask { String *key; String *value; - bool is_set; + ConfigTaskType task_type; }; struct Message diff --git a/lib/comms/radio_comms.cpp b/lib/comms/radio_comms.cpp index fa269b6..0cce77b 100644 --- a/lib/comms/radio_comms.cpp +++ b/lib/comms/radio_comms.cpp @@ -48,24 +48,25 @@ size_t _write(uint8_t *m, size_t sz, size_t p, uint8_t *v, size_t v_sz) #define RSSI_HI -80 #define DETAIL_RSSIS 8 #define RSSI_LO (RSSI_HI - 1 - DETAIL_RSSIS) -int16_t RadioComms::send(Message &m) + +uint8_t *_serialize_scan_result(Message &m, size_t &p, uint8_t *msg) { if (m.type != SCAN_RESULT) { - return RADIOLIB_ERR_INVALID_FUNCTION; + return NULL; } - uint8_t msg[MAX_MSG]; size_t dump_sz = m.payload.dump.sz; - size_t p = _write(msg, MAX_MSG, 0, (uint8_t)m.type); + size_t max_msg = p; + p = _write(msg, max_msg, 0, (uint8_t)m.type); // first cut: dump the RSSI as-is // optimize the message size later - p = _write(msg, MAX_MSG, p, (uint8_t *)&m.payload.dump.freqs_khz[0], 4); - p = _write(msg, MAX_MSG, p, (uint8_t *)&m.payload.dump.freqs_khz[dump_sz - 1], 4); - p = _write(msg, MAX_MSG, p, (uint8_t *)&dump_sz, 2); + p = _write(msg, max_msg, p, (uint8_t *)&m.payload.dump.freqs_khz[0], 4); + p = _write(msg, max_msg, p, (uint8_t *)&m.payload.dump.freqs_khz[dump_sz - 1], 4); + p = _write(msg, max_msg, p, (uint8_t *)&dump_sz, 2); - size_t rem = MAX_MSG - p; + size_t rem = max_msg - p; if (rem > dump_sz) rem = dump_sz; @@ -75,7 +76,7 @@ int16_t RadioComms::send(Message &m) { if (i * rem / dump_sz > p - pp) { - p = _write(msg, MAX_MSG, p, bits); + p = _write(msg, max_msg, p, bits); bits = 0; } int16_t v = m.payload.dump.rssis[i]; @@ -97,10 +98,79 @@ int16_t RadioComms::send(Message &m) if (dump_sz > 0) { - p = _write(msg, MAX_MSG, p, bits); + p = _write(msg, max_msg, p, bits); } - return radio.transmit(msg, p); + return msg; +} + +uint8_t *_serialize_config_task(Message &m, size_t &p, uint8_t *msg) +{ + if (m.type != CONFIG_TASK) + { + return NULL; + } + size_t max_msg = p; + ConfigTaskType ctt = m.payload.config.task_type; + p = _write(msg, max_msg, 0, (uint8_t)m.type); + p = _write(msg, max_msg, p, (uint8_t)ctt); + + int key_len = m.payload.config.key->length(); + if (max_msg - p < key_len + 1) + { + return NULL; + } + + p = _write(msg, max_msg, p, (uint8_t)key_len); + p = _write(msg, max_msg, p, (uint8_t *)m.payload.config.key->c_str(), key_len); + + if (ctt == GET || ctt == SET_FAIL) + { + return msg; + } + + int v_len = m.payload.config.value->length(); + + if (max_msg - p < v_len + 1) + { + return NULL; + } + + p = _write(msg, max_msg, p, (uint8_t)v_len); + p = _write(msg, max_msg, p, (uint8_t *)m.payload.config.value->c_str(), v_len); + + return msg; +} + +int16_t RadioComms::send(Message &m) +{ + uint8_t msg_buf[MAX_MSG]; + size_t p = MAX_MSG; + uint8_t *msg = NULL; + + if (m.type == SCAN_RESULT) + { + msg = _serialize_scan_result(m, p, msg_buf); + } + else if (m.type == MessageType::CONFIG_TASK) + { + msg = _serialize_config_task(m, p, msg_buf); + } + + if (msg == NULL) + { + Serial.printf("Failed to encode message\n"); + return RADIOLIB_ERR_INVALID_FUNCTION; + } + + int16_t status = radio.transmit(msg, p); + + if (msg != msg_buf) + { + delete[] msg; + } + + return status; } size_t _read(uint8_t *buf, size_t sz, size_t p, uint8_t *v, size_t len) @@ -118,6 +188,96 @@ size_t _read(uint8_t *buf, size_t sz, size_t p, uint8_t *v) return _read(buf, sz, p, v, 1); } +Message *_deserialize_scan_result(size_t len, size_t &p, uint8_t *packet) +{ + Message *message = new Message(); + message->type = SCAN_RESULT; + + uint32_t s, e; + size_t dump_sz = 0; + p = _read(packet, len, p, (uint8_t *)&s, 4); + p = _read(packet, len, p, (uint8_t *)&e, 4); + p = _read(packet, len, p, (uint8_t *)&dump_sz, 2); + size_t rem = len - p; + + message->payload.dump.sz = dump_sz; + if (dump_sz > 0) + { + message->payload.dump.rssis = new int16_t[dump_sz]; + message->payload.dump.freqs_khz = new uint32_t[dump_sz]; + message->payload.dump.freqs_khz[0] = s; + message->payload.dump.freqs_khz[dump_sz - 1] = e; + + for (int i = 1; i < dump_sz - 1; i++) + { + uint32_t incr = (e - s) / (dump_sz - i); + s += incr; + message->payload.dump.freqs_khz[i] = s; + } + + for (int i = 0, k = 0; i < rem; i++) + { + int j = (i + 1) * dump_sz / rem; + int16_t rssi = 0; + p = _read(packet, len, p, (uint8_t *)&rssi); + rssi -= 255; + for (; k < j; k++) + { + message->payload.dump.rssis[k] = rssi; + } + } + } + + return message; +} + +Message *_deserialize_config_task(size_t len, size_t &p, uint8_t *packet) +{ + Message *message = new Message(); + message->type = CONFIG_TASK; + + ConfigTaskType ctt = GET; + p = _read(packet, len, p, (uint8_t *)&ctt); + message->payload.config.task_type = ctt; + + size_t key_len = 0; + size_t p1 = _read(packet, len, p, (uint8_t *)&key_len); + memmove(packet + p, packet + p + 1, key_len); + packet[p + key_len] = 0; + String *key = new String((char *)packet + p); + message->payload.config.key = key; + + p = p1 + key_len; + + if (key->length() != key_len) + { + delete message; + return NULL; + } + + if (ctt == GET || ctt == SET_FAIL) + { + return message; + } + + size_t v_len = 0; + p1 = _read(packet, len, p, (uint8_t *)&v_len); + memmove(packet + p, packet + p + 1, v_len); + packet[p + v_len] = 0; + String *value = new String((char *)packet + p); + message->payload.config.value = value; + + p = p1 + v_len; + + if (value->length() != v_len) + { + delete message; + return NULL; + } + + return message; +} + volatile bool _received = false; void _rcv() { _received = true; } @@ -184,43 +344,11 @@ Message *RadioComms::receive(uint16_t timeout_ms) Message *message = NULL; if (b == SCAN_RESULT) { - message = new Message(); - message->type = SCAN_RESULT; - - uint32_t s, e; - size_t dump_sz = 0; - p = _read(packet, len, p, (uint8_t *)&s, 4); - p = _read(packet, len, p, (uint8_t *)&e, 4); - p = _read(packet, len, p, (uint8_t *)&dump_sz, 2); - size_t rem = len - p; - - message->payload.dump.sz = dump_sz; - if (dump_sz > 0) - { - message->payload.dump.rssis = new int16_t[dump_sz]; - message->payload.dump.freqs_khz = new uint32_t[dump_sz]; - message->payload.dump.freqs_khz[0] = s; - message->payload.dump.freqs_khz[dump_sz - 1] = e; - - for (int i = 1; i < dump_sz - 1; i++) - { - uint32_t incr = (e - s) / (dump_sz - i); - s += incr; - message->payload.dump.freqs_khz[i] = s; - } - - for (int i = 0, k = 0; i < rem; i++) - { - int j = (i + 1) * dump_sz / rem; - int16_t rssi = 0; - p = _read(packet, len, p, (uint8_t *)&rssi); - rssi -= 255; - for (; k < j; k++) - { - message->payload.dump.rssis[k] = rssi; - } - } - } + message = _deserialize_scan_result(len, p, packet); + } + else if (b == CONFIG_TASK) + { + message = _deserialize_config_task(len, p, packet); } else { diff --git a/lib/config/config.h b/lib/config/config.h index 29ce03e..8c9fc59 100644 --- a/lib/config/config.h +++ b/lib/config/config.h @@ -3,6 +3,16 @@ #include +template struct Result +{ + bool is_ok; + union + { + L not_ok; + R ok; + }; +}; + struct ScanRange { uint64_t start_khz; diff --git a/src/main.cpp b/src/main.cpp index 6b24e25..b693133 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1425,7 +1425,13 @@ void checkComms() Comms1->send(*m); // forward to peer break; case MessageType::CONFIG_TASK: - if (m->payload.config.is_set) + ConfigTaskType ctt = m->payload.config.task_type; + if (ctt == GET) + { + Serial.printf("GET config: %s = %s\n", m->payload.config.key->c_str(), + config.getConfig(*m->payload.config.key).c_str()); + } + else if (ctt == SET) { String v = config.getConfig(*m->payload.config.key); bool r = @@ -1494,7 +1500,8 @@ void doScan(); void reportScan(RadioComms &c); -int16_t checkRadio(RadioComms &c); +Result checkRadio(RadioComms &c); +int16_t sendMessage(RadioComms &c, Message &m); void loop(void) { @@ -1504,27 +1511,43 @@ void loop(void) drone_detected_frequency_start = 0; checkComms(); + // NB: swapping the use of Tx and Rx comms, so a pair of modules + // with identical rx/tx_lora config can talk + RadioComms *rx = config.is_host ? TxComms : RxComms; + RadioComms *tx = config.is_host ? RxComms : TxComms; - if (config.is_host) + if (rx != NULL && (config.is_host || config.lora_enabled)) { - if (TxComms != NULL) + Result res = checkRadio(*rx); + + if (!res.is_ok) { - // NB: swapping the use of Tx and Rx comms, so a pair of modules - // with identical rx/tx_lora config can talk - int16_t status = checkRadio(*TxComms); + int16_t status = res.not_ok; if (status != RADIOLIB_ERR_NONE) { Serial.printf("Error getting a message: %d\n", status); } } + else if (res.ok != NULL) + { + if (config.is_host || tx == NULL) + { + HostComms->send(*res.ok); + } + else + { + sendMessage(*tx, *res.ok); + } + + delete res.ok; + } } - else + + if (!config.is_host) { doScan(); - if (TxComms != NULL && config.lora_enabled) - reportScan(*TxComms); - if (RxComms != NULL && config.lora_enabled) - checkRadio(*RxComms); + if (tx != NULL && config.lora_enabled) + reportScan(*tx); } } @@ -1961,56 +1984,87 @@ void doScan() #endif } -int16_t checkRadio(RadioComms &comms) +Result checkRadio(RadioComms &comms) { radioIsScan = false; - int16_t status = comms.configureRadio(); - if (status != RADIOLIB_ERR_NONE) - return status; + Result ret; + ret.is_ok = false; + + ret.not_ok = comms.configureRadio(); + if (ret.not_ok != RADIOLIB_ERR_NONE) + return ret; + + ret.is_ok = true; Message *msg = comms.receive( config.is_host ? 2000 : 200); // 200ms should be enough to receive 500 bytes at SF 7 and BW 500 + ret.ok = msg; if (msg == NULL) { - return status; + return ret; } - if (msg->type == SCAN_RESULT) + if (msg->type == CONFIG_TASK) { - HostComms->send(*msg); - } - else - { - Serial.printf("Received a message of unsupported type: %d\n", msg->type); + ConfigTaskType ctt = msg->payload.config.task_type; + + if (ctt == ConfigTaskType::GET || ctt == ConfigTaskType::SET) + { + // must be GET or SET - both require sending back a response + + String old_v = config.getConfig(*msg->payload.config.key); + bool success = true; + if (ctt == ConfigTaskType::SET) + { + success = config.updateConfig(*msg->payload.config.key, + *msg->payload.config.value); + delete msg->payload.config.value; + } + + if (success) + { + msg->payload.config.task_type = GETSET_SUCCESS; + msg->payload.config.value = new String(old_v); + } + else + { + msg->payload.config.task_type = SET_FAIL; + } + } } - delete msg; - - return status; + return ret; } -void reportScan(RadioComms &comms) +int16_t sendMessage(RadioComms &comms, Message &msg) { radioIsScan = false; int16_t status = comms.configureRadio(); if (status != RADIOLIB_ERR_NONE) { Serial.printf("Failed to configure Radio: %d\n", status); - return; + return status; } - Message m; - m.type = SCAN_RESULT; - m.payload.dump = frequency_scan_result.dump; - status = comms.send(m); - - m.payload.dump.sz = 0; // dump is shared, so should not delete underlying arrays + status = comms.send(msg); if (status != RADIOLIB_ERR_NONE) { - Serial.printf("Failed to send scan result: %d\n", status); - return; + Serial.printf("Failed to send message of type %d: %d\n", msg.type, status); + return status; } + + return status; +} + +void reportScan(RadioComms &comms) +{ + Message m; + m.type = SCAN_RESULT; + m.payload.dump = frequency_scan_result.dump; + int16_t status = sendMessage(comms, m); + + m.payload.dump.sz = 0; // dump is shared, so should not delete underlying arrays } From 5af6a829f35626792cf448e50f9ad94cf519eaad Mon Sep 17 00:00:00 2001 From: Sassa NF Date: Tue, 31 Dec 2024 14:42:48 +0000 Subject: [PATCH 3/8] Support message routing --- lib/comms/comms.h | 17 +++ src/main.cpp | 312 +++++++++++++++++++++++++++------------------- 2 files changed, 200 insertions(+), 129 deletions(-) diff --git a/lib/comms/comms.h b/lib/comms/comms.h index 990babb..ff11325 100644 --- a/lib/comms/comms.h +++ b/lib/comms/comms.h @@ -71,6 +71,23 @@ struct Message ~Message(); }; +enum Endpoint +{ + LOOP = 0, // self + UART0, + UART1, + LORA, // rx or tx_lora, depending on is_host + HOST, // USB + MAX_ENDPOINT = HOST +}; + +struct RoutedMessage +{ + Endpoint from; + Endpoint to; + Message *message; +}; + struct Comms { String name; diff --git a/src/main.cpp b/src/main.cpp index b693133..f8d3225 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1407,47 +1407,141 @@ bool is_new_x_pixel(int x) return false; } -void checkComms() +/* + * Given message type and config, modify from/to to route the message to the + * right destination. + */ +void routeMessage(RoutedMessage &m) { + if (m.message == NULL) + { + return; + } + + if (m.message->type == MessageType::SCAN_RESULT || + m.message->type == MessageType::CONFIG_TASK && + (m.message->payload.config.task_type == ConfigTaskType::GETSET_SUCCESS || + m.message->payload.config.task_type == ConfigTaskType::SET_FAIL)) + { + m.to = HOST; + return; + } + + if (config.is_host && + (m.message->type == MessageType::SCAN || + m.message->type == MessageType::CONFIG_TASK && + m.message->payload.config.key->equalsIgnoreCase("detection_strategy"))) + { + m.to = LORA; + return; + } +} + +int16_t sendMessage(RadioComms &c, Message &m); + +void loraSendMessage(Message &m); + +/* + * If m.to is LOOP, the message is directed at this module; enact the message. + * If m.to is not LOOP, send the message via the respective interface. + */ +void sendMessage(RoutedMessage &m) +{ + if (m.message == NULL) + { + return; + } + + Message *msg = m.message; + + if (m.to == LOOP) + { + switch (msg->type) + { + case MessageType::SCAN: + report_scans = msg->payload.scan; + requested_host = m.from == HOST; + return; + case MessageType::CONFIG_TASK: + ConfigTaskType ctt = msg->payload.config.task_type; + + // sanity check; GETSET_SUCCESS and SET_FAIL should get routed to HOST + if (ctt == ConfigTaskType::GET || ctt == ConfigTaskType::SET) + { + // must be GET or SET - both require sending back a response + + String old_v = config.getConfig(*msg->payload.config.key); + bool success = true; + if (ctt == ConfigTaskType::SET) + { + success = config.updateConfig(*msg->payload.config.key, + *msg->payload.config.value); + delete msg->payload.config.value; + } + + if (success) + { + msg->payload.config.task_type = GETSET_SUCCESS; + msg->payload.config.value = new String(old_v); + } + else + { + msg->payload.config.task_type = SET_FAIL; + } + + m.to = m.from; + m.from = LOOP; + sendMessage(m); + } + return; + } + + return; + } + + if (m.to == HOST) + { + HostComms->send(*m.message); + return; + } + + if (m.to == UART0) + { + Comms0->send(*m.message); + return; + } + + if (m.to == UART1) + { + Comms1->send(*m.message); + return; + } + + if (m.to == LORA) + { + loraSendMessage(*m.message); + return; + } +} + +Result checkRadio(RadioComms &c); + +RoutedMessage checkComms() +{ + RoutedMessage mess; + mess.from = LOOP; + mess.to = LOOP; + mess.message = NULL; + while (HostComms->available() > 0) { Message *m = HostComms->receive(); if (m == NULL) continue; - switch (m->type) - { - case MessageType::SCAN: - report_scans = m->payload.scan; - requested_host = true; - Serial.println("Host: forwarding message SCAN to peer"); - Comms0->send(*m); // forward to peer - Comms1->send(*m); // forward to peer - break; - case MessageType::CONFIG_TASK: - ConfigTaskType ctt = m->payload.config.task_type; - if (ctt == GET) - { - Serial.printf("GET config: %s = %s\n", m->payload.config.key->c_str(), - config.getConfig(*m->payload.config.key).c_str()); - } - else if (ctt == SET) - { - String v = config.getConfig(*m->payload.config.key); - bool r = - config.updateConfig(*m->payload.config.key, *m->payload.config.value); - Serial.printf("SET config (%s): %s = %s (was: %s)\n", r ? "OK" : "failed", - m->payload.config.key->c_str(), - m->payload.config.value->c_str(), v.c_str()); - } - else - { - Serial.printf("GET config: %s = %s\n", m->payload.config.key->c_str(), - config.getConfig(*m->payload.config.key).c_str()); - } - break; - } - delete m; + mess.from = HOST; + mess.message = m; + return mess; } while (Comms0->available() > 0) @@ -1457,18 +1551,9 @@ void checkComms() if (m == NULL) continue; - switch (m->type) - { - case MessageType::SCAN: - report_scans = m->payload.scan; // receive from peer - requested_host = false; - break; - - case MessageType::SCAN_RESULT: - HostComms->send(*m); // forward from peer - break; - } - delete m; + mess.from = UART0; + mess.message = m; + return mess; } while (Comms1->available() > 0) @@ -1478,43 +1563,14 @@ void checkComms() if (m == NULL) continue; - switch (m->type) - { - case MessageType::SCAN: - report_scans = m->payload.scan; // receive from peer - requested_host = false; - break; - - case MessageType::SCAN_RESULT: - HostComms->send(*m); // forward from peer - break; - } - delete m; + mess.from = UART1; + mess.message = m; + return mess; } -} -// MAX Frequency RSSI BIN value of the samples -int max_rssi_x = 999; - -void doScan(); - -void reportScan(RadioComms &c); - -Result checkRadio(RadioComms &c); -int16_t sendMessage(RadioComms &c, Message &m); - -void loop(void) -{ - r.led_flag = false; - - r.detection_count = 0; - drone_detected_frequency_start = 0; - - checkComms(); // NB: swapping the use of Tx and Rx comms, so a pair of modules // with identical rx/tx_lora config can talk RadioComms *rx = config.is_host ? TxComms : RxComms; - RadioComms *tx = config.is_host ? RxComms : TxComms; if (rx != NULL && (config.is_host || config.lora_enabled)) { @@ -1528,26 +1584,43 @@ void loop(void) Serial.printf("Error getting a message: %d\n", status); } } - else if (res.ok != NULL) + else { - if (config.is_host || tx == NULL) - { - HostComms->send(*res.ok); - } - else - { - sendMessage(*tx, *res.ok); - } - - delete res.ok; + mess.from = LORA; + mess.message = res.ok; } + + return mess; + } + + return mess; +} + +// MAX Frequency RSSI BIN value of the samples +int max_rssi_x = 999; + +void doScan(); + +void reportScan(); + +void loop(void) +{ + r.led_flag = false; + + r.detection_count = 0; + drone_detected_frequency_start = 0; + + for (RoutedMessage mess = checkComms(); mess.message != NULL; mess = checkComms()) + { + routeMessage(mess); + sendMessage(mess); + delete mess.message; } if (!config.is_host) { doScan(); - if (tx != NULL && config.lora_enabled) - reportScan(*tx); + reportScan(); } } @@ -1703,9 +1776,9 @@ void doScan() else if (config.detection_strategy.equalsIgnoreCase("CAD")) { g = &getCAD; - samples = min( - 1, - CONF_SAMPLES); // TODO: do we need to support values other than 1 + samples = min(1, + CONF_SAMPLES); // TODO: do we need to support values + // other than 1 } else g = &getRSSI; @@ -2001,39 +2074,6 @@ Result checkRadio(RadioComms &comms) ? 2000 : 200); // 200ms should be enough to receive 500 bytes at SF 7 and BW 500 ret.ok = msg; - if (msg == NULL) - { - return ret; - } - - if (msg->type == CONFIG_TASK) - { - ConfigTaskType ctt = msg->payload.config.task_type; - - if (ctt == ConfigTaskType::GET || ctt == ConfigTaskType::SET) - { - // must be GET or SET - both require sending back a response - - String old_v = config.getConfig(*msg->payload.config.key); - bool success = true; - if (ctt == ConfigTaskType::SET) - { - success = config.updateConfig(*msg->payload.config.key, - *msg->payload.config.value); - delete msg->payload.config.value; - } - - if (success) - { - msg->payload.config.task_type = GETSET_SUCCESS; - msg->payload.config.value = new String(old_v); - } - else - { - msg->payload.config.task_type = SET_FAIL; - } - } - } return ret; } @@ -2059,12 +2099,26 @@ int16_t sendMessage(RadioComms &comms, Message &msg) return status; } -void reportScan(RadioComms &comms) +void loraSendMessage(Message &msg) { + RadioComms *tx = config.is_host ? RxComms : TxComms; + if (tx == NULL) + { + return; + } + + sendMessage(*tx, msg); +} + +void reportScan() +{ + if (!config.lora_enabled) + return; + Message m; m.type = SCAN_RESULT; m.payload.dump = frequency_scan_result.dump; - int16_t status = sendMessage(comms, m); + loraSendMessage(m); m.payload.dump.sz = 0; // dump is shared, so should not delete underlying arrays } From 500599ec7a2bf36235c377d559905fb1517eb431 Mon Sep 17 00:00:00 2001 From: Sassa NF Date: Wed, 1 Jan 2025 23:03:34 +0000 Subject: [PATCH 4/8] Support RSSI_MAX method --- lib/comms/comms.cpp | 3 +- lib/comms/comms.h | 7 ++++ lib/comms/radio_comms.cpp | 80 ++++++++++++++++++++++++++++++++++++++- lib/config/config.h | 16 ++++++++ lib/scan/scan.h | 16 -------- src/main.cpp | 62 +++++++++++++++++++++++++++--- 6 files changed, 160 insertions(+), 24 deletions(-) diff --git a/lib/comms/comms.cpp b/lib/comms/comms.cpp index b68bcde..48dfae6 100644 --- a/lib/comms/comms.cpp +++ b/lib/comms/comms.cpp @@ -262,6 +262,7 @@ bool ReadlineComms::send(Message &m) Serial.println(name + ": the message is: " + p); break; case MessageType::SCAN_RESULT: + case MessageType::SCAN_MAX_RESULT: p = _scan_result_str(m.payload.dump); break; case MessageType::CONFIG_TASK: @@ -420,7 +421,7 @@ String _wrap_str(String v) Message::~Message() { - if (type == SCAN_RESULT) + if (type == SCAN_RESULT || type == SCAN_MAX_RESULT) { if (payload.dump.sz > 0) { diff --git a/lib/comms/comms.h b/lib/comms/comms.h index ff11325..3573ebd 100644 --- a/lib/comms/comms.h +++ b/lib/comms/comms.h @@ -13,11 +13,18 @@ #define SERIAL0 Serial0 #endif +#ifndef SCAN_MAX_RESULT_KHZ_SCALE +// kHz scale: round frequency, so it fits into 2 bytes +// 2500000 / 40 = 62500, scale 40 fits 2.5GHz into two bytes +#define SCAN_MAX_RESULT_KHZ_SCALE 40 +#endif + enum MessageType { WRAP = 0, SCAN, SCAN_RESULT, + SCAN_MAX_RESULT, CONFIG_TASK, _MAX_MESSAGE_TYPE = CONFIG_TASK }; diff --git a/lib/comms/radio_comms.cpp b/lib/comms/radio_comms.cpp index 0cce77b..49b5296 100644 --- a/lib/comms/radio_comms.cpp +++ b/lib/comms/radio_comms.cpp @@ -62,9 +62,9 @@ uint8_t *_serialize_scan_result(Message &m, size_t &p, uint8_t *msg) // first cut: dump the RSSI as-is // optimize the message size later + p = _write(msg, max_msg, p, (uint8_t *)&dump_sz, 2); p = _write(msg, max_msg, p, (uint8_t *)&m.payload.dump.freqs_khz[0], 4); p = _write(msg, max_msg, p, (uint8_t *)&m.payload.dump.freqs_khz[dump_sz - 1], 4); - p = _write(msg, max_msg, p, (uint8_t *)&dump_sz, 2); size_t rem = max_msg - p; if (rem > dump_sz) @@ -104,6 +104,44 @@ uint8_t *_serialize_scan_result(Message &m, size_t &p, uint8_t *msg) return msg; } +uint8_t *_serialize_scan_max_result(Message &m, size_t &p, uint8_t *msg) +{ + if (m.type != SCAN_MAX_RESULT) + { + return NULL; + } + + size_t dump_sz = m.payload.dump.sz; + size_t max_msg = p; + p = _write(msg, max_msg, 0, (uint8_t)m.type); + p = _write(msg, max_msg, p, (uint8_t *)&dump_sz, 2); + + int16_t b = SCAN_MAX_RESULT_KHZ_SCALE; // scale to fit khz into 2 bytes + p = _write(msg, max_msg, p, (uint8_t)b); + + for (int i = 0; i < dump_sz; i++) + { + b = m.payload.dump.freqs_khz[i] / SCAN_MAX_RESULT_KHZ_SCALE; + p = _write(msg, max_msg, p, (uint8_t *)&b, 2); + b = m.payload.dump.rssis[i]; + if (b >= 0) + { + b = 255; + } + else + { + b += 255; + if (b < 0) + { + b = 0; + } + } + p = _write(msg, max_msg, p, (uint8_t)b); + } + + return msg; +} + uint8_t *_serialize_config_task(Message &m, size_t &p, uint8_t *msg) { if (m.type != CONFIG_TASK) @@ -152,6 +190,10 @@ int16_t RadioComms::send(Message &m) { msg = _serialize_scan_result(m, p, msg_buf); } + else if (m.type == MessageType::SCAN_MAX_RESULT) + { + msg = _serialize_scan_max_result(m, p, msg_buf); + } else if (m.type == MessageType::CONFIG_TASK) { msg = _serialize_config_task(m, p, msg_buf); @@ -195,9 +237,9 @@ Message *_deserialize_scan_result(size_t len, size_t &p, uint8_t *packet) uint32_t s, e; size_t dump_sz = 0; + p = _read(packet, len, p, (uint8_t *)&dump_sz, 2); p = _read(packet, len, p, (uint8_t *)&s, 4); p = _read(packet, len, p, (uint8_t *)&e, 4); - p = _read(packet, len, p, (uint8_t *)&dump_sz, 2); size_t rem = len - p; message->payload.dump.sz = dump_sz; @@ -231,6 +273,36 @@ Message *_deserialize_scan_result(size_t len, size_t &p, uint8_t *packet) return message; } +Message *_deserialize_scan_max_result(size_t len, size_t &p, uint8_t *packet) +{ + Message *message = new Message(); + message->type = SCAN_MAX_RESULT; + + uint32_t b = 0; + size_t dump_sz = 0; + p = _read(packet, len, p, (uint8_t *)&dump_sz, 2); + uint32_t *freqs = new uint32_t[dump_sz]; + int16_t *rssis = new int16_t[dump_sz]; + message->payload.dump.sz = dump_sz; + message->payload.dump.freqs_khz = freqs; + message->payload.dump.rssis = rssis; + + uint32_t scale = 0; + p = _read(packet, len, p, (uint8_t *)&scale); + + for (int i = 0; i < dump_sz; i++) + { + p = _read(packet, len, p, (uint8_t *)&b, 2); + freqs[i] = scale * b; + + b = 0; + p = _read(packet, len, p, (uint8_t *)&b); + rssis[i] = ((int16_t)b) - 255; + } + + return message; +} + Message *_deserialize_config_task(size_t len, size_t &p, uint8_t *packet) { Message *message = new Message(); @@ -346,6 +418,10 @@ Message *RadioComms::receive(uint16_t timeout_ms) { message = _deserialize_scan_result(len, p, packet); } + else if (b == SCAN_MAX_RESULT) + { + message = _deserialize_scan_max_result(len, p, packet); + } else if (b == CONFIG_TASK) { message = _deserialize_config_task(len, p, packet); diff --git a/lib/config/config.h b/lib/config/config.h index 8c9fc59..7eaaca0 100644 --- a/lib/config/config.h +++ b/lib/config/config.h @@ -20,6 +20,22 @@ struct ScanRange uint64_t step_khz; }; +struct ScanPage +{ + uint64_t start_mhz; + uint64_t end_mhz; + size_t page_sz; + ScanRange *scan_ranges; + + ~ScanPage() + { + if (page_sz > 0) + { + delete[] scan_ranges; + } + } +}; + struct LoRaConfig { float freq; diff --git a/lib/scan/scan.h b/lib/scan/scan.h index 6b8c28c..5bf8800 100644 --- a/lib/scan/scan.h +++ b/lib/scan/scan.h @@ -34,22 +34,6 @@ constexpr float LO_RSSI_THRESHOLD = HI_RSSI_THRESHOLD - 66; #define SAMPLES_RSSI 20 #endif -struct ScanPage -{ - uint64_t start_mhz; - uint64_t end_mhz; - size_t page_sz; - ScanRange *scan_ranges; - - ~ScanPage() - { - if (page_sz > 0) - { - delete[] scan_ranges; - } - } -}; - struct Scan { uint64_t epoch; diff --git a/src/main.cpp b/src/main.cpp index f8d3225..d0d6de0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -909,7 +909,7 @@ void configureDetection() CONF_FREQ_BEGIN = config.scan_ranges[0].start_khz / 1000; CONF_FREQ_END = config.scan_ranges[0].end_khz / 1000; - for (int i = 0; i < config.scan_ranges_sz; i++) + for (int i = 1; i < config.scan_ranges_sz; i++) { CONF_FREQ_BEGIN = min(CONF_FREQ_BEGIN, config.scan_ranges[i].start_khz / 1000); CONF_FREQ_END = max(CONF_FREQ_END, config.scan_ranges[i].end_khz / 1000); @@ -1419,6 +1419,7 @@ void routeMessage(RoutedMessage &m) } if (m.message->type == MessageType::SCAN_RESULT || + m.message->type == MessageType::SCAN_MAX_RESULT || m.message->type == MessageType::CONFIG_TASK && (m.message->payload.config.task_type == ConfigTaskType::GETSET_SUCCESS || m.message->payload.config.task_type == ConfigTaskType::SET_FAIL)) @@ -1477,6 +1478,12 @@ void sendMessage(RoutedMessage &m) success = config.updateConfig(*msg->payload.config.key, *msg->payload.config.value); delete msg->payload.config.value; + + if (success && + msg->payload.config.key->equalsIgnoreCase("detection_strategy")) + { + configureDetection(); // redo the pages and scan ranges + } } if (success) @@ -1771,7 +1778,8 @@ void doScan() float (*g)(void *); samples = CONF_SAMPLES; - if (config.detection_strategy.equalsIgnoreCase("RSSI")) + if (config.detection_strategy.equalsIgnoreCase("RSSI") || + config.detection_strategy.equalsIgnoreCase("RSSI_MAX")) g = &getRSSI; else if (config.detection_strategy.equalsIgnoreCase("CAD")) { @@ -2117,8 +2125,52 @@ void reportScan() Message m; m.type = SCAN_RESULT; - m.payload.dump = frequency_scan_result.dump; - loraSendMessage(m); + m.payload.dump.sz = 0; - m.payload.dump.sz = 0; // dump is shared, so should not delete underlying arrays + if (config.detection_strategy.equalsIgnoreCase("RSSI")) + { + size_t sz = frequency_scan_result.dump.sz; + m.payload.dump.sz = sz; + m.payload.dump.freqs_khz = new uint32_t[sz]; + m.payload.dump.rssis = new int16_t[sz]; + + memcpy(m.payload.dump.freqs_khz, frequency_scan_result.dump.freqs_khz, + sizeof(uint32_t) * sz); + memcpy(m.payload.dump.rssis, frequency_scan_result.dump.rssis, + sizeof(int16_t) * sz); + } + else if (config.detection_strategy.equalsIgnoreCase("RSSI_MAX")) + { + m.type = SCAN_MAX_RESULT; + + size_t sz = config.scan_ranges_sz; + m.payload.dump.sz = sz; + m.payload.dump.freqs_khz = new uint32_t[sz]; + m.payload.dump.rssis = new int16_t[sz]; + + for (int i = 0; i < sz; i++) + { + int16_t rssi = -999; + for (int j = 0; j < frequency_scan_result.dump.sz; j++) + { + uint32_t f = frequency_scan_result.dump.freqs_khz[j]; + + if (config.scan_ranges[i].start_khz > f || + config.scan_ranges[i].end_khz < f) + continue; + + rssi = max(rssi, frequency_scan_result.dump.rssis[j]); + } + + m.payload.dump.freqs_khz[i] = + (config.scan_ranges[i].start_khz + config.scan_ranges[i].end_khz) / 2; + m.payload.dump.rssis[i] = rssi; + } + } + else + { + return; + } + + loraSendMessage(m); } From bef27966074190bf0ccd04ffde95caf8ad30bbaa Mon Sep 17 00:00:00 2001 From: Sassa NF Date: Thu, 2 Jan 2025 19:04:14 +0000 Subject: [PATCH 5/8] Fixup config parse --- lib/config/config.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/config/config.cpp b/lib/config/config.cpp index 07ae40a..07a9a57 100644 --- a/lib/config/config.cpp +++ b/lib/config/config.cpp @@ -212,7 +212,7 @@ String detectionStrategyToStr(Config &c) res += ".." + String(c.scan_ranges[i].end_khz); if (c.scan_ranges[i].step_khz < s) { - res += ":" + String(c.scan_ranges[i].step_khz); + res += "+" + String(c.scan_ranges[i].step_khz); } } } From 69e93556af53d6f6dcde769c693a2de8d6437edb Mon Sep 17 00:00:00 2001 From: Sassa NF Date: Thu, 2 Jan 2025 22:18:22 +0000 Subject: [PATCH 6/8] Wait for peer before forwarding command --- src/main.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index d0d6de0..6cae37f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1442,6 +1442,8 @@ int16_t sendMessage(RadioComms &c, Message &m); void loraSendMessage(Message &m); +Result checkRadio(RadioComms &c); + /* * If m.to is LOOP, the message is directed at this module; enact the message. * If m.to is not LOOP, send the message via the respective interface. @@ -1526,13 +1528,17 @@ void sendMessage(RoutedMessage &m) if (m.to == LORA) { + if (config.is_host && TxComms != NULL) + { + checkRadio(*TxComms); // waiting for peer to squak first, so message sending + // will land on the receiving cycle + } + loraSendMessage(*m.message); return; } } -Result checkRadio(RadioComms &c); - RoutedMessage checkComms() { RoutedMessage mess; From a2076c7a36781b1e5fc64b34a34d59a11ac41984 Mon Sep 17 00:00:00 2001 From: Sassa NF Date: Sat, 4 Jan 2025 17:13:43 +0000 Subject: [PATCH 7/8] Multi-destination routing --- lib/comms/comms.h | 20 +++++++---- src/main.cpp | 87 +++++++++++++++++++++++++++++------------------ 2 files changed, 66 insertions(+), 41 deletions(-) diff --git a/lib/comms/comms.h b/lib/comms/comms.h index 3573ebd..b4b50e6 100644 --- a/lib/comms/comms.h +++ b/lib/comms/comms.h @@ -78,14 +78,20 @@ struct Message ~Message(); }; -enum Endpoint +struct Endpoint { - LOOP = 0, // self - UART0, - UART1, - LORA, // rx or tx_lora, depending on is_host - HOST, // USB - MAX_ENDPOINT = HOST + union + { + + struct + { + uint8_t loop : 1, // self + uart0 : 1, uart1 : 1, + lora : 1, // rx or tx_lora, depending on is_host + host : 1; // USB + }; + uint8_t addr; + }; }; struct RoutedMessage diff --git a/src/main.cpp b/src/main.cpp index 6cae37f..49190a5 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1419,12 +1419,18 @@ void routeMessage(RoutedMessage &m) } if (m.message->type == MessageType::SCAN_RESULT || - m.message->type == MessageType::SCAN_MAX_RESULT || - m.message->type == MessageType::CONFIG_TASK && - (m.message->payload.config.task_type == ConfigTaskType::GETSET_SUCCESS || - m.message->payload.config.task_type == ConfigTaskType::SET_FAIL)) + m.message->type == MessageType::SCAN_MAX_RESULT) { - m.to = HOST; + m.to.host = 1; + return; + } + + if (m.message->type == MessageType::CONFIG_TASK && + (m.message->payload.config.task_type == ConfigTaskType::GETSET_SUCCESS || + m.message->payload.config.task_type == ConfigTaskType::SET_FAIL)) + { + m.to.addr = 0; + m.to.host = 1; return; } @@ -1433,7 +1439,7 @@ void routeMessage(RoutedMessage &m) m.message->type == MessageType::CONFIG_TASK && m.message->payload.config.key->equalsIgnoreCase("detection_strategy"))) { - m.to = LORA; + m.to.lora = 1; // apply to self, and send over to lora return; } } @@ -1457,21 +1463,30 @@ void sendMessage(RoutedMessage &m) Message *msg = m.message; - if (m.to == LOOP) + if (m.to.loop) { switch (msg->type) { case MessageType::SCAN: report_scans = msg->payload.scan; - requested_host = m.from == HOST; - return; + requested_host = !!m.from.host; + break; case MessageType::CONFIG_TASK: + { ConfigTaskType ctt = msg->payload.config.task_type; // sanity check; GETSET_SUCCESS and SET_FAIL should get routed to HOST if (ctt == ConfigTaskType::GET || ctt == ConfigTaskType::SET) { // must be GET or SET - both require sending back a response + RoutedMessage resp; + resp.to.addr = m.from.addr; + resp.to.loop = 0; + + resp.from.addr = 0; + resp.from.loop = 1; + resp.message = new Message(); + resp.message->type = msg->type; String old_v = config.getConfig(*msg->payload.config.key); bool success = true; @@ -1479,7 +1494,6 @@ void sendMessage(RoutedMessage &m) { success = config.updateConfig(*msg->payload.config.key, *msg->payload.config.value); - delete msg->payload.config.value; if (success && msg->payload.config.key->equalsIgnoreCase("detection_strategy")) @@ -1488,45 +1502,49 @@ void sendMessage(RoutedMessage &m) } } + resp.message->payload.config.key = new String(*msg->payload.config.key); if (success) { - msg->payload.config.task_type = GETSET_SUCCESS; - msg->payload.config.value = new String(old_v); + resp.message->payload.config.task_type = GETSET_SUCCESS; + resp.message->payload.config.value = new String(old_v); } else { - msg->payload.config.task_type = SET_FAIL; + resp.message->payload.config.task_type = SET_FAIL; } - m.to = m.from; - m.from = LOOP; - sendMessage(m); - } - return; - } + sendMessage(resp); - return; + delete resp.message; + } + } + break; + case SCAN_RESULT: + case SCAN_MAX_RESULT: + if (config.is_host) + { + display_scan_result(m.message->payload.dump); + } + break; + } } - if (m.to == HOST) + if (m.to.host) { HostComms->send(*m.message); - return; } - if (m.to == UART0) + if (m.to.uart0) { Comms0->send(*m.message); - return; } - if (m.to == UART1) + if (m.to.uart1) { Comms1->send(*m.message); - return; } - if (m.to == LORA) + if (m.to.lora) { if (config.is_host && TxComms != NULL) { @@ -1535,15 +1553,15 @@ void sendMessage(RoutedMessage &m) } loraSendMessage(*m.message); - return; } } RoutedMessage checkComms() { RoutedMessage mess; - mess.from = LOOP; - mess.to = LOOP; + mess.from.addr = 0; + mess.to.addr = 0; + mess.to.loop = 1; mess.message = NULL; while (HostComms->available() > 0) @@ -1552,7 +1570,7 @@ RoutedMessage checkComms() if (m == NULL) continue; - mess.from = HOST; + mess.from.host = 1; mess.message = m; return mess; } @@ -1564,7 +1582,7 @@ RoutedMessage checkComms() if (m == NULL) continue; - mess.from = UART0; + mess.from.uart0 = 1; mess.message = m; return mess; } @@ -1576,7 +1594,7 @@ RoutedMessage checkComms() if (m == NULL) continue; - mess.from = UART1; + mess.from.uart1 = 1; mess.message = m; return mess; } @@ -1599,13 +1617,14 @@ RoutedMessage checkComms() } else { - mess.from = LORA; + mess.from.lora = 1; mess.message = res.ok; } return mess; } + mess.from.loop = 1; return mess; } From ed976d1b3870390e17c132fc993bd76c37741ddf Mon Sep 17 00:00:00 2001 From: Sassa NF Date: Sat, 4 Jan 2025 17:14:20 +0000 Subject: [PATCH 8/8] Display scan results received over LoRa --- lib/charts/BarChart.cpp | 10 ++++++++ lib/charts/charts.h | 1 + src/main.cpp | 54 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 65 insertions(+) diff --git a/lib/charts/BarChart.cpp b/lib/charts/BarChart.cpp index 305cdbd..b73f731 100644 --- a/lib/charts/BarChart.cpp +++ b/lib/charts/BarChart.cpp @@ -18,6 +18,16 @@ void BarChart::reset(uint16_t x, uint16_t y, uint16_t w, uint16_t h) Chart::reset(x, y, w, h); } +void BarChart::clear() +{ + for (int i = 0; i < width; i++) + { + ys[i] = min_y; + } + + redraw_all = true; +} + int BarChart::updatePoint(float x, float y) { if (x < min_x || x >= max_x) diff --git a/lib/charts/charts.h b/lib/charts/charts.h index d159cc0..516e5e8 100644 --- a/lib/charts/charts.h +++ b/lib/charts/charts.h @@ -81,6 +81,7 @@ struct BarChart : ProgressChart, Listener }; void reset(uint16_t x, uint16_t y, uint16_t w, uint16_t h) override; + void clear(); int updatePoint(float x, float y) override; void drawOne(int x) override; void draw() override; diff --git a/src/main.cpp b/src/main.cpp index 49190a5..034d3fa 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -924,6 +924,12 @@ void configureDetection() range = RANGE; configurePages(); + + if (bar != NULL) + { + bar->bar.min_x = CONF_FREQ_BEGIN; + bar->bar.max_x = CONF_FREQ_END; + } } void readConfigFile() @@ -1450,6 +1456,8 @@ void loraSendMessage(Message &m); Result checkRadio(RadioComms &c); +void display_scan_result(ScanTaskResult &dump); + /* * If m.to is LOOP, the message is directed at this module; enact the message. * If m.to is not LOOP, send the message via the respective interface. @@ -2199,3 +2207,49 @@ void reportScan() loraSendMessage(m); } + +void display_scan_result(ScanTaskResult &dump) +{ + if (bar == NULL) + return; + // assuming this module and the peer are in sync w.r.t. scan ranges + if (config.detection_strategy.equalsIgnoreCase("RSSI")) + { + for (int i = 0; i < dump.sz; i++) + bar->bar.updatePoint(dump.freqs_khz[i] / 1000, dump.rssis[i]); + + bar->draw(); + display.display(); + + return; + } + + if (config.detection_strategy.equalsIgnoreCase("RSSI_MAX")) + { + float step = (bar->bar.max_x - bar->bar.min_x) / bar->bar.width; + + bar->bar.clear(); + + for (int i = 0; i < config.scan_ranges_sz; i++) + { + int j; + for (j = 0; j < dump.sz; j++) + { + if (config.scan_ranges[i].start_khz <= dump.freqs_khz[j] && + config.scan_ranges[i].end_khz >= dump.freqs_khz[j]) + break; + } + + int16_t rssi = j < dump.sz ? dump.rssis[j] : bar->bar.min_y; + + for (float f = config.scan_ranges[i].start_khz / 1000; + f <= config.scan_ranges[i].end_khz / 1000; f += step) + bar->bar.updatePoint(f, rssi); + } + + bar->draw(); + display.display(); + + return; + } +}