From 696a82d7c235c09dac4b071d97df3b9e01ff8f30 Mon Sep 17 00:00:00 2001 From: taco Date: Wed, 1 Jul 2026 18:17:48 +1000 Subject: [PATCH] LR2021 multi-SF support (side detectors) --- examples/simple_repeater/MyMesh.cpp | 6 +++ examples/simple_repeater/MyMesh.h | 4 ++ src/helpers/CommonCLI.cpp | 30 ++++++++++++++ src/helpers/CommonCLI.h | 7 ++++ src/helpers/radiolib/CustomLR2021Wrapper.h | 47 +++++++++++++++++++++- src/helpers/radiolib/RadioLibWrappers.h | 2 + 6 files changed, 95 insertions(+), 1 deletion(-) diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index 09f74cbea..cc0cc1f1a 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -1064,6 +1064,12 @@ bool MyMesh::setRxBoostedGain(bool enable) { return radio_driver.setRxBoostedGainMode(enable); } +#if defined(USE_LR2021) +bool MyMesh::configSideDetectors(const uint8_t sideDetSFs[], uint8_t num) { + return radio_driver.configSideDetectors(sideDetSFs, num); +} +#endif + void MyMesh::formatNeighborsReply(char *reply) { char *dp = reply; diff --git a/examples/simple_repeater/MyMesh.h b/examples/simple_repeater/MyMesh.h index aa7d30b06..6a260e339 100644 --- a/examples/simple_repeater/MyMesh.h +++ b/examples/simple_repeater/MyMesh.h @@ -255,4 +255,8 @@ public: bool setRxBoostedGain(bool enable) override; + #if defined(USE_LR2021) + virtual bool configSideDetectors(const uint8_t sideDetSFs[], uint8_t num) override; + #endif + }; diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index 9d0571d98..bb54197f5 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -753,6 +753,28 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep _prefs->adc_multiplier = 0.0f; strcpy(reply, "Error: unsupported"); }; + #if defined(USE_LR2021) + } else if (memcmp(config, "extra.sf ", 9) == 0) { + strcpy(tmp, &config[9]); + const char *parts[4]; + uint8_t sideDetSFs[4]; + int num = mesh::Utils::parseTextParts(tmp, parts, 4); + if (num > 3) { + sprintf(reply, "Invalid extra SF config"); + } else { + for (int i = 0; i < num; i++) { + sideDetSFs[i] = atoi(parts[i]); + } + sideDetSFs[num] = 0; + if (_callbacks->configSideDetectors(sideDetSFs, num)) { + for (int i = 0; i <= num; i++) _prefs->extra_sf[i] = sideDetSFs[i]; + savePrefs(); + sprintf(reply, "OK - extra SFs set"); + } else { + sprintf(reply, "Invalid extra SF config"); + } + } + #endif } else { strcpy(reply, "unknown config: "); StrHelper::strncpy(&reply[16], config, 160-17); @@ -922,6 +944,14 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* rep #else strcpy(reply, "ERROR: Power management not supported"); #endif + } else if (memcmp(config, "extra.sf", 8) == 0) { + char* tmp = reply; + for (int i = 0; i < 3 && _prefs->extra_sf[i] != 0; i++) { + tmp += sprintf(tmp, "%s%d", (i == 0) ? "" : ",", _prefs->extra_sf[i]); + } + if (tmp == reply) { + sprintf(reply, "No extra SF configured"); + } } else { sprintf(reply, "??: %s", config); } diff --git a/src/helpers/CommonCLI.h b/src/helpers/CommonCLI.h index 69fe03150..fafabcb17 100644 --- a/src/helpers/CommonCLI.h +++ b/src/helpers/CommonCLI.h @@ -68,6 +68,7 @@ public: uint8_t path_hash_mode = 0; // which path mode to use when sending uint8_t loop_detect = 0; uint8_t cad_enabled = 0; // hardware Channel Activity Detection before TX (boolean) + uint8_t extra_sf[4]; private: class RadioPrefs : public ConfigSerializer { @@ -238,6 +239,12 @@ public: virtual bool setRxBoostedGain(bool enable) { return false; // CommonCLI reports unsupported if not overridden by wrapper }; + + #if defined(USE_LR2021) + virtual bool configSideDetectors(const uint8_t sideDetSFs[], uint8_t num) { + return false; // Override in wrapper + } + #endif }; class CommonCLI { diff --git a/src/helpers/radiolib/CustomLR2021Wrapper.h b/src/helpers/radiolib/CustomLR2021Wrapper.h index 0f1b1f9e5..6d0204ac1 100644 --- a/src/helpers/radiolib/CustomLR2021Wrapper.h +++ b/src/helpers/radiolib/CustomLR2021Wrapper.h @@ -21,6 +21,46 @@ public: ((CustomLR2021 *)_radio)->setBandwidth(bw); ((CustomLR2021 *)_radio)->setCodingRate(cr); updatePreamble(sf); + applySideDetectorConfig(); + } + + bool configSideDetectors(const uint8_t* sideDetSFs, uint8_t num) override { + LR2021LoRaSideDetector_t tmp[3]; + uint8_t sf = getSpreadingFactor(); + + if (sf >= 10 && num > 1) { return false; } // only 1 side detector allowed when primary SF >= 10 + for (int i = 0; i < num; i++) { + if (sideDetSFs[i] > 12 || sideDetSFs[i] < 5) { return false; } // must be valid SF + if (sideDetSFs[i] <= sf) { return false; } // must be < primary SF + if (sideDetSFs[i] > sf + 4) { return false; } // span must not be > 4 + + tmp[i].sf = sideDetSFs[i]; + if (sideDetSFs[i] == 10) { // TODO: set ldro=true when tSym >=16 + tmp[i].ldro = true; + } else { + tmp[i].ldro = false; + } + tmp[i].invertIQ = false; + tmp[i].syncWord = 0x12; + } + int16_t status = ((CustomLR2021 *)_radio)->setSideDetector(tmp, num); + RadioLibWrapper::idle(); // trigger startReceive() + MESH_DEBUG_PRINTLN("setSideDetector() returned %d", status); + + if (status == RADIOLIB_ERR_NONE) { + for (int i = 0; i < num; i++) { _sideDet[i] = tmp[i]; } + _numSideDet = num; + } else { + return false; + } + + return true; + } + + int16_t applySideDetectorConfig() { + int16_t status = ((CustomLR2021 *)_radio)->setSideDetector(_sideDet, _numSideDet); + RadioLibWrapper::idle(); // trigger startReceive() + return status; } bool isReceivingPacket() override { @@ -44,7 +84,7 @@ public: uint8_t getSpreadingFactor() const override { return ((CustomLR2021 *)_radio)->getSpreadingFactor(); } bool setRxBoostedGainMode(bool en) override { - ((CustomLR2021 *)_radio)->standby(); // radio must be in standby to accept the setRxBoostedGainMode command, otherwise it returns -707 error. + ((CustomLR2021 *)_radio)->standby(); // LR2021 must be in standby to accept setRxBoostedGainMode int16_t status = ((CustomLR2021 *)_radio)->setRxBoostedGainMode(en ? LR2021_RX_BOOST_LEVEL: 0); RadioLibWrapper::idle(); // trigger startReceive() return status == RADIOLIB_ERR_NONE; @@ -54,4 +94,9 @@ public: return ((CustomLR2021 *)_radio)->getRxBoostedGainMode(); } + protected: + LR2021LoRaSideDetector_t _sideDet[3]; + size_t _numSideDet = 0; + + }; diff --git a/src/helpers/radiolib/RadioLibWrappers.h b/src/helpers/radiolib/RadioLibWrappers.h index e886c81b2..4ca958e47 100644 --- a/src/helpers/radiolib/RadioLibWrappers.h +++ b/src/helpers/radiolib/RadioLibWrappers.h @@ -77,6 +77,8 @@ public: virtual bool setRxBoostedGainMode(bool) { return false; } virtual bool getRxBoostedGainMode() const { return false; } + + virtual bool configSideDetectors(const uint8_t sideDetSFs[], uint8_t num) { return false; } }; /**