Fix peripheral power shutdown and auto-detect Station G3 radio slots

This commit is contained in:
mikecarper committed 2026-10-15 22:30:37 -07:00
1 parent c6e43c5530
commit 8eced48d34
103 files changed
+4900 -194

No files matched your search

+4 -1
View File
@@ -4204,7 +4204,10 @@ void loop() {
#endif
}
#if defined(ESP32_PLATFORM)
else if (!morse_timing && the_mesh.getNodePrefs()->powersaving_enabled) {
else if (!morse_timing && !the_mesh.isDualRadioActive()
&& the_mesh.getNodePrefs()->powersaving_enabled) {
// The scanner has a zero-delay polling contract and sub-ms RX visits.
// Keep the idle yield for USB/logging/button holds, not live radio2.
delay(1); // CPU idle remains available while USB/logging/button holds are active.
}
#endif
+53
View File
@@ -1,5 +1,8 @@
#include "KissModem.h"
#include <CayenneLPP.h>
#if defined(STATION_G3_ESP32)
#include "StationG3RadioSlot.h"
#endif
namespace {
@@ -467,6 +470,14 @@ void KissModem::handleHardwareCommand(uint8_t sub_cmd, const uint8_t* data, uint
if (len != 0) writeHardwareError(HW_ERR_INVALID_LENGTH);
else handleGetRadio2(true);
break;
#if defined(STATION_G3_ESP32)
case HW_CMD_GET_RADIO_SLOT:
handleGetRadioSlot(len);
break;
case HW_CMD_SET_RADIO_SLOT:
handleSetRadioSlot(data, len);
break;
#endif
#if defined(MESH_HIL_TX_FREQ_MHZ)
case HW_CMD_GET_LAB_TX_GUARD: {
if (len != 0) {
@@ -489,6 +500,48 @@ void KissModem::handleHardwareCommand(uint8_t sub_cmd, const uint8_t* data, uint
}
}
#if defined(STATION_G3_ESP32)
void KissModem::handleGetRadioSlot(uint16_t len) {
if (len != 0) {
writeHardwareError(HW_ERR_INVALID_LENGTH);
return;
}
const auto& state = station_g3::slotState();
const uint8_t status[KISS_RADIO_SLOT_STATUS_SIZE] = {
state.savedMode, state.active, state.detected,
static_cast<uint8_t>(state.savedMode != state.bootMode)};
writeHardwareFrame(HW_RESP(HW_CMD_GET_RADIO_SLOT), status, sizeof(status));
}
void KissModem::handleSetRadioSlot(const uint8_t* data, uint16_t len) {
if (len != 1) {
writeHardwareError(HW_ERR_INVALID_LENGTH);
return;
}
if (data[0] > 2) {
writeHardwareError(HW_ERR_INVALID_PARAM);
return;
}
static const char* const commands[] = {
"set radio.slot auto", "set radio.slot 1", "set radio.slot 2"};
char reply[160] = {};
if (!_board.handleCommand(commands[data[0]], 0, reply)) {
writeHardwareError(HW_ERR_NO_CALLBACK);
return;
}
// handleCommand's bool means "recognized", not "saved". The board is the
// single checked-persistence owner; never acknowledge its error as success.
if (strncmp(reply, "OK - ", 5) != 0
|| station_g3::slotState().savedMode != data[0]) {
writeHardwareError(HW_ERR_SAVE_FAILED);
return;
}
// This changes only next-boot selection. No radio retune, probe, RF packet,
// physical pin switch or reboot is performed by the KISS control command.
writeHardwareFrame(HW_RESP_OK, nullptr, 0);
}
#endif
void KissModem::expireTemporaryRadio2() {
if (!_temporary_radio2_active
|| (int32_t)(millis() - _temporary_radio2_end_ms) < 0) return;
+16
View File
@@ -72,6 +72,8 @@
#define HW_CMD_SET_RADIO2_EXT 0x1F
#define HW_CMD_GET_RADIO2_EXT 0x20
#define HW_CMD_GET_LAB_TX_GUARD 0x21
#define HW_CMD_GET_RADIO_SLOT 0x22
#define HW_CMD_SET_RADIO_SLOT 0x23
/* Response code = command code | 0x80. Generic / unsolicited use 0xF0+. */
#define HW_RESP(cmd) ((cmd) | 0x80)
@@ -91,12 +93,22 @@
#define HW_ERR_UNKNOWN_CMD 0x05
#define HW_ERR_ENCRYPT_FAILED 0x06
#define HW_ERR_TX_BUSY 0x07
#define HW_ERR_SAVE_FAILED 0x08
#define KISS_RADIO_PARAMS_SIZE 10
#define KISS_RADIO2_PARAMS_SIZE 13
#define KISS_RADIO2_EXT_PARAMS_SIZE 14
#define KISS_TEMPRADIO2_PARAMS_SIZE 15
#define KISS_MAX_TEMPRADIO2_MINUTES 10080
#define KISS_RADIO_SLOT_STATUS_SIZE 4
/* Station G3 physical socket selection, not the logical radio2 PHY:
* GET_RADIO_SLOT: no payload; response 0xA2 payload is
* [saved (0=auto,1,2), active (0=none,1,2), detected (bit0=1,bit1=2),
* reboot_required (saved mode differs from the mode used at boot)].
* SET_RADIO_SLOT: exactly one byte (0=auto,1,2); persistent, reboot-only.
* Success is generic OK, failure is framed ERROR (SAVE_FAILED for storage).
* Unsupported boards return UNKNOWN_CMD for both additive commands. */
/* Version 2 adds the logical secondary-radio KISS port and radio2 commands.
* Extended radio2 commands are additive; old firmware returns UnknownCmd. */
@@ -224,6 +236,10 @@ class KissModem {
void handleGetRadio2(bool extended = false);
void handleSetTempRadio2(const uint8_t* data, uint16_t len);
void handleGetTempRadio2();
#if defined(STATION_G3_ESP32)
void handleGetRadioSlot(uint16_t len);
void handleSetRadioSlot(const uint8_t* data, uint16_t len);
#endif
void handleGetTxPower();
void handleGetVersion();
void handleGetCurrentRssi();
+9
View File
@@ -170,6 +170,13 @@ void setup() {
// KISS owns the entire serial byte stream, including gaps between chunks.
mesh::setUsbLoggingEnabled(false);
mesh::setUsbDebugEnabled(false);
#endif
#if defined(STATION_G3_ESP32)
// G3 can wait in no-radio recovery before modem construction. Bring up
// USB now so its prefixed recovery console is reachable on an empty slot.
mesh::prepareUsbLoggingPort();
Serial.begin(115200);
mesh::beginUsbLoggingPort();
#endif
KISS_BOOT_STAGE("board.before");
board.begin();
@@ -239,6 +246,7 @@ void setup() {
#endif
modem = new KissModem(Serial1, identity, rng, radio_driver, board, sensors);
#else
#if !defined(STATION_G3_ESP32)
#if KISS_HWCDC_TRANSPORT
// Allocate the USB queues before begin() enables the live driver ISR.
mesh::prepareUsbLoggingPort();
@@ -247,6 +255,7 @@ void setup() {
#if KISS_HWCDC_TRANSPORT
mesh::beginUsbLoggingPort();
#endif
#endif // G3 already started USB before radio discovery, including UART builds.
uint32_t start = millis();
while (!Serial && millis() - start < 3000) delay(10);
delay(100);
+3 -1
View File
@@ -624,7 +624,9 @@ void loop() {
}
#if defined(ESP32_PLATFORM)
if (!morse_timing && !can_power_save && the_mesh.getNodePrefs()->powersaving_enabled) {
if (!morse_timing && !can_power_save && !the_mesh.isDualRadioActive()
&& the_mesh.getNodePrefs()->powersaving_enabled) {
// Live radio2 needs the scanner's zero-delay polling cadence.
delay(1); // Idle without suspending USB or the button wake interval.
}
#endif
+3 -1
View File
@@ -375,7 +375,9 @@ void loop() {
#endif
}
#if defined(ESP32_PLATFORM)
if (!morse_timing && !can_power_save && the_mesh.getNodePrefs()->powersaving_enabled) {
if (!morse_timing && !can_power_save && !the_mesh.isDualRadioActive()
&& the_mesh.getNodePrefs()->powersaving_enabled) {
// Live radio2 needs the scanner's zero-delay polling cadence.
delay(1); // Keep USB and the button wake interval serviced while idle.
}
#endif
+3 -1
View File
@@ -375,8 +375,10 @@ void loop() {
#endif
}
#if defined(ESP32_PLATFORM)
if (!morse_timing && !can_power_save && (the_mesh.getNodePrefs()->powersaving_enabled
if (!morse_timing && !can_power_save && !the_mesh.isDualRadioActive()
&& (the_mesh.getNodePrefs()->powersaving_enabled
|| the_mesh.isTrackerModeEnabled())) {
// Live radio2 needs the scanner's zero-delay polling cadence.
delay(1); // Keep USB and the button wake interval serviced while idle.
}
#endif