Unify full firmware profiles and add dual USB logging

This commit is contained in:
mikecarper committed 2026-08-23 19:22:45 -07:00
1 parent b699e552ec
commit f2d4ffbb7e
56 files changed
+1652 -361

No files matched your search

+16 -11
View File
@@ -222,10 +222,12 @@ uint32_t Dispatcher::getCADFailMaxDuration() const {
#if MESH_PACKET_LOGGING
void Dispatcher::logPacketStart(const char* direction, const Packet* packet, int len) {
Serial.print(getLogDateTime());
Serial.printf(": %s, len=%d (type=%d, route=%s, payload_len=%d)",
direction, len, packet->getPayloadType(),
packet->isRouteDirect() ? "D" : "F", packet->payload_len);
Stream& logging_port = usbLoggingPort();
logging_port.print(getLogDateTime());
logging_port.printf(": %s, len=%d (type=%d, route=%s, payload_len=%d)",
direction, len, packet->getPayloadType(),
packet->isRouteDirect() ? "D" : "F",
packet->payload_len);
}
void Dispatcher::logPacketEnd(const Packet* packet) {
@@ -233,10 +235,11 @@ void Dispatcher::logPacketEnd(const Packet* packet) {
if (packet->payload_len >= 2
&& (type == PAYLOAD_TYPE_PATH || type == PAYLOAD_TYPE_REQ
|| type == PAYLOAD_TYPE_RESPONSE || type == PAYLOAD_TYPE_TXT_MSG)) {
Serial.printf(" [%02X -> %02X]\n", (uint32_t)packet->payload[1],
(uint32_t)packet->payload[0]);
usbLoggingPort().printf(" [%02X -> %02X]\n",
(uint32_t)packet->payload[1],
(uint32_t)packet->payload[0]);
} else {
Serial.write((uint8_t)'\n');
usbLoggingPort().write((uint8_t)'\n');
}
}
#endif
@@ -593,13 +596,15 @@ void Dispatcher::checkRecv() {
#if MESH_PACKET_LOGGING
if (isUsbLoggingEnabled()) {
logPacketStart("RX", pkt, pkt->getRawLength());
Serial.printf(" SNR=%d RSSI=%d score=%d time=%d", (int)pkt->getSNR(),
(int)rssi, (int)(score * 1000), air_time);
Stream& logging_port = usbLoggingPort();
logging_port.printf(" SNR=%d RSSI=%d score=%d time=%d",
(int)pkt->getSNR(), (int)rssi,
(int)(score * 1000), air_time);
static uint8_t packet_hash[MAX_HASH_SIZE];
pkt->calculatePacketHash(packet_hash);
Serial.print(" hash=");
mesh::Utils::printHex(Serial, packet_hash, MAX_HASH_SIZE);
logging_port.print(" hash=");
mesh::Utils::printHex(logging_port, packet_hash, MAX_HASH_SIZE);
logPacketEnd(pkt);
}
#endif
+5 -5
View File
@@ -25,23 +25,23 @@
#if MESH_DEBUG && ARDUINO
#include <Arduino.h>
#define MESH_DEBUG_PRINT(F, ...) do { if (mesh::isUsbLoggingEnabled() && Serial.availableForWrite() > 0) { Serial.printf("DEBUG: " F, ##__VA_ARGS__); } } while(0)
#define MESH_DEBUG_PRINTLN(F, ...) do { if (mesh::isUsbLoggingEnabled() && Serial.availableForWrite() > 0) { Serial.printf("DEBUG: " F "\n", ##__VA_ARGS__); } } while(0)
#define MESH_DEBUG_PRINT(F, ...) do { if (mesh::isUsbLoggingEnabled() && mesh::usbLoggingPort().availableForWrite() > 0) { mesh::usbLoggingPort().printf("DEBUG: " F, ##__VA_ARGS__); } } while(0)
#define MESH_DEBUG_PRINTLN(F, ...) do { if (mesh::isUsbLoggingEnabled() && mesh::usbLoggingPort().availableForWrite() > 0) { mesh::usbLoggingPort().printf("DEBUG: " F "\n", ##__VA_ARGS__); } } while(0)
#else
#define MESH_DEBUG_PRINT(...) {}
#define MESH_DEBUG_PRINTLN(...) {}
#endif
#if BRIDGE_DEBUG && ARDUINO
#define BRIDGE_DEBUG_PRINTLN(F, ...) do { if (mesh::isUsbLoggingEnabled() && Serial.availableForWrite() > 0) { Serial.printf("%s BRIDGE: " F, getLogDateTime(), ##__VA_ARGS__); } } while(0)
#define BRIDGE_DEBUG_PRINTLN(F, ...) do { if (mesh::isUsbLoggingEnabled() && mesh::usbLoggingPort().availableForWrite() > 0) { mesh::usbLoggingPort().printf("%s BRIDGE: " F, getLogDateTime(), ##__VA_ARGS__); } } while(0)
#else
#define BRIDGE_DEBUG_PRINTLN(...) {}
#endif
#if POWERSAVING_DEBUG && ARDUINO
#include <Arduino.h>
#define POWERSAVING_DEBUG_PRINT(F, ...) do { if (mesh::isUsbLoggingEnabled()) { Serial.printf("POWERSAVING: " F, ##__VA_ARGS__); } } while(0)
#define POWERSAVING_DEBUG_PRINTLN(F, ...) do { if (mesh::isUsbLoggingEnabled()) { Serial.printf("POWERSAVING: " F "\n", ##__VA_ARGS__); } } while(0)
#define POWERSAVING_DEBUG_PRINT(F, ...) do { if (mesh::isUsbLoggingEnabled()) { mesh::usbLoggingPort().printf("POWERSAVING: " F, ##__VA_ARGS__); } } while(0)
#define POWERSAVING_DEBUG_PRINTLN(F, ...) do { if (mesh::isUsbLoggingEnabled()) { mesh::usbLoggingPort().printf("POWERSAVING: " F "\n", ##__VA_ARGS__); } } while(0)
#else
#define POWERSAVING_DEBUG_PRINT(...) {}
#define POWERSAVING_DEBUG_PRINTLN(...) {}
+1 -1
View File
@@ -21,7 +21,7 @@
#ifdef MQTT_DEBUG
#include <Arduino.h>
#define ALERT_DEBUG_PRINTLN(...) do { if (mesh::isUsbLoggingEnabled()) { Serial.printf("Alert: " __VA_ARGS__); Serial.println(); } } while (0)
#define ALERT_DEBUG_PRINTLN(...) do { if (mesh::isUsbLoggingEnabled()) { mesh::usbLoggingPort().printf("Alert: " __VA_ARGS__); mesh::usbLoggingPort().println(); } } while (0)
#else
#define ALERT_DEBUG_PRINTLN(...) do {} while (0)
#endif
+58 -3
View File
@@ -722,6 +722,7 @@ void CommonCLI::loadPrefs(FILESYSTEM* fs) {
// contain its appended byte, so they safely inherit the enabled default.
_prefs->system_watchdog_enabled = 1;
memset(_prefs->extra_sf, 0, sizeof(_prefs->extra_sf));
_prefs->usb_logging_enabled = 1;
#ifdef WITH_MQTT_BRIDGE
bool node_prefs_needs_migration = false;
@@ -825,6 +826,9 @@ void CommonCLI::loadPrefs(FILESYSTEM* fs) {
#if defined(ENABLE_OTA)
if (loaded) syncOtaConfigFromPrefs(); // persisted OTA policy/keys -> OtaContext (else keep safe defaults)
#endif
#if MESH_USB_LOGGING_AVAILABLE
mesh::setUsbLoggingEnabled(_prefs->usb_logging_enabled != 0);
#endif
}
#if defined(ENABLE_OTA)
@@ -1205,6 +1209,10 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) {
if (file.available() >= (int)sizeof(_prefs->radio_fem_txgain)) {
file.read((uint8_t *)&_prefs->radio_fem_txgain,
sizeof(_prefs->radio_fem_txgain));
if (file.available() >= (int)sizeof(_prefs->usb_logging_enabled)) {
file.read((uint8_t *)&_prefs->usb_logging_enabled,
sizeof(_prefs->usb_logging_enabled));
}
}
} else if (file.available() > 0) {
// Never accept a torn append as a partial detector list.
@@ -1275,6 +1283,7 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) {
_prefs->rx_boosted_gain = constrain(_prefs->rx_boosted_gain, 0, 1); // boolean
_prefs->radio_fem_rxgain = constrain(_prefs->radio_fem_rxgain, 0, 1); // boolean
_prefs->radio_fem_txgain = constrain(_prefs->radio_fem_txgain, 0, 1); // boolean
_prefs->usb_logging_enabled = constrain(_prefs->usb_logging_enabled, 0, 1); // boolean
_prefs->cad_enabled = constrain(_prefs->cad_enabled, 0, 1); // boolean
if (!directRetryPrefsValid(_prefs)) {
setDefaultDirectRetryPrefs(_prefs);
@@ -1482,6 +1491,7 @@ static bool writeCommonPrefsImage(Writer& writer, NodePrefs* prefs) {
WRITE_COMMON_PREFS(&prefs->system_watchdog_enabled); // 855
WRITE_COMMON_PREFS(&prefs->extra_sf); // 856
WRITE_COMMON_PREFS(&prefs->radio_fem_txgain); // 860
WRITE_COMMON_PREFS(&prefs->usb_logging_enabled); // 861
#undef WRITE_COMMON_PREFS_BYTES
#undef WRITE_COMMON_PREFS
@@ -1633,7 +1643,8 @@ void CommonCLI::savePrefs(FILESYSTEM* fs, PrefsSaveRouting::Scope scope) {
sizeof(_prefs->system_watchdog_enabled)); // 855
file.write((uint8_t *)_prefs->extra_sf, sizeof(_prefs->extra_sf)); // 856
file.write((uint8_t *)&_prefs->radio_fem_txgain, sizeof(_prefs->radio_fem_txgain)); // 860
// next: 861
file.write((uint8_t *)&_prefs->usb_logging_enabled, sizeof(_prefs->usb_logging_enabled)); // 861
// next: 862
#if defined(NRF52_PLATFORM)
if (!file.commit()) {
@@ -3028,15 +3039,48 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
while (*value == ' ' || *value == '\t') value++;
if (strcmp(value, "on") == 0 || strcmp(value, "off") == 0) {
const bool enabled = strcmp(value, "on") == 0;
_prefs->usb_logging_enabled = enabled ? 1 : 0;
mesh::setUsbLoggingEnabled(enabled);
snprintf(reply, 160, "OK - USB logging %s until reboot",
enabled ? "on" : "off");
savePrefs();
snprintf(reply, 160, "OK - USB logging %s (saved)", enabled ? "on" : "off");
} else {
strcpy(reply, "Error: usage set usb.logging on|off");
}
return;
}
#endif
#if MESH_USB_LOGGING_AVAILABLE && defined(WITH_MQTT_BRIDGE)
if (strncmp(config, "logging.output", 14) == 0
&& (config[14] == 0 || config[14] == ' ' || config[14] == '\t')) {
const char* value = &config[14];
while (*value == ' ' || *value == '\t') value++;
bool usb_enabled;
bool wifi_enabled;
if (strcmp(value, "off") == 0) {
usb_enabled = false;
wifi_enabled = false;
} else if (strcmp(value, "usb") == 0) {
usb_enabled = true;
wifi_enabled = false;
} else if (strcmp(value, "wifi") == 0) {
usb_enabled = false;
wifi_enabled = true;
} else if (strcmp(value, "both") == 0) {
usb_enabled = true;
wifi_enabled = true;
} else {
strcpy(reply, "Error: usage set logging.output off|usb|wifi|both");
return;
}
_prefs->usb_logging_enabled = usb_enabled ? 1 : 0;
_prefs->bridge_enabled = wifi_enabled ? 1 : 0;
mesh::setUsbLoggingEnabled(usb_enabled);
_callbacks->setBridgeState(wifi_enabled);
savePrefs();
snprintf(reply, 160, "OK - logging.output %s (saved)", value);
return;
}
#endif
#if defined(ESP32_PLATFORM) || defined(USER_GPIO_CONTROL)
if (isGpioConfig(config)) {
const UserGpio::SetResult result = _user_gpio.handleSet(
@@ -4000,6 +4044,17 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* rep
return;
}
#endif
#if MESH_USB_LOGGING_AVAILABLE && defined(WITH_MQTT_BRIDGE)
if (strcmp(config, "logging.output") == 0) {
const bool usb_enabled = mesh::isUsbLoggingEnabled();
const bool wifi_enabled = _prefs->bridge_enabled != 0;
const char* mode = usb_enabled
? (wifi_enabled ? "both" : "usb")
: (wifi_enabled ? "wifi" : "off");
snprintf(reply, 160, "> %s", mode);
return;
}
#endif
#if defined(ESP32_PLATFORM) || defined(USER_GPIO_CONTROL)
if (isGpioConfig(config)) {
_user_gpio.handleGet(config + 4, reply, 160);
+5
View File
@@ -166,6 +166,10 @@ public:
// in-memory placement here does not shift the established binary layout.
uint8_t extra_sf[4] = {};
uint8_t radio_fem_txgain = 0; // LoRa FEM TX gain; persisted at /com_prefs offset 860
// Runtime USB packet output gate. Appended at /com_prefs offset 861 so
// older images remain readable and logging builds keep their historical
// enabled-at-first-boot behavior.
uint8_t usb_logging_enabled = 1;
uint8_t retry_preset = 0;
uint8_t direct_retry_attempts = 0;
uint16_t direct_retry_base_ms = 0;
@@ -263,6 +267,7 @@ private:
def("baud", _parent->bridge_baud);
def("ch", _parent->bridge_channel);
def("secret", _parent->bridge_secret, sizeof(_parent->bridge_secret));
def("usb_log", _parent->usb_logging_enabled);
}
public:
+51
View File
@@ -1,12 +1,28 @@
#include "UsbLogging.h"
#if defined(ARDUINO)
#include <Arduino.h>
#include <atomic>
#if defined(MESH_DUAL_CDC_LOGGING)
#if !defined(NRF52_PLATFORM) || !defined(COMPANION_RADIO_FULL)
#error "MESH_DUAL_CDC_LOGGING is only supported by nRF52 Full Companion"
#endif
#if !defined(CFG_TUD_CDC) || CFG_TUD_CDC < 2
#error "MESH_DUAL_CDC_LOGGING requires CFG_TUD_CDC >= 2"
#endif
#include <Adafruit_TinyUSB.h>
#endif
namespace mesh {
static std::atomic<bool> usb_logging_enabled{true};
#if defined(MESH_DUAL_CDC_LOGGING)
static Adafruit_USBD_CDC dedicated_usb_logging_port;
static bool dedicated_usb_logging_port_started = false;
#endif
bool isUsbLoggingEnabled() {
return usb_logging_enabled.load(std::memory_order_relaxed);
}
@@ -15,5 +31,40 @@ void setUsbLoggingEnabled(bool enabled) {
usb_logging_enabled.store(enabled, std::memory_order_relaxed);
}
void beginUsbLoggingPort() {
#if defined(MESH_DUAL_CDC_LOGGING)
if (dedicated_usb_logging_port_started) return;
dedicated_usb_logging_port.begin(115200);
dedicated_usb_logging_port_started = true;
// The nRF52 core starts TinyUSB before setup(). If the host completed
// enumeration in that small window, reconnect once so it reads the expanded
// two-CDC descriptor. Usually enumeration has not completed and no reconnect
// is needed.
if (TinyUSBDevice.mounted()) {
TinyUSBDevice.detach();
delay(10);
TinyUSBDevice.attach();
}
#endif
}
Stream& usbLoggingPort() {
#if defined(MESH_DUAL_CDC_LOGGING)
return dedicated_usb_logging_port;
#else
return Serial;
#endif
}
bool hasDedicatedUsbLoggingPort() {
#if defined(MESH_DUAL_CDC_LOGGING)
return true;
#else
return false;
#endif
}
} // namespace mesh
#endif
+11 -2
View File
@@ -12,12 +12,21 @@
#endif
#if defined(ARDUINO)
class Stream;
namespace mesh {
// This is intentionally session-only. Logging artifacts start enabled after
// every boot so a stale setting cannot make a diagnostic image look silent.
// Logging starts enabled on first boot. Roles with saved preferences restore
// the persisted setting after their preferences are loaded.
bool isUsbLoggingEnabled();
void setUsbLoggingEnabled(bool enabled);
// Start the optional dedicated USB logging interface. Ordinary builds keep
// using Serial. nRF52 Full Companion builds expose a second CDC ACM interface
// so plaintext diagnostics never share the framed Companion stream.
void beginUsbLoggingPort();
Stream& usbLoggingPort();
bool hasDedicatedUsbLoggingPort();
} // namespace mesh
#endif
+17
View File
@@ -45,6 +45,7 @@ static const uint32_t kRebootConfirmMs = 3000; // .cpp:784 first result-read
static const uint32_t kSetupWiFiConnectTimeoutMs = 20000;
static const uint32_t kSetupHandoffRebootConfirmMs = 1000;
static const uint32_t kStopWarnMs = 10000; // .h:95 STOP_WARN_MS
static const uint32_t kFullSetupApWindowMs = 30UL * 60UL * 1000UL;
// The batch lifecycle. A fresh POST moves Idle->Pending; the drainer moves
// Pending->Done; Done stays re-readable until the next POST claims the slot;
@@ -70,6 +71,22 @@ static inline bool deadlineReached(uint32_t now, uint32_t deadline) {
return (int32_t)(now - deadline) >= 0;
}
// FULL images expose an unconfigured setup AP for one bounded, boot-local
// window. Activity and connected stations do not extend it: until an SSID is
// saved the WiFi radio must go dark after 30 minutes. A reboot constructs a new
// WebConfigServer and therefore starts a new automatic window without
// persisting a timeout state. An explicit later `start webconfig` is still an
// operator override. timeout_ms == 0 keeps this policy disabled for profiles
// that retain the legacy idle-only behavior.
static inline bool unconfiguredSetupWindowExpired(bool setup_mode,
bool has_saved_ssid,
uint32_t now,
uint32_t setup_started_at,
uint32_t timeout_ms) {
return timeout_ms != 0 && setup_mode && !has_saved_ssid
&& elapsedMs(now, setup_started_at) >= timeout_ms;
}
// --------------------------------------------------------------------------
// POST accept classification (.cpp:637-718). Precedence, verbatim from the
// source: an in-flight/finished batch with the SAME reqid is an idempotent
+1 -1
View File
@@ -248,7 +248,7 @@ static void agentLogHeap(const char* location, const char* message, const char*
"\"data\":{\"free\":%u,\"max_alloc\":%u,\"internal_free\":%lu,\"spiram_free\":%lu},\"timestamp\":%lu}",
location, message, hypothesisId, (unsigned)free_h, (unsigned)max_alloc, internal_free, spiram_free,
(unsigned long)millis());
Serial.println(buf);
mesh::usbLoggingPort().println(buf);
}
// #endregion
#endif
+2 -2
View File
@@ -31,8 +31,8 @@ class MeshSNMPAgent; // Forward declaration
// USB CDC-aware debug macros: only print if Serial is ready (non-blocking check)
// Serial.availableForWrite() returns bytes available in write buffer (>0 means ready)
// This prevents hangs when USB CDC isn't ready yet (e.g., ESP32-S3 native USB)
#define MQTT_DEBUG_PRINT(F, ...) do { if (mesh::isUsbLoggingEnabled() && Serial.availableForWrite() > 0) { Serial.printf("MQTT: " F, ##__VA_ARGS__); } } while(0)
#define MQTT_DEBUG_PRINTLN(F, ...) do { if (mesh::isUsbLoggingEnabled() && Serial.availableForWrite() > 0) { Serial.printf("MQTT: " F "\n", ##__VA_ARGS__); } } while(0)
#define MQTT_DEBUG_PRINT(F, ...) do { if (mesh::isUsbLoggingEnabled() && mesh::usbLoggingPort().availableForWrite() > 0) { mesh::usbLoggingPort().printf("MQTT: " F, ##__VA_ARGS__); } } while(0)
#define MQTT_DEBUG_PRINTLN(F, ...) do { if (mesh::isUsbLoggingEnabled() && mesh::usbLoggingPort().availableForWrite() > 0) { mesh::usbLoggingPort().printf("MQTT: " F "\n", ##__VA_ARGS__); } } while(0)
#else
#define MQTT_DEBUG_PRINT(...) {}
#define MQTT_DEBUG_PRINTLN(...) {}
+2 -2
View File
@@ -64,8 +64,8 @@ public:
#if ESPNOW_DEBUG_LOGGING && ARDUINO
#include <Arduino.h>
#define ESPNOW_DEBUG_PRINT(F, ...) do { if (mesh::isUsbLoggingEnabled()) { Serial.printf("ESP-Now: " F, ##__VA_ARGS__); } } while(0)
#define ESPNOW_DEBUG_PRINTLN(F, ...) do { if (mesh::isUsbLoggingEnabled()) { Serial.printf("ESP-Now: " F "\n", ##__VA_ARGS__); } } while(0)
#define ESPNOW_DEBUG_PRINT(F, ...) do { if (mesh::isUsbLoggingEnabled()) { mesh::usbLoggingPort().printf("ESP-Now: " F, ##__VA_ARGS__); } } while(0)
#define ESPNOW_DEBUG_PRINTLN(F, ...) do { if (mesh::isUsbLoggingEnabled()) { mesh::usbLoggingPort().printf("ESP-Now: " F "\n", ##__VA_ARGS__); } } while(0)
#else
#define ESPNOW_DEBUG_PRINT(...) {}
#define ESPNOW_DEBUG_PRINTLN(...) {}
+2 -2
View File
@@ -115,8 +115,8 @@ public:
#if BLE_DEBUG_LOGGING && ARDUINO
#include <Arduino.h>
#define BLE_DEBUG_PRINT(F, ...) do { if (mesh::isUsbLoggingEnabled()) { Serial.printf("BLE: " F, ##__VA_ARGS__); } } while(0)
#define BLE_DEBUG_PRINTLN(F, ...) do { if (mesh::isUsbLoggingEnabled()) { Serial.printf("BLE: " F "\n", ##__VA_ARGS__); } } while(0)
#define BLE_DEBUG_PRINT(F, ...) do { if (mesh::isUsbLoggingEnabled()) { mesh::usbLoggingPort().printf("BLE: " F, ##__VA_ARGS__); } } while(0)
#define BLE_DEBUG_PRINTLN(F, ...) do { if (mesh::isUsbLoggingEnabled()) { mesh::usbLoggingPort().printf("BLE: " F "\n", ##__VA_ARGS__); } } while(0)
#else
#define BLE_DEBUG_PRINT(...) {}
#define BLE_DEBUG_PRINTLN(...) {}
+2 -2
View File
@@ -67,8 +67,8 @@ public:
#if WIFI_DEBUG_LOGGING && ARDUINO
#include <Arduino.h>
#define WIFI_DEBUG_PRINT(F, ...) do { if (mesh::isUsbLoggingEnabled()) { Serial.printf("WiFi: " F, ##__VA_ARGS__); } } while(0)
#define WIFI_DEBUG_PRINTLN(F, ...) do { if (mesh::isUsbLoggingEnabled()) { Serial.printf("WiFi: " F "\n", ##__VA_ARGS__); } } while(0)
#define WIFI_DEBUG_PRINT(F, ...) do { if (mesh::isUsbLoggingEnabled()) { mesh::usbLoggingPort().printf("WiFi: " F, ##__VA_ARGS__); } } while(0)
#define WIFI_DEBUG_PRINTLN(F, ...) do { if (mesh::isUsbLoggingEnabled()) { mesh::usbLoggingPort().printf("WiFi: " F "\n", ##__VA_ARGS__); } } while(0)
#else
#define WIFI_DEBUG_PRINT(...) {}
#define WIFI_DEBUG_PRINTLN(...) {}
+23 -1
View File
@@ -585,6 +585,7 @@ bool WebConfigServer::startSetupMode(char reply[]) {
_retry_saved_wifi_in_setup = false;
_setup_reconnect_in_progress = false;
_setup_reconnect_deadline = 0;
_setup_started_at = 0;
// AP_STA (not pure AP) so the WiFi scan for the SSID picker works while
// the AP is up. STA stays unconnected - the bridge won't touch WiFi
// while wifi_ssid is empty, and `start webconfig ap` requires it stopped.
@@ -630,7 +631,8 @@ bool WebConfigServer::startSetupMode(char reply[]) {
NodeSnapshot node = {};
_cb->getNodeSnapshot(node);
_initial_setup = _wifi_ssid[0] == 0 && node.admin_password[0] != 0;
_last_activity = millis();
_setup_started_at = millis();
_last_activity = _setup_started_at;
WiFi.scanNetworks(true); // pre-populate the SSID picker
sprintf(reply, "WebConfig AP started: join '%s' then open http://%s/", _ap_ssid, ip.toString().c_str());
@@ -647,6 +649,7 @@ bool WebConfigServer::startLanMode(char reply[]) {
return false;
}
_mode = MODE_LAN;
_setup_started_at = 0;
_wifi_reconnect_tracker.noteConnected();
_retry_saved_wifi_in_setup = false;
_setup_reconnect_in_progress = false;
@@ -678,6 +681,7 @@ bool WebConfigServer::startAutoMode(char reply[]) {
_retry_saved_wifi_in_setup = false;
_setup_reconnect_in_progress = false;
_setup_reconnect_deadline = 0;
_setup_started_at = 0;
_wifi_reconnect_tracker.noteDisconnected(millis());
WiFi.begin(_wifi_ssid, _wifi_password);
_wifi_power_save = effectiveWiFiPowerSave(_wifi_power_save);
@@ -749,6 +753,7 @@ void WebConfigServer::finalizeTeardown() {
WiFi.mode(WIFI_OFF);
}
_initial_setup = false;
_setup_started_at = 0;
_stopping = false;
_stop_warn_at = 0;
_stop_warned = false;
@@ -857,6 +862,7 @@ void WebConfigServer::tick(uint32_t now) {
WiFi.setAutoReconnect(true);
_was_setup_ap = false;
_initial_setup = false;
_setup_started_at = 0;
_retry_saved_wifi_in_setup = false;
_setup_reconnect_in_progress = false;
_setup_reconnect_deadline = 0;
@@ -907,6 +913,21 @@ void WebConfigServer::tick(uint32_t now) {
_stats_built_at = now;
}
// A FULL image with no saved SSID gets one absolute setup window per boot.
// Browser activity and an attached station cannot extend it. requestStop()
// tears down the AP and finalizeTeardown() powers WiFi fully off; the saved
// bridge/output preference is deliberately left unchanged for the next boot.
if (WebConfigBatch::unconfiguredSetupWindowExpired(
_mode == MODE_SETUP, _wifi_ssid[0] != 0, now,
_setup_started_at,
(uint32_t)WEBCONFIG_UNCONFIGURED_SETUP_TIMEOUT_MS)) {
Serial.printf(
"WebConfig: WiFi still unconfigured after %lu minutes; powering off until reboot or explicit restart\n",
(unsigned long)((uint32_t)WEBCONFIG_UNCONFIGURED_SETUP_TIMEOUT_MS / 60000UL));
requestStop();
return;
}
// Idle timeout: only the setup AP auto-stops (a deployed node must not be
// left broadcasting an open AP). LAN mode runs until `stop webconfig`.
if (_mode == MODE_SETUP && WiFi.softAPgetStationNum() == 0 &&
@@ -925,6 +946,7 @@ void WebConfigServer::tick(uint32_t now) {
WiFi.setAutoReconnect(true);
_was_setup_ap = false;
_initial_setup = false;
_setup_started_at = 0;
_retry_saved_wifi_in_setup = false;
_setup_reconnect_in_progress = false;
_setup_reconnect_deadline = 0;
+13 -1
View File
@@ -36,7 +36,18 @@ class AsyncWebServerRequest;
class DNSServer;
#ifndef WEBCONFIG_AP_IDLE_TIMEOUT_MS
#define WEBCONFIG_AP_IDLE_TIMEOUT_MS (10UL * 60UL * 1000UL)
#if defined(MESHCORE_ESP32_FULL_PROFILE)
#define WEBCONFIG_AP_IDLE_TIMEOUT_MS WebConfigBatch::kFullSetupApWindowMs
#else
#define WEBCONFIG_AP_IDLE_TIMEOUT_MS (10UL * 60UL * 1000UL)
#endif
#endif
#ifndef WEBCONFIG_UNCONFIGURED_SETUP_TIMEOUT_MS
#if defined(MESHCORE_ESP32_FULL_PROFILE)
#define WEBCONFIG_UNCONFIGURED_SETUP_TIMEOUT_MS WebConfigBatch::kFullSetupApWindowMs
#else
#define WEBCONFIG_UNCONFIGURED_SETUP_TIMEOUT_MS 0UL
#endif
#endif
#ifndef WEBCONFIG_SESSION_TTL_MS
#define WEBCONFIG_SESSION_TTL_MS (20UL * 60UL * 1000UL)
@@ -227,6 +238,7 @@ private:
bool _stopping = false;
bool _was_setup_ap = false;
bool _initial_setup = false;
uint32_t _setup_started_at = 0;
uint32_t _connect_deadline = 0;
char _wifi_ssid[32] = {0};
char _wifi_password[64] = {0};
@@ -70,9 +70,9 @@ class SerialEthernetInterface : public BaseSerialInterface {
#if ETHERNET_DEBUG_LOGGING && ARDUINO
#include <Arduino.h>
#define ETHERNET_DEBUG_PRINT(F, ...) do { if (mesh::isUsbLoggingEnabled()) { Serial.printf("ETH: " F, ##__VA_ARGS__); } } while(0)
#define ETHERNET_DEBUG_PRINTLN(F, ...) do { if (mesh::isUsbLoggingEnabled()) { Serial.printf("ETH: " F "\n", ##__VA_ARGS__); } } while(0)
#define ETHERNET_DEBUG_PRINT_IP(name, ip) do { if (mesh::isUsbLoggingEnabled()) { Serial.printf("ETH: " name ": %u.%u.%u.%u" "\n", ip[0], ip[1], ip[2], ip[3]); } } while(0)
#define ETHERNET_DEBUG_PRINT(F, ...) do { if (mesh::isUsbLoggingEnabled()) { mesh::usbLoggingPort().printf("ETH: " F, ##__VA_ARGS__); } } while(0)
#define ETHERNET_DEBUG_PRINTLN(F, ...) do { if (mesh::isUsbLoggingEnabled()) { mesh::usbLoggingPort().printf("ETH: " F "\n", ##__VA_ARGS__); } } while(0)
#define ETHERNET_DEBUG_PRINT_IP(name, ip) do { if (mesh::isUsbLoggingEnabled()) { mesh::usbLoggingPort().printf("ETH: " name ": %u.%u.%u.%u" "\n", ip[0], ip[1], ip[2], ip[3]); } } while(0)
#else
#define ETHERNET_DEBUG_PRINT(...) {}
#define ETHERNET_DEBUG_PRINTLN(...) {}
+2 -2
View File
@@ -95,8 +95,8 @@ public:
#if BLE_DEBUG_LOGGING && ARDUINO
#include <Arduino.h>
#define BLE_DEBUG_PRINT(F, ...) do { if (mesh::isUsbLoggingEnabled()) { Serial.printf("BLE: " F, ##__VA_ARGS__); } } while(0)
#define BLE_DEBUG_PRINTLN(F, ...) do { if (mesh::isUsbLoggingEnabled()) { Serial.printf("BLE: " F "\n", ##__VA_ARGS__); } } while(0)
#define BLE_DEBUG_PRINT(F, ...) do { if (mesh::isUsbLoggingEnabled()) { mesh::usbLoggingPort().printf("BLE: " F, ##__VA_ARGS__); } } while(0)
#define BLE_DEBUG_PRINTLN(F, ...) do { if (mesh::isUsbLoggingEnabled()) { mesh::usbLoggingPort().printf("BLE: " F "\n", ##__VA_ARGS__); } } while(0)
#else
#define BLE_DEBUG_PRINT(...) {}
#define BLE_DEBUG_PRINTLN(...) {}
+1 -1
View File
@@ -6,7 +6,7 @@
#if defined(OTA_DEBUG) && defined(ARDUINO)
#include <Arduino.h>
#include <helpers/UsbLogging.h>
#define OTA_DBG(...) do { if (mesh::isUsbLoggingEnabled()) { Serial.printf(__VA_ARGS__); } } while (0)
#define OTA_DBG(...) do { if (mesh::isUsbLoggingEnabled()) { mesh::usbLoggingPort().printf(__VA_ARGS__); } } while (0)
#else
#define OTA_DBG(...) do {} while (0)
#endif
+2 -2
View File
@@ -51,8 +51,8 @@ class CustomLLCC68 : public LLCC68 {
status = begin(LORA_FREQ, LORA_BW, LORA_SF, cr, RADIOLIB_SX126X_SYNC_WORD_PRIVATE, LORA_TX_POWER, 16, tcxo);
}
if (status != RADIOLIB_ERR_NONE) {
Serial.print("ERROR: radio init failed: ");
Serial.println(status);
mesh::usbLoggingPort().print("ERROR: radio init failed: ");
mesh::usbLoggingPort().println(status);
return false; // fail
}
+2 -2
View File
@@ -58,8 +58,8 @@ class CustomLR2021 : public LR2021 {
status = begin(LORA_FREQ, LORA_BW, LORA_SF, cr, RADIOLIB_LR2021_LORA_SYNC_WORD_PRIVATE, LORA_TX_POWER, 16, tcxo);
}
if (status != RADIOLIB_ERR_NONE) {
Serial.print("ERROR: radio init failed: ");
Serial.println(status);
mesh::usbLoggingPort().print("ERROR: radio init failed: ");
mesh::usbLoggingPort().println(status);
return false; // fail
}
+2 -2
View File
@@ -96,8 +96,8 @@ class CustomSX1262 : public SX1262 {
status = begin(LORA_FREQ, LORA_BW, LORA_SF, cr, RADIOLIB_SX126X_SYNC_WORD_PRIVATE, LORA_TX_POWER, 16, tcxo, useRegulatorLDO);
}
if (status != RADIOLIB_ERR_NONE) {
Serial.print("ERROR: radio init failed: ");
Serial.println(status);
mesh::usbLoggingPort().print("ERROR: radio init failed: ");
mesh::usbLoggingPort().println(status);
return false; // fail
}
+2 -2
View File
@@ -51,8 +51,8 @@ class CustomSX1268 : public SX1268 {
status = begin(LORA_FREQ, LORA_BW, LORA_SF, cr, RADIOLIB_SX126X_SYNC_WORD_PRIVATE, LORA_TX_POWER, 16, tcxo);
}
if (status != RADIOLIB_ERR_NONE) {
Serial.print("ERROR: radio init failed: ");
Serial.println(status);
mesh::usbLoggingPort().print("ERROR: radio init failed: ");
mesh::usbLoggingPort().println(status);
return false; // fail
}
+2 -2
View File
@@ -43,8 +43,8 @@ class CustomSX1276 : public SX1276 {
int status = begin(LORA_FREQ, LORA_BW, LORA_SF, cr, RADIOLIB_SX126X_SYNC_WORD_PRIVATE, LORA_TX_POWER, 16);
// if radio init fails with -707/-706, try again with tcxo voltage set to 0.0f
if (status != RADIOLIB_ERR_NONE) {
Serial.print("ERROR: radio init failed: ");
Serial.println(status);
mesh::usbLoggingPort().print("ERROR: radio init failed: ");
mesh::usbLoggingPort().println(status);
return false; // fail
}
#ifdef SX127X_CURRENT_LIMIT
@@ -147,7 +147,7 @@ public :
while (_gps_serial->available()) {
char c = _gps_serial->read();
#ifdef GPS_NMEA_DEBUG
if (mesh::isUsbLoggingEnabled()) Serial.print(c);
if (mesh::isUsbLoggingEnabled()) mesh::usbLoggingPort().print(c);
#endif
nmea.process(c);
}
+3 -2
View File
@@ -442,11 +442,12 @@ bool ST7735Display::begin() {
// alloc offscreen canvas
sprite = new TFT_eSprite(&lcd);
if (!sprite) {
Serial.println("ST7735Display: failed to alloc canvas");
mesh::usbLoggingPort().println("ST7735Display: failed to alloc canvas");
return false;
}
if (!sprite->createSprite(160, 80)) {
Serial.println("ST7735Display: failed to alloc canvas pixels");
mesh::usbLoggingPort().println(
"ST7735Display: failed to alloc canvas pixels");
delete sprite;
sprite = NULL;
return false;