Add T-Beam WiFi button toggle

This commit is contained in:
mikecarper committed 2026-08-15 18:12:09 -07:00
1 parent 843d309e2b
commit bd2e668961
15 files changed
+263 -45

No files matched your search

+10
View File
@@ -0,0 +1,10 @@
#pragma once
#if defined(ESP32) && defined(WIFI_SSID)
// The requested state is persisted immediately. Network services transition
// from the main loop so a button callback never tears down an active server.
bool toggleCompanionWiFi();
bool isCompanionWiFiEnabled();
#endif
+6
View File
@@ -336,6 +336,10 @@ void DataStore::loadPrefsInt(const char *filename, CompanionNodePrefs& _prefs, d
if (file.available() >= (int)sizeof(_prefs.powersaving_enabled)) {
file.read((uint8_t *)&_prefs.powersaving_enabled,
sizeof(_prefs.powersaving_enabled)); // 137
if (file.available() >= (int)sizeof(_prefs.wifi_enabled)) {
file.read((uint8_t *)&_prefs.wifi_enabled,
sizeof(_prefs.wifi_enabled)); // 138
}
}
}
@@ -401,6 +405,8 @@ bool DataStore::savePrefs(const CompanionNodePrefs& _prefs, double node_lat, dou
sizeof(_prefs.rx_ps_preamble)) == sizeof(_prefs.rx_ps_preamble); // 136
success = success && file.write((uint8_t *)&_prefs.powersaving_enabled,
sizeof(_prefs.powersaving_enabled)) == sizeof(_prefs.powersaving_enabled); // 137
success = success && file.write((uint8_t *)&_prefs.wifi_enabled,
sizeof(_prefs.wifi_enabled)) == sizeof(_prefs.wifi_enabled); // 138
#if defined(NRF52_PLATFORM)
success = file.commit(success);
+11
View File
@@ -1356,6 +1356,7 @@ MyMesh::MyMesh(mesh::Radio &radio, mesh::RNG &rng, mesh::RTCClock &rtc, SimpleMe
_prefs.rx_ps_rx_us = RX_POWERSAVING_DEFAULT_RX_US;
_prefs.rx_ps_sleep_us = RX_POWERSAVING_DEFAULT_SLEEP_US;
_prefs.powersaving_enabled = 0;
_prefs.wifi_enabled = 1;
recalcRxPowerSavingFromLevel(_prefs.rx_ps_level, _prefs.sf, _prefs.bw,
_prefs.rx_ps_preamble, &_prefs.rx_ps_rx_us,
&_prefs.rx_ps_sleep_us);
@@ -1439,6 +1440,7 @@ void MyMesh::begin(bool has_display) {
_prefs.radio_fem_txgain = constrain(_prefs.radio_fem_txgain, 0, 1);
_prefs.rx_powersaving_enabled = constrain(_prefs.rx_powersaving_enabled, 0, 1);
_prefs.powersaving_enabled = constrain(_prefs.powersaving_enabled, 0, 1);
_prefs.wifi_enabled = constrain(_prefs.wifi_enabled, 0, 1);
_prefs.rx_ps_level = constrain(_prefs.rx_ps_level, 0, 10);
if (_prefs.rx_ps_preamble != 16 && _prefs.rx_ps_preamble != 32) {
_prefs.rx_ps_preamble = 0;
@@ -1813,6 +1815,11 @@ void MyMesh::serviceMQTT(const char* wifi_ssid, const char* wifi_password) {
}
}
}
void MyMesh::stopMQTT() {
if (_mqtt_started && _mqtt_bridge) _mqtt_bridge->end();
_mqtt_started = false;
}
#endif
#ifdef WITH_WEBCONFIG
@@ -1938,6 +1945,10 @@ void MyMesh::serviceWebConfig() {
if (_webconfig) _webconfig->tick(millis());
}
bool MyMesh::isWebConfigActiveOrStopping() const {
return _webconfig && (_webconfig->isRunning() || _webconfig->isStopping());
}
bool MyMesh::isWebConfigSetupActive() const {
return _webconfig && _webconfig->mode() == WebConfigServer::MODE_SETUP
&& !_webconfig->isStopping();
+2
View File
@@ -121,6 +121,7 @@ public:
#if defined(WITH_MQTT_BRIDGE) && defined(ESP32_PLATFORM) && defined(WIFI_SSID)
void serviceMQTT(const char* wifi_ssid, const char* wifi_password);
void stopMQTT();
bool isMQTTConfigured() const { return _mqtt_configured; }
#endif
@@ -128,6 +129,7 @@ public:
bool startWebConfig(bool force_ap, char* reply);
void stopWebConfig();
void serviceWebConfig();
bool isWebConfigActiveOrStopping() const;
bool isWebConfigSetupActive() const;
bool isWebConfigWiFiRecoveryActive() const;
+1
View File
@@ -45,6 +45,7 @@ struct CompanionNodePrefs { // persisted to file
uint8_t rx_ps_level; // 0=manual timings, 1..10=level-derived
uint8_t rx_ps_preamble; // 0=auto from SF, otherwise 16 or 32
uint8_t powersaving_enabled; // device CPU/GPS idle power saving
uint8_t wifi_enabled; // Companion WiFi radio and services
// Keep the upstream repeat API while retaining the existing binary prefs
// layout used by this branch.
+162 -43
View File
@@ -1,6 +1,7 @@
#include <Arduino.h> // needed for PlatformIO
#include <Mesh.h>
#include "MyMesh.h"
#include "CompanionWiFi.h"
#ifdef ESP32_PLATFORM
#include "esp_bt.h"
@@ -431,6 +432,27 @@ void halt() {
bool wifi_setup_recovery_mode = false;
static char configured_wifi_ssid[32];
static char configured_wifi_password[64];
static bool companion_wifi_has_credentials = false;
static bool companion_wifi_requested = true;
static bool companion_wifi_active = false;
static bool companion_wifi_disable_in_progress = false;
static bool companion_wifi_services_stopped = false;
bool isCompanionWiFiEnabled() {
return companion_wifi_requested;
}
bool toggleCompanionWiFi() {
companion_wifi_requested = !companion_wifi_requested;
the_mesh.getNodePrefs()->wifi_enabled = companion_wifi_requested ? 1 : 0;
the_mesh.savePrefs();
if (!companion_wifi_requested && companion_wifi_active) {
companion_wifi_disable_in_progress = true;
}
WIFI_DEBUG_PRINTLN("GPIO 17 triple click requested WiFi %s",
companion_wifi_requested ? "on" : "off");
return companion_wifi_requested;
}
#if defined(WITH_WEBCONFIG) && defined(DISPLAY_CLASS)
static DisplayDriver* companion_setup_display = nullptr;
@@ -492,6 +514,7 @@ void halt() {
configured_wifi_ssid[sizeof(configured_wifi_ssid) - 1] = '\0';
strncpy(configured_wifi_password, password ? password : "", sizeof(configured_wifi_password) - 1);
configured_wifi_password[sizeof(configured_wifi_password) - 1] = '\0';
companion_wifi_has_credentials = true;
return true;
}
#endif
@@ -502,6 +525,7 @@ void halt() {
`normalradio` here so a host can run an end-to-end mOTA source without occupying the binary port. */
#if defined(ESP32) && defined(WIFI_SSID) && defined(ENABLE_OTA)
#include <helpers/ota/OtaCli.h> // mesh::ota::handle_ota_command(line, reply, board)
#include <helpers/esp32/WiFiOtaSeeder.h>
#ifndef OTA_CONSOLE_TCP_PORT
#define OTA_CONSOLE_TCP_PORT 5002
#endif
@@ -510,6 +534,18 @@ void halt() {
static char ota_console_line[128];
static uint8_t ota_console_len = 0;
static void ota_console_start() {
ota_console_server.begin();
WIFI_DEBUG_PRINTLN("OTA console listening on :%d (nc <ip> %d, type `ota ...`)",
OTA_CONSOLE_TCP_PORT, OTA_CONSOLE_TCP_PORT);
}
static void ota_console_stop() {
if (ota_console_client) ota_console_client.stop();
ota_console_server.end();
ota_console_len = 0;
}
static void ota_console_loop() {
if (!ota_console_client || !ota_console_client.connected()) {
WiFiClient c = ota_console_server.available();
@@ -543,6 +579,107 @@ void halt() {
}
#endif
#if defined(ESP32) && defined(WIFI_SSID)
static void resetCompanionWiFiRecoveryState() {
wifi_reconnect_tracker = WiFiReconnectPolicy::Tracker();
wifi_setup_attempted = false;
last_wifi_setup_attempt = 0;
wifi_setup_recovery_mode = false;
}
static void startCompanionWiFi() {
if (companion_wifi_active) return;
board.setInhibitSleep(true);
WiFi.setAutoReconnect(true);
resetCompanionWiFiRecoveryState();
if (companion_wifi_has_credentials) {
WiFi.mode(WIFI_STA);
wifi_reconnect_tracker.noteDisconnected(millis());
WiFi.begin(configured_wifi_ssid, configured_wifi_password);
}
#ifndef WITH_WEBCONFIG
else if (!wifiSetupPortal().begin(COMPANION_WIFI_SETUP_AP,
saveCompanionWiFi, nullptr)) {
WIFI_DEBUG_PRINTLN("WiFi setup: could not start setup portal");
}
#endif
#ifdef WITH_WEBCONFIG
if (WebConfigServer::loadEnabled(true)) {
char web_reply[160];
the_mesh.startWebConfig(!companion_wifi_has_credentials, web_reply);
WIFI_DEBUG_PRINTLN("%s", web_reply);
}
#endif
#if defined(BLE_PIN_CODE)
WiFi.setSleep(true);
#else
WiFi.setSleep(false);
#endif
wifi_interface.begin(TCP_PORT);
wifi_interface.enable();
#ifdef ENABLE_OTA
ota_console_start();
#endif
companion_wifi_active = true;
companion_wifi_services_stopped = false;
WIFI_DEBUG_PRINTLN("WiFi enabled by GPIO 17 control");
}
static void stopCompanionWiFiServices() {
if (companion_wifi_services_stopped) return;
wifi_interface.end();
#ifdef ENABLE_OTA
ota_console_stop();
mesh::ota::WiFiOtaSeeder::stop();
#endif
#ifdef WITH_MQTT_BRIDGE
the_mesh.stopMQTT();
#endif
companion_wifi_services_stopped = true;
}
static bool finishStoppingCompanionWiFi() {
stopCompanionWiFiServices();
#ifdef WITH_WEBCONFIG
if (the_mesh.isWebConfigActiveOrStopping()) {
the_mesh.stopWebConfig();
return false;
}
#else
if (wifiSetupPortal().isActive()) {
wifiSetupPortal().stop();
return false;
}
if (wifiSetupPortal().isStopping()) return false;
#endif
WiFi.setAutoReconnect(false);
WiFi.disconnect(true, false);
WiFi.mode(WIFI_OFF);
board.setInhibitSleep(false);
resetCompanionWiFiRecoveryState();
companion_wifi_active = false;
WIFI_DEBUG_PRINTLN("WiFi radio disabled by GPIO 17 control");
return true;
}
static void serviceCompanionWiFiState() {
if (!companion_wifi_requested && companion_wifi_active) {
companion_wifi_disable_in_progress = true;
}
if (companion_wifi_disable_in_progress) {
if (!finishStoppingCompanionWiFi()) return;
companion_wifi_disable_in_progress = false;
}
if (companion_wifi_requested && !companion_wifi_active) {
startCompanionWiFi();
}
}
#endif
void setup() {
Serial.begin(115200);
board.begin();
@@ -644,64 +781,39 @@ void setup() {
// add wifi interface
#ifdef WIFI_SSID
board.setInhibitSleep(true); // prevent sleep when WiFi is active
WiFi.setAutoReconnect(true);
WiFi.onEvent([](WiFiEvent_t event, WiFiEventInfo_t info){
if (event == ARDUINO_EVENT_WIFI_STA_DISCONNECTED) {
WIFI_DEBUG_PRINTLN("WiFi disconnected; automatic recovery is active");
if (companion_wifi_requested) {
WIFI_DEBUG_PRINTLN("WiFi disconnected; automatic recovery is active");
}
} else if (event == ARDUINO_EVENT_WIFI_STA_GOT_IP) {
WIFI_DEBUG_PRINTLN("connected! IP %s (companion app on :%d)",
WiFi.localIP().toString().c_str(), TCP_PORT);
}
});
bool have_wifi = WiFiSetupPortal::loadStoredCredentials(
companion_wifi_has_credentials = WiFiSetupPortal::loadStoredCredentials(
configured_wifi_ssid, sizeof(configured_wifi_ssid),
configured_wifi_password, sizeof(configured_wifi_password));
if (!have_wifi && !WiFiSetupPortal::isPlaceholderSSID(WIFI_SSID)) {
if (!companion_wifi_has_credentials
&& !WiFiSetupPortal::isPlaceholderSSID(WIFI_SSID)) {
strncpy(configured_wifi_ssid, WIFI_SSID, sizeof(configured_wifi_ssid) - 1);
configured_wifi_ssid[sizeof(configured_wifi_ssid) - 1] = '\0';
strncpy(configured_wifi_password, WIFI_PWD, sizeof(configured_wifi_password) - 1);
configured_wifi_password[sizeof(configured_wifi_password) - 1] = '\0';
have_wifi = true;
companion_wifi_has_credentials = true;
}
if (have_wifi) {
WiFi.mode(WIFI_STA);
wifi_reconnect_tracker.noteDisconnected(millis());
WiFi.begin(configured_wifi_ssid, configured_wifi_password);
}
#ifndef WITH_WEBCONFIG
else if (!wifiSetupPortal().begin(COMPANION_WIFI_SETUP_AP, saveCompanionWiFi, nullptr)) {
WIFI_DEBUG_PRINTLN("WiFi setup: could not start setup portal");
}
#endif
#ifdef WITH_WEBCONFIG
// WiFi companions expose the shared WebUI by default. With no stored
// credentials it opens the captive setup AP; otherwise it waits for the
// station connection and serves the same page on the LAN.
if (WebConfigServer::loadEnabled(true)) {
char web_reply[160];
the_mesh.startWebConfig(!have_wifi, web_reply);
WIFI_DEBUG_PRINTLN("%s", web_reply);
}
#endif
// ESP-IDF requires WiFi modem sleep when Bluetooth is active. Full ESP32
// companions provide both transports, so use the lowest sleep policy there;
// WiFi-only companions keep modem sleep disabled to avoid pauses in SX1262
// SPI/DIO service.
#if defined(BLE_PIN_CODE)
WiFi.setSleep(true);
#else
WiFi.setSleep(false);
#endif
wifi_interface.begin(TCP_PORT);
interface_manager.addInterface(InterfaceType::WiFi, &wifi_interface);
#ifdef ENABLE_OTA
ota_console_server.begin(); // dedicated OTA text-console port (`nc <ip> 5002` -> `ota ...`)
WIFI_DEBUG_PRINTLN("OTA console listening on :%d (nc <ip> %d, type `ota ...`)", OTA_CONSOLE_TCP_PORT, OTA_CONSOLE_TCP_PORT);
#endif
companion_wifi_requested = the_mesh.getNodePrefs()->wifi_enabled != 0;
if (companion_wifi_requested) {
startCompanionWiFi();
} else {
WiFi.setAutoReconnect(false);
WiFi.mode(WIFI_OFF);
board.setInhibitSleep(false);
WIFI_DEBUG_PRINTLN("WiFi remains off from the saved GPIO 17 setting");
}
#endif
// add usb interface
@@ -729,6 +841,9 @@ void setup() {
#endif
the_mesh.startInterface(interface_manager);
#if defined(ESP32) && defined(WIFI_SSID)
if (!companion_wifi_requested) wifi_interface.disable();
#endif
sensors.begin();
#if ENV_INCLUDE_GPS == 1
@@ -766,11 +881,12 @@ void loop() {
sensors.loop();
#ifdef DISPLAY_CLASS
#if defined(ESP32) && defined(WIFI_SSID) && defined(WITH_WEBCONFIG)
if (the_mesh.isWebConfigSetupActive()
if (isCompanionWiFiEnabled() && (the_mesh.isWebConfigSetupActive()
#ifdef WITH_MQTT_BRIDGE
|| !the_mesh.isMQTTConfigured()
#endif
) {
)) {
ui_task.serviceWiFiToggleButton();
renderCompanionSetupDisplay();
} else {
ui_task.loop();
@@ -807,6 +923,8 @@ void loop() {
#ifdef WITH_WEBCONFIG
the_mesh.serviceWebConfig();
#endif
serviceCompanionWiFiState();
if (companion_wifi_requested && companion_wifi_active) {
#ifdef ENABLE_OTA
ota_console_loop(); // service the OTA text console (port 5002)
#endif
@@ -903,5 +1021,6 @@ void loop() {
#ifdef WITH_MQTT_BRIDGE
the_mesh.serviceMQTT(configured_wifi_ssid, configured_wifi_password);
#endif
}
#endif
}
+21 -2
View File
@@ -1,6 +1,7 @@
#include "UITask.h"
#include <helpers/TxtDataHelpers.h>
#include "../MyMesh.h"
#include "../CompanionWiFi.h"
#include "target.h"
#ifdef WIFI_SSID
#include <WiFi.h>
@@ -234,8 +235,12 @@ public:
display.drawTextCentered(display.width() / 2, 22, tmp);
#ifdef WIFI_SSID
IPAddress ip = WiFi.localIP();
snprintf(tmp, sizeof(tmp), "IP: %d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
if (isCompanionWiFiEnabled()) {
IPAddress ip = WiFi.localIP();
snprintf(tmp, sizeof(tmp), "IP: %d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
} else {
strcpy(tmp, "WiFi: OFF");
}
display.setTextSize(1);
display.drawTextCentered(display.width() / 2, 54, tmp);
#endif
@@ -622,6 +627,14 @@ void UITask::begin(DisplayDriver* display, SensorManager* sensors, CompanionNode
setCurrScreen(splash);
}
void UITask::serviceWiFiToggleButton() {
#if defined(TBEAM_1W) && defined(WIFI_SSID) && defined(PIN_USER_BTN)
if (user_btn.check() == BUTTON_EVENT_TRIPLE_CLICK) {
handleTripleClick(KEY_SELECT);
}
#endif
}
void UITask::showAlert(const char* text, int duration_millis) {
strcpy(_alert, text);
_alert_expiry = millis() + duration_millis;
@@ -1000,7 +1013,13 @@ char UITask::handleDoubleClick(char c) {
char UITask::handleTripleClick(char c) {
MESH_DEBUG_PRINTLN("UITask: triple click triggered");
checkDisplayOn(c);
#if defined(TBEAM_1W) && defined(WIFI_SSID)
const bool enabled = toggleCompanionWiFi();
showAlert(enabled ? "WiFi: ON" : "WiFi: OFF", 1200);
_next_refresh = 0;
#else
toggleBuzzer();
#endif
c = 0;
return c;
}
+1
View File
@@ -80,6 +80,7 @@ public:
curr = NULL;
}
void begin(DisplayDriver* display, SensorManager* sensors, CompanionNodePrefs* node_prefs);
void serviceWiFiToggleButton();
void gotoHomeScreen() { setCurrScreen(home); }
void showAlert(const char* text, int duration_millis);