From a3f8c21ff49c7a4cb60b84c0ed17484507bda93b Mon Sep 17 00:00:00 2001 From: Florent Date: Mon, 2 Jun 2025 19:33:48 +0200 Subject: [PATCH 1/9] t1000e: light and temp sensor support --- variants/t1000-e/t1000e_sensors.cpp | 119 ++++++++++++++++++++++++++++ variants/t1000-e/t1000e_sensors.h | 8 ++ variants/t1000-e/target.cpp | 5 ++ 3 files changed, 132 insertions(+) create mode 100644 variants/t1000-e/t1000e_sensors.cpp create mode 100644 variants/t1000-e/t1000e_sensors.h diff --git a/variants/t1000-e/t1000e_sensors.cpp b/variants/t1000-e/t1000e_sensors.cpp new file mode 100644 index 00000000..17693022 --- /dev/null +++ b/variants/t1000-e/t1000e_sensors.cpp @@ -0,0 +1,119 @@ +#include +#include "t1000e_sensors.h" + +#define HEATER_NTC_BX 4250 // thermistor coefficient B +#define HEATER_NTC_RP 8250 // ohm, series resistance to thermistor +#define HEATER_NTC_KA 273.15 // 25 Celsius at Kelvin +#define NTC_REF_VCC 3000 // mV, output voltage of LDO +#define LIGHT_REF_VCC 2400 // + +static unsigned int ntc_res2[136]={ + 113347,107565,102116,96978,92132,87559,83242,79166,75316,71677, + 68237,64991,61919,59011,56258,53650,51178,48835,46613,44506, + 42506,40600,38791,37073,35442,33892,32420,31020,29689,28423, + 27219,26076,24988,23951,22963,22021,21123,20267,19450,18670, + 17926,17214,16534,15886,15266,14674,14108,13566,13049,12554, + 12081,11628,11195,10780,10382,10000,9634,9284,8947,8624, + 8315,8018,7734,7461,7199,6948,6707,6475,6253,6039, + 5834,5636,5445,5262,5086,4917,4754,4597,4446,4301, + 4161,4026,3896,3771,3651,3535,3423,3315,3211,3111, + 3014,2922,2834,2748,2666,2586,2509,2435,2364,2294, + 2228,2163,2100,2040,1981,1925,1870,1817,1766,1716, + 1669,1622,1578,1535,1493,1452,1413,1375,1338,1303, + 1268,1234,1202,1170,1139,1110,1081,1053,1026,999, + 974,949,925,902,880,858, +}; + +static char ntc_temp2[136]= +{ + -30,-29,-28,-27,-26,-25,-24,-23,-22,-21, + -20,-19,-18,-17,-16,-15,-14,-13,-12,-11, + -10,-9,-8,-7,-6,-5,-4,-3,-2,-1, + 0,1,2,3,4,5,6,7,8,9, + 10,11,12,13,14,15,16,17,18,19, + 20,21,22,23,24,25,26,27,28,29, + 30,31,32,33,34,35,36,37,38,39, + 40,41,42,43,44,45,46,47,48,49, + 50,51,52,53,54,55,56,57,58,59, + 60,61,62,63,64,65,66,67,68,69, + 70,71,72,73,74,75,76,77,78,79, + 80,81,82,83,84,85,86,87,88,89, + 90,91,92,93,94,95,96,97,98,99, + 100,101,102,103,104,105, +}; + +static float get_heater_temperature( unsigned int vcc_volt, unsigned int ntc_volt ) +{ + int i = 0; + float Vout = 0, Rt = 0, temp = 0; + Vout = ntc_volt; + + Rt = ( HEATER_NTC_RP * vcc_volt ) / Vout - HEATER_NTC_RP; + + for( i = 0; i < 136; i++ ) + { + if( Rt >= ntc_res2[i] ) + { + break; + } + } + + temp = ntc_temp2[i - 1] + 1 * ( ntc_res2[i - 1] - Rt ) / ( float )( ntc_res2[i - 1] - ntc_res2[i] ); + + temp = ( temp * 100 + 5 ) / 100; + return temp; +} + +static int get_light_lv( unsigned int light_volt ) +{ + float Vout = 0, Vin = 0, Rt = 0, temp = 0; + unsigned int light_level = 0; + + if( light_volt <= 80 ) + { + light_level = 0; + return light_level; + } + else if( light_volt >= 2480 ) + { + light_level = 100; + return light_level; + } + Vout = light_volt; + light_level = 100 * ( Vout - 80 ) / LIGHT_REF_VCC; + + return light_level; +} + +float t1000e_get_temperature( void ) +{ + unsigned int ntc_v, vcc_v; + + digitalWrite(PIN_3V3_EN, HIGH); + digitalWrite(SENSOR_EN, HIGH); + analogReference(AR_INTERNAL_3_0); + analogReadResolution(12); + delay(10); + vcc_v = (1000.0*(analogRead(BATTERY_PIN) * ADC_MULTIPLIER * AREF_VOLTAGE)) / 4096; + ntc_v = (1000.0 * AREF_VOLTAGE * analogRead(TEMP_SENSOR)) / 4096; + digitalWrite(PIN_3V3_EN, LOW); + digitalWrite(SENSOR_EN, LOW); + + return get_heater_temperature (vcc_v, ntc_v); +} + +uint32_t t1000e_get_light( void ) +{ + int lux = 0; + unsigned int lux_v = 0; + + digitalWrite(SENSOR_EN, HIGH); + analogReference(AR_INTERNAL_3_0); + analogReadResolution(12); + delay(10); + lux_v = 1000 * analogRead(LUX_SENSOR) * AREF_VOLTAGE / 4096; + lux = get_light_lv( lux_v ); + digitalWrite(SENSOR_EN, LOW); + + return lux; +} \ No newline at end of file diff --git a/variants/t1000-e/t1000e_sensors.h b/variants/t1000-e/t1000e_sensors.h new file mode 100644 index 00000000..8f3967ca --- /dev/null +++ b/variants/t1000-e/t1000e_sensors.h @@ -0,0 +1,8 @@ +#pragma once + +// Light and temperature sensors are on ADC ports +// functions adapted from Seeed examples to get values +// see : https://github.com/Seeed-Studio/Seeed-Tracker-T1000-E-for-LoRaWAN-dev-board + +extern uint32_t t1000e_get_light(); +extern float t1000e_get_temperature(); \ No newline at end of file diff --git a/variants/t1000-e/target.cpp b/variants/t1000-e/target.cpp index 29ca1acd..caa53930 100644 --- a/variants/t1000-e/target.cpp +++ b/variants/t1000-e/target.cpp @@ -1,4 +1,5 @@ #include +#include "t1000e_sensors.h" #include "target.h" #include @@ -160,6 +161,10 @@ bool T1000SensorManager::querySensors(uint8_t requester_permissions, CayenneLPP& if (requester_permissions & TELEM_PERM_LOCATION) { // does requester have permission? telemetry.addGPS(TELEM_CHANNEL_SELF, node_lat, node_lon, node_altitude); } + if (requester_permissions & TELEM_PERM_ENVIRONMENT) { + telemetry.addLuminosity(TELEM_CHANNEL_SELF, t1000e_get_light()); + telemetry.addTemperature(TELEM_CHANNEL_SELF, t1000e_get_temperature()); + } return true; } From 203a7f2bd363e74281bf6cb3b9b51057ef91f266 Mon Sep 17 00:00:00 2001 From: JQ Date: Mon, 2 Jun 2025 14:10:10 -0700 Subject: [PATCH 2/9] add display to station G2 firmwares --- variants/station_g2/platformio.ini | 11 ++++++++--- variants/station_g2/target.cpp | 4 ++++ variants/station_g2/target.h | 8 ++++++++ 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/variants/station_g2/platformio.ini b/variants/station_g2/platformio.ini index d3f45eb0..03914dce 100644 --- a/variants/station_g2/platformio.ini +++ b/variants/station_g2/platformio.ini @@ -9,18 +9,23 @@ build_flags = -D WRAPPER_CLASS=CustomSX1262Wrapper -D LORA_TX_POWER=7 ; -D P_LORA_TX_LED=35 -; -D PIN_BOARD_SDA=5 -; -D PIN_BOARD_SCL=6 - -D PIN_USER_BTN=0 + -D PIN_BOARD_SDA=5 + -D PIN_BOARD_SCL=6 + -D PIN_USER_BTN=38 -D SX126X_DIO2_AS_RF_SWITCH=true -D SX126X_DIO3_TCXO_VOLTAGE=1.8 -D SX126X_CURRENT_LIMIT=140 ; -D SX126X_RX_BOOSTED_GAIN=1 - DO NOT ENABLE THIS! + -I src/helpers/ui + -D DISPLAY_CLASS=SH1106Display ; https://wiki.uniteng.com/en/meshtastic/station-g2#impact-of-lora-node-dense-areashigh-noise-environments-on-rf-performance build_src_filter = ${esp32_base.build_src_filter} +<../variants/station_g2> + + lib_deps = ${esp32_base.lib_deps} + adafruit/Adafruit SH110X @ ~2.1.13 + adafruit/Adafruit GFX Library @ ^1.12.1 [env:Station_G2_repeater] extends = Station_G2 diff --git a/variants/station_g2/target.cpp b/variants/station_g2/target.cpp index e279b139..dca94f21 100644 --- a/variants/station_g2/target.cpp +++ b/variants/station_g2/target.cpp @@ -16,6 +16,10 @@ ESP32RTCClock fallback_clock; AutoDiscoverRTCClock rtc_clock(fallback_clock); SensorManager sensors; +#ifdef DISPLAY_CLASS + DISPLAY_CLASS display; +#endif + #ifndef LORA_CR #define LORA_CR 5 #endif diff --git a/variants/station_g2/target.h b/variants/station_g2/target.h index e44c2ebe..695e700a 100644 --- a/variants/station_g2/target.h +++ b/variants/station_g2/target.h @@ -8,11 +8,19 @@ #include #include +#ifdef DISPLAY_CLASS + #include +#endif + extern StationG2Board board; extern WRAPPER_CLASS radio_driver; extern AutoDiscoverRTCClock rtc_clock; extern SensorManager sensors; +#ifdef DISPLAY_CLASS + extern DISPLAY_CLASS display; +#endif + bool radio_init(); uint32_t radio_get_rng_seed(); void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr); From 8f5e521717c5956aeb910805a60eec50009225c1 Mon Sep 17 00:00:00 2001 From: Florent Date: Mon, 2 Jun 2025 22:09:37 +0200 Subject: [PATCH 3/9] sx1262_wio: hook rxen, specify txen as not connected --- variants/xiao_nrf52/platformio.ini | 2 ++ variants/xiao_nrf52/target.cpp | 4 ++++ variants/xiao_s3_wio/platformio.ini | 2 ++ variants/xiao_s3_wio/target.cpp | 6 +++++- 4 files changed, 13 insertions(+), 1 deletion(-) diff --git a/variants/xiao_nrf52/platformio.ini b/variants/xiao_nrf52/platformio.ini index 45d28740..50baccad 100644 --- a/variants/xiao_nrf52/platformio.ini +++ b/variants/xiao_nrf52/platformio.ini @@ -36,6 +36,8 @@ build_flags = ${nrf52840_xiao.build_flags} -D P_LORA_RESET=D2 -D P_LORA_BUSY=D3 -D P_LORA_NSS=D4 + -D SX126X_RXEN=D5 + -D SX126X_TXEN=RADIOLIB_NC -D SX126X_DIO2_AS_RF_SWITCH=1 -D SX126X_DIO3_TCXO_VOLTAGE=1.8 -D SX126X_CURRENT_LIMIT=140 diff --git a/variants/xiao_nrf52/target.cpp b/variants/xiao_nrf52/target.cpp index c78ea159..724e7c63 100644 --- a/variants/xiao_nrf52/target.cpp +++ b/variants/xiao_nrf52/target.cpp @@ -35,6 +35,10 @@ bool radio_init() { radio.setCRC(1); +#if defined(SX126X_RXEN) && defined(SX126X_TXEN) + radio.setRfSwitchPins(SX126X_RXEN, SX126X_TXEN); +#endif + #ifdef SX126X_CURRENT_LIMIT radio.setCurrentLimit(SX126X_CURRENT_LIMIT); #endif diff --git a/variants/xiao_s3_wio/platformio.ini b/variants/xiao_s3_wio/platformio.ini index cfc8e774..bb5ae698 100644 --- a/variants/xiao_s3_wio/platformio.ini +++ b/variants/xiao_s3_wio/platformio.ini @@ -15,6 +15,8 @@ build_flags = ${esp32_base.build_flags} -D P_LORA_MOSI=9 -D PIN_USER_BTN=21 -D PIN_STATUS_LED=48 + -D SX126X_RXEN=38 + -D SX126X_TXEN=RADIOLIB_NC -D SX126X_DIO2_AS_RF_SWITCH=true -D SX126X_DIO3_TCXO_VOLTAGE=1.8 -D SX126X_CURRENT_LIMIT=140 diff --git a/variants/xiao_s3_wio/target.cpp b/variants/xiao_s3_wio/target.cpp index 09ee5daa..2f443205 100644 --- a/variants/xiao_s3_wio/target.cpp +++ b/variants/xiao_s3_wio/target.cpp @@ -47,7 +47,11 @@ bool radio_init() { } radio.setCRC(1); - + +#if defined(SX126X_RXEN) && defined(SX126X_TXEN) + radio.setRfSwitchPins(SX126X_RXEN, SX126X_TXEN); +#endif + #ifdef SX126X_CURRENT_LIMIT radio.setCurrentLimit(SX126X_CURRENT_LIMIT); #endif From bdc369be67e62ad04480138e9109385520da0bf8 Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Tue, 3 Jun 2025 19:01:03 +1000 Subject: [PATCH 4/9] * repeater & room server: new SERVER_RESPONSE_DELAY and TXT_ACK_DELAY defines. --- examples/simple_repeater/main.cpp | 21 ++++++++++++++------- examples/simple_room_server/main.cpp | 26 +++++++++++++++++--------- 2 files changed, 31 insertions(+), 16 deletions(-) diff --git a/examples/simple_repeater/main.cpp b/examples/simple_repeater/main.cpp index 5db62ff1..d8ae9556 100644 --- a/examples/simple_repeater/main.cpp +++ b/examples/simple_repeater/main.cpp @@ -59,6 +59,14 @@ #define ADMIN_PASSWORD "password" #endif +#ifndef SERVER_RESPONSE_DELAY + #define SERVER_RESPONSE_DELAY 300 +#endif + +#ifndef TXT_ACK_DELAY + #define TXT_ACK_DELAY 200 +#endif + #ifdef DISPLAY_CLASS #include "UITask.h" static UITask ui_task(display); @@ -112,8 +120,7 @@ struct NeighbourInfo { 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 +#define CLI_REPLY_DELAY_MILLIS 1000 class MyMesh : public mesh::Mesh, public CommonCLICallbacks { FILESYSTEM* _fs; @@ -446,14 +453,14 @@ protected: // let this sender know path TO here, so they can use sendDirect(), and ALSO encode the response mesh::Packet* path = createPathReturn(client->id, secret, packet->path, packet->path_len, PAYLOAD_TYPE_RESPONSE, reply_data, reply_len); - if (path) sendFlood(path); + if (path) sendFlood(path, SERVER_RESPONSE_DELAY); } else { mesh::Packet* reply = createDatagram(PAYLOAD_TYPE_RESPONSE, client->id, secret, reply_data, reply_len); if (reply) { if (client->out_path_len >= 0) { // we have an out_path, so send DIRECT - sendDirect(reply, client->out_path, client->out_path_len); + sendDirect(reply, client->out_path, client->out_path_len, SERVER_RESPONSE_DELAY); } else { - sendFlood(reply); + sendFlood(reply, SERVER_RESPONSE_DELAY); } } } @@ -482,9 +489,9 @@ protected: mesh::Packet* ack = createAck(ack_hash); if (ack) { if (client->out_path_len < 0) { - sendFlood(ack); + sendFlood(ack, TXT_ACK_DELAY); } else { - sendDirect(ack, client->out_path, client->out_path_len); + sendDirect(ack, client->out_path, client->out_path_len, TXT_ACK_DELAY); } } } diff --git a/examples/simple_room_server/main.cpp b/examples/simple_room_server/main.cpp index 219c73ab..9f2149fe 100644 --- a/examples/simple_room_server/main.cpp +++ b/examples/simple_room_server/main.cpp @@ -67,6 +67,14 @@ #define MAX_UNSYNCED_POSTS 32 #endif +#ifndef SERVER_RESPONSE_DELAY + #define SERVER_RESPONSE_DELAY 300 +#endif + +#ifndef TXT_ACK_DELAY + #define TXT_ACK_DELAY 200 +#endif + #ifdef DISPLAY_CLASS #include "UITask.h" static UITask ui_task(display); @@ -568,12 +576,12 @@ protected: mesh::Packet* ack = createAck(ack_hash); if (ack) { if (client->out_path_len < 0) { - sendFlood(ack); + sendFlood(ack, TXT_ACK_DELAY); } else { - sendDirect(ack, client->out_path, client->out_path_len); + sendDirect(ack, client->out_path, client->out_path_len, TXT_ACK_DELAY); } } - delay_millis = REPLY_DELAY_MILLIS; + delay_millis = TXT_ACK_DELAY + REPLY_DELAY_MILLIS; } else { delay_millis = 0; } @@ -592,9 +600,9 @@ protected: auto reply = createDatagram(PAYLOAD_TYPE_TXT_MSG, client->id, secret, temp, 5 + text_len); if (reply) { if (client->out_path_len < 0) { - sendFlood(reply, delay_millis); + sendFlood(reply, delay_millis + SERVER_RESPONSE_DELAY); } else { - sendDirect(reply, client->out_path, client->out_path_len, delay_millis); + sendDirect(reply, client->out_path, client->out_path_len, delay_millis + SERVER_RESPONSE_DELAY); } } } @@ -637,7 +645,7 @@ protected: auto reply = createAck(ack_hash); if (reply) { reply->payload[reply->payload_len++] = getUnsyncedCount(client); // NEW: add unsynced counter to end of ACK packet - sendDirect(reply, client->out_path, client->out_path_len); + sendDirect(reply, client->out_path, client->out_path_len, SERVER_RESPONSE_DELAY); } } } else { @@ -647,14 +655,14 @@ protected: // let this sender know path TO here, so they can use sendDirect(), and ALSO encode the response mesh::Packet* path = createPathReturn(client->id, secret, packet->path, packet->path_len, PAYLOAD_TYPE_RESPONSE, reply_data, reply_len); - if (path) sendFlood(path); + if (path) sendFlood(path, SERVER_RESPONSE_DELAY); } else { mesh::Packet* reply = createDatagram(PAYLOAD_TYPE_RESPONSE, client->id, secret, reply_data, reply_len); if (reply) { if (client->out_path_len >= 0) { // we have an out_path, so send DIRECT - sendDirect(reply, client->out_path, client->out_path_len); + sendDirect(reply, client->out_path, client->out_path_len, SERVER_RESPONSE_DELAY); } else { - sendFlood(reply); + sendFlood(reply, SERVER_RESPONSE_DELAY); } } } From 6e2a0f3a9cf4a59201e9220dc824bc83dd5b5540 Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Tue, 3 Jun 2025 19:01:45 +1000 Subject: [PATCH 5/9] * ESP32-C3 targets, now 80Mhz cpu --- variants/xiao_c3/platformio.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/variants/xiao_c3/platformio.ini b/variants/xiao_c3/platformio.ini index 6024905f..55baf2b8 100644 --- a/variants/xiao_c3/platformio.ini +++ b/variants/xiao_c3/platformio.ini @@ -4,6 +4,7 @@ board = seeed_xiao_esp32c3 build_flags = ${esp32_base.build_flags} -I variants/xiao_c3 + -D ESP32_CPU_FREQ=80 -D LORA_TX_BOOST_PIN=D3 -D P_LORA_TX_LED=D5 -D PIN_VBAT_READ=D0 From 75503ed52abccb79ee2c377888ec9fc6e3806189 Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Tue, 3 Jun 2025 20:27:09 +1000 Subject: [PATCH 6/9] * Companion now can be configured with TXT_ACK_DELAY (default is 200ms) --- src/helpers/BaseChatMesh.cpp | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/helpers/BaseChatMesh.cpp b/src/helpers/BaseChatMesh.cpp index 36ddcbb4..bcf83f8b 100644 --- a/src/helpers/BaseChatMesh.cpp +++ b/src/helpers/BaseChatMesh.cpp @@ -1,6 +1,10 @@ #include #include +#ifndef TXT_ACK_DELAY + #define TXT_ACK_DELAY 200 +#endif + mesh::Packet* BaseChatMesh::createSelfAdvert(const char* name, double lat, double lon) { uint8_t app_data[MAX_ADVERT_DATA_SIZE]; uint8_t app_data_len; @@ -131,14 +135,14 @@ void BaseChatMesh::onPeerDataRecv(mesh::Packet* packet, uint8_t type, int sender // let this sender know path TO here, so they can use sendDirect(), and ALSO encode the ACK mesh::Packet* path = createPathReturn(from.id, secret, packet->path, packet->path_len, PAYLOAD_TYPE_ACK, (uint8_t *) &ack_hash, 4); - if (path) sendFlood(path); + if (path) sendFlood(path, TXT_ACK_DELAY); } else { mesh::Packet* ack = createAck(ack_hash); if (ack) { if (from.out_path_len < 0) { - sendFlood(ack); + sendFlood(ack, TXT_ACK_DELAY); } else { - sendDirect(ack, from.out_path, from.out_path_len); + sendDirect(ack, from.out_path, from.out_path_len, TXT_ACK_DELAY); } } } @@ -164,14 +168,14 @@ void BaseChatMesh::onPeerDataRecv(mesh::Packet* packet, uint8_t type, int sender // let this sender know path TO here, so they can use sendDirect(), and ALSO encode the ACK mesh::Packet* path = createPathReturn(from.id, secret, packet->path, packet->path_len, PAYLOAD_TYPE_ACK, (uint8_t *) &ack_hash, 4); - if (path) sendFlood(path); + if (path) sendFlood(path, TXT_ACK_DELAY); } else { mesh::Packet* ack = createAck(ack_hash); if (ack) { if (from.out_path_len < 0) { - sendFlood(ack); + sendFlood(ack, TXT_ACK_DELAY); } else { - sendDirect(ack, from.out_path, from.out_path_len); + sendDirect(ack, from.out_path, from.out_path_len, TXT_ACK_DELAY); } } } From 0535919d63e4524dad66c4cbf04971a2c492f44d Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Tue, 3 Jun 2025 20:28:05 +1000 Subject: [PATCH 7/9] * Mesh: reciprocal path send now with slightly less priority and 500ms delay --- src/Mesh.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Mesh.cpp b/src/Mesh.cpp index 6029c192..a6b06c07 100644 --- a/src/Mesh.cpp +++ b/src/Mesh.cpp @@ -142,7 +142,7 @@ DispatcherAction Mesh::onRecvPacket(Packet* pkt) { if (pkt->isRouteFlood()) { // send a reciprocal return path to sender, but send DIRECTLY! mesh::Packet* rpath = createPathReturn(&src_hash, secret, pkt->path, pkt->path_len, 0, NULL, 0); - if (rpath) sendDirect(rpath, path, path_len); + if (rpath) sendDirect(rpath, path, path_len, 500); } } } else { @@ -518,7 +518,11 @@ void Mesh::sendDirect(Packet* packet, const uint8_t* path, uint8_t path_len, uin pri = 5; // maybe make this configurable } else { memcpy(packet->path, path, packet->path_len = path_len); - pri = 0; + if (packet->getPayloadType() == PAYLOAD_TYPE_PATH) { + pri = 1; // slightly less priority + } else { + pri = 0; + } } _tables->hasSeen(packet); // mark this packet as already sent in case it is rebroadcast back to us sendPacket(packet, pri, delay_millis); From 5d15a68d0d8f62dc21aeac4ce2a4cd993f8dd0d6 Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Wed, 4 Jun 2025 18:10:47 +1000 Subject: [PATCH 8/9] * SERVER_RESPONSE_DELAY now applied to: login responses, companion telemetry responses --- examples/simple_repeater/main.cpp | 6 +++--- examples/simple_room_server/main.cpp | 6 +++--- src/helpers/BaseChatMesh.cpp | 10 +++++++--- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/examples/simple_repeater/main.cpp b/examples/simple_repeater/main.cpp index d8ae9556..c2a8ae92 100644 --- a/examples/simple_repeater/main.cpp +++ b/examples/simple_repeater/main.cpp @@ -383,14 +383,14 @@ protected: // let this sender know path TO here, so they can use sendDirect(), and ALSO encode the response mesh::Packet* path = createPathReturn(sender, client->secret, packet->path, packet->path_len, PAYLOAD_TYPE_RESPONSE, reply_data, 12); - if (path) sendFlood(path); + if (path) sendFlood(path, SERVER_RESPONSE_DELAY); } else { mesh::Packet* reply = createDatagram(PAYLOAD_TYPE_RESPONSE, sender, client->secret, reply_data, 12); if (reply) { if (client->out_path_len >= 0) { // we have an out_path, so send DIRECT - sendDirect(reply, client->out_path, client->out_path_len); + sendDirect(reply, client->out_path, client->out_path_len, SERVER_RESPONSE_DELAY); } else { - sendFlood(reply); + sendFlood(reply, SERVER_RESPONSE_DELAY); } } } diff --git a/examples/simple_room_server/main.cpp b/examples/simple_room_server/main.cpp index 9f2149fe..32fe4a04 100644 --- a/examples/simple_room_server/main.cpp +++ b/examples/simple_room_server/main.cpp @@ -479,14 +479,14 @@ protected: // let this sender know path TO here, so they can use sendDirect(), and ALSO encode the response mesh::Packet* path = createPathReturn(sender, client->secret, packet->path, packet->path_len, PAYLOAD_TYPE_RESPONSE, reply_data, 8 + 2); - if (path) sendFlood(path); + if (path) sendFlood(path, SERVER_RESPONSE_DELAY); } else { mesh::Packet* reply = createDatagram(PAYLOAD_TYPE_RESPONSE, sender, client->secret, reply_data, 8 + 2); if (reply) { if (client->out_path_len >= 0) { // we have an out_path, so send DIRECT - sendDirect(reply, client->out_path, client->out_path_len); + sendDirect(reply, client->out_path, client->out_path_len, SERVER_RESPONSE_DELAY); } else { - sendFlood(reply); + sendFlood(reply, SERVER_RESPONSE_DELAY); } } } diff --git a/src/helpers/BaseChatMesh.cpp b/src/helpers/BaseChatMesh.cpp index bcf83f8b..ba8c3e28 100644 --- a/src/helpers/BaseChatMesh.cpp +++ b/src/helpers/BaseChatMesh.cpp @@ -1,6 +1,10 @@ #include #include +#ifndef SERVER_RESPONSE_DELAY + #define SERVER_RESPONSE_DELAY 300 +#endif + #ifndef TXT_ACK_DELAY #define TXT_ACK_DELAY 200 #endif @@ -191,14 +195,14 @@ void BaseChatMesh::onPeerDataRecv(mesh::Packet* packet, uint8_t type, int sender // let this sender know path TO here, so they can use sendDirect(), and ALSO encode the response mesh::Packet* path = createPathReturn(from.id, secret, packet->path, packet->path_len, PAYLOAD_TYPE_RESPONSE, temp_buf, reply_len); - if (path) sendFlood(path); + if (path) sendFlood(path, SERVER_RESPONSE_DELAY); } else { mesh::Packet* reply = createDatagram(PAYLOAD_TYPE_RESPONSE, from.id, secret, temp_buf, reply_len); if (reply) { if (from.out_path_len >= 0) { // we have an out_path, so send DIRECT - sendDirect(reply, from.out_path, from.out_path_len); + sendDirect(reply, from.out_path, from.out_path_len, SERVER_RESPONSE_DELAY); } else { - sendFlood(reply); + sendFlood(reply, SERVER_RESPONSE_DELAY); } } } From 647d712ae8a553d3f3aab3036076db7a2667e1ee Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Wed, 4 Jun 2025 21:33:48 +1000 Subject: [PATCH 9/9] * Companion: long-press in first 8 seconds now enters CLI Rescue mode --- examples/companion_radio/MyMesh.cpp | 84 +++++++++++++++++++++++++- examples/companion_radio/MyMesh.h | 7 +++ examples/companion_radio/UITask.cpp | 7 ++- examples/companion_radio/UITask.h | 4 +- variants/heltec_tracker/platformio.ini | 2 +- 5 files changed, 99 insertions(+), 5 deletions(-) diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp index 121584a6..730747e1 100644 --- a/examples/companion_radio/MyMesh.cpp +++ b/examples/companion_radio/MyMesh.cpp @@ -728,6 +728,7 @@ MyMesh::MyMesh(mesh::Radio &radio, mesh::RNG &rng, mesh::RTCClock &rtc, SimpleMe : BaseChatMesh(radio, *new ArduinoMillis(), rng, rtc, *new StaticPoolPacketManager(16), tables), _serial(NULL), telemetry(MAX_PACKET_PAYLOAD - 4) { _iter_started = false; + _cli_rescue = false; offline_queue_len = 0; app_target_ver = 0; _identity_store = NULL; @@ -1529,9 +1530,78 @@ void MyMesh::handleCmdFrame(size_t len) { } } -void MyMesh::loop() { - BaseChatMesh::loop(); +void MyMesh::enterCLIRescue() { + _cli_rescue = true; + cli_command[0] = 0; + Serial.println("========= CLI Rescue ========="); +} +bool MyMesh::formatFileSystem() { +#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) + return InternalFS.format(); +#elif defined(RP2040_PLATFORM) + return LittleFS.format(); +#elif defined(ESP32) + return SPIFFS.format(); +#else + #error "need to implement file system erase" + return false; +#endif +} + +void MyMesh::checkCLIRescueCmd() { + int len = strlen(cli_command); + while (Serial.available() && len < sizeof(cli_command)-1) { + char c = Serial.read(); + if (c != '\n') { + cli_command[len++] = c; + cli_command[len] = 0; + } + Serial.print(c); // echo + } + if (len == sizeof(cli_command)-1) { // command buffer full + cli_command[sizeof(cli_command)-1] = '\r'; + } + + if (len > 0 && cli_command[len - 1] == '\r') { // received complete line + cli_command[len - 1] = 0; // replace newline with C string null terminator + + if (memcmp(cli_command, "set ", 4) == 0) { + const char* config = &cli_command[4]; + if (memcmp(config, "pin ", 4) == 0) { + _prefs.ble_pin = atoi(&config[4]); + savePrefs(); + Serial.printf(" > pin is now %06d\n", _prefs.ble_pin); + } else { + Serial.printf(" Error: unknown config: %s\n", config); + } + } else if (strcmp(cli_command, "rebuild") == 0) { + bool success = formatFileSystem(); + if (success) { + saveMainIdentity(self_id); + saveContacts(); + Serial.println(" > erase and rebuild done"); + } else { + Serial.println(" Error: erase failed"); + } + } else if (strcmp(cli_command, "erase") == 0) { + bool success = formatFileSystem(); + if (success) { + Serial.println(" > erase done"); + } else { + Serial.println(" Error: erase failed"); + } + } else if (strcmp(cli_command, "reboot") == 0) { + board.reboot(); // doesn't return + } else { + Serial.println(" Error: unknown command"); + } + + cli_command[0] = 0; // reset command buffer + } +} + +void MyMesh::checkSerialInterface() { size_t len = _serial->checkRecvFrame(cmd_frame); if (len > 0) { handleCmdFrame(len); @@ -1556,6 +1626,16 @@ void MyMesh::loop() { } else if (!_serial->isWriteBusy()) { checkConnections(); } +} + +void MyMesh::loop() { + BaseChatMesh::loop(); + + if (_cli_rescue) { + checkCLIRescueCmd(); + } else { + checkSerialInterface(); + } // is there are pending dirty contacts write needed? if (dirty_contacts_expiry && millisHasNowPassed(dirty_contacts_expiry)) { diff --git a/examples/companion_radio/MyMesh.h b/examples/companion_radio/MyMesh.h index 9d7da0df..92da8c9d 100644 --- a/examples/companion_radio/MyMesh.h +++ b/examples/companion_radio/MyMesh.h @@ -92,6 +92,7 @@ public: void loop(); void handleCmdFrame(size_t len); bool advert(); + void enterCLIRescue(); protected: float getAirtimeBudgetFactor() const override; @@ -143,6 +144,10 @@ private: int getBlobByKey(const uint8_t key[], int key_len, uint8_t dest_buf[]) override; bool putBlobByKey(const uint8_t key[], int key_len, const uint8_t src_buf[], int len) override; + void checkCLIRescueCmd(); + void checkSerialInterface(); + bool formatFileSystem(); + private: FILESYSTEM *_fs; IdentityStore *_identity_store; @@ -157,6 +162,8 @@ private: uint32_t _most_recent_lastmod; uint32_t _active_ble_pin; bool _iter_started; + bool _cli_rescue; + char cli_command[80]; uint8_t app_target_ver; uint8_t *sign_data; uint32_t sign_data_len; diff --git a/examples/companion_radio/UITask.cpp b/examples/companion_radio/UITask.cpp index 00263e5e..f0f780d6 100644 --- a/examples/companion_radio/UITask.cpp +++ b/examples/companion_radio/UITask.cpp @@ -75,6 +75,7 @@ void UITask::begin(DisplayDriver* display, NodePrefs* node_prefs) { _userButton->onLongPress([this]() { handleButtonLongPress(); }); _userButton->onAnyPress([this]() { handleButtonAnyPress(); }); #endif + ui_started_at = millis(); } void UITask::soundBuzzer(UIEventType bet) { @@ -365,5 +366,9 @@ void UITask::handleButtonTriplePress() { void UITask::handleButtonLongPress() { MESH_DEBUG_PRINTLN("UITask: long press triggered"); - shutdown(); + if (millis() - ui_started_at < 8000) { // long press in first 8 seconds since startup -> CLI/rescue + the_mesh.enterCLIRescue(); + } else { + shutdown(); + } } \ No newline at end of file diff --git a/examples/companion_radio/UITask.h b/examples/companion_radio/UITask.h index 6914e9ec..fff0bbe1 100644 --- a/examples/companion_radio/UITask.h +++ b/examples/companion_radio/UITask.h @@ -36,6 +36,7 @@ class UITask { int _msgcount; bool _need_refresh = true; bool _displayWasOn = false; // Track display state before button press + unsigned long ui_started_at; // Button handlers #if defined(PIN_USER_BTN) || defined(PIN_USER_BTN_ANA) @@ -57,7 +58,8 @@ class UITask { public: UITask(mesh::MainBoard* board) : _board(board), _display(NULL) { - _next_refresh = 0; + _next_refresh = 0; + ui_started_at = 0; _connected = false; } void begin(DisplayDriver* display, NodePrefs* node_prefs); diff --git a/variants/heltec_tracker/platformio.ini b/variants/heltec_tracker/platformio.ini index d62771f4..ed4cbe28 100644 --- a/variants/heltec_tracker/platformio.ini +++ b/variants/heltec_tracker/platformio.ini @@ -39,7 +39,7 @@ extends = Heltec_tracker_base build_flags = ${Heltec_tracker_base.build_flags} -I src/helpers/ui - ; -D ARDUINO_USB_CDC_ON_BOOT=1 ; need for debugging + -D ARDUINO_USB_CDC_ON_BOOT=1 ; need for Serial -D DISPLAY_ROTATION=1 -D DISPLAY_CLASS=ST7735Display -D MAX_CONTACTS=100