@alteriom/painlessmesh 1.6.1 → 1.7.2
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.
- package/CHANGELOG.md +380 -143
- package/LICENSE +674 -674
- package/README.md +477 -434
- package/RELEASE_GUIDE.md +504 -418
- package/docs/DOCUMENTATION_MIGRATION_PLAN.md +175 -175
- package/docs/IMPLEMENTATION_PLAN_MESH_TOPOLOGY.md +1062 -0
- package/docs/MESH_TOPOLOGY_GUIDE.md +992 -0
- package/docs/MESH_TOPOLOGY_PROGRESS.md +422 -0
- package/docs/MQTT_BRIDGE_COMMANDS.md +894 -0
- package/docs/MQTT_BRIDGE_IMPLEMENTATION_SUMMARY.md +324 -0
- package/docs/MQTT_COMMAND_SCHEMA_PROPOSAL.md +576 -0
- package/docs/MQTT_SCHEMA_COMPLIANCE.md +285 -0
- package/docs/MQTT_SCHEMA_PROPOSALS.md +446 -0
- package/docs/MQTT_SCHEMA_REVIEW.md +690 -0
- package/docs/OTA_COMMANDS_REFERENCE.md +554 -0
- package/docs/PHASE1_GUIDE.md +349 -0
- package/docs/PHASE2_GUIDE.md +543 -0
- package/docs/README.md +77 -70
- package/docs/SCHEMA_VALIDATION_CHECKLIST.md +222 -0
- package/docs/alteriom/overview.md +507 -507
- package/docs/api/core-api.md +606 -606
- package/docs/architecture/mesh-architecture.md +378 -378
- package/docs/architecture/plugin-system.md +516 -516
- package/docs/getting-started/first-mesh.md +409 -409
- package/docs/getting-started/installation.md +274 -274
- package/docs/getting-started/quickstart.md +157 -157
- package/docs/improvements/FEATURE_PROPOSALS.md +337 -0
- package/docs/improvements/PHASE1_IMPLEMENTATION.md +325 -0
- package/docs/improvements/PHASE2_IMPLEMENTATION.md +567 -0
- package/docs/improvements/README.md +86 -68
- package/docs/improvements/ota-and-status-enhancements.md +911 -0
- package/docs/improvements/ota-status-architecture-diagrams.md +658 -0
- package/docs/improvements/ota-status-quick-reference.md +284 -0
- package/docs/platformio-publishing.md +255 -0
- package/docs/platformio-setup-summary.md +121 -0
- package/docs/troubleshooting/common-issues.md +520 -520
- package/docs/troubleshooting/faq.md +472 -472
- package/docs/tutorials/basic-examples.md +717 -717
- package/docs/wiki/API-Reference.md +245 -245
- package/docs/wiki/Complete-Documentation.md +122 -122
- package/examples/alteriom/README.md +139 -81
- package/examples/alteriom/alteriom.ino +186 -185
- package/examples/alteriom/alteriom_sensor_package.hpp +240 -127
- package/examples/alteriom/platformio.ini +24 -24
- package/examples/alteriomImproved/alteriom_sensor_package.hpp +224 -0
- package/examples/{alteriom → alteriomImproved}/improved_sensor_node.ino +245 -245
- package/examples/alteriomImproved/platformio.ini +25 -0
- package/examples/alteriomPhase1/alteriom_sensor_package.hpp +224 -0
- package/examples/alteriomPhase1/phase1_features.ino +242 -0
- package/examples/alteriomPhase1/platformio.ini +25 -0
- package/examples/alteriomPhase2/alteriom_sensor_package.hpp +224 -0
- package/examples/alteriomPhase2/phase2_features.ino +186 -0
- package/examples/alteriomPhase2/platformio.ini +25 -0
- package/examples/{alteriom → alteriomSensorNode}/alteriom_sensor_node.ino +183 -183
- package/examples/alteriomSensorNode/alteriom_sensor_package.hpp +224 -0
- package/examples/alteriomSensorNode/platformio.ini +25 -0
- package/examples/basic/basic.ino +66 -66
- package/examples/basic/platformio.ini +25 -25
- package/examples/bridge/bridge.ino +51 -51
- package/examples/bridge/mesh_event_publisher.hpp +253 -0
- package/examples/bridge/mesh_topology_reporter.hpp +303 -0
- package/examples/bridge/mqtt_command_bridge.hpp +459 -0
- package/examples/bridge/mqtt_status_bridge.hpp +519 -0
- package/examples/bridge/platformio.ini +25 -25
- package/examples/echoNode/echoNode.ino +33 -33
- package/examples/echoNode/platformio.ini +25 -25
- package/examples/logClient/logClient.ino +109 -109
- package/examples/logClient/platformio.ini +25 -25
- package/examples/logServer/logServer.ino +81 -81
- package/examples/logServer/platformio.ini +25 -25
- package/examples/meshCommandNode/alteriom_sensor_package.hpp +235 -0
- package/examples/meshCommandNode/meshCommandNode.ino +263 -0
- package/examples/meshCommandNode/platformio.ini +25 -0
- package/examples/mqttBridge/mqttBridge.ino +118 -118
- package/examples/mqttBridge/platformio.ini +26 -26
- package/examples/mqttCommandBridge/alteriom_sensor_package.hpp +235 -0
- package/examples/mqttCommandBridge/mesh_event_publisher.hpp +253 -0
- package/examples/mqttCommandBridge/mesh_topology_reporter.hpp +303 -0
- package/examples/mqttCommandBridge/mqttCommandBridge.ino +252 -0
- package/examples/mqttCommandBridge/mqtt_command_bridge.hpp +459 -0
- package/examples/mqttCommandBridge/platformio.ini +26 -0
- package/examples/mqttStatusBridge/mqttStatusBridge.ino +216 -0
- package/examples/mqttStatusBridge/mqtt_status_bridge.hpp +522 -0
- package/examples/mqttStatusBridge/platformio.ini +26 -0
- package/examples/mqttTopologyTest/README.md +467 -0
- package/examples/mqttTopologyTest/mqttTopologyTest.ino +748 -0
- package/examples/mqttTopologyTest/platformio.ini +26 -0
- package/examples/namedMesh/namedMesh.ino +97 -97
- package/examples/namedMesh/platformio.ini +25 -25
- package/examples/otaReceiver/otaReceiver.ino +79 -79
- package/examples/otaReceiver/platformio.ini +25 -25
- package/examples/otaSender/otaSender.ino +160 -151
- package/examples/otaSender/platformio.ini +25 -25
- package/examples/startHere/platformio.ini +25 -25
- package/examples/startHere/startHere.ino +159 -159
- package/examples/webServer/platformio.ini +27 -27
- package/examples/webServer/webServer.ino +89 -89
- package/keywords.txt +48 -48
- package/library.json +55 -34
- package/library.properties +10 -10
- package/package.json +86 -78
- package/src/AlteriomPainlessMesh.h +97 -97
- package/src/arduino/wifi.hpp +365 -365
- package/src/boost/asynctcp.hpp +279 -279
- package/src/painlessMesh.h +70 -70
- package/src/painlessMeshSTA.cpp +236 -236
- package/src/painlessMeshSTA.h +58 -58
- package/src/painlessTaskOptions.h +4 -4
- package/src/painlessmesh/base64.hpp +111 -111
- package/src/painlessmesh/buffer.hpp +229 -229
- package/src/painlessmesh/callback.hpp +91 -91
- package/src/painlessmesh/configuration.hpp +77 -77
- package/src/painlessmesh/connection.hpp +192 -192
- package/src/painlessmesh/layout.hpp +188 -188
- package/src/painlessmesh/logger.hpp +158 -158
- package/src/painlessmesh/memory.hpp +119 -119
- package/src/painlessmesh/mesh.hpp +761 -560
- package/src/painlessmesh/metrics.hpp +322 -322
- package/src/painlessmesh/ntp.hpp +263 -263
- package/src/painlessmesh/ota.hpp +582 -553
- package/src/painlessmesh/plugin.hpp +188 -188
- package/src/painlessmesh/protocol.hpp +813 -813
- package/src/painlessmesh/router.hpp +322 -322
- package/src/painlessmesh/tcp.hpp +71 -71
- package/src/painlessmesh/validation.hpp +238 -238
- package/src/plugin/performance.hpp +214 -214
- package/src/plugin/remote.hpp +64 -64
- package/src/scheduler.cpp +10 -10
- package/src/wifi.cpp +2 -2
package/examples/basic/basic.ino
CHANGED
|
@@ -1,66 +1,66 @@
|
|
|
1
|
-
//************************************************************
|
|
2
|
-
// this is a simple example that uses the painlessMesh library
|
|
3
|
-
//
|
|
4
|
-
// 1. sends a silly message to every node on the mesh at a random time between 1 and 5 seconds
|
|
5
|
-
// 2. prints anything it receives to Serial.print
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//************************************************************
|
|
9
|
-
#include "painlessMesh.h"
|
|
10
|
-
|
|
11
|
-
#define MESH_PREFIX "whateverYouLike"
|
|
12
|
-
#define MESH_PASSWORD "somethingSneaky"
|
|
13
|
-
#define MESH_PORT 5555
|
|
14
|
-
|
|
15
|
-
Scheduler userScheduler; // to control your personal task
|
|
16
|
-
painlessMesh mesh;
|
|
17
|
-
|
|
18
|
-
// User stub
|
|
19
|
-
void sendMessage() ; // Prototype so PlatformIO doesn't complain
|
|
20
|
-
|
|
21
|
-
Task taskSendMessage( TASK_SECOND * 1 , TASK_FOREVER, &sendMessage );
|
|
22
|
-
|
|
23
|
-
void sendMessage() {
|
|
24
|
-
String msg = "Hello from node ";
|
|
25
|
-
msg += mesh.getNodeId();
|
|
26
|
-
mesh.sendBroadcast( msg );
|
|
27
|
-
taskSendMessage.setInterval( random( TASK_SECOND * 1, TASK_SECOND * 5 ));
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
// Needed for painless library
|
|
31
|
-
void receivedCallback( uint32_t from, String &msg ) {
|
|
32
|
-
Serial.printf("startHere: Received from %u msg=%s\n", from, msg.c_str());
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
void newConnectionCallback(uint32_t nodeId) {
|
|
36
|
-
Serial.printf("--> startHere: New Connection, nodeId = %u\n", nodeId);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
void changedConnectionCallback() {
|
|
40
|
-
Serial.printf("Changed connections\n");
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
void nodeTimeAdjustedCallback(int32_t offset) {
|
|
44
|
-
Serial.printf("Adjusted time %u. Offset = %d\n", mesh.getNodeTime(),offset);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
void setup() {
|
|
48
|
-
Serial.begin(115200);
|
|
49
|
-
|
|
50
|
-
//mesh.setDebugMsgTypes( ERROR | MESH_STATUS | CONNECTION | SYNC | COMMUNICATION | GENERAL | MSG_TYPES | REMOTE ); // all types on
|
|
51
|
-
mesh.setDebugMsgTypes( ERROR | STARTUP ); // set before init() so that you can see startup messages
|
|
52
|
-
|
|
53
|
-
mesh.init( MESH_PREFIX, MESH_PASSWORD, &userScheduler, MESH_PORT );
|
|
54
|
-
mesh.onReceive(&receivedCallback);
|
|
55
|
-
mesh.onNewConnection(&newConnectionCallback);
|
|
56
|
-
mesh.onChangedConnections(&changedConnectionCallback);
|
|
57
|
-
mesh.onNodeTimeAdjusted(&nodeTimeAdjustedCallback);
|
|
58
|
-
|
|
59
|
-
userScheduler.addTask( taskSendMessage );
|
|
60
|
-
taskSendMessage.enable();
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
void loop() {
|
|
64
|
-
// it will run the user scheduler as well
|
|
65
|
-
mesh.update();
|
|
66
|
-
}
|
|
1
|
+
//************************************************************
|
|
2
|
+
// this is a simple example that uses the painlessMesh library
|
|
3
|
+
//
|
|
4
|
+
// 1. sends a silly message to every node on the mesh at a random time between 1 and 5 seconds
|
|
5
|
+
// 2. prints anything it receives to Serial.print
|
|
6
|
+
//
|
|
7
|
+
//
|
|
8
|
+
//************************************************************
|
|
9
|
+
#include "painlessMesh.h"
|
|
10
|
+
|
|
11
|
+
#define MESH_PREFIX "whateverYouLike"
|
|
12
|
+
#define MESH_PASSWORD "somethingSneaky"
|
|
13
|
+
#define MESH_PORT 5555
|
|
14
|
+
|
|
15
|
+
Scheduler userScheduler; // to control your personal task
|
|
16
|
+
painlessMesh mesh;
|
|
17
|
+
|
|
18
|
+
// User stub
|
|
19
|
+
void sendMessage() ; // Prototype so PlatformIO doesn't complain
|
|
20
|
+
|
|
21
|
+
Task taskSendMessage( TASK_SECOND * 1 , TASK_FOREVER, &sendMessage );
|
|
22
|
+
|
|
23
|
+
void sendMessage() {
|
|
24
|
+
String msg = "Hello from node ";
|
|
25
|
+
msg += mesh.getNodeId();
|
|
26
|
+
mesh.sendBroadcast( msg );
|
|
27
|
+
taskSendMessage.setInterval( random( TASK_SECOND * 1, TASK_SECOND * 5 ));
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Needed for painless library
|
|
31
|
+
void receivedCallback( uint32_t from, String &msg ) {
|
|
32
|
+
Serial.printf("startHere: Received from %u msg=%s\n", from, msg.c_str());
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
void newConnectionCallback(uint32_t nodeId) {
|
|
36
|
+
Serial.printf("--> startHere: New Connection, nodeId = %u\n", nodeId);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
void changedConnectionCallback() {
|
|
40
|
+
Serial.printf("Changed connections\n");
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
void nodeTimeAdjustedCallback(int32_t offset) {
|
|
44
|
+
Serial.printf("Adjusted time %u. Offset = %d\n", mesh.getNodeTime(),offset);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
void setup() {
|
|
48
|
+
Serial.begin(115200);
|
|
49
|
+
|
|
50
|
+
//mesh.setDebugMsgTypes( ERROR | MESH_STATUS | CONNECTION | SYNC | COMMUNICATION | GENERAL | MSG_TYPES | REMOTE ); // all types on
|
|
51
|
+
mesh.setDebugMsgTypes( ERROR | STARTUP ); // set before init() so that you can see startup messages
|
|
52
|
+
|
|
53
|
+
mesh.init( MESH_PREFIX, MESH_PASSWORD, &userScheduler, MESH_PORT );
|
|
54
|
+
mesh.onReceive(&receivedCallback);
|
|
55
|
+
mesh.onNewConnection(&newConnectionCallback);
|
|
56
|
+
mesh.onChangedConnections(&changedConnectionCallback);
|
|
57
|
+
mesh.onNodeTimeAdjusted(&nodeTimeAdjustedCallback);
|
|
58
|
+
|
|
59
|
+
userScheduler.addTask( taskSendMessage );
|
|
60
|
+
taskSendMessage.enable();
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
void loop() {
|
|
64
|
+
// it will run the user scheduler as well
|
|
65
|
+
mesh.update();
|
|
66
|
+
}
|
|
@@ -1,25 +1,25 @@
|
|
|
1
|
-
[platformio]
|
|
2
|
-
src_dir = .
|
|
3
|
-
|
|
4
|
-
[env]
|
|
5
|
-
lib_deps =
|
|
6
|
-
bblanchon/ArduinoJson
|
|
7
|
-
arkhipenko/TaskScheduler
|
|
8
|
-
|
|
9
|
-
[env:esp8266]
|
|
10
|
-
platform = espressif8266
|
|
11
|
-
board = nodemcuv2
|
|
12
|
-
framework = arduino
|
|
13
|
-
lib_extra_dirs = ../../
|
|
14
|
-
lib_deps =
|
|
15
|
-
${env.lib_deps} ; Inherit common dependencies
|
|
16
|
-
esp32async/ESPAsyncTCP ; Only for ESP8266
|
|
17
|
-
|
|
18
|
-
[env:esp32]
|
|
19
|
-
platform = espressif32
|
|
20
|
-
board = esp32dev
|
|
21
|
-
framework = arduino
|
|
22
|
-
lib_extra_dirs = ../../
|
|
23
|
-
lib_deps =
|
|
24
|
-
${env.lib_deps} ; Inherit common dependencies
|
|
25
|
-
esp32async/AsyncTCP
|
|
1
|
+
[platformio]
|
|
2
|
+
src_dir = .
|
|
3
|
+
|
|
4
|
+
[env]
|
|
5
|
+
lib_deps =
|
|
6
|
+
bblanchon/ArduinoJson
|
|
7
|
+
arkhipenko/TaskScheduler
|
|
8
|
+
|
|
9
|
+
[env:esp8266]
|
|
10
|
+
platform = espressif8266
|
|
11
|
+
board = nodemcuv2
|
|
12
|
+
framework = arduino
|
|
13
|
+
lib_extra_dirs = ../../
|
|
14
|
+
lib_deps =
|
|
15
|
+
${env.lib_deps} ; Inherit common dependencies
|
|
16
|
+
esp32async/ESPAsyncTCP ; Only for ESP8266
|
|
17
|
+
|
|
18
|
+
[env:esp32]
|
|
19
|
+
platform = espressif32
|
|
20
|
+
board = esp32dev
|
|
21
|
+
framework = arduino
|
|
22
|
+
lib_extra_dirs = ../../
|
|
23
|
+
lib_deps =
|
|
24
|
+
${env.lib_deps} ; Inherit common dependencies
|
|
25
|
+
esp32async/AsyncTCP
|
|
@@ -1,51 +1,51 @@
|
|
|
1
|
-
//************************************************************
|
|
2
|
-
// this is a simple example that uses the painlessMesh library to
|
|
3
|
-
// connect to a node on another network. Please see the WIKI on gitlab
|
|
4
|
-
// for more details
|
|
5
|
-
// https://gitlab.com/painlessMesh/painlessMesh/wikis/bridge-between-mesh-and-another-network
|
|
6
|
-
//************************************************************
|
|
7
|
-
#include "painlessMesh.h"
|
|
8
|
-
|
|
9
|
-
#define MESH_PREFIX "whateverYouLike"
|
|
10
|
-
#define MESH_PASSWORD "somethingSneaky"
|
|
11
|
-
#define MESH_PORT 5555
|
|
12
|
-
|
|
13
|
-
#define STATION_SSID "MSYSSID"
|
|
14
|
-
#define STATION_PASSWORD "SSIDPASSWORD"
|
|
15
|
-
#define STATION_PORT 5555
|
|
16
|
-
uint8_t station_ip[4] = {192, 168, 1, 3}; // IP of the server
|
|
17
|
-
|
|
18
|
-
// prototypes
|
|
19
|
-
void receivedCallback(uint32_t from, String &msg);
|
|
20
|
-
|
|
21
|
-
painlessMesh mesh;
|
|
22
|
-
|
|
23
|
-
void setup() {
|
|
24
|
-
Serial.begin(115200);
|
|
25
|
-
mesh.setDebugMsgTypes(
|
|
26
|
-
ERROR | STARTUP |
|
|
27
|
-
CONNECTION); // set before init() so that you can see startup messages
|
|
28
|
-
|
|
29
|
-
// Channel set to 6. Make sure to use the same channel for your mesh and for
|
|
30
|
-
// you other network (STATION_SSID)
|
|
31
|
-
mesh.init(MESH_PREFIX, MESH_PASSWORD, MESH_PORT, WIFI_AP_STA, 1);
|
|
32
|
-
// Setup over the air update support
|
|
33
|
-
mesh.initOTAReceive("bridge");
|
|
34
|
-
|
|
35
|
-
mesh.stationManual(STATION_SSID, STATION_PASSWORD, STATION_PORT, station_ip);
|
|
36
|
-
// Bridge node, should (in most cases) be a root node. See [the
|
|
37
|
-
// wiki](https://gitlab.com/painlessMesh/painlessMesh/wikis/Possible-challenges-in-mesh-formation)
|
|
38
|
-
// for some background
|
|
39
|
-
mesh.setRoot(true);
|
|
40
|
-
// This node and all other nodes should ideally know the mesh contains a root,
|
|
41
|
-
// so call this on all nodes
|
|
42
|
-
mesh.setContainsRoot(true);
|
|
43
|
-
|
|
44
|
-
mesh.onReceive(&receivedCallback);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
void loop() { mesh.update(); }
|
|
48
|
-
|
|
49
|
-
void receivedCallback(uint32_t from, String &msg) {
|
|
50
|
-
Serial.printf("bridge: Received from %u msg=%s\n", from, msg.c_str());
|
|
51
|
-
}
|
|
1
|
+
//************************************************************
|
|
2
|
+
// this is a simple example that uses the painlessMesh library to
|
|
3
|
+
// connect to a node on another network. Please see the WIKI on gitlab
|
|
4
|
+
// for more details
|
|
5
|
+
// https://gitlab.com/painlessMesh/painlessMesh/wikis/bridge-between-mesh-and-another-network
|
|
6
|
+
//************************************************************
|
|
7
|
+
#include "painlessMesh.h"
|
|
8
|
+
|
|
9
|
+
#define MESH_PREFIX "whateverYouLike"
|
|
10
|
+
#define MESH_PASSWORD "somethingSneaky"
|
|
11
|
+
#define MESH_PORT 5555
|
|
12
|
+
|
|
13
|
+
#define STATION_SSID "MSYSSID"
|
|
14
|
+
#define STATION_PASSWORD "SSIDPASSWORD"
|
|
15
|
+
#define STATION_PORT 5555
|
|
16
|
+
uint8_t station_ip[4] = {192, 168, 1, 3}; // IP of the server
|
|
17
|
+
|
|
18
|
+
// prototypes
|
|
19
|
+
void receivedCallback(uint32_t from, String &msg);
|
|
20
|
+
|
|
21
|
+
painlessMesh mesh;
|
|
22
|
+
|
|
23
|
+
void setup() {
|
|
24
|
+
Serial.begin(115200);
|
|
25
|
+
mesh.setDebugMsgTypes(
|
|
26
|
+
ERROR | STARTUP |
|
|
27
|
+
CONNECTION); // set before init() so that you can see startup messages
|
|
28
|
+
|
|
29
|
+
// Channel set to 6. Make sure to use the same channel for your mesh and for
|
|
30
|
+
// you other network (STATION_SSID)
|
|
31
|
+
mesh.init(MESH_PREFIX, MESH_PASSWORD, MESH_PORT, WIFI_AP_STA, 1);
|
|
32
|
+
// Setup over the air update support
|
|
33
|
+
mesh.initOTAReceive("bridge");
|
|
34
|
+
|
|
35
|
+
mesh.stationManual(STATION_SSID, STATION_PASSWORD, STATION_PORT, station_ip);
|
|
36
|
+
// Bridge node, should (in most cases) be a root node. See [the
|
|
37
|
+
// wiki](https://gitlab.com/painlessMesh/painlessMesh/wikis/Possible-challenges-in-mesh-formation)
|
|
38
|
+
// for some background
|
|
39
|
+
mesh.setRoot(true);
|
|
40
|
+
// This node and all other nodes should ideally know the mesh contains a root,
|
|
41
|
+
// so call this on all nodes
|
|
42
|
+
mesh.setContainsRoot(true);
|
|
43
|
+
|
|
44
|
+
mesh.onReceive(&receivedCallback);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
void loop() { mesh.update(); }
|
|
48
|
+
|
|
49
|
+
void receivedCallback(uint32_t from, String &msg) {
|
|
50
|
+
Serial.printf("bridge: Received from %u msg=%s\n", from, msg.c_str());
|
|
51
|
+
}
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
#ifndef MESH_EVENT_PUBLISHER_HPP
|
|
2
|
+
#define MESH_EVENT_PUBLISHER_HPP
|
|
3
|
+
|
|
4
|
+
#include "painlessMesh.h"
|
|
5
|
+
#include <PubSubClient.h>
|
|
6
|
+
#include <ArduinoJson.h>
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* MeshEventPublisher - Publish real-time mesh events
|
|
10
|
+
*
|
|
11
|
+
* Publishes mesh state change events conforming to @alteriom/mqtt-schema v0.5.0
|
|
12
|
+
* mesh_event.schema.json specification.
|
|
13
|
+
*
|
|
14
|
+
* Event Types:
|
|
15
|
+
* - node_join: New node entered mesh
|
|
16
|
+
* - node_leave: Node left mesh (clean disconnect)
|
|
17
|
+
* - connection_lost: Direct connection failed
|
|
18
|
+
* - connection_restored: Connection recovered
|
|
19
|
+
*
|
|
20
|
+
* @author Alteriom Development Team
|
|
21
|
+
* @date October 12, 2025
|
|
22
|
+
*/
|
|
23
|
+
class MeshEventPublisher {
|
|
24
|
+
private:
|
|
25
|
+
painlessMesh& mesh;
|
|
26
|
+
PubSubClient& mqttClient;
|
|
27
|
+
String meshId;
|
|
28
|
+
String eventTopic;
|
|
29
|
+
String firmwareVersion;
|
|
30
|
+
|
|
31
|
+
public:
|
|
32
|
+
MeshEventPublisher(painlessMesh& mesh, PubSubClient& mqttClient,
|
|
33
|
+
const String& meshId, const String& topic = "alteriom/mesh/events",
|
|
34
|
+
const String& fwVersion = "GW 2.3.4")
|
|
35
|
+
: mesh(mesh), mqttClient(mqttClient), meshId(meshId),
|
|
36
|
+
eventTopic(topic), firmwareVersion(fwVersion) {}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Publish node join event
|
|
40
|
+
* Called when a new node connects to the mesh
|
|
41
|
+
*/
|
|
42
|
+
void publishNodeJoin(uint32_t nodeId) {
|
|
43
|
+
DynamicJsonDocument doc(1024);
|
|
44
|
+
|
|
45
|
+
// Envelope
|
|
46
|
+
doc["schema_version"] = 1;
|
|
47
|
+
doc["device_id"] = generateDeviceId(mesh.getNodeId());
|
|
48
|
+
doc["device_type"] = "gateway";
|
|
49
|
+
doc["timestamp"] = getISO8601Timestamp();
|
|
50
|
+
doc["firmware_version"] = firmwareVersion;
|
|
51
|
+
|
|
52
|
+
// Event
|
|
53
|
+
doc["event"] = "mesh_event";
|
|
54
|
+
doc["event_type"] = "node_join";
|
|
55
|
+
doc["mesh_id"] = meshId;
|
|
56
|
+
|
|
57
|
+
JsonArray affected = doc.createNestedArray("affected_nodes");
|
|
58
|
+
affected.add(generateDeviceId(nodeId));
|
|
59
|
+
|
|
60
|
+
JsonObject details = doc.createNestedObject("details");
|
|
61
|
+
details["total_nodes"] = mesh.getNodeList().size() + 1;
|
|
62
|
+
details["timestamp"] = getISO8601Timestamp();
|
|
63
|
+
|
|
64
|
+
publishEvent(doc, "Node Join");
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Publish node leave event
|
|
69
|
+
* Called when a node disconnects from the mesh
|
|
70
|
+
*/
|
|
71
|
+
void publishNodeLeave(uint32_t nodeId) {
|
|
72
|
+
DynamicJsonDocument doc(1024);
|
|
73
|
+
|
|
74
|
+
// Envelope
|
|
75
|
+
doc["schema_version"] = 1;
|
|
76
|
+
doc["device_id"] = generateDeviceId(mesh.getNodeId());
|
|
77
|
+
doc["device_type"] = "gateway";
|
|
78
|
+
doc["timestamp"] = getISO8601Timestamp();
|
|
79
|
+
doc["firmware_version"] = firmwareVersion;
|
|
80
|
+
|
|
81
|
+
// Event
|
|
82
|
+
doc["event"] = "mesh_event";
|
|
83
|
+
doc["event_type"] = "node_leave";
|
|
84
|
+
doc["mesh_id"] = meshId;
|
|
85
|
+
|
|
86
|
+
JsonArray affected = doc.createNestedArray("affected_nodes");
|
|
87
|
+
affected.add(generateDeviceId(nodeId));
|
|
88
|
+
|
|
89
|
+
JsonObject details = doc.createNestedObject("details");
|
|
90
|
+
details["reason"] = "connection_lost";
|
|
91
|
+
details["last_seen"] = getISO8601Timestamp();
|
|
92
|
+
details["total_nodes"] = mesh.getNodeList().size() + 1;
|
|
93
|
+
|
|
94
|
+
publishEvent(doc, "Node Leave");
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Publish connection lost event
|
|
99
|
+
* Called when a direct connection fails
|
|
100
|
+
*/
|
|
101
|
+
void publishConnectionLost(uint32_t nodeId) {
|
|
102
|
+
DynamicJsonDocument doc(1024);
|
|
103
|
+
|
|
104
|
+
doc["schema_version"] = 1;
|
|
105
|
+
doc["device_id"] = generateDeviceId(mesh.getNodeId());
|
|
106
|
+
doc["device_type"] = "gateway";
|
|
107
|
+
doc["timestamp"] = getISO8601Timestamp();
|
|
108
|
+
doc["firmware_version"] = firmwareVersion;
|
|
109
|
+
|
|
110
|
+
doc["event"] = "mesh_event";
|
|
111
|
+
doc["event_type"] = "connection_lost";
|
|
112
|
+
doc["mesh_id"] = meshId;
|
|
113
|
+
|
|
114
|
+
JsonArray affected = doc.createNestedArray("affected_nodes");
|
|
115
|
+
affected.add(generateDeviceId(nodeId));
|
|
116
|
+
|
|
117
|
+
JsonObject details = doc.createNestedObject("details");
|
|
118
|
+
details["reason"] = "timeout";
|
|
119
|
+
details["timestamp"] = getISO8601Timestamp();
|
|
120
|
+
|
|
121
|
+
publishEvent(doc, "Connection Lost");
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Publish connection restored event
|
|
126
|
+
* Called when a connection is re-established
|
|
127
|
+
*/
|
|
128
|
+
void publishConnectionRestored(uint32_t nodeId) {
|
|
129
|
+
DynamicJsonDocument doc(1024);
|
|
130
|
+
|
|
131
|
+
doc["schema_version"] = 1;
|
|
132
|
+
doc["device_id"] = generateDeviceId(mesh.getNodeId());
|
|
133
|
+
doc["device_type"] = "gateway";
|
|
134
|
+
doc["timestamp"] = getISO8601Timestamp();
|
|
135
|
+
doc["firmware_version"] = firmwareVersion;
|
|
136
|
+
|
|
137
|
+
doc["event"] = "mesh_event";
|
|
138
|
+
doc["event_type"] = "connection_restored";
|
|
139
|
+
doc["mesh_id"] = meshId;
|
|
140
|
+
|
|
141
|
+
JsonArray affected = doc.createNestedArray("affected_nodes");
|
|
142
|
+
affected.add(generateDeviceId(nodeId));
|
|
143
|
+
|
|
144
|
+
JsonObject details = doc.createNestedObject("details");
|
|
145
|
+
details["timestamp"] = getISO8601Timestamp();
|
|
146
|
+
|
|
147
|
+
publishEvent(doc, "Connection Restored");
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Publish network split event
|
|
152
|
+
* Called when mesh partitions into separate segments
|
|
153
|
+
*/
|
|
154
|
+
void publishNetworkSplit() {
|
|
155
|
+
DynamicJsonDocument doc(1024);
|
|
156
|
+
|
|
157
|
+
doc["schema_version"] = 1;
|
|
158
|
+
doc["device_id"] = generateDeviceId(mesh.getNodeId());
|
|
159
|
+
doc["device_type"] = "gateway";
|
|
160
|
+
doc["timestamp"] = getISO8601Timestamp();
|
|
161
|
+
doc["firmware_version"] = firmwareVersion;
|
|
162
|
+
|
|
163
|
+
doc["event"] = "mesh_event";
|
|
164
|
+
doc["event_type"] = "network_split";
|
|
165
|
+
doc["mesh_id"] = meshId;
|
|
166
|
+
|
|
167
|
+
JsonArray affected = doc.createNestedArray("affected_nodes");
|
|
168
|
+
// TODO: Detect which nodes are in different partitions
|
|
169
|
+
|
|
170
|
+
JsonObject details = doc.createNestedObject("details");
|
|
171
|
+
details["timestamp"] = getISO8601Timestamp();
|
|
172
|
+
details["partition_count"] = 2; // TODO: Calculate actual partitions
|
|
173
|
+
|
|
174
|
+
publishEvent(doc, "Network Split");
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Publish network merged event
|
|
179
|
+
* Called when mesh partitions rejoin
|
|
180
|
+
*/
|
|
181
|
+
void publishNetworkMerged() {
|
|
182
|
+
DynamicJsonDocument doc(1024);
|
|
183
|
+
|
|
184
|
+
doc["schema_version"] = 1;
|
|
185
|
+
doc["device_id"] = generateDeviceId(mesh.getNodeId());
|
|
186
|
+
doc["device_type"] = "gateway";
|
|
187
|
+
doc["timestamp"] = getISO8601Timestamp();
|
|
188
|
+
doc["firmware_version"] = firmwareVersion;
|
|
189
|
+
|
|
190
|
+
doc["event"] = "mesh_event";
|
|
191
|
+
doc["event_type"] = "network_merged";
|
|
192
|
+
doc["mesh_id"] = meshId;
|
|
193
|
+
|
|
194
|
+
JsonArray affected = doc.createNestedArray("affected_nodes");
|
|
195
|
+
// TODO: List nodes that rejoined
|
|
196
|
+
|
|
197
|
+
JsonObject details = doc.createNestedObject("details");
|
|
198
|
+
details["timestamp"] = getISO8601Timestamp();
|
|
199
|
+
|
|
200
|
+
publishEvent(doc, "Network Merged");
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
private:
|
|
204
|
+
/**
|
|
205
|
+
* Internal method to publish event to MQTT
|
|
206
|
+
*/
|
|
207
|
+
void publishEvent(const DynamicJsonDocument& doc, const char* eventName) {
|
|
208
|
+
if (!mqttClient.connected()) {
|
|
209
|
+
Serial.printf("[Event] ⚠️ MQTT not connected, cannot publish %s\n", eventName);
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
String payload;
|
|
214
|
+
serializeJson(doc, payload);
|
|
215
|
+
|
|
216
|
+
bool success = mqttClient.publish(eventTopic.c_str(), payload.c_str());
|
|
217
|
+
|
|
218
|
+
if (success) {
|
|
219
|
+
Serial.printf("[Event] ✅ Published: %s (node: %s)\n",
|
|
220
|
+
eventName,
|
|
221
|
+
doc["affected_nodes"][0].as<const char*>());
|
|
222
|
+
} else {
|
|
223
|
+
Serial.printf("[Event] ❌ Failed to publish: %s\n", eventName);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Generate device ID in Alteriom format (ALT-XXXXXXXXXXXX)
|
|
229
|
+
*/
|
|
230
|
+
String generateDeviceId(uint32_t nodeId) {
|
|
231
|
+
char deviceId[20];
|
|
232
|
+
snprintf(deviceId, sizeof(deviceId), "ALT-%012X", nodeId);
|
|
233
|
+
return String(deviceId);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Get current timestamp in ISO 8601 format
|
|
238
|
+
* TODO: Use proper NTP time if available
|
|
239
|
+
*/
|
|
240
|
+
String getISO8601Timestamp() {
|
|
241
|
+
// For now, use epoch-based timestamp
|
|
242
|
+
// In production, this should use actual NTP-synced time
|
|
243
|
+
unsigned long ms = millis();
|
|
244
|
+
char timestamp[32];
|
|
245
|
+
snprintf(timestamp, sizeof(timestamp), "2025-10-12T%02lu:%02lu:%02luZ",
|
|
246
|
+
(ms / 3600000) % 24,
|
|
247
|
+
(ms / 60000) % 60,
|
|
248
|
+
(ms / 1000) % 60);
|
|
249
|
+
return String(timestamp);
|
|
250
|
+
}
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
#endif // MESH_EVENT_PUBLISHER_HPP
|