@alteriom/painlessmesh 1.9.18 → 1.9.20

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 (41) hide show
  1. package/CHANGELOG.md +62 -0
  2. package/README.md +82 -63
  3. package/examples/alteriom/README.md +4 -4
  4. package/examples/alteriom/alteriom_custom_package_template.hpp +320 -0
  5. package/examples/alteriom/alteriom_sensor_package.hpp +1 -1
  6. package/examples/alteriom/mppt_example/alteriom_mppt_example.ino +208 -0
  7. package/examples/bridge_failover/bridge_failover.ino +17 -0
  8. package/examples/sendToInternet/CMakeLists.txt +54 -0
  9. package/examples/sendToInternet/PC_NODE_README.md +517 -0
  10. package/examples/sendToInternet/README.md +39 -1
  11. package/examples/sendToInternet/build.sh +153 -0
  12. package/examples/sendToInternet/mock_server_test.ino +361 -0
  13. package/examples/sendToInternet/pc_mesh_node.cpp +361 -0
  14. package/library.json +4 -1
  15. package/library.properties +1 -1
  16. package/package.json +3 -3
  17. package/src/AlteriomPainlessMesh.h +5 -13
  18. package/src/arduino/wifi.hpp +306 -100
  19. package/src/connection.cpp +10 -0
  20. package/src/painlessMesh.h +1 -14
  21. package/src/painlessmesh/connection.hpp +11 -16
  22. package/src/painlessmesh/gateway.hpp +0 -1061
  23. package/src/painlessmesh/mesh.hpp +58 -86
  24. package/src/painlessmesh/message_queue.hpp +1 -2
  25. package/src/painlessmesh/metrics.hpp +2 -262
  26. package/src/painlessmesh/validation.hpp +0 -143
  27. package/docs/README.md +0 -132
  28. package/docs/alteriom/overview.md +0 -531
  29. package/docs/api/core-api.md +0 -607
  30. package/docs/api/shared-gateway.md +0 -1207
  31. package/docs/architecture/mesh-architecture.md +0 -399
  32. package/docs/architecture/plugin-system.md +0 -517
  33. package/docs/getting-started/arduino-manual-install.md +0 -313
  34. package/docs/getting-started/first-mesh.md +0 -410
  35. package/docs/getting-started/installation.md +0 -275
  36. package/docs/getting-started/quickstart.md +0 -158
  37. package/docs/troubleshooting/common-issues.md +0 -679
  38. package/docs/troubleshooting/debugging.md +0 -455
  39. package/docs/troubleshooting/external-device-connection.md +0 -283
  40. package/docs/troubleshooting/faq.md +0 -574
  41. package/docs/tutorials/basic-examples.md +0 -718
