From b1ca3d1eb1c8b3a30c59e69b5d74886feaad5c60 Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Mon, 23 Jun 2025 01:14:08 +1000 Subject: [PATCH 01/24] * new PAYLOAD_TYPE_MULTIPART * experimental double ACK's (at each hop), sent 300ms apart (direct mode only) --- examples/simple_room_server/main.cpp | 19 ++-- src/Mesh.cpp | 161 ++++++++++++++++++++++++--- src/Mesh.h | 6 + src/Packet.h | 1 + src/helpers/BaseChatMesh.cpp | 31 +++--- src/helpers/BaseChatMesh.h | 1 + 6 files changed, 181 insertions(+), 38 deletions(-) diff --git a/examples/simple_room_server/main.cpp b/examples/simple_room_server/main.cpp index d9d9f122..b2525843 100644 --- a/examples/simple_room_server/main.cpp +++ b/examples/simple_room_server/main.cpp @@ -576,15 +576,18 @@ protected: uint32_t delay_millis; if (send_ack) { - mesh::Packet* ack = createAck(ack_hash); - if (ack) { - if (client->out_path_len < 0) { - sendFlood(ack, TXT_ACK_DELAY); - } else { - sendDirect(ack, client->out_path, client->out_path_len, TXT_ACK_DELAY); - } + if (client->out_path_len < 0) { + mesh::Packet* ack = createAck(ack_hash); + if (ack) sendFlood(ack, TXT_ACK_DELAY); + delay_millis = TXT_ACK_DELAY + REPLY_DELAY_MILLIS; + } else { + mesh::Packet* a1 = createMultiAck(ack_hash, 1); + if (a1) sendDirect(a1, client->out_path, client->out_path_len, TXT_ACK_DELAY); + + mesh::Packet* a2 = createAck(ack_hash); + if (a2) sendDirect(a2, client->out_path, client->out_path_len, TXT_ACK_DELAY + 300); + delay_millis = TXT_ACK_DELAY + REPLY_DELAY_MILLIS + 300; } - delay_millis = TXT_ACK_DELAY + REPLY_DELAY_MILLIS; } else { delay_millis = 0; } diff --git a/src/Mesh.cpp b/src/Mesh.cpp index a6b06c07..ac949a94 100644 --- a/src/Mesh.cpp +++ b/src/Mesh.cpp @@ -67,22 +67,22 @@ DispatcherAction Mesh::onRecvPacket(Packet* pkt) { if (pkt->isRouteDirect() && pkt->path_len >= PATH_HASH_SIZE) { if (self_id.isHashMatch(pkt->path) && allowPacketForward(pkt)) { - if (_tables->hasSeen(pkt)) return ACTION_RELEASE; // don't retransmit! - - // remove our hash from 'path', then re-broadcast - pkt->path_len -= PATH_HASH_SIZE; - #if 0 - memcpy(pkt->path, &pkt->path[PATH_HASH_SIZE], pkt->path_len); - #elif PATH_HASH_SIZE == 1 - for (int k = 0; k < pkt->path_len; k++) { // shuffle bytes by 1 - pkt->path[k] = pkt->path[k + 1]; + if (pkt->getPayloadType() == PAYLOAD_TYPE_MULTIPART) { + return forwardMultipartDirect(pkt); + } else if (pkt->getPayloadType() == PAYLOAD_TYPE_ACK) { + if (!_tables->hasSeen(pkt)) { // don't retransmit! + removeSelfFromPath(pkt); + routeDirectRecvAcks(pkt, 0); + } + return ACTION_RELEASE; } - #else - #error "need path remove impl" - #endif - uint32_t d = getDirectRetransmitDelay(pkt); - return ACTION_RETRANSMIT_DELAYED(0, d); // Routed traffic is HIGHEST priority + if (!_tables->hasSeen(pkt)) { + removeSelfFromPath(pkt); + + uint32_t d = getDirectRetransmitDelay(pkt); + return ACTION_RETRANSMIT_DELAYED(0, d); // Routed traffic is HIGHEST priority + } } return ACTION_RELEASE; // this node is NOT the next hop (OR this packet has already been forwarded), so discard. } @@ -99,6 +99,7 @@ DispatcherAction Mesh::onRecvPacket(Packet* pkt) { } else if (!_tables->hasSeen(pkt)) { onAckRecv(pkt, ack_crc); action = routeRecvPacket(pkt); + // routeRecvAcks(pkt, 0); // experimental, double Acks in flood mode(?) } break; } @@ -261,6 +262,33 @@ DispatcherAction Mesh::onRecvPacket(Packet* pkt) { } break; } + case PAYLOAD_TYPE_MULTIPART: + if (pkt->payload_len > 2) { + uint8_t remaining = pkt->payload[0] >> 4; // num of packets in this multipart sequence still to be sent + uint8_t type = pkt->payload[0] & 0x0F; + + if (type == PAYLOAD_TYPE_ACK && pkt->payload_len >= 5) { // a multipart ACK + Packet tmp; + tmp.header = pkt->header; + tmp.path_len = pkt->path_len; + memcpy(tmp.path, pkt->path, pkt->path_len); + tmp.payload_len = pkt->payload_len - 1; + memcpy(tmp.payload, &pkt->payload[1], tmp.payload_len); + + if (!_tables->hasSeen(&tmp)) { + uint32_t ack_crc; + memcpy(&ack_crc, tmp.payload, 4); + + onAckRecv(&tmp, ack_crc); + // routeRecvAcks(&tmp, ((uint32_t)remaining) * 600); // expect multipart ACK 300ms apart (x2) + //action = routeRecvPacket(&tmp); // NOTE: currently not needed, as multipart ACKs not sent Flood + } + } else { + // FUTURE: other multipart types?? + } + } + break; + default: MESH_DEBUG_PRINTLN("%s Mesh::onRecvPacket(): unknown payload type, header: %d", getLogDateTime(), (int) pkt->header); // Don't flood route unknown packet types! action = routeRecvPacket(pkt); @@ -269,6 +297,20 @@ DispatcherAction Mesh::onRecvPacket(Packet* pkt) { return action; } +void Mesh::removeSelfFromPath(Packet* pkt) { + // remove our hash from 'path' + pkt->path_len -= PATH_HASH_SIZE; +#if 0 + memcpy(pkt->path, &pkt->path[PATH_HASH_SIZE], pkt->path_len); +#elif PATH_HASH_SIZE == 1 + for (int k = 0; k < pkt->path_len; k++) { // shuffle bytes by 1 + pkt->path[k] = pkt->path[k + 1]; + } +#else + #error "need path remove impl" +#endif +} + DispatcherAction Mesh::routeRecvPacket(Packet* packet) { if (packet->isRouteFlood() && !packet->isMarkedDoNotRetransmit() && packet->path_len + PATH_HASH_SIZE <= MAX_PATH_SIZE && allowPacketForward(packet)) { @@ -282,6 +324,82 @@ DispatcherAction Mesh::routeRecvPacket(Packet* packet) { return ACTION_RELEASE; } +#if 0 +void Mesh::routeRecvAcks(Packet* packet, uint32_t delay_millis) { + if (packet->isRouteFlood() && !packet->isMarkedDoNotRetransmit() + && packet->path_len + PATH_HASH_SIZE <= MAX_PATH_SIZE && allowPacketForward(packet)) { + // append this node's hash to 'path' + packet->path_len += self_id.copyHashTo(&packet->path[packet->path_len]); + + uint32_t crc; + memcpy(&crc, packet->payload, 4); + + delay_millis += getRetransmitDelay(packet); + auto a1 = createMultiAck(crc, 1); + if (a1) { + memcpy(a1->path, packet->path, a1->path_len = packet->path_len); + a1->header &= ~PH_ROUTE_MASK; + a1->header |= ROUTE_TYPE_FLOOD; + sendPacket(a1, 1, delay_millis); + } + + delay_millis += 300; + auto a2 = createAck(crc); + if (a2) { + memcpy(a2->path, packet->path, a2->path_len = packet->path_len); + a2->header &= ~PH_ROUTE_MASK; + a2->header |= ROUTE_TYPE_FLOOD; + sendPacket(a2, 1, delay_millis); + } + } +} +#endif + +DispatcherAction Mesh::forwardMultipartDirect(Packet* pkt) { + uint8_t remaining = pkt->payload[0] >> 4; // num of packets in this multipart sequence still to be sent + uint8_t type = pkt->payload[0] & 0x0F; + + if (type == PAYLOAD_TYPE_ACK && pkt->payload_len >= 5) { // a multipart ACK + Packet tmp; + tmp.header = pkt->header; + tmp.path_len = pkt->path_len; + memcpy(tmp.path, pkt->path, pkt->path_len); + tmp.payload_len = pkt->payload_len - 1; + memcpy(tmp.payload, &pkt->payload[1], tmp.payload_len); + + if (!_tables->hasSeen(&tmp)) { // don't retransmit! + removeSelfFromPath(&tmp); + routeDirectRecvAcks(&tmp, ((uint32_t)remaining) * 600); // expect multipart ACKs 300ms apart (x2) + } + } + return ACTION_RELEASE; +} + +void Mesh::routeDirectRecvAcks(Packet* packet, uint32_t delay_millis) { + if (!packet->isMarkedDoNotRetransmit()) { + uint32_t crc; + memcpy(&crc, packet->payload, 4); + + delay_millis += getDirectRetransmitDelay(packet); + auto a1 = createMultiAck(crc, 1); + if (a1) { + memcpy(a1->path, packet->path, a1->path_len = packet->path_len); + a1->header &= ~PH_ROUTE_MASK; + a1->header |= ROUTE_TYPE_DIRECT; + sendPacket(a1, 0, delay_millis); + } + + delay_millis += 300; + auto a2 = createAck(crc); + if (a2) { + memcpy(a2->path, packet->path, a2->path_len = packet->path_len); + a2->header &= ~PH_ROUTE_MASK; + a2->header |= ROUTE_TYPE_DIRECT; + sendPacket(a2, 0, delay_millis); + } + } +} + Packet* Mesh::createAdvert(const LocalIdentity& id, const uint8_t* app_data, size_t app_data_len) { if (app_data_len > MAX_ADVERT_DATA_SIZE) return NULL; @@ -449,6 +567,21 @@ Packet* Mesh::createAck(uint32_t ack_crc) { return packet; } +Packet* Mesh::createMultiAck(uint32_t ack_crc, uint8_t remaining) { + Packet* packet = obtainNewPacket(); + if (packet == NULL) { + MESH_DEBUG_PRINTLN("%s Mesh::createMultiAck(): error, packet pool empty", getLogDateTime()); + return NULL; + } + packet->header = (PAYLOAD_TYPE_MULTIPART << PH_TYPE_SHIFT); // ROUTE_TYPE_* set later + + packet->payload[0] = (remaining << 4) | PAYLOAD_TYPE_ACK; + memcpy(&packet->payload[1], &ack_crc, 4); + packet->payload_len = 5; + + return packet; +} + Packet* Mesh::createRawData(const uint8_t* data, size_t len) { if (len > sizeof(Packet::payload)) return NULL; // invalid arg diff --git a/src/Mesh.h b/src/Mesh.h index 9649187c..5c847df2 100644 --- a/src/Mesh.h +++ b/src/Mesh.h @@ -28,6 +28,11 @@ class Mesh : public Dispatcher { RNG* _rng; MeshTables* _tables; + void removeSelfFromPath(Packet* packet); + void routeDirectRecvAcks(Packet* packet, uint32_t delay_millis); + //void routeRecvAcks(Packet* packet, uint32_t delay_millis); + DispatcherAction forwardMultipartDirect(Packet* pkt); + protected: DispatcherAction onRecvPacket(Packet* pkt) override; @@ -165,6 +170,7 @@ public: Packet* createAnonDatagram(uint8_t type, const LocalIdentity& sender, const Identity& dest, const uint8_t* secret, const uint8_t* data, size_t data_len); Packet* createGroupDatagram(uint8_t type, const GroupChannel& channel, const uint8_t* data, size_t data_len); Packet* createAck(uint32_t ack_crc); + Packet* createMultiAck(uint32_t ack_crc, uint8_t remaining); Packet* createPathReturn(const uint8_t* dest_hash, const uint8_t* secret, const uint8_t* path, uint8_t path_len, uint8_t extra_type, const uint8_t*extra, size_t extra_len); Packet* createPathReturn(const Identity& dest, const uint8_t* secret, const uint8_t* path, uint8_t path_len, uint8_t extra_type, const uint8_t*extra, size_t extra_len); Packet* createRawData(const uint8_t* data, size_t len); diff --git a/src/Packet.h b/src/Packet.h index 7c7df8e3..e52ab526 100644 --- a/src/Packet.h +++ b/src/Packet.h @@ -26,6 +26,7 @@ namespace mesh { #define PAYLOAD_TYPE_ANON_REQ 0x07 // generic request (prefixed with dest_hash, ephemeral pub_key, MAC) (enc data: ...) #define PAYLOAD_TYPE_PATH 0x08 // returned path (prefixed with dest/src hashes, MAC) (enc data: path, extra) #define PAYLOAD_TYPE_TRACE 0x09 // trace a path, collecting SNI for each hop +#define PAYLOAD_TYPE_MULTIPART 0x0A // packet is one of a set of packets //... #define PAYLOAD_TYPE_RAW_CUSTOM 0x0F // custom packet as raw bytes, for applications with custom encryption, payloads, etc diff --git a/src/helpers/BaseChatMesh.cpp b/src/helpers/BaseChatMesh.cpp index 271f28da..04c87c19 100644 --- a/src/helpers/BaseChatMesh.cpp +++ b/src/helpers/BaseChatMesh.cpp @@ -31,6 +31,19 @@ mesh::Packet* BaseChatMesh::createSelfAdvert(const char* name, double lat, doubl return createAdvert(self_id, app_data, app_data_len); } +void BaseChatMesh::sendAckTo(const ContactInfo& dest, uint32_t ack_hash) { + if (dest.out_path_len < 0) { + mesh::Packet* ack = createAck(ack_hash); + if (ack) sendFlood(ack, TXT_ACK_DELAY); + } else { + mesh::Packet* a1 = createMultiAck(ack_hash, 1); + if (a1) sendDirect(a1, dest.out_path, dest.out_path_len, TXT_ACK_DELAY); + + mesh::Packet* a2 = createAck(ack_hash); + if (a2) sendDirect(a2, dest.out_path, dest.out_path_len, TXT_ACK_DELAY + 300); + } +} + void BaseChatMesh::onAdvertRecv(mesh::Packet* packet, const mesh::Identity& id, uint32_t timestamp, const uint8_t* app_data, size_t app_data_len) { AdvertDataParser parser(app_data, app_data_len); if (!(parser.isValid() && parser.hasName())) { @@ -152,14 +165,7 @@ void BaseChatMesh::onPeerDataRecv(mesh::Packet* packet, uint8_t type, int sender PAYLOAD_TYPE_ACK, (uint8_t *) &ack_hash, 4); if (path) sendFlood(path, TXT_ACK_DELAY); } else { - mesh::Packet* ack = createAck(ack_hash); - if (ack) { - if (from.out_path_len < 0) { - sendFlood(ack, TXT_ACK_DELAY); - } else { - sendDirect(ack, from.out_path, from.out_path_len, TXT_ACK_DELAY); - } - } + sendAckTo(from, ack_hash); } } else if (flags == TXT_TYPE_CLI_DATA) { onCommandDataRecv(from, packet, timestamp, (const char *) &data[5]); // let UI know @@ -185,14 +191,7 @@ void BaseChatMesh::onPeerDataRecv(mesh::Packet* packet, uint8_t type, int sender PAYLOAD_TYPE_ACK, (uint8_t *) &ack_hash, 4); if (path) sendFlood(path, TXT_ACK_DELAY); } else { - mesh::Packet* ack = createAck(ack_hash); - if (ack) { - if (from.out_path_len < 0) { - sendFlood(ack, TXT_ACK_DELAY); - } else { - sendDirect(ack, from.out_path, from.out_path_len, TXT_ACK_DELAY); - } - } + sendAckTo(from, ack_hash); } } else { MESH_DEBUG_PRINTLN("onPeerDataRecv: unsupported message type: %u", (uint32_t) flags); diff --git a/src/helpers/BaseChatMesh.h b/src/helpers/BaseChatMesh.h index 53cd5018..9a525af8 100644 --- a/src/helpers/BaseChatMesh.h +++ b/src/helpers/BaseChatMesh.h @@ -72,6 +72,7 @@ class BaseChatMesh : public mesh::Mesh { ConnectionInfo connections[MAX_CONNECTIONS]; mesh::Packet* composeMsgPacket(const ContactInfo& recipient, uint32_t timestamp, uint8_t attempt, const char *text, uint32_t& expected_ack); + void sendAckTo(const ContactInfo& dest, uint32_t ack_hash); protected: BaseChatMesh(mesh::Radio& radio, mesh::MillisecondClock& ms, mesh::RNG& rng, mesh::RTCClock& rtc, mesh::PacketManager& mgr, mesh::MeshTables& tables) From 6be8e19a9fe2669879b9c5bf5b88410ece92db10 Mon Sep 17 00:00:00 2001 From: recrof Date: Sun, 13 Jul 2025 11:37:33 +0200 Subject: [PATCH 02/24] move radiolib wrappers to dedicated directory --- platformio.ini | 1 + src/helpers/{ => radiolib}/CustomLLCC68.h | 0 src/helpers/{ => radiolib}/CustomLLCC68Wrapper.h | 0 src/helpers/{ => radiolib}/CustomLR1110.h | 0 src/helpers/{ => radiolib}/CustomLR1110Wrapper.h | 0 src/helpers/{ => radiolib}/CustomSTM32WLx.h | 0 src/helpers/{ => radiolib}/CustomSTM32WLxWrapper.h | 0 src/helpers/{ => radiolib}/CustomSX1262.h | 0 src/helpers/{ => radiolib}/CustomSX1262Wrapper.h | 0 src/helpers/{ => radiolib}/CustomSX1268.h | 0 src/helpers/{ => radiolib}/CustomSX1268Wrapper.h | 0 src/helpers/{ => radiolib}/CustomSX1276.h | 0 src/helpers/{ => radiolib}/CustomSX1276Wrapper.h | 0 src/helpers/{ => radiolib}/RadioLibWrappers.cpp | 0 src/helpers/{ => radiolib}/RadioLibWrappers.h | 0 variants/generic-e22/target.h | 6 +++--- variants/heltec_ct62/target.h | 4 ++-- variants/heltec_tracker/target.h | 4 ++-- variants/heltec_v2/target.h | 4 ++-- variants/heltec_v3/target.h | 4 ++-- variants/heltec_wireless_paper/target.h | 4 ++-- variants/lilygo_t3s3/target.h | 4 ++-- variants/lilygo_t3s3_sx1276/target.h | 4 ++-- variants/lilygo_tbeam_SX1262/target.h | 4 ++-- variants/lilygo_tbeam_SX1276/target.h | 4 ++-- variants/lilygo_tbeam_supreme_SX1262/target.h | 4 ++-- variants/lilygo_tlora_c6/target.h | 4 ++-- variants/lilygo_tlora_v2_1/target.h | 4 ++-- variants/meshadventurer/target.h | 6 +++--- variants/minewsemi_me25ls01/target.h | 4 ++-- variants/nano_g2_ultra/target.h | 7 +++---- variants/picow/target.h | 4 ++-- variants/promicro/target.h | 6 ++---- variants/rak3x72/target.h | 6 +++--- variants/rak4631/target.h | 4 ++-- variants/sensecap_solar/target.h | 4 ++-- variants/station_g2/target.h | 4 ++-- variants/t1000-e/target.h | 4 ++-- variants/t114/target.h | 4 ++-- variants/techo/target.h | 4 ++-- variants/tenstar_c3/target.h | 6 +++--- variants/thinknode_m1/target.h | 4 ++-- variants/waveshare_rp2040_lora/target.h | 4 ++-- variants/wio-e5-dev/target.h | 6 +++--- variants/wio-e5-mini/target.h | 8 ++++---- variants/wio-tracker-l1/target.h | 4 ++-- variants/xiao_c3/target.h | 6 +++--- variants/xiao_c6/target.h | 4 ++-- variants/xiao_nrf52/target.h | 4 ++-- variants/xiao_rp2040/target.h | 4 ++-- variants/xiao_s3_wio/target.h | 4 ++-- 51 files changed, 82 insertions(+), 84 deletions(-) rename src/helpers/{ => radiolib}/CustomLLCC68.h (100%) rename src/helpers/{ => radiolib}/CustomLLCC68Wrapper.h (100%) rename src/helpers/{ => radiolib}/CustomLR1110.h (100%) rename src/helpers/{ => radiolib}/CustomLR1110Wrapper.h (100%) rename src/helpers/{ => radiolib}/CustomSTM32WLx.h (100%) rename src/helpers/{ => radiolib}/CustomSTM32WLxWrapper.h (100%) rename src/helpers/{ => radiolib}/CustomSX1262.h (100%) rename src/helpers/{ => radiolib}/CustomSX1262Wrapper.h (100%) rename src/helpers/{ => radiolib}/CustomSX1268.h (100%) rename src/helpers/{ => radiolib}/CustomSX1268Wrapper.h (100%) rename src/helpers/{ => radiolib}/CustomSX1276.h (100%) rename src/helpers/{ => radiolib}/CustomSX1276Wrapper.h (100%) rename src/helpers/{ => radiolib}/RadioLibWrappers.cpp (100%) rename src/helpers/{ => radiolib}/RadioLibWrappers.h (100%) diff --git a/platformio.ini b/platformio.ini index 00a2d0fb..cd1c21ad 100644 --- a/platformio.ini +++ b/platformio.ini @@ -46,6 +46,7 @@ build_flags = -w -DNDEBUG -DRADIOLIB_STATIC_ONLY=1 -DRADIOLIB_GODMODE=1 build_src_filter = +<*.cpp> + + + ; ----------------- ESP32 --------------------- diff --git a/src/helpers/CustomLLCC68.h b/src/helpers/radiolib/CustomLLCC68.h similarity index 100% rename from src/helpers/CustomLLCC68.h rename to src/helpers/radiolib/CustomLLCC68.h diff --git a/src/helpers/CustomLLCC68Wrapper.h b/src/helpers/radiolib/CustomLLCC68Wrapper.h similarity index 100% rename from src/helpers/CustomLLCC68Wrapper.h rename to src/helpers/radiolib/CustomLLCC68Wrapper.h diff --git a/src/helpers/CustomLR1110.h b/src/helpers/radiolib/CustomLR1110.h similarity index 100% rename from src/helpers/CustomLR1110.h rename to src/helpers/radiolib/CustomLR1110.h diff --git a/src/helpers/CustomLR1110Wrapper.h b/src/helpers/radiolib/CustomLR1110Wrapper.h similarity index 100% rename from src/helpers/CustomLR1110Wrapper.h rename to src/helpers/radiolib/CustomLR1110Wrapper.h diff --git a/src/helpers/CustomSTM32WLx.h b/src/helpers/radiolib/CustomSTM32WLx.h similarity index 100% rename from src/helpers/CustomSTM32WLx.h rename to src/helpers/radiolib/CustomSTM32WLx.h diff --git a/src/helpers/CustomSTM32WLxWrapper.h b/src/helpers/radiolib/CustomSTM32WLxWrapper.h similarity index 100% rename from src/helpers/CustomSTM32WLxWrapper.h rename to src/helpers/radiolib/CustomSTM32WLxWrapper.h diff --git a/src/helpers/CustomSX1262.h b/src/helpers/radiolib/CustomSX1262.h similarity index 100% rename from src/helpers/CustomSX1262.h rename to src/helpers/radiolib/CustomSX1262.h diff --git a/src/helpers/CustomSX1262Wrapper.h b/src/helpers/radiolib/CustomSX1262Wrapper.h similarity index 100% rename from src/helpers/CustomSX1262Wrapper.h rename to src/helpers/radiolib/CustomSX1262Wrapper.h diff --git a/src/helpers/CustomSX1268.h b/src/helpers/radiolib/CustomSX1268.h similarity index 100% rename from src/helpers/CustomSX1268.h rename to src/helpers/radiolib/CustomSX1268.h diff --git a/src/helpers/CustomSX1268Wrapper.h b/src/helpers/radiolib/CustomSX1268Wrapper.h similarity index 100% rename from src/helpers/CustomSX1268Wrapper.h rename to src/helpers/radiolib/CustomSX1268Wrapper.h diff --git a/src/helpers/CustomSX1276.h b/src/helpers/radiolib/CustomSX1276.h similarity index 100% rename from src/helpers/CustomSX1276.h rename to src/helpers/radiolib/CustomSX1276.h diff --git a/src/helpers/CustomSX1276Wrapper.h b/src/helpers/radiolib/CustomSX1276Wrapper.h similarity index 100% rename from src/helpers/CustomSX1276Wrapper.h rename to src/helpers/radiolib/CustomSX1276Wrapper.h diff --git a/src/helpers/RadioLibWrappers.cpp b/src/helpers/radiolib/RadioLibWrappers.cpp similarity index 100% rename from src/helpers/RadioLibWrappers.cpp rename to src/helpers/radiolib/RadioLibWrappers.cpp diff --git a/src/helpers/RadioLibWrappers.h b/src/helpers/radiolib/RadioLibWrappers.h similarity index 100% rename from src/helpers/RadioLibWrappers.h rename to src/helpers/radiolib/RadioLibWrappers.h diff --git a/variants/generic-e22/target.h b/variants/generic-e22/target.h index e5c116e1..442706f3 100644 --- a/variants/generic-e22/target.h +++ b/variants/generic-e22/target.h @@ -2,10 +2,10 @@ #define RADIOLIB_STATIC_ONLY 1 #include -#include +#include #include -#include -#include +#include +#include #include #include diff --git a/variants/heltec_ct62/target.h b/variants/heltec_ct62/target.h index ff114e6e..9639ab2d 100644 --- a/variants/heltec_ct62/target.h +++ b/variants/heltec_ct62/target.h @@ -2,9 +2,9 @@ #define RADIOLIB_STATIC_ONLY 1 #include -#include +#include #include "HT-CT62Board.h" -#include +#include #include #include diff --git a/variants/heltec_tracker/target.h b/variants/heltec_tracker/target.h index 3184bec9..c08be80a 100644 --- a/variants/heltec_tracker/target.h +++ b/variants/heltec_tracker/target.h @@ -2,9 +2,9 @@ #define RADIOLIB_STATIC_ONLY 1 #include -#include +#include #include -#include +#include #include #include #include diff --git a/variants/heltec_v2/target.h b/variants/heltec_v2/target.h index f758c193..0c330316 100644 --- a/variants/heltec_v2/target.h +++ b/variants/heltec_v2/target.h @@ -2,9 +2,9 @@ #define RADIOLIB_STATIC_ONLY 1 #include -#include +#include #include -#include +#include #include #include #ifdef DISPLAY_CLASS diff --git a/variants/heltec_v3/target.h b/variants/heltec_v3/target.h index 701f9cd5..992a3d2c 100644 --- a/variants/heltec_v3/target.h +++ b/variants/heltec_v3/target.h @@ -2,9 +2,9 @@ #define RADIOLIB_STATIC_ONLY 1 #include -#include +#include #include -#include +#include #include #include #include diff --git a/variants/heltec_wireless_paper/target.h b/variants/heltec_wireless_paper/target.h index 7d901c01..cb95905c 100644 --- a/variants/heltec_wireless_paper/target.h +++ b/variants/heltec_wireless_paper/target.h @@ -2,9 +2,9 @@ #define RADIOLIB_STATIC_ONLY 1 #include -#include +#include #include -#include +#include #include #include #ifdef DISPLAY_CLASS diff --git a/variants/lilygo_t3s3/target.h b/variants/lilygo_t3s3/target.h index 609248ae..b768b2b0 100644 --- a/variants/lilygo_t3s3/target.h +++ b/variants/lilygo_t3s3/target.h @@ -2,9 +2,9 @@ #define RADIOLIB_STATIC_ONLY 1 #include -#include +#include #include -#include +#include #include #include #ifdef DISPLAY_CLASS diff --git a/variants/lilygo_t3s3_sx1276/target.h b/variants/lilygo_t3s3_sx1276/target.h index c5fc0be0..52ecf867 100644 --- a/variants/lilygo_t3s3_sx1276/target.h +++ b/variants/lilygo_t3s3_sx1276/target.h @@ -2,9 +2,9 @@ #define RADIOLIB_STATIC_ONLY 1 #include -#include +#include #include -#include +#include #include #include #ifdef DISPLAY_CLASS diff --git a/variants/lilygo_tbeam_SX1262/target.h b/variants/lilygo_tbeam_SX1262/target.h index da4c5d5c..cb9b28df 100644 --- a/variants/lilygo_tbeam_SX1262/target.h +++ b/variants/lilygo_tbeam_SX1262/target.h @@ -2,9 +2,9 @@ #define RADIOLIB_STATIC_ONLY 1 #include -#include +#include #include -#include +#include #include #include #ifdef DISPLAY_CLASS diff --git a/variants/lilygo_tbeam_SX1276/target.h b/variants/lilygo_tbeam_SX1276/target.h index 41435210..bcd8cb0b 100644 --- a/variants/lilygo_tbeam_SX1276/target.h +++ b/variants/lilygo_tbeam_SX1276/target.h @@ -2,9 +2,9 @@ #define RADIOLIB_STATIC_ONLY 1 //#include -#include +#include #include -#include +#include #include #include #ifdef DISPLAY_CLASS diff --git a/variants/lilygo_tbeam_supreme_SX1262/target.h b/variants/lilygo_tbeam_supreme_SX1262/target.h index ea1138af..62a92329 100644 --- a/variants/lilygo_tbeam_supreme_SX1262/target.h +++ b/variants/lilygo_tbeam_supreme_SX1262/target.h @@ -2,9 +2,9 @@ #define RADIOLIB_STATIC_ONLY 1 #include -#include +#include #include -#include +#include #include #include diff --git a/variants/lilygo_tlora_c6/target.h b/variants/lilygo_tlora_c6/target.h index eef923ab..c26d5958 100644 --- a/variants/lilygo_tlora_c6/target.h +++ b/variants/lilygo_tlora_c6/target.h @@ -2,9 +2,9 @@ #define RADIOLIB_STATIC_ONLY 1 #include -#include +#include #include -#include +#include #include #include diff --git a/variants/lilygo_tlora_v2_1/target.h b/variants/lilygo_tlora_v2_1/target.h index 8e48c3e7..f05b8055 100644 --- a/variants/lilygo_tlora_v2_1/target.h +++ b/variants/lilygo_tlora_v2_1/target.h @@ -2,9 +2,9 @@ #define RADIOLIB_STATIC_ONLY 1 #include -#include +#include #include -#include +#include #include #include #include diff --git a/variants/meshadventurer/target.h b/variants/meshadventurer/target.h index 0e0235ba..6aeaf079 100644 --- a/variants/meshadventurer/target.h +++ b/variants/meshadventurer/target.h @@ -2,10 +2,10 @@ #define RADIOLIB_STATIC_ONLY 1 #include -#include +#include #include -#include -#include +#include +#include #include #include #include diff --git a/variants/minewsemi_me25ls01/target.h b/variants/minewsemi_me25ls01/target.h index db832f91..a5da5823 100644 --- a/variants/minewsemi_me25ls01/target.h +++ b/variants/minewsemi_me25ls01/target.h @@ -2,9 +2,9 @@ #define RADIOLIB_STATIC_ONLY 1 #include -#include +#include #include -#include +#include #include #include #include diff --git a/variants/nano_g2_ultra/target.h b/variants/nano_g2_ultra/target.h index e0d891e9..5cde6405 100644 --- a/variants/nano_g2_ultra/target.h +++ b/variants/nano_g2_ultra/target.h @@ -3,8 +3,8 @@ #define RADIOLIB_STATIC_ONLY 1 #include #include "nano-g2.h" -#include -#include +#include +#include #include #include #ifdef DISPLAY_CLASS @@ -12,8 +12,7 @@ #endif #include -class NanoG2UltraSensorManager : public SensorManager -{ +class NanoG2UltraSensorManager : public SensorManager { bool gps_active = false; LocationProvider *_location; diff --git a/variants/picow/target.h b/variants/picow/target.h index a89b5ba8..7b1e7437 100644 --- a/variants/picow/target.h +++ b/variants/picow/target.h @@ -2,9 +2,9 @@ #define RADIOLIB_STATIC_ONLY 1 #include -#include +#include #include -#include +#include #include #include diff --git a/variants/promicro/target.h b/variants/promicro/target.h index 7646d459..de2719e6 100644 --- a/variants/promicro/target.h +++ b/variants/promicro/target.h @@ -2,10 +2,9 @@ #define RADIOLIB_STATIC_ONLY 1 #include -#include +#include #include -#include -#include +#include #include #ifdef DISPLAY_CLASS #include @@ -27,4 +26,3 @@ uint32_t radio_get_rng_seed(); void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr); void radio_set_tx_power(uint8_t dbm); mesh::LocalIdentity radio_new_identity(); - diff --git a/variants/rak3x72/target.h b/variants/rak3x72/target.h index 6c86a702..61e4747d 100644 --- a/variants/rak3x72/target.h +++ b/variants/rak3x72/target.h @@ -2,9 +2,9 @@ #define RADIOLIB_STATIC_ONLY 1 #include -#include +#include #include -#include +#include #include #include @@ -22,7 +22,7 @@ public: uint32_t raw = 0; for (int i=0; i<8;i++) { raw += analogRead(PIN_VBAT_READ); - } + } return ((double)raw) * ADC_MULTIPLIER / 8 / 4096; } }; diff --git a/variants/rak4631/target.h b/variants/rak4631/target.h index e1545581..d1278a97 100644 --- a/variants/rak4631/target.h +++ b/variants/rak4631/target.h @@ -2,9 +2,9 @@ #define RADIOLIB_STATIC_ONLY 1 #include -#include +#include #include -#include +#include #include #include #if ENV_INCLUDE_GPS diff --git a/variants/sensecap_solar/target.h b/variants/sensecap_solar/target.h index 63506951..90d60ba5 100644 --- a/variants/sensecap_solar/target.h +++ b/variants/sensecap_solar/target.h @@ -2,9 +2,9 @@ #define RADIOLIB_STATIC_ONLY 1 #include -#include +#include #include -#include +#include #include #include #include diff --git a/variants/station_g2/target.h b/variants/station_g2/target.h index 695e700a..6d80f098 100644 --- a/variants/station_g2/target.h +++ b/variants/station_g2/target.h @@ -2,9 +2,9 @@ #define RADIOLIB_STATIC_ONLY 1 #include -#include +#include #include -#include +#include #include #include diff --git a/variants/t1000-e/target.h b/variants/t1000-e/target.h index 855c4d67..6ac0d3a6 100644 --- a/variants/t1000-e/target.h +++ b/variants/t1000-e/target.h @@ -2,9 +2,9 @@ #define RADIOLIB_STATIC_ONLY 1 #include -#include +#include #include "T1000eBoard.h" -#include +#include #include #include #include diff --git a/variants/t114/target.h b/variants/t114/target.h index 0f6ebaa1..8831d9f7 100644 --- a/variants/t114/target.h +++ b/variants/t114/target.h @@ -2,9 +2,9 @@ #define RADIOLIB_STATIC_ONLY 1 #include -#include +#include #include -#include +#include #include #include #include diff --git a/variants/techo/target.h b/variants/techo/target.h index 8fb8b6ca..7c05e742 100644 --- a/variants/techo/target.h +++ b/variants/techo/target.h @@ -2,9 +2,9 @@ #define RADIOLIB_STATIC_ONLY 1 #include -#include +#include #include -#include +#include #include #include #include diff --git a/variants/tenstar_c3/target.h b/variants/tenstar_c3/target.h index 0aea87dd..fa29e52b 100644 --- a/variants/tenstar_c3/target.h +++ b/variants/tenstar_c3/target.h @@ -2,10 +2,10 @@ #define RADIOLIB_STATIC_ONLY 1 #include -#include +#include #include -#include -#include +#include +#include #include #include diff --git a/variants/thinknode_m1/target.h b/variants/thinknode_m1/target.h index 550ee62a..c938d422 100644 --- a/variants/thinknode_m1/target.h +++ b/variants/thinknode_m1/target.h @@ -2,9 +2,9 @@ #define RADIOLIB_STATIC_ONLY 1 #include -#include +#include #include -#include +#include #include #include #include diff --git a/variants/waveshare_rp2040_lora/target.h b/variants/waveshare_rp2040_lora/target.h index 3ebe0570..149b9469 100644 --- a/variants/waveshare_rp2040_lora/target.h +++ b/variants/waveshare_rp2040_lora/target.h @@ -4,8 +4,8 @@ #include #include -#include -#include +#include +#include #include #include diff --git a/variants/wio-e5-dev/target.h b/variants/wio-e5-dev/target.h index 83fd9cf1..5fdd0aba 100644 --- a/variants/wio-e5-dev/target.h +++ b/variants/wio-e5-dev/target.h @@ -2,9 +2,9 @@ #define RADIOLIB_STATIC_ONLY 1 #include -#include +#include #include -#include +#include #include #include @@ -16,7 +16,7 @@ public: // Just returns ADC value for now to test adc uint16_t getBattMilliVolts() override { - uint32_t raw = analogRead(PIN_A3); + uint32_t raw = analogRead(PIN_A3); return raw; } }; diff --git a/variants/wio-e5-mini/target.h b/variants/wio-e5-mini/target.h index d0ad24b7..921c38d3 100644 --- a/variants/wio-e5-mini/target.h +++ b/variants/wio-e5-mini/target.h @@ -2,9 +2,9 @@ #define RADIOLIB_STATIC_ONLY 1 #include -#include +#include #include -#include +#include #include #include #ifdef DISPLAY_CLASS @@ -37,14 +37,14 @@ public: uint32_t raw = 0; for (int i=0; i<8;i++) { raw += analogRead(PIN_A3); - } + } return ((double)raw) * 1.73 * 5 * 1000 / 8 / 4096; } }; class WIOE5SensorManager : public SensorManager { BME280I2C bme; - bool has_bme = false; + bool has_bme = false; public: WIOE5SensorManager() {} diff --git a/variants/wio-tracker-l1/target.h b/variants/wio-tracker-l1/target.h index 0aac6c59..ab42b7b5 100644 --- a/variants/wio-tracker-l1/target.h +++ b/variants/wio-tracker-l1/target.h @@ -2,9 +2,9 @@ #define RADIOLIB_STATIC_ONLY 1 #include -#include +#include #include -#include +#include #include #include #ifdef DISPLAY_CLASS diff --git a/variants/xiao_c3/target.h b/variants/xiao_c3/target.h index 0aea87dd..fa29e52b 100644 --- a/variants/xiao_c3/target.h +++ b/variants/xiao_c3/target.h @@ -2,10 +2,10 @@ #define RADIOLIB_STATIC_ONLY 1 #include -#include +#include #include -#include -#include +#include +#include #include #include diff --git a/variants/xiao_c6/target.h b/variants/xiao_c6/target.h index eef923ab..c26d5958 100644 --- a/variants/xiao_c6/target.h +++ b/variants/xiao_c6/target.h @@ -2,9 +2,9 @@ #define RADIOLIB_STATIC_ONLY 1 #include -#include +#include #include -#include +#include #include #include diff --git a/variants/xiao_nrf52/target.h b/variants/xiao_nrf52/target.h index c8c6a42a..86f546b8 100644 --- a/variants/xiao_nrf52/target.h +++ b/variants/xiao_nrf52/target.h @@ -2,9 +2,9 @@ #define RADIOLIB_STATIC_ONLY 1 #include -#include +#include #include -#include +#include #include #include #include diff --git a/variants/xiao_rp2040/target.h b/variants/xiao_rp2040/target.h index 6a3c192b..34861db3 100644 --- a/variants/xiao_rp2040/target.h +++ b/variants/xiao_rp2040/target.h @@ -4,8 +4,8 @@ #include #include -#include -#include +#include +#include #include #include diff --git a/variants/xiao_s3_wio/target.h b/variants/xiao_s3_wio/target.h index 609248ae..b768b2b0 100644 --- a/variants/xiao_s3_wio/target.h +++ b/variants/xiao_s3_wio/target.h @@ -2,9 +2,9 @@ #define RADIOLIB_STATIC_ONLY 1 #include -#include +#include #include -#include +#include #include #include #ifdef DISPLAY_CLASS From 57f93a4196068ea27e2d55f00f2ec28377de86fd Mon Sep 17 00:00:00 2001 From: taco Date: Wed, 16 Jul 2025 10:35:48 +1000 Subject: [PATCH 03/24] add GPS UI alert to quad-press --- examples/companion_radio/UITask.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/examples/companion_radio/UITask.cpp b/examples/companion_radio/UITask.cpp index 1eb5be8e..a7f03a26 100644 --- a/examples/companion_radio/UITask.cpp +++ b/examples/companion_radio/UITask.cpp @@ -408,8 +408,12 @@ void UITask::handleButtonQuadruplePress() { if (strcmp(_sensors->getSettingName(i), "gps") == 0) { if (strcmp(_sensors->getSettingValue(i), "1") == 0) { _sensors->setSettingValue("gps", "0"); + soundBuzzer(UIEventType::ack); + sprintf(_alert, "GPS: Disabled"); } else { _sensors->setSettingValue("gps", "1"); + soundBuzzer(UIEventType::ack); + sprintf(_alert, "GPS: Enabled"); } break; } From 3f996ef4fcb2d3a56f95a106dafba12b56663057 Mon Sep 17 00:00:00 2001 From: taco Date: Wed, 16 Jul 2025 12:25:16 +1000 Subject: [PATCH 04/24] fix: Wio Tracker L1: add DIO2 as rfSwitch and correct TCXO voltage. --- variants/wio-tracker-l1/variant.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/variants/wio-tracker-l1/variant.h b/variants/wio-tracker-l1/variant.h index 7f51dcb8..9d6eb532 100644 --- a/variants/wio-tracker-l1/variant.h +++ b/variants/wio-tracker-l1/variant.h @@ -70,6 +70,8 @@ #define P_LORA_NSS (4) #define SX126X_RXEN (5) #define SX126X_TXEN RADIOLIB_NC +#define SX126X_DIO2_AS_RF_SWITCH true +#define SX126X_DIO3_TCXO_VOLTAGE (1.8f) // Wire Interfaces #define WIRE_INTERFACES_COUNT (2) From d15b374c29b62db14adb29b2caf8074087b796cb Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Wed, 16 Jul 2025 14:18:05 +1000 Subject: [PATCH 05/24] * Sensor permission levels renamed. Misc sensor fixes. --- examples/simple_sensor/SensorMesh.cpp | 19 +++++++++++-------- examples/simple_sensor/SensorMesh.h | 18 +++++++++--------- 2 files changed, 20 insertions(+), 17 deletions(-) diff --git a/examples/simple_sensor/SensorMesh.cpp b/examples/simple_sensor/SensorMesh.cpp index 5bbe9f4d..bfc0b0d1 100644 --- a/examples/simple_sensor/SensorMesh.cpp +++ b/examples/simple_sensor/SensorMesh.cpp @@ -243,7 +243,7 @@ static uint8_t putFloat(uint8_t * dest, float value, uint8_t size, uint32_t mult uint8_t SensorMesh::handleRequest(uint8_t perms, uint32_t sender_timestamp, uint8_t req_type, uint8_t* payload, size_t payload_len) { memcpy(reply_data, &sender_timestamp, 4); // reflect sender_timestamp back in response packet (kind of like a 'tag') - if (req_type == REQ_TYPE_GET_TELEMETRY_DATA && (perms & PERM_GET_TELEMETRY) != 0) { + if (req_type == REQ_TYPE_GET_TELEMETRY_DATA) { // allow all telemetry.reset(); telemetry.addVoltage(TELEM_CHANNEL_SELF, (float)board.getBattMilliVolts() / 1000.0f); // query other sensors -- target specific @@ -254,7 +254,7 @@ uint8_t SensorMesh::handleRequest(uint8_t perms, uint32_t sender_timestamp, uint memcpy(&reply_data[4], telemetry.getBuffer(), tlen); return 4 + tlen; // reply_len } - if (req_type == REQ_TYPE_GET_AVG_MIN_MAX && (perms & PERM_GET_OTHER_STATS) != 0) { + if (req_type == REQ_TYPE_GET_AVG_MIN_MAX && (perms & PERM_ACL_ROLE_MASK) >= PERM_ACL_READ_ONLY) { uint32_t start_secs_ago, end_secs_ago; memcpy(&start_secs_ago, &payload[0], 4); memcpy(&end_secs_ago, &payload[4], 4); @@ -288,13 +288,14 @@ uint8_t SensorMesh::handleRequest(uint8_t perms, uint32_t sender_timestamp, uint } return ofs; } - if (req_type == REQ_TYPE_GET_ACCESS_LIST && (perms & PERM_ACL_ROLE_MASK) == PERM_ACL_LEVEL3) { + if (req_type == REQ_TYPE_GET_ACCESS_LIST && (perms & PERM_ACL_ROLE_MASK) == PERM_ACL_ADMIN) { uint8_t res1 = payload[0]; // reserved for future (extra query params) uint8_t res2 = payload[1]; if (res1 == 0 && res2 == 0) { uint8_t ofs = 4; for (int i = 0; i < num_contacts && ofs + 7 <= sizeof(reply_data) - 4; i++) { auto c = &contacts[i]; + if (c->permissions == 0) continue; // skip deleted entries memcpy(&reply_data[ofs], c->id.pub_key, 6); ofs += 6; // just 6-byte pub_key prefix reply_data[ofs++] = c->permissions; } @@ -315,7 +316,7 @@ mesh::Packet* SensorMesh::createSelfAdvert() { return createAdvert(self_id, app_data, app_data_len); } -ContactInfo* SensorMesh::putContact(const mesh::Identity& id) { +ContactInfo* SensorMesh::putContact(const mesh::Identity& id, uint8_t init_perms) { uint32_t min_time = 0xFFFFFFFF; ContactInfo* oldest = &contacts[MAX_CONTACTS - 1]; for (int i = 0; i < num_contacts; i++) { @@ -333,6 +334,7 @@ ContactInfo* SensorMesh::putContact(const mesh::Identity& id) { c = oldest; // evict least active contact } memset(c, 0, sizeof(*c)); + c->permissions = init_perms; c->id = id; c->out_path_len = -1; // initially out_path is unknown return c; @@ -340,7 +342,7 @@ ContactInfo* SensorMesh::putContact(const mesh::Identity& id) { void SensorMesh::applyContactPermissions(const uint8_t* pubkey, uint8_t perms) { mesh::Identity id(pubkey); - auto c = putContact(id); + auto c = putContact(id, 0); if ((perms & PERM_ACL_ROLE_MASK) == PERM_ACL_GUEST) { // guest role is not persisted in contacts memset(c, 0, sizeof(*c)); @@ -441,7 +443,7 @@ uint8_t SensorMesh::handleLoginReq(const mesh::Identity& sender, const uint8_t* return 0; } - auto client = putContact(sender); // add to contacts (if not already known) + auto client = putContact(sender, PERM_RECV_ALERTS_HI | PERM_RECV_ALERTS_LO); // add to contacts (if not already known) if (sender_timestamp <= client->last_timestamp) { MESH_DEBUG_PRINTLN("Possible login replay attack!"); return 0; // FATAL: client table is full -OR- replay attack @@ -450,7 +452,7 @@ uint8_t SensorMesh::handleLoginReq(const mesh::Identity& sender, const uint8_t* MESH_DEBUG_PRINTLN("Login success!"); client->last_timestamp = sender_timestamp; client->last_activity = getRTCClock()->getCurrentTime(); - client->permissions = PERM_ACL_LEVEL3 | PERM_RECV_ALERTS_HI | PERM_RECV_ALERTS_LO; // initially opt-in to receive alerts (can opt out) + client->permissions |= PERM_ACL_ADMIN; memcpy(client->shared_secret, secret, PUB_KEY_SIZE); dirty_contacts_expiry = futureMillis(LAZY_CONTACTS_WRITE_DELAY); @@ -502,6 +504,7 @@ void SensorMesh::handleCommand(uint32_t sender_timestamp, char* command, char* r Serial.println("ACL:"); for (int i = 0; i < num_contacts; i++) { auto c = &contacts[i]; + if (c->permissions == 0) continue; // skip deleted entries Serial.printf("%02X ", c->permissions); mesh::Utils::printHex(Serial, c->id.pub_key, PUB_KEY_SIZE); @@ -569,7 +572,7 @@ void SensorMesh::onPeerDataRecv(mesh::Packet* packet, uint8_t type, int sender_i memcpy(×tamp, data, 4); if (timestamp > from.last_timestamp) { // prevent replay attacks - uint8_t reply_len = handleRequest(from.isAdmin() ? 0xFFFF : from.permissions, timestamp, data[4], &data[5], len - 5); + uint8_t reply_len = handleRequest(from.isAdmin() ? 0xFF : from.permissions, timestamp, data[4], &data[5], len - 5); if (reply_len == 0) return; // invalid command from.last_timestamp = timestamp; diff --git a/examples/simple_sensor/SensorMesh.h b/examples/simple_sensor/SensorMesh.h index a357506f..84f803f1 100644 --- a/examples/simple_sensor/SensorMesh.h +++ b/examples/simple_sensor/SensorMesh.h @@ -25,14 +25,14 @@ #define PERM_ACL_ROLE_MASK 3 // lower 2 bits #define PERM_ACL_GUEST 0 -#define PERM_ACL_LEVEL1 1 -#define PERM_ACL_LEVEL2 2 -#define PERM_ACL_LEVEL3 3 // admin +#define PERM_ACL_READ_ONLY 1 +#define PERM_ACL_READ_WRITE 2 +#define PERM_ACL_ADMIN 3 -#define PERM_GET_TELEMETRY (1 << 2) -#define PERM_GET_OTHER_STATS (1 << 3) -#define PERM_RESERVED1 (1 << 4) -#define PERM_RESERVED2 (1 << 5) +#define PERM_RESERVED1 (1 << 2) +#define PERM_RESERVED2 (1 << 3) +#define PERM_RESERVED3 (1 << 4) +#define PERM_RESERVED4 (1 << 5) #define PERM_RECV_ALERTS_LO (1 << 6) // low priority alerts #define PERM_RECV_ALERTS_HI (1 << 7) // high priority alerts @@ -45,7 +45,7 @@ struct ContactInfo { uint32_t last_timestamp; // by THEIR clock (transient) uint32_t last_activity; // by OUR clock (transient) - bool isAdmin() const { return (permissions & PERM_ACL_ROLE_MASK) == PERM_ACL_LEVEL3; } + bool isAdmin() const { return (permissions & PERM_ACL_ROLE_MASK) == PERM_ACL_ADMIN; } }; #ifndef FIRMWARE_BUILD_DATE @@ -160,7 +160,7 @@ private: uint8_t handleLoginReq(const mesh::Identity& sender, const uint8_t* secret, uint32_t sender_timestamp, const uint8_t* data); uint8_t handleRequest(uint8_t perms, uint32_t sender_timestamp, uint8_t req_type, uint8_t* payload, size_t payload_len); mesh::Packet* createSelfAdvert(); - ContactInfo* putContact(const mesh::Identity& id); + ContactInfo* putContact(const mesh::Identity& id, uint8_t init_perms); void applyContactPermissions(const uint8_t* pubkey, uint8_t perms); void sendAlert(ContactInfo* c, Trigger* t); From 6bc8dd28d438ea5a1d59cefdfec121f6f88baf69 Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Wed, 16 Jul 2025 18:51:18 +1000 Subject: [PATCH 06/24] * CommonCLI: new "multi.acks" config setting --- examples/simple_repeater/main.cpp | 3 ++ examples/simple_room_server/main.cpp | 15 ++++++-- src/Mesh.cpp | 57 ++++++++-------------------- src/Mesh.h | 5 +++ src/helpers/CommonCLI.cpp | 11 +++++- src/helpers/CommonCLI.h | 2 +- 6 files changed, 44 insertions(+), 49 deletions(-) diff --git a/examples/simple_repeater/main.cpp b/examples/simple_repeater/main.cpp index 2ec94c4b..cee3a008 100644 --- a/examples/simple_repeater/main.cpp +++ b/examples/simple_repeater/main.cpp @@ -339,6 +339,9 @@ protected: int getAGCResetInterval() const override { return ((int)_prefs.agc_reset_interval) * 4000; // milliseconds } + uint8_t getExtraAckTransmitCount() const override { + return _prefs.multi_acks; + } void onAnonDataRecv(mesh::Packet* packet, const uint8_t* secret, const mesh::Identity& sender, uint8_t* data, size_t len) override { if (packet->getPayloadType() == PAYLOAD_TYPE_ANON_REQ) { // received an initial request by a possible admin client (unknown at this stage) diff --git a/examples/simple_room_server/main.cpp b/examples/simple_room_server/main.cpp index 87038ac7..1ab08d9b 100644 --- a/examples/simple_room_server/main.cpp +++ b/examples/simple_room_server/main.cpp @@ -424,6 +424,9 @@ protected: int getAGCResetInterval() const override { return ((int)_prefs.agc_reset_interval) * 4000; // milliseconds } + uint8_t getExtraAckTransmitCount() const override { + return _prefs.multi_acks; + } bool allowPacketForward(const mesh::Packet* packet) override { if (_prefs.disable_fwd) return false; @@ -583,12 +586,16 @@ protected: if (ack) sendFlood(ack, TXT_ACK_DELAY); delay_millis = TXT_ACK_DELAY + REPLY_DELAY_MILLIS; } else { - mesh::Packet* a1 = createMultiAck(ack_hash, 1); - if (a1) sendDirect(a1, client->out_path, client->out_path_len, TXT_ACK_DELAY); + uint32_t d = TXT_ACK_DELAY; + if (getExtraAckTransmitCount() > 0) { + mesh::Packet* a1 = createMultiAck(ack_hash, 1); + if (a1) sendDirect(a1, client->out_path, client->out_path_len, d); + d += 300; + } mesh::Packet* a2 = createAck(ack_hash); - if (a2) sendDirect(a2, client->out_path, client->out_path_len, TXT_ACK_DELAY + 300); - delay_millis = TXT_ACK_DELAY + REPLY_DELAY_MILLIS + 300; + if (a2) sendDirect(a2, client->out_path, client->out_path_len, d); + delay_millis = d + REPLY_DELAY_MILLIS; } } else { delay_millis = 0; diff --git a/src/Mesh.cpp b/src/Mesh.cpp index 0d9b88c7..b055d811 100644 --- a/src/Mesh.cpp +++ b/src/Mesh.cpp @@ -22,6 +22,9 @@ uint32_t Mesh::getRetransmitDelay(const mesh::Packet* packet) { uint32_t Mesh::getDirectRetransmitDelay(const Packet* packet) { return 0; // by default, no delay } +uint8_t Mesh::getExtraAckTransmitCount() const { + return 0; +} uint32_t Mesh::getCADFailRetryDelay() const { return _rng->nextInt(1, 4)*120; @@ -99,7 +102,6 @@ DispatcherAction Mesh::onRecvPacket(Packet* pkt) { } else if (!_tables->hasSeen(pkt)) { onAckRecv(pkt, ack_crc); action = routeRecvPacket(pkt); - // routeRecvAcks(pkt, 0); // experimental, double Acks in flood mode(?) } break; } @@ -280,7 +282,6 @@ DispatcherAction Mesh::onRecvPacket(Packet* pkt) { memcpy(&ack_crc, tmp.payload, 4); onAckRecv(&tmp, ack_crc); - // routeRecvAcks(&tmp, ((uint32_t)remaining) * 600); // expect multipart ACK 300ms apart (x2) //action = routeRecvPacket(&tmp); // NOTE: currently not needed, as multipart ACKs not sent Flood } } else { @@ -324,37 +325,6 @@ DispatcherAction Mesh::routeRecvPacket(Packet* packet) { return ACTION_RELEASE; } -#if 0 -void Mesh::routeRecvAcks(Packet* packet, uint32_t delay_millis) { - if (packet->isRouteFlood() && !packet->isMarkedDoNotRetransmit() - && packet->path_len + PATH_HASH_SIZE <= MAX_PATH_SIZE && allowPacketForward(packet)) { - // append this node's hash to 'path' - packet->path_len += self_id.copyHashTo(&packet->path[packet->path_len]); - - uint32_t crc; - memcpy(&crc, packet->payload, 4); - - delay_millis += getRetransmitDelay(packet); - auto a1 = createMultiAck(crc, 1); - if (a1) { - memcpy(a1->path, packet->path, a1->path_len = packet->path_len); - a1->header &= ~PH_ROUTE_MASK; - a1->header |= ROUTE_TYPE_FLOOD; - sendPacket(a1, 1, delay_millis); - } - - delay_millis += 300; - auto a2 = createAck(crc); - if (a2) { - memcpy(a2->path, packet->path, a2->path_len = packet->path_len); - a2->header &= ~PH_ROUTE_MASK; - a2->header |= ROUTE_TYPE_FLOOD; - sendPacket(a2, 1, delay_millis); - } - } -} -#endif - DispatcherAction Mesh::forwardMultipartDirect(Packet* pkt) { uint8_t remaining = pkt->payload[0] >> 4; // num of packets in this multipart sequence still to be sent uint8_t type = pkt->payload[0] & 0x0F; @@ -369,7 +339,7 @@ DispatcherAction Mesh::forwardMultipartDirect(Packet* pkt) { if (!_tables->hasSeen(&tmp)) { // don't retransmit! removeSelfFromPath(&tmp); - routeDirectRecvAcks(&tmp, ((uint32_t)remaining) * 600); // expect multipart ACKs 300ms apart (x2) + routeDirectRecvAcks(&tmp, ((uint32_t)remaining + 1) * 300); // expect multipart ACKs 300ms apart (x2) } } return ACTION_RELEASE; @@ -380,16 +350,19 @@ void Mesh::routeDirectRecvAcks(Packet* packet, uint32_t delay_millis) { uint32_t crc; memcpy(&crc, packet->payload, 4); - delay_millis += getDirectRetransmitDelay(packet); - auto a1 = createMultiAck(crc, 1); - if (a1) { - memcpy(a1->path, packet->path, a1->path_len = packet->path_len); - a1->header &= ~PH_ROUTE_MASK; - a1->header |= ROUTE_TYPE_DIRECT; - sendPacket(a1, 0, delay_millis); + uint8_t extra = getExtraAckTransmitCount(); + while (extra > 0) { + delay_millis += getDirectRetransmitDelay(packet) + 300; + auto a1 = createMultiAck(crc, extra); + if (a1) { + memcpy(a1->path, packet->path, a1->path_len = packet->path_len); + a1->header &= ~PH_ROUTE_MASK; + a1->header |= ROUTE_TYPE_DIRECT; + sendPacket(a1, 0, delay_millis); + } + extra--; } - delay_millis += 300; auto a2 = createAck(crc); if (a2) { memcpy(a2->path, packet->path, a2->path_len = packet->path_len); diff --git a/src/Mesh.h b/src/Mesh.h index da734572..a8fdb2a4 100644 --- a/src/Mesh.h +++ b/src/Mesh.h @@ -59,6 +59,11 @@ protected: */ virtual uint32_t getDirectRetransmitDelay(const Packet* packet); + /** + * \returns number of extra (Direct) ACK transmissions wanted. + */ + virtual uint8_t getExtraAckTransmitCount() const; + /** * \brief Perform search of local DB of peers/contacts. * \returns Number of peers with matching hash diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index df510c28..afb7dfbc 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -51,7 +51,7 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { file.read((uint8_t *) &_prefs->sf, sizeof(_prefs->sf)); // 112 file.read((uint8_t *) &_prefs->cr, sizeof(_prefs->cr)); // 113 file.read((uint8_t *) &_prefs->allow_read_only, sizeof(_prefs->allow_read_only)); // 114 - file.read((uint8_t *) &_prefs->reserved2, sizeof(_prefs->reserved2)); // 115 + file.read((uint8_t *) &_prefs->multi_acks, sizeof(_prefs->multi_acks)); // 115 file.read((uint8_t *) &_prefs->bw, sizeof(_prefs->bw)); // 116 file.read((uint8_t *) &_prefs->agc_reset_interval, sizeof(_prefs->agc_reset_interval)); // 120 file.read(pad, 3); // 121 @@ -69,6 +69,7 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { _prefs->sf = constrain(_prefs->sf, 7, 12); _prefs->cr = constrain(_prefs->cr, 5, 8); _prefs->tx_power_dbm = constrain(_prefs->tx_power_dbm, 1, 30); + _prefs->multi_acks = constrain(_prefs->multi_acks, 0, 1); file.close(); } @@ -106,7 +107,7 @@ void CommonCLI::savePrefs(FILESYSTEM* fs) { file.write((uint8_t *) &_prefs->sf, sizeof(_prefs->sf)); // 112 file.write((uint8_t *) &_prefs->cr, sizeof(_prefs->cr)); // 113 file.write((uint8_t *) &_prefs->allow_read_only, sizeof(_prefs->allow_read_only)); // 114 - file.write((uint8_t *) &_prefs->reserved2, sizeof(_prefs->reserved2)); // 115 + file.write((uint8_t *) &_prefs->multi_acks, sizeof(_prefs->multi_acks)); // 115 file.write((uint8_t *) &_prefs->bw, sizeof(_prefs->bw)); // 116 file.write((uint8_t *) &_prefs->agc_reset_interval, sizeof(_prefs->agc_reset_interval)); // 120 file.write(pad, 3); // 121 @@ -180,6 +181,8 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch sprintf(reply, "> %d", (uint32_t) _prefs->interference_threshold); } else if (memcmp(config, "agc.reset.interval", 18) == 0) { sprintf(reply, "> %d", ((uint32_t) _prefs->agc_reset_interval) * 4); + } else if (memcmp(config, "multi.acks", 10) == 0) { + sprintf(reply, "> %d", (uint32_t) _prefs->multi_acks); } else if (memcmp(config, "allow.read.only", 15) == 0) { sprintf(reply, "> %s", _prefs->allow_read_only ? "on" : "off"); } else if (memcmp(config, "flood.advert.interval", 21) == 0) { @@ -235,6 +238,10 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch _prefs->agc_reset_interval = atoi(&config[19]) / 4; savePrefs(); strcpy(reply, "OK"); + } else if (memcmp(config, "multi.acks ", 11) == 0) { + _prefs->multi_acks = atoi(&config[11]); + savePrefs(); + strcpy(reply, "OK"); } else if (memcmp(config, "allow.read.only ", 16) == 0) { _prefs->allow_read_only = memcmp(&config[16], "on", 2) == 0; savePrefs(); diff --git a/src/helpers/CommonCLI.h b/src/helpers/CommonCLI.h index b50bf7f3..91a5bd3b 100644 --- a/src/helpers/CommonCLI.h +++ b/src/helpers/CommonCLI.h @@ -21,7 +21,7 @@ struct NodePrefs { // persisted to file uint8_t sf; uint8_t cr; uint8_t allow_read_only; - uint8_t reserved2; + uint8_t multi_acks; float bw; uint8_t flood_max; uint8_t interference_threshold; From 5881b04a31b9d75ac565da262a16eca8e25a3519 Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Wed, 16 Jul 2025 19:25:28 +1000 Subject: [PATCH 07/24] * companion: optional double ACKs, new prefs.multi_acks --- examples/companion_radio/DataStore.cpp | 10 ++++++---- examples/companion_radio/MyMesh.cpp | 9 ++++++++- examples/companion_radio/MyMesh.h | 1 + examples/companion_radio/NodePrefs.h | 2 +- src/helpers/BaseChatMesh.cpp | 10 +++++++--- 5 files changed, 23 insertions(+), 9 deletions(-) diff --git a/examples/companion_radio/DataStore.cpp b/examples/companion_radio/DataStore.cpp index 5c19d7a1..b5d70edc 100644 --- a/examples/companion_radio/DataStore.cpp +++ b/examples/companion_radio/DataStore.cpp @@ -154,7 +154,7 @@ void DataStore::loadPrefsInt(const char *filename, NodePrefs& _prefs, double& no file.read((uint8_t *)&_prefs.freq, sizeof(_prefs.freq)); // 56 file.read((uint8_t *)&_prefs.sf, sizeof(_prefs.sf)); // 60 file.read((uint8_t *)&_prefs.cr, sizeof(_prefs.cr)); // 61 - file.read((uint8_t *)&_prefs.reserved1, sizeof(_prefs.reserved1)); // 62 + file.read(pad, 1); // 62 file.read((uint8_t *)&_prefs.manual_add_contacts, sizeof(_prefs.manual_add_contacts)); // 63 file.read((uint8_t *)&_prefs.bw, sizeof(_prefs.bw)); // 64 file.read((uint8_t *)&_prefs.tx_power_dbm, sizeof(_prefs.tx_power_dbm)); // 68 @@ -163,7 +163,8 @@ void DataStore::loadPrefsInt(const char *filename, NodePrefs& _prefs, double& no file.read((uint8_t *)&_prefs.telemetry_mode_env, sizeof(_prefs.telemetry_mode_env)); // 71 file.read((uint8_t *)&_prefs.rx_delay_base, sizeof(_prefs.rx_delay_base)); // 72 file.read((uint8_t *)&_prefs.advert_loc_policy, sizeof(_prefs.advert_loc_policy)); // 76 - file.read(pad, 3); // 77 + file.read((uint8_t *)&_prefs.multi_acks, sizeof(_prefs.multi_acks)); // 77 + file.read(pad, 2); // 78 file.read((uint8_t *)&_prefs.ble_pin, sizeof(_prefs.ble_pin)); // 80 file.close(); @@ -184,7 +185,7 @@ void DataStore::savePrefs(const NodePrefs& _prefs, double node_lat, double node_ file.write((uint8_t *)&_prefs.freq, sizeof(_prefs.freq)); // 56 file.write((uint8_t *)&_prefs.sf, sizeof(_prefs.sf)); // 60 file.write((uint8_t *)&_prefs.cr, sizeof(_prefs.cr)); // 61 - file.write((uint8_t *)&_prefs.reserved1, sizeof(_prefs.reserved1)); // 62 + file.write(pad, 1); // 62 file.write((uint8_t *)&_prefs.manual_add_contacts, sizeof(_prefs.manual_add_contacts)); // 63 file.write((uint8_t *)&_prefs.bw, sizeof(_prefs.bw)); // 64 file.write((uint8_t *)&_prefs.tx_power_dbm, sizeof(_prefs.tx_power_dbm)); // 68 @@ -193,7 +194,8 @@ void DataStore::savePrefs(const NodePrefs& _prefs, double node_lat, double node_ file.write((uint8_t *)&_prefs.telemetry_mode_env, sizeof(_prefs.telemetry_mode_env)); // 71 file.write((uint8_t *)&_prefs.rx_delay_base, sizeof(_prefs.rx_delay_base)); // 72 file.write((uint8_t *)&_prefs.advert_loc_policy, sizeof(_prefs.advert_loc_policy)); // 76 - file.write(pad, 3); // 77 + file.write((uint8_t *)&_prefs.multi_acks, sizeof(_prefs.multi_acks)); // 77 + file.write(pad, 2); // 78 file.write((uint8_t *)&_prefs.ble_pin, sizeof(_prefs.ble_pin)); // 80 file.close(); diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp index 9331a50c..939c93de 100644 --- a/examples/companion_radio/MyMesh.cpp +++ b/examples/companion_radio/MyMesh.cpp @@ -210,6 +210,10 @@ int MyMesh::calcRxDelay(float score, uint32_t air_time) const { return (int)((pow(_prefs.rx_delay_base, 0.85f - score) - 1.0) * air_time); } +uint8_t MyMesh::getExtraAckTransmitCount() const { + return _prefs.multi_acks; +} + void MyMesh::logRxRaw(float snr, float rssi, const uint8_t raw[], int len) { if (_serial->isConnected() && len + 3 <= MAX_FRAME_SIZE) { int i = 0; @@ -719,7 +723,7 @@ void MyMesh::handleCmdFrame(size_t len) { i += 4; memcpy(&out_frame[i], &lon, 4); i += 4; - out_frame[i++] = 0; // reserved + out_frame[i++] = _prefs.multi_acks; // new v7+ out_frame[i++] = _prefs.advert_loc_policy; out_frame[i++] = (_prefs.telemetry_mode_env << 4) | (_prefs.telemetry_mode_loc << 2) | (_prefs.telemetry_mode_base); // v5+ @@ -1050,6 +1054,9 @@ void MyMesh::handleCmdFrame(size_t len) { if (len >= 4) { _prefs.advert_loc_policy = cmd_frame[3]; + if (len >= 5) { + _prefs.multi_acks = cmd_frame[4]; + } } } savePrefs(); diff --git a/examples/companion_radio/MyMesh.h b/examples/companion_radio/MyMesh.h index 81bf261e..cd4a72dc 100644 --- a/examples/companion_radio/MyMesh.h +++ b/examples/companion_radio/MyMesh.h @@ -97,6 +97,7 @@ protected: float getAirtimeBudgetFactor() const override; int getInterferenceThreshold() const override; int calcRxDelay(float score, uint32_t air_time) const override; + uint8_t getExtraAckTransmitCount() const override; void logRxRaw(float snr, float rssi, const uint8_t raw[], int len) override; bool isAutoAddEnabled() const override; diff --git a/examples/companion_radio/NodePrefs.h b/examples/companion_radio/NodePrefs.h index bf4739e4..bfde7218 100644 --- a/examples/companion_radio/NodePrefs.h +++ b/examples/companion_radio/NodePrefs.h @@ -14,7 +14,7 @@ struct NodePrefs { // persisted to file float freq; uint8_t sf; uint8_t cr; - uint8_t reserved1; + uint8_t multi_acks; uint8_t manual_add_contacts; float bw; uint8_t tx_power_dbm; diff --git a/src/helpers/BaseChatMesh.cpp b/src/helpers/BaseChatMesh.cpp index c5e2915c..476e6e8f 100644 --- a/src/helpers/BaseChatMesh.cpp +++ b/src/helpers/BaseChatMesh.cpp @@ -36,11 +36,15 @@ void BaseChatMesh::sendAckTo(const ContactInfo& dest, uint32_t ack_hash) { mesh::Packet* ack = createAck(ack_hash); if (ack) sendFlood(ack, TXT_ACK_DELAY); } else { - mesh::Packet* a1 = createMultiAck(ack_hash, 1); - if (a1) sendDirect(a1, dest.out_path, dest.out_path_len, TXT_ACK_DELAY); + uint32_t d = TXT_ACK_DELAY; + if (getExtraAckTransmitCount() > 0) { + mesh::Packet* a1 = createMultiAck(ack_hash, 1); + if (a1) sendDirect(a1, dest.out_path, dest.out_path_len, d); + d += 300; + } mesh::Packet* a2 = createAck(ack_hash); - if (a2) sendDirect(a2, dest.out_path, dest.out_path_len, TXT_ACK_DELAY + 300); + if (a2) sendDirect(a2, dest.out_path, dest.out_path_len, d); } } From 335878303998a7e6452068a70f6382548aa04732 Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Wed, 16 Jul 2025 21:16:05 +1000 Subject: [PATCH 08/24] * sensor: "setperm {pubkey-hex} 0" command can now remove by partial pubkey * sensor: login with blank password now just checks if sender is in ACL, and returns permissions (if so) --- examples/simple_sensor/SensorMesh.cpp | 94 ++++++++++++++++++--------- examples/simple_sensor/SensorMesh.h | 3 +- 2 files changed, 66 insertions(+), 31 deletions(-) diff --git a/examples/simple_sensor/SensorMesh.cpp b/examples/simple_sensor/SensorMesh.cpp index bfc0b0d1..abdc7182 100644 --- a/examples/simple_sensor/SensorMesh.cpp +++ b/examples/simple_sensor/SensorMesh.cpp @@ -316,6 +316,13 @@ mesh::Packet* SensorMesh::createSelfAdvert() { return createAdvert(self_id, app_data, app_data_len); } +ContactInfo* SensorMesh::getContact(const uint8_t* pubkey, int key_len) { + for (int i = 0; i < num_contacts; i++) { + if (memcmp(pubkey, contacts[i].id.pub_key, key_len) == 0) return &contacts[i]; // already known + } + return NULL; // not found +} + ContactInfo* SensorMesh::putContact(const mesh::Identity& id, uint8_t init_perms) { uint32_t min_time = 0xFFFFFFFF; ContactInfo* oldest = &contacts[MAX_CONTACTS - 1]; @@ -340,17 +347,29 @@ ContactInfo* SensorMesh::putContact(const mesh::Identity& id, uint8_t init_perms return c; } -void SensorMesh::applyContactPermissions(const uint8_t* pubkey, uint8_t perms) { - mesh::Identity id(pubkey); - auto c = putContact(id, 0); - +bool SensorMesh::applyContactPermissions(const uint8_t* pubkey, int key_len, uint8_t perms) { + ContactInfo* c; if ((perms & PERM_ACL_ROLE_MASK) == PERM_ACL_GUEST) { // guest role is not persisted in contacts - memset(c, 0, sizeof(*c)); + c = getContact(pubkey, key_len); + if (c == NULL) return false; // partial pubkey not found + + num_contacts--; // delete from contacts[] + int i = c - contacts; + while (i < num_contacts) { + contacts[i] = contacts[i + 1]; + i++; + } } else { + if (key_len < PUB_KEY_SIZE) return false; // need complete pubkey when adding/modifying + + mesh::Identity id(pubkey); + c = putContact(id, 0); + c->permissions = perms; // update their permissions self_id.calcSharedSecret(c->shared_secret, pubkey); } dirty_contacts_expiry = futureMillis(LAZY_CONTACTS_WRITE_DELAY); // trigger saveContacts() + return true; } void SensorMesh::sendAlert(ContactInfo* c, Trigger* t) { @@ -436,32 +455,43 @@ int SensorMesh::getAGCResetInterval() const { } uint8_t SensorMesh::handleLoginReq(const mesh::Identity& sender, const uint8_t* secret, uint32_t sender_timestamp, const uint8_t* data) { - if (strcmp((char *) data, _prefs.password) != 0) { // check for valid password - #if MESH_DEBUG - MESH_DEBUG_PRINTLN("Invalid password: %s", &data[4]); - #endif - return 0; + ContactInfo* client; + if (data[0] == 0) { // blank password, just check if sender is in ACL + client = getContact(sender.pub_key, PUB_KEY_SIZE); + if (client == NULL) { + #if MESH_DEBUG + MESH_DEBUG_PRINTLN("Login, sender not in ACL"); + #endif + return 0; + } + } else { + if (strcmp((char *) data, _prefs.password) != 0) { // check for valid admin password + #if MESH_DEBUG + MESH_DEBUG_PRINTLN("Invalid password: %s", &data[4]); + #endif + return 0; + } + + client = putContact(sender, PERM_RECV_ALERTS_HI | PERM_RECV_ALERTS_LO); // add to contacts (if not already known) + if (sender_timestamp <= client->last_timestamp) { + MESH_DEBUG_PRINTLN("Possible login replay attack!"); + return 0; // FATAL: client table is full -OR- replay attack + } + + MESH_DEBUG_PRINTLN("Login success!"); + client->last_timestamp = sender_timestamp; + client->last_activity = getRTCClock()->getCurrentTime(); + client->permissions |= PERM_ACL_ADMIN; + memcpy(client->shared_secret, secret, PUB_KEY_SIZE); + + dirty_contacts_expiry = futureMillis(LAZY_CONTACTS_WRITE_DELAY); } - auto client = putContact(sender, PERM_RECV_ALERTS_HI | PERM_RECV_ALERTS_LO); // add to contacts (if not already known) - if (sender_timestamp <= client->last_timestamp) { - MESH_DEBUG_PRINTLN("Possible login replay attack!"); - return 0; // FATAL: client table is full -OR- replay attack - } - - MESH_DEBUG_PRINTLN("Login success!"); - client->last_timestamp = sender_timestamp; - client->last_activity = getRTCClock()->getCurrentTime(); - client->permissions |= PERM_ACL_ADMIN; - memcpy(client->shared_secret, secret, PUB_KEY_SIZE); - - dirty_contacts_expiry = futureMillis(LAZY_CONTACTS_WRITE_DELAY); - uint32_t now = getRTCClock()->getCurrentTimeUnique(); memcpy(reply_data, &now, 4); // response packets always prefixed with timestamp reply_data[4] = RESP_SERVER_LOGIN_OK; reply_data[5] = 0; // NEW: recommended keep-alive interval (secs / 16) - reply_data[6] = 1; // 1 = is admin + reply_data[6] = client->isAdmin() ? 1 : 0; reply_data[7] = client->permissions; getRNG()->random(&reply_data[8], 4); // random blob to help packet-hash uniqueness @@ -486,16 +516,20 @@ void SensorMesh::handleCommand(uint32_t sender_timestamp, char* command, char* r if (memcmp(command, "setperm ", 8) == 0) { // format: setperm {pubkey-hex} {permissions-int8} char* hex = &command[8]; char* sp = strchr(hex, ' '); // look for separator char - if (sp == NULL || sp - hex != PUB_KEY_SIZE*2) { - strcpy(reply, "Err - bad pubkey len"); + if (sp == NULL) { + strcpy(reply, "Err - bad params"); } else { *sp++ = 0; // replace space with null terminator uint8_t pubkey[PUB_KEY_SIZE]; - if (mesh::Utils::fromHex(pubkey, PUB_KEY_SIZE, hex)) { + int hex_len = min(sp - hex, PUB_KEY_SIZE*2); + if (mesh::Utils::fromHex(pubkey, hex_len / 2, hex)) { uint8_t perms = atoi(sp); - applyContactPermissions(pubkey, perms); - strcpy(reply, "OK"); + if (applyContactPermissions(pubkey, hex_len / 2, perms)) { + strcpy(reply, "OK"); + } else { + strcpy(reply, "Err - invalid params"); + } } else { strcpy(reply, "Err - bad pubkey"); } diff --git a/examples/simple_sensor/SensorMesh.h b/examples/simple_sensor/SensorMesh.h index 84f803f1..3f687c3f 100644 --- a/examples/simple_sensor/SensorMesh.h +++ b/examples/simple_sensor/SensorMesh.h @@ -160,8 +160,9 @@ private: uint8_t handleLoginReq(const mesh::Identity& sender, const uint8_t* secret, uint32_t sender_timestamp, const uint8_t* data); uint8_t handleRequest(uint8_t perms, uint32_t sender_timestamp, uint8_t req_type, uint8_t* payload, size_t payload_len); mesh::Packet* createSelfAdvert(); + ContactInfo* getContact(const uint8_t* pubkey, int key_len); ContactInfo* putContact(const mesh::Identity& id, uint8_t init_perms); - void applyContactPermissions(const uint8_t* pubkey, uint8_t perms); + bool applyContactPermissions(const uint8_t* pubkey, int key_len, uint8_t perms); void sendAlert(ContactInfo* c, Trigger* t); From 58cffa8f763cef97c16c438638ee0d3290ed255f Mon Sep 17 00:00:00 2001 From: marcelverdult <37555613+marcelverdult@users.noreply.github.com> Date: Wed, 16 Jul 2025 13:50:23 +0200 Subject: [PATCH 09/24] Fixed Barometric Pressure Reading for BMP280 Measurement has to be divided by 100 same as BME280 --- src/helpers/sensors/EnvironmentSensorManager.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/helpers/sensors/EnvironmentSensorManager.cpp b/src/helpers/sensors/EnvironmentSensorManager.cpp index ab5b459e..c54e45c2 100644 --- a/src/helpers/sensors/EnvironmentSensorManager.cpp +++ b/src/helpers/sensors/EnvironmentSensorManager.cpp @@ -162,7 +162,7 @@ bool EnvironmentSensorManager::querySensors(uint8_t requester_permissions, Cayen #if ENV_INCLUDE_BMP280 if (BMP280_initialized) { telemetry.addTemperature(TELEM_CHANNEL_SELF, BMP280.readTemperature()); - telemetry.addBarometricPressure(TELEM_CHANNEL_SELF, BMP280.readPressure()); + telemetry.addBarometricPressure(TELEM_CHANNEL_SELF, BMP280.readPressure()/100); telemetry.addAltitude(TELEM_CHANNEL_SELF, BME280.readAltitude(TELEM_BME280_SEALEVELPRESSURE_HPA)); } #endif @@ -332,4 +332,4 @@ void EnvironmentSensorManager::loop() { next_gps_update = millis() + 1000; } } -#endif \ No newline at end of file +#endif From 660ab0692fd448a66250446c634f48e895e33906 Mon Sep 17 00:00:00 2001 From: cod3doomy Date: Wed, 16 Jul 2025 19:04:50 -0700 Subject: [PATCH 10/24] RAK4631 ESM Migration Changes to migrate sensor code to the ESM. Added a separate GPS init sequence for the RAK that scans I2C and Serial1 on the various sockets of the various base boards to find the RAK12500. (and soon the RAK12501) Removed the GPS specific envs from platformio.ini and enabled GPS for all envs. Verified working with RAK12500 on RAK19007 sockets A and D, as well as RAK19003. --- .../sensors/EnvironmentSensorManager.cpp | 119 +++++- .../sensors/EnvironmentSensorManager.h | 4 + variants/rak4631/RAK4631Board.h | 13 +- variants/rak4631/platformio.ini | 59 +-- variants/rak4631/target.cpp | 400 +----------------- variants/rak4631/target.h | 71 +--- 6 files changed, 146 insertions(+), 520 deletions(-) diff --git a/src/helpers/sensors/EnvironmentSensorManager.cpp b/src/helpers/sensors/EnvironmentSensorManager.cpp index c54e45c2..f314f677 100644 --- a/src/helpers/sensors/EnvironmentSensorManager.cpp +++ b/src/helpers/sensors/EnvironmentSensorManager.cpp @@ -47,10 +47,26 @@ static Adafruit_INA3221 INA3221; static Adafruit_INA219 INA219(TELEM_INA219_ADDRESS); #endif +#if ENV_INCLUDE_GPS & RAK_BOARD +uint32_t gpsResetPin = 0; +bool i2cGPSFlag = false; +bool serialGPSFlag = false; +//#define PIN_GPS_STANDBY_A 34 //GPS Reset/Standby pin (IO2 for socket A) +//#define PIN_GPS_STANDBY_C 4 //GPS Reset/Standby pin (IO4 for socket C) +//#define PIN_GPS_STANDBY_F 9 //GPS Reset/Standby pin (IO5 for socket F) +#define TELEM_RAK12500_ADDRESS 0x42 //RAK12500 Ublox GPS via i2c +#include +static SFE_UBLOX_GNSS ublox_GNSS; +#endif + bool EnvironmentSensorManager::begin() { #if ENV_INCLUDE_GPS + #if RAK_BOARD + rakGPSInit(); //probe base board/sockets for GPS + #else initBasicGPS(); #endif + #endif #if ENV_INCLUDE_AHTX0 if (AHTX0.begin(&Wire, 0, TELEM_AHTX_ADDRESS)) { @@ -296,8 +312,87 @@ void EnvironmentSensorManager::initBasicGPS() { gps_active = false; //Set GPS visibility off until setting is changed } +#ifdef RAK_BOARD +void EnvironmentSensorManager::rakGPSInit(){ + + Serial1.setPins(PIN_GPS_TX, PIN_GPS_RX); + + #ifdef GPS_BAUD_RATE + Serial1.begin(GPS_BAUD_RATE); + #else + Serial1.begin(9600); + #endif + + delay(1000); + + //search for the correct IO standby pin depending on socket used + if(gpsIsAwake(WB_IO2)){ + // MESH_DEBUG_PRINTLN("RAK base board is RAK19007/10"); + // MESH_DEBUG_PRINTLN("GPS is installed on Socket A"); + } + else if(gpsIsAwake(WB_IO4)){ + // MESH_DEBUG_PRINTLN("RAK base board is RAK19003/9"); + // MESH_DEBUG_PRINTLN("GPS is installed on Socket C"); + } + else if(gpsIsAwake(WB_IO5)){ + // MESH_DEBUG_PRINTLN("RAK base board is RAK19001/11"); + // MESH_DEBUG_PRINTLN("GPS is installed on Socket F"); + } + else{ + MESH_DEBUG_PRINTLN("No GPS found"); + gps_active = false; + gps_detected = false; + return; + } + + #ifndef FORCE_GPS_ALIVE // for use with repeaters, until GPS toggle is implimented + //Now that GPS is found and set up, set to sleep for initial state + stop_gps(); + #endif +} + +bool EnvironmentSensorManager::gpsIsAwake(uint8_t ioPin){ + + //set initial waking state + pinMode(ioPin,OUTPUT); + digitalWrite(ioPin,LOW); + delay(1000); + digitalWrite(ioPin,HIGH); + delay(1000); + + //Try to init RAK12500 on I2C + if (ublox_GNSS.begin(Wire) == true){ + MESH_DEBUG_PRINTLN("RAK12500 GPS init correctly with pin %i",ioPin); + ublox_GNSS.setI2COutput(COM_TYPE_NMEA); + ublox_GNSS.saveConfigSelective(VAL_CFG_SUBSEC_IOPORT); + gpsResetPin = ioPin; + i2cGPSFlag = true; + gps_active = true; + gps_detected = true; + return true; + } + else if(Serial1){ + MESH_DEBUG_PRINTLN("Serial GPS init correctly and is turned on"); + if(PIN_GPS_EN){ + gpsResetPin = PIN_GPS_EN; + } + serialGPSFlag = true; + gps_active = true; + gps_detected = true; + return true; + } + MESH_DEBUG_PRINTLN("GPS did not init with this IO pin... try the next"); + return false; +} +#endif + void EnvironmentSensorManager::start_gps() { gps_active = true; + #ifdef RAK_BOARD + pinMode(gpsResetPin, OUTPUT); + digitalWrite(gpsResetPin, HIGH); + return; + #endif #ifdef PIN_GPS_EN pinMode(PIN_GPS_EN, OUTPUT); digitalWrite(PIN_GPS_EN, HIGH); @@ -309,6 +404,11 @@ void EnvironmentSensorManager::start_gps() { void EnvironmentSensorManager::stop_gps() { gps_active = false; + #ifdef RAK_BOARD + pinMode(gpsResetPin, OUTPUT); + digitalWrite(gpsResetPin, LOW); + return; + #endif #ifdef PIN_GPS_EN pinMode(PIN_GPS_EN, OUTPUT); digitalWrite(PIN_GPS_EN, LOW); @@ -324,11 +424,28 @@ void EnvironmentSensorManager::loop() { _location->loop(); if (millis() > next_gps_update) { - if (gps_active && _location->isValid()) { + if(gps_active){ + #ifndef RAK_BOARD + if (_location->isValid()) { node_lat = ((double)_location->getLatitude())/1000000.; node_lon = ((double)_location->getLongitude())/1000000.; MESH_DEBUG_PRINTLN("lat %f lon %f", node_lat, node_lon); } + #else + if(i2cGPSFlag){ + node_lat = ((double)ublox_GNSS.getLatitude())/10000000.; + node_lon = ((double)ublox_GNSS.getLongitude())/10000000.; + MESH_DEBUG_PRINTLN("lat %f lon %f", node_lat, node_lon); + } + else if (serialGPSFlag && _location->isValid()) { + node_lat = ((double)_location->getLatitude())/1000000.; + node_lon = ((double)_location->getLongitude())/1000000.; + MESH_DEBUG_PRINTLN("lat %f lon %f", node_lat, node_lon); + } + //else + //MESH_DEBUG_PRINTLN("No valid GPS data"); + #endif + } next_gps_update = millis() + 1000; } } diff --git a/src/helpers/sensors/EnvironmentSensorManager.h b/src/helpers/sensors/EnvironmentSensorManager.h index f7804431..2a2b209a 100644 --- a/src/helpers/sensors/EnvironmentSensorManager.h +++ b/src/helpers/sensors/EnvironmentSensorManager.h @@ -24,6 +24,10 @@ protected: void start_gps(); void stop_gps(); void initBasicGPS(); + #ifdef RAK_BOARD + void rakGPSInit(); + bool gpsIsAwake(uint8_t ioPin); + #endif #endif diff --git a/variants/rak4631/RAK4631Board.h b/variants/rak4631/RAK4631Board.h index 9232e39c..7f3a8fea 100644 --- a/variants/rak4631/RAK4631Board.h +++ b/variants/rak4631/RAK4631Board.h @@ -13,14 +13,11 @@ #define P_LORA_MOSI 44 #define SX126X_POWER_EN 37 -#define P_GPS_SDA 13 //GPS SDA pin (output option) -#define P_GPS_SCL 14 //GPS SCL pin (output option) -#define P_GPS_TX 16 //GPS TX pin -#define P_GPS_RX 15 //GPS RX pin -#define P_GPS_STANDBY_A 34 //GPS Reset/Standby pin (IO2 for socket A) -#define P_GPS_STANDBY_C 4 //GPS Reset/Standby pin (IO4 for socket C) -#define P_GPS_STANDBY_F 9 //GPS Reset/Standby pin (IO5 for socket F) -#define P_GPS_1PPS 17 //GPS PPS pin +//#define PIN_GPS_SDA 13 //GPS SDA pin (output option) +//#define PIN_GPS_SCL 14 //GPS SCL pin (output option) +//#define PIN_GPS_TX 16 //GPS TX pin +//#define PIN_GPS_RX 15 //GPS RX pin +#define PIN_GPS_1PPS 17 //GPS PPS pin #define GPS_BAUD_RATE 9600 #define GPS_ADDRESS 0x42 //i2c address for GPS diff --git a/variants/rak4631/platformio.ini b/variants/rak4631/platformio.ini index d4f8028b..95ea5b08 100644 --- a/variants/rak4631/platformio.ini +++ b/variants/rak4631/platformio.ini @@ -5,21 +5,27 @@ board = wiscore_rak4631 board_check = true build_flags = ${nrf52_base.build_flags} -I variants/rak4631 - -D RAK_4631 + -D RAK_BOARD -D PIN_BOARD_SCL=14 -D PIN_BOARD_SDA=13 + -D PIN_GPS_TX=16 + -D PIN_GPS_RX=15 + -D PIN_GPS_EN=-1 -D PIN_OLED_RESET=-1 -D RADIO_CLASS=CustomSX1262 -D WRAPPER_CLASS=CustomSX1262Wrapper -D LORA_TX_POWER=22 -D SX126X_CURRENT_LIMIT=140 -D SX126X_RX_BOOSTED_GAIN=1 + -D ENV_INCLUDE_GPS=1 build_src_filter = ${nrf52_base.build_src_filter} +<../variants/rak4631> + + lib_deps = ${nrf52_base.lib_deps} adafruit/Adafruit SSD1306 @ ^2.5.13 stevemarple/MicroNMEA @ ^2.0.6 + sparkfun/SparkFun u-blox GNSS Arduino Library @ ^2.2.27 [env:RAK_4631_Repeater] extends = rak4631 @@ -37,30 +43,6 @@ build_src_filter = ${rak4631.build_src_filter} + +<../examples/simple_repeater> -[env:RAK_4631_GPS_Repeater] -extends = rak4631 -build_flags = - ${rak4631.build_flags} - -D DISPLAY_CLASS=SSD1306Display - -D ADVERT_NAME='"RAK4631 GPS Repeater"' - -D ADVERT_LAT=0.0 - -D ADVERT_LON=0.0 - -D ADMIN_PASSWORD='"password"' - -D MAX_NEIGHBOURS=8 - -D FORCE_GPS_ALIVE=1 - -D ENV_INCLUDE_GPS=1 - -D ENV_INCLUDE_BME680=1 -; -D MESH_PACKET_LOGGING=1 -; -D MESH_DEBUG=1 -build_src_filter = ${rak4631.build_src_filter} - + - +<../examples/simple_repeater> -lib_deps = - ${rak4631.lib_deps} - sparkfun/SparkFun u-blox GNSS Arduino Library @ ^2.2.27 - https://github.com/boschsensortec/Bosch-BSEC2-Library - https://github.com/boschsensortec/Bosch-BME68x-Library - [env:RAK_4631_room_server] extends = rak4631 build_flags = @@ -117,33 +99,6 @@ lib_deps = ${rak4631.lib_deps} densaugeo/base64 @ ~1.4.0 -[env:RAK_4631_GPS_companion_radio_ble] -extends = rak4631 -build_flags = - ${rak4631.build_flags} - -D PIN_USER_BTN=9 - -D PIN_USER_BTN_ANA=31 - -D DISPLAY_CLASS=SSD1306Display - -D MAX_CONTACTS=100 - -D MAX_GROUP_CHANNELS=8 - -D BLE_PIN_CODE=123456 - -D BLE_DEBUG_LOGGING=1 - -D OFFLINE_QUEUE_SIZE=256 - -D ENV_INCLUDE_GPS=1 - -D ENV_INCLUDE_BME680=1 -; -D MESH_PACKET_LOGGING=1 -; -D MESH_DEBUG=1 -build_src_filter = ${rak4631.build_src_filter} - + - + - +<../examples/companion_radio> -lib_deps = - ${rak4631.lib_deps} - sparkfun/SparkFun u-blox GNSS Arduino Library @ ^2.2.27 - https://github.com/boschsensortec/Bosch-BSEC2-Library - https://github.com/boschsensortec/Bosch-BME68x-Library - densaugeo/base64 @ ~1.4.0 - [env:RAK_4631_terminal_chat] extends = rak4631 build_flags = diff --git a/variants/rak4631/target.cpp b/variants/rak4631/target.cpp index e9d80421..4e9d3cce 100644 --- a/variants/rak4631/target.cpp +++ b/variants/rak4631/target.cpp @@ -1,10 +1,13 @@ #include #include "target.h" #include -#include RAK4631Board board; +#ifdef DISPLAY_CLASS + DISPLAY_CLASS display; +#endif + RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, SPI); WRAPPER_CLASS radio_driver(radio, board); @@ -13,80 +16,11 @@ VolatileRTCClock fallback_clock; AutoDiscoverRTCClock rtc_clock(fallback_clock); #if ENV_INCLUDE_GPS -MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Wire); -RAK4631SensorManager sensors = RAK4631SensorManager(nmea); + #include + MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1); + EnvironmentSensorManager sensors = EnvironmentSensorManager(nmea); #else -RAK4631SensorManager sensors; -#endif - -#if ENV_INCLUDE_BME680 -#ifndef TELEM_BME680_ADDRESS -#define TELEM_BME680_ADDRESS 0x76 // BME680 environmental sensor I2C address -#endif -#include -static Bsec2 BME680; -static float rawPressure = 0; -static float rawTemperature = 0; -static float compTemperature = 0; -static float rawHumidity = 0; -static float compHumidity = 0; -static float readIAQ = 0; -static float readStaticIAQ = 0; -static float readCO2 = 0; -#endif - -#ifdef DISPLAY_CLASS - DISPLAY_CLASS display; -#endif - -#ifdef MESH_DEBUG -uint32_t deviceOnline = 0x00; -static void scanDevices(TwoWire *w) -{ - uint8_t err, addr; - int nDevices = 0; - uint32_t start = 0; - - Serial.println("Scanning I2C for Devices"); - for (addr = 1; addr < 127; addr++) { - start = millis(); - w->beginTransmission(addr); delay(2); - err = w->endTransmission(); - if (err == 0) { - nDevices++; - switch (addr) { - case 0x42: - Serial.println("\tFound RAK12500 GPS Sensor"); - deviceOnline |= RAK12500_ONLINE; - break; - case 0x76: - Serial.println("\tFound RAK1906 Environment Sensor"); - deviceOnline |= BME680_ONLINE; - break; - default: - Serial.print("\tI2C device found at address 0x"); - if (addr < 16) { - Serial.print("0"); - } - Serial.print(addr, HEX); - Serial.println(" !"); - break; - } - - } else if (err == 4) { - Serial.print("Unknow error at address 0x"); - if (addr < 16) { - Serial.print("0"); - } - Serial.println(addr, HEX); - } - } - if (nDevices == 0) - Serial.println("No I2C devices found\n"); - - Serial.println("Scan for devices is complete."); - Serial.println("\n"); -} + EnvironmentSensorManager sensors; #endif bool radio_init() { @@ -109,324 +43,6 @@ void radio_set_tx_power(uint8_t dbm) { radio.setOutputPower(dbm); } -#if ENV_INCLUDE_GPS -void RAK4631SensorManager::start_gps() -{ - //function currently not used - gps_active = true; - pinMode(disStandbyPin, OUTPUT); - digitalWrite(disStandbyPin, 1); - MESH_DEBUG_PRINTLN("GPS should be on now"); -} - -void RAK4631SensorManager::stop_gps() -{ - //function currently not used - gps_active = false; - pinMode(disStandbyPin, OUTPUT); - digitalWrite(disStandbyPin, 0); - MESH_DEBUG_PRINTLN("GPS should be off now"); -} - -void RAK4631SensorManager::sleep_gps() { - gps_active = false; - ublox_GNSS.powerSaveMode(); - MESH_DEBUG_PRINTLN("GPS should be sleeping now"); -} - -void RAK4631SensorManager::wake_gps() { - gps_active = true; - ublox_GNSS.powerSaveMode(false); - MESH_DEBUG_PRINTLN("GPS should be waking now"); -} - -bool RAK4631SensorManager::gpsIsAwake(uint32_t ioPin){ - - int pinInitialState = 0; - - //set initial waking state - pinMode(ioPin,OUTPUT); - digitalWrite(ioPin,0); - delay(1000); - digitalWrite(ioPin,1); - delay(1000); - - if (ublox_GNSS.begin(Wire) == true){ - MESH_DEBUG_PRINTLN("GPS init correctly and GPS is turned on"); - ublox_GNSS.setI2COutput(COM_TYPE_NMEA); - ublox_GNSS.saveConfigSelective(VAL_CFG_SUBSEC_IOPORT); - disStandbyPin = ioPin; - gps_active = true; - gps_detected = true; - return true; - } - else - MESH_DEBUG_PRINTLN("GPS failed to init on this IO pin... try the next"); - //digitalWrite(ioPin,pinInitialState); //reset the IO pin to initial state - return false; -} -#endif - -#if ENV_INCLUDE_BME680 -static void checkBMEStatus(Bsec2 bsec) { - if (bsec.status < BSEC_OK) - { - MESH_DEBUG_PRINTLN("BSEC error code : %f", float(bsec.status)); - } - else if (bsec.status > BSEC_OK) - { - MESH_DEBUG_PRINTLN("BSEC warning code : %f", float(bsec.status)); - } - - if (bsec.sensor.status < BME68X_OK) - { - MESH_DEBUG_PRINTLN("BME68X error code : %f", bsec.sensor.status); - } - else if (bsec.sensor.status > BME68X_OK) - { - MESH_DEBUG_PRINTLN("BME68X warning code : %f", bsec.sensor.status); - } -} - -static void newDataCallback(const bme68xData data, const bsecOutputs outputs, Bsec2 bsec) { - if (!outputs.nOutputs) { - MESH_DEBUG_PRINTLN("No new data to report out"); - return; - } - - MESH_DEBUG_PRINTLN("BSEC outputs:\n\tTime stamp = %f", (int) (outputs.output[0].time_stamp / INT64_C(1000000))); - for (uint8_t i = 0; i < outputs.nOutputs; i++) { - const bsecData output = outputs.output[i]; - switch (output.sensor_id) - { - case BSEC_OUTPUT_IAQ: - readIAQ = output.signal; - MESH_DEBUG_PRINTLN("\tIAQ = %f", output.signal); - MESH_DEBUG_PRINTLN("\tIAQ accuracy = %f", output.accuracy); - break; - case BSEC_OUTPUT_RAW_TEMPERATURE: - rawTemperature = output.signal; - MESH_DEBUG_PRINTLN("\tTemperature = %f", output.signal); - break; - case BSEC_OUTPUT_RAW_PRESSURE: - rawPressure = output.signal; - MESH_DEBUG_PRINTLN("\tPressure = %f", output.signal); - break; - case BSEC_OUTPUT_RAW_HUMIDITY: - rawHumidity = output.signal; - MESH_DEBUG_PRINTLN("\tHumidity = %f", output.signal); - break; - case BSEC_OUTPUT_RAW_GAS: - MESH_DEBUG_PRINTLN("\tGas resistance = %f", output.signal); - break; - case BSEC_OUTPUT_STABILIZATION_STATUS: - MESH_DEBUG_PRINTLN("\tStabilization status = %f", output.signal); - break; - case BSEC_OUTPUT_RUN_IN_STATUS: - MESH_DEBUG_PRINTLN("\tRun in status = %f", output.signal); - break; - case BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE: - compTemperature = output.signal; - MESH_DEBUG_PRINTLN("\tCompensated temperature = %f", output.signal); - break; - case BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY: - compHumidity = output.signal; - MESH_DEBUG_PRINTLN("\tCompensated humidity = %f", output.signal); - break; - case BSEC_OUTPUT_STATIC_IAQ: - readStaticIAQ = output.signal; - MESH_DEBUG_PRINTLN("\tStatic IAQ = %f", output.signal); - break; - case BSEC_OUTPUT_CO2_EQUIVALENT: - readCO2 = output.signal; - MESH_DEBUG_PRINTLN("\tCO2 Equivalent = %f", output.signal); - break; - case BSEC_OUTPUT_BREATH_VOC_EQUIVALENT: - MESH_DEBUG_PRINTLN("\tbVOC equivalent = %f", output.signal); - break; - case BSEC_OUTPUT_GAS_PERCENTAGE: - MESH_DEBUG_PRINTLN("\tGas percentage = %f", output.signal); - break; - case BSEC_OUTPUT_COMPENSATED_GAS: - MESH_DEBUG_PRINTLN("\tCompensated gas = %f", output.signal); - break; - default: - break; - } - } -} -#endif - -bool RAK4631SensorManager::begin() { - - #ifdef MESH_DEBUG - scanDevices(&Wire); - #endif - - #if ENV_INCLUDE_GPS - //search for the correct IO standby pin depending on socket used - if(gpsIsAwake(P_GPS_STANDBY_A)){ - MESH_DEBUG_PRINTLN("GPS is on socket A"); - } - else if(gpsIsAwake(P_GPS_STANDBY_C)){ - MESH_DEBUG_PRINTLN("GPS is on socket C"); - } - else if(gpsIsAwake(P_GPS_STANDBY_F)){ - MESH_DEBUG_PRINTLN("GPS is on socket F"); - } - else{ - MESH_DEBUG_PRINTLN("Error: No GPS found on sockets A, C or F"); - gps_active = false; - gps_detected = false; - return false; - } - - #ifndef FORCE_GPS_ALIVE - //Now that GPS is found and set up, set to sleep for initial state - stop_gps(); - #endif - #endif - - #if ENV_INCLUDE_BME680 - - bsecSensor sensorList[5] = { - BSEC_OUTPUT_IAQ, - // BSEC_OUTPUT_RAW_TEMPERATURE, - BSEC_OUTPUT_RAW_PRESSURE, - // BSEC_OUTPUT_RAW_HUMIDITY, - // BSEC_OUTPUT_RAW_GAS, - // BSEC_OUTPUT_STABILIZATION_STATUS, - // BSEC_OUTPUT_RUN_IN_STATUS, - BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE, - BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY, - BSEC_OUTPUT_STATIC_IAQ, - // BSEC_OUTPUT_CO2_EQUIVALENT, - // BSEC_OUTPUT_BREATH_VOC_EQUIVALENT, - // BSEC_OUTPUT_GAS_PERCENTAGE, - // BSEC_OUTPUT_COMPENSATED_GAS - }; - - if(!BME680.begin(TELEM_BME680_ADDRESS, Wire)){ - checkBMEStatus(BME680); - bme680_present = false; - bme680_active = false; - return false; - } - - MESH_DEBUG_PRINTLN("Found BME680 at address: %02X", TELEM_BME680_ADDRESS); - bme680_present = true; - bme680_active = true; - - if (SAMPLING_RATE == BSEC_SAMPLE_RATE_ULP) - { - BME680.setTemperatureOffset(BSEC_SAMPLE_RATE_ULP); - } - else if (SAMPLING_RATE == BSEC_SAMPLE_RATE_LP) - { - BME680.setTemperatureOffset(TEMP_OFFSET_LP); - } - - if (!BME680.updateSubscription(sensorList, ARRAY_LEN(sensorList), SAMPLING_RATE)) - { - checkBMEStatus(BME680); - } - - BME680.attachCallback(newDataCallback); - - #endif -} - -bool RAK4631SensorManager::querySensors(uint8_t requester_permissions, CayenneLPP& telemetry) { - #ifdef ENV_INCLUDE_GPS - if (requester_permissions & TELEM_PERM_LOCATION && gps_active) { // does requester have permission? - telemetry.addGPS(TELEM_CHANNEL_SELF, node_lat, node_lon, node_altitude); - } - #endif - - if (requester_permissions & TELEM_PERM_ENVIRONMENT) { - - #if ENV_INCLUDE_BME680 - if (bme680_active) { - telemetry.addTemperature(TELEM_CHANNEL_SELF, compTemperature); - telemetry.addRelativeHumidity(TELEM_CHANNEL_SELF, compHumidity); - telemetry.addBarometricPressure(TELEM_CHANNEL_SELF, rawPressure); - telemetry.addTemperature(TELEM_CHANNEL_SELF+1, readIAQ); - telemetry.addRelativeHumidity(TELEM_CHANNEL_SELF+1, readStaticIAQ); - } - #endif - } - return true; -} - -void RAK4631SensorManager::loop() { - static long next_update = 0; - - #ifdef ENV_INCLUDE_GPS - _nmea->loop(); - #endif - - if (millis() > next_update) { - - #ifdef ENV_INCLUDE_GPS - if(gps_active){ - node_lat = (double)ublox_GNSS.getLatitude()/10000000.; - node_lon = (double)ublox_GNSS.getLongitude()/10000000.; - node_altitude = (double)ublox_GNSS.getAltitude()/1000.; - MESH_DEBUG_PRINT("lat %f lon %f alt %f\r\n", node_lat, node_lon, node_altitude); - } - #endif - - #ifdef ENV_INCLUDE_BME680 - if(bme680_active){ - if (!BME680.run()){ - checkBMEStatus(BME680); - } - } - #endif - next_update = millis() + 1000; - } - -} - -int RAK4631SensorManager::getNumSettings() const { - #if ENV_INCLUDE_GPS - return gps_detected ? 1 : 0; // only show GPS setting if GPS is detected - #else - return 0; - #endif -} - -const char* RAK4631SensorManager::getSettingName(int i) const { - #if ENV_INCLUDE_GPS - return (gps_detected && i == 0) ? "gps" : NULL; - #else - return NULL; - #endif -} - -const char* RAK4631SensorManager::getSettingValue(int i) const { - #if ENV_INCLUDE_GPS - if (gps_detected && i == 0) { - return gps_active ? "1" : "0"; - } - #endif - return NULL; -} - -bool RAK4631SensorManager::setSettingValue(const char* name, const char* value) { - #if ENV_INCLUDE_GPS - if (gps_detected && strcmp(name, "gps") == 0) { - if (strcmp(value, "0") == 0) { - stop_gps(); - } else { - start_gps(); - } - return true; - } - #endif - return false; // not supported -} - mesh::LocalIdentity radio_new_identity() { RadioNoiseListener rng(radio); return mesh::LocalIdentity(&rng); // create new random identity diff --git a/variants/rak4631/target.h b/variants/rak4631/target.h index e1545581..bf51e0f5 100644 --- a/variants/rak4631/target.h +++ b/variants/rak4631/target.h @@ -6,80 +6,17 @@ #include #include #include -#include -#if ENV_INCLUDE_GPS - #include - #include -#endif +#include + #ifdef DISPLAY_CLASS #include + extern DISPLAY_CLASS display; #endif -#define _BV(x) (1 << x) - -class RAK4631SensorManager: public SensorManager { - #if ENV_INCLUDE_GPS - bool gps_active = false; - bool gps_detected = false; - LocationProvider * _nmea; - SFE_UBLOX_GNSS ublox_GNSS; - uint32_t disStandbyPin = 0; - - void start_gps(); - void stop_gps(); - void sleep_gps(); - void wake_gps(); - bool gpsIsAwake(uint32_t ioPin); - #endif - - #if ENV_INCLUDE_BME680 - bool bme680_active = false; - bool bme680_present = false; - #define SAMPLING_RATE BSEC_SAMPLE_RATE_ULP - #endif - - public: - #if ENV_INCLUDE_GPS - RAK4631SensorManager(LocationProvider &nmea): _nmea(&nmea) { } - #else - RAK4631SensorManager() { } - #endif - - void loop() override; - bool querySensors(uint8_t requester_permissions, CayenneLPP& telemetry) override; - int getNumSettings() const override; - const char* getSettingName(int i) const override; - const char* getSettingValue(int i) const override; - bool setSettingValue(const char* name, const char* value) override; - bool begin() override; -}; - extern RAK4631Board board; extern WRAPPER_CLASS radio_driver; extern AutoDiscoverRTCClock rtc_clock; -extern RAK4631SensorManager sensors; - -#ifdef DISPLAY_CLASS - extern DISPLAY_CLASS display; -#endif - -enum { - POWERMANAGE_ONLINE = _BV(0), - DISPLAY_ONLINE = _BV(1), - RADIO_ONLINE = _BV(2), - GPS_ONLINE = _BV(3), - PSRAM_ONLINE = _BV(4), - SDCARD_ONLINE = _BV(5), - AXDL345_ONLINE = _BV(6), - BME280_ONLINE = _BV(7), - BMP280_ONLINE = _BV(8), - BME680_ONLINE = _BV(9), - QMC6310_ONLINE = _BV(10), - QMI8658_ONLINE = _BV(11), - PCF8563_ONLINE = _BV(12), - OSC32768_ONLINE = _BV(13), - RAK12500_ONLINE = _BV(14), -}; +extern EnvironmentSensorManager sensors; bool radio_init(); uint32_t radio_get_rng_seed(); From 6b4592bfe2e726ba95f0f9d4dd00c5cbfbc525c3 Mon Sep 17 00:00:00 2001 From: cod3doomy Date: Thu, 17 Jul 2025 10:42:18 -0700 Subject: [PATCH 11/24] Cleanup and fixes -Added RAK_4631 define back -Added includes for common RAK sensors that are currently supported in ESM -Set global variables to static -Reduced delay time within the RAK gps init sequence --- .../sensors/EnvironmentSensorManager.cpp | 17 ++++++----------- variants/rak4631/platformio.ini | 9 ++++++++- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/helpers/sensors/EnvironmentSensorManager.cpp b/src/helpers/sensors/EnvironmentSensorManager.cpp index f314f677..ab9487f8 100644 --- a/src/helpers/sensors/EnvironmentSensorManager.cpp +++ b/src/helpers/sensors/EnvironmentSensorManager.cpp @@ -47,13 +47,10 @@ static Adafruit_INA3221 INA3221; static Adafruit_INA219 INA219(TELEM_INA219_ADDRESS); #endif -#if ENV_INCLUDE_GPS & RAK_BOARD -uint32_t gpsResetPin = 0; -bool i2cGPSFlag = false; -bool serialGPSFlag = false; -//#define PIN_GPS_STANDBY_A 34 //GPS Reset/Standby pin (IO2 for socket A) -//#define PIN_GPS_STANDBY_C 4 //GPS Reset/Standby pin (IO4 for socket C) -//#define PIN_GPS_STANDBY_F 9 //GPS Reset/Standby pin (IO5 for socket F) +#if ENV_INCLUDE_GPS && RAK_BOARD +static uint32_t gpsResetPin = 0; +static bool i2cGPSFlag = false; +static bool serialGPSFlag = false; #define TELEM_RAK12500_ADDRESS 0x42 //RAK12500 Ublox GPS via i2c #include static SFE_UBLOX_GNSS ublox_GNSS; @@ -323,8 +320,6 @@ void EnvironmentSensorManager::rakGPSInit(){ Serial1.begin(9600); #endif - delay(1000); - //search for the correct IO standby pin depending on socket used if(gpsIsAwake(WB_IO2)){ // MESH_DEBUG_PRINTLN("RAK base board is RAK19007/10"); @@ -356,9 +351,9 @@ bool EnvironmentSensorManager::gpsIsAwake(uint8_t ioPin){ //set initial waking state pinMode(ioPin,OUTPUT); digitalWrite(ioPin,LOW); - delay(1000); + delay(500); digitalWrite(ioPin,HIGH); - delay(1000); + delay(500); //Try to init RAK12500 on I2C if (ublox_GNSS.begin(Wire) == true){ diff --git a/variants/rak4631/platformio.ini b/variants/rak4631/platformio.ini index 95ea5b08..33d3fbc8 100644 --- a/variants/rak4631/platformio.ini +++ b/variants/rak4631/platformio.ini @@ -5,6 +5,7 @@ board = wiscore_rak4631 board_check = true build_flags = ${nrf52_base.build_flags} -I variants/rak4631 + -D RAK_4631 -D RAK_BOARD -D PIN_BOARD_SCL=14 -D PIN_BOARD_SDA=13 @@ -18,6 +19,9 @@ build_flags = ${nrf52_base.build_flags} -D SX126X_CURRENT_LIMIT=140 -D SX126X_RX_BOOSTED_GAIN=1 -D ENV_INCLUDE_GPS=1 + -D ENV_INCLUDE_SHTC3=1 + -D ENV_INCLUDE_LPS22HB=1 + -D ENV_INCLUDE_INA219=1 build_src_filter = ${nrf52_base.build_src_filter} +<../variants/rak4631> + @@ -25,7 +29,10 @@ lib_deps = ${nrf52_base.lib_deps} adafruit/Adafruit SSD1306 @ ^2.5.13 stevemarple/MicroNMEA @ ^2.0.6 - sparkfun/SparkFun u-blox GNSS Arduino Library @ ^2.2.27 + adafruit/Adafruit SHTC3 Library@^1.0.1 + arduino-libraries/Arduino_LPS22HB@^1.0.2 + adafruit/Adafruit INA219@^1.2.3 + sparkfun/SparkFun u-blox GNSS Arduino Library@^2.2.27 [env:RAK_4631_Repeater] extends = rak4631 From 855e4831f5798ca7189a4e04f124ec40dbad790a Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Fri, 18 Jul 2025 12:56:31 +1000 Subject: [PATCH 12/24] * updates to packet/payload structure docs --- docs/packet_structure.md | 14 ++++++----- docs/payloads.md | 50 +++++++++++++++++++++------------------- 2 files changed, 34 insertions(+), 30 deletions(-) diff --git a/docs/packet_structure.md b/docs/packet_structure.md index 4a28526f..aa260855 100644 --- a/docs/packet_structure.md +++ b/docs/packet_structure.md @@ -1,11 +1,12 @@ # Packet Structure -| Field | Size (bytes) | Description | -|----------|----------------------------------|-----------------------------------------------------------| -| header | 1 | Contains routing type, payload type, and payload version. | -| path_len | 1 | Length of the path field in bytes. | -| path | up to 64 (`MAX_PATH_SIZE`) | Stores the routing path if applicable. | -| payload | up to 184 (`MAX_PACKET_PAYLOAD`) | The actual data being transmitted. | +| Field | Size (bytes) | Description | +|-----------------|----------------------------------|-----------------------------------------------------------| +| header | 1 | Contains routing type, payload type, and payload version. | +| transport_codes | 4 (optional) | 2x 16-bit transport codes (if ROUTE_TYPE_TRANSPORT_*) | +| path_len | 1 | Length of the path field in bytes. | +| path | up to 64 (`MAX_PATH_SIZE`) | Stores the routing path if applicable. | +| payload | up to 184 (`MAX_PACKET_PAYLOAD`) | The actual data being transmitted. | Note: see the [payloads doc](./payloads.md) for more information about the content of payload. @@ -42,6 +43,7 @@ bit 0 means the lowest bit (1s place) | `0x07` | `PAYLOAD_TYPE_ANON_REQ` | Anonymous request. | | `0x08` | `PAYLOAD_TYPE_PATH` | Returned path. | | `0x09` | `PAYLOAD_TYPE_TRACE` | trace a path, collecting SNI for each hop. | +| `0x0A` | `PAYLOAD_TYPE_MULTIPART` | packet is part of a sequence of packets. | | `0x0F` | `PAYLOAD_TYPE_RAW_CUSTOM` | Custom packet (raw bytes, custom encryption). | ## Payload Version Values diff --git a/docs/payloads.md b/docs/payloads.md index b094d9a9..4d00f930 100644 --- a/docs/payloads.md +++ b/docs/payloads.md @@ -10,13 +10,16 @@ Inside of each [meshcore packet](./packet_structure.md) is a payload, identified * Anonymous request. * Group text message (unverified). * Group datagram (unverified). +* Multi-part packet * Custom packet (raw bytes, custom encryption). -This document defines the structure of each of these payload types +This document defines the structure of each of these payload types. + +NOTE: all 16 and 32-bit integer fields are Little Endian. ## Important concepts: -* Node/channel hash: the first byte of the node or channel's public key +* Node hash: the first byte of the node's public key # Node advertisement This kind of payload notifies receivers that a node exists, and gives information about the node @@ -33,10 +36,10 @@ Appdata | Field | Size (bytes) | Description | |---------------|-----------------|-------------------------------------------------------| | flags | 1 | specifies which of the fields are present, see below | -| latitude | 4 | decimal latitude multiplied by 1000000, integer | -| longitude | 4 | decimal longitude multiplied by 1000000, integer | -| feature 1 | 2 | reserved for future use | -| feature 2 | 2 | reserved for future use | +| latitude | 4 (optional) | decimal latitude multiplied by 1000000, integer | +| longitude | 4 (optional) | decimal longitude multiplied by 1000000, integer | +| feature 1 | 2 (optional) | reserved for future use | +| feature 2 | 2 (optional) | reserved for future use | | name | rest of appdata | name of the node | Appdata Flags @@ -46,6 +49,7 @@ Appdata Flags | `0x01` | is chat node | advert is for a chat node | | `0x02` | is repeater | advert is for a repeater | | `0x03` | is room server | advert is for a room server | +| `0x04` | is sensor | advert is for a sensor server | | `0x10` | has location | appdata contains lat/long information | | `0x20` | has feature 1 | Reserved for future use. | | `0x40` | has feature 2 | Reserved for future use. | @@ -92,13 +96,15 @@ Returned path messages provide a description of the route a packet took from the Request type -| Value | Name | Description | -|--------|--------------------|---------------------------------------| -| `0x01` | get status | get status of repeater or room server | -| `0x02` | keepalive | TODO | -| `0x03` | get telemetry data | TODO | +| Value | Name | Description | +|--------|----------------------|---------------------------------------| +| `0x01` | get stats | get stats of repeater or room server | +| `0x02` | keepalive | (deprecated) | +| `0x03` | get telemetry data | TODO | +| `0x04` | get min,max,avg data | sensor nodes - get min, max, average for given time span | +| `0x05` | get access list | get node's approved access list | -### Get status +### Get stats Gets information about the node, possibly including the following: @@ -121,10 +127,6 @@ Gets information about the node, possibly including the following: * Number posted (?) * Number of post pushes (?) -### Keepalive - -No-op request. - ### Get telemetry data Request data about sensors on the node, including battery level. @@ -138,11 +140,11 @@ Request data about sensors on the node, including battery level. ## Plain text message -| Field | Size (bytes) | Description | -|--------------|-----------------|--------------------------------------------------------------| -| timestamp | 4 | send time (unix timestamp) | -| flags + TODO | 1 | first six bits are flags (see below), last two bits are TODO | -| message | rest of payload | the message content, see next table | +| Field | Size (bytes) | Description | +|-----------------|-----------------|--------------------------------------------------------------| +| timestamp | 4 | send time (unix timestamp) | +| flags + attempt | 1 | upper six bits are flags (see below), lower two bits are attempt number (0..3) | +| message | rest of payload | the message content, see next table | Flags @@ -150,7 +152,7 @@ Flags |--------|---------------------------|------------------------------------------------------------| | `0x00` | plain text message | the plain text of the message | | `0x01` | CLI command | the command text of the message | -| `0x02` | signed plain text message | two bytes of sender prefix, followed by plain text message | +| `0x02` | signed plain text message | first four bytes is sender pubkey prefix, followed by plain text message | # Anonymous request @@ -166,14 +168,14 @@ Plaintext message | Field | Size (bytes) | Description | |----------------|-----------------|-------------------------------------------------------------------------------| | timestamp | 4 | send time (unix timestamp) | -| sync timestamp | 4 | for room server, otherwise absent: sender's "sync messages SINCE x" timestamp | +| sync timestamp | 4 | NOTE: room server only! - sender's "sync messages SINCE x" timestamp | | password | rest of message | password for repeater/room | # Group text message / datagram | Field | Size (bytes) | Description | |--------------|-----------------|--------------------------------------------| -| channel hash | 1 | the first byte of the channel's public key | +| channel hash | 1 | first byte of SHA256 of channel's shared key | | cipher MAC | 2 | MAC for encrypted data in next field | | ciphertext | rest of payload | encrypted message, see below for details | From e4f7b9e37f0b08732d4de85bb8d0dda062589961 Mon Sep 17 00:00:00 2001 From: Mike Cochrane Date: Fri, 18 Jul 2025 18:16:59 +1200 Subject: [PATCH 13/24] Allow the SDA and SCL pins for Environment sensors to be configured independantly. Add Heltec V3 Sensor. --- .../sensors/EnvironmentSensorManager.cpp | 23 ++++++++++++++----- variants/heltec_v3/platformio.ini | 21 ++++++++++++++++- 2 files changed, 37 insertions(+), 7 deletions(-) diff --git a/src/helpers/sensors/EnvironmentSensorManager.cpp b/src/helpers/sensors/EnvironmentSensorManager.cpp index ab9487f8..b9653248 100644 --- a/src/helpers/sensors/EnvironmentSensorManager.cpp +++ b/src/helpers/sensors/EnvironmentSensorManager.cpp @@ -1,5 +1,11 @@ #include "EnvironmentSensorManager.h" +#if ENV_PIN_SDA && ENV_PIN_SCL +#define TELEM_WIRE &Wire1 // Use Wire1 as the I2C bus for Environment Sensors +#else +#define TELEM_WIRE &Wire // Use default I2C bus for Environment Sensors +#endif + #if ENV_INCLUDE_AHTX0 #define TELEM_AHTX_ADDRESS 0x38 // AHT10, AHT20 temperature and humidity sensor I2C address #include @@ -65,8 +71,13 @@ bool EnvironmentSensorManager::begin() { #endif #endif + #if ENV_PIN_SDA && ENV_PIN_SCL + Wire1.begin(ENV_PIN_SDA, ENV_PIN_SCL, 100000); + MESH_DEBUG_PRINTLN("Second I2C initialized on pins SDA: %d SCL: %d", ENV_PIN_SDA, ENV_PIN_SCL); + #endif + #if ENV_INCLUDE_AHTX0 - if (AHTX0.begin(&Wire, 0, TELEM_AHTX_ADDRESS)) { + if (AHTX0.begin(TELEM_WIRE, 0, TELEM_AHTX_ADDRESS)) { MESH_DEBUG_PRINTLN("Found AHT10/AHT20 at address: %02X", TELEM_AHTX_ADDRESS); AHTX0_initialized = true; } else { @@ -76,7 +87,7 @@ bool EnvironmentSensorManager::begin() { #endif #if ENV_INCLUDE_BME280 - if (BME280.begin(TELEM_BME280_ADDRESS, &Wire)) { + if (BME280.begin(TELEM_BME280_ADDRESS, TELEM_WIRE)) { MESH_DEBUG_PRINTLN("Found BME280 at address: %02X", TELEM_BME280_ADDRESS); MESH_DEBUG_PRINTLN("BME sensor ID: %02X", BME280.sensorID()); BME280_initialized = true; @@ -118,7 +129,7 @@ bool EnvironmentSensorManager::begin() { #endif #if ENV_INCLUDE_INA3221 - if (INA3221.begin(TELEM_INA3221_ADDRESS, &Wire)) { + if (INA3221.begin(TELEM_INA3221_ADDRESS, TELEM_WIRE)) { MESH_DEBUG_PRINTLN("Found INA3221 at address: %02X", TELEM_INA3221_ADDRESS); MESH_DEBUG_PRINTLN("%04X %04X", INA3221.getDieID(), INA3221.getManufacturerID()); @@ -133,7 +144,7 @@ bool EnvironmentSensorManager::begin() { #endif #if ENV_INCLUDE_INA219 - if (INA219.begin(&Wire)) { + if (INA219.begin(TELEM_WIRE)) { MESH_DEBUG_PRINTLN("Found INA219 at address: %02X", TELEM_INA219_ADDRESS); INA219_initialized = true; } else { @@ -327,14 +338,14 @@ void EnvironmentSensorManager::rakGPSInit(){ } else if(gpsIsAwake(WB_IO4)){ // MESH_DEBUG_PRINTLN("RAK base board is RAK19003/9"); - // MESH_DEBUG_PRINTLN("GPS is installed on Socket C"); + // MESH_DEBUG_PRINTLN("GPS is installed on Socket C"); } else if(gpsIsAwake(WB_IO5)){ // MESH_DEBUG_PRINTLN("RAK base board is RAK19001/11"); // MESH_DEBUG_PRINTLN("GPS is installed on Socket F"); } else{ - MESH_DEBUG_PRINTLN("No GPS found"); + MESH_DEBUG_PRINTLN("No GPS found"); gps_active = false; gps_detected = false; return; diff --git a/variants/heltec_v3/platformio.ini b/variants/heltec_v3/platformio.ini index edc683d2..2c5c124e 100644 --- a/variants/heltec_v3/platformio.ini +++ b/variants/heltec_v3/platformio.ini @@ -147,6 +147,26 @@ lib_deps = ${Heltec_lora32_v3.lib_deps} densaugeo/base64 @ ~1.4.0 +[env:Heltec_v3_sensor] +extends = Heltec_lora32_v3 +build_flags = + ${Heltec_lora32_v3.build_flags} + -D ADVERT_NAME='"Heltec v3 Sensor"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D ENV_PIN_SDA=33 + -D ENV_PIN_SCL=34 + -D DISPLAY_CLASS=SSD1306Display +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${Heltec_lora32_v3.build_src_filter} + + + +<../examples/simple_sensor> +lib_deps = + ${Heltec_lora32_v3.lib_deps} + ${esp32_ota.lib_deps} + [env:Heltec_WSL3_repeater] extends = Heltec_lora32_v3 build_flags = @@ -214,4 +234,3 @@ build_src_filter = ${Heltec_lora32_v3.build_src_filter} lib_deps = ${Heltec_lora32_v3.lib_deps} ${esp32_ota.lib_deps} - From 9f2a77c92eaae99a8395df54dbcf6ac0864e2bc1 Mon Sep 17 00:00:00 2001 From: Mike Cochrane Date: Fri, 18 Jul 2025 18:51:00 +1200 Subject: [PATCH 14/24] Add Melexis Contact-less Infrared Sensor - MLX90614 to Heltec V3 Sensor --- .../sensors/EnvironmentSensorManager.cpp | 23 +++++++++++++++++++ .../sensors/EnvironmentSensorManager.h | 1 + variants/heltec_v3/platformio.ini | 2 ++ 3 files changed, 26 insertions(+) diff --git a/src/helpers/sensors/EnvironmentSensorManager.cpp b/src/helpers/sensors/EnvironmentSensorManager.cpp index b9653248..7299b8fc 100644 --- a/src/helpers/sensors/EnvironmentSensorManager.cpp +++ b/src/helpers/sensors/EnvironmentSensorManager.cpp @@ -53,6 +53,12 @@ static Adafruit_INA3221 INA3221; static Adafruit_INA219 INA219(TELEM_INA219_ADDRESS); #endif +#if ENV_INCLUDE_MLX90614 +#define TELEM_MLX90614_ADDRESS 0x5A // MLX90614 IR temperature sensor I2C address +#include +static Adafruit_MLX90614 MLX90614; +#endif + #if ENV_INCLUDE_GPS && RAK_BOARD static uint32_t gpsResetPin = 0; static bool i2cGPSFlag = false; @@ -153,6 +159,16 @@ bool EnvironmentSensorManager::begin() { } #endif + #if ENV_INCLUDE_MLX90614 + if (MLX90614.begin(TELEM_MLX90614_ADDRESS, TELEM_WIRE)) { + MESH_DEBUG_PRINTLN("Found MLX90614 at address: %02X", TELEM_MLX90614_ADDRESS); + MLX90614_initialized = true; + } else { + MLX90614_initialized = false; + MESH_DEBUG_PRINTLN("MLX90614 was not found at I2C address %02X", TELEM_MLX90614_ADDRESS); + } + #endif + return true; } @@ -233,6 +249,13 @@ bool EnvironmentSensorManager::querySensors(uint8_t requester_permissions, Cayen } #endif + #if ENV_INCLUDE_MLX90614 + if (MLX90614_initialized) { + telemetry.addTemperature(TELEM_CHANNEL_SELF, MLX90614.readObjectTempC()); + telemetry.addTemperature(TELEM_CHANNEL_SELF + 1, MLX90614.readAmbientTempC()); + } + #endif + } return true; diff --git a/src/helpers/sensors/EnvironmentSensorManager.h b/src/helpers/sensors/EnvironmentSensorManager.h index 2a2b209a..535d3d0f 100644 --- a/src/helpers/sensors/EnvironmentSensorManager.h +++ b/src/helpers/sensors/EnvironmentSensorManager.h @@ -15,6 +15,7 @@ protected: bool INA219_initialized = false; bool SHTC3_initialized = false; bool LPS22HB_initialized = false; + bool MLX90614_initialized = false; bool gps_detected = false; bool gps_active = false; diff --git a/variants/heltec_v3/platformio.ini b/variants/heltec_v3/platformio.ini index 2c5c124e..a2670a94 100644 --- a/variants/heltec_v3/platformio.ini +++ b/variants/heltec_v3/platformio.ini @@ -157,6 +157,7 @@ build_flags = -D ADMIN_PASSWORD='"password"' -D ENV_PIN_SDA=33 -D ENV_PIN_SCL=34 + -D ENV_INCLUDE_MLX90614=1 -D DISPLAY_CLASS=SSD1306Display ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 @@ -166,6 +167,7 @@ build_src_filter = ${Heltec_lora32_v3.build_src_filter} lib_deps = ${Heltec_lora32_v3.lib_deps} ${esp32_ota.lib_deps} + adafruit/Adafruit MLX90614 Library @ ^2.1.5 [env:Heltec_WSL3_repeater] extends = Heltec_lora32_v3 From 93802fe25059bf9364b49305dd666403576030e8 Mon Sep 17 00:00:00 2001 From: Mike Cochrane Date: Fri, 18 Jul 2025 22:01:47 +1200 Subject: [PATCH 15/24] Add VL53L0X time-of-flight distance sensor to Heltec V3 Sensor --- .../sensors/EnvironmentSensorManager.cpp | 28 +++++++++++++++++++ .../sensors/EnvironmentSensorManager.h | 1 + variants/heltec_v3/platformio.ini | 2 ++ 3 files changed, 31 insertions(+) diff --git a/src/helpers/sensors/EnvironmentSensorManager.cpp b/src/helpers/sensors/EnvironmentSensorManager.cpp index 7299b8fc..c95b1828 100644 --- a/src/helpers/sensors/EnvironmentSensorManager.cpp +++ b/src/helpers/sensors/EnvironmentSensorManager.cpp @@ -59,6 +59,12 @@ static Adafruit_INA219 INA219(TELEM_INA219_ADDRESS); static Adafruit_MLX90614 MLX90614; #endif +#if ENV_INCLUDE_VL53L0X +#define TELEM_VL53L0X_ADDRESS 0x29 // VL53L0X time-of-flight distance sensor I2C address +#include +static Adafruit_VL53L0X VL53L0X; +#endif + #if ENV_INCLUDE_GPS && RAK_BOARD static uint32_t gpsResetPin = 0; static bool i2cGPSFlag = false; @@ -169,6 +175,16 @@ bool EnvironmentSensorManager::begin() { } #endif + #if ENV_INCLUDE_VL53L0X + if (VL53L0X.begin(TELEM_VL53L0X_ADDRESS, false, TELEM_WIRE)) { + MESH_DEBUG_PRINTLN("Found VL53L0X at address: %02X", TELEM_VL53L0X_ADDRESS); + VL53L0X_initialized = true; + } else { + VL53L0X_initialized = false; + MESH_DEBUG_PRINTLN("VL53L0X was not found at I2C address %02X", TELEM_VL53L0X_ADDRESS); + } + #endif + return true; } @@ -256,6 +272,18 @@ bool EnvironmentSensorManager::querySensors(uint8_t requester_permissions, Cayen } #endif + #if ENV_INCLUDE_VL53L0X + if (VL53L0X_initialized) { + VL53L0X_RangingMeasurementData_t measure; + VL53L0X.rangingTest(&measure, false); // pass in 'true' to get debug data + if (measure.RangeStatus != 4) { // phase failures + telemetry.addDistance(TELEM_CHANNEL_SELF, measure.RangeMilliMeter / 1000.0f); // convert mm to m + } else { + telemetry.addDistance(TELEM_CHANNEL_SELF, 0.0f); // no valid measurement + } + } + #endif + } return true; diff --git a/src/helpers/sensors/EnvironmentSensorManager.h b/src/helpers/sensors/EnvironmentSensorManager.h index 535d3d0f..63c56643 100644 --- a/src/helpers/sensors/EnvironmentSensorManager.h +++ b/src/helpers/sensors/EnvironmentSensorManager.h @@ -16,6 +16,7 @@ protected: bool SHTC3_initialized = false; bool LPS22HB_initialized = false; bool MLX90614_initialized = false; + bool VL53L0X_initialized = false; bool gps_detected = false; bool gps_active = false; diff --git a/variants/heltec_v3/platformio.ini b/variants/heltec_v3/platformio.ini index a2670a94..67415ae9 100644 --- a/variants/heltec_v3/platformio.ini +++ b/variants/heltec_v3/platformio.ini @@ -158,6 +158,7 @@ build_flags = -D ENV_PIN_SDA=33 -D ENV_PIN_SCL=34 -D ENV_INCLUDE_MLX90614=1 + -D ENV_INCLUDE_VL53L0X=1 -D DISPLAY_CLASS=SSD1306Display ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 @@ -168,6 +169,7 @@ lib_deps = ${Heltec_lora32_v3.lib_deps} ${esp32_ota.lib_deps} adafruit/Adafruit MLX90614 Library @ ^2.1.5 + adafruit/Adafruit_VL53L0X @ ^1.2.4 [env:Heltec_WSL3_repeater] extends = Heltec_lora32_v3 From 9be28c2002f7bd4fbdfa56f2bde3b74553cc161d Mon Sep 17 00:00:00 2001 From: Florent Date: Fri, 18 Jul 2025 12:18:03 +0200 Subject: [PATCH 16/24] rak3x72: sensor target --- variants/rak3x72/platformio.ini | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/variants/rak3x72/platformio.ini b/variants/rak3x72/platformio.ini index 386866ba..2fee1a76 100644 --- a/variants/rak3x72/platformio.ini +++ b/variants/rak3x72/platformio.ini @@ -21,6 +21,14 @@ build_flags = ${rak3x72.build_flags} build_src_filter = ${rak3x72.build_src_filter} +<../examples/simple_repeater/main.cpp> +[env:rak3x72-sensor] +extends = rak3x72 +build_flags = ${rak3x72.build_flags} + -D ADVERT_NAME='"RAK3x72 Sensor"' + -D ADMIN_PASSWORD='"password"' +build_src_filter = ${rak3x72.build_src_filter} + +<../examples/simple_sensor> + [env:rak3x72_companion_radio_usb] extends = rak3x72 build_flags = ${rak3x72.build_flags} From c5783660c48590918dc284f0819e2ef3e3ab7eeb Mon Sep 17 00:00:00 2001 From: taco Date: Sat, 19 Jul 2025 16:09:54 +1000 Subject: [PATCH 17/24] radio.std_init() for heltec ct62 --- variants/heltec_ct62/target.cpp | 37 ++------------------------------- 1 file changed, 2 insertions(+), 35 deletions(-) diff --git a/variants/heltec_ct62/target.cpp b/variants/heltec_ct62/target.cpp index eae43bd1..a8c15f5f 100644 --- a/variants/heltec_ct62/target.cpp +++ b/variants/heltec_ct62/target.cpp @@ -10,43 +10,10 @@ ESP32RTCClock fallback_clock; AutoDiscoverRTCClock rtc_clock(fallback_clock); SensorManager sensors; -#ifndef LORA_CR - #define LORA_CR 5 -#endif - bool radio_init() { fallback_clock.begin(); - rtc_clock.begin(Wire); - -#ifdef SX126X_DIO3_TCXO_VOLTAGE - float tcxo = SX126X_DIO3_TCXO_VOLTAGE; -#else - float tcxo = 1.6f; -#endif - -#if defined(P_LORA_SCLK) - SPI.begin(P_LORA_SCLK, P_LORA_MISO, P_LORA_MOSI); -#endif - int status = radio.begin(LORA_FREQ, LORA_BW, LORA_SF, LORA_CR, RADIOLIB_SX126X_SYNC_WORD_PRIVATE, LORA_TX_POWER, 8, tcxo); - if (status != RADIOLIB_ERR_NONE) { - Serial.print("ERROR: radio init failed: "); - Serial.println(status); - return false; // fail - } - - radio.setCRC(1); - -#ifdef SX126X_CURRENT_LIMIT - radio.setCurrentLimit(SX126X_CURRENT_LIMIT); -#endif -#ifdef SX126X_DIO2_AS_RF_SWITCH - radio.setDio2AsRfSwitch(SX126X_DIO2_AS_RF_SWITCH); -#endif -#ifdef SX126X_RX_BOOSTED_GAIN - radio.setRxBoostedGainMode(SX126X_RX_BOOSTED_GAIN); -#endif - - return true; // success + rtc_clock.begin(Wire); + return radio.std_init(&SPI); } uint32_t radio_get_rng_seed() { From 599e3a187cac5b4ea7060abef6b4260215943f62 Mon Sep 17 00:00:00 2001 From: liamcottle Date: Sun, 20 Jul 2025 23:27:54 +1200 Subject: [PATCH 18/24] start rx again immediately after receive --- src/helpers/radiolib/RadioLibWrappers.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/helpers/radiolib/RadioLibWrappers.cpp b/src/helpers/radiolib/RadioLibWrappers.cpp index 96fb7aab..9014743a 100644 --- a/src/helpers/radiolib/RadioLibWrappers.cpp +++ b/src/helpers/radiolib/RadioLibWrappers.cpp @@ -96,8 +96,9 @@ bool RadioLibWrapper::isInRecvMode() const { } int RadioLibWrapper::recvRaw(uint8_t* bytes, int sz) { + int len = 0; if (state & STATE_INT_READY) { - int len = _radio->getPacketLength(); + len = _radio->getPacketLength(); if (len > 0) { if (len > sz) { len = sz; } int err = _radio->readData(bytes, len); @@ -110,7 +111,6 @@ int RadioLibWrapper::recvRaw(uint8_t* bytes, int sz) { } } state = STATE_IDLE; // need another startReceive() - return len; } if (state != STATE_RX) { @@ -121,7 +121,7 @@ int RadioLibWrapper::recvRaw(uint8_t* bytes, int sz) { MESH_DEBUG_PRINTLN("RadioLibWrapper: error: startReceive(%d)", err); } } - return 0; + return len; } uint32_t RadioLibWrapper::getEstAirtimeFor(int len_bytes) { From 296a1e45fb91e05a68dd79fd3e920f28569b2208 Mon Sep 17 00:00:00 2001 From: taco Date: Mon, 21 Jul 2025 18:17:32 +1000 Subject: [PATCH 19/24] fix: Wio Tracker L1: correct joystick press button pin --- variants/wio-tracker-l1/variant.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/variants/wio-tracker-l1/variant.h b/variants/wio-tracker-l1/variant.h index 9d6eb532..094f8edf 100644 --- a/variants/wio-tracker-l1/variant.h +++ b/variants/wio-tracker-l1/variant.h @@ -30,7 +30,7 @@ #define PIN_BUTTON3 (26) // Joystick Down #define PIN_BUTTON4 (27) // Joystick Left #define PIN_BUTTON5 (28) // Joystick Right -#define PIN_BUTTON6 (28) // Joystick Press +#define PIN_BUTTON6 (29) // Joystick Press #define PIN_USER_BTN PIN_BUTTON1 #define JOYSTICK_UP PIN_BUTTON2 #define JOYSTICK_DOWN PIN_BUTTON3 From f88ebad604c286f9b0e54dedd4faa0a9f2c00b46 Mon Sep 17 00:00:00 2001 From: recrof Date: Mon, 21 Jul 2025 15:30:14 +0200 Subject: [PATCH 20/24] enable all sensors on RAK4631 --- variants/rak4631/platformio.ini | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/variants/rak4631/platformio.ini b/variants/rak4631/platformio.ini index 33d3fbc8..29d07a78 100644 --- a/variants/rak4631/platformio.ini +++ b/variants/rak4631/platformio.ini @@ -9,7 +9,7 @@ build_flags = ${nrf52_base.build_flags} -D RAK_BOARD -D PIN_BOARD_SCL=14 -D PIN_BOARD_SDA=13 - -D PIN_GPS_TX=16 + -D PIN_GPS_TX=16 -D PIN_GPS_RX=15 -D PIN_GPS_EN=-1 -D PIN_OLED_RESET=-1 @@ -19,19 +19,27 @@ build_flags = ${nrf52_base.build_flags} -D SX126X_CURRENT_LIMIT=140 -D SX126X_RX_BOOSTED_GAIN=1 -D ENV_INCLUDE_GPS=1 + -D ENV_INCLUDE_AHTX0=1 + -D ENV_INCLUDE_BME280=1 + -D ENV_INCLUDE_BMP280=1 -D ENV_INCLUDE_SHTC3=1 -D ENV_INCLUDE_LPS22HB=1 + -D ENV_INCLUDE_INA3221=1 -D ENV_INCLUDE_INA219=1 build_src_filter = ${nrf52_base.build_src_filter} +<../variants/rak4631> - + + + lib_deps = ${nrf52_base.lib_deps} adafruit/Adafruit SSD1306 @ ^2.5.13 stevemarple/MicroNMEA @ ^2.0.6 - adafruit/Adafruit SHTC3 Library@^1.0.1 arduino-libraries/Arduino_LPS22HB@^1.0.2 - adafruit/Adafruit INA219@^1.2.3 + adafruit/Adafruit INA3221 Library @ ^1.0.1 + adafruit/Adafruit INA219 @ ^1.2.3 + adafruit/Adafruit AHTX0 @ ^2.0.5 + adafruit/Adafruit BME280 Library @ ^2.3.0 + adafruit/Adafruit BMP280 Library @ ^2.6.8 + adafruit/Adafruit SHTC3 Library @ ^1.0.1 sparkfun/SparkFun u-blox GNSS Arduino Library@^2.2.27 [env:RAK_4631_Repeater] From e48e64ae8446be6b8fbbbb22cff8cc8e07533adc Mon Sep 17 00:00:00 2001 From: recrof Date: Wed, 23 Jul 2025 12:17:40 +0200 Subject: [PATCH 21/24] fix: bad linker directory; added companion ble and usb roles --- variants/sensecap_solar/platformio.ini | 39 +++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/variants/sensecap_solar/platformio.ini b/variants/sensecap_solar/platformio.ini index 281f6688..9626e9cd 100644 --- a/variants/sensecap_solar/platformio.ini +++ b/variants/sensecap_solar/platformio.ini @@ -32,13 +32,11 @@ build_flags = ${nrf52_base.build_flags} build_src_filter = ${nrf52_base.build_src_filter} + + - + - +<../variants/SenseCap_Solar> + +<../variants/sensecap_solar> debug_tool = jlink upload_protocol = nrfutil lib_deps = ${nrf52_base.lib_deps} - rweather/Crypto @ ^0.4.0 adafruit/Adafruit INA3221 Library @ ^1.0.1 adafruit/Adafruit INA219 @ ^1.2.3 adafruit/Adafruit AHTX0 @ ^2.0.5 @@ -72,4 +70,37 @@ build_flags = ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 build_src_filter = ${SenseCap_Solar.build_src_filter} - +<../examples/simple_room_server/main.cpp> \ No newline at end of file + +<../examples/simple_room_server/main.cpp> + +[env:SenseCap_Solar_companion_radio_ble] +extends = SenseCap_Solar +build_flags = + ${SenseCap_Solar.build_flags} + -D MAX_CONTACTS=100 + -D MAX_GROUP_CHANNELS=8 + -D BLE_PIN_CODE=123456 + -D OFFLINE_QUEUE_SIZE=256 +; -D BLE_DEBUG_LOGGING=1 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${SenseCap_Solar.build_src_filter} + + + +<../examples/companion_radio> +lib_deps = + ${SenseCap_Solar.lib_deps} + densaugeo/base64 @ ~1.4.0 + +[env:SenseCap_Solar_companion_radio_usb] +extends = SenseCap_Solar +build_flags = + ${SenseCap_Solar.build_flags} + -D MAX_CONTACTS=100 + -D MAX_GROUP_CHANNELS=8 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${SenseCap_Solar.build_src_filter} + + + +<../examples/companion_radio> +lib_deps = + ${SenseCap_Solar.lib_deps} + densaugeo/base64 @ ~1.4.0 \ No newline at end of file From ea4aa93594727add712336c5c75f0b2b4c904509 Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Wed, 23 Jul 2025 21:40:37 +1000 Subject: [PATCH 22/24] * CommonCLI: "set radio " now with optional 5th param timeout_mins, for applying temporary radio params for that many mins * "advert" command now with longer delay, so that CLI reply is sent first --- examples/simple_repeater/main.cpp | 31 ++++++++++++++++++++++++++- examples/simple_room_server/main.cpp | 28 ++++++++++++++++++++++++ examples/simple_sensor/SensorMesh.cpp | 23 ++++++++++++++++++++ examples/simple_sensor/SensorMesh.h | 6 ++++++ src/helpers/CommonCLI.cpp | 24 +++++++++++++-------- src/helpers/CommonCLI.h | 1 + 6 files changed, 103 insertions(+), 10 deletions(-) diff --git a/examples/simple_repeater/main.cpp b/examples/simple_repeater/main.cpp index cee3a008..264b451b 100644 --- a/examples/simple_repeater/main.cpp +++ b/examples/simple_repeater/main.cpp @@ -120,7 +120,7 @@ struct NeighbourInfo { int8_t snr; // multiplied by 4, user should divide to get float value }; -#define CLI_REPLY_DELAY_MILLIS 1000 +#define CLI_REPLY_DELAY_MILLIS 600 class MyMesh : public mesh::Mesh, public CommonCLICallbacks { FILESYSTEM* _fs; @@ -134,6 +134,11 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks { NeighbourInfo neighbours[MAX_NEIGHBOURS]; #endif CayenneLPP telemetry; + unsigned long set_radio_at, revert_radio_at; + float pending_freq; + float pending_bw; + uint8_t pending_sf; + uint8_t pending_cr; ClientInfo* putClient(const mesh::Identity& id) { uint32_t min_time = 0xFFFFFFFF; @@ -558,6 +563,7 @@ public: { memset(known_clients, 0, sizeof(known_clients)); next_local_advert = next_flood_advert = 0; + set_radio_at = revert_radio_at = 0; _logging = false; #if MAX_NEIGHBOURS @@ -609,6 +615,16 @@ public: _cli.savePrefs(_fs); } + void applyTempRadioParams(float freq, float bw, uint8_t sf, uint8_t cr, int timeout_mins) override { + set_radio_at = futureMillis(2000); // give CLI reply some time to be sent back, before applying temp radio params + pending_freq = freq; + pending_bw = bw; + pending_sf = sf; + pending_cr = cr; + + revert_radio_at = futureMillis(2000 + timeout_mins*60*1000); // schedule when to revert radio params + } + bool formatFileSystem() override { #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) return InternalFS.format(); @@ -734,6 +750,19 @@ public: updateAdvertTimer(); // schedule next local advert } + + if (set_radio_at && millisHasNowPassed(set_radio_at)) { // apply pending (temporary) radio params + set_radio_at = 0; // clear timer + radio_set_params(pending_freq, pending_bw, pending_sf, pending_cr); + MESH_DEBUG_PRINTLN("Temp radio params"); + } + + if (revert_radio_at && millisHasNowPassed(revert_radio_at)) { // revert radio params to orig + revert_radio_at = 0; // clear timer + radio_set_params(_prefs.freq, _prefs.bw, _prefs.sf, _prefs.cr); + MESH_DEBUG_PRINTLN("Radio params restored"); + } + #ifdef DISPLAY_CLASS ui_task.loop(); #endif diff --git a/examples/simple_room_server/main.cpp b/examples/simple_room_server/main.cpp index 1ab08d9b..c9feaa68 100644 --- a/examples/simple_room_server/main.cpp +++ b/examples/simple_room_server/main.cpp @@ -165,6 +165,11 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks { int next_post_idx; PostInfo posts[MAX_UNSYNCED_POSTS]; // cyclic queue CayenneLPP telemetry; + unsigned long set_radio_at, revert_radio_at; + float pending_freq; + float pending_bw; + uint8_t pending_sf; + uint8_t pending_cr; ClientInfo* putClient(const mesh::Identity& id) { for (int i = 0; i < num_clients; i++) { @@ -721,6 +726,7 @@ public: { next_local_advert = next_flood_advert = 0; _logging = false; + set_radio_at = revert_radio_at = 0; // defaults memset(&_prefs, 0, sizeof(_prefs)); @@ -778,6 +784,16 @@ public: _cli.savePrefs(_fs); } + void applyTempRadioParams(float freq, float bw, uint8_t sf, uint8_t cr, int timeout_mins) override { + set_radio_at = futureMillis(2000); // give CLI reply some time to be sent back, before applying temp radio params + pending_freq = freq; + pending_bw = bw; + pending_sf = sf; + pending_cr = cr; + + revert_radio_at = futureMillis(2000 + timeout_mins*60*1000); // schedule when to revert radio params + } + bool formatFileSystem() override { #if defined(NRF52_PLATFORM) return InternalFS.format(); @@ -922,6 +938,18 @@ public: updateAdvertTimer(); // schedule next local advert } + if (set_radio_at && millisHasNowPassed(set_radio_at)) { // apply pending (temporary) radio params + set_radio_at = 0; // clear timer + radio_set_params(pending_freq, pending_bw, pending_sf, pending_cr); + MESH_DEBUG_PRINTLN("Temp radio params"); + } + + if (revert_radio_at && millisHasNowPassed(revert_radio_at)) { // revert radio params to orig + revert_radio_at = 0; // clear timer + radio_set_params(_prefs.freq, _prefs.bw, _prefs.sf, _prefs.cr); + MESH_DEBUG_PRINTLN("Radio params restored"); + } + #ifdef DISPLAY_CLASS ui_task.loop(); #endif diff --git a/examples/simple_sensor/SensorMesh.cpp b/examples/simple_sensor/SensorMesh.cpp index abdc7182..0816af72 100644 --- a/examples/simple_sensor/SensorMesh.cpp +++ b/examples/simple_sensor/SensorMesh.cpp @@ -722,6 +722,7 @@ SensorMesh::SensorMesh(mesh::MainBoard& board, mesh::Radio& radio, mesh::Millise dirty_contacts_expiry = 0; last_read_time = 0; num_alert_tasks = 0; + set_radio_at = revert_radio_at = 0; // defaults memset(&_prefs, 0, sizeof(_prefs)); @@ -772,6 +773,16 @@ bool SensorMesh::formatFileSystem() { #endif } +void SensorMesh::applyTempRadioParams(float freq, float bw, uint8_t sf, uint8_t cr, int timeout_mins) { + set_radio_at = futureMillis(2000); // give CLI reply some time to be sent back, before applying temp radio params + pending_freq = freq; + pending_bw = bw; + pending_sf = sf; + pending_cr = cr; + + revert_radio_at = futureMillis(2000 + timeout_mins*60*1000); // schedule when to revert radio params +} + void SensorMesh::sendSelfAdvertisement(int delay_millis) { mesh::Packet* pkt = createSelfAdvert(); if (pkt) { @@ -847,6 +858,18 @@ void SensorMesh::loop() { updateAdvertTimer(); // schedule next local advert } + if (set_radio_at && millisHasNowPassed(set_radio_at)) { // apply pending (temporary) radio params + set_radio_at = 0; // clear timer + radio_set_params(pending_freq, pending_bw, pending_sf, pending_cr); + MESH_DEBUG_PRINTLN("Temp radio params"); + } + + if (revert_radio_at && millisHasNowPassed(revert_radio_at)) { // revert radio params to orig + revert_radio_at = 0; // clear timer + radio_set_params(_prefs.freq, _prefs.bw, _prefs.sf, _prefs.cr); + MESH_DEBUG_PRINTLN("Radio params restored"); + } + uint32_t curr = getRTCClock()->getCurrentTime(); if (curr >= last_read_time + SENSOR_READ_INTERVAL_SECS) { telemetry.reset(); diff --git a/examples/simple_sensor/SensorMesh.h b/examples/simple_sensor/SensorMesh.h index 3f687c3f..ea6bec74 100644 --- a/examples/simple_sensor/SensorMesh.h +++ b/examples/simple_sensor/SensorMesh.h @@ -90,6 +90,7 @@ public: } const uint8_t* getSelfIdPubKey() override { return self_id.pub_key; } void clearStats() override { } + void applyTempRadioParams(float freq, float bw, uint8_t sf, uint8_t cr, int timeout_mins) override; float getTelemValue(uint8_t channel, uint8_t type); @@ -154,6 +155,11 @@ private: int matching_peer_indexes[MAX_SEARCH_RESULTS]; int num_alert_tasks; Trigger* alert_tasks[MAX_CONCURRENT_ALERTS]; + unsigned long set_radio_at, revert_radio_at; + float pending_freq; + float pending_bw; + uint8_t pending_sf; + uint8_t pending_cr; void loadContacts(); void saveContacts(); diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index afb7dfbc..e036f0c0 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -132,7 +132,7 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch if (memcmp(command, "reboot", 6) == 0) { _board->reboot(); // doesn't return } else if (memcmp(command, "advert", 6) == 0) { - _callbacks->sendSelfAdvertisement(400); + _callbacks->sendSelfAdvertisement(1500); // longer delay, give CLI response time to be sent first strcpy(reply, "OK - Advert sent"); } else if (memcmp(command, "clock sync", 10) == 0) { uint32_t curr = getRTCClock()->getCurrentTime(); @@ -280,19 +280,25 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch strcpy(reply, _prefs->disable_fwd ? "OK - repeat is now OFF" : "OK - repeat is now ON"); } else if (memcmp(config, "radio ", 6) == 0) { strcpy(tmp, &config[6]); - const char *parts[4]; - int num = mesh::Utils::parseTextParts(tmp, parts, 4); + const char *parts[5]; + int num = mesh::Utils::parseTextParts(tmp, parts, 5); float freq = num > 0 ? atof(parts[0]) : 0.0f; float bw = num > 1 ? atof(parts[1]) : 0.0f; uint8_t sf = num > 2 ? atoi(parts[2]) : 0; uint8_t cr = num > 3 ? atoi(parts[3]) : 0; + int temp_timeout_mins = num > 4 ? atoi(parts[4]) : 0; if (freq >= 300.0f && freq <= 2500.0f && sf >= 7 && sf <= 12 && cr >= 5 && cr <= 8 && bw >= 7.0f && bw <= 500.0f) { - _prefs->sf = sf; - _prefs->cr = cr; - _prefs->freq = freq; - _prefs->bw = bw; - _callbacks->savePrefs(); - strcpy(reply, "OK - reboot to apply"); + if (temp_timeout_mins > 0) { + _callbacks->applyTempRadioParams(freq, bw, sf, cr, temp_timeout_mins); + sprintf(reply, "OK - temp params for %d mins", temp_timeout_mins); + } else { + _prefs->sf = sf; + _prefs->cr = cr; + _prefs->freq = freq; + _prefs->bw = bw; + _callbacks->savePrefs(); + strcpy(reply, "OK - reboot to apply"); + } } else { strcpy(reply, "Error, invalid radio params"); } diff --git a/src/helpers/CommonCLI.h b/src/helpers/CommonCLI.h index 91a5bd3b..e2608379 100644 --- a/src/helpers/CommonCLI.h +++ b/src/helpers/CommonCLI.h @@ -45,6 +45,7 @@ public: virtual void formatNeighborsReply(char *reply) = 0; virtual const uint8_t* getSelfIdPubKey() = 0; virtual void clearStats() = 0; + virtual void applyTempRadioParams(float freq, float bw, uint8_t sf, uint8_t cr, int timeout_mins) = 0; }; class CommonCLI { From 4689f9b425a164653ecd9c54c107713c979f4a00 Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Wed, 23 Jul 2025 22:04:47 +1000 Subject: [PATCH 23/24] * CommonCLI: reverted "set radio ..." command, added new "tempradio ..." command --- src/helpers/CommonCLI.cpp | 37 +++++++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index e036f0c0..d62253f9 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -165,6 +165,21 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch } } else if (memcmp(command, "neighbors", 9) == 0) { _callbacks->formatNeighborsReply(reply); + } else if (memcmp(command, "tempradio ", 10) == 0) { + strcpy(tmp, &command[10]); + const char *parts[5]; + int num = mesh::Utils::parseTextParts(tmp, parts, 5); + float freq = num > 0 ? atof(parts[0]) : 0.0f; + float bw = num > 1 ? atof(parts[1]) : 0.0f; + uint8_t sf = num > 2 ? atoi(parts[2]) : 0; + uint8_t cr = num > 3 ? atoi(parts[3]) : 0; + int temp_timeout_mins = num > 4 ? atoi(parts[4]) : 0; + if (freq >= 300.0f && freq <= 2500.0f && sf >= 7 && sf <= 12 && cr >= 5 && cr <= 8 && bw >= 7.0f && bw <= 500.0f && temp_timeout_mins > 0) { + _callbacks->applyTempRadioParams(freq, bw, sf, cr, temp_timeout_mins); + sprintf(reply, "OK - temp params for %d mins", temp_timeout_mins); + } else { + strcpy(reply, "Error, invalid params"); + } } else if (memcmp(command, "password ", 9) == 0) { // change admin password StrHelper::strncpy(_prefs->password, &command[9], sizeof(_prefs->password)); @@ -280,25 +295,19 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch strcpy(reply, _prefs->disable_fwd ? "OK - repeat is now OFF" : "OK - repeat is now ON"); } else if (memcmp(config, "radio ", 6) == 0) { strcpy(tmp, &config[6]); - const char *parts[5]; - int num = mesh::Utils::parseTextParts(tmp, parts, 5); + const char *parts[4]; + int num = mesh::Utils::parseTextParts(tmp, parts, 4); float freq = num > 0 ? atof(parts[0]) : 0.0f; float bw = num > 1 ? atof(parts[1]) : 0.0f; uint8_t sf = num > 2 ? atoi(parts[2]) : 0; uint8_t cr = num > 3 ? atoi(parts[3]) : 0; - int temp_timeout_mins = num > 4 ? atoi(parts[4]) : 0; if (freq >= 300.0f && freq <= 2500.0f && sf >= 7 && sf <= 12 && cr >= 5 && cr <= 8 && bw >= 7.0f && bw <= 500.0f) { - if (temp_timeout_mins > 0) { - _callbacks->applyTempRadioParams(freq, bw, sf, cr, temp_timeout_mins); - sprintf(reply, "OK - temp params for %d mins", temp_timeout_mins); - } else { - _prefs->sf = sf; - _prefs->cr = cr; - _prefs->freq = freq; - _prefs->bw = bw; - _callbacks->savePrefs(); - strcpy(reply, "OK - reboot to apply"); - } + _prefs->sf = sf; + _prefs->cr = cr; + _prefs->freq = freq; + _prefs->bw = bw; + _callbacks->savePrefs(); + strcpy(reply, "OK - reboot to apply"); } else { strcpy(reply, "Error, invalid radio params"); } From 365cb8963444a40021f966a4e0330e6e342e9728 Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Thu, 24 Jul 2025 20:30:35 +1000 Subject: [PATCH 24/24] * ver bump to 1.7.4 --- examples/companion_radio/MyMesh.h | 4 ++-- examples/simple_repeater/main.cpp | 4 ++-- examples/simple_room_server/main.cpp | 4 ++-- examples/simple_sensor/SensorMesh.h | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/examples/companion_radio/MyMesh.h b/examples/companion_radio/MyMesh.h index cd4a72dc..5ad6198f 100644 --- a/examples/companion_radio/MyMesh.h +++ b/examples/companion_radio/MyMesh.h @@ -10,11 +10,11 @@ #define FIRMWARE_VER_CODE 7 #ifndef FIRMWARE_BUILD_DATE -#define FIRMWARE_BUILD_DATE "15 Jul 2025" +#define FIRMWARE_BUILD_DATE "24 Jul 2025" #endif #ifndef FIRMWARE_VERSION -#define FIRMWARE_VERSION "v1.7.3" +#define FIRMWARE_VERSION "v1.7.4" #endif #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) diff --git a/examples/simple_repeater/main.cpp b/examples/simple_repeater/main.cpp index 264b451b..a28df7a4 100644 --- a/examples/simple_repeater/main.cpp +++ b/examples/simple_repeater/main.cpp @@ -22,11 +22,11 @@ /* ------------------------------ Config -------------------------------- */ #ifndef FIRMWARE_BUILD_DATE - #define FIRMWARE_BUILD_DATE "15 Jul 2025" + #define FIRMWARE_BUILD_DATE "24 Jul 2025" #endif #ifndef FIRMWARE_VERSION - #define FIRMWARE_VERSION "v1.7.3" + #define FIRMWARE_VERSION "v1.7.4" #endif #ifndef LORA_FREQ diff --git a/examples/simple_room_server/main.cpp b/examples/simple_room_server/main.cpp index c9feaa68..9a416835 100644 --- a/examples/simple_room_server/main.cpp +++ b/examples/simple_room_server/main.cpp @@ -22,11 +22,11 @@ /* ------------------------------ Config -------------------------------- */ #ifndef FIRMWARE_BUILD_DATE - #define FIRMWARE_BUILD_DATE "15 Jul 2025" + #define FIRMWARE_BUILD_DATE "24 Jul 2025" #endif #ifndef FIRMWARE_VERSION - #define FIRMWARE_VERSION "v1.7.3" + #define FIRMWARE_VERSION "v1.7.4" #endif #ifndef LORA_FREQ diff --git a/examples/simple_sensor/SensorMesh.h b/examples/simple_sensor/SensorMesh.h index ea6bec74..8f6e3bc3 100644 --- a/examples/simple_sensor/SensorMesh.h +++ b/examples/simple_sensor/SensorMesh.h @@ -49,11 +49,11 @@ struct ContactInfo { }; #ifndef FIRMWARE_BUILD_DATE - #define FIRMWARE_BUILD_DATE "2 Jul 2025" + #define FIRMWARE_BUILD_DATE "24 Jul 2025" #endif #ifndef FIRMWARE_VERSION - #define FIRMWARE_VERSION "v1.7.2" + #define FIRMWARE_VERSION "v1.7.4" #endif #define FIRMWARE_ROLE "sensor"