diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index 4930e81e9a..e73af5b77a 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -396,7 +396,11 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, char* command, char* re bool enabled = l->isEnabled(); // is EN pin on ? bool fix = l->isValid(); // has fix ? int sats = l->satellitesCount(); - bool active = !strcmp(_sensors->getSettingByKey("gps"), "1"); + // getSettingByKey() returns NULL when no "gps" setting is registered + // (no GPS detected at boot) -- treat that as "deactivated" rather than + // handing strcmp() a NULL, which crashes. + const char* gps_setting = _sensors->getSettingByKey("gps"); + bool active = gps_setting != NULL && strcmp(gps_setting, "1") == 0; if (enabled) { sprintf(reply, "on, %s, %s, %d sats", active?"active":"deactivated", diff --git a/src/helpers/ConfigSerializer.cpp b/src/helpers/ConfigSerializer.cpp index adff147f47..a0911303cd 100644 --- a/src/helpers/ConfigSerializer.cpp +++ b/src/helpers/ConfigSerializer.cpp @@ -1,5 +1,8 @@ #include "ConfigSerializer.h" +#include // atoi/atol/atof -- reaches these via Arduino.h on MCU + // targets, but not in the host `native` test build + bool ConfigSerializer::saveSerial(Stream& s) { Context context(&s, OP::WRITE); _context = &context; // set the context for structure() call diff --git a/src/helpers/radiolib/CustomLLCC68Wrapper.h b/src/helpers/radiolib/CustomLLCC68Wrapper.h index ae0fe0a253..d5f97c65fd 100644 --- a/src/helpers/radiolib/CustomLLCC68Wrapper.h +++ b/src/helpers/radiolib/CustomLLCC68Wrapper.h @@ -35,7 +35,7 @@ class CustomLLCC68Wrapper : public RadioLibWrapper { } uint8_t getSpreadingFactor() const override { return ((CustomLLCC68 *)_radio)->spreadingFactor; } - void doResetAGC() override { sx126xResetAGC((SX126x *)_radio); } + void doResetAGC() override { sx126xResetAGC((SX126x *)_radio, getRxBoostedGainMode()); } bool setRxBoostedGainMode(bool en) override { return ((CustomLLCC68 *)_radio)->setRxBoostedGainMode(en) == RADIOLIB_ERR_NONE; diff --git a/src/helpers/radiolib/RadioLibWrappers.cpp b/src/helpers/radiolib/RadioLibWrappers.cpp index e4d2ba1c27..27d0cd71c2 100644 --- a/src/helpers/radiolib/RadioLibWrappers.cpp +++ b/src/helpers/radiolib/RadioLibWrappers.cpp @@ -11,6 +11,14 @@ #define NUM_NOISE_FLOOR_SAMPLES 64 #define SAMPLING_THRESHOLD 14 +// Payload budget calcMaxPacketMillis() assumes when the modem cannot tell it +// how long a packet takes. Long on purpose: this deadline exists to break a +// stuck header IRQ, and one that expires early would clear the flags of a +// packet still arriving. +#ifndef MAX_PACKET_FALLBACK_PAYLOAD_US + #define MAX_PACKET_FALLBACK_PAYLOAD_US 4000000UL +#endif + static volatile uint8_t state = STATE_IDLE; // this function is called when a complete packet @@ -133,7 +141,21 @@ int RadioLibWrapper::recvRaw(uint8_t* bytes, int sz) { if (len > sz) { len = sz; } int err = _radio->readData(bytes, len); if (err != RADIOLIB_ERR_NONE) { - MESH_DEBUG_PRINTLN("RadioLibWrapper: error: readData(%d)", err); + // Signal quality of the packet that just failed. A CRC mismatch (-7) is + // the common case and says nothing on its own about *why*: a packet at + // the edge of the demodulator and one lost to a collision both land + // here. The modem's packet-status registers are written whether or not + // the CRC passed, so this reads the same values a successful receive + // would report, at no extra SPI cost -- enough to tell a failure + // distribution sitting on the SF's SNR floor apart from one spread + // across strong signals. + // + // SNR is scaled by 4 rather than truncated because the threshold this + // is meant to resolve is a fraction of a dB wide, and %f is not + // portable across every platform this file builds for. Same quarter-dB + // convention as Packet::_snr. + MESH_DEBUG_PRINTLN("RadioLibWrapper: error: readData(%d) len=%d rssi=%d snr4=%d", + err, len, (int)getLastRSSI(), (int)(getLastSNR() * 4)); len = 0; n_recv_errors++; } else { @@ -252,7 +274,22 @@ PacketMillis RadioLibWrapper::calcMaxPacketMillis(uint8_t sf, float bw, uint8_t // airtime for max packet at current radio settings uint32_t total_us = _radio->getTimeOnAir(MAX_TRANS_UNIT); // airtime for payload only (no preamble, header or SOF) - uint32_t payload_us = total_us > preamble_us ? total_us - preamble_us : 4000 - preamble_us; // fallback to 4 secs at worst case + uint32_t payload_us; + if (total_us > preamble_us) { + payload_us = total_us - preamble_us; + } else { + // getTimeOnAir() gave nothing usable (it returns 0 on an unconfigured + // modem). Fall back to the 4 s this has always claimed -- as 4 s of + // *payload*, not as 4 s of total airtime minus the preamble. + // + // The value used to be 4000, i.e. 4 ms, and the subtraction underflowed for + // any setting whose preamble exceeds that: every one of them. An underflow + // here is not a mis-sized deadline but the absence of one, because the + // result becomes a ~49-day payload watchdog, so CustomSX1262::isReceiving() + // would hold a latched HEADER_VALID true forever and isReceiving() would + // never again report the channel idle. + payload_us = MAX_PACKET_FALLBACK_PAYLOAD_US; + } // rescale payload_us for max possible CR if (cr >= 5 && cr < 8) { payload_us = (payload_us * 8) / cr; } diff --git a/src/helpers/sensors/LocationProvider.h b/src/helpers/sensors/LocationProvider.h index 81d08652ed..488a755115 100644 --- a/src/helpers/sensors/LocationProvider.h +++ b/src/helpers/sensors/LocationProvider.h @@ -16,7 +16,7 @@ class LocationProvider { virtual long satellitesCount() = 0; virtual bool isValid() = 0; virtual long getTimestamp() = 0; - virtual void sendSentence(const char * sentence); + virtual void sendSentence(const char * sentence) {} virtual void reset() = 0; virtual void begin() = 0; virtual void stop() = 0;