Fix BLE pairing display and RX gain defaults

This commit is contained in:
mikecarper committed 2026-08-10 19:42:44 -07:00
1 parent 968ca3363b
commit 2ff1166c15
22 files changed
+297 -39

No files matched your search

+7 -2
View File
@@ -554,9 +554,14 @@ disabled.
**Parameters:**
- `state`: `on`|`off`
**Default:** `on`
**Default:** Target-specific. Most SX1262 and LR1110 targets default to `on`;
Station G2/G3 targets default to `off`.
**Temporary Note:** If you upgraded from an older version to 1.14.1 without erasing flash, this setting is `off` because of [#2118](https://github.com/meshcore-dev/MeshCore/issues/2118)
**Notes:**
- The saved setting is applied immediately and persists across reboots.
- Periodic AGC resets restore the saved runtime setting; they do not replace it
with the target's compile-time default.
- Existing installations retain their previously saved value after an upgrade.
---
+1 -1
View File
@@ -676,7 +676,7 @@ Bytes 2+: Variable length based on firmware version
For firmware version >= 3:
Byte 2: Max Contacts Raw (uint8, actual = value * 2)
Byte 3: Max Channels (uint8)
Bytes 4-7: BLE PIN (32-bit little-endian)
Bytes 4-7: Active BLE PIN (32-bit little-endian; includes a generated session PIN)
Bytes 8-19: Firmware Build (12 bytes, UTF-8, null-padded)
Bytes 20-59: Model (40 bytes, UTF-8, null-padded)
Bytes 60-79: Version (20 bytes, UTF-8, null-padded)
+9 -1
View File
@@ -80,7 +80,7 @@ are requested.
| Platform | Interface | Purpose |
| --- | --- | --- |
| Both | USB, 115200 baud | Binary Companion by default; terminal switch available |
| Both | BLE | Binary Companion; default pairing PIN `123456` |
| Both | BLE | Binary Companion; display builds show a random session PIN, while headless builds default to `123456` |
| ESP32 | TCP 5000 | Binary Companion over WiFi |
| ESP32 | HTTP 80 | Companion WebConfig and first-boot WiFi setup |
| ESP32 | TCP 5001 | Host `.mota` folder from `motatool serve --tcp` |
@@ -91,6 +91,14 @@ Binary Companion replies are broadcast through the multi-interface manager,
so use one active Companion application at a time. On nRF52, BLE remains
available while USB is in terminal or mOTA mode.
When a BLE client requests pairing, a display-equipped build wakes the screen,
switches to the first home page, and keeps the active six-digit PIN visible
until Bluetooth connects or the two-minute pairing window expires. USB, WiFi,
Ethernet, and hardware-serial connections do not suppress this screen. With no
saved BLE PIN, display builds generate a new PIN at boot; builds without a
physical display use `123456`. A PIN saved through the Companion protocol takes
effect after reboot.
ESP32 ports 5000, 5001, 5002, and WebConfig have no independent login layer.
Expose them only on a trusted LAN or temporary setup network. See
[WiFi setup](./WiFi.md) for credential setup and reconnect behavior.
+4 -1
View File
@@ -686,7 +686,10 @@ You can get the epoch time on <https://www.epochconverter.com> and use it to set
**A:** Make sure that you flashed the Bluetooth companion firmware and not the USB-only companion firmware.
### 6.5. Q: I can't connect via Bluetooth, what is the Bluetooth pairing code?
**A:** The default Bluetooth pairing code is `123456`
**A:** The default Bluetooth pairing code is `123456` on builds without a
physical display. Display-equipped Companion builds generate a six-digit PIN
at boot and show it when a client requests pairing. The display stays awake on
the PIN page until Bluetooth connects or the two-minute pairing window expires.
### 6.6. Q: My Heltec V3 keeps disconnecting from my smartphone. It can't hold a solid Bluetooth connection.
**A:** Heltec V3 has a very small coil antenna on its PCB for Wi-Fi and Bluetooth connectivity. It has a very short range, only a few feet. It is possible to remove the coil antenna and replace it with a 31mm wire. The BT range is much improved with the modification.
@@ -53,6 +53,7 @@ protected:
public:
void setHasConnection(bool connected) { _connected = connected; }
bool hasConnection() const { return _connected; }
bool hasBluetoothConnection() const { return _interfaceManager->isBluetoothConnected(); }
uint16_t getBattMilliVolts() const { return _board->getBattMilliVolts(); }
bool isBluetoothEnabled() const { return _interfaceManager->isBluetoothEnabled(); }
void enableBluetooth() { _interfaceManager->enableBluetooth(); }
+6 -7
View File
@@ -3,6 +3,7 @@
#include <Arduino.h> // needed for PlatformIO
#include <Mesh.h>
#include "helpers/radiolib/RXPowerSaving.h"
#include "helpers/radiolib/RxBoostedGainDefaults.h"
#ifdef ENABLE_USB_INTERFACE
#include <helpers/CLICommandUtils.h>
@@ -1336,12 +1337,10 @@ MyMesh::MyMesh(mesh::Radio &radio, mesh::RNG &rng, mesh::RTCClock &rtc, SimpleMe
_prefs.path_hash_mode = DEFAULT_PATH_HASH_MODE;
//_prefs.rx_delay_base = 10.0f; enable once new algo fixed
_prefs.setRepeatEn(false);
#if defined(USE_SX1262) || defined(USE_SX1268)
#ifdef SX126X_RX_BOOSTED_GAIN
_prefs.rx_boosted_gain = SX126X_RX_BOOSTED_GAIN;
#else
_prefs.rx_boosted_gain = 1; // enabled by default
#endif
#if defined(USE_SX1262) || defined(USE_SX1268) || defined(USE_LR1110) \
|| defined(USE_LR2021) || defined(SX126X_RX_BOOSTED_GAIN) \
|| defined(RX_BOOSTED_GAIN)
_prefs.rx_boosted_gain = mesh::radio::configuredRxBoostedGainDefault();
#endif
_prefs.radio_fem_rxgain = DEFAULT_FEM_RX_GAIN;
@@ -2257,7 +2256,7 @@ void MyMesh::handleCmdFrame(size_t len) {
out_frame[i++] = FIRMWARE_VER_CODE;
out_frame[i++] = MAX_CONTACTS / 2; // v3+
out_frame[i++] = MAX_GROUP_CHANNELS; // v3+
memcpy(&out_frame[i], &_prefs.ble_pin, 4);
memcpy(&out_frame[i], &_active_ble_pin, 4);
i += 4;
memset(&out_frame[i], 0, 12);
strcpy((char *)&out_frame[i], FIRMWARE_BUILD_DATE);
+3 -3
View File
@@ -239,7 +239,7 @@ public:
display.setTextSize(1);
display.drawTextCentered(display.width() / 2, 54, tmp);
#endif
if (_task->hasConnection()) {
if (_task->hasBluetoothConnection()) {
display.setColor(UIColor::warning_txt);
display.setTextSize(1);
display.drawTextCentered(display.width() / 2, 43, "< Connected >");
@@ -725,7 +725,7 @@ void UITask::setCurrScreen(UIScreen* c) {
bool UITask::isPairingScreenActive() const {
return _pairing_screen_until != 0
&& !hasConnection()
&& !hasBluetoothConnection()
&& static_cast<int32_t>(millis() - _pairing_screen_until) < 0;
}
@@ -805,7 +805,7 @@ void UITask::loop() {
if (_pairing_screen_until != 0) {
const bool timed_out = static_cast<int32_t>(millis() - _pairing_screen_until) >= 0;
if (hasConnection() || timed_out) {
if (hasBluetoothConnection() || timed_out) {
finishPairingScreen(timed_out);
}
}
+50 -6
View File
@@ -5,6 +5,9 @@
#define AUTO_OFF_MILLIS 15000 // 15 seconds
#define BOOT_SCREEN_MILLIS 3000 // 3 seconds
#ifndef BLE_PAIRING_DISPLAY_MILLIS
#define BLE_PAIRING_DISPLAY_MILLIS 120000UL
#endif
#if defined(PIN_STATUS_LED) || defined(PIN_STATUS_LED_R)
#define LED_ON_MILLIS 20
@@ -104,7 +107,7 @@ void UITask::begin(DisplayDriver* display, SensorManager* sensors, NodePrefs* no
bool UITask::shouldPlayMessageTone() const {
#ifdef BLE_PIN_CODE
return !hasConnection();
return !hasBluetoothConnection();
#else
// A USB or Ethernet companion connection is not a Bluetooth connection and
// should not silence the standalone new-message alert.
@@ -214,7 +217,8 @@ void UITask::renderCurrScreen() {
if (_display == NULL) return; // assert() ??
char tmp[80];
if (_alert[0]) {
const bool pairing_screen_active = isPairingScreenActive();
if (!pairing_screen_active && _alert[0]) {
_display->setTextSize(1.4);
uint16_t textWidth = _display->getTextWidth(_alert);
_display->setCursor((_display->width() - textWidth) / 2, 22);
@@ -223,7 +227,7 @@ void UITask::renderCurrScreen() {
_alert[0] = 0;
_need_refresh = true;
return;
} else if (_origin[0] && _msg[0]) { // message preview
} else if (!pairing_screen_active && _origin[0] && _msg[0]) { // message preview
// render message preview
_display->setCursor(0, 0);
_display->setTextSize(1);
@@ -242,7 +246,7 @@ void UITask::renderCurrScreen() {
sprintf(tmp, "%d", _msgcount);
_display->print(tmp);
_display->setColor(UIColor::secondary_txt); // last color will be kept on T114
} else if ((millis() - ui_started_at) < BOOT_SCREEN_MILLIS) { // boot screen
} else if (!pairing_screen_active && (millis() - ui_started_at) < BOOT_SCREEN_MILLIS) { // boot screen
// meshcore logo
_display->setColor(UIColor::corp_blue);
int logoWidth = 128;
@@ -276,7 +280,7 @@ void UITask::renderCurrScreen() {
_display->print(tmp);
// BT pin
if (!_connected && the_mesh.getBLEPin() != 0) {
if (!hasBluetoothConnection() && the_mesh.getBLEPin() != 0) {
_display->setColor(UIColor::warning_txt);
_display->setTextSize(2);
_display->setCursor(0, 43);
@@ -447,7 +451,47 @@ void UITask::shutdown(bool restart){
}
}
bool UITask::isPairingScreenActive() const {
return _pairing_screen_until != 0
&& !hasBluetoothConnection()
&& static_cast<int32_t>(millis() - _pairing_screen_until) < 0;
}
void UITask::showPairingPin() {
const unsigned long now = millis();
_pairing_screen_until = now + BLE_PAIRING_DISPLAY_MILLIS;
_need_refresh = true;
_next_refresh = 0;
if (_display != NULL && !_display->isOn()) {
_display->turnOn();
}
}
void UITask::finishPairingScreen(bool timed_out) {
_pairing_screen_until = 0;
_need_refresh = true;
_next_refresh = 0;
if (_display == NULL) return;
if (timed_out) {
_display->turnOff();
} else {
_auto_off = millis() + AUTO_OFF_MILLIS;
}
}
void UITask::loop() {
if (_interfaceManager->takePairingRequest()) {
showPairingPin();
}
if (_pairing_screen_until != 0) {
const bool timed_out = static_cast<int32_t>(millis() - _pairing_screen_until) >= 0;
if (hasBluetoothConnection() || timed_out) {
finishPairingScreen(timed_out);
}
}
#ifdef PIN_USER_BTN
if (_userButton) {
_userButton->update();
@@ -486,7 +530,7 @@ void UITask::loop() {
_auto_off = millis() + AUTO_OFF_MILLIS;
}
#endif
if (isDisplayAutoOffDue(_auto_off, AUTO_OFF_MILLIS)) {
if (!isPairingScreenActive() && isDisplayAutoOffDue(_auto_off, AUTO_OFF_MILLIS)) {
_display->turnOff();
}
}
@@ -36,6 +36,7 @@ class UITask : public AbstractUITask {
int _msgcount;
bool _need_refresh = true;
bool _displayWasOn = false; // Track display state before button press
unsigned long _pairing_screen_until;
unsigned long ui_started_at;
// Button handlers
@@ -58,12 +59,16 @@ class UITask : public AbstractUITask {
void handleButtonQuadruplePress();
void handleButtonLongPress();
bool shouldPlayMessageTone() const;
bool isPairingScreenActive() const;
void showPairingPin();
void finishPairingScreen(bool timed_out);
public:
UITask(mesh::MainBoard* board, MultiSerialInterface* serial) : AbstractUITask(board, serial), _display(NULL), _sensors(NULL) {
_next_refresh = 0;
_pairing_screen_until = 0;
ui_started_at = 0;
}
void begin(DisplayDriver* display, SensorManager* sensors, NodePrefs* node_prefs);
+55 -3
View File
@@ -11,6 +11,9 @@
#ifndef AUTO_OFF_MILLIS
#define AUTO_OFF_MILLIS 15000 // 15 seconds
#endif
#ifndef BLE_PAIRING_DISPLAY_MILLIS
#define BLE_PAIRING_DISPLAY_MILLIS 120000UL
#endif
#define BOOT_SCREEN_MILLIS 4000 // 4 seconds
#ifdef PIN_STATUS_LED
@@ -144,6 +147,8 @@ public:
: _task(task), _rtc(rtc), _sensors(sensors), _node_prefs(node_prefs), _page(0),
_shutdown_init(false), sensors_lpp(200) { }
void showFirstPage() { _page = HomePage::FIRST; }
void poll() override {
if (_shutdown_init && !_task->isButtonPressed()) { // must wait for USR button to be released
_task->shutdown();
@@ -179,7 +184,7 @@ public:
display.setTextSize(1);
display.drawTextCentered(display.width() / 2, 54, tmp);
#endif
if (_task->hasConnection()) {
if (_task->hasBluetoothConnection()) {
display.setColor(UIColor::warning_txt);
display.setTextSize(1);
display.drawTextCentered(display.width() / 2, display.height()-8, "< Connected >");
@@ -550,6 +555,36 @@ void UITask::setCurrScreen(UIScreen* c) {
_next_refresh = 100;
}
bool UITask::isPairingScreenActive() const {
return _pairing_screen_until != 0
&& !hasBluetoothConnection()
&& static_cast<int32_t>(millis() - _pairing_screen_until) < 0;
}
void UITask::showPairingPin() {
const unsigned long now = millis();
_pairing_screen_until = now + BLE_PAIRING_DISPLAY_MILLIS;
static_cast<HomeScreen*>(home)->showFirstPage();
setCurrScreen(home);
if (_display != NULL) {
if (!_display->isOn()) _display->turnOn();
_next_refresh = 0;
}
}
void UITask::finishPairingScreen(bool timed_out) {
_pairing_screen_until = 0;
_next_refresh = 0;
if (_display == NULL) return;
if (timed_out) {
_display->turnOff();
} else {
_auto_off = millis() + AUTO_OFF_MILLIS;
}
}
/*
hardware-agnostic pre-shutdown activity should be done here
*/
@@ -585,6 +620,17 @@ bool UITask::isButtonPressed() const {
}
void UITask::loop() {
if (_interfaceManager->takePairingRequest()) {
showPairingPin();
}
if (_pairing_screen_until != 0) {
const bool timed_out = static_cast<int32_t>(millis() - _pairing_screen_until) >= 0;
if (hasBluetoothConnection() || timed_out) {
finishPairingScreen(timed_out);
}
}
char c = 0;
#if UI_HAS_JOYSTICK
int ev = user_btn.check();
@@ -657,6 +703,12 @@ void UITask::loop() {
}
#endif
if (isPairingScreenActive()) {
static_cast<HomeScreen*>(home)->showFirstPage();
if (curr != home) setCurrScreen(home);
c = 0;
}
if (c != 0 && curr) {
curr->handleInput(c);
_auto_off = millis() + AUTO_OFF_MILLIS; // extend auto-off timer
@@ -693,7 +745,7 @@ void UITask::loop() {
}
}
if (millis() < _alert_expiry) { // render alert popup
if (!isPairingScreenActive() && millis() < _alert_expiry) { // render alert popup
_display->setTextSize(1);
int y = _display->height() / 3;
int p = _display->height() / 32;
@@ -717,7 +769,7 @@ void UITask::loop() {
_auto_off = millis() + AUTO_OFF_MILLIS;
}
#endif
if (isDisplayAutoOffDue(_auto_off, AUTO_OFF_MILLIS)) {
if (!isPairingScreenActive() && isDisplayAutoOffDue(_auto_off, AUTO_OFF_MILLIS)) {
_display->turnOff();
}
#endif
@@ -39,6 +39,7 @@ class UITask : public AbstractUITask {
NodePrefs* _node_prefs;
char _alert[80];
unsigned long _alert_expiry;
unsigned long _pairing_screen_until;
int _msgcount;
unsigned long ui_started_at, next_batt_chck;
int next_backlight_btn_check = 0;
@@ -68,12 +69,16 @@ class UITask : public AbstractUITask {
char handleTripleClick(char c);
void setCurrScreen(UIScreen* c);
bool isPairingScreenActive() const;
void showPairingPin();
void finishPairingScreen(bool timed_out);
public:
UITask(mesh::MainBoard* board, MultiSerialInterface* serial) : AbstractUITask(board, serial), _display(NULL), _sensors(NULL) {
next_batt_chck = _next_refresh = 0;
_cached_batt_mv = 0;
_pairing_screen_until = 0;
ui_started_at = 0;
curr = NULL;
}
+5 -6
View File
@@ -1,4 +1,5 @@
#include "MyMesh.h"
#include <helpers/radiolib/RxBoostedGainDefaults.h>
#include <algorithm>
#include <stdlib.h> // for qsort()
#include <helpers/CLICommandUtils.h>
@@ -3023,12 +3024,10 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc
_prefs.adc_multiplier = 0.0f; // 0.0f means use default board multiplier
#if defined(USE_SX1262) || defined(USE_SX1268)
#ifdef SX126X_RX_BOOSTED_GAIN
_prefs.rx_boosted_gain = SX126X_RX_BOOSTED_GAIN;
#else
_prefs.rx_boosted_gain = 1; // enabled by default;
#endif
#if defined(USE_SX1262) || defined(USE_SX1268) || defined(USE_LR1110) \
|| defined(USE_LR2021) || defined(SX126X_RX_BOOSTED_GAIN) \
|| defined(RX_BOOSTED_GAIN)
_prefs.rx_boosted_gain = mesh::radio::configuredRxBoostedGainDefault();
#endif
_prefs.radio_fem_rxgain = 1; // LoRa FEM RX gain on by default (FEM boards)
+5 -6
View File
@@ -1,4 +1,5 @@
#include "MyMesh.h"
#include <helpers/radiolib/RxBoostedGainDefaults.h>
#include <helpers/CLICommandUtils.h>
#if defined(MESHCORE_ESP32_FULL_PROFILE)
#include <helpers/FloodFilterPolicy.h>
@@ -1157,12 +1158,10 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc
_prefs.gps_interval = 0;
_prefs.advert_loc_policy = ADVERT_LOC_PREFS;
#if defined(USE_SX1262) || defined(USE_SX1268)
#ifdef SX126X_RX_BOOSTED_GAIN
_prefs.rx_boosted_gain = SX126X_RX_BOOSTED_GAIN;
#else
_prefs.rx_boosted_gain = 1; // enabled by default;
#endif
#if defined(USE_SX1262) || defined(USE_SX1268) || defined(USE_LR1110) \
|| defined(USE_LR2021) || defined(SX126X_RX_BOOSTED_GAIN) \
|| defined(RX_BOOSTED_GAIN)
_prefs.rx_boosted_gain = mesh::radio::configuredRxBoostedGainDefault();
#endif
_prefs.radio_fem_rxgain = 1;
+14 -1
View File
@@ -90,6 +90,18 @@ public:
return false;
}
bool isBluetoothConnected() const {
if (!_enabled) return false;
for (auto iface : _interfaces) {
if (iface.instance && iface.type == InterfaceType::Bluetooth
&& iface.instance->isEnabled() && iface.instance->isConnected()) {
return true;
}
}
return false;
}
// enable all interfaces
void enable() override {
_enabled = true;
@@ -181,7 +193,8 @@ public:
bool takePairingRequest() override {
for (auto iface : _interfaces) {
if (iface.instance && iface.instance->isEnabled()
if (iface.instance && iface.type == InterfaceType::Bluetooth
&& iface.instance->isEnabled()
&& iface.instance->takePairingRequest()) {
return true;
}
+7 -1
View File
@@ -35,6 +35,7 @@ static bool isBondAuthenticationFailure(uint8_t reason) {
void SerialBLEInterface::onConnect(uint16_t connection_handle) {
BLE_DEBUG_PRINTLN("SerialBLEInterface: connected handle=0x%04X", connection_handle);
if (instance) {
instance->_pairingRequestPending.store(false, std::memory_order_release);
instance->_conn_handle = connection_handle;
instance->_isDeviceConnected = false;
instance->_security_timer.start(millis());
@@ -46,6 +47,7 @@ void SerialBLEInterface::onDisconnect(uint16_t connection_handle, uint8_t reason
BLE_DEBUG_PRINTLN("SerialBLEInterface: disconnected handle=0x%04X reason=%u", connection_handle, reason);
if (instance) {
if (instance->_conn_handle == connection_handle) {
instance->_pairingRequestPending.store(false, std::memory_order_release);
instance->_conn_handle = BLE_CONN_HANDLE_INVALID;
instance->_isDeviceConnected = false;
instance->_security_timer.cancel();
@@ -99,9 +101,11 @@ void SerialBLEInterface::onSecured(uint16_t connection_handle) {
}
bool SerialBLEInterface::onPairingPasskey(uint16_t connection_handle, uint8_t const passkey[6], bool match_request) {
(void)connection_handle;
(void)passkey;
BLE_DEBUG_PRINTLN("SerialBLEInterface: pairing passkey request match=%d", match_request);
if (instance && instance->isValidConnection(connection_handle)) {
instance->_pairingRequestPending.store(true, std::memory_order_release);
}
return true;
}
@@ -397,6 +401,7 @@ bool SerialBLEInterface::isAdvertising() const {
void SerialBLEInterface::enable() {
if (_isEnabled) return;
_pairingRequestPending.store(false, std::memory_order_release);
_isEnabled = true;
clearBuffers();
_last_health_check = millis();
@@ -413,6 +418,7 @@ void SerialBLEInterface::disconnect() {
void SerialBLEInterface::disable() {
_isEnabled = false;
_pairingRequestPending.store(false, std::memory_order_release);
BLE_DEBUG_PRINTLN("SerialBLEInterface: disable");
Bluefruit.Advertising.restartOnDisconnect(false);
+5
View File
@@ -4,6 +4,7 @@
#include "../BleTxStallWatchdog.h"
#include "SecuritySessionTimer.h"
#include <bluefruit.h>
#include <atomic>
#ifndef BLE_TX_POWER
#define BLE_TX_POWER 4
@@ -20,6 +21,7 @@ class SerialBLEInterface : public BaseSerialInterface {
ble_gap_addr_t _peer_address = {};
bool _peer_address_valid;
bool _bond_removed_for_connection;
std::atomic<bool> _pairingRequestPending{false};
SecuritySessionTimer _security_timer;
mesh::BleTxStallWatchdog _tx_stall_watchdog;
mesh::BleDisconnectRecovery _tx_disconnect_recovery;
@@ -83,6 +85,9 @@ public:
bool isReadBusy() const override;
bool isWriteBusy() const override;
bool hasPendingIO() const override;
bool takePairingRequest() override {
return _pairingRequestPending.exchange(false, std::memory_order_acq_rel);
}
size_t writeFrame(const uint8_t src[], size_t len) override;
size_t checkRecvFrame(uint8_t dest[]) override;
};
+2
View File
@@ -16,6 +16,8 @@ inline void lr11x0ResetAGC(LR11x0* radio, float freqMHz) {
radio->calibrateImageRejection(freqMHz - 4.0f, freqMHz + 4.0f);
#ifdef RX_BOOSTED_GAIN
// Fallback for callers without a runtime override. RadioLibWrapper::resetAGC()
// reapplies its cached radio.rxgain selection before receive mode resumes.
radio->setRxBoostedGainMode(RX_BOOSTED_GAIN);
#endif
}
@@ -0,0 +1,33 @@
#pragma once
#include <stdint.h>
namespace mesh {
namespace radio {
// SX126x targets use SX126X_RX_BOOSTED_GAIN while LR11xx and several other
// radios use RX_BOOSTED_GAIN. Keep persisted first-install defaults aligned
// with whichever target-level setting initialized the physical radio.
constexpr uint8_t selectRxBoostedGainDefault(bool sx_configured,
int sx_value,
bool generic_configured,
int generic_value,
uint8_t fallback = 1) {
return sx_configured ? (sx_value ? 1 : 0)
: (generic_configured ? (generic_value ? 1 : 0)
: (fallback ? 1 : 0));
}
inline uint8_t configuredRxBoostedGainDefault() {
#ifdef SX126X_RX_BOOSTED_GAIN
return selectRxBoostedGainDefault(true, SX126X_RX_BOOSTED_GAIN,
false, 0);
#elif defined(RX_BOOSTED_GAIN)
return selectRxBoostedGainDefault(false, 0, true, RX_BOOSTED_GAIN);
#else
return selectRxBoostedGainDefault(false, 0, false, 0);
#endif
}
} // namespace radio
} // namespace mesh
+2
View File
@@ -26,6 +26,8 @@ inline void sx126xResetAGC(SX126x* radio) {
radio->setDio2AsRfSwitch(SX126X_DIO2_AS_RF_SWITCH);
#endif
#ifdef SX126X_RX_BOOSTED_GAIN
// Fallback for callers without a runtime override. RadioLibWrapper::resetAGC()
// reapplies its cached radio.rxgain selection before receive mode resumes.
radio->setRxBoostedGainMode(SX126X_RX_BOOSTED_GAIN);
#endif
#ifdef SX126X_REGISTER_PATCH
+2 -1
View File
@@ -31,6 +31,7 @@ does not reflect the GoogleTest count -- run the built binary directly
| `test_mqtt_topic_router` | `src/helpers/MQTTTopicRouter.h` | complete preset/custom topic-routing contract; MeshRank packets-only behavior; required identifiers; invalid inputs/slots; exact buffer boundaries |
| `test_mqtt_connection_policy` | `src/helpers/MQTTConnectionPolicy.h` | reconnect guard/backoff/stagger and breaker transitions; stable reset; JWT lifetime/renewal policy; exact timing boundaries and 32-bit `millis()` rollover |
| `test_wifi_reconnect_policy` | `src/helpers/WiFiReconnectPolicy.h` | five-minute forced reconnect cadence, connection resets, duplicate disconnect observations, and 32-bit `millis()` rollover |
| `test_radio_liveness` | `src/helpers/RadioLivenessTracker.h`, `src/helpers/radiolib/RxBoostedGainDefaults.h` | staged radio recovery, activity reset and rollover handling; target-specific RX boosted-gain defaults and SX126x precedence |
| `test_lr1110_rx_recovery` | `src/helpers/radiolib/LR1110RxRecovery.h` | LR1110 four-byte RX-buffer shift signature; captured and accumulated shifts; ordinary/scoped packet exclusions |
| `test_mqtt_packet_queue_policy` | `src/helpers/MQTTPacketQueuePolicy.h` | queue-full eviction; stale-disconnect flush; adaptive drain limits; bounded QoS0 retries; exact timing boundaries and 32-bit `millis()` rollover |
| `test_mqtt_runtime_buffer_lifecycle` | `src/helpers/MQTTRuntimeBufferLifecycle.h` | idempotent allocation/release; partial-allocation degradation; retry of only missing buffers |
@@ -43,7 +44,7 @@ does not reflect the GoogleTest count -- run the built binary directly
| `test_logical_message_cache` | `src/helpers/LogicalMessageCache.h` | bounded logical-message mapping; stable retry timestamps; exact older retries after newer messages; stale and same-timestamp mismatch rejection |
| `test_remote_cli_reply_cache` | `src/helpers/RemoteCliReplyCache.h`, `src/helpers/RemoteCliRequest.h` | authenticated logical-request matching; bounded recent-reply history; backward-compatible retry identity; empty-response completion; on-air truncation and clearing |
| `test_companion_frame_queue` | `src/helpers/CompanionFrameQueue.h` | response/required/best-effort classification; reserved capacity; stable priority; safe eviction; message-waiting coalescing |
| `test_serial_mode_switch` | `src/helpers/ArduinoSerialInterface.cpp` | independent terminal/seeder control-sequence recognition across reads and binary-frame boundaries; passthrough ownership of USB input and suppression of binary output |
| `test_serial_mode_switch` | `src/helpers/ArduinoSerialInterface.cpp`, `src/helpers/MultiSerialInterface.h` | independent terminal/seeder control-sequence recognition across reads and binary-frame boundaries; passthrough ownership of USB input and suppression of binary output; Bluetooth-only connection and pairing-request routing |
| `test_ble_tx_stall_watchdog` | `src/helpers/BleTxStallWatchdog.h` | exact BLE fragment progress; blocked-reply timeout; rollover-safe elapsed time; disconnect recovery retry and completion |
| `test_utils` | `src/Utils.cpp` | `Utils::toHex` (upstream) |
@@ -1,10 +1,28 @@
#include <gtest/gtest.h>
#include <helpers/RadioLivenessTracker.h>
#include <helpers/radiolib/RxBoostedGainDefaults.h>
using mesh::RadioLivenessTracker;
using mesh::RadioRecoveryAction;
TEST(RxBoostedGainDefaults, UsesTargetCompileTimeSetting) {
using mesh::radio::selectRxBoostedGainDefault;
EXPECT_EQ(selectRxBoostedGainDefault(true, 0, false, 0), 0);
EXPECT_EQ(selectRxBoostedGainDefault(true, 1, false, 0), 1);
EXPECT_EQ(selectRxBoostedGainDefault(false, 0, true, 0), 0);
EXPECT_EQ(selectRxBoostedGainDefault(false, 0, true, 1), 1);
EXPECT_EQ(selectRxBoostedGainDefault(false, 0, false, 0), 1);
}
TEST(RxBoostedGainDefaults, Sx126xSettingTakesPrecedence) {
using mesh::radio::selectRxBoostedGainDefault;
EXPECT_EQ(selectRxBoostedGainDefault(true, 0, true, 1), 0);
EXPECT_EQ(selectRxBoostedGainDefault(true, 1, true, 0), 1);
}
TEST(RadioLivenessTracker, StagesSoftThenHardRecovery) {
RadioLivenessTracker tracker;
tracker.begin(1000);
@@ -4,6 +4,7 @@
#include <vector>
#include "helpers/ArduinoSerialInterface.h"
#include "helpers/MultiSerialInterface.h"
class BufferStream : public Stream {
public:
@@ -41,6 +42,63 @@ public:
static const char START_TOKEN[] = "+++MESHCORE-TERM-START";
static const char SEEDER_TOKEN[] = "ota folder on";
class FakeSerialInterface : public BaseSerialInterface {
public:
bool enabled = false;
bool connected = false;
bool pairing_request = false;
void enable() override { enabled = true; }
void disable() override { enabled = false; }
bool isEnabled() const override { return enabled; }
bool isConnected() const override { return connected; }
bool isReadBusy() const override { return false; }
bool isWriteBusy() const override { return false; }
bool takePairingRequest() override {
bool pending = pairing_request;
pairing_request = false;
return pending;
}
size_t writeFrame(const uint8_t[], size_t len) override { return len; }
size_t checkRecvFrame(uint8_t[]) override { return 0; }
};
TEST(MultiSerialInterface, TracksBluetoothConnectionSeparately) {
MultiSerialInterface manager;
FakeSerialInterface usb;
FakeSerialInterface bluetooth;
usb.connected = true;
ASSERT_TRUE(manager.addInterface(InterfaceType::USB, &usb));
ASSERT_TRUE(manager.addInterface(InterfaceType::Bluetooth, &bluetooth));
manager.enable();
EXPECT_TRUE(manager.isConnected());
EXPECT_FALSE(manager.isBluetoothConnected());
bluetooth.connected = true;
EXPECT_TRUE(manager.isBluetoothConnected());
manager.disableBluetooth();
EXPECT_FALSE(manager.isBluetoothConnected());
}
TEST(MultiSerialInterface, PairingRequestsComeOnlyFromBluetooth) {
MultiSerialInterface manager;
FakeSerialInterface usb;
FakeSerialInterface bluetooth;
ASSERT_TRUE(manager.addInterface(InterfaceType::USB, &usb));
ASSERT_TRUE(manager.addInterface(InterfaceType::Bluetooth, &bluetooth));
manager.enable();
usb.pairing_request = true;
EXPECT_FALSE(manager.takePairingRequest());
bluetooth.pairing_request = true;
EXPECT_TRUE(manager.takePairingRequest());
EXPECT_FALSE(manager.takePairingRequest());
}
TEST(SerialModeSwitch, RecognizesControlSequenceAcrossReads) {
BufferStream stream;
ArduinoSerialInterface interface;