@alteriom/painlessmesh 1.8.14 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (208) hide show
  1. package/BRIDGE_TO_INTERNET.md +229 -0
  2. package/CHANGELOG.md +89 -1
  3. package/CONTRIBUTING.md +79 -0
  4. package/README.md +70 -143
  5. package/docs/README.md +1 -0
  6. package/docs/api/shared-gateway.md +1207 -0
  7. package/docs/troubleshooting/common-issues.md +28 -0
  8. package/docs/troubleshooting/faq.md +113 -12
  9. package/examples/basic/test/simulator/CMakeLists.txt +40 -0
  10. package/examples/basic/test/simulator/README.md +149 -0
  11. package/examples/basic/test/simulator/firmware/basic_firmware.hpp +117 -0
  12. package/examples/basic/test/simulator/scenarios/basic_mesh_test.yaml +81 -0
  13. package/examples/bridge/bridge.ino +17 -4
  14. package/examples/bridge_failover/README.md +81 -0
  15. package/examples/bridge_failover/bridge_failover.ino +51 -6
  16. package/examples/sharedGateway/README.md +235 -0
  17. package/examples/{meshCommandNode → sharedGateway}/platformio.ini +3 -2
  18. package/examples/sharedGateway/sharedGateway.ino +303 -0
  19. package/library.json +3 -22
  20. package/library.properties +1 -1
  21. package/package.json +3 -3
  22. package/src/arduino/wifi.hpp +380 -13
  23. package/src/painlessmesh/gateway.hpp +2120 -0
  24. package/src/painlessmesh/mesh.hpp +1034 -6
  25. package/src/painlessmesh/message_tracker.hpp +311 -0
  26. package/src/painlessmesh/protocol.hpp +6 -0
  27. package/DOCUMENTATION_INDEX.md +0 -146
  28. package/docs/API_DESIGN_GUIDELINES.md +0 -414
  29. package/docs/ARDUINO_LIBRARY_MANAGER_SUBMISSION.md +0 -331
  30. package/docs/BOOLEAN_NAMING_CONVENTION.md +0 -235
  31. package/docs/BRIDGE_FAILOVER.md +0 -512
  32. package/docs/BRIDGE_HEALTH_MONITORING.md +0 -293
  33. package/docs/CHANNEL_SYNCHRONIZATION.md +0 -209
  34. package/docs/CREATE_MISSING_RELEASES.md +0 -321
  35. package/docs/DOCUMENTATION_MIGRATION_PLAN.md +0 -176
  36. package/docs/FAQ_VERSION_NUMBERS.md +0 -152
  37. package/docs/IMPLEMENTATION_PLAN_MESH_TOPOLOGY.md +0 -1062
  38. package/docs/MESH_TOPOLOGY_GUIDE.md +0 -992
  39. package/docs/MESH_TOPOLOGY_PROGRESS.md +0 -422
  40. package/docs/MQTT_BRIDGE_COMMANDS.md +0 -894
  41. package/docs/MQTT_BRIDGE_IMPLEMENTATION_SUMMARY.md +0 -324
  42. package/docs/MQTT_COMMAND_SCHEMA_PROPOSAL.md +0 -576
  43. package/docs/MQTT_SCHEMA_COMPLIANCE.md +0 -340
  44. package/docs/MQTT_SCHEMA_PROPOSALS.md +0 -446
  45. package/docs/MQTT_SCHEMA_REVIEW.md +0 -690
  46. package/docs/OTA_COMMANDS_REFERENCE.md +0 -554
  47. package/docs/PHASE1_GUIDE.md +0 -349
  48. package/docs/PHASE2_GUIDE.md +0 -543
  49. package/docs/QUICK_REFERENCE_VERSIONING.md +0 -127
  50. package/docs/RELEASE_AGENT_SUMMARY.md +0 -386
  51. package/docs/SCHEMA_VALIDATION_CHECKLIST.md +0 -222
  52. package/docs/VERSION_MANAGEMENT.md +0 -213
  53. package/docs/archive/DOCUSAURUS_DEPLOYMENT.md +0 -166
  54. package/docs/archive/FEATURE_PROPOSALS.md +0 -337
  55. package/docs/archive/LIBRARY_JSON_FIX.md +0 -98
  56. package/docs/archive/LIBRARY_STRUCTURE_FIX.md +0 -215
  57. package/docs/archive/PHASE1_IMPLEMENTATION.md +0 -325
  58. package/docs/archive/PHASE2_IMPLEMENTATION.md +0 -567
  59. package/docs/archive/RELEASE_SUMMARY.md +0 -173
  60. package/docs/archive/SCONS_BUILD_FIX.md +0 -313
  61. package/docs/archive/TRIGGER_RELEASE.md +0 -280
  62. package/docs/archive/VECTOR_INCLUDE_FIX.md +0 -129
  63. package/docs/archive/ota-and-status-enhancements.md +0 -911
  64. package/docs/archive/ota-status-architecture-diagrams.md +0 -658
  65. package/docs/archive/ota-status-quick-reference.md +0 -284
  66. package/docs/design/.gitkeep +0 -1
  67. package/docs/design/STATION_CREDENTIALS_DESIGN.md +0 -182
  68. package/docs/development/ARDUINO_COMPLIANCE_SUMMARY.md +0 -71
  69. package/docs/development/CODE_REFACTORING_RECOMMENDATIONS.md +0 -1011
  70. package/docs/development/DOCKER_TESTING.md +0 -196
  71. package/docs/development/PLATFORMIO_USAGE.md +0 -180
  72. package/docs/development/TESTING_SUMMARY.md +0 -126
  73. package/docs/development/contributing.md +0 -301
  74. package/docs/development/documentation.md +0 -583
  75. package/docs/features/DIAGNOSTICS_API.md +0 -534
  76. package/docs/implementation/BRIDGE_ARCHITECTURE_IMPLEMENTATION.md +0 -340
  77. package/docs/implementation/BRIDGE_HEALTH_MONITORING_IMPLEMENTATION.md +0 -213
  78. package/docs/implementation/BRIDGE_STATUS_FEATURE.md +0 -635
  79. package/docs/implementation/DIAGNOSTICS_API_IMPLEMENTATION.md +0 -232
  80. package/docs/implementation/IMPLEMENTATION_COMPLETE.md +0 -228
  81. package/docs/implementation/IMPLEMENTATION_NTP_TIME_SYNC.md +0 -325
  82. package/docs/implementation/IMPLEMENTATION_SUMMARY.md +0 -316
  83. package/docs/implementation/MESSAGE_QUEUE_IMPLEMENTATION.md +0 -405
  84. package/docs/implementation/MULTI_BRIDGE_IMPLEMENTATION.md +0 -520
  85. package/docs/implementation/NTP_TIME_SYNC_FEATURE.md +0 -392
  86. package/docs/improvements/FUTURE_PROPOSALS.md +0 -1016
  87. package/docs/improvements/IMPLEMENTATION_HISTORY.md +0 -1091
  88. package/docs/improvements/OTA_STATUS_ENHANCEMENTS.md +0 -709
  89. package/docs/improvements/README.md +0 -212
  90. package/docs/internal/CUSTOM_AGENT_ANALYSIS.md +0 -391
  91. package/docs/internal/ISSUE_65_VERIFICATION.md +0 -947
  92. package/docs/internal/ISSUE_66_CLOSURE.md +0 -249
  93. package/docs/internal/ISSUE_66_STATUS.md +0 -316
  94. package/docs/internal/PR_SUMMARY.md +0 -315
  95. package/docs/internal/REVIEW_SUMMARY.md +0 -332
  96. package/docs/multi-bridge-setup.md +0 -1025
  97. package/docs/platformio-publishing.md +0 -255
  98. package/docs/platformio-setup-summary.md +0 -121
  99. package/docs/releases/ANNOUNCEMENT_v1.8.6.md +0 -63
  100. package/docs/releases/ANNOUNCEMENT_v1.8.7.md +0 -113
  101. package/docs/releases/BRIDGE_STATUS_SELF_REGISTRATION_FIX.md +0 -221
  102. package/docs/releases/FEATURE_HISTORY.md +0 -543
  103. package/docs/releases/GITHUB_RELEASE_v1.8.6.md +0 -71
  104. package/docs/releases/GITHUB_RELEASE_v1.8.7.md +0 -90
  105. package/docs/releases/PATCH_v1.7.2.md +0 -262
  106. package/docs/releases/PATCH_v1.7.3.md +0 -262
  107. package/docs/releases/PATCH_v1.7.4.md +0 -219
  108. package/docs/releases/PHASE1_SUMMARY.md +0 -246
  109. package/docs/releases/PHASE2_SUMMARY.md +0 -499
  110. package/docs/releases/PUBLISH_v1.8.0_INSTRUCTIONS.md +0 -163
  111. package/docs/releases/QUICK_START_RELEASES.md +0 -113
  112. package/docs/releases/RELEASE_CHECKLIST_1.8.10.md +0 -331
  113. package/docs/releases/RELEASE_CHECKLIST_1.8.9.md +0 -207
  114. package/docs/releases/RELEASE_CHECKLIST_v1.7.4.md +0 -253
  115. package/docs/releases/RELEASE_CHECKLIST_v1.7.5.md +0 -315
  116. package/docs/releases/RELEASE_CHECKLIST_v1.7.6.md +0 -389
  117. package/docs/releases/RELEASE_CHECKLIST_v1.8.0.md +0 -331
  118. package/docs/releases/RELEASE_CHECKLIST_v1.8.2.md +0 -309
  119. package/docs/releases/RELEASE_NOTES_1.7.0.md +0 -539
  120. package/docs/releases/RELEASE_NOTES_1.8.10.md +0 -239
  121. package/docs/releases/RELEASE_NOTES_1.8.9.md +0 -213
  122. package/docs/releases/RELEASE_NOTES_v1.8.0.md +0 -685
  123. package/docs/releases/RELEASE_NOTES_v1.8.1.md +0 -221
  124. package/docs/releases/RELEASE_NOTES_v1.8.2.md +0 -421
  125. package/docs/releases/RELEASE_NOTES_v1.8.3.md +0 -292
  126. package/docs/releases/RELEASE_NOTES_v1.8.4.md +0 -277
  127. package/docs/releases/RELEASE_NOTES_v1.8.6.md +0 -205
  128. package/docs/releases/RELEASE_NOTES_v1.8.7.md +0 -184
  129. package/docs/releases/RELEASE_PLAN_v1.7.6.md +0 -816
  130. package/docs/releases/RELEASE_SUMMARY_1.8.10.md +0 -193
  131. package/docs/releases/RELEASE_SUMMARY_v1.7.4.md +0 -276
  132. package/docs/releases/RELEASE_SUMMARY_v1.7.5.md +0 -322
  133. package/docs/releases/RELEASE_SUMMARY_v1.7.6.md +0 -436
  134. package/docs/releases/RELEASE_SUMMARY_v1.7.7.md +0 -391
  135. package/docs/releases/RELEASE_SUMMARY_v1.7.8.md +0 -523
  136. package/docs/releases/RELEASE_SUMMARY_v1.7.9.md +0 -542
  137. package/docs/troubleshooting/ARDUINO_IDE_VERSION_FIX_SUMMARY.md +0 -229
  138. package/docs/troubleshooting/ARDUINO_LIBRARY_NAME_FIX.md +0 -197
  139. package/docs/troubleshooting/CRASH_QUICK_REF.md +0 -93
  140. package/docs/troubleshooting/ESP32_C6_COMPATIBILITY.md +0 -157
  141. package/docs/troubleshooting/FREERTOS_ASSERTION_FAILURE.md +0 -288
  142. package/docs/troubleshooting/FREERTOS_FIX_IMPLEMENTATION.md +0 -267
  143. package/docs/troubleshooting/NPM_PUBLISHING_ISSUE_SUMMARY.md +0 -110
  144. package/docs/troubleshooting/PAINLESSMESH_V1.7.4_COMPILATION_ISSUES.md +0 -547
  145. package/docs/troubleshooting/QUICK_FIX_FREERTOS.md +0 -164
  146. package/docs/troubleshooting/SENSOR_NODE_CONNECTION_CRASH.md +0 -264
  147. package/docs/troubleshooting/station-reconnection-issues.md +0 -172
  148. package/docs/v1.7.7_MQTT_IMPROVEMENTS.md +0 -794
  149. package/docs/wiki/API-Reference.md +0 -246
  150. package/docs/wiki/Complete-Documentation.md +0 -123
  151. package/examples/alteriomImproved/alteriom_sensor_package.hpp +0 -224
  152. package/examples/alteriomImproved/improved_sensor_node.ino +0 -248
  153. package/examples/alteriomImproved/platformio.ini +0 -32
  154. package/examples/alteriomMetricsHealth/alteriom_sensor_package.hpp +0 -796
  155. package/examples/alteriomMetricsHealth/metrics_health_node.ino +0 -429
  156. package/examples/alteriomMetricsHealth/platformio.ini +0 -26
  157. package/examples/alteriomPhase1/alteriom_sensor_package.hpp +0 -224
  158. package/examples/alteriomPhase1/phase1_features.ino +0 -242
  159. package/examples/alteriomPhase1/platformio.ini +0 -26
  160. package/examples/alteriomPhase2/alteriom_sensor_package.hpp +0 -224
  161. package/examples/alteriomPhase2/phase2_features.ino +0 -186
  162. package/examples/alteriomPhase2/platformio.ini +0 -26
  163. package/examples/alteriomSensorNode/alteriom_sensor_node.ino +0 -186
  164. package/examples/alteriomSensorNode/alteriom_sensor_package.hpp +0 -1227
  165. package/examples/alteriomSensorNode/platformio.ini +0 -26
  166. package/examples/bridge/alteriom_sensor_package.hpp +0 -1170
  167. package/examples/bridge/bridge_health_monitoring_example.ino +0 -188
  168. package/examples/bridge/enhanced_mqtt_bridge.hpp +0 -610
  169. package/examples/bridge/enhanced_mqtt_bridge_example.ino +0 -226
  170. package/examples/bridge/mesh_event_publisher.hpp +0 -253
  171. package/examples/bridge/mesh_topology_reporter.hpp +0 -303
  172. package/examples/bridge/mqtt_command_bridge.hpp +0 -459
  173. package/examples/bridge/mqtt_status_bridge.hpp +0 -519
  174. package/examples/bridgeAwareSensorNode/alteriom_sensor_package.hpp +0 -1227
  175. package/examples/bridgeAwareSensorNode/bridgeAwareSensorNode.ino +0 -342
  176. package/examples/bridgeAwareSensorNode/platformio.ini +0 -26
  177. package/examples/diagnosticsExample/diagnosticsExample.ino +0 -171
  178. package/examples/diagnosticsExample/platformio.ini +0 -26
  179. package/examples/echoNode/echoNode.ino +0 -33
  180. package/examples/echoNode/platformio.ini +0 -26
  181. package/examples/meshCommandNode/alteriom_sensor_package.hpp +0 -235
  182. package/examples/meshCommandNode/meshCommandNode.ino +0 -265
  183. package/examples/mqttCommandBridge/alteriom_sensor_package.hpp +0 -235
  184. package/examples/mqttCommandBridge/mesh_event_publisher.hpp +0 -253
  185. package/examples/mqttCommandBridge/mesh_topology_reporter.hpp +0 -303
  186. package/examples/mqttCommandBridge/mqttCommandBridge.ino +0 -254
  187. package/examples/mqttCommandBridge/mqtt_command_bridge.hpp +0 -462
  188. package/examples/mqttCommandBridge/platformio.ini +0 -27
  189. package/examples/mqttStatusBridge/mqttStatusBridge.ino +0 -218
  190. package/examples/mqttStatusBridge/mqtt_status_bridge.hpp +0 -522
  191. package/examples/mqttStatusBridge/platformio.ini +0 -27
  192. package/examples/mqttTopologyTest/README.md +0 -467
  193. package/examples/mqttTopologyTest/mqttTopologyTest.ino +0 -754
  194. package/examples/mqttTopologyTest/platformio.ini +0 -27
  195. package/examples/multi_bridge/README.md +0 -346
  196. package/examples/multi_bridge/primary_bridge.ino +0 -96
  197. package/examples/multi_bridge/regular_node.ino +0 -141
  198. package/examples/multi_bridge/secondary_bridge.ino +0 -111
  199. package/examples/ntpTimeSyncBridge/alteriom_sensor_package.hpp +0 -1383
  200. package/examples/ntpTimeSyncBridge/ntpTimeSyncBridge.ino +0 -86
  201. package/examples/ntpTimeSyncNode/alteriom_sensor_package.hpp +0 -1383
  202. package/examples/ntpTimeSyncNode/ntpTimeSyncNode.ino +0 -109
  203. package/examples/queued_alarms/README.md +0 -390
  204. package/examples/queued_alarms/queued_alarms.ino +0 -265
  205. package/examples/routing_demo/README.md +0 -172
  206. package/examples/routing_demo/routing_demo.ino +0 -102
  207. package/examples/rtcIntegration/README.md +0 -294
  208. package/examples/rtcIntegration/rtcIntegration.ino +0 -210
