mirror of
https://github.com/MarekZegare4/MeshCore-Solo.git
synced 2026-09-14 15:16:40 +00:00
Merge upstream companion-v1.17.0 (CAD) into power-saving
Adopts hardware Channel Activity Detection (wired into RadioLibWrapper::isChannelActive() alongside our RSSI-threshold check and RX duty-cycle power-save), MCU temperature telemetry, LR2021 standby workaround, DISPLAY_SCALE/FLIP overrides, NRF52Board shutdownPeripherals() refactor, and misc upstream fixes. Declines upstream's ConfigSerializer-based NodePrefs rewrite, MultiSerialInterface/interface_manager, and UIColor palette system — each would have broken large parts of the Solo-specific feature set (NodePrefs fields, per-variant single serial_interface, enum-based DisplayDriver::Color). Flagged as candidate follow-up migrations, not permanent no's. Also fixes several pre-existing bugs surfaced while chasing silent merge breaks (stale newMsg() override signature in ui-tiny/ui-orig, dead UIEventType::newContactMessage case, missing ContactsIterator init), bumps FIRMWARE_VERSION/MESHCORE_VERSION to 1.17, and fixes a missing <cstdlib> include that broke the native ConfigSerializer unit tests. Verified via 13+ pio run builds across ESP32/nRF52, all 3 companion UI variants, and 7 display drivers, plus the full native unit test suite (33/33 passing). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -147,11 +147,10 @@ uint8_t MyMesh::handleLoginReq(const mesh::Identity& sender, const uint8_t* secr
|
||||
uint8_t MyMesh::handleAnonRegionsReq(const mesh::Identity& sender, uint32_t sender_timestamp, const uint8_t* data) {
|
||||
if (anon_limiter.allow(rtc_clock.getCurrentTime())) {
|
||||
// request data has: {reply-path-len}{reply-path}
|
||||
reply_path_len = *data & 63;
|
||||
reply_path_hash_size = (*data >> 6) + 1;
|
||||
data++;
|
||||
reply_path_len = *data++;
|
||||
if (!mesh::Packet::isValidPathLen(reply_path_len)) return 0; // reject - bad encoding
|
||||
|
||||
memcpy(reply_path, data, ((uint8_t)reply_path_len) * reply_path_hash_size);
|
||||
mesh::Packet::writePath(reply_path, data, reply_path_len);
|
||||
// data += (uint8_t)reply_path_len * reply_path_hash_size;
|
||||
|
||||
memcpy(reply_data, &sender_timestamp, 4); // prefix with sender_timestamp, like a tag
|
||||
@@ -166,11 +165,10 @@ uint8_t MyMesh::handleAnonRegionsReq(const mesh::Identity& sender, uint32_t send
|
||||
uint8_t MyMesh::handleAnonOwnerReq(const mesh::Identity& sender, uint32_t sender_timestamp, const uint8_t* data) {
|
||||
if (anon_limiter.allow(rtc_clock.getCurrentTime())) {
|
||||
// request data has: {reply-path-len}{reply-path}
|
||||
reply_path_len = *data & 63;
|
||||
reply_path_hash_size = (*data >> 6) + 1;
|
||||
data++;
|
||||
reply_path_len = *data++;
|
||||
if (!mesh::Packet::isValidPathLen(reply_path_len)) return 0; // reject - bad encoding
|
||||
|
||||
memcpy(reply_path, data, ((uint8_t)reply_path_len) * reply_path_hash_size);
|
||||
mesh::Packet::writePath(reply_path, data, reply_path_len);
|
||||
// data += (uint8_t)reply_path_len * reply_path_hash_size;
|
||||
|
||||
memcpy(reply_data, &sender_timestamp, 4); // prefix with sender_timestamp, like a tag
|
||||
@@ -186,11 +184,10 @@ uint8_t MyMesh::handleAnonOwnerReq(const mesh::Identity& sender, uint32_t sender
|
||||
uint8_t MyMesh::handleAnonClockReq(const mesh::Identity& sender, uint32_t sender_timestamp, const uint8_t* data) {
|
||||
if (anon_limiter.allow(rtc_clock.getCurrentTime())) {
|
||||
// request data has: {reply-path-len}{reply-path}
|
||||
reply_path_len = *data & 63;
|
||||
reply_path_hash_size = (*data >> 6) + 1;
|
||||
data++;
|
||||
reply_path_len = *data++;
|
||||
if (!mesh::Packet::isValidPathLen(reply_path_len)) return 0; // reject - bad encoding
|
||||
|
||||
memcpy(reply_path, data, ((uint8_t)reply_path_len) * reply_path_hash_size);
|
||||
mesh::Packet::writePath(reply_path, data, reply_path_len);
|
||||
// data += (uint8_t)reply_path_len * reply_path_hash_size;
|
||||
|
||||
memcpy(reply_data, &sender_timestamp, 4); // prefix with sender_timestamp, like a tag
|
||||
@@ -549,8 +546,7 @@ uint32_t MyMesh::getDirectRetransmitDelay(const mesh::Packet *packet) {
|
||||
return getRNG()->nextInt(0, 5*t + 1);
|
||||
}
|
||||
|
||||
bool MyMesh::filterRecvFloodPacket(mesh::Packet* pkt) {
|
||||
// just try to determine region for packet (apply later in allowPacketForward())
|
||||
mesh::DispatcherAction MyMesh::onRecvPacket(mesh::Packet* pkt) {
|
||||
if (pkt->getRouteType() == ROUTE_TYPE_TRANSPORT_FLOOD) {
|
||||
recv_pkt_region = region_map.findMatch(pkt, REGION_DENY_FLOOD);
|
||||
} else if (pkt->getRouteType() == ROUTE_TYPE_FLOOD) {
|
||||
@@ -562,8 +558,7 @@ bool MyMesh::filterRecvFloodPacket(mesh::Packet* pkt) {
|
||||
} else {
|
||||
recv_pkt_region = NULL;
|
||||
}
|
||||
// do normal processing
|
||||
return false;
|
||||
return Mesh::onRecvPacket(pkt);
|
||||
}
|
||||
|
||||
void MyMesh::onAnonDataRecv(mesh::Packet *packet, const uint8_t *secret, const mesh::Identity &sender,
|
||||
@@ -576,7 +571,7 @@ void MyMesh::onAnonDataRecv(mesh::Packet *packet, const uint8_t *secret, const m
|
||||
data[len] = 0; // ensure null terminator
|
||||
uint8_t reply_len;
|
||||
|
||||
reply_path_len = -1;
|
||||
reply_path_len = 0xFF;
|
||||
if (data[4] == 0 || data[4] >= ' ') { // is password, ie. a login request
|
||||
reply_len = handleLoginReq(sender, secret, timestamp, &data[4], packet->isRouteFlood());
|
||||
} else if (data[4] == ANON_REQ_TYPE_REGIONS && packet->isRouteDirect()) {
|
||||
@@ -596,13 +591,12 @@ void MyMesh::onAnonDataRecv(mesh::Packet *packet, const uint8_t *secret, const m
|
||||
mesh::Packet* path = createPathReturn(sender, secret, packet->path, packet->path_len,
|
||||
PAYLOAD_TYPE_RESPONSE, reply_data, reply_len);
|
||||
if (path) sendFloodReply(path, SERVER_RESPONSE_DELAY, packet->getPathHashSize());
|
||||
} else if (reply_path_len < 0) {
|
||||
} else if (reply_path_len == 0xFF) {
|
||||
mesh::Packet* reply = createDatagram(PAYLOAD_TYPE_RESPONSE, sender, secret, reply_data, reply_len);
|
||||
if (reply) sendFloodReply(reply, SERVER_RESPONSE_DELAY, packet->getPathHashSize());
|
||||
} else {
|
||||
mesh::Packet* reply = createDatagram(PAYLOAD_TYPE_RESPONSE, sender, secret, reply_data, reply_len);
|
||||
uint8_t path_len = ((reply_path_hash_size - 1) << 6) | (reply_path_len & 63);
|
||||
if (reply) sendDirect(reply, reply_path, path_len, SERVER_RESPONSE_DELAY);
|
||||
if (reply) sendDirect(reply, reply_path, reply_path_len, SERVER_RESPONSE_DELAY);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -867,13 +861,13 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc
|
||||
set_radio_at = revert_radio_at = 0;
|
||||
_logging = false;
|
||||
region_load_active = false;
|
||||
recv_pkt_region = NULL;
|
||||
|
||||
#if MAX_NEIGHBOURS
|
||||
memset(neighbours, 0, sizeof(neighbours));
|
||||
#endif
|
||||
|
||||
// defaults
|
||||
memset(&_prefs, 0, sizeof(_prefs));
|
||||
_prefs.airtime_factor = 1.0;
|
||||
_prefs.rx_delay_base = 0.0f; // turn off by default, was 10.0;
|
||||
_prefs.tx_delay_factor = 0.5f; // was 0.25f
|
||||
@@ -893,6 +887,7 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc
|
||||
_prefs.flood_max_unscoped = 64;
|
||||
_prefs.flood_max_advert = 8;
|
||||
_prefs.interference_threshold = 0; // disabled
|
||||
_prefs.cad_enabled = 0; // hardware CAD before TX (off by default; 'set cad on')
|
||||
|
||||
// bridge defaults
|
||||
_prefs.bridge_enabled = 1; // enabled
|
||||
@@ -917,6 +912,7 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc
|
||||
_prefs.rx_boosted_gain = 1; // enabled by default;
|
||||
#endif
|
||||
#endif
|
||||
_prefs.radio_fem_rxgain = 1;
|
||||
|
||||
pending_discover_tag = 0;
|
||||
pending_discover_until = 0;
|
||||
@@ -965,6 +961,7 @@ void MyMesh::begin(FILESYSTEM *fs) {
|
||||
radio_driver.setRxBoostedGainMode(_prefs.rx_boosted_gain);
|
||||
MESH_DEBUG_PRINTLN("RX Boosted Gain Mode: %s",
|
||||
radio_driver.getRxBoostedGainMode() ? "Enabled" : "Disabled");
|
||||
board.setLoRaFemLnaEnabled(_prefs.radio_fem_rxgain);
|
||||
|
||||
updateAdvertTimer();
|
||||
updateFloodAdvertTimer();
|
||||
@@ -1059,9 +1056,13 @@ void MyMesh::setTxPower(int8_t power_dbm) {
|
||||
radio_driver.setTxPower(power_dbm);
|
||||
}
|
||||
|
||||
#if defined(USE_SX1262) || defined(USE_SX1268)
|
||||
void MyMesh::setRxBoostedGain(bool enable) {
|
||||
radio_driver.setRxBoostedGainMode(enable);
|
||||
bool MyMesh::setRxBoostedGain(bool enable) {
|
||||
return radio_driver.setRxBoostedGainMode(enable);
|
||||
}
|
||||
|
||||
#if defined(USE_LR2021)
|
||||
bool MyMesh::configSideDetectors(const uint8_t sideDetSFs[], uint8_t num, float bw) {
|
||||
return radio_driver.configSideDetectors(sideDetSFs, num, bw);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <LittleFS.h>
|
||||
#elif defined(ESP32)
|
||||
#include <SPIFFS.h>
|
||||
using File = fs::File;
|
||||
#endif
|
||||
|
||||
#ifdef WITH_RS232_BRIDGE
|
||||
@@ -69,11 +70,11 @@ struct NeighbourInfo {
|
||||
};
|
||||
|
||||
#ifndef FIRMWARE_BUILD_DATE
|
||||
#define FIRMWARE_BUILD_DATE "6 Jun 2026"
|
||||
#define FIRMWARE_BUILD_DATE "9 Aug 2026"
|
||||
#endif
|
||||
|
||||
#ifndef FIRMWARE_VERSION
|
||||
#define FIRMWARE_VERSION "v1.16.0"
|
||||
#define FIRMWARE_VERSION "v1.17.0"
|
||||
#endif
|
||||
|
||||
#define FIRMWARE_ROLE "repeater"
|
||||
@@ -91,8 +92,7 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks {
|
||||
CommonCLI _cli;
|
||||
uint8_t reply_data[MAX_PACKET_PAYLOAD];
|
||||
uint8_t reply_path[MAX_PATH_SIZE];
|
||||
int8_t reply_path_len;
|
||||
uint8_t reply_path_hash_size;
|
||||
uint8_t reply_path_len;
|
||||
TransportKeyStore key_store;
|
||||
RegionMap region_map, temp_map;
|
||||
RegionEntry* load_stack[8];
|
||||
@@ -150,6 +150,9 @@ protected:
|
||||
int getInterferenceThreshold() const override {
|
||||
return _prefs.interference_threshold;
|
||||
}
|
||||
bool getCADEnabled() const override {
|
||||
return _prefs.cad_enabled;
|
||||
}
|
||||
int getAGCResetInterval() const override {
|
||||
return ((int)_prefs.agc_reset_interval) * 4000; // milliseconds
|
||||
}
|
||||
@@ -163,7 +166,7 @@ protected:
|
||||
}
|
||||
#endif
|
||||
|
||||
bool filterRecvFloodPacket(mesh::Packet* pkt) override;
|
||||
mesh::DispatcherAction onRecvPacket(mesh::Packet* pkt) override;
|
||||
|
||||
void onAnonDataRecv(mesh::Packet* packet, const uint8_t* secret, const mesh::Identity& sender, uint8_t* data, size_t len) override;
|
||||
int searchPeersByHash(const uint8_t* hash) override;
|
||||
@@ -249,7 +252,10 @@ public:
|
||||
// To check if there is pending work
|
||||
bool hasPendingWork() const;
|
||||
|
||||
#if defined(USE_SX1262) || defined(USE_SX1268)
|
||||
void setRxBoostedGain(bool enable) override;
|
||||
#endif
|
||||
bool setRxBoostedGain(bool enable) override;
|
||||
|
||||
#if defined(USE_LR2021)
|
||||
virtual bool configSideDetectors(const uint8_t sideDetSFs[], uint8_t num, float bw) override;
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
@@ -8,6 +8,11 @@
|
||||
static UITask ui_task(display);
|
||||
#endif
|
||||
|
||||
#ifdef ETHERNET_ENABLED
|
||||
#define ETHERNET_CLI_BANNER "MeshCore Repeater CLI"
|
||||
#include <helpers/nrf52/EthernetCLI.h>
|
||||
#endif
|
||||
|
||||
StdRNG fast_rng;
|
||||
SimpleMeshTables tables;
|
||||
|
||||
@@ -18,6 +23,9 @@ void halt() {
|
||||
}
|
||||
|
||||
static char command[160];
|
||||
#ifdef ETHERNET_ENABLED
|
||||
static char ethernet_command[160];
|
||||
#endif
|
||||
|
||||
// For power saving
|
||||
unsigned long POWERSAVING_FIRSTSLEEP_SECS = 120; // The first sleep (if enabled) from boot
|
||||
@@ -33,6 +41,10 @@ void setup() {
|
||||
|
||||
board.begin();
|
||||
|
||||
#ifdef HAS_EXTERNAL_WATCHDOG
|
||||
external_watchdog.begin();
|
||||
#endif
|
||||
|
||||
#if defined(MESH_DEBUG) && defined(NRF52_PLATFORM)
|
||||
// give some extra time for serial to settle so
|
||||
// boot debug messages can be seen on terminal
|
||||
@@ -86,6 +98,9 @@ void setup() {
|
||||
mesh::Utils::printHex(Serial, the_mesh.self_id.pub_key, PUB_KEY_SIZE); Serial.println();
|
||||
|
||||
command[0] = 0;
|
||||
#ifdef ETHERNET_ENABLED
|
||||
ethernet_command[0] = 0;
|
||||
#endif
|
||||
|
||||
sensors.begin();
|
||||
|
||||
@@ -95,6 +110,10 @@ void setup() {
|
||||
ui_task.begin(the_mesh.getNodePrefs(), FIRMWARE_BUILD_DATE, FIRMWARE_VERSION);
|
||||
#endif
|
||||
|
||||
#ifdef ETHERNET_ENABLED
|
||||
ethernet_start_task();
|
||||
#endif
|
||||
|
||||
// send out initial zero hop Advertisement to the mesh
|
||||
#if ENABLE_ADVERT_ON_BOOT == 1
|
||||
the_mesh.sendSelfAdvertisement(16000, false);
|
||||
@@ -104,6 +123,7 @@ void setup() {
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// Handle Serial CLI
|
||||
int len = strlen(command);
|
||||
while (Serial.available() && len < sizeof(command)-1) {
|
||||
char c = Serial.read();
|
||||
@@ -122,7 +142,14 @@ void loop() {
|
||||
Serial.print('\n');
|
||||
command[len - 1] = 0; // replace newline with C string null terminator
|
||||
char reply[160];
|
||||
reply[0] = 0;
|
||||
#ifdef ETHERNET_ENABLED
|
||||
if (!ethernet_handle_command(command, reply)) {
|
||||
the_mesh.handleCommand(0, command, reply);
|
||||
}
|
||||
#else
|
||||
the_mesh.handleCommand(0, command, reply); // NOTE: there is no sender_timestamp via serial!
|
||||
#endif
|
||||
if (reply[0]) {
|
||||
Serial.print(" -> "); Serial.println(reply);
|
||||
}
|
||||
@@ -130,7 +157,20 @@ void loop() {
|
||||
command[0] = 0; // reset command buffer
|
||||
}
|
||||
|
||||
#if defined(PIN_USER_BTN) && defined(_SEEED_SENSECAP_SOLAR_H_)
|
||||
#ifdef ETHERNET_ENABLED
|
||||
ethernet_loop_maintain();
|
||||
if (ethernet_read_line(ethernet_command, sizeof(ethernet_command))) {
|
||||
char reply[160];
|
||||
reply[0] = 0;
|
||||
if (!ethernet_handle_command(ethernet_command, reply)) {
|
||||
the_mesh.handleCommand(0, ethernet_command, reply);
|
||||
}
|
||||
ethernet_send_reply(reply);
|
||||
ethernet_command[0] = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(PIN_USER_BTN) && defined(_SEEED_SENSECAP_SOLAR_H_) && !defined(DISPLAY_CLASS)
|
||||
// Hold the user button to power off the SenseCAP Solar repeater.
|
||||
int btnState = digitalRead(PIN_USER_BTN);
|
||||
if (btnState == LOW) {
|
||||
@@ -152,6 +192,9 @@ void loop() {
|
||||
#endif
|
||||
rtc_clock.tick();
|
||||
|
||||
#ifdef HAS_EXTERNAL_WATCHDOG
|
||||
external_watchdog.loop();
|
||||
#endif
|
||||
if (the_mesh.getNodePrefs()->powersaving_enabled && !the_mesh.hasPendingWork()) {
|
||||
#if defined(NRF52_PLATFORM)
|
||||
board.sleep(0); // nrf ignores seconds param, sleeps whenever possible
|
||||
|
||||
Reference in New Issue
Block a user