fix: reserve controls and expose ESP-NOW channel

This commit is contained in:
mikecarper
2026-08-28 16:55:40 -07:00
parent 6d6c89e411
commit a55660b795
4 changed files with 85 additions and 13 deletions
+14 -12
View File
@@ -201,18 +201,20 @@ selection is rejected. Such a WiFi/BLE/primary-ESP-NOW build therefore uses
get espnow.channel
set espnow.channel <1-13>
```
On Full Companion builds whose primary mesh radio is ESP-NOW, this shows or
saves the single channel shared by ESP-NOW, the setup AP, and the
infrastructure-WiFi station. The default is channel 1. Use only a channel
permitted in your region and supported by the router. A saved change takes
effect only after reboot. Every primary ESP-NOW node and the router's 2.4 GHz
radio must use the same fixed channel; power saving does not permit separate
channels. On these ESP32 Full builds, `wifi.powersave max` is unavailable and
`min` is the coexistence setting. This channel setting is also distinct from
`bridge.channel`, which controls the optional ESP-NOW bridge transport. An
updated LoRa-primary ESP-NOW bridge can join this raw primary transport by
selecting the same channel and running `set bridge.format raw`; its default
`wrapped` format remains the bridge-to-bridge protocol.
On builds whose primary mesh radio is ESP-NOW, this shows or saves the primary
radio's channel. The default is channel 1. Use only a channel permitted in
your region, and restart or power-cycle the node after changing it. Every
primary ESP-NOW peer must use the same channel.
On Full Companion, the setup AP and infrastructure-WiFi station also share
that channel, so the router's 2.4 GHz radio must remain fixed to it. Power
saving does not permit separate channels. `wifi.powersave max` is unavailable
on these ESP32 Full builds and `min` is the coexistence setting. This channel
setting is also distinct from `bridge.channel`, which controls the optional
ESP-NOW bridge transport. An updated LoRa-primary ESP-NOW bridge can join this
raw primary transport by selecting the same channel and running
`set bridge.format raw`; its default `wrapped` format remains the
bridge-to-bridge protocol.
```
get radio.rxgain
+45 -1
View File
@@ -20,6 +20,9 @@
#if defined(ESP32_PLATFORM)
#include <helpers/ESP32TrueRandom.h>
#endif
#if defined(MESH_PRIMARY_ESPNOW) && MESH_PRIMARY_ESPNOW
#include <helpers/esp32/WiFiRadioPolicy.h>
#endif
#include <RTClib.h>
#include <target.h>
@@ -503,7 +506,44 @@ public:
}
} else if (memcmp(command, "import ", 7) == 0) {
importCard(&command[7]);
} else if (strcmp(command, "get usb.logging") == 0) {
}
#if defined(MESH_PRIMARY_ESPNOW) && MESH_PRIMARY_ESPNOW
else if (strcmp(command, "get espnow.channel") == 0) {
const uint8_t saved = mesh::wifi::loadConfiguredEspNowChannel();
const uint8_t active = mesh::wifi::activeEspNowChannel();
if (saved == active) {
Serial.printf("> %u (saved and active)\n", (unsigned)saved);
} else {
Serial.printf("> saved %u, active %u; reboot required\n",
(unsigned)saved, (unsigned)active);
}
} else if (strncmp(command, "set espnow.channel", 18) == 0
&& (command[18] == 0 || command[18] == ' '
|| command[18] == '\t')) {
const char* value = command + 18;
while (*value == ' ' || *value == '\t') value++;
uint8_t channel = 0;
if (!mesh::wifi::parseEspNowChannel(value, channel)) {
Serial.println("Error: ESP-NOW channel must be 1-13");
} else {
// Snapshot the boot channel before writing NVS. The running radio
// remains on that channel until restart, keeping this node joined to
// its current peers for the rest of this boot.
const uint8_t active = mesh::wifi::activeEspNowChannel();
if (!mesh::wifi::saveConfiguredEspNowChannel(channel)) {
Serial.println("Error: failed to save ESP-NOW channel");
} else if (channel == active) {
Serial.printf("OK - ESP-NOW channel %u saved and active\n",
(unsigned)channel);
} else {
Serial.printf(
"OK - ESP-NOW channel %u saved; active %u; reboot required\n",
(unsigned)channel, (unsigned)active);
}
}
}
#endif
else if (strcmp(command, "get usb.logging") == 0) {
#if MESH_USB_LOGGING_AVAILABLE
Serial.printf(" usb.logging %s\n",
mesh::isUsbLoggingEnabled() ? "on" : "off");
@@ -558,6 +598,10 @@ public:
Serial.println(" set {name|lat|lon|freq|tx|af} {value}");
Serial.println(" get usb.logging");
Serial.println(" set usb.logging {on|off}");
#if defined(MESH_PRIMARY_ESPNOW) && MESH_PRIMARY_ESPNOW
Serial.println(" get espnow.channel");
Serial.println(" set espnow.channel <1-13>");
#endif
Serial.println(" card");
Serial.println(" import {biz card}");
Serial.println(" clock");
+12
View File
@@ -60,6 +60,12 @@ inline bool isFirmwareReserved(uint8_t pin) {
#ifdef P_LORA_PA_POWER
P_LORA_PA_POWER,
#endif
#ifdef P_LORA_LF_PA_POWER
P_LORA_LF_PA_POWER,
#endif
#ifdef P_LORA_HF_PA_POWER
P_LORA_HF_PA_POWER,
#endif
#ifdef P_LORA_TX_LED
P_LORA_TX_LED,
#endif
@@ -406,6 +412,12 @@ inline bool isFirmwareReserved(uint8_t pin) {
#ifdef PIN_TFT_EN
PIN_TFT_EN,
#endif
#ifdef ST7789_CS
ST7789_CS,
#endif
#ifdef ST7789_RS
ST7789_RS,
#endif
#ifdef PIN_TOUCH_RST
PIN_TOUCH_RST,
#endif
+14
View File
@@ -1,7 +1,13 @@
#include <gtest/gtest.h>
#define P_LORA_LF_PA_POWER 4
#define P_LORA_HF_PA_POWER 3
#define ST7789_CS 16
#define ST7789_RS 15
#include <Arduino.h>
#include <helpers/UserGpio.h>
#include <helpers/UserGpioPinPolicy.h>
#include <helpers/UserGpioReplyTracker.h>
#include <climits>
@@ -38,6 +44,14 @@ protected:
}
};
TEST(UserGpioPinPolicyTest, ReservesRadioPaAndSt7789ControlAliases) {
EXPECT_TRUE(UserGpioPinPolicy::isFirmwareReserved(P_LORA_LF_PA_POWER));
EXPECT_TRUE(UserGpioPinPolicy::isFirmwareReserved(P_LORA_HF_PA_POWER));
EXPECT_TRUE(UserGpioPinPolicy::isFirmwareReserved(ST7789_CS));
EXPECT_TRUE(UserGpioPinPolicy::isFirmwareReserved(ST7789_RS));
EXPECT_FALSE(UserGpioPinPolicy::isFirmwareReserved(22));
}
TEST_F(UserGpioTest, ListsOnlyBoardApprovedPins) {
UserGpio gpio(board);