From 6368eca7aa9c4293ee95d5a807620f847e2f8ee2 Mon Sep 17 00:00:00 2001 From: Sassa NF Date: Tue, 31 Dec 2024 11:00:48 +0000 Subject: [PATCH 01/14] 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 02/14] 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 03/14] 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 04/14] 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 05/14] 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 06/14] 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 07/14] 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 08/14] 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; + } +} From 810f9031d5d1f2f118c4fc6e3729adccd7b2b53c Mon Sep 17 00:00:00 2001 From: Sassa NF Date: Sun, 5 Jan 2025 18:47:27 +0000 Subject: [PATCH 09/14] Visualize scan_max_result better - add frequency labels --- lib/charts/BarChart.cpp | 16 +++++ lib/charts/Font.cpp | 147 ++++++++++++++++++++++++++++++++++++++++ lib/charts/charts.h | 10 ++- src/main.cpp | 2 + 4 files changed, 174 insertions(+), 1 deletion(-) create mode 100644 lib/charts/Font.cpp diff --git a/lib/charts/BarChart.cpp b/lib/charts/BarChart.cpp index b73f731..485c0a0 100644 --- a/lib/charts/BarChart.cpp +++ b/lib/charts/BarChart.cpp @@ -200,5 +200,21 @@ void DecoratedBarChart::draw() int tick_pos = bar.x2pos(bar.min_x + step * MINOR_TICKS); display.drawVerticalLine(pos_x + tick_pos, y, MINOR_TICK_LENGTH); } + + if (draw_labels) + { // TODO: adjust chart size; for now we'll just assume that we are not ruining + // display + display.setColor(BLACK); + display.fillRect(pos_x, y + MAJOR_TICK_LENGTH, width, + display.getStringWidth("00000")); + display.setColor(WHITE); + for (float step = 1; bar.min_x + step * MAJOR_TICKS < bar.max_x; step += 1) + { + int tick_pos = bar.x2pos(bar.min_x + step * MAJOR_TICKS); + drawVerticalString(display, ArialMT_Plain_10_Vert, pos_x + tick_pos - 5, + y + MAJOR_TICK_LENGTH, + String((int)(bar.min_x + step * MAJOR_TICKS))); + } + } } } diff --git a/lib/charts/Font.cpp b/lib/charts/Font.cpp new file mode 100644 index 0000000..518c6d5 --- /dev/null +++ b/lib/charts/Font.cpp @@ -0,0 +1,147 @@ +#include "charts.h" + +extern const uint8_t ArialMT_Plain_10[]; // borrowed from OLEDDisplayFonts.h + +uint8_t *ArialMT_Plain_10_Vert = NULL; + +uint64_t _horz_line(const uint8_t *font, size_t chr_off, size_t height, size_t bytes, + size_t ln) +{ + size_t raster_height = (height + 7) / 8; + uint8_t b = 1 << (ln & 7); + + uint64_t ret = 0; + uint64_t mask = 1; + for (size_t y = ln / 8; y < bytes; y += raster_height, mask <<= 1) + { + if (font[chr_off + y] & b) + { + ret |= mask; + } + } + + return ret; +} + +uint64_t _flip(uint64_t v, size_t width) +{ + for (uint64_t b = 1, e = 1 << (width - 1); b < e; b <<= 1, e >>= 1) + { + if (!!(v & b) != !!(v & e)) + { + v = v ^ (b | e); + } + } + + return v; +} + +uint8_t *_rot(const uint8_t *font) +{ + size_t sz = 10000; + size_t p = 0; + uint8_t *vert = (uint8_t *)malloc(sz); + + size_t height = font[1]; + vert[0] = (uint8_t)height; // height -> width + vert[1] = font[0]; // width -> height + vert[2] = font[2]; // first char + size_t chars = font[3]; // char count + vert[3] = (uint8_t)chars; + + size_t map_off = chars * 4 + 4; + + for (int i = 0; i < chars; i++) + { + uint32_t v = ((uint32_t *)font)[i + 1]; + size_t chr_off = ((v & 0xff) << 8) | ((v >> 8) & 0xff); + if (chr_off != 0xffff) + { + size_t bs = (v >> 16) & 0xff; + size_t width = (v >> 24) & 0xff; + size_t raster_height = + (width + 7) / 8; // how many bytes needed to store one vertical line + + size_t bytes = height * raster_height; + v = (v & 0xff000000L) | (bytes << 16) | ((p >> 8) & 0xff) | ((p & 0xff) << 8); + + chr_off += map_off; + + if (map_off + p + bytes > sz) + { + uint8_t *vv = vert; + sz += 1000; + vert = (uint8_t *)malloc(sz); + memcpy(vert, vv, sz - 1000); + free(vv); + } + + for (size_t j = 0; j < height; j++) + { + uint64_t ln = _flip(_horz_line(font, chr_off, height, bs, j), width); + for (size_t k = 0; k < raster_height; k++, p++, ln >>= 8) + { + vert[map_off + p] = (uint8_t)ln; + } + } + } + + ((uint32_t *)vert)[i + 1] = v; + } + + if (map_off + p < sz) + { + uint8_t *vv = vert; + sz = map_off + p; + vert = (uint8_t *)malloc(sz); + memcpy(vert, vv, sz); + free(vv); + } + return vert; +} + +void init_fonts() +{ + if (ArialMT_Plain_10_Vert != NULL) + { + free(ArialMT_Plain_10_Vert); + ArialMT_Plain_10_Vert = NULL; + } + + ArialMT_Plain_10_Vert = _rot(ArialMT_Plain_10); +} + +void drawVerticalString(Display_t &display, const uint8_t *v_font, int x, int y, String s) +{ + if (v_font == NULL) + return; + + size_t w = v_font[0]; + char init_ch = v_font[2]; + size_t chars = v_font[3]; + + const uint8_t *v_chars = v_font + (chars + 1) * 4; + + for (int i = s.length(); i-- > 0;) + { + char c = s.charAt(i); + if (c < init_ch) + continue; + c -= init_ch; + if (c >= chars) + continue; + + size_t map_off = (c + 1) * 4; + size_t chr_off = (((uint16_t)v_font[map_off]) << 8) | (v_font[map_off + 1]); + size_t bs = v_font[map_off + 2]; + size_t h = v_font[map_off + 3]; + if (chr_off == 0xffff) + { + y += h; + continue; + } + + display.drawFastImage(x, y, w, h, v_chars + chr_off); + y += h; + } +} diff --git a/lib/charts/charts.h b/lib/charts/charts.h index 516e5e8..12497e8 100644 --- a/lib/charts/charts.h +++ b/lib/charts/charts.h @@ -101,12 +101,14 @@ struct BarChart : ProgressChart, Listener struct DecoratedBarChart : Chart { BarChart bar; + bool draw_labels; DecoratedBarChart(Display_t &d, uint16_t x, uint16_t y, uint16_t w, uint16_t h, float min_x, float max_x, float min_y, float max_y, float level_y) : Chart(d, x, y, w, h), bar(d, x, y + LABEL_HEIGHT, w, h - LABEL_HEIGHT - AXIS_HEIGHT, min_x, max_x, - min_y, max_y, level_y) {}; + min_y, max_y, level_y), + draw_labels(false) {}; void reset(uint16_t x, uint16_t y, uint16_t w, uint16_t h) override; void draw() override; @@ -176,4 +178,10 @@ struct UptimeClock : Chart virtual void draw() override; }; +extern uint8_t *ArialMT_Plain_10_Vert; + +void init_fonts(); +void drawVerticalString(Display_t &display, const uint8_t *v_font, int x, int y, + String s); + #endif diff --git a/src/main.cpp b/src/main.cpp index 034d3fa..fc42d25 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1177,6 +1177,7 @@ void setup(void) configurePages(); display.clear(); + init_fonts(); Serial.println(); #ifdef METHOD_RSSI @@ -2229,6 +2230,7 @@ void display_scan_result(ScanTaskResult &dump) float step = (bar->bar.max_x - bar->bar.min_x) / bar->bar.width; bar->bar.clear(); + bar->draw_labels = true; for (int i = 0; i < config.scan_ranges_sz; i++) { From e01d5aa694e89a365c26e84f6326252fc9784654 Mon Sep 17 00:00:00 2001 From: Sassa NF Date: Sun, 5 Jan 2025 22:53:31 +0000 Subject: [PATCH 10/14] Sensible defaults for RSSI_MAX range --- src/main.cpp | 63 +++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 57 insertions(+), 6 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index fc42d25..10a3e1e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -892,12 +892,63 @@ void configureDetection() { if (config.scan_ranges_sz == 0) { - config.scan_ranges_sz = 1; - config.scan_ranges = new ScanRange[1]; - config.scan_ranges[0].start_khz = FREQ_BEGIN * 1000; - config.scan_ranges[0].end_khz = FREQ_END * 1000; - config.scan_ranges[0].step_khz = - (float)(FREQ_END - FREQ_BEGIN) * 1000 / (STEPS * SCAN_RBW_FACTOR); + if (config.detection_strategy.equalsIgnoreCase("RSSI_MAX")) + { + size_t sz = 10; + uint32_t f_khz = FREQ_BEGIN * 1000; + uint32_t ssz = (FREQ_END - FREQ_BEGIN) * 1000 / 10; + uint32_t step = + (float)(FREQ_END - FREQ_BEGIN) * 1000 / (STEPS * SCAN_RBW_FACTOR); + + uint32_t rx_b = FREQ_END * 1000 + 100000; + uint32_t rx_e = rx_b + 500; + if (RxComms != NULL) + { + rx_e = RxComms->loraCfg.bw; + rx_b = RxComms->loraCfg.freq * 1000 - rx_e; + rx_e = rx_b + 2 * rx_e; + if (rx_e / ssz == rx_b / ssz && rx_e > f_khz && rx_b < FREQ_END * 1000) + { + // entire exclusion range is in one bucket + sz++; + } + } + + config.scan_ranges_sz = sz; + config.scan_ranges = new ScanRange[sz]; + for (int i = 0; i < sz; i++) + { + config.scan_ranges[i].step_khz = step; + config.scan_ranges[i].start_khz = f_khz > rx_b ? max(f_khz, rx_e) : f_khz; + + bool starts_before = f_khz < rx_e; + bool ends_after = f_khz + ssz > rx_b; + + if (starts_before && ends_after) + { + config.scan_ranges[i].end_khz = rx_b; + i++; + config.scan_ranges[i].start_khz = rx_e; + config.scan_ranges[i].step_khz = step; + } + + f_khz += ssz; + config.scan_ranges[i].end_khz = + f_khz - step < rx_e ? min(f_khz - step, rx_b) : f_khz - step; + } + + if (config.scan_ranges[sz - 1].end_khz > rx_e) + config.scan_ranges[sz - 1].end_khz = FREQ_END * 1000; + } + else + { + config.scan_ranges_sz = 1; + config.scan_ranges = new ScanRange[1]; + config.scan_ranges[0].start_khz = FREQ_BEGIN * 1000; + config.scan_ranges[0].end_khz = FREQ_END * 1000; + config.scan_ranges[0].step_khz = + (float)(FREQ_END - FREQ_BEGIN) * 1000 / (STEPS * SCAN_RBW_FACTOR); + } } if (config.samples <= 0) From 6504bd358b70e734dc7ceef9a863ca0618d2b00c Mon Sep 17 00:00:00 2001 From: Egor Date: Sun, 5 Jan 2025 15:06:21 -0800 Subject: [PATCH 11/14] add output raw --- platformio.ini | 51 +++++++++++++++++++++ src/main.cpp | 120 +++++++++++++++++++++++-------------------------- 2 files changed, 107 insertions(+), 64 deletions(-) diff --git a/platformio.ini b/platformio.ini index 370060d..7429ca6 100644 --- a/platformio.ini +++ b/platformio.ini @@ -35,6 +35,26 @@ build_flags = -DHELTEC -DARDUINO_USB_CDC_ON_BOOT=1 -DARDUINO_USB_MODE=1 + +[env:heltec_wifi_lora_32_V3_lora-TX-scanner] +platform = espressif32 +board = heltec_wifi_lora_32_V3 +framework = arduino +upload_speed = 921600 +monitor_speed = 115200 +board_build.f_cpu = 240000000 +board_build.filesystem = littlefs +lib_deps = + ropg/Heltec_ESP32_LoRa_v3@^0.9.1 + ESP Async WebServer +build_flags = + -DHELTEC_POWER_BUTTON + -DHELTEC + -DARDUINO_USB_CDC_ON_BOOT=1 + -DARDUINO_USB_MODE=1 + -DDEFAULT_RX=916 + -DDEFAULT_TX=915 + -DDEFAULT_LORA_ENABLED=true [env:heltec_wifi_lora_32_V3-OSD] platform = espressif32 @@ -99,6 +119,37 @@ build_flags = -DARDUINO_LILYGO_T3_S3_V1_X -DARDUINO_USB_MODE=1 +[env:lilygo-T3S3-v1-2-sx1262-lora-RX-host-reciver] +platform = espressif32 +board = t3_s3_v1_x +framework = arduino +upload_speed = 921600 +monitor_speed = 115200 +board_build.f_cpu = 240000000 +board_build.filesystem = littlefs +lib_deps = + ropg/Heltec_ESP32_LoRa_v3@^0.9.1 + RadioLib + U8g2 + XPowersLib + ESP Async WebServer +build_flags = + -DLILYGO + -DT3_S3_V1_2_SX1262 + -DT3_V1_3_SX1262 + -DARDUINO_LILYGO_T3S3_SX1262 + -DESP32 + -DUSING_SX1262 + -DARDUINO_ARCH_ESP32 + -DARDUINO_USB_CDC_ON_BOOT=1 + -DARDUINO_LILYGO_T3_S3_V1_X + -DARDUINO_USB_MODE=1 + -DDEFAULT_RX=916 + -DDEFAULT_TX=915 + -DDEFAULT_LORA_ENABLED=true + -DDEFAULT_IS_LORA_HOST=true + -DDISPLAY_RAW_SCAN=1 + [env:lilygo-T3S3-v1-2-lr1121] platform = espressif32 board = t3_s3_v1_x diff --git a/src/main.cpp b/src/main.cpp index ac52a4d..697414c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1460,6 +1460,57 @@ Result checkRadio(RadioComms &c); void display_scan_result(ScanTaskResult &dump); +std::unordered_map findMaxRssi(int16_t *rssis, uint32_t *freqs_khz, + int dump_sz, int level = 80); + +void display_raw_scan(ScanTaskResult &dump) +{ + // display.setDisplayRotation(1); + // display.println("Host Mode ->"); + + size_t dump_sz = dump.sz; + int16_t *rssi = dump.rssis; + uint32_t *fr = dump.freqs_khz; + + std::unordered_map maxMhzRssi = findMaxRssi(rssi, fr, dump_sz, 85); + Serial.println("PRINT SIZE :" + String(maxMhzRssi.size())); + int lx = 0; + int ly = 0; + int i = 0; + for (const auto &pair : maxMhzRssi) + { + if (i == 0 && maxMhzRssi.size() > 0) + { + display.clear(); + } + int16_t rssi = pair.second; + int16_t fr = (int)pair.first; + + Serial.println("PRINT FR:" + String(fr) + ":" + String(rssi) + " lx " + + String(lx)); + // screen overflow protection + if (lx < 130) + { + + display.drawString(lx, ly, String(fr) + ":" + String(rssi)); + Serial.println("PRINT FR:" + String(fr) + ":" + String(rssi)); + // go to next line + ly = ly + 10; + if (ly > 60) + { + ly = 0; + + // go to next column + lx = lx + 45; + } + } + i++; + } + if (maxMhzRssi.size() > 0) + { + display.display(); + } +} /* * 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. @@ -1533,7 +1584,11 @@ void sendMessage(RoutedMessage &m) case SCAN_MAX_RESULT: if (config.is_host) { +#ifdef DISPLAY_RAW_SCAN + display_raw_scan(m.message->payload.dump); +#else display_scan_result(m.message->payload.dump); +#endif } break; } @@ -2111,7 +2166,7 @@ void doScan() std::unordered_map previousPac = {/*{916, true}, {915, true}*/}; std::unordered_map findMaxRssi(int16_t *rssis, uint32_t *freqs_khz, - int dump_sz, int level = 80) + int dump_sz, int level) { std::unordered_map maxRssiPerMHz; // Map to store max RSSI per MHz @@ -2156,34 +2211,6 @@ Result checkRadio(RadioComms &comms) return ret; } -std::unordered_map previousPac = {/*{916, true}, {915, true}*/}; - -std::unordered_map findMaxRssi(int16_t *rssis, uint32_t *freqs_khz, - int dump_sz, int level = 80) -{ - std::unordered_map maxRssiPerMHz; // Map to store max RSSI per MHz - - for (int i = 0; i < dump_sz; i++) - { - int16_t rssi = rssis[i]; - int freq_mhz = (int)freqs_khz[i] / 1000; // Convert kHz to MHz - - // Update the maximum RSSI for this MHz frequency - if (maxRssiPerMHz.find(freq_mhz) == maxRssiPerMHz.end() || - maxRssiPerMHz[freq_mhz] < rssi) - { - if (abs(rssi) < level) - { - maxRssiPerMHz[freq_mhz] = rssi; - } - } - } - - return maxRssiPerMHz; -} - -bool lock = false; - int16_t sendMessage(RadioComms &comms, Message &msg) { radioIsScan = false; @@ -2198,41 +2225,6 @@ int16_t sendMessage(RadioComms &comms, Message &msg) { lock = true; - // display.setDisplayRotation(1); - // display.println("Host Mode ->"); - - size_t dump_sz = msg->payload.dump.sz; - int16_t *rssi = msg->payload.dump.rssis; - uint32_t *fr = msg->payload.dump.freqs_khz; - - std::unordered_map maxMhzRssi = findMaxRssi(rssi, fr, dump_sz, 85); - - int lx, ly, i = 0; - for (const auto &pair : maxMhzRssi) - { - if (i == 0 && maxMhzRssi.size() > 0) - { - display.clear(); - } - // screen overflow protection - if (lx < 130) - { - int16_t rssi = pair.second; - int16_t fr = (int)pair.first; - display.drawString(lx, ly, String(fr) + ":" + String(rssi)); - // go to next line - ly += 10; - if (ly > 60) - { - ly = 0; - - // go to next column - lx += 50; - } - display.display(); - } - i++; - } lock = false; } From ca28d3a3793f1079a97de1ed66720295deedbebf Mon Sep 17 00:00:00 2001 From: Egor Date: Sun, 5 Jan 2025 15:43:25 -0800 Subject: [PATCH 12/14] add 80 --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.cpp b/src/main.cpp index 4c0059b..9a2907c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1524,7 +1524,7 @@ void display_raw_scan(ScanTaskResult &dump) int16_t *rssi = dump.rssis; uint32_t *fr = dump.freqs_khz; - std::unordered_map maxMhzRssi = findMaxRssi(rssi, fr, dump_sz, 85); + std::unordered_map maxMhzRssi = findMaxRssi(rssi, fr, dump_sz, 80); Serial.println("PRINT SIZE :" + String(maxMhzRssi.size())); int lx = 0; int ly = 0; From 670ba4e74f44400675aa2aa461f4bb5522f12b7e Mon Sep 17 00:00:00 2001 From: Egor Date: Sun, 5 Jan 2025 15:49:26 -0800 Subject: [PATCH 13/14] max level --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.cpp b/src/main.cpp index 9a2907c..db90953 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2231,7 +2231,7 @@ std::unordered_map findMaxRssi(int16_t *rssis, uint32_t *freqs_khz if (maxRssiPerMHz.find(freq_mhz) == maxRssiPerMHz.end() || maxRssiPerMHz[freq_mhz] < rssi) { - if (abs(rssi) < level) + if (abs(rssi) <= level) { maxRssiPerMHz[freq_mhz] = rssi; } From 87ea49c1d3e5989d7c0639272e1763559f87242e Mon Sep 17 00:00:00 2001 From: Egor Date: Sun, 5 Jan 2025 15:52:20 -0800 Subject: [PATCH 14/14] max rssi --- src/main.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main.cpp b/src/main.cpp index db90953..f63ca41 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1524,7 +1524,8 @@ void display_raw_scan(ScanTaskResult &dump) int16_t *rssi = dump.rssis; uint32_t *fr = dump.freqs_khz; - std::unordered_map maxMhzRssi = findMaxRssi(rssi, fr, dump_sz, 80); + std::unordered_map maxMhzRssi = + findMaxRssi(rssi, fr, dump_sz, abs(TRIGGER_LEVEL)); Serial.println("PRINT SIZE :" + String(maxMhzRssi.size())); int lx = 0; int ly = 0;