@@ -0,0 +1,361 @@
1
+ /**
2
+ * PC Mesh Node - Test sendToInternet() from Regular Node Through Bridge
3
+ *
4
+ * This example demonstrates a PC-based mesh node that can join a painlessMesh
5
+ * network with ESP32/ESP8266 devices and test sendToInternet() functionality
6
+ * as a REGULAR NODE (not a bridge).
7
+ *
8
+ * PURPOSE:
9
+ * Test the complete flow: PC Node → Bridge → Internet → Bridge → PC Node
10
+ *
11
+ * CONNECTION METHOD:
12
+ * ESP nodes use WiFi mesh credentials: mesh.init(MESH_PREFIX, MESH_PASSWORD, &scheduler, MESH_PORT)
13
+ * PC node uses TCP/IP connection: connects to bridge's IP:port (e.g., 192.168.1.100:5555)
14
+ *
15
+ * Both methods result in a mesh node that can use sendToInternet() - the difference
16
+ * is the transport layer (WiFi mesh vs TCP/IP), not the painlessMesh protocol.
17
+ *
18
+ * SETUP:
19
+ * 1. Configure ESP bridge with: mesh.init("FishFarmMesh", "securepass", &scheduler, 5555)
20
+ * 2. Run this PC node: ./pc_mesh_node <bridge_ip> 5555
21
+ * 3. Run mock HTTP server: cd test/mock-http-server && python3 server.py
22
+ * 4. PC node sends HTTP requests via sendToInternet() through bridge
23
+ *
24
+ * COMPILE:
25
+ * g++ -std=c++14 -o pc_mesh_node pc_mesh_node.cpp \
26
+ * -I../../src -I../../test/include -I../../test/ArduinoJson/src \
27
+ * -I../../test/TaskScheduler/src -I../../test/boost \
28
+ * ../../src/scheduler.cpp \
29
+ * -lboost_system -pthread
30
+ *
31
+ * Or use CMake (see CMakeLists.txt in this directory)
32
+ *
33
+ * RUN:
34
+ * ./pc_mesh_node 192.168.1.100 5555
35
+ *
36
+ * Where:
37
+ * - 192.168.1.100 is your ESP bridge's IP address on the LAN
38
+ * - 5555 is the TCP port the bridge is listening on (MESH_PORT)
39
+ **/
40
+
41
+ #include <iostream>
42
+ #include <string>
43
+ #include <memory>
44
+ #include <chrono>
45
+ #include <thread>
46
+
47
+ // Boost includes
48
+ #include <boost/asio/ip/address.hpp>
49
+
50
+ // Arduino emulation for PC
51
+ #include "Arduino.h"
52
+ #include "boost/asynctcp.hpp"
53
+
54
+ // painlessMesh includes
55
+ #include "painlessmesh/mesh.hpp"
56
+ #include "painlessmesh/gateway.hpp"
57
+
58
+ // Global objects required by Arduino emulation
59
+ WiFiClass WiFi;
60
+ ESPClass ESP;
61
+
62
+ using PMesh = painlessmesh::Mesh<painlessmesh::Connection>;
63
+ using namespace painlessmesh;
64
+
65
+ // Global logger
66
+ painlessmesh::logger::LogClass Log;
67
+
68
+ /**
69
+ * PC Mesh Node Class
70
+ *
71
+ * This class wraps a painlessMesh instance and provides methods
72
+ * to connect to a bridge node and send Internet requests through it.
73
+ */
74
+ class PCMeshNode : public PMesh {
75
+ public:
76
+ PCMeshNode(Scheduler* scheduler, uint32_t nodeId, boost::asio::io_context& io)
77
+ : io_service(io), scheduler_(scheduler) {
78
+ this->nodeId = nodeId;
79
+ this->init(scheduler, this->nodeId);
80
+
81
+ // Start listening server (so bridge can connect back to us if needed)
82
+ pServer = std::make_shared<AsyncServer>(io_service, this->nodeId);
83
+ painlessmesh::tcp::initServer<painlessmesh::Connection, PMesh>(*pServer, (*this));
84
+
85
+ std::cout << "✓ PC Mesh Node initialized with ID: " << this->nodeId << std::endl;
86
+ }
87
+
88
+ /**
89
+ * Connect to a bridge node
90
+ */
91
+ bool connectToBridge(const std::string& bridgeIP, uint16_t bridgePort) {
92
+ std::cout << "Connecting to bridge at " << bridgeIP << ":" << bridgePort << "..." << std::endl;
93
+
94
+ try {
95
+ auto pClient = new AsyncClient(io_service);
96
+
97
+ // Set up connection callbacks
98
+ bool connected = false;
99
+ pClient->onConnect([&connected](void*, AsyncClient* client) {
100
+ connected = true;
101
+ std::cout << "✓ Connected to bridge!" << std::endl;
102
+ });
103
+
104
+ pClient->onDisconnect([](void*, AsyncClient* client) {
105
+ std::cout << "✗ Disconnected from bridge" << std::endl;
106
+ });
107
+
108
+ // Connect to the bridge
109
+ painlessmesh::tcp::connect<Connection, PMesh>(
110
+ (*pClient),
111
+ boost::asio::ip::make_address(bridgeIP),
112
+ bridgePort,
113
+ (*this)
114
+ );
115
+
116
+ // Wait for connection with timeout
117
+ auto startTime = std::chrono::steady_clock::now();
118
+ while (!connected) {
119
+ this->update();
120
+ io_service.poll();
121
+
122
+ // Use longer sleep to reduce CPU usage during wait
123
+ std::this_thread::sleep_for(std::chrono::milliseconds(100));
124
+
125
+ auto elapsed = std::chrono::duration_cast<std::chrono::seconds>(
126
+ std::chrono::steady_clock::now() - startTime
127
+ ).count();
128
+
129
+ if (elapsed > 30) {
130
+ std::cout << "✗ Connection timeout after 30 seconds" << std::endl;
131
+ return false;
132
+ }
133
+ }
134
+
135
+ return true;
136
+ } catch (const std::exception& e) {
137
+ std::cout << "✗ Connection error: " << e.what() << std::endl;
138
+ return false;
139
+ }
140
+ }
141
+
142
+ /**
143
+ * Send an HTTP request to the Internet via the bridge
144
+ */
145
+ void testSendToInternet(const std::string& url, const std::string& payload = "") {
146
+ std::cout << "\n📡 Testing sendToInternet()..." << std::endl;
147
+ std::cout << " URL: " << url << std::endl;
148
+ if (!payload.empty()) {
149
+ std::cout << " Payload: " << payload << std::endl;
150
+ }
151
+
152
+ // Check if we have Internet connection through bridge
153
+ if (!this->hasInternetConnection()) {
154
+ std::cout << " ⚠️ No Internet connection available through bridge" << std::endl;
155
+ std::cout << " Make sure the bridge node has router access and is connected" << std::endl;
156
+ return;
157
+ }
158
+
159
+ std::cout << " ✓ Bridge with Internet found" << std::endl;
160
+
161
+ // Send the request
162
+ uint32_t msgId = this->sendToInternet(
163
+ url,
164
+ payload,
165
+ [url](bool success, uint16_t httpStatus, std::string error) {
166
+ std::cout << "\n📥 Response received:" << std::endl;
167
+ std::cout << " Success: " << (success ? "✓ YES" : "✗ NO") << std::endl;
168
+ std::cout << " HTTP Status: " << httpStatus << std::endl;
169
+ if (!error.empty()) {
170
+ std::cout << " Error: " << error << std::endl;
171
+ }
172
+
173
+ // Validate the result
174
+ if (success && httpStatus == 200) {
175
+ std::cout << " 🎉 TEST PASSED: Request successful!" << std::endl;
176
+ } else {
177
+ std::cout << " ❌ TEST FAILED: Request unsuccessful" << std::endl;
178
+ }
179
+ }
180
+ );
181
+
182
+ if (msgId > 0) {
183
+ std::cout << " ✓ Request queued with message ID: " << msgId << std::endl;
184
+ std::cout << " Waiting for response from bridge..." << std::endl;
185
+ } else {
186
+ std::cout << " ✗ Failed to queue request" << std::endl;
187
+ }
188
+ }
189
+
190
+ /**
191
+ * Main update loop
192
+ */
193
+ void runUpdateLoop(int durationSeconds = 0) {
194
+ std::cout << "\n🔄 Starting update loop";
195
+ if (durationSeconds > 0) {
196
+ std::cout << " (running for " << durationSeconds << " seconds)";
197
+ }
198
+ std::cout << "..." << std::endl;
199
+
200
+ auto startTime = std::chrono::steady_clock::now();
201
+
202
+ while (true) {
203
+ this->update();
204
+ io_service.poll();
205
+ scheduler_->execute();
206
+
207
+ std::this_thread::sleep_for(std::chrono::milliseconds(10));
208
+
209
+ if (durationSeconds > 0) {
210
+ auto elapsed = std::chrono::duration_cast<std::chrono::seconds>(
211
+ std::chrono::steady_clock::now() - startTime
212
+ ).count();
213
+
214
+ if (elapsed >= durationSeconds) {
215
+ std::cout << "✓ Update loop completed after " << elapsed << " seconds" << std::endl;
216
+ break;
217
+ }
218
+ }
219
+ }
220
+ }
221
+
222
+ std::shared_ptr<AsyncServer> pServer;
223
+ boost::asio::io_context& io_service;
224
+ Scheduler* scheduler_;
225
+ };
226
+
227
+ /**
228
+ * Print usage information
229
+ */
230
+ void printUsage(const char* programName) {
231
+ std::cout << "\nUsage: " << programName << " <bridge_ip> <bridge_port> [mock_server_ip] [mock_server_port]\n" << std::endl;
232
+ std::cout << "Arguments:" << std::endl;
233
+ std::cout << " bridge_ip IP address of ESP bridge node (e.g., 192.168.1.100)" << std::endl;
234
+ std::cout << " bridge_port Mesh port of bridge (default: 5555)" << std::endl;
235
+ std::cout << " mock_server_ip IP address of mock HTTP server (optional, default: localhost)" << std::endl;
236
+ std::cout << " mock_server_port Port of mock HTTP server (optional, default: 8080)" << std::endl;
237
+ std::cout << "\nExample:" << std::endl;
238
+ std::cout << " " << programName << " 192.168.1.100 5555" << std::endl;
239
+ std::cout << " " << programName << " 192.168.1.100 5555 192.168.1.50 8080" << std::endl;
240
+ std::cout << std::endl;
241
+ }
242
+
243
+ /**
244
+ * Run automated test suite
245
+ */
246
+ void runTests(PCMeshNode& node, const std::string& mockServerUrl) {
247
+ std::cout << "\n" << std::string(60, '=') << std::endl;
248
+ std::cout << "Starting Automated Test Suite" << std::endl;
249
+ std::cout << std::string(60, '=') << std::endl;
250
+
251
+ // Wait for mesh to stabilize
252
+ std::cout << "\nWaiting 5 seconds for mesh to stabilize..." << std::endl;
253
+ std::this_thread::sleep_for(std::chrono::seconds(5));
254
+
255
+ // Test 1: HTTP 200 Success
256
+ std::cout << "\n[Test 1/5] HTTP 200 Success" << std::endl;
257
+ node.testSendToInternet(mockServerUrl + "/status/200");
258
+ node.runUpdateLoop(5);
259
+
260
+ // Test 2: HTTP 404 Not Found
261
+ std::cout << "\n[Test 2/5] HTTP 404 Not Found" << std::endl;
262
+ node.testSendToInternet(mockServerUrl + "/status/404");
263
+ node.runUpdateLoop(5);
264
+
265
+ // Test 3: HTTP 500 Server Error
266
+ std::cout << "\n[Test 3/5] HTTP 500 Server Error" << std::endl;
267
+ node.testSendToInternet(mockServerUrl + "/status/500");
268
+ node.runUpdateLoop(5);
269
+
270
+ // Test 4: WhatsApp API Simulation
271
+ std::cout << "\n[Test 4/5] WhatsApp API Simulation" << std::endl;
272
+ std::string whatsappUrl = mockServerUrl + "/whatsapp?phone=%2B1234567890&apikey=test&text=Hello%20from%20PC%20node";
273
+ node.testSendToInternet(whatsappUrl);
274
+ node.runUpdateLoop(5);
275
+
276
+ // Test 5: Echo with JSON Payload
277
+ std::cout << "\n[Test 5/5] Echo Endpoint with JSON Payload" << std::endl;
278
+ // Using raw string literal for safer JSON construction
279
+ // In production, consider using ArduinoJson for proper JSON handling
280
+ char jsonBuffer[256];
281
+ snprintf(jsonBuffer, sizeof(jsonBuffer),
282
+ R"({"source":"pc_node","test":"sendToInternet","timestamp":%lu})",
283
+ (unsigned long)millis());
284
+ std::string payload(jsonBuffer);
285
+ node.testSendToInternet(mockServerUrl + "/echo", payload);
286
+ node.runUpdateLoop(5);
287
+
288
+ std::cout << "\n" << std::string(60, '=') << std::endl;
289
+ std::cout << "Test Suite Complete!" << std::endl;
290
+ std::cout << std::string(60, '=') << std::endl;
291
+ }
292
+
293
+ /**
294
+ * Main entry point
295
+ */
296
+ int main(int argc, char* argv[]) {
297
+ std::cout << "\n" << std::string(60, '=') << std::endl;
298
+ std::cout << "painlessMesh - PC Mesh Node Example" << std::endl;
299
+ std::cout << "Testing sendToInternet() Through Bridge" << std::endl;
300
+ std::cout << std::string(60, '=') << std::endl;
301
+
302
+ // Parse command line arguments
303
+ if (argc < 3) {
304
+ printUsage(argv[0]);
305
+ return 1;
306
+ }
307
+
308
+ std::string bridgeIP = argv[1];
309
+ uint16_t bridgePort = std::stoi(argv[2]);
310
+ std::string mockServerIP = (argc > 3) ? argv[3] : "127.0.0.1";
311
+ uint16_t mockServerPort = (argc > 4) ? std::stoi(argv[4]) : 8080;
312
+
313
+ std::cout << "\nConfiguration:" << std::endl;
314
+ std::cout << " Bridge: " << bridgeIP << ":" << bridgePort << std::endl;
315
+ std::cout << " Mock Server: http://" << mockServerIP << ":" << mockServerPort << std::endl;
316
+ std::cout << std::endl;
317
+
318
+ // Setup logging
319
+ Log.setLogLevel(painlessmesh::logger::ERROR | painlessmesh::logger::STARTUP |
320
+ painlessmesh::logger::CONNECTION | painlessmesh::logger::COMMUNICATION);
321
+
322
+ // Create IO context and scheduler
323
+ boost::asio::io_context io_service;
324
+ Scheduler scheduler;
325
+
326
+ // Create PC mesh node with a random node ID
327
+ uint32_t nodeId = 1000000 + (millis() % 1000000);
328
+ PCMeshNode node(&scheduler, nodeId, io_service);
329
+
330
+ // Enable sendToInternet API
331
+ node.enableSendToInternet();
332
+ std::cout << "✓ sendToInternet() API enabled" << std::endl;
333
+
334
+ // Connect to bridge
335
+ if (!node.connectToBridge(bridgeIP, bridgePort)) {
336
+ std::cerr << "\n✗ Failed to connect to bridge" << std::endl;
337
+ std::cerr << "Make sure:" << std::endl;
338
+ std::cerr << " 1. Bridge node is running on " << bridgeIP << ":" << bridgePort << std::endl;
339
+ std::cerr << " 2. Firewall allows TCP connections on port " << bridgePort << std::endl;
340
+ std::cerr << " 3. PC and bridge are on the same network" << std::endl;
341
+ return 1;
342
+ }
343
+
344
+ // Wait for mesh to establish
345
+ std::cout << "\nWaiting for mesh to establish..." << std::endl;
346
+ node.runUpdateLoop(10);
347
+
348
+ // Build mock server URL
349
+ std::string mockServerUrl = "http://" + mockServerIP + ":" + std::to_string(mockServerPort);
350
+
351
+ // Run automated tests
352
+ runTests(node, mockServerUrl);
353
+
354
+ // Keep running for a bit to receive any late responses
355
+ std::cout << "\nKeeping connection alive for 10 more seconds..." << std::endl;
356
+ node.runUpdateLoop(10);
357
+
358
+ std::cout << "\n✓ PC Mesh Node shutting down..." << std::endl;
359
+
360
+ return 0;
361
+ }
package/library.json CHANGED
@@ -6,7 +6,7 @@
6
6
  "type": "git",
