Skip to content
40 changes: 29 additions & 11 deletions src/mesh/LR11x0Interface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -173,21 +173,33 @@ template <typename T> bool LR11x0Interface<T>::reconfigure()
// set mode to standby
setStandby();

// configure publicly accessible settings
// The caller reboots the device when reconfigure returns false; track
// every SPI setter so we don't silently report success after a glitch.
bool ok = true;

int err = lora.setSpreadingFactor(sf);
if (err != RADIOLIB_ERR_NONE)
if (err != RADIOLIB_ERR_NONE) {
RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_INVALID_RADIO_SETTING);
ok = false;
}

err = lora.setBandwidth(bw, wideLora() && (getFreq() > 1000.0f));
if (err != RADIOLIB_ERR_NONE)
if (err != RADIOLIB_ERR_NONE) {
RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_INVALID_RADIO_SETTING);
ok = false;
}

err = lora.setCodingRate(cr, cr != 7); // use long interleaving except if CR is 4/7 which doesn't support it
if (err != RADIOLIB_ERR_NONE)
if (err != RADIOLIB_ERR_NONE) {
RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_INVALID_RADIO_SETTING);
ok = false;
}

err = lora.setSyncWord(syncWord);
assert(err == RADIOLIB_ERR_NONE);
if (err != RADIOLIB_ERR_NONE) {
LOG_ERROR("LR11x0 setSyncWord %s%d", radioLibErr, err);
ok = false;
}

if (config.lora.region == meshtastic_Config_LoRaConfig_RegionCode_LORA_24) { // clamp if wide freq range
limitPower(LR1120_MAX_POWER);
Expand All @@ -196,14 +208,22 @@ template <typename T> bool LR11x0Interface<T>::reconfigure()
}

err = lora.setPreambleLength(preambleLength);
assert(err == RADIOLIB_ERR_NONE);
if (err != RADIOLIB_ERR_NONE) {
LOG_ERROR("LR11x0 setPreambleLength %s%d", radioLibErr, err);
ok = false;
}

err = lora.setFrequency(getFreq());
if (err != RADIOLIB_ERR_NONE)
if (err != RADIOLIB_ERR_NONE) {
RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_INVALID_RADIO_SETTING);
ok = false;
}

err = lora.setOutputPower(power);
assert(err == RADIOLIB_ERR_NONE);
if (err != RADIOLIB_ERR_NONE) {
LOG_ERROR("LR11x0 setOutputPower %s%d", radioLibErr, err);
ok = false;
}

// Apply RX gain mode — valid in STDBY, matches resetAGC() pattern
err = lora.setRxBoostedGainMode(config.lora.sx126x_rx_boosted_gain);
Expand All @@ -212,7 +232,7 @@ template <typename T> bool LR11x0Interface<T>::reconfigure()

startReceive(); // restart receiving

return true;
return ok;
}

template <typename T> void LR11x0Interface<T>::disableInterrupt()
Expand All @@ -230,8 +250,6 @@ template <typename T> void LR11x0Interface<T>::setStandby()
LOG_DEBUG("LR11x0 standby failed with error %d", err);
}

assert(err == RADIOLIB_ERR_NONE);

isReceiving = false; // If we were receiving, not any more
activeReceiveStart = 0;
disableInterrupt();
Expand Down
40 changes: 29 additions & 11 deletions src/mesh/LR20x0Interface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -201,21 +201,33 @@ template <typename T> bool LR20x0Interface<T>::reconfigure()
// set mode to standby
setStandby();

// configure publicly accessible settings
// The caller reboots the device when reconfigure returns false; track
// every SPI setter so we don't silently report success after a glitch.
bool ok = true;

int err = lora.setSpreadingFactor(sf);
if (err != RADIOLIB_ERR_NONE)
if (err != RADIOLIB_ERR_NONE) {
RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_INVALID_RADIO_SETTING);
ok = false;
}

err = lora.setBandwidth(bw); // different form than LR11xx
if (err != RADIOLIB_ERR_NONE)
if (err != RADIOLIB_ERR_NONE) {
RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_INVALID_RADIO_SETTING);
ok = false;
}

err = lora.setCodingRate(cr, cr != 7); // use long interleaving except if CR is 4/7 which doesn't support it
if (err != RADIOLIB_ERR_NONE)
if (err != RADIOLIB_ERR_NONE) {
RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_INVALID_RADIO_SETTING);
ok = false;
}

err = lora.setSyncWord(syncWord);
assert(err == RADIOLIB_ERR_NONE);
if (err != RADIOLIB_ERR_NONE) {
LOG_ERROR("LR20x0 setSyncWord %s%d", radioLibErr, err);
ok = false;
}

if (config.lora.region == meshtastic_Config_LoRaConfig_RegionCode_LORA_24) { // clamp if wide freq range
limitPower(LR2021_MAX_POWER_HF);
Expand All @@ -224,14 +236,22 @@ template <typename T> bool LR20x0Interface<T>::reconfigure()
}