@@ -0,0 +1,1207 @@
1
+ # Shared Gateway Mode API Reference
2
+
3
+ > **Version:** v1.9.0+
4
+ > **Status:** Implemented
5
+ > **Author:** AlteriomPainlessMesh Team
6
+
7
+ This document provides comprehensive API reference for the Shared Gateway Mode feature in painlessMesh.
8
+
9
+ ---
10
+
11
+ ## Table of Contents
12
+
13
+ 1. [Overview](#overview)
14
+ 2. [Quick Start](#quick-start)
15
+ 3. [API Reference](#api-reference)
16
+ 4. [Configuration](#configuration)
17
+ 5. [Package Types](#package-types)
18
+ 6. [Advanced Usage](#advanced-usage)
19
+ 7. [Troubleshooting](#troubleshooting)
20
+ 8. [Performance](#performance)
21
+ 9. [Migration](#migration)
22
+
23
+ ---
24
+
25
+ ## Overview
26
+
27
+ ### What is Shared Gateway Mode?
28
+
29
+ Shared Gateway Mode is a painlessMesh feature that enables **all nodes** in a mesh network to connect to the same WiFi router while maintaining mesh connectivity. Unlike Bridge Mode where only one dedicated node connects to the router, Shared Gateway Mode allows every node to potentially send data directly to the Internet.
30
+
31
+ ### Architecture Diagram
32
+
33
+ ```
34
+ Internet
35
+ │
36
+ Router (shared SSID)
37
+ ┌─────────────┼─────────────┐
38
+ │ │ │
39
+ Node A Node B Node C
40
+ (STA+AP) (STA+AP) (STA+AP)
41
+ [Primary] [Backup] [Backup]
42
+ │ │ │
43
+ └─────────────┼─────────────┘
44
+ Mesh Network
45
+
46
+ Legend:
47
+ - STA: Station connection to router (Internet access)
48
+ - AP: Access point for mesh network
49
+ - [Primary]: Elected primary gateway for relayed messages
50
+ - [Backup]: Backup gateway, can relay if primary fails
51
+ ```
52
+
53
+ ### Key Benefits
54
+
55
+ | Benefit | Description |
56
+ |---------|-------------|
57
+ | **Redundant Internet Access** | All nodes can send data to the Internet, eliminating single points of failure |
58
+ | **Automatic Failover** | If a node's Internet connection fails, data routes through the mesh to find a working gateway |
59
+ | **Simplified Deployment** | No dedicated bridge node required; any node can serve as gateway |
60
+ | **Load Distribution** | Internet traffic can be distributed across multiple nodes |
61
+ | **Resilient Architecture** | Mesh continues to function even if individual router connections fail |
62
+
63
+ ### Use Cases
64
+
65
+ - **Fish Farm Monitoring**: Critical O₂ alarms can reach the cloud through any available path
66
+ - **Industrial Sensor Networks**: Sensor data reliably uploaded even during partial network outages
67
+ - **Smart Building Systems**: HVAC and security data maintains connectivity through redundant paths
68
+ - **Remote Monitoring**: Environmental stations with spotty Internet can relay through nearby nodes
69
+
70
+ ### Comparison: Bridge Mode vs Shared Gateway Mode
71
+
72
+ | Aspect | Bridge Mode | Shared Gateway Mode |
73
+ |--------|-------------|---------------------|
74
+ | Internet Access | Bridge node only | All nodes |
75
+ | Router Connection | Bridge node only | All nodes |
76
+ | WiFi Mode | AP+STA (bridge only) | AP+STA (all nodes) |
77
+ | Failover Time | 60-70 seconds (election) | Near-instant relay |
78
+ | Primary Gateway | Fixed (bridge) | Elected dynamically |
79
+ | Duplicate Prevention | Not required | Built-in |
80
+ | Single Point of Failure | Yes (bridge node) | No |
81
+
82
+ ---
83
+
84
+ ## Quick Start
85
+
86
+ ### Basic Setup Example
87
+
88
+ ```cpp
89
+ #include "painlessMesh.h"
90
+
91
+ // Mesh network configuration
92
+ #define MESH_PREFIX "SharedGatewayMesh"
93
+ #define MESH_PASSWORD "meshPassword123"
94
+ #define MESH_PORT 5555
95
+
96
+ // Router configuration (all nodes connect to same router)
97
+ #define ROUTER_SSID "YourRouterSSID"
98
+ #define ROUTER_PASSWORD "YourRouterPassword"
99
+
100
+ Scheduler userScheduler;
101
+ painlessMesh mesh;
102
+
103
+ void setup() {
104
+ Serial.begin(115200);
105
+
106
+ // Set debug level
107
+ mesh.setDebugMsgTypes(ERROR | STARTUP | CONNECTION);
108
+
109
+ // Initialize as Shared Gateway
110
+ bool success = mesh.initAsSharedGateway(
111
+ MESH_PREFIX, // Mesh network name
112
+ MESH_PASSWORD, // Mesh network password
113
+ ROUTER_SSID, // Router SSID
114
+ ROUTER_PASSWORD, // Router password
115
+ &userScheduler, // Task scheduler
116
+ MESH_PORT // Mesh TCP port
117
+ );
118
+
119
+ if (success) {
120
+ Serial.println("Shared Gateway Mode initialized!");
121
+ Serial.printf("Node ID: %u\n", mesh.getNodeId());
122
+ Serial.printf("Router IP: %s\n", WiFi.localIP().toString().c_str());
123
+ } else {
124
+ Serial.println("Failed to initialize - falling back to mesh only");
125
+ mesh.init(MESH_PREFIX, MESH_PASSWORD, &userScheduler, MESH_PORT);
126
+ }
127
+
128
+ // Register callbacks
129
+ mesh.onReceive(&receivedCallback);
130
+ mesh.onNewConnection(&newConnectionCallback);
131
+ }
132
+
133
+ void loop() {
134
+ mesh.update();
135
+ }
136
+
137
+ void receivedCallback(uint32_t from, String& msg) {
138
+ Serial.printf("Received from %u: %s\n", from, msg.c_str());
139
+ }
140
+
141
+ void newConnectionCallback(uint32_t nodeId) {
142
+ Serial.printf("New connection: %u\n", nodeId);
143
+ }
144
+ ```
145
+
146
+ ---
147
+
148
+ ## API Reference
149
+
150
+ ### Initialization Methods
151
+
152
+ #### `initAsSharedGateway()`
153
+
154
+ Initialize the mesh in Shared Gateway Mode, connecting to both the mesh network and an external WiFi router.
155
+
156
+ ```cpp
157
+ bool initAsSharedGateway(
158
+ TSTRING meshPrefix,
159
+ TSTRING meshPassword,
160
+ TSTRING routerSSID,
161
+ TSTRING routerPassword,
162
+ Scheduler* userScheduler,
163
+ uint16_t port = 5555,
164
+ painlessmesh::gateway::SharedGatewayConfig config = SharedGatewayConfig()
165
+ );
166
+ ```
167
+
168
+ **Parameters:**
169
+
170
+ | Parameter | Type | Description |
171
+ |-----------|------|-------------|
172
+ | `meshPrefix` | TSTRING | Mesh network name (SSID prefix) |
173
+ | `meshPassword` | TSTRING | Mesh network password |
174
+ | `routerSSID` | TSTRING | External WiFi router SSID |
175
+ | `routerPassword` | TSTRING | External WiFi router password |
176
+ | `userScheduler` | Scheduler* | TaskScheduler instance for mesh operations |
177
+ | `port` | uint16_t | TCP port for mesh communication (default: 5555) |
178
+ | `config` | SharedGatewayConfig | Optional advanced configuration |
179
+
180
+ **Returns:** `true` if initialization succeeded, `false` otherwise
181
+
182
+ **Behavior:**
183
+
184
+ 1. Connects to the router to detect its WiFi channel
185
+ 2. Initializes the mesh network on the same channel
186
+ 3. Re-establishes the router connection using `stationManual()`
187
+ 4. Sets up automatic router reconnection monitoring
188
+
189
+ **Example:**
190
+
191
+ ```cpp
192
+ // Basic initialization
193
+ bool success = mesh.initAsSharedGateway(
194
+ "MyMesh", "meshPass",
195
+ "HomeRouter", "routerPass",
196
+ &userScheduler
197
+ );
198
+
199
+ // With custom configuration
200
+ painlessmesh::gateway::SharedGatewayConfig config;
201
+ config.internetCheckInterval = 15000; // Check every 15 seconds
202
+ config.gatewayHeartbeatInterval = 10000;
203
+
204
+ bool success = mesh.initAsSharedGateway(
205
+ "MyMesh", "meshPass",
206
+ "HomeRouter", "routerPass",
207
+ &userScheduler, 5555, config
208
+ );
209
+ ```
210
+
211
+ ---
212
+
213
+ ### Internet Connectivity Methods
214
+
215
+ #### `sendToInternet()`
216
+
217
+ Send data to an Internet destination. Automatically handles failover through the mesh if local Internet is unavailable.
218
+
219
+ ```cpp
220
+ uint32_t sendToInternet(
221
+ TSTRING destination,
222
+ TSTRING payload,
223
+ std::function<void(bool success, uint16_t httpStatus, TSTRING error)> callback,
224
+ uint8_t priority = static_cast<uint8_t>(GatewayPriority::PRIORITY_NORMAL)
225
+ );
226
+ ```
227
+
228
+ **Parameters:**
229
+
230
+ | Parameter | Type | Description |
231
+ |-----------|------|-------------|
232
+ | `destination` | TSTRING | URL or endpoint (e.g., "https://api.example.com/data") |
233
+ | `payload` | TSTRING | Data to send (typically JSON) |
234
+ | `callback` | function | Callback for delivery result |
235
+ | `priority` | uint8_t | Message priority (0=CRITICAL to 3=LOW) |
236
+
237
+ **Returns:** Unique message ID for tracking
238
+
239
+ **Priority Levels:**
240
+
241
+ | Value | Enum | Description |
242
+ |-------|------|-------------|
243
+ | 0 | `PRIORITY_CRITICAL` | Immediate processing, alarms |
244
+ | 1 | `PRIORITY_HIGH` | High priority data |
245
+ | 2 | `PRIORITY_NORMAL` | Standard data (default) |
246
+ | 3 | `PRIORITY_LOW` | Background/bulk data |
247
+
248
+ **Example:**
249
+
250
+ ```cpp
251
+ // Send sensor data to cloud
252
+ String sensorData = "{\"temperature\": 25.5, \"humidity\": 60}";
253
+
254
+ uint32_t msgId = mesh.sendToInternet(
255
+ "https://api.example.com/sensors",
256
+ sensorData,
257
+ [](bool success, uint16_t httpStatus, String error) {
258
+ if (success) {
259
+ Serial.printf("Delivered! HTTP %d\n", httpStatus);
260
+ } else {
261
+ Serial.printf("Failed: %s\n", error.c_str());
262
+ }
263
+ },
264
+ static_cast<uint8_t>(painlessmesh::gateway::GatewayPriority::PRIORITY_HIGH)
265
+ );
266
+
267
+ Serial.printf("Message queued with ID: %u\n", msgId);
268
+ ```
269
+
270
+ ---
271
+
272
+ #### `hasLocalInternet()`
273
+
274
+ Check if this node has direct Internet connectivity.
275
+
276
+ ```cpp
277
+ bool hasLocalInternet();
278
+ ```
279
+
280
+ **Returns:** `true` if this node can reach the Internet directly
281
+
282
+ **Example:**
283
+
284
+ ```cpp
285
+ if (mesh.hasLocalInternet()) {
286
+ Serial.println("Local Internet available");
287
+ // Send directly
288
+ } else {
289
+ Serial.println("No local Internet - will route through mesh");
290
+ }
291
+ ```
292
+
293
+ ---
294
+
295
+ #### `getInternetStatus()`
296
+
297
+ Get detailed information about Internet connectivity status.
298
+
299
+ ```cpp
300
+ painlessmesh::gateway::InternetStatus getInternetStatus();
301
+ ```
302
+
303
+ **Returns:** `InternetStatus` structure with detailed connectivity information
304
+
305
+ **InternetStatus Structure:**
306
+
307
+ | Field | Type | Description |
308
+ |-------|------|-------------|
309
+ | `available` | bool | Whether Internet is currently available |
310
+ | `lastCheckTime` | uint32_t | Timestamp of last check (millis) |
311
+ | `lastSuccessTime` | uint32_t | Timestamp of last successful check |
312
+ | `checkCount` | uint32_t | Total number of checks performed |
313
+ | `successCount` | uint32_t | Number of successful checks |
314
+ | `failureCount` | uint32_t | Number of failed checks |
315
+ | `lastLatencyMs` | uint32_t | Latency of last successful check |
316
+ | `lastError` | TSTRING | Error message from last failed check |
317
+ | `checkHost` | TSTRING | Host used for connectivity check |
318
+ | `checkPort` | uint16_t | Port used for connectivity check |
319
+
320
+ **InternetStatus Methods:**
321
+
322
+ ```cpp
323
+ // Get uptime percentage (0-100)
324
+ uint8_t getUptimePercent() const;
325
+
326
+ // Get time since last successful check
327
+ uint32_t getTimeSinceLastSuccess() const;
328
+
329
+ // Check if status is stale (no recent check)
330
+ bool isStale(uint32_t maxAgeMs = 60000) const;
331
+ ```
332
+
333
+ **Example:**
334
+
335
+ ```cpp
336
+ auto status = mesh.getInternetStatus();
337
+
338
+ Serial.printf("Internet: %s\n", status.available ? "Available" : "Unavailable");
339
+ Serial.printf("Uptime: %d%%\n", status.getUptimePercent());
340
+ Serial.printf("Last latency: %u ms\n", status.lastLatencyMs);
341
+ Serial.printf("Checks: %u success / %u total\n",
342
+ status.successCount, status.checkCount);
343
+
344
+ if (!status.lastError.empty()) {
345
+ Serial.printf("Last error: %s\n", status.lastError.c_str());
346
+ }
347
+ ```
348
+
349
+ ---
350
+
351
+ ### Gateway Status Methods
352
+
353
+ #### `isPrimaryGateway()`
354
+
355
+ Check if this node is the elected primary gateway.
356
+
357
+ ```cpp
358
+ bool isPrimaryGateway();
359
+ ```
360
+
361
+ **Returns:** `true` if this node is the primary gateway
362
+
363
+ **Example:**
364
+
365
+ ```cpp
366
+ if (mesh.isPrimaryGateway()) {
367
+ Serial.println("This node is the PRIMARY gateway");
368
+ // Handle relayed messages from other nodes
369
+ } else {
370
+ Serial.println("This node is a backup gateway");
371
+ }
372
+ ```
373
+
374
+ ---
375
+
376
+ #### `getPrimaryGateway()`
377
+
378
+ Get the node ID of the current primary gateway.
379
+
380
+ ```cpp
381
+ uint32_t getPrimaryGateway();
382
+ ```
383
+
384
+ **Returns:** Node ID of the primary gateway, or 0 if none elected
385
+
386
+ **Example:**
387
+
388
+ ```cpp
389
+ uint32_t primaryId = mesh.getPrimaryGateway();
390
+
391
+ if (primaryId != 0) {
392
+ Serial.printf("Primary gateway: %u\n", primaryId);
393
+
394
+ if (primaryId == mesh.getNodeId()) {
395
+ Serial.println("(That's me!)");
396
+ }
397
+ } else {
398
+ Serial.println("No primary gateway elected yet");
399
+ }
400
+ ```
401
+
402
+ ---
403
+
404
+ #### `getGateways()`
405
+
406
+ Get a list of all nodes with Internet connectivity.
407
+
408
+ ```cpp
409
+ std::vector<uint32_t> getGateways();
410
+ ```
411
+
412
+ **Returns:** Vector of node IDs that have Internet access
413
+
414
+ **Example:**
415
+
416
+ ```cpp
417
+ auto gateways = mesh.getGateways();
418
+
419
+ Serial.printf("Available gateways: %d\n", gateways.size());
420
+ for (auto nodeId : gateways) {
421
+ Serial.printf(" - Node %u", nodeId);
422
+ if (nodeId == mesh.getPrimaryGateway()) {
423
+ Serial.print(" [PRIMARY]");
424
+ }
425
+ Serial.println();
426
+ }
427
+ ```
428
+
429
+ ---
430
+
431
+ #### `isSharedGatewayMode()`
432
+
433
+ Check if the mesh is operating in Shared Gateway Mode.
434
+
435
+ ```cpp
436
+ bool isSharedGatewayMode();
437
+ ```
438
+
439
+ **Returns:** `true` if initialized with `initAsSharedGateway()`
440
+
441
+ **Example:**
442
+
443
+ ```cpp
444
+ if (mesh.isSharedGatewayMode()) {
445
+ Serial.println("Running in Shared Gateway Mode");
446
+ Serial.printf("Router connected: %s\n",
447
+ WiFi.status() == WL_CONNECTED ? "Yes" : "No");
448
+ } else {
449
+ Serial.println("Running in standard mesh mode");
450
+ }
451
+ ```
452
+
453
+ ---
454
+
455
+ ### Callbacks
456
+
457
+ #### `onLocalInternetChanged()`
458
+
459
+ Register a callback for Internet connectivity changes.
460
+
461
+ ```cpp
462
+ void onLocalInternetChanged(std::function<void(bool available)> callback);
463
+ ```
464
+
465
+ **Parameters:**
466
+
467
+ | Parameter | Type | Description |
468
+ |-----------|------|-------------|
469
+ | `callback` | function | Function called when Internet status changes |
470
+
471
+ **Example:**
472
+
473
+ ```cpp
474
+ mesh.onLocalInternetChanged([](bool available) {
475
+ if (available) {
476
+ Serial.println("✓ Internet connection restored");
477
+ // Resume direct sending
478
+ } else {
479
+ Serial.println("✗ Internet connection lost");
480
+ // Data will be routed through mesh
481
+ }
482
+ });
483
+ ```
484
+
485
+ ---
486
+
487
+ #### `onGatewayChanged()`
488
+
489
+ Register a callback for primary gateway changes.
490
+
491
+ ```cpp
492
+ void onGatewayChanged(std::function<void(uint32_t newGatewayId)> callback);
493
+ ```
494
+
495
+ **Parameters:**
496
+
497
+ | Parameter | Type | Description |
498
+ |-----------|------|-------------|
499
+ | `callback` | function | Function called when primary gateway changes |
500
+
501
+ **Example:**
502
+
503
+ ```cpp
504
+ mesh.onGatewayChanged([](uint32_t newGatewayId) {
505
+ Serial.printf("Primary gateway changed to: %u\n", newGatewayId);
506
+
507
+ if (newGatewayId == mesh.getNodeId()) {
508
+ Serial.println("This node is now the primary gateway!");
509
+ // Start handling relayed messages
510
+ }
511
+ });
512
+ ```
513
+
514
+ ---
515
+
516
+ ## Configuration
517
+
518
+ ### SharedGatewayConfig Structure
519
+
520
+ The `SharedGatewayConfig` structure provides fine-grained control over Shared Gateway Mode behavior.
521
+
522
+ ```cpp
523
+ #include "painlessmesh/gateway.hpp"
524
+
525
+ painlessmesh::gateway::SharedGatewayConfig config;
526
+ ```
527
+
528
+ ### Configuration Options
529
+
530
+ | Option | Type | Default | Description |
531
+ |--------|------|---------|-------------|
532
+ | `enabled` | bool | false | Enable shared gateway functionality |
533
+ | `routerSSID` | TSTRING | "" | Router SSID (set automatically by initAsSharedGateway) |
534
+ | `routerPassword` | TSTRING | "" | Router password |
535
+ | `internetCheckInterval` | uint32_t | 30000 | Interval between Internet checks (ms) |
536
+ | `internetCheckHost` | TSTRING | "8.8.8.8" | Host to ping for connectivity check |
537
+ | `internetCheckPort` | uint16_t | 53 | Port for connectivity check |
538
+ | `internetCheckTimeout` | uint32_t | 5000 | Timeout for connectivity check (ms) |
539
+ | `messageRetryCount` | uint8_t | 3 | Retries before failover |
540
+ | `retryInterval` | uint32_t | 1000 | Interval between retries (ms) |
541
+ | `duplicateTrackingTimeout` | uint32_t | 60000 | How long to track message IDs (ms) |
542
+ | `maxTrackedMessages` | uint16_t | 500 | Maximum messages to track |
543
+ | `gatewayHeartbeatInterval` | uint32_t | 15000 | Primary gateway heartbeat interval (ms) |
544
+ | `gatewayFailureTimeout` | uint32_t | 45000 | Time to consider gateway failed (ms) |
545
+ | `participateInElection` | bool | true | Whether node can become primary |
546
+ | `relayedMessagePriority` | uint8_t | 0 | Priority for relayed messages (0=highest) |
547
+ | `maintainPermanentConnection` | bool | true | Keep router connection active |
548
+
549
+ ### Configuration Examples
550
+
551
+ #### Low-Latency Configuration (Fast Failover)
552
+
553
+ ```cpp
554
+ painlessmesh::gateway::SharedGatewayConfig config;
555
+ config.internetCheckInterval = 10000; // Check every 10 seconds
556
+ config.internetCheckTimeout = 2000; // 2 second timeout
557
+ config.gatewayHeartbeatInterval = 5000; // Heartbeat every 5 seconds
558
+ config.gatewayFailureTimeout = 15000; // Fail after 15 seconds
559
+
560
+ mesh.initAsSharedGateway("Mesh", "pass", "Router", "pass",
561
+ &userScheduler, 5555, config);
562
+ ```
563
+
564
+ #### Memory-Constrained Configuration (ESP8266)
565
+
566
+ ```cpp
567
+ painlessmesh::gateway::SharedGatewayConfig config;
568
+ config.maxTrackedMessages = 100; // Limit tracked messages
569
+ config.duplicateTrackingTimeout = 30000; // Shorter tracking window
570
+ config.internetCheckInterval = 60000; // Less frequent checks
571
+
572
+ mesh.initAsSharedGateway("Mesh", "pass", "Router", "pass",
573
+ &userScheduler, 5555, config);
574
+ ```
575
+
576
+ #### Non-Participating Node (Backup Only)
577
+
578
+ ```cpp
579
+ painlessmesh::gateway::SharedGatewayConfig config;
580
+ config.participateInElection = false; // Never become primary
581
+
582
+ mesh.initAsSharedGateway("Mesh", "pass", "Router", "pass",
583
+ &userScheduler, 5555, config);
584
+ ```
585
+
586
+ ### Configuration Validation
587
+
588
+ Validate configuration before use:
589
+
590
+ ```cpp
591
+ painlessmesh::gateway::SharedGatewayConfig config;
592
+ config.enabled = true;
593
+ config.routerSSID = "MyRouter";
594
+ config.internetCheckInterval = 500; // Too low!
595
+
596
+ auto result = config.validate();
597
+ if (!result.valid) {
598
+ Serial.printf("Config error: %s\n", result.errorMessage.c_str());
599
+ // Output: "internetCheckInterval must be at least 1000ms"
600
+ }
601
+ ```
602
+
603
+ ---
604
+
605
+ ## Package Types
606
+
607
+ Shared Gateway Mode uses three internal package types for coordination. These are automatically handled by the mesh but are documented here for advanced users implementing custom handlers.
608
+
609
+ ### GatewayDataPackage (Type ID: 620)
610
+
611
+ Used to route Internet-bound data through the mesh when local Internet is unavailable.
612
+
613
+ ```cpp
614
+ class GatewayDataPackage : public plugin::SinglePackage {
615
+ public:
616
+ uint32_t messageId; // Unique message identifier
617
+ uint32_t originNode; // Node that created the message
618
+ uint32_t timestamp; // Creation timestamp (mesh time)
619
+ uint8_t priority; // 0=CRITICAL, 1=HIGH, 2=NORMAL, 3=LOW
620
+ TSTRING destination; // URL/endpoint
621
+ TSTRING payload; // Application data
622
+ TSTRING contentType; // MIME type (default: "application/json")
623
+ uint8_t retryCount; // Number of relay attempts
624
+ bool requiresAck; // Whether to send acknowledgment
625
+ };
626
+ ```
627
+
628
+ **Message ID Generation:**
629
+
630
+ ```cpp
631
+ // Generate unique message ID
632
+ uint32_t msgId = GatewayDataPackage::generateMessageId(mesh.getNodeId());
633
+ ```
634
+
635
+ **Manual Package Creation:**
636
+
637
+ ```cpp
638
+ painlessmesh::gateway::GatewayDataPackage pkg;
639
+ pkg.messageId = GatewayDataPackage::generateMessageId(mesh.getNodeId());
640
+ pkg.originNode = mesh.getNodeId();
641
+ pkg.timestamp = mesh.getNodeTime();
642
+ pkg.priority = static_cast<uint8_t>(GatewayPriority::PRIORITY_HIGH);
643
+ pkg.destination = "https://api.example.com/data";
644
+ pkg.payload = "{\"sensor\": 42}";
645
+ pkg.contentType = "application/json";
646
+ pkg.requiresAck = true;
647
+ pkg.dest = primaryGatewayId;
648
+ pkg.from = mesh.getNodeId();
649
+
650
+ mesh.sendPackage(&pkg);
651
+ ```
652
+
653
+ ---
654
+
655
+ ### GatewayAckPackage (Type ID: 621)
656
+
657
+ Sent by the gateway back to the origin node to confirm delivery status.
658
+
659
+ ```cpp
660
+ class GatewayAckPackage : public plugin::SinglePackage {
661
+ public:
662
+ uint32_t messageId; // Message ID being acknowledged
663
+ uint32_t originNode; // Original sender node
664
+ bool success; // Whether delivery succeeded
665
+ uint16_t httpStatus; // HTTP status code (0 if N/A)
666
+ TSTRING error; // Error message if failed
667
+ uint32_t timestamp; // Acknowledgment timestamp
668
+ };
669
+ ```
670
+
671
+ **Handling Acknowledgments:**
672
+
673
+ ```cpp
674
+ mesh.onPackage(protocol::GATEWAY_ACK, [](protocol::Variant& variant) {
675
+ auto ack = variant.to<painlessmesh::gateway::GatewayAckPackage>();
676
+
677
+ Serial.printf("Ack for message %u: %s\n",
678
+ ack.messageId,
679
+ ack.success ? "Success" : "Failed");
680
+
681
+ if (!ack.success) {
682
+ Serial.printf("Error: %s\n", ack.error.c_str());
683
+ } else {
684
+ Serial.printf("HTTP Status: %d\n", ack.httpStatus);
685
+ }
686
+
687
+ return false; // Don't stop propagation
688
+ });
689
+ ```
690
+
691
+ ---
692
+
693
+ ### GatewayHeartbeatPackage (Type ID: 622)
694
+
695
+ Broadcast periodically by the primary gateway to inform all nodes of its status.
696
+
697
+ ```cpp
698
+ class GatewayHeartbeatPackage : public plugin::BroadcastPackage {
699
+ public:
700
+ bool isPrimary; // Is this the primary gateway?
701
+ bool hasInternet; // Internet currently available?
702
+ int8_t routerRSSI; // Router signal strength (dBm)
703
+ uint32_t uptime; // Gateway uptime (seconds)
704
+ uint32_t timestamp; // Heartbeat timestamp
705
+ };
706
+ ```
707
+
708
+ **Helper Methods:**
709
+
710
+ ```cpp
711
+ // Check if gateway is healthy
712
+ bool isHealthy() const; // Returns isPrimary && hasInternet
713
+
714
+ // Check if signal strength is acceptable (> -70 dBm)
715
+ bool hasAcceptableSignal() const;
716
+ ```
717
+
718
+ **Monitoring Heartbeats:**
719
+
720
+ ```cpp
721
+ mesh.onPackage(protocol::GATEWAY_HEARTBEAT, [](protocol::Variant& variant) {
722
+ auto heartbeat = variant.to<painlessmesh::gateway::GatewayHeartbeatPackage>();
723
+
724
+ Serial.printf("Gateway %u heartbeat:\n", heartbeat.from);
725
+ Serial.printf(" Primary: %s\n", heartbeat.isPrimary ? "Yes" : "No");
726
+ Serial.printf(" Internet: %s\n", heartbeat.hasInternet ? "Yes" : "No");
727
+ Serial.printf(" RSSI: %d dBm\n", heartbeat.routerRSSI);
728
+ Serial.printf(" Uptime: %u seconds\n", heartbeat.uptime);
729
+ Serial.printf(" Healthy: %s\n", heartbeat.isHealthy() ? "Yes" : "No");
730
+
731
+ return false;
732
+ });
733
+ ```
734
+
735
+ ---
736
+
737
+ ## Advanced Usage
738
+
739
+ ### Failover Scenarios
740
+
741
+ #### Scenario 1: Local Internet Failure
742
+
743
+ ```
744
+ ┌────────┐ ┌─────────┐ ┌────────────┐
745
+ │ Node A │ │ Node B │ │ Internet │
746
+ │(Sender)│ │(Gateway)│ │ Service │
747
+ └───┬────┘ └────┬────┘ └─────┬──────┘
748
+ │ │ │
749
+ │ 1. sendToInternet() - local fails │ │
750
+ │ │ │
751
+ │ 2. GatewayDataPackage (Type 620) │ │
752
+ │────────────────────────────────────→│ │
753
+ │ │ 3. HTTP POST │
754
+ │ │───────────────────→│
755
+ │ │ │
756
+ │ │ 4. HTTP 200 OK │
757
+ │ │←───────────────────│
758
+ │ │ │
759
+ │ 5. GatewayAckPackage (Type 621) │ │
760
+ │←────────────────────────────────────│ │
761
+ │ │ │
762
+ │ 6. callback(true, 200, "") │ │
763
+ ```
764
+
765
+ #### Scenario 2: Primary Gateway Failure
766
+
767
+ ```
768
+ Timeline:
769
+ ─────────────────────────────────────────────────────────────────────
770
+ 0s Primary Gateway (Node A) broadcasting heartbeats
771
+ └─→ All nodes track Node A as primary
772
+
773
+ 15s Node A sends heartbeat (normal)
774
+
775
+ 30s Node A crashes! No more heartbeats...
776
+
777
+ 45s Nodes detect missing heartbeat (gatewayFailureTimeout)
778
+ └─→ Election triggered
779
+
780
+ 50s Node B wins election (highest RSSI)
781
+ └─→ Node B broadcasts as new primary
782
+
783
+ 55s All nodes acknowledge Node B as primary
784
+ └─→ Normal operation resumes
785
+ ```
786
+
787
+ ### Election Protocol
788
+
789
+ The gateway election uses a deterministic algorithm to ensure consistent winner selection across all nodes:
790
+
791
+ 1. **Eligibility**: Only nodes with Internet connectivity can participate
792
+ 2. **Selection Criteria**:
793
+ - **Highest RSSI wins** (better router signal)
794
+ - **If RSSI tie, highest Node ID wins** (deterministic tiebreaker)
795
+ 3. **Split-Brain Prevention**: If multiple nodes claim primary, nodes defer to the one with higher priority
796
+
797
+ **Election State Machine:**
798
+
799
+ ```
800
+ ┌─────────────────────────────────────┐
801
+ │ IDLE │
802
+ │ (monitoring primary heartbeats) │
803
+ └─────────────────┬───────────────────┘
804
+ │
805
+ Primary timeout detected
806
+ │
807
+ ▼
808
+ ┌─────────────────────────────────────┐
809
+ │ ELECTION_RUNNING │
810
+ │ (collecting candidates, 5 seconds) │
811
+ └─────────────────┬───────────────────┘
812
+ │
813
+ Election duration elapsed
814
+ │
815
+ ▼
816
+ ┌─────────────────────────────────────┐
817
+ │ COOLDOWN │
818
+ │ (preventing rapid re-elections) │
819
+ └─────────────────┬───────────────────┘
820
+ │
821
+ Cooldown period elapsed
822
+ │
823
+ ▼
824
+ [IDLE]
825
+ ```
826
+
827
+ ### Custom Message Handling
828
+
829
+ Register handlers for gateway packages:
830
+
831
+ ```cpp
832
+ // Handle relayed data (as gateway)
833
+ mesh.onPackage(protocol::GATEWAY_DATA, [](protocol::Variant& variant) {
834
+ auto pkg = variant.to<painlessmesh::gateway::GatewayDataPackage>();
835
+
836
+ Serial.printf("Relayed request from %u to %s\n",
837
+ pkg.originNode, pkg.destination.c_str());
838
+
839
+ // Custom handling (e.g., add authentication)
840
+ // The default handler will forward to Internet
841
+
842
+ return false; // Let default handler process
843
+ });
844
+
845
+ // Monitor gateway changes
846
+ mesh.onPackage(protocol::GATEWAY_HEARTBEAT, [](protocol::Variant& variant) {
847
+ auto hb = variant.to<painlessmesh::gateway::GatewayHeartbeatPackage>();
848
+
849
+ if (hb.isPrimary && !hb.hasInternet) {
850
+ Serial.println("Warning: Primary gateway lost Internet!");
851
+ }
852
+
853
+ return false;
854
+ });
855
+ ```
856
+
857
+ ### Message Tracking and Deduplication
858
+
859
+ The `GatewayMessageHandler` class prevents duplicate message processing:
860
+
861
+ ```cpp
862
+ painlessmesh::GatewayMessageHandler handler;
863
+
864
+ // Configure from SharedGatewayConfig
865
+ handler.configure(config);
866
+
867
+ // Handle incoming message
868
+ bool shouldProcess = handler.handleIncomingMessage(pkg);
869
+ if (!shouldProcess) {
870
+ Serial.println("Duplicate message dropped");
871
+ return;
872
+ }
873
+
874
+ // Check if we should send acknowledgment
875
+ if (pkg.requiresAck && handler.shouldSendAcknowledgment(pkg.messageId, pkg.originNode)) {
876
+ sendAck(pkg);
877
+ handler.markAcknowledgmentSent(pkg.messageId, pkg.originNode);
878
+ }
879
+
880
+ // Get metrics
881
+ auto metrics = handler.getMetrics();
882
+ Serial.printf("Processed: %u, Duplicates: %u (%d%%)\n",
883
+ metrics.messagesProcessed,
884
+ metrics.duplicatesDetected,
885
+ metrics.getDuplicateRate());
886
+ ```
887
+
888
+ ---
889
+
890
+ ## Troubleshooting
891
+
892
+ ### Common Issues
893
+
894
+ #### Issue: Node fails to connect to router
895
+
896
+ **Symptoms:**
897
+ - `initAsSharedGateway()` returns false
898
+ - `WiFi.status()` != `WL_CONNECTED`
899
+
900
+ **Causes:**
901
+ - Incorrect router credentials
902
+ - Router out of range
903
+ - Channel conflict
904
+
905
+ **Solutions:**
906
+
907
+ ```cpp
908
+ bool success = mesh.initAsSharedGateway(...);
909
+
910
+ if (!success) {
911
+ Serial.println("Shared Gateway init failed!");
912
+ Serial.printf("WiFi Status: %d\n", WiFi.status());
913
+
914
+ // Check WiFi status codes:
915
+ // WL_IDLE_STATUS = 0
916
+ // WL_NO_SSID_AVAIL = 1
917
+ // WL_SCAN_COMPLETED = 2
918
+ // WL_CONNECTED = 3
919
+ // WL_CONNECT_FAILED = 4
920
+ // WL_CONNECTION_LOST = 5
921
+ // WL_DISCONNECTED = 6
922
+
923
+ // Fallback to mesh-only mode
924
+ mesh.init(MESH_PREFIX, MESH_PASSWORD, &userScheduler, MESH_PORT);
925
+ }
926
+ ```
927
+
928
+ ---
929
+
930
+ #### Issue: Internet health check always fails
931
+
932
+ **Symptoms:**
933
+ - `hasLocalInternet()` returns false despite router connection
934
+ - `getInternetStatus().lastError` shows "Connection refused"
935
+
936
+ **Causes:**
937
+ - Firewall blocking outbound connections
938
+ - DNS not working
939
+ - Check host unreachable
940
+
941
+ **Solutions:**
942
+
943
+ ```cpp
944
+ // Try alternate check hosts
945
+ painlessmesh::gateway::SharedGatewayConfig config;
946
+ config.internetCheckHost = "1.1.1.1"; // Cloudflare DNS
947
+ config.internetCheckPort = 53;
948
+
949
+ // Or use HTTP endpoint
950
+ config.internetCheckHost = "www.google.com";
951
+ config.internetCheckPort = 80;
952
+
953
+ // Increase timeout
954
+ config.internetCheckTimeout = 10000;
955
+ ```
956
+
957
+ ---
958
+
959
+ #### Issue: Frequent gateway elections
960
+
961
+ **Symptoms:**
962
+ - `onGatewayChanged()` fires frequently
963
+ - Logs show repeated election cycles
964
+
965
+ **Causes:**
966
+ - Heartbeat interval too short
967
+ - Network congestion
968
+ - Unstable Internet connection
969
+
970
+ **Solutions:**
971
+
972
+ ```cpp
973
+ // Increase heartbeat interval and failure timeout
974
+ painlessmesh::gateway::SharedGatewayConfig config;
975
+ config.gatewayHeartbeatInterval = 30000; // 30 seconds
976
+ config.gatewayFailureTimeout = 90000; // 90 seconds
977
+
978
+ // Or disable election participation for unstable nodes
979
+ config.participateInElection = false;
980
+ ```
981
+
982
+ ---
983
+
984
+ #### Issue: Duplicate messages delivered
985
+
986
+ **Symptoms:**
987
+ - Same data appears multiple times
988
+ - `GatewayMetrics.duplicatesDetected` is 0 but duplicates exist
989
+
990
+ **Causes:**
991
+ - `duplicateTrackingTimeout` too short
992
+ - `maxTrackedMessages` limit reached
993
+ - Messages not using `messageId`
994
+
995
+ **Solutions:**
996
+
997
+ ```cpp
998
+ // Increase tracking capacity
999
+ painlessmesh::gateway::SharedGatewayConfig config;
1000
+ config.duplicateTrackingTimeout = 120000; // 2 minutes
1001
+ config.maxTrackedMessages = 1000;
1002
+
1003
+ // Ensure messages have unique IDs
1004
+ pkg.messageId = GatewayDataPackage::generateMessageId(mesh.getNodeId());
1005
+ ```
1006
+
1007
+ ---
1008
+
1009
+ #### Issue: High memory usage on ESP8266
1010
+
1011
+ **Symptoms:**
1012
+ - `ESP.getFreeHeap()` drops significantly
1013
+ - Crashes or reboots
1014
+
1015
+ **Solutions:**
1016
+
1017
+ ```cpp
1018
+ // Memory-optimized configuration
1019
+ painlessmesh::gateway::SharedGatewayConfig config;
1020
+ config.maxTrackedMessages = 50; // Minimum viable
1021
+ config.duplicateTrackingTimeout = 20000; // Short window
1022
+ config.internetCheckInterval = 60000; // Less frequent
1023
+
1024
+ // Monitor memory
1025
+ Serial.printf("Free heap: %u bytes\n", ESP.getFreeHeap());
1026
+ Serial.printf("Tracked messages: %u\n", handler.getTrackedMessageCount());
1027
+
1028
+ // Manual cleanup
1029
+ handler.cleanup(); // Remove expired entries
1030
+ ```
1031
+
1032
+ ---
1033
+
1034
+ ### Debug Logging
1035
+
1036
+ Enable detailed logging for troubleshooting:
1037
+
1038
+ ```cpp
1039
+ // Enable all debug types
1040
+ mesh.setDebugMsgTypes(ERROR | STARTUP | MESH_STATUS | CONNECTION |
1041
+ SYNC | COMMUNICATION | GENERAL | MSG_TYPES | REMOTE);
1042
+
1043
+ // Gateway-specific logging happens automatically through Log() calls
1044
+ // Look for:
1045
+ // - "GatewayElectionManager:" - Election events
1046
+ // - "GatewayMessageHandler:" - Message processing
1047
+ // - "InternetHealthChecker:" - Connectivity checks
1048
+ ```
1049
+
1050
+ ---
1051
+
1052
+ ## Performance
1053
+
1054
+ ### Memory Footprint
1055
+
1056
+ #### SharedGatewayConfig
1057
+
1058
+ | Platform | Minimum | With Typical Content |
1059
+ |----------|---------|---------------------|
1060
+ | ESP8266 | ~80 bytes | ~150-250 bytes |
1061
+ | ESP32 | ~80 bytes | ~150-250 bytes |
1062
+ | PC/Test | ~140 bytes | ~200-300 bytes |
1063
+
1064
+ #### GatewayDataPackage
1065
+
1066
+ | Platform | Base | With 1KB Payload |
1067
+ |----------|------|------------------|
1068
+ | ESP8266 | ~74 bytes | ~1100 bytes |
1069
+ | ESP32 | ~74 bytes | ~1100 bytes |
1070
+
1071
+ #### MessageTracker (per message)
1072
+
1073
+ | Platform | Per Entry | 500 Messages |
1074
+ |----------|-----------|--------------|
1075
+ | ESP8266 | ~40 bytes | ~20 KB |
1076
+ | ESP32 | ~40 bytes | ~20 KB |
1077
+
1078
+ ### Recommendations by Platform
1079
+
1080
+ #### ESP8266 (~80KB RAM)
1081
+
1082
+ ```cpp
1083
+ // Conservative settings
1084
+ config.maxTrackedMessages = 100; // ~4KB for tracker
1085
+ config.duplicateTrackingTimeout = 30000; // 30 second window
1086
+ config.internetCheckInterval = 60000; // Check every minute
1087
+
1088
+ // Keep payloads under 1KB
1089
+ // Leave 40KB+ for application
1090
+ ```
1091
+
1092
+ #### ESP32 (~320KB RAM)
1093
+
1094
+ ```cpp
1095
+ // Standard settings work well
1096
+ config.maxTrackedMessages = 500; // ~20KB for tracker
1097
+ config.duplicateTrackingTimeout = 60000; // 60 second window
1098
+ config.internetCheckInterval = 30000; // Check every 30 seconds
1099
+
1100
+ // Can handle larger payloads (up to 4KB)
1101
+ // Leave 200KB+ for application
1102
+ ```
1103
+
1104
+ ### Bandwidth Considerations
1105
+
1106
+ | Traffic Type | Interval | Size | Impact |
1107
+ |--------------|----------|------|--------|
1108
+ | Gateway Heartbeat | 15s | ~100 bytes | Low |
1109
+ | Internet Check | 30s | N/A (local TCP) | None |
1110
+ | GatewayDataPackage | Varies | 200-2000 bytes | Medium |
1111
+ | GatewayAckPackage | Per message | ~80 bytes | Low |
1112
+
1113
+ ---
1114
+
1115
+ ## Migration
1116
+
1117
+ ### From Bridge Mode to Shared Gateway Mode
1118
+
1119
+ #### Step 1: Update Dependencies
1120
+
1121
+ Ensure you have painlessMesh v1.9.0 or later.
1122
+
1123
+ #### Step 2: Modify Initialization
1124
+
1125
+ **Before (Bridge Mode):**
1126
+
1127
+ ```cpp
1128
+ mesh.init(MESH_PREFIX, MESH_PASSWORD, &userScheduler, MESH_PORT);
1129
+ mesh.stationManual(ROUTER_SSID, ROUTER_PASSWORD);
1130
+ ```
1131
+
1132
+ **After (Shared Gateway Mode):**
1133
+
1134
+ ```cpp
1135
+ mesh.initAsSharedGateway(
1136
+ MESH_PREFIX, MESH_PASSWORD,
1137
+ ROUTER_SSID, ROUTER_PASSWORD,
1138
+ &userScheduler, MESH_PORT
1139
+ );
1140
+ ```
1141
+
1142
+ #### Step 3: Update Internet Sending
1143
+
1144
+ **Before:**
1145
+
1146
+ ```cpp
1147
+ // Only bridge node could send to Internet
1148
+ if (mesh.isBridge()) {
1149
+ httpClient.POST(url, data);
1150
+ }
1151
+ ```
1152
+
1153
+ **After:**
1154
+
1155
+ ```cpp
1156
+ // Any node can send to Internet with automatic failover
1157
+ mesh.sendToInternet(url, data, [](bool success, uint16_t status, String error) {
1158
+ // Handle result
1159
+ });
1160
+ ```
1161
+
1162
+ #### Step 4: Handle Gateway Events
1163
+
1164
+ ```cpp
1165
+ // Monitor gateway changes
1166
+ mesh.onGatewayChanged([](uint32_t newGateway) {
1167
+ Serial.printf("New primary gateway: %u\n", newGateway);
1168
+ });
1169
+
1170
+ // Monitor Internet connectivity
1171
+ mesh.onLocalInternetChanged([](bool available) {
1172
+ Serial.printf("Internet: %s\n", available ? "Available" : "Lost");
1173
+ });
1174
+ ```
1175
+
1176
+ #### Step 5: Update All Nodes
1177
+
1178
+ **Important:** All nodes in the mesh should be updated to use Shared Gateway Mode for full functionality. Mixed-mode networks (some bridge, some shared gateway) are not recommended.
1179
+
1180
+ ### Configuration Migration
1181
+
1182
+ | Bridge Mode Setting | Shared Gateway Equivalent |
1183
+ |---------------------|--------------------------|
1184
+ | `stationManual()` | `routerSSID`, `routerPassword` in config |
1185
+ | Bridge election | `participateInElection`, `gatewayFailureTimeout` |
1186
+ | N/A | `internetCheckInterval`, `duplicateTrackingTimeout` |
1187
+
1188
+ ---
1189
+
1190
+ ## Related Documentation
1191
+
1192
+ - [Design Document](../design/SHARED_GATEWAY_DESIGN.md) - Full technical design
1193
+ - [Implementation Plan](../design/SHARED_GATEWAY_IMPLEMENTATION_PLAN.md) - Development roadmap
1194
+ - [Core API Reference](core-api.md) - Base painlessMesh API
1195
+ - [Example Code](../../examples/sharedGateway/sharedGateway.ino) - Working example
1196
+
1197
+ ---
1198
+
1199
+ ## Changelog
1200
+
1201
+ | Version | Changes |
1202
+ |---------|---------|
1203
+ | v1.9.0 | Initial release of Shared Gateway Mode |
1204
+
1205
+ ---
1206
+
1207
+ *Last updated: November 2025*