7
7
  "url": "https://github.com/Alteriom/painlessMesh"
8
8
  },
9
- "version": "1.9.18",
9
+ "version": "1.9.20",
10
10
  "frameworks": [
11
11
  "arduino"
12
12
  ],
@@ -87,7 +87,10 @@
87
87
  "examples/namedMesh/namedMesh.ino",
88
88
  "examples/otaReceiver/otaReceiver.ino",
89
89
  "examples/otaSender/otaSender.ino",
90
+ "examples/alteriom/mppt_example/alteriom_mppt_example.ino",
90
91
  "examples/priority/priority_basic_example.ino",
92
+ "examples/priority/priority_with_queue.ino",
93
+ "examples/sendToInternet/mock_server_test.ino",
91
94
  "examples/sendToInternet/sendToInternet.ino",
92
95
  "examples/sharedGateway/sharedGateway.ino",
93
96
  "examples/startHere/startHere.ino",
@@ -1,5 +1,5 @@
1
1
  name=Alteriom PainlessMesh
2
- version=1.9.18
2
+ version=1.9.20
3
3
  author=Coopdis,Scotty Franzyshen,Edwin van Leeuwen,Germán Martín,Maximilian Schwarz,Doanh Doanh,Alteriom
4
4
  maintainer=Alteriom
