1#ifndef _PAINLESS_MESH_ARDUINO_WIFI_HPP_
2#define _PAINLESS_MESH_ARDUINO_WIFI_HPP_
7#ifdef PAINLESSMESH_ENABLE_ARDUINO_WIFI
17#include <HTTPClient.h>
18#include <WiFiClientSecure.h>
20#include <ESP8266HTTPClient.h>
21#include <WiFiClientSecure.h>
32#define PAINLESSMESH_HAS_TCP_LISTENING 1
63 WiFiMode_t connectMode = WIFI_AP_STA, uint8_t channel = 1,
64 uint8_t hidden = 0, uint8_t maxconn =
MAX_CONN,
66 using namespace logger;
71 if (WiFi.status() != WL_DISCONNECTED) WiFi.disconnect();
84 Log(STARTUP,
"init(): %d\n", WiFi.setAutoReconnect(
false));
85#if ESP_ARDUINO_VERSION_MAJOR < 3
86 WiFi.setAutoConnect(
false);
88 WiFi.persistent(
false);
91 if (!WiFi.mode(connectMode)) {
92 Log(GENERAL,
"WiFi.mode() false");
95#if SOC_WIFI_SUPPORT_5G && ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 2)
103 if (!WiFi.setBandMode(WIFI_BAND_MODE_2G_ONLY)) {
104 Log(ERROR,
"init(): could not restrict the radio to 2.4 GHz\n");
117 uint8_t MAC[] = {0, 0, 0, 0, 0, 0};
118#if ESP_ARDUINO_VERSION_MAJOR >= 3
119 esp_read_mac(MAC, ESP_MAC_WIFI_SOFTAP);
121 if (WiFi.softAPmacAddress(MAC) == 0) {
122 Log(ERROR,
"init(): WiFi.softAPmacAddress(MAC) failed.\n");
126 if (
nodeId == 0)
Log(ERROR,
"NodeId set to 0\n");
135 this->
addTask(30 * TASK_SECOND, TASK_FOREVER, [
this]() {
148 variant.printTo(str);
149 deserializeJson(doc, str);
150 JsonObject obj = doc.as<JsonObject>();
152 if (obj[
"routerRSSI"].is<int>()) {
153 uint32_t fromNode = obj[
"from"];
154 int8_t routerRSSI = obj[
"routerRSSI"];
155 uint8_t routerChannel = obj[
"routerChannel"] | 0;
156 uint32_t uptime = obj[
"uptime"] | 0;
157 uint32_t freeMemory = obj[
"freeMemory"] | 0;
162 Log(CONNECTION,
"Bridge election candidate from %u: RSSI %d dBm\n",
163 fromNode, routerRSSI);
175 variant.printTo(str);
176 deserializeJson(doc, str);
177 JsonObject obj = doc.as<JsonObject>();
179 if (obj[
"previousBridge"].is<unsigned int>()) {
180 uint32_t newBridge = obj[
"from"];
181 uint32_t previousBridge = obj[
"previousBridge"];
183 uint8_t routerChannel = obj[
"routerChannel"] | 0;
186 "Bridge takeover: Node %u replaced %u on channel %u (%s)\n",
187 newBridge, previousBridge, routerChannel, reason.c_str());
192 this->nodeId, newBridge,
_meshChannel, routerChannel)) {
193 this->
addTask(1000, TASK_ONCE, [
this, routerChannel]() {
211 Log(CONNECTION,
"Bridge %u lost Internet, considering election...\n",
216 Log(CONNECTION,
"No healthy bridges, starting election\n");
265 "Isolated bridge retry: Reset timeout reached, resetting attempt "
270 "Isolated bridge retry: Max attempts (%d) reached, reset in %u "
286 "Isolated bridge retry: Only %d empty scans, waiting for more "
296 "Isolated bridge retry: Node isolated with %d empty scans, "
297 "attempting bridge promotion\n",
318 if (connectMode & WIFI_AP) {
327 if (connectMode & WIFI_STA) {
332 if (!stationSSID.isEmpty() && (connectMode & WIFI_STA)) {
333 Log(STARTUP,
"init(): Connecting to station %s\n", stationSSID.c_str());
356 uint16_t port = 5555, WiFiMode_t connectMode = WIFI_AP_STA,
357 uint8_t channel = 1, uint8_t hidden = 0, uint8_t maxconn =
MAX_CONN,
360 init(ssid, password, port, connectMode, channel, hidden, maxconn,
361 stationSSID, stationPassword);
387 uint16_t port = 5555) {
388 using namespace logger;
390 Log(STARTUP,
"=== Bridge Mode Initialization ===\n");
391 Log(STARTUP,
"Step 1: Attempting to connect to router %s...\n",
routerSSID.c_str());
398 if (WiFi.status() != WL_DISCONNECTED) WiFi.disconnect();
401 Log(STARTUP,
"initAsBridge(): %d\n", WiFi.setAutoReconnect(
false));
402#if ESP_ARDUINO_VERSION_MAJOR < 3
403 WiFi.setAutoConnect(
false);
405 WiFi.persistent(
false);
413 while (WiFi.status() != WL_CONNECTED && timeout > 0) {
415 for (
int i = 0; i < 100; i++) { delay(10); yield(); }
420 uint8_t detectedChannel = 1;
421 bool routerConnected =
false;
423 if (WiFi.status() == WL_CONNECTED) {
424 detectedChannel = WiFi.channel();
426 if (detectedChannel < 1 || detectedChannel > 13) {
428 "\n[FAIL] Invalid channel detected: %d, falling back to channel 1\n",
432 Log(STARTUP,
"\n[OK] Router connected on channel %d\n", detectedChannel);
433 Log(STARTUP,
"[OK] Router IP: %s\n", WiFi.localIP().toString().c_str());
434 routerConnected =
true;
437 Log(STARTUP,
"\n[WARN] Router connection unavailable during initialization\n");
441 Log(STARTUP,
"[WARN] Scanning for router '%s' to detect channel...\n",
routerSSID.c_str());
445 int16_t numNetworks = WiFi.scanNetworks(
false,
false,
false, 300U, 0);
446#elif defined(ESP8266)
447 int16_t numNetworks = WiFi.scanNetworks(
false,
false, 0);
450 if (numNetworks > 0) {
451 for (int16_t i = 0; i < numNetworks; i++) {
453 uint8_t scannedChannel = WiFi.channel(i);
454 if (scannedChannel >= 1 && scannedChannel <= 13) {
455 detectedChannel = scannedChannel;
456 Log(STARTUP,
"[OK] Router found on channel %d (not connected, will retry)\n",
465 if (detectedChannel == 1) {
466 Log(STARTUP,
"[WARN] Router not found in scan, using default channel %d\n", detectedChannel);
469 Log(STARTUP,
"[WARN] Proceeding with bridge setup on channel %d\n", detectedChannel);
470 Log(STARTUP,
"[WARN] Bridge will retry router connection in background\n");
473 Log(STARTUP,
"Step 2: Initializing mesh on channel %d...\n",
478 init(meshSSID, meshPassword, baseScheduler, port, WIFI_AP_STA,
485 Log(STARTUP,
"Step 3: Establishing bridge connection...\n");
517 Log(STARTUP,
"=== Bridge Mode Active ===\n");
518 Log(STARTUP,
" Mesh SSID: %s\n", meshSSID.c_str());
519 Log(STARTUP,
" Mesh Channel: %d%s\n", detectedChannel,
520 routerConnected ?
" (matches router)" :
" (default, router pending)");
522 routerConnected ?
" (connected)" :
" (will retry)");
523 Log(STARTUP,
" Port: %d\n", port);
525 if (!routerConnected) {
526 Log(STARTUP,
"\nINFO: Bridge initialized without router connection\n");
527 Log(STARTUP,
"INFO: Mesh network is active and accepting node connections\n");
528 Log(STARTUP,
"INFO: Router connection will be established automatically when available\n");
554 uint16_t port, uint8_t priority) {
555 using namespace logger;
558 if (priority < 1) priority = 1;
559 if (priority > 10) priority = 10;
565 }
else if (priority >= 5) {
572 "=== Bridge Mode Initialization (Priority: %d, Role: %s) ===\n",
615 using namespace logger;
617 Log(STARTUP,
"=== Shared Gateway Mode Initialization ===\n");
620 if (config.enabled) {
621 auto result = config.validate();
623 Log(ERROR,
"initAsSharedGateway(): Config validation failed: %s\n",
624 result.errorMessage.c_str());
636 Log(STARTUP,
"Step 1: Scanning for router %s to detect channel...\n",
641 if (WiFi.status() != WL_DISCONNECTED) WiFi.disconnect();
644 WiFi.setAutoReconnect(
false);
645#if ESP_ARDUINO_VERSION_MAJOR < 3
646 WiFi.setAutoConnect(
false);
648 Log(STARTUP,
"initAsSharedGateway(): AutoReconnect disabled\n");
649 WiFi.persistent(
false);
657 while (WiFi.status() != WL_CONNECTED && timeout > 0) {
659 for (
int i = 0; i < 100; i++) { delay(10); yield(); }
664 uint8_t detectedChannel = 1;
666 if (WiFi.status() == WL_CONNECTED) {
667 detectedChannel = WiFi.channel();
672 "\n[FAIL] Invalid channel detected: %d, falling back to channel 1\n",
676 Log(STARTUP,
"\n[OK] Router connected on channel %d\n", detectedChannel);
677 Log(STARTUP,
"[OK] Router IP: %s\n", WiFi.localIP().toString().c_str());
680 Log(ERROR,
"\n[FAIL] Failed to connect to router during channel detection\n");
682 "Continuing with default channel 1, will retry router connection "
690 Log(STARTUP,
"Step 2: Initializing mesh on channel %d...\n",
696 init(meshPrefix, meshPassword, port, WIFI_AP_STA, detectedChannel, 0,
704 "Step 3: Establishing router connection in shared gateway mode...\n");
727 Log(STARTUP,
"=== Shared Gateway Mode Active ===\n");
728 Log(STARTUP,
" Mesh Prefix: %s\n", meshPrefix.c_str());
729 Log(STARTUP,
" Mesh Channel: %d (synced with router)\n", detectedChannel);
731 Log(STARTUP,
" Port: %d\n", port);
732 Log(STARTUP,
" Mode: AP+STA (all nodes can connect to router)\n");
763 using namespace logger;
767 Log(STARTUP,
"stationManual(): Connecting to %s\n", ssid.c_str());
784 WiFi.setAutoReconnect(
true);
787 WiFi.begin(ssid.c_str(), password.c_str());
789 Log(STARTUP,
"stationManual(): Connection initiated\n");
799 [
this](uint32_t
nodeId,
bool station) {
801 if (WiFi.status() == WL_CONNECTED) {
805 _pendingStationReconnect = true;
813 using namespace logger;
815 "Station link closed by this node's channel move\n");
824 using namespace logger;
826 "Station attempt failed, scanning this channel again\n");
834 using namespace logger;
836 "Station link lost unexpectedly, scanning now\n");
845 "Station link lost at home: scanning this channel\n");
856 "Station link lost in a rooted mesh: re-detecting the "
857 "mesh channel on this scan\n");
881 using namespace logger;
882 Log(GENERAL,
"tcpServerInit():\n");
896 "tcpServerInit(): listener on port %d already listening, kept\n",
904 painlessmesh::tcp::initServer<Connection, painlessmesh::Mesh<Connection>>(
905 (*_tcpListener), (*this));
906 Log(STARTUP,
"AP tcp server established on port %d\n",
_meshPort);
911 Log(CONNECTION,
"tcpServerInit(): listener on port %d, state %u\n",
962 using namespace logger;
963 Log(GENERAL,
"tcpConnect():\n");
967 if (WiFi.status() == WL_CONNECTED && WiFi.localIP()) {
973 Log(CONNECTION,
"tcpConnect(): Connecting to %s:%d\n",
974 targetIP.toString().c_str(), targetPort);
985 [
this, targetIP, targetPort]() {
987 if (WiFi.status() != WL_CONNECTED || !WiFi.localIP()) {
989 "tcpConnect(): WiFi disconnected during stabilization "
995 "tcpConnect(): Starting TCP connection after stabilization\n");
999 painlessmesh::tcp::connect<Connection, wifi::Mesh>(
1000 (*pConn), targetIP, targetPort, (*
this));
1003 Log(ERROR,
"tcpConnect(): err Something unexpected in tcpConnect()\n");
1018 using namespace logger;
1022 Log(CONNECTION,
"blockNodeAfterTCPFailure(): Invalid mesh IP %s, cannot block\n",
1023 ip.toString().c_str());
1027 Log(CONNECTION,
"blockNodeAfterTCPFailure(): Blocking node %u (IP: %s) for %u ms\n",
1028 nodeId, ip.toString().c_str(), blockDurationMs);
1035 return WiFi.hostname(hostname);
1037 if (strlen(hostname) > 32) {
1040 return WiFi.setHostname(hostname);
1091 if (minRSSI < -100) minRSSI = -100;
1092 if (minRSSI > -30) minRSSI = -30;
1107 if (delayMs < 10000) delayMs = 10000;
1124 if (minMs < 100) minMs = 100;
1125 if (maxMs < minMs) maxMs = minMs + 1000;
1155 using namespace logger;
1167 variant.printTo(str);
1168 deserializeJson(doc, str);
1169 JsonObject obj = doc.as<JsonObject>();
1171 if (obj[
"priority"].is<unsigned int>()) {
1172 uint32_t fromNode = obj[
"from"];
1188 auto& prev = it->second;
1189 bool changed = (prev.priority != pkg.
priority ||
1190 prev.role != pkg.
role ||
1191 prev.load != pkg.
load);
1193 prev.role = pkg.
role;
1194 prev.load = pkg.
load;
1195 prev.lastSeen = (uint32_t)millis();
1206 "onBridgeCoordination(): Registered Type 613 handler for non-bridge "
1224 using namespace logger;
1230 30000, TASK_FOREVER, [
this]() {
1231 uint32_t now = (uint32_t)millis();
1234 if ((now - it->second.lastSeen) > 60000) {
1235 uint32_t lostNode = it->first;
1238 pkg.
from = lostNode;
1239 pkg.
priority = it->second.priority;
1240 pkg.
role = it->second.role;
1241 pkg.
load = it->second.load;
1253 "onBridgeCoordinationChanged(): Lost detection task started "
1254 "(interval: 30s, timeout: 60s)\n");
1272 "enableMultiBridge(): Multi-bridge coordination enabled\n");
1296 if (maxBridges < 1) maxBridges = 1;
1297 if (maxBridges > 5) maxBridges = 5;
1309 std::vector<uint32_t> activeBridges;
1312 for (
const auto& bridge : bridges) {
1313 if (bridge.internetConnected && bridge.isHealthy()) {
1314 activeBridges.push_back(bridge.nodeId);
1318 return activeBridges;
1359 bool hasInternet = (WiFi.status() == WL_CONNECTED) &&
1360 (WiFi.localIP() !=
IPAddress(0, 0, 0, 0));
1381 if (activeBridges.empty()) {
1386 if (activeBridges.size() == 1) {
1387 return activeBridges[0];
1401 uint32_t bestBridge = 0;
1402 int8_t bestRSSI = -127;
1404 for (
const auto& bridge : this->
getBridges()) {
1405 if (bridge.internetConnected && bridge.isHealthy() &&
1406 bridge.routerRSSI > bestRSSI) {
1407 bestRSSI = bridge.routerRSSI;
1408 bestBridge = bridge.nodeId;
1417 uint32_t bestBridge = 0;
1418 uint8_t highestPriority = 0;
1420 for (uint32_t bridgeId : activeBridges) {
1422 if (priority > highestPriority) {
1423 highestPriority = priority;
1424 bestBridge = bridgeId;
1429 return bestBridge ? bestBridge : activeBridges[0];
1446 using namespace logger;
1471 "Bridge monitor: Skipping - no active mesh connections\n");
1476 bool hasHealthyBridge =
false;
1477 for (
const auto& bridge : this->
getBridges()) {
1479 hasHealthyBridge =
true;
1485 if (!hasHealthyBridge) {
1487 "Bridge monitor: No healthy bridge detected, triggering "
1491 uint32_t randomDelay =
1493 Log(CONNECTION,
"Bridge monitor: Scheduling election in %u ms\n",
1495 this->
addTask(randomDelay, TASK_ONCE,
1508 this->
addTask(wait + 1000, TASK_ONCE, [
this]() {
1524 WiFi.removeEvent(eventScanDoneHandler);
1525 WiFi.removeEvent(eventSTAStartHandler);
1526 WiFi.removeEvent(eventSTADisconnectedHandler);
1527 WiFi.removeEvent(eventSTAGotIPHandler);
1528#elif defined(ESP8266)
1529 eventSTAConnectedHandler = WiFiEventHandler();
1530 eventSTADisconnectedHandler = WiFiEventHandler();
1531 eventSTAGotIPHandler = WiFiEventHandler();
1542 if (WiFi.status() != WL_DISCONNECTED) WiFi.disconnect();
1553 friend class ::StationScan;
1567 void init(Scheduler* scheduler, uint32_t
id) {
1574 using namespace logger;
1583 WiFi.enableAP(
true);
1589 Log(STARTUP,
"apInit(): AP configured - SSID: %s, Channel: %d, IP: %s\n",
1591 Log(STARTUP,
"apInit(): AP active - Max connections: %d\n",
_meshMaxConn);
1599 using namespace logger;
1606 "initBridgeStatusBroadcast(): Setting up bridge status broadcast\n");
1613 const uint32_t INIT_DELAY_MS = 100;
1617 this->
addTask(INIT_DELAY_MS, TASK_ONCE, [
this]() {
1619 bool hasInternet = (WiFi.status() == WL_CONNECTED) &&
1620 (WiFi.localIP() !=
IPAddress(0, 0, 0, 0));
1627 WiFi.gatewayIP().toString(),
1632 "initBridgeStatusBroadcast(): Registered self as bridge (nodeId: "
1639 this->
addTask(INIT_DELAY_MS, TASK_ONCE, [
this]() {
1647 this->
addTask(INIT_DELAY_MS + 50, TASK_ONCE, [
this]() {
1648 Log(STARTUP,
"Sending initial bridge status broadcast\n");
1658 "Node %u connection changed, sending bridge status directly\n",
1669 this->
addTask(1000, TASK_ONCE, [
this]() {
1672 "Topology changed; broadcasting bridge status now rather "
1673 "than at the next interval\n");
1688 auto conn = router::findRoute<Connection>((*
this),
nodeId);
1689 if (!conn || !conn->connected()) {
1691 "Bridge status send cancelled: Node %u no longer connected\n",
1698 JsonObject obj = doc.to<JsonObject>();
1701 obj[
"from"] = this->
nodeId;
1706 bool hasInternet = (WiFi.status() == WL_CONNECTED) &&
1707 (WiFi.localIP() !=
IPAddress(0, 0, 0, 0));
1708 obj[
"internetConnected"] = hasInternet;
1709 obj[
"routerRSSI"] = WiFi.RSSI();
1710 obj[
"routerChannel"] = WiFi.channel();
1711 obj[
"uptime"] = millis();
1712 obj[
"gatewayIP"] = WiFi.gatewayIP().toString();
1716 serializeJson(doc, msg);
1719 "Sending bridge status directly to node %u (Internet: %s)\n",
1720 nodeId, hasInternet ?
"YES" :
"NO");
1726 conn->addMessage(msg,
true);
1730 Log(STARTUP,
"Bridge status broadcast enabled (interval: %d ms)\n",
1739 using namespace logger;
1746 "initBridgeCoordination(): Setting up multi-bridge coordination\n");
1753 "initBridgeCoordination(): Registered self priority (nodeId: %u, "
1764 variant.printTo(str);
1765 deserializeJson(doc, str);
1766 JsonObject obj = doc.as<JsonObject>();
1768 if (obj[
"priority"].is<unsigned int>()) {
1769 uint32_t fromNode = obj[
"from"];
1770 uint8_t priority = obj[
"priority"];
1772 uint8_t load = obj[
"load"] | 0;
1778 if (obj[
"peerBridges"].is<JsonArray>()) {
1779 JsonArray peers = obj[
"peerBridges"];
1780 for (JsonVariant peer : peers) {
1781 uint32_t peerId = peer.as<uint32_t>();
1782 if (peerId != this->
nodeId &&
1794 "Bridge coordination from %u: priority=%d, role=%s, "
1796 fromNode, priority, role.c_str(), load);
1810 priority, role, load, (uint32_t)millis()};
1813 auto& prev = it->second;
1814 bool changed = (prev.priority != priority ||
1815 prev.role != role ||
1817 prev.priority = priority;
1820 prev.lastSeen = (uint32_t)millis();
1836 Log(STARTUP,
"Bridge coordination enabled (priority: %d, role: %s)\n",
1845 using namespace logger;
1852 uint8_t currentLoad = 0;
1854 if (nodeCount > 0) {
1855 currentLoad = (nodeCount * 100) /
MAX_CONN;
1856 if (currentLoad > 100) currentLoad = 100;
1861 JsonObject obj = doc.to<JsonObject>();
1864 obj[
"from"] = this->
nodeId;
1868 obj[
"load"] = currentLoad;
1870 obj[
"message_type"] = 613;
1873 JsonArray peers = obj[
"peerBridges"].to<JsonArray>();
1879 serializeJson(doc, msg);
1888 "Bridge coordination sent: priority=%d, role=%s, load=%d%%\n",
1899 uint8_t* routerChannel =
nullptr) {
1900 using namespace logger;
1901 Log(CONNECTION,
"scanRouterSignalStrength(): Scanning for %s...\n",
1904 int n = WiFi.scanNetworks(
false,
false);
1905 if (n == WIFI_SCAN_RUNNING) {
1912 "scanRouterSignalStrength(): a scan is already running, waiting "
1914 uint32_t waitedUntil = millis() + 8000;
1926 while ((WiFiGenericClass::getStatusBits() & WIFI_SCANNING_BIT) &&
1927 (int32_t)(waitedUntil - millis()) > 0) {
1934 while (WiFi.scanComplete() == WIFI_SCAN_RUNNING &&
1935 (int32_t)(waitedUntil - millis()) > 0) {
1939 n = WiFi.scanNetworks(
false,
false);
1941 Log(CONNECTION,
"scanRouterSignalStrength(): Found %d networks\n", n);
1943 for (
int i = 0; i < n; i++) {
1945 int8_t rssi = WiFi.RSSI(i);
1946 uint8_t channel = WiFi.channel(i);
1947 if (routerChannel !=
nullptr) *routerChannel = channel;
1949 "scanRouterSignalStrength(): Found %s with RSSI %d dBm on "
1956 Log(CONNECTION,
"scanRouterSignalStrength(): Router %s not found\n",
1966 using namespace logger;
1969 Log(CONNECTION,
"startBridgeElection(): Failover disabled\n");
1975 "startBridgeElection(): No router credentials, cannot participate\n");
1980 Log(CONNECTION,
"startBridgeElection(): Election already in progress\n");
1991 "startBridgeElection(): Too soon after last role change, "
1992 "checking again in %u ms\n",
1996 this->
addTask(wait, TASK_ONCE, [
this]() {
2011 if (emptyScans >= 3 && WiFi.status() != WL_CONNECTED) {
2013 "startBridgeElection(): Mesh connectivity lost (%d empty scans), "
2014 "deferring election to allow channel re-sync\n",
2021 uint32_t retryDelay =
2024 "startBridgeElection(): Will retry election in %u seconds if still "
2030 Log(CONNECTION,
"=== Bridge Election Started ===\n");
2034 uint8_t routerChannel = 0;
2038 if (routerRSSI == 0) {
2040 "startBridgeElection(): Router not visible, cannot participate\n");
2045 Log(CONNECTION,
"startBridgeElection(): My router RSSI: %d dBm\n",
2056 selfCandidate.
uptime = millis();
2057 selfCandidate.
freeMemory = ESP.getFreeHeap();
2063 JsonObject obj = doc.to<JsonObject>();
2065 obj[
"from"] = this->
nodeId;
2067 obj[
"routerRSSI"] = routerRSSI;
2068 obj[
"routerChannel"] = routerChannel;
2069 obj[
"uptime"] = millis();
2070 obj[
"freeMemory"] = ESP.getFreeHeap();
2076 serializeJson(doc, msg);
2081 router::broadcast<Connection>(variant, (*
this), 0);
2083 Log(CONNECTION,
"startBridgeElection(): Candidacy broadcast sent\n");
2099 using namespace logger;
2102 Log(CONNECTION,
"evaluateElection(): Not in collecting state\n");
2106 Log(CONNECTION,
"=== Evaluating Election ===\n");
2107 Log(CONNECTION,
"evaluateElection(): %d candidates\n",
2112 int8_t bestRSSI = -127;
2116 "evaluateElection(): Candidate %u: RSSI=%d, uptime=%u, mem=%u\n",
2117 candidate.nodeId, candidate.routerRSSI, candidate.uptime,
2118 candidate.freeMemory);
2120 if (candidate.routerRSSI > bestRSSI) {
2121 bestRSSI = candidate.routerRSSI;
2122 winner = &candidate;
2123 }
else if (candidate.routerRSSI == bestRSSI && winner !=
nullptr) {
2125 if (candidate.uptime > winner->
uptime) {
2126 winner = &candidate;
2127 }
else if (candidate.uptime == winner->
uptime) {
2129 if (candidate.freeMemory > winner->
freeMemory) {
2130 winner = &candidate;
2131 }
else if (candidate.freeMemory == winner->
freeMemory) {
2133 if (candidate.nodeId < winner->
nodeId) {
2134 winner = &candidate;
2141 if (winner ==
nullptr) {
2142 Log(ERROR,
"evaluateElection(): No winner found!\n");
2154 Log(CONNECTION,
"=== Election Failed: Insufficient Signal Quality ===\n");
2156 " Single candidate with RSSI %d dBm (minimum required: %d dBm)\n",
2158 Log(CONNECTION,
" Node is isolated from mesh with poor router signal\n");
2159 Log(CONNECTION,
" Rejecting election to prevent unreliable bridge\n");
2161 " Recommendation: Move closer to router or wait for mesh "
2170 false,
"Insufficient signal quality for isolated bridge");
2175 Log(CONNECTION,
"=== Election Winner: Node %u ===\n", winner->
nodeId);
2176 Log(CONNECTION,
" Router RSSI: %d dBm\n", winner->
routerRSSI);
2177 Log(CONNECTION,
" Uptime: %u ms\n", winner->
uptime);
2178 Log(CONNECTION,
" Free Memory: %u bytes\n", winner->
freeMemory);
2187 record.
reason =
"Bridge failure detected";
2196 Log(CONNECTION,
"evaluateElection(): Election recorded in history\n");
2199 if (winner->
nodeId == this->nodeId) {
2200 Log(CONNECTION,
"[TARGET] I WON! Promoting to bridge...\n");
2203 Log(CONNECTION,
"Winner is node %u, remaining as regular node\n",
2224 using namespace logger;
2226 Log(STARTUP,
"=== Becoming Bridge Node ===\n");
2227 Log(STARTUP,
"Scheduling bridge promotion (async to avoid task corruption)\n");
2239 "Sending takeover announcement on current channel before "
2242 JsonObject obj = doc.to<JsonObject>();
2244 obj[
"from"] = this->
nodeId;
2246 obj[
"previousBridge"] = previousBridgeId;
2247 obj[
"reason"] =
"Election winner - best router signal";
2248 obj[
"routerRSSI"] = 0;
2249 obj[
"routerChannel"] = routerChannel;
2254 serializeJson(doc, msg);
2259 router::broadcast<Connection>(variant, (*
this), 0);
2263 for (
int i = 0; i < 100; i++) { delay(10); yield(); }
2264 Log(STARTUP,
"[OK] Takeover announcement sent on channel %d\n",
_meshChannel);
2288 [
this, savedChannel, savedMeshSSID, savedMeshPassword,
2289 savedRouterSSID, savedRouterPassword, savedScheduler,
2290 savedBridgeRoleChangedCallback]() {
2291 using namespace logger;
2293 Log(STARTUP,
"Executing bridge promotion (stop/reinit cycle)\n");
2298 for (
int i = 0; i < 100; i++) { delay(10); yield(); }
2302 this->
initAsBridge(savedMeshSSID, savedMeshPassword, savedRouterSSID,
2303 savedRouterPassword, savedScheduler,
_meshPort);
2307 Log(STARTUP,
"[OK] Bridge promotion complete on channel %d\n",
_meshChannel);
2310 if (savedBridgeRoleChangedCallback) {
2311 static const TSTRING reason =
"Election winner - best router signal";
2312 savedBridgeRoleChangedCallback(
true, reason);
2325 "Bridge takeover complete. Status broadcasts will announce bridge to "
2352 using namespace logger;
2354 Log(CONNECTION,
"=== Isolated Bridge Promotion Attempt ===\n");
2361 if (routerRSSI == 0) {
2363 "attemptIsolatedBridgePromotion(): Router %s not visible\n",
2371 "attemptIsolatedBridgePromotion(): Router RSSI %d dBm below "
2372 "threshold %d dBm\n",
2378 "attemptIsolatedBridgePromotion(): Router visible with RSSI %d dBm\n",
2381 "Attempting direct bridge promotion (bypassing election)\n");
2383 "Scheduling stop/reinit (async to avoid task corruption)\n");
2403 [
this, savedChannel, savedMeshSSID, savedMeshPassword,
2404 savedRouterSSID, savedRouterPassword, savedScheduler,
2405 savedBridgeRoleChangedCallback]() {
2406 using namespace logger;
2408 Log(CONNECTION,
"Executing isolated bridge promotion (stop/reinit cycle)\n");
2413 for (
int i = 0; i < 100; i++) { delay(10); yield(); }
2417 this->
initAsBridge(savedMeshSSID, savedMeshPassword, savedRouterSSID,
2418 savedRouterPassword, savedScheduler,
_meshPort);
2424 Log(STARTUP,
"[OK] Isolated bridge promotion complete on channel %d\n",
2428 if (savedBridgeRoleChangedCallback) {
2429 static const TSTRING reason =
"Isolated node promoted to bridge";
2430 savedBridgeRoleChangedCallback(
true, reason);
2440 "Bridge status announcement will be sent by bridge status broadcast "
2452 uint8_t routerChannel, uint32_t uptime,
2453 uint32_t freeMemory) {
2454 using namespace logger;
2458 "handleBridgeElection(): Not collecting candidates, ignoring\n");
2464 if (candidate.nodeId == fromNode) {
2466 "handleBridgeElection(): Duplicate candidate from %u, ignoring\n",
2473 candidate.
nodeId = fromNode;
2476 candidate.
uptime = uptime;
2482 "handleBridgeElection(): Added candidate %u (RSSI: %d dBm)\n", fromNode,
2491 using namespace logger;
2505 "sendBridgeStatus(): TCP listener on port %d is in state %u, not "
2506 "LISTEN; re-creating it\n",
2517 JsonObject obj = doc.to<JsonObject>();
2520 obj[
"from"] = this->
nodeId;
2530 bool hasInternet = !leaving && (WiFi.status() == WL_CONNECTED) &&
2531 (WiFi.localIP() !=
IPAddress(0, 0, 0, 0));
2532 obj[
"internetConnected"] = hasInternet;
2536 if (leaving) obj[
"leaving"] =
true;
2538 int8_t rssi = WiFi.RSSI();
2539 uint8_t channel = WiFi.channel();
2540 uint32_t uptime = millis();
2541 TSTRING gatewayIP = WiFi.gatewayIP().toString();
2543 obj[
"routerRSSI"] = rssi;
2544 obj[
"routerChannel"] = channel;
2545 obj[
"uptime"] = uptime;
2546 obj[
"gatewayIP"] = gatewayIP;
2550 serializeJson(doc, msg);
2552 Log(GENERAL,
"sendBridgeStatus(): Broadcasting status (Internet: %s)%s\n",
2553 hasInternet ?
"Connected" :
"Disconnected",
2554 leaving ?
" — stepping down" :
"");
2556 "sendBridgeStatus(): WiFi status=%d, localIP=%s, gatewayIP=%s\n",
2557 WiFi.status(), WiFi.localIP().toString().c_str(),
2558 WiFi.gatewayIP().toString().c_str());
2569 router::broadcast<Connection>(variant, (*
this), 0);
2583 using namespace logger;
2586 static uint32_t lastCheckTime = 0;
2587 static bool lastResult =
false;
2588 uint32_t now = millis();
2594 if (WiFi.status() != WL_CONNECTED) {
2595 lastCheckTime = millis();
2601 if (WiFi.localIP() ==
IPAddress(0, 0, 0, 0)) {
2602 lastCheckTime = millis();
2620 if (dnsResult != 1) {
2621 Log(COMMUNICATION,
"hasActualInternetAccess(): DNS resolution failed (code=%d)\n", dnsResult);
2622 lastCheckTime = millis();
2630 TSTRING resultStr = result.toString();
2631 Log(COMMUNICATION,
"hasActualInternetAccess(): Invalid DNS result IP: %s\n", resultStr.c_str());
2632 lastCheckTime = millis();
2647 lastCheckTime = millis();
2653 lastCheckTime = millis();
2658 TSTRING resultStr = result.toString();
2659 Log(COMMUNICATION,
"hasActualInternetAccess(): Internet connectivity verified (resolved to %s)\n",
2661 lastCheckTime = millis();
2680 using namespace logger;
2682#if defined(ESP32) || defined(ESP8266)
2687 static uint32_t lastCheckTime = 0;
2688 static bool lastResult =
true;
2689 uint32_t now = millis();
2696 lastCheckTime = millis();
2706 const char* testUrl =
"http://captive.apple.com/hotspot-detect.html";
2708 Log(COMMUNICATION,
"detectCaptivePortal(): Testing %s\n", testUrl);
2711 if (!http.begin(client, testUrl)) {
2712 Log(COMMUNICATION,
"detectCaptivePortal(): Failed to begin HTTP client - treating as potential network restriction\n");
2717 if (!http.begin(testUrl)) {
2718 Log(COMMUNICATION,
"detectCaptivePortal(): Failed to begin HTTP client - treating as potential network restriction\n");
2723 int httpCode = http.GET();
2725 if (httpCode != 200) {
2727 Log(COMMUNICATION,
"detectCaptivePortal(): Unexpected HTTP code %d (expected 200)\n", httpCode);
2733 String response = http.getString();
2739 if (response.indexOf(
"Success") == -1) {
2740 Log(COMMUNICATION,
"detectCaptivePortal(): Response doesn't contain 'Success', likely captive portal\n");
2744 Log(COMMUNICATION,
"detectCaptivePortal(): No captive portal detected\n");
2761#if defined(ESP32) || defined(ESP8266)
2770 static TSTRING readResponseExcerpt(HTTPClient& http) {
2771 const int size = http.getSize();
2773 static_cast<size_t>(size) <=
2774 gateway::GATEWAY_RESPONSE_HEAD_BYTES + gateway::GATEWAY_RESPONSE_TAIL_BYTES) {
2775 return http.getString();
2778 WiFiClient* stream = http.getStreamPtr();
2779 if (stream ==
nullptr)
return excerpt.
text();
2780 const bool chunked =
2783 const uint32_t deadline =
2784 millis() + gateway::GATEWAY_RESPONSE_HEAD_TIMEOUT_MS;
2786 while (more &&
static_cast<int32_t
>(deadline - millis()) > 0) {
2787 int available = stream->available();
2788 if (available <= 0) {
2789 if (!stream->connected())
break;
2793 while (more && available-- > 0) {
2794 const int c = stream->read();
2796 more = chunked ? decoder.
add(
static_cast<char>(c))
2797 : excerpt.
add(
static_cast<char>(c));
2800 return excerpt.
text();
2839 using namespace logger;
2841 "initGatewayInternetHandler(): Registering GATEWAY_DATA handler\n");
2845 std::shared_ptr<Connection> ingress,
2850 "Gateway received Internet request: msgId=%u dest=%s\n",
2851 pkg.messageId, pkg.destination.c_str());
2874 const auto blockingStartedMs = millis();
2878 auto finish = [
this, &pkg, ingress, blockingStartedMs](
2879 bool ok, uint16_t code,
const TSTRING& err,
2881 uint32_t retryAfterMs = 0) {
2882 const auto stalledMs = millis() - blockingStartedMs;
2884 if (refreshed > 0) {
2886 "Gateway postponed the mesh watchdog on %u peer(s) by %lu ms "
2887 "after a blocking Internet request\n",
2888 static_cast<unsigned>(refreshed),
2889 static_cast<unsigned long>(stalledMs));
2891 this->
sendGatewayAck(pkg, ok, code, err, ingress, response, retryable,
2897 if (WiFi.status() != WL_CONNECTED) {
2898 finish(
false, 0,
"Gateway WiFi not connected", 0);
2905 finish(
false, 0,
"Router has no internet access - check WAN connection", 0);
2912 finish(
false, 0,
"Captive portal detected - requires web authentication. Check router/WiFi settings", 0);
2916#if defined(ESP32) || defined(ESP8266)
2932 uint32_t failedAgoMs = 0;
2935 snprintf(dnsBuf,
sizeof(dnsBuf),
2936 "DNS lookup for %s failed %lus ago; not retried for "
2939 static_cast<unsigned long>(failedAgoMs / 1000),
2940 static_cast<unsigned long>(
2941 (gateway::GATEWAY_DNS_NEGATIVE_TTL_MS - failedAgoMs) /
2943 Log(ERROR,
"%s\n", dnsBuf);
2944 finish(
false, 0,
TSTRING(dnsBuf), 0);
2948 if (WiFi.hostByName(host.c_str(), resolved) != 1) {
2951 snprintf(dnsBuf,
sizeof(dnsBuf),
"DNS lookup failed for %s",
2953 Log(ERROR,
"%s\n", dnsBuf);
2954 finish(
false, 0,
TSTRING(dnsBuf), 0);
2964 bool success =
false;
2980 WiFiClientSecure secureClient;
2983 if (pkg.destination.startsWith(
"https://")) {
2988 http.begin(pkg.destination.c_str());
2989#elif defined(ESP8266)
2992 secureClient.setInsecure();
2993 http.begin(secureClient, pkg.destination.c_str());
2997 http.begin(pkg.destination.c_str());
2998#elif defined(ESP8266)
3000 http.begin(client, pkg.destination.c_str());
3009 http.addHeader(
"X-Request-Id", requestId.c_str());
3010 http.addHeader(
"Idempotency-Key", requestId.c_str());
3012 const char* collected[] = {
"Retry-After",
"Transfer-Encoding"};
3013 http.collectHeaders(collected, 2);
3016 if (pkg.payload.length() > 0) {
3017 http.addHeader(
"Content-Type", pkg.contentType.c_str());
3018 httpCode = http.POST(pkg.payload.c_str());
3020 httpCode = http.GET();
3037 readResponseExcerpt(http);
3039 const auto outcome =
3041 success = outcome.success;
3042 const TSTRING responseSummary =
3044 uint32_t retryAfterMs = 0;
3045 if (outcome.retryable && httpCode > 0) {
3049 if (!outcome.transportError) {
3051 const char* reason = outcome.reason.c_str();
3052 const char* sep = outcome.reason.length() > 0 ?
": " :
"";
3054 Log(COMMUNICATION,
"HTTP request completed: code=%d\n", httpCode);
3055 }
else if (outcome.unverifiedStatus) {
3060 snprintf(errorBuf,
sizeof(errorBuf),
3061 "HTTP %d: not a delivery the gateway can confirm%s%s",
3062 httpCode, sep, reason);
3064 Log(ERROR,
"HTTP response unverified: %s\n", errorBuf);
3065 }
else if (outcome.retryable) {
3067 snprintf(errorBuf,
sizeof(errorBuf),
"HTTP %d%s%s", httpCode, sep,
3070 Log(COMMUNICATION,
"HTTP %d: the server asked to retry (after %lu ms)\n",
3071 httpCode,
static_cast<unsigned long>(retryAfterMs));
3073 snprintf(errorBuf,
sizeof(errorBuf),
"HTTP %d%s%s", httpCode, sep,
3076 Log(ERROR,
"HTTP request failed: %s\n", errorBuf);
3083 error = http.errorToString(httpCode);
3084 if (!outcome.retryable) {
3085 error +=
" (the request may have reached the server; not retried)";
3087 Log(ERROR,
"HTTP request failed: %s\n", error.c_str());
3094 finish(success, outcome.ackStatus, error, outcome.retryable ? 1 : 0,
3095 responseSummary, retryAfterMs);
3098 finish(
false, 0,
"HTTP client not available on this platform", 0);
3121 using namespace logger;
3136 auto noteRoot = [
this](uint32_t) {
3146 variant.printTo(str);
3147 if (deserializeJson(doc, str))
return false;
3148 JsonObject obj = doc.as<JsonObject>();
3149 if (obj[
"leaving"] |
false) {
3155 uint8_t routerChannel = obj[
"routerChannel"] | 0;
3162 eventScanDoneHandler = WiFi.onEvent(
3163 [
this](WiFiEvent_t event, WiFiEventInfo_t info) {
3166 "eventScanDoneHandler: ARDUINO_EVENT_WIFI_SCAN_DONE\n");
3180#if ESP_ARDUINO_VERSION_MAJOR >= 2
3181 WiFiEvent_t::ARDUINO_EVENT_WIFI_SCAN_DONE);
3183 WiFiEvent_t::SYSTEM_EVENT_SCAN_DONE);
3186 eventSTAStartHandler = WiFi.onEvent(
3187 [
this](WiFiEvent_t event, WiFiEventInfo_t info) {
3190 "eventSTAStartHandler: ARDUINO_EVENT_WIFI_STA_START\n");
3194#if ESP_ARDUINO_VERSION_MAJOR >= 2
3195 WiFiEvent_t::ARDUINO_EVENT_WIFI_STA_START);
3197 WiFiEvent_t::SYSTEM_EVENT_STA_START);
3200 eventSTADisconnectedHandler = WiFi.onEvent(
3201 [
this](WiFiEvent_t event, WiFiEventInfo_t info) {
3204 "eventSTADisconnectedHandler: "
3205 "ARDUINO_EVENT_WIFI_STA_DISCONNECTED\n");
3214#if ESP_ARDUINO_VERSION_MAJOR >= 2
3215 WiFiEvent_t::ARDUINO_EVENT_WIFI_STA_DISCONNECTED);
3217 WiFiEvent_t::SYSTEM_EVENT_STA_DISCONNECTED);
3220 eventSTAGotIPHandler = WiFi.onEvent(
3221 [
this](WiFiEvent_t event, WiFiEventInfo_t info) {
3224 "eventSTAGotIPHandler: ARDUINO_EVENT_WIFI_STA_GOT_IP\n");
3232#if ESP_ARDUINO_VERSION_MAJOR >= 2
3233 WiFiEvent_t::ARDUINO_EVENT_WIFI_STA_GOT_IP);
3235 WiFiEvent_t::SYSTEM_EVENT_STA_GOT_IP);
3238#elif defined(ESP8266)
3239 eventSTAConnectedHandler = WiFi.onStationModeConnected(
3240 [&](
const WiFiEventStationModeConnected& event) {
3243 Log(CONNECTION,
"Event: Station Mode Connected\n");
3246 eventSTADisconnectedHandler = WiFi.onStationModeDisconnected(
3247 [&](
const WiFiEventStationModeDisconnected& event) {
3248 Log(CONNECTION,
"Event: Station Mode Disconnected\n");
3256 eventSTAGotIPHandler =
3257 WiFi.onStationModeGotIP([&](
const WiFiEventStationModeGotIP& event) {
3259 "Event: Station Mode Got IP (IP: %s Mask: %s Gateway: %s)\n",
3260 event.ip.toString().c_str(), event.mask.toString().c_str(),
3261 event.gw.toString().c_str());
3276 WiFiEventId_t eventScanDoneHandler = 0;
3277 WiFiEventId_t eventSTAStartHandler = 0;
3278 WiFiEventId_t eventSTADisconnectedHandler = 0;
3279 WiFiEventId_t eventSTAGotIPHandler = 0;
3280#elif defined(ESP8266)
3281 WiFiEventHandler eventSTAConnectedHandler;
3282 WiFiEventHandler eventSTADisconnectedHandler;
3283 WiFiEventHandler eventSTAGotIPHandler;
3373 uint8_t load_, uint32_t lastSeen_)
3405 using namespace logger;
3412 "initSharedGatewayMonitoring(): Setting up router connection "
3417 [
this](uint32_t
nodeId,
bool station) {
3420 "Router disconnected in shared gateway mode, scheduling "
3429 [
this]() { monitorRouterConnection(); });
3431 Log(STARTUP,
"Router connection monitoring enabled (interval: %u ms)\n",
3441 using namespace logger;
3447 bool isConnected = (WiFi.status() == WL_CONNECTED) &&
3448 (WiFi.localIP() !=
IPAddress(0, 0, 0, 0));
3452 "monitorRouterConnection(): Router connection lost, triggering "
3461 "monitorRouterConnection(): Router connected (RSSI: %d dBm, IP: "
3463 WiFi.RSSI(), WiFi.localIP().toString().c_str());
3471 using namespace logger;
3478 if (WiFi.status() == WL_CONNECTED) {
3485 "scheduleRouterReconnect(): Max reconnection attempts reached (%d)\n",
3488 "Router reconnection suspended. Manual intervention may be "
3495 uint8_t shiftAmount =
3502 uint32_t now = millis();
3511 "scheduleRouterReconnect(): Attempting reconnection (attempt %d/%d, "
3523 using namespace logger;
3530 if (WiFi.status() == WL_CONNECTED) {
3532 "attemptRouterReconnect(): Already connected to router\n");
3537 Log(CONNECTION,
"attemptRouterReconnect(): Reconnecting to router %s...\n",
boost::asio::ip::address IPAddress
Definition asynctcp.hpp:29
Definition asynctcp.hpp:31
Definition asynctcp.hpp:217
Definition painlessMeshSTA.h:23
void noteEverRooted()
Definition painlessMeshSTA.h:55
void init(painlessmesh::wifi::Mesh *pMesh, TSTRING ssid, TSTRING password, uint16_t port, uint8_t channel, bool hidden)
Definition painlessMeshSTA.cpp:25
bool droppedByMove() const
Definition painlessMeshSTA.h:77
bool followBridgeChannel(uint8_t targetChannel)
Definition painlessMeshSTA.cpp:854
bool stationLinkUp
Definition painlessMeshSTA.h:74
Task task
Definition painlessMeshSTA.h:25
IPAddress manualIP
Definition painlessMeshSTA.h:139
void scanDone()
Definition painlessMeshSTA.cpp:192
void noteRooted(uint8_t channel)
Definition painlessMeshSTA.h:48
void stationAttemptOver()
Definition painlessMeshSTA.h:61
uint16_t getConsecutiveEmptyScans() const
Definition painlessMeshSTA.h:108
void yieldConnectToAP()
Definition painlessMeshSTA.h:88
void stationUp()
Definition painlessMeshSTA.h:65
void redetectOnNextScan()
Definition painlessMeshSTA.h:83
bool atHome() const
Definition painlessMeshSTA.h:85
void stationDown()
Definition painlessMeshSTA.h:73
uint16_t port
Definition painlessMeshSTA.h:130
bool manual
Definition painlessMeshSTA.h:138
static const uint16_t EMPTY_SCAN_THRESHOLD
Definition painlessMeshSTA.h:232
void blockNodeAfterTCPFailure(uint32_t nodeId, uint32_t blockDurationMs=60000)
Definition painlessMeshSTA.cpp:483
void stop()
Definition mesh.hpp:516
std::list< uint32_t > getNodeList(bool includeSelf=false)
Definition mesh.hpp:3094
callback::List< uint32_t > newConnectionCallbacks
Definition mesh.hpp:4094
void sendGatewayAck(const gateway::GatewayDataPackage &request, bool success, uint16_t httpStatus, const TSTRING &error, std::shared_ptr< Connection > ingressConnection=nullptr, const TSTRING &response=TSTRING(), int8_t retryable=-1, uint32_t retryAfterMs=0)
Definition mesh.hpp:2138
uint32_t bridgeStatusIntervalMs
Definition mesh.hpp:4148
bool diagnosticsEnabled
Definition mesh.hpp:4166
bool isConnected(uint32_t nodeId)
Definition mesh.hpp:3085
void configureInternetHealthCheck(const gateway::SharedGatewayConfig &config)
Definition mesh.hpp:1601
std::vector< BridgeInfo > getBridges()
Definition mesh.hpp:1087
bool sendBroadcast(TSTRING msg, bool includeSelf=false)
Definition mesh.hpp:686
std::vector< ElectionRecord > electionHistory
Definition mesh.hpp:4167
bool isInternetHealthCheckEnabled() const
Definition mesh.hpp:1705
void setRoot(bool on=true)
Definition mesh.hpp:492
uint32_t bridgeTimeoutMs
Definition mesh.hpp:4149
bool bridgeStatusBroadcastEnabled
Definition mesh.hpp:4150
Scheduler * mScheduler
Definition mesh.hpp:4116
callback::List< uint32_t > changedConnectionCallbacks
Definition mesh.hpp:4096
uint32_t getPrimaryGateway()
Definition mesh.hpp:1283
void setScheduler(Scheduler *baseScheduler)
Definition mesh.hpp:4009
void onBridgeStatusChanged(bridgeStatusChangedCallback_t onBridgeStatusChanged)
Definition mesh.hpp:1003
bool capacityCheck(uint32_t freeHeap, uint32_t floor)
Definition mesh.hpp:1221
bool hasActiveMeshConnections()
Definition mesh.hpp:1168
void updateBridgeStatus(uint32_t bridgeNodeId, bool internetConnected, int8_t routerRSSI, uint8_t routerChannel, uint32_t uptime, TSTRING gatewayIP, uint32_t timestamp)
Definition mesh.hpp:2582
void setContainsRoot(bool on=true)
Definition mesh.hpp:501
callback::List< uint32_t, bool > droppedConnectionCallbacks
Definition mesh.hpp:4095
void enableInternetHealthCheck()
Definition mesh.hpp:1661
painlessmesh::tcp::TcpRetryConfig getTcpRetryConfig() const
Definition mesh.hpp:2015
bool shouldContainRoot
Is the node a root node.
Definition mesh.hpp:4113
bool semaphoreTake()
Definition mesh.hpp:4127
void semaphoreGive()
Definition mesh.hpp:4140
bool isBridge()
Definition mesh.hpp:1183
bool checkInternetNow()
Definition mesh.hpp:1643
std::shared_ptr< Task > addTask(unsigned long aInterval, long aIterations, std::function< void()> aCallback)
Definition mesh.hpp:3890
void init(uint32_t id)
Definition mesh.hpp:289
bool hasInternetConnection()
Definition mesh.hpp:1046
int execute(Args... args)
Definition callback.hpp:20
void push_back(std::function< void(Args...)> func)
Definition callback.hpp:45
Removes HTTP/1.1 chunked transfer framing from a body read a byte at a time, and passes the content o...
Definition gateway.hpp:1282
bool add(char c)
Definition gateway.hpp:1288
Gateway Data Package for routing Internet requests through mesh.
Definition gateway.hpp:815
A few hosts that recently failed to resolve, and when.
Definition gateway.hpp:1531
bool isFailing(const TSTRING &host, uint32_t nowMs, uint32_t *ageMs=nullptr)
Definition gateway.hpp:1558
void remember(const TSTRING &host, uint32_t nowMs, uint32_t ttlMs=GATEWAY_DNS_NEGATIVE_TTL_MS)
Definition gateway.hpp:1538
The start and the end of a response body, read a byte at a time.
Definition gateway.hpp:1228
bool add(char c)
Definition gateway.hpp:1231
TSTRING text() const
Definition gateway.hpp:1252
uint32_t nodeId
Definition layout.hpp:125
protocol::NodeTree asNodeTree()
Definition layout.hpp:115
uint32_t getNodeTime()
Definition ntp.hpp:43
Definition plugin.hpp:117
TSTRING role
Definition plugin.hpp:120
uint8_t priority
Definition plugin.hpp:119
uint8_t load
Definition plugin.hpp:122
uint32_t from
Definition plugin.hpp:71
callback::MeshPackageCallbackList< T > callbackList
Definition plugin.hpp:307
Definition protocol.hpp:481
T to()
Definition protocol.hpp:743
BridgeSelectionStrategy
Definition wifi.hpp:39
@ PRIORITY_BASED
Definition wifi.hpp:40
@ BEST_SIGNAL
Definition wifi.hpp:42
@ ROUND_ROBIN
Definition wifi.hpp:41
bool _sharedGatewayMode
Definition wifi.hpp:3382
int8_t scanRouterSignalStrength(TSTRING routerSSID, uint8_t *routerChannel=nullptr)
Definition wifi.hpp:1898
bool initAsBridge(TSTRING meshSSID, TSTRING meshPassword, TSTRING routerSSID, TSTRING routerPassword, Scheduler *baseScheduler, uint16_t port, uint8_t priority)
Definition wifi.hpp:552
TSTRING _meshPassword
Definition wifi.hpp:1558
bool isSharedGatewayMode() const
Definition wifi.hpp:742
uint32_t electionRandomDelayMinMs
Definition wifi.hpp:3319
std::shared_ptr< Task > _sharedGatewayMonitorTask
Definition wifi.hpp:3384
void init(uint32_t id)
Definition wifi.hpp:1571
void attemptRouterReconnect()
Definition wifi.hpp:3522
IPAddress _apIp
Definition wifi.hpp:1564
void initBridgeCoordination()
Definition wifi.hpp:1738
static const uint16_t ISOLATED_BRIDGE_RETRY_SCAN_THRESHOLD
Definition wifi.hpp:3344
StationScan stationScan
Definition wifi.hpp:1565
bool multiBridgeEnabled
Definition wifi.hpp:3351
size_t lastSelectedBridgeIndex
Definition wifi.hpp:3359
void evaluateElection()
Definition wifi.hpp:2098
void setMinimumBridgeRSSI(int8_t minRSSI)
Definition wifi.hpp:1090
uint8_t _meshChannel
Definition wifi.hpp:1559
uint8_t _isolatedBridgeRetryAttempts
Definition wifi.hpp:3333
static constexpr uint8_t TCP_STATE_LISTEN
Definition wifi.hpp:936
void eventHandleInit()
Definition wifi.hpp:3120
void setElectionRandomDelay(uint32_t minMs, uint32_t maxMs)
Definition wifi.hpp:1123
static const uint8_t MIN_WIFI_CHANNEL
Definition wifi.hpp:3394
void initGatewayInternetHandler()
Definition wifi.hpp:2838
static const uint32_t ROUTER_RECONNECT_MAX_INTERVAL
Definition wifi.hpp:3390
void blockNodeAfterTCPFailure(IPAddress ip, uint32_t blockDurationMs=painlessmesh::tcp::TCP_FAILURE_BLOCK_DURATION_MS)
Definition wifi.hpp:1017
void onBridgeCoordination(std::function< void(const plugin::BridgeCoordinationPackage &, uint32_t)> callback)
Definition wifi.hpp:1152
bool isMultiBridgeEnabled() const
Definition wifi.hpp:1439
std::shared_ptr< Task > bridgeLostDetectionTask
Definition wifi.hpp:3379
uint32_t electionStartupDelayMs
Definition wifi.hpp:3317
ElectionState electionState
Definition wifi.hpp:3327
uint32_t electionTimeoutMs
Definition wifi.hpp:3314
gateway::SharedGatewayConfig _sharedGatewayConfig
Definition wifi.hpp:3383
AsyncServer * _tcpListener
Definition wifi.hpp:3290
bool _pendingStationReconnect
Definition wifi.hpp:3297
void setMaxBridges(uint8_t maxBridges)
Definition wifi.hpp:1295
void sendBridgeStatus(bool leaving=false)
Definition wifi.hpp:2490
uint8_t maxConcurrentBridges
Definition wifi.hpp:3353
std::vector< uint32_t > getActiveBridges()
Definition wifi.hpp:1308
uint8_t bridgePriority
Definition wifi.hpp:3354
bool tcpListening()
Definition wifi.hpp:925
std::function< void(const plugin::BridgeCoordinationPackage &, uint32_t, TSTRING)> bridgeCoordinationChangedCallback
Definition wifi.hpp:3378
TSTRING routerPassword
Definition wifi.hpp:3313
void onBridgeCoordinationChanged(std::function< void(const plugin::BridgeCoordinationPackage &, uint32_t, TSTRING)> callback)
Definition wifi.hpp:1220
void promoteToBridge(uint8_t routerChannel)
Definition wifi.hpp:2223
void probeUplinkAfterAssociation()
Definition wifi.hpp:3115
bool routerCredentialsConfigured
Definition wifi.hpp:3311
static const uint32_t ROUTER_RECONNECT_BASE_INTERVAL
Definition wifi.hpp:3388
uint32_t _lastBridgeStatusBroadcast
Definition wifi.hpp:3294
uint32_t lastRoleChangeTime
Definition wifi.hpp:3323
void enableBridgeFailover(bool enabled)
Definition wifi.hpp:1056
uint32_t electionRandomDelayMaxMs
Definition wifi.hpp:3321
void handleBridgeElection(uint32_t fromNode, int8_t routerRSSI, uint8_t routerChannel, uint32_t uptime, uint32_t freeMemory)
Definition wifi.hpp:2451
void startBridgeElection()
Definition wifi.hpp:1965
std::function< void(const plugin::BridgeCoordinationPackage &, uint32_t)> bridgeCoordinationCallback
Definition wifi.hpp:3377
void setBridgeSelectionStrategy(BridgeSelectionStrategy strategy)
Definition wifi.hpp:1284
void enableMultiBridge(bool enabled)
Definition wifi.hpp:1268
void initSharedGatewayMonitoring()
Definition wifi.hpp:3404
int8_t minimumBridgeRSSI
Definition wifi.hpp:3315
std::function< void(bool isBridge, const TSTRING &reason)> bridgeRoleChangedCallback
Definition wifi.hpp:3330
uint8_t _routerReconnectAttempts
Definition wifi.hpp:3386
static const uint32_t isolatedBridgeRetryResetIntervalMs
Definition wifi.hpp:3342
void initStation()
Definition wifi.hpp:792
std::shared_ptr< Task > bridgeCoordinationTask
Definition wifi.hpp:3356
IPAddress getAPIP()
Definition wifi.hpp:1045
static const uint8_t MAX_WIFI_CHANNEL
Definition wifi.hpp:3395
bool hasActualInternetAccess()
Definition wifi.hpp:2582
static const uint32_t ASYNC_PROMOTION_DELAY_MS
Definition wifi.hpp:3346
void scheduleRouterReconnect()
Definition wifi.hpp:3470
gateway::NegativeDnsCache negativeDns
Definition wifi.hpp:2759
void setElectionTimeout(uint32_t timeoutMs)
Definition wifi.hpp:1079
TSTRING _meshSSID
Definition wifi.hpp:1557
std::shared_ptr< Task > bridgeStatusTask
Definition wifi.hpp:3291
ElectionState
Definition wifi.hpp:3300
@ ELECTION_COLLECTING
Definition wifi.hpp:3300
@ ELECTION_IDLE
Definition wifi.hpp:3300
@ ELECTION_SCANNING
Definition wifi.hpp:3300
bool detectCaptivePortal()
Definition wifi.hpp:2679
void checkForBridge()
Definition wifi.hpp:1445
bool hasInternetConnection()
Definition wifi.hpp:1355
void init(TSTRING ssid, TSTRING password, Scheduler *baseScheduler, uint16_t port=5555, WiFiMode_t connectMode=WIFI_AP_STA, uint8_t channel=1, uint8_t hidden=0, uint8_t maxconn=MAX_CONN, TSTRING stationSSID="", TSTRING stationPassword="")
Definition wifi.hpp:355
bool _isolatedRetryPending
Definition wifi.hpp:3336
void init(TSTRING ssid, TSTRING password, uint16_t port=5555, WiFiMode_t connectMode=WIFI_AP_STA, uint8_t channel=1, uint8_t hidden=0, uint8_t maxconn=MAX_CONN, TSTRING stationSSID="", TSTRING stationPassword="")
Definition wifi.hpp:62
uint8_t _meshHidden
Definition wifi.hpp:1560
const gateway::SharedGatewayConfig & getSharedGatewayConfig() const
Definition wifi.hpp:749
uint32_t electionDeadline
Definition wifi.hpp:3328
void tcpServerInit()
Definition wifi.hpp:880
static const uint8_t MAX_ROUTER_RECONNECT_ATTEMPTS
Definition wifi.hpp:3387
uint16_t _meshPort
Definition wifi.hpp:1562
void initBridgeStatusBroadcast()
Definition wifi.hpp:1598
uint32_t _lastRouterReconnectAttempt
Definition wifi.hpp:3385
bool initAsSharedGateway(TSTRING meshPrefix, TSTRING meshPassword, TSTRING routerSSID, TSTRING routerPassword, Scheduler *userScheduler, uint16_t port=5555, gateway::SharedGatewayConfig config=gateway::SharedGatewayConfig())
Definition wifi.hpp:611
void stop()
Definition wifi.hpp:1514
void tcpConnect()
Definition wifi.hpp:961
void sendBridgeCoordination()
Definition wifi.hpp:1844
std::map< uint32_t, BridgeCoordinationState > lastBridgeCoordinationState
Definition wifi.hpp:3376
void setRouterCredentials(TSTRING ssid, TSTRING password)
Definition wifi.hpp:1068
static const int ROUTER_CONNECTION_TIMEOUT_SECONDS
Definition wifi.hpp:3392
void setElectionStartupDelay(uint32_t delayMs)
Definition wifi.hpp:1106
bool bridgeFailoverEnabled
Definition wifi.hpp:3310
void handleStationDisconnectComplete()
Definition wifi.hpp:871
bool attemptIsolatedBridgePromotion()
Definition wifi.hpp:2351
void checkForBridgeSoon()
Definition wifi.hpp:1502
void apInit(uint32_t nodeId)
Definition wifi.hpp:1573
uint32_t _isolatedBridgeRetryResetTime
Definition wifi.hpp:3334
IPAddress getStationIP()
Definition wifi.hpp:1044
uint32_t getRecommendedBridge()
Definition wifi.hpp:1378
void stationManual(TSTRING ssid, TSTRING password, uint16_t port=0, IPAddress remote_ip=IPAddress(0, 0, 0, 0))
Definition wifi.hpp:761
bool initAsBridge(TSTRING meshSSID, TSTRING meshPassword, TSTRING routerSSID, TSTRING routerPassword, Scheduler *baseScheduler, uint16_t port=5555)
Definition wifi.hpp:385
static const uint32_t isolatedBridgeRetryIntervalMs
Definition wifi.hpp:3340
static const uint8_t MAX_ISOLATED_BRIDGE_RETRY_ATTEMPTS
Definition wifi.hpp:3338
std::vector< BridgeCandidate > electionCandidates
Definition wifi.hpp:3329
std::vector< uint32_t > knownBridgePeers
Definition wifi.hpp:3358
TSTRING bridgeRole
Definition wifi.hpp:3355
bool bridgeCheckPending
Definition wifi.hpp:3326
bool setHostname(const char *hostname)
Definition wifi.hpp:1033
void monitorRouterConnection()
Definition wifi.hpp:3440
BridgeSelectionStrategy bridgeSelectionStrategy
Definition wifi.hpp:3352
TSTRING routerSSID
Definition wifi.hpp:3312
uint8_t _meshMaxConn
Definition wifi.hpp:1561
void onBridgeRoleChanged(std::function< void(bool isBridge, const TSTRING &reason)> callback)
Definition wifi.hpp:1135
std::map< uint32_t, uint8_t > bridgePriorities
Definition wifi.hpp:3357
void init(Scheduler *scheduler, uint32_t id)
Definition wifi.hpp:1567
#define MAX_CONN
Definition configuration.hpp:153
String TSTRING
Definition configuration.hpp:132
Shared Gateway Mode configuration for painlessMesh.
#define GATEWAY_HTTP_TIMEOUT_MS
Definition gateway.hpp:1847
#define GATEWAY_CAPTIVE_PORTAL_TIMEOUT_MS
Definition gateway.hpp:1853
#define GATEWAY_CONNECTIVITY_CACHE_MS
Definition gateway.hpp:1875
#define GATEWAY_DNS_TIMEOUT_MS
Definition gateway.hpp:1865
uint32_t parseRetryAfterMs(const TSTRING &value)
The delay a Retry-After header asks for, in milliseconds.
Definition gateway.hpp:1388
TSTRING summarizeResponseBody(const TSTRING &body, size_t maxLen=GATEWAY_RESPONSE_REASON_MAX)
Reduce a response body to one line fit for a log or an error string.
Definition gateway.hpp:1179
constexpr bool isValidMeshChannel(uint8_t channel)
Definition gateway.hpp:52
HttpRequestOutcome classifyHttpResult(int rawCode, const TSTRING &body=TSTRING())
Classify an HTTPClient result for the gateway acknowledgment.
Definition gateway.hpp:1451
constexpr bool shouldFollowBridgeChannel(uint32_t localNodeId, uint32_t electedBridgeId, uint8_t currentChannel, uint8_t announcedChannel)
Definition gateway.hpp:57
bool looksLikeIpLiteral(const TSTRING &host)
Definition gateway.hpp:1515
TSTRING requestIdFor(uint32_t originNode, uint32_t messageId, uint32_t requestNonce=0)
The identifier the gateway sends with a request, as both X-Request-Id and Idempotency-Key.
Definition gateway.hpp:1417
bool transferEncodingIsChunked(const TSTRING &value)
Definition gateway.hpp:1371
TSTRING hostFromUrl(const TSTRING &url)
The host of an http(s) URL, without scheme, port, path or query.
Definition gateway.hpp:1497
size_t refreshPeerWatchdogs(T &mesh, unsigned long stalledMs)
Give every peer's running watchdog back the time a blocking call hid.
Definition gateway.hpp:1983
bool isRooted(protocol::NodeTree nodeTree)
Definition layout.hpp:251
@ GENERAL
Definition logger.hpp:26
constexpr int BRIDGE_ELECTION
Definition protocol.hpp:55
constexpr int BRIDGE_TAKEOVER
Definition protocol.hpp:56
constexpr int GATEWAY_DATA
Definition protocol.hpp:61
constexpr int BRIDGE_STATUS
Definition protocol.hpp:54
uint32_t decodeNodeIdFromIP(IPAddress ip)
Definition tcp.hpp:127
uint32_t encodeNodeId(const uint8_t *hwaddr)
Definition tcp.hpp:111
uint32_t candidateCount
Definition mesh.hpp:251
uint32_t timestamp
Definition mesh.hpp:248
uint32_t winnerNodeId
Definition mesh.hpp:249
TSTRING reason
Definition mesh.hpp:252
int8_t winnerRSSI
Definition mesh.hpp:250
Configuration structure for Shared Gateway Mode.
Definition gateway.hpp:162
uint32_t internetCheckInterval
Interval between Internet connectivity checks in milliseconds.
Definition gateway.hpp:201
TSTRING routerSSID
Router SSID to connect for Internet access.
Definition gateway.hpp:180
bool enabled
Enable or disable shared gateway functionality.
Definition gateway.hpp:172
TSTRING routerPassword
Router password for authentication.
Definition gateway.hpp:188
uint32_t nodeId
Definition wifi.hpp:3303
uint32_t freeMemory
Definition wifi.hpp:3307
uint8_t routerChannel
Definition wifi.hpp:3305
int8_t routerRSSI
Definition wifi.hpp:3304
uint32_t uptime
Definition wifi.hpp:3306
BridgeCoordinationState()
Definition wifi.hpp:3371
uint8_t priority
Definition wifi.hpp:3367
TSTRING role
Definition wifi.hpp:3368
uint8_t load
Definition wifi.hpp:3369
uint32_t lastSeen
Definition wifi.hpp:3370
BridgeCoordinationState(uint8_t priority_, const TSTRING &role_, uint8_t load_, uint32_t lastSeen_)
Definition wifi.hpp:3372
painlessmesh::logger::LogClass Log
Definition wifi.cpp:2