err = lora.setPreambleLength(preambleLength);
assert(err == RADIOLIB_ERR_NONE);
if (err != RADIOLIB_ERR_NONE) {
LOG_ERROR("LR20x0 setPreambleLength %s%d", radioLibErr, err);
ok = false;
}

err = lora.setFrequency(getFreq());
if (err != RADIOLIB_ERR_NONE)
if (err != RADIOLIB_ERR_NONE) {
RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_INVALID_RADIO_SETTING);
ok = false;
}

err = lora.setOutputPower(power);
assert(err == RADIOLIB_ERR_NONE);
if (err != RADIOLIB_ERR_NONE) {
LOG_ERROR("LR20x0 setOutputPower %s%d", radioLibErr, err);
ok = false;
}

// Apply RX gain mode — valid in STDBY, matches resetAGC() pattern
err = lora.setRxBoostedGainMode(config.lora.sx126x_rx_boosted_gain);
Expand All @@ -240,7 +260,7 @@ template <typename T> bool LR20x0Interface<T>::reconfigure()

startReceive(); // restart receiving

return true;
return ok;
}

template <typename T> void LR20x0Interface<T>::disableInterrupt()
Expand All @@ -258,8 +278,6 @@ template <typename T> void LR20x0Interface<T>::setStandby()
LOG_DEBUG("LR20x0 standby failed with error %d", err);
}

assert(err == RADIOLIB_ERR_NONE);

isReceiving = false; // If we were receiving, not any more
activeReceiveStart = 0;
disableInterrupt();
Expand Down
5 changes: 3 additions & 2 deletions src/mesh/MeshService.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,9 @@ int MeshService::handleFromRadio(const meshtastic_MeshPacket *mp)
mp->decoded.portnum == meshtastic_PortNum_TELEMETRY_APP && mp->decoded.request_id > 0) {
LOG_DEBUG("Received telemetry response. Skip sending our NodeInfo");
// ignore our request for its NodeInfo
} else if (mp->which_payload_variant == meshtastic_MeshPacket_decoded_tag && !nodeDB->getMeshNode(mp->from)->has_user &&
nodeInfoModule && !isPreferredRebroadcaster && !nodeDB->isFull()) {
} else if (auto *senderNode = (mp->which_payload_variant == meshtastic_MeshPacket_decoded_tag) ? nodeDB->getMeshNode(mp->from)
: nullptr;
senderNode && !senderNode->has_user && nodeInfoModule && !isPreferredRebroadcaster && !nodeDB->isFull()) {
if (airTime->isTxAllowedChannelUtil(true)) {
const int8_t hopsUsed = getHopsAway(*mp, config.lora.hop_limit);
if (hopsUsed > (int32_t)(config.lora.hop_limit + 2)) {
Expand Down
11 changes: 9 additions & 2 deletions src/mesh/NextHopRouter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -349,9 +349,16 @@ int32_t NextHopRouter::doRetransmissions()
void NextHopRouter::setNextTx(PendingPacket *pending)
{
assert(iface);
auto d = iface->getRetransmissionMsec(pending->packet);
// Two nodes that NAK each other will otherwise re-collide on near-identical
// schedules; spread successive attempts with exponential backoff plus jitter.
uint32_t base = iface->getRetransmissionMsec(pending->packet);
uint8_t attempt = (NUM_RELIABLE_RETX - 1) - pending->numRetransmissions;
uint8_t shift = attempt < 3 ? attempt : 3; // cap multiplier at 8x
uint32_t backoff = base << shift;
uint32_t jitter = base ? random(base / 2 + 1) : 0;
uint32_t d = backoff + jitter;
pending->nextTxMsec = millis() + d;
LOG_DEBUG("Setting next retransmission in %u msecs: ", d);
LOG_DEBUG("Setting next retransmission in %u msecs (attempt %u): ", d, attempt);
printPacket("", pending->packet);
setReceivedMessage(); // Run ASAP, so we can figure out our correct sleep time
}
2 changes: 1 addition & 1 deletion src/mesh/NodeDB.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1881,7 +1881,7 @@ bool NodeDB::updateUser(uint32_t nodeId, meshtastic_User &p, uint8_t channelInde
meshtastic_ClientNotification *cn = clientNotificationPool.allocZeroed();
cn->level = meshtastic_LogRecord_Level_WARNING;
cn->time = getValidTime(RTCQualityFromNet);
sprintf(cn->message, warning, p.long_name);
snprintf(cn->message, sizeof(cn->message), warning, p.long_name);
service->sendClientNotification(cn);
}
return false;
Expand Down
42 changes: 29 additions & 13 deletions src/mesh/RF95Interface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -208,37 +208,51 @@ bool RF95Interface::reconfigure()
// set mode to standby
setStandby();

// configure publicly accessible settings
// The caller reboots the device when reconfigure returns false; track
// every SPI setter so we don't silently report success after a glitch.
bool ok = true;

int err = lora->setSpreadingFactor(sf);
if (err != RADIOLIB_ERR_NONE)
if (err != RADIOLIB_ERR_NONE) {
RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_INVALID_RADIO_SETTING);
ok = false;
}

err = lora->setBandwidth(bw);
if (err != RADIOLIB_ERR_NONE)
if (err != RADIOLIB_ERR_NONE) {
RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_INVALID_RADIO_SETTING);
ok = false;
}

err = lora->setCodingRate(cr);
if (err != RADIOLIB_ERR_NONE)
if (err != RADIOLIB_ERR_NONE) {
RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_INVALID_RADIO_SETTING);
ok = false;
}

err = lora->setSyncWord(syncWord);
if (err != RADIOLIB_ERR_NONE)
if (err != RADIOLIB_ERR_NONE) {
LOG_ERROR("RF95 setSyncWord %s%d", radioLibErr, err);
assert(err == RADIOLIB_ERR_NONE);
ok = false;
}

err = lora->setCurrentLimit(currentLimit);
if (err != RADIOLIB_ERR_NONE)
if (err != RADIOLIB_ERR_NONE) {
LOG_ERROR("RF95 setCurrentLimit %s%d", radioLibErr, err);
assert(err == RADIOLIB_ERR_NONE);
ok = false;
}

err = lora->setPreambleLength(preambleLength);
if (err != RADIOLIB_ERR_NONE)
if (err != RADIOLIB_ERR_NONE) {
LOG_ERROR("RF95 setPreambleLength %s%d", radioLibErr, err);
assert(err == RADIOLIB_ERR_NONE);
ok = false;
}

err = lora->setFrequency(getFreq());
if (err != RADIOLIB_ERR_NONE)
if (err != RADIOLIB_ERR_NONE) {
RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_INVALID_RADIO_SETTING);
ok = false;
}

limitPower(RF95_MAX_POWER);

Expand All @@ -247,12 +261,14 @@ bool RF95Interface::reconfigure()
#else
err = lora->setOutputPower(power);
#endif
if (err != RADIOLIB_ERR_NONE)
if (err != RADIOLIB_ERR_NONE) {
RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_INVALID_RADIO_SETTING);
ok = false;
}

startReceive(); // restart receiving

return true;
return ok;
}

/**
Expand Down
27 changes: 26 additions & 1 deletion src/mesh/RadioLibInterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -530,7 +530,13 @@ void RadioLibInterface::handleReceiveInterrupt()

mp->which_payload_variant =
meshtastic_MeshPacket_encrypted_tag; // Mark that the payload is still encrypted at this point
assert(((uint32_t)payloadLen) <= sizeof(mp->encrypted.bytes));
if ((uint32_t)payloadLen > sizeof(mp->encrypted.bytes)) {
LOG_WARN("Drop oversized RX packet (%d > %u)", payloadLen, (unsigned)sizeof(mp->encrypted.bytes));
packetPool.release(mp);
rxBad++;
airTime->logAirtime(RX_ALL_LOG, rxMsec);
return;
}
memcpy(mp->encrypted.bytes, radioBuffer.payload, payloadLen);
mp->encrypted.size = payloadLen;

Expand All @@ -555,6 +561,17 @@ void RadioLibInterface::pollMissedIrqs()
if (isReceiving) {
checkRxDoneIrqFlag();
}
if (sendingPacket != NULL) {
checkTxDoneIrqFlag();
// The stuck-TX guard in canSendImmediately only runs when the queue
// gets new packets. If the queue is empty, a wedged radio sits
// forever — fire the same reboot path here so it always trips.
if (!Throttle::isWithinTimespanMs(lastTxStart, 60000) && rebootAtMsec == 0) {
LOG_ERROR("Hardware Failure! TX stuck for more than 60s (poll)");
RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_TRANSMIT_FAILED);
rebootAtMsec = lastTxStart + 65000;
}
}
}

void RadioLibInterface::resetAGC()
Expand All @@ -570,6 +587,14 @@ void RadioLibInterface::checkRxDoneIrqFlag()
}
}

void RadioLibInterface::checkTxDoneIrqFlag()
{
if (iface->checkIrq(RADIOLIB_IRQ_TX_DONE)) {
LOG_WARN("caught missed TX_DONE");
notify(ISR_TX, true);
}
}

void RadioLibInterface::configHardwareForSend()
{
powerMon->setState(meshtastic_PowerMon_State_Lora_TXOn);
Expand Down
1 change: 1 addition & 0 deletions src/mesh/RadioLibInterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -293,4 +293,5 @@ class RadioLibInterface : public RadioInterface, protected concurrency::Notified
bool removePendingTXPacket(NodeNum from, PacketId id, uint32_t hop_limit_lt) override;

void checkRxDoneIrqFlag();
void checkTxDoneIrqFlag();
};
Loading
Loading