5
5
  sentence=A painless way to setup a mesh with ESP8266 and ESP32 devices with Alteriom extensions
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alteriom/painlessmesh",
3
- "version": "1.9.18",
3
+ "version": "1.9.20",
4
4
  "description": "painlessMesh is a user-friendly library for creating mesh networks with ESP8266 and ESP32 devices. This Alteriom fork includes additional packages for sensor data (SensorPackage), device commands (CommandPackage), and status monitoring (StatusPackage). It handles routing and network management automatically, so you can focus on your application. The library uses JSON-based messaging and syncs time across all nodes, making it ideal for coordinated behaviour like synchronized light displays or sensor networks reporting to a central node.",
5
5
  "keywords": [
6
6
  "arduino",
@@ -40,10 +40,10 @@
40
40
  },
41
41
  "devDependencies": {
42
42
  "@alteriom/mqtt-schema": "^0.8.0",
43
- "@eslint/js": "^9.39.2",
43
+ "@eslint/js": "^10.0.1",
44
44
  "ajv": "^8.17.1",
45
45
  "ajv-formats": "^3.0.1",
46
- "prettier": "^3.7.4"
46
+ "prettier": "^3.8.1"
47
47
  },
48
48
  "scripts": {
49
49
  "test": "run-parts --regex catch_ bin/ || echo 'Tests completed'",
@@ -29,10 +29,10 @@
29
29
  /**
30
30
  * @brief AlteriomPainlessMesh library version information
31
31
  */
32
- #define ALTERIOM_PAINLESS_MESH_VERSION "1.9.18"
32
+ #define ALTERIOM_PAINLESS_MESH_VERSION "1.9.20"
33
33
  #define ALTERIOM_PAINLESS_MESH_VERSION_MAJOR 1
34
34
  #define ALTERIOM_PAINLESS_MESH_VERSION_MINOR 9
35
- #define ALTERIOM_PAINLESS_MESH_VERSION_PATCH 18
35
+ #define ALTERIOM_PAINLESS_MESH_VERSION_PATCH 20
36
36
 
37
37
  /**
38
38
  * @brief Library description and usage information
@@ -79,19 +79,11 @@
79
79
  *
80
80
  * @code{.cpp}
81
81
  * using namespace alteriom;
82
- *
82
+ *
83
83
  * // Send sensor data
84
84
  * SensorPackage sensor;
85
- * sensor.temperature = 25.5;
86
- * sensor.humidity = 60.0;
87
- * mesh.sendPackage(&sensor);
88
- *
89
- * // Handle received sensor data
90
- * mesh.onPackage(200, [](protocol::Variant& variant) {
91
- * SensorPackage data = variant.to<SensorPackage>();
92
- * Serial.printf("Temperature: %.1f°C\n", data.temperature);
93
- * return true;
94
- * });
85
+ * sensor.temperature = 22.5;
86
+ * mesh.sendBroadcast(sensor.toJsonString());
95
87
  * @endcode
96
88
  */
97
89