diff --git a/.gitignore b/.gitignore index 9b9580a3..7e7cc694 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ .vscode/launch.json .vscode/ipch out/ +.direnv/ diff --git a/examples/companion_radio/UITask.cpp b/examples/companion_radio/UITask.cpp index 7448f303..479c684f 100644 --- a/examples/companion_radio/UITask.cpp +++ b/examples/companion_radio/UITask.cpp @@ -4,6 +4,12 @@ #define AUTO_OFF_MILLIS 15000 // 15 seconds +#ifdef PIN_STATUS_LED +#define LED_ON_MILLIS 20 +#define LED_ON_MSG_MILLIS 200 +#define LED_CYCLE_MILLIS 4000 +#endif + #ifndef USER_BTN_PRESSED #define USER_BTN_PRESSED LOW #endif @@ -43,10 +49,6 @@ void UITask::begin(DisplayDriver* display, const char* node_name, const char* bu *dash = 0; } - #ifdef PIN_USER_BTN - pinMode(PIN_USER_BTN, INPUT); - #endif - // v1.2.3 (1 Jan 2025) sprintf(_version_info, "%s (%s)", version, build_date); } @@ -135,22 +137,21 @@ void UITask::userLedHandler() { #ifdef PIN_STATUS_LED static int state = 0; static int next_change = 0; + static int last_increment = 0; + int cur_time = millis(); if (cur_time > next_change) { if (state == 0) { - state = 1; // led on, short = unread msg + state = 1; if (_msgcount > 0) { - next_change = cur_time + 500; + last_increment = LED_ON_MSG_MILLIS; } else { - next_change = cur_time + 2000; + last_increment = LED_ON_MILLIS; } + next_change = cur_time + last_increment; } else { state = 0; - if (_board->getBattMilliVolts() > 3800) { - next_change = cur_time + 2000; - } else { - next_change = cur_time + 4000; // 4s blank if bat level low - } + next_change = cur_time + LED_CYCLE_MILLIS - last_increment; } digitalWrite(PIN_STATUS_LED, state); } diff --git a/examples/simple_repeater/main.cpp b/examples/simple_repeater/main.cpp index 1e012b58..201d4fc9 100644 --- a/examples/simple_repeater/main.cpp +++ b/examples/simple_repeater/main.cpp @@ -105,6 +105,13 @@ struct ClientInfo { #define MAX_CLIENTS 4 +struct NeighbourInfo { + mesh::Identity id; + uint32_t advert_timestamp; + uint32_t heard_timestamp; + int8_t snr; // multiplied by 4, user should divide to get float value +}; + // NOTE: need to space the ACK and the reply text apart (in CLI) #define CLI_REPLY_DELAY_MILLIS 1500 @@ -116,6 +123,9 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks { CommonCLI _cli; uint8_t reply_data[MAX_PACKET_PAYLOAD]; ClientInfo known_clients[MAX_CLIENTS]; +#if MAX_NEIGHBOURS + NeighbourInfo neighbours[MAX_NEIGHBOURS]; +#endif ClientInfo* putClient(const mesh::Identity& id) { uint32_t min_time = 0xFFFFFFFF; @@ -135,6 +145,33 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks { return oldest; } + void putNeighbour(const mesh::Identity& id, uint32_t timestamp, float snr) { + #if MAX_NEIGHBOURS // check if neighbours enabled + // find existing neighbour, else use least recently updated + uint32_t oldest_timestamp = 0xFFFFFFFF; + NeighbourInfo* neighbour = &neighbours[0]; + for (int i = 0; i < MAX_NEIGHBOURS; i++) { + // if neighbour already known, we should update it + if (id.matches(neighbours[i].id)) { + neighbour = &neighbours[i]; + break; + } + + // otherwise we should update the least recently updated neighbour + if (neighbours[i].heard_timestamp < oldest_timestamp) { + neighbour = &neighbours[i]; + oldest_timestamp = neighbour->heard_timestamp; + } + } + + // update neighbour info + neighbour->id = id; + neighbour->advert_timestamp = timestamp; + neighbour->heard_timestamp = getRTCClock()->getCurrentTime(); + neighbour->snr = (int8_t) (snr * 4); + #endif + } + int handleRequest(ClientInfo* sender, uint8_t* payload, size_t payload_len) { uint32_t now = getRTCClock()->getCurrentTimeUnique(); memcpy(reply_data, &now, 4); // response packets always prefixed with timestamp @@ -361,6 +398,18 @@ protected: } } + void onAdvertRecv(mesh::Packet* packet, const mesh::Identity& id, uint32_t timestamp, const uint8_t* app_data, size_t app_data_len) { + mesh::Mesh::onAdvertRecv(packet, id, timestamp, app_data, app_data_len); // chain to super impl + + // if this a zero hop advert, add it to neighbours + if (packet->path_len == 0) { + AdvertDataParser parser(app_data, app_data_len); + if (parser.isValid() && parser.getType() == ADV_TYPE_REPEATER) { // just keep neigbouring Repeaters + putNeighbour(id, timestamp, packet->getSNR()); + } + } + } + void onPeerDataRecv(mesh::Packet* packet, uint8_t type, int sender_idx, const uint8_t* secret, uint8_t* data, size_t len) override { int i = matching_peer_indexes[sender_idx]; if (i < 0 || i >= MAX_CLIENTS) { // get from our known_clients table (sender SHOULD already be known in this context) @@ -427,12 +476,35 @@ protected: } uint8_t temp[166]; + const char *command = (const char *) &data[5]; + char *reply = (char *) &temp[5]; if (is_retry) { - temp[0] = 0; + *reply = 0; + #if MAX_NEIGHBOURS + } else if (memcmp(command, "neighbors", 9) == 0) { + char *dp = reply; + + for (int i = 0; i < MAX_NEIGHBOURS && dp - reply < 136; i++) { + NeighbourInfo* neighbour = &neighbours[i]; + if (neighbour->heard_timestamp == 0) continue; // skip empty slots + + // add new line if not first item + if (i > 0) *dp++ = '\n'; + + char hex[10]; + // get 4 bytes of neighbour id as hex + mesh::Utils::toHex(hex, neighbour->id.pub_key, 4); + + // add next neighbour + sprintf(dp, "%s:%d:%d", hex, neighbour->advert_timestamp, neighbour->snr); + while (*dp) dp++; // find end of string + } + *dp = 0; // null terminator + #endif } else { - _cli.handleCommand(sender_timestamp, (const char *) &data[5], (char *) &temp[5]); + _cli.handleCommand(sender_timestamp, command, reply); } - int text_len = strlen((char *) &temp[5]); + int text_len = strlen(reply); if (text_len > 0) { uint32_t timestamp = getRTCClock()->getCurrentTimeUnique(); if (timestamp == sender_timestamp) { @@ -482,6 +554,10 @@ public: next_local_advert = next_flood_advert = 0; _logging = false; + #if MAX_NEIGHBOURS + memset(neighbours, 0, sizeof(neighbours)); + #endif + // defaults memset(&_prefs, 0, sizeof(_prefs)); _prefs.airtime_factor = 1.0; // one half diff --git a/src/Packet.cpp b/src/Packet.cpp index cb31638c..2d54ca45 100644 --- a/src/Packet.cpp +++ b/src/Packet.cpp @@ -52,6 +52,7 @@ bool Packet::readFrom(const uint8_t src[], uint8_t len) { memcpy(path, &src[i], path_len); i += path_len; if (i >= len) return false; // bad encoding payload_len = len - i; + if (payload_len > sizeof(payload)) return false; // bad encoding memcpy(payload, &src[i], payload_len); //i += payload_len; return true; // success } diff --git a/variants/xiao_c3/platformio.ini b/variants/xiao_c3/platformio.ini index e790e74e..7e89b377 100644 --- a/variants/xiao_c3/platformio.ini +++ b/variants/xiao_c3/platformio.ini @@ -33,6 +33,7 @@ build_flags = -D ADVERT_LAT=0.0 -D ADVERT_LON=0.0 -D ADMIN_PASSWORD='"password"' + -D MAX_NEIGHBOURS=8 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 lib_deps = diff --git a/variants/xiao_s3_wio/platformio.ini b/variants/xiao_s3_wio/platformio.ini index ca043f1f..5073efd8 100644 --- a/variants/xiao_s3_wio/platformio.ini +++ b/variants/xiao_s3_wio/platformio.ini @@ -79,17 +79,20 @@ build_flags = -D MAX_CONTACTS=100 -D MAX_GROUP_CHANNELS=8 -D BLE_PIN_CODE=123456 + -D DISPLAY_CLASS=SSD1306Display ; -D BLE_DEBUG_LOGGING=1 ; -D ENABLE_PRIVATE_KEY_IMPORT=1 ; -D ENABLE_PRIVATE_KEY_EXPORT=1 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 build_src_filter = ${Xiao_S3_WIO.build_src_filter} + + + - +<../examples/companion_radio/main.cpp> + +<../examples/companion_radio> lib_deps = ${Xiao_S3_WIO.lib_deps} densaugeo/base64 @ ~1.4.0 + adafruit/Adafruit SSD1306 @ ^2.5.13 [env:Xiao_S3_WIO_companion_radio_serial] extends = Xiao_S3_WIO @@ -108,24 +111,4 @@ lib_deps = ${Xiao_S3_WIO.lib_deps} densaugeo/base64 @ ~1.4.0 -[env:Xiao_S3_WIO_expansion_companion_radio_ble] -extends = Xiao_S3_WIO -build_flags = - ${Xiao_S3_WIO.build_flags} - -D MAX_CONTACTS=100 - -D MAX_GROUP_CHANNELS=8 - -D BLE_PIN_CODE=123456 - -D DISPLAY_CLASS=SSD1306Display -; -D BLE_DEBUG_LOGGING=1 -; -D ENABLE_PRIVATE_KEY_IMPORT=1 -; -D ENABLE_PRIVATE_KEY_EXPORT=1 -; -D MESH_PACKET_LOGGING=1 -; -D MESH_DEBUG=1 -build_src_filter = ${Xiao_S3_WIO.build_src_filter} - + - + - +<../examples/companion_radio> -lib_deps = - ${Xiao_S3_WIO.lib_deps} - densaugeo/base64 @ ~1.4.0 - adafruit/Adafruit SSD1306 @ ^2.5.13 +