@alteriom/painlessmesh 1.9.19 → 1.10.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 (51) hide show
  1. package/CHANGELOG.md +168 -0
  2. package/README.md +102 -63
  3. package/RELEASE_GUIDE.md +147 -8
  4. package/examples/alteriom/README.md +4 -4
  5. package/examples/alteriom/alteriom_custom_package_template.hpp +320 -0
  6. package/examples/alteriom/alteriom_sensor_package.hpp +1 -1
  7. package/examples/alteriom/mppt_example/alteriom_mppt_example.ino +208 -0
  8. package/examples/bridge_failover/bridge_failover.ino +17 -0
  9. package/examples/sendToInternet/CMakeLists.txt +54 -0
  10. package/examples/sendToInternet/PC_NODE_README.md +517 -0
  11. package/examples/sendToInternet/README.md +39 -1
  12. package/examples/sendToInternet/build.sh +153 -0
  13. package/examples/sendToInternet/mock_server_test.ino +361 -0
  14. package/examples/sendToInternet/pc_mesh_node.cpp +361 -0
  15. package/examples/tcpRetryConfig/README.md +110 -0
  16. package/examples/tcpRetryConfig/platformio.ini +26 -0
  17. package/examples/tcpRetryConfig/tcpRetryConfig.ino +154 -0
  18. package/keywords.txt +3 -0
  19. package/library.json +4 -1
  20. package/library.properties +1 -1
  21. package/package.json +3 -3
  22. package/src/AlteriomPainlessMesh.h +6 -14
  23. package/src/arduino/wifi.hpp +352 -114
  24. package/src/connection.cpp +10 -0
  25. package/src/painlessMesh.h +2 -15
  26. package/src/painlessTaskOptions.h +9 -0
  27. package/src/painlessmesh/buffer.hpp +4 -1
  28. package/src/painlessmesh/configuration.hpp +13 -2
  29. package/src/painlessmesh/connection.hpp +36 -21
  30. package/src/painlessmesh/gateway.hpp +0 -1061
  31. package/src/painlessmesh/mesh.hpp +102 -107
  32. package/src/painlessmesh/message_queue.hpp +25 -15
  33. package/src/painlessmesh/metrics.hpp +2 -262
  34. package/src/painlessmesh/plugin.hpp +27 -5
  35. package/src/painlessmesh/tcp.hpp +158 -29
  36. package/src/painlessmesh/validation.hpp +0 -143
  37. package/docs/README.md +0 -132
  38. package/docs/alteriom/overview.md +0 -531
  39. package/docs/api/core-api.md +0 -607
  40. package/docs/api/shared-gateway.md +0 -1207
  41. package/docs/architecture/mesh-architecture.md +0 -399
  42. package/docs/architecture/plugin-system.md +0 -517
  43. package/docs/getting-started/arduino-manual-install.md +0 -313
  44. package/docs/getting-started/first-mesh.md +0 -410
  45. package/docs/getting-started/installation.md +0 -275
  46. package/docs/getting-started/quickstart.md +0 -158
  47. package/docs/troubleshooting/common-issues.md +0 -679
  48. package/docs/troubleshooting/debugging.md +0 -455
  49. package/docs/troubleshooting/external-device-connection.md +0 -283
  50. package/docs/troubleshooting/faq.md +0 -574
  51. package/docs/tutorials/basic-examples.md +0 -718
@@ -0,0 +1,320 @@
1
+ #ifndef ALTERIOM_CUSTOM_PACKAGE_TEMPLATE_HPP
2
+ #define ALTERIOM_CUSTOM_PACKAGE_TEMPLATE_HPP
3
+
4
+ #include "painlessmesh/plugin.hpp"
5
+
6
+ /**
7
+ * @file alteriom_custom_package_template.hpp
8
+ * @brief Template and example for creating custom Alteriom packages
9
+ *
10
+ * HOW TO CREATE A CUSTOM PACKAGE
11
+ * ===============================
12
+ *
13
+ * This file serves two purposes:
14
+ * 1. A step-by-step guide to creating custom painlessMesh packages
15
+ * 2. A concrete example: MpptPackage for MPPT solar charge controllers
16
+ *
17
+ * QUICK START
18
+ * -----------
19
+ * To create your own custom package:
20
+ * 1. Pick an unused Type ID from the table below (use 206+ range)
21
+ * 2. Choose a base class: BroadcastPackage (all nodes) or SinglePackage (one
22
+ * node)
23
+ * 3. Add your data fields with appropriate types
24
+ * 4. Implement the JSON constructor and addTo() method
25
+ * 5. Add a test in test/catch/catch_custom_package.cpp
26
+ *
27
+ * RESERVED TYPE IDS
28
+ * -----------------
29
+ * The following IDs are already used; do NOT reuse them:
30
+ *
31
+ * 200 : SensorPackage (environmental sensors: temp, humidity, pressure)
32
+ * 202 : StatusPackage (device health and configuration)
33
+ * 204 : MetricsPackage (network performance metrics)
34
+ * 205 : MpptPackage (MPPT solar charge controller data) <-- this file
35
+ * 400 : CommandPackage (device control commands)
36
+ * 600 : MeshNodeListPackage
37
+ * 601 : MeshTopologyPackage
38
+ * 602 : MeshAlertPackage
39
+ * 603 : MeshBridgePackage
40
+ * 604 : EnhancedStatusPackage
41
+ * 605 : HealthCheckPackage
42
+ * 610 : BridgeStatusPackage
43
+ * 611 : BridgeElectionPackage
44
+ * 612 : BridgeTakeoverPackage
45
+ * 614 : NTPTimeSyncPackage
46
+ *
47
+ * Available ranges: 206-399 (add your package here and update this table).
48
+ *
49
+ *
50
+ * CHOOSING BASE CLASS
51
+ * -------------------
52
+ *
53
+ * BroadcastPackage – sent to every node in the mesh.
54
+ * Use for: sensor readings, status updates, telemetry data.
55
+ * Base fields: from, routing (BROADCAST), type (noJsonFields = 3)
56
+ *
57
+ * SinglePackage – sent to one specific destination node.
58
+ * Use for: commands, acknowledgements, targeted responses.
59
+ * Base fields: from, dest, routing (SINGLE), type (noJsonFields = 4)
60
+ *
61
+ *
62
+ * FIELD TYPE GUIDELINES
63
+ * ---------------------
64
+ *
65
+ * Choose types appropriate for your platform:
66
+ *
67
+ * uint8_t – flags, states, small counts (0-255)
68
+ * uint16_t – larger counts, port numbers, voltages in mV (0-65535)
69
+ * uint32_t – device IDs, Unix timestamps, large counters
70
+ * int8_t – signed small values, e.g. temperature in °C (-128 to +127)
71
+ * float – measured values requiring decimals (4 bytes; fine on both
72
+ * ESP8266 and ESP32)
73
+ * double – high-precision measurements (8 bytes; prefer float on ESP8266)
74
+ * TSTRING – text strings (always use TSTRING, NOT Arduino String)
75
+ * bool – boolean flags; see BOOLEAN NAMING CONVENTION below
76
+ *
77
+ * BOOLEAN NAMING CONVENTION
78
+ * -------------------------
79
+ * *Set suffix – configuration data has been provided
80
+ * e.g., serverAddressSet = true
81
+ * *Enabled suffix – feature is currently active/on
82
+ * e.g., loggingEnabled = true
83
+ * is* prefix – current runtime state
84
+ * e.g., isCharging = true
85
+ *
86
+ *
87
+ * JSON FIELD NAMING
88
+ * -----------------
89
+ *
90
+ * Use SHORT keys to minimise over-the-air message sizes:
91
+ *
92
+ * batteryVoltage -> "bv"
93
+ * solarCurrent -> "sc"
94
+ * chargeState -> "cs"
95
+ * deviceId -> "did"
96
+ * timestamp -> "ts"
97
+ *
98
+ * Always document the mapping in a comment near the field declaration.
99
+ *
100
+ *
101
+ * TIME FIELDS
102
+ * -----------
103
+ *
104
+ * For interval / duration fields, follow the Alteriom time convention:
105
+ * - Store internally in milliseconds (uint32_t)
106
+ * - Serialise both a _ms and a _s variant in JSON
107
+ * - Deserialise from the _ms variant only
108
+ *
109
+ * Timestamp fields (Unix epoch seconds) are an exception: single field, no
110
+ * dual-unit serialisation needed.
111
+ *
112
+ *
113
+ * ARDUINOJSON COMPATIBILITY
114
+ * -------------------------
115
+ *
116
+ * Always wrap the jsonObjectSize() method in an
117
+ * #if ARDUINOJSON_VERSION_MAJOR < 7 guard. ArduinoJson v7 computes document
118
+ * sizes automatically; v6 requires an explicit capacity hint.
119
+ *
120
+ * The formula is:
121
+ * JSON_OBJECT_SIZE(noJsonFields + <number of your own fields>)
122
+ * + <total length of all TSTRING fields>
123
+ *
124
+ *
125
+ * MINIMAL PACKAGE TEMPLATE
126
+ * ========================
127
+ *
128
+ * Copy this skeleton and replace the placeholder names / IDs:
129
+ *
130
+ * @code
131
+ * namespace alteriom {
132
+ *
133
+ * class MyCustomPackage : public painlessmesh::plugin::BroadcastPackage {
134
+ * public:
135
+ * // --- Your data fields ---
136
+ * uint32_t myId = 0;
137
+ * float myValue = 0.0f;
138
+ * TSTRING myText = "";
139
+ *
140
+ * // MQTT message_type (set to your chosen type ID)
141
+ * uint16_t messageType = 206;
142
+ *
143
+ * MyCustomPackage() : BroadcastPackage(206) {}
144
+ *
145
+ * MyCustomPackage(JsonObject jsonObj) : BroadcastPackage(jsonObj) {
146
+ * myId = jsonObj["id"];
147
+ * myValue = jsonObj["val"];
148
+ * myText = jsonObj["txt"].as<TSTRING>();
149
+ * messageType = jsonObj["message_type"] | 206;
150
+ * }
151
+ *
152
+ * JsonObject addTo(JsonObject&& jsonObj) const {
153
+ * jsonObj = BroadcastPackage::addTo(std::move(jsonObj));
154
+ * jsonObj["id"] = myId;
155
+ * jsonObj["val"] = myValue;
156
+ * jsonObj["txt"] = myText;
157
+ * jsonObj["message_type"] = messageType;
158
+ * return jsonObj;
159
+ * }
160
+ *
161
+ * #if ARDUINOJSON_VERSION_MAJOR < 7
162
+ * size_t jsonObjectSize() const {
163
+ * // noJsonFields covers base-class fields; 3 = number of YOUR fields
164
+ * return JSON_OBJECT_SIZE(noJsonFields + 3) + myText.length();
165
+ * }
166
+ * #endif
167
+ * };
168
+ *
169
+ * } // namespace alteriom
170
+ * @endcode
171
+ *
172
+ *
173
+ * CONCRETE EXAMPLE: MpptPackage
174
+ * ==============================
175
+ *
176
+ * The MpptPackage (Type 205) transmits real-time telemetry from an MPPT solar
177
+ * charge controller (e.g. Renegy, Epever, Victron). It is a BroadcastPackage
178
+ * so every node in the mesh receives the data automatically.
179
+ *
180
+ * Fields at a glance:
181
+ *
182
+ * solarVoltage (float, V) – PV panel open-circuit / input voltage
183
+ * solarCurrent (float, A) – PV panel current
184
+ * solarPower (uint16_t, W) – PV panel instantaneous power
185
+ * batteryVoltage (float, V) – Battery terminal voltage
186
+ * batterySOC (uint8_t, %) – State of charge 0–100
187
+ * loadVoltage (float, V) – Load output voltage
188
+ * loadCurrent (float, A) – Load output current
189
+ * chargeState (uint8_t) – Controller state (see ChargeState enum)
190
+ * controllerTemp (int8_t, °C) – Internal controller temperature
191
+ * deviceId (uint32_t) – Unique hardware identifier
192
+ * timestamp (uint32_t) – Unix timestamp of the reading
193
+ */
194
+
195
+ namespace alteriom {
196
+
197
+ /**
198
+ * @brief Charge state values for MpptPackage::chargeState
199
+ */
200
+ enum ChargeState : uint8_t {
201
+ CHARGE_OFF = 0, ///< Charging disabled
202
+ CHARGE_NORMAL = 1, ///< Normal PWM charging
203
+ CHARGE_MPPT = 2, ///< Maximum Power Point Tracking active
204
+ CHARGE_EQUALIZE = 3, ///< Equalisation charge (battery maintenance)
205
+ CHARGE_BOOST = 4, ///< Boost / bulk charge stage
206
+ CHARGE_FLOAT = 5, ///< Float / maintenance stage
207
+ CHARGE_LIMITED = 6 ///< Current-limited charging
208
+ };
209
+
210
+ /**
211
+ * @brief Real-time telemetry from an MPPT solar charge controller
212
+ *
213
+ * Broadcasts voltage, current, power and status from an MPPT charge controller
214
+ * to all nodes in the mesh (e.g. for logging, display, or load management).
215
+ *
216
+ * Adapting for your controller
217
+ * ----------------------------
218
+ * Most MPPT controllers expose data over RS-232/RS-485 or I²C. Read the raw
219
+ * values from your hardware, assign them to the struct fields, then call
220
+ * sendBroadcast() as shown in alteriom_mppt_example.ino.
221
+ *
222
+ * Type ID: 205
223
+ */
224
+ class MpptPackage : public painlessmesh::plugin::BroadcastPackage {
225
+ public:
226
+ // JSON key : "sv" – PV panel voltage in Volts
227
+ float solarVoltage = 0.0f;
228
+ // JSON key : "sc" – PV panel current in Amperes
229
+ float solarCurrent = 0.0f;
230
+ // JSON key : "sp" – PV panel power in Watts
231
+ uint16_t solarPower = 0;
232
+ // JSON key : "bv" – Battery terminal voltage in Volts
233
+ float batteryVoltage = 0.0f;
234
+ // JSON key : "bsoc" – Battery state of charge, 0–100 %
235
+ uint8_t batterySOC = 0;
236
+ // JSON key : "lv" – Load output voltage in Volts
237
+ float loadVoltage = 0.0f;
238
+ // JSON key : "lc" – Load output current in Amperes
239
+ float loadCurrent = 0.0f;
240
+ // JSON key : "cs" – Charge controller state (see ChargeState enum)
241
+ uint8_t chargeState = CHARGE_OFF;
242
+ // JSON key : "ct" – Controller internal temperature in °C (signed)
243
+ int8_t controllerTemp = 0;
244
+ // JSON key : "did" – Unique hardware / node identifier
245
+ uint32_t deviceId = 0;
246
+ // JSON key : "ts" – Unix timestamp of measurement (seconds since epoch)
247
+ uint32_t timestamp = 0;
248
+
249
+ // MQTT Schema message_type for fast classification at the bridge
250
+ uint16_t messageType = 205; // MPPT_DATA
251
+
252
+ // -------------------------------------------------------------------------
253
+ // Constructors
254
+ // -------------------------------------------------------------------------
255
+
256
+ MpptPackage() : BroadcastPackage(205) {}
257
+
258
+ /**
259
+ * @brief Deserialise from a JSON object received over the mesh
260
+ *
261
+ * @param jsonObj Parsed JSON object (ArduinoJson JsonObject)
262
+ */
263
+ MpptPackage(JsonObject jsonObj) : BroadcastPackage(jsonObj) {
264
+ solarVoltage = jsonObj["sv"];
265
+ solarCurrent = jsonObj["sc"];
266
+ solarPower = jsonObj["sp"];
267
+ batteryVoltage = jsonObj["bv"];
268
+ batterySOC = jsonObj["bsoc"];
269
+ loadVoltage = jsonObj["lv"];
270
+ loadCurrent = jsonObj["lc"];
271
+ chargeState = jsonObj["cs"];
272
+ controllerTemp = jsonObj["ct"];
273
+ deviceId = jsonObj["did"];
274
+ timestamp = jsonObj["ts"];
275
+ messageType = jsonObj["message_type"] | 205;
276
+ }
277
+
278
+ // -------------------------------------------------------------------------
279
+ // Serialisation
280
+ // -------------------------------------------------------------------------
281
+
282
+ /**
283
+ * @brief Serialise this package into the provided JSON object
284
+ *
285
+ * Call addTo() on a freshly created JsonObject, then serialise with
286
+ * ArduinoJson's serializeJson() before passing the result to
287
+ * mesh.sendBroadcast().
288
+ */
289
+ JsonObject addTo(JsonObject&& jsonObj) const {
290
+ jsonObj = BroadcastPackage::addTo(std::move(jsonObj));
291
+ jsonObj["sv"] = solarVoltage;
292
+ jsonObj["sc"] = solarCurrent;
293
+ jsonObj["sp"] = solarPower;
294
+ jsonObj["bv"] = batteryVoltage;
295
+ jsonObj["bsoc"] = batterySOC;
296
+ jsonObj["lv"] = loadVoltage;
297
+ jsonObj["lc"] = loadCurrent;
298
+ jsonObj["cs"] = chargeState;
299
+ jsonObj["ct"] = controllerTemp;
300
+ jsonObj["did"] = deviceId;
301
+ jsonObj["ts"] = timestamp;
302
+ jsonObj["message_type"] = messageType;
303
+ return jsonObj;
304
+ }
305
+
306
+ #if ARDUINOJSON_VERSION_MAJOR < 7
307
+ /**
308
+ * @brief Required capacity hint for ArduinoJson v6
309
+ *
310
+ * noJsonFields covers the 3 base-class fields (from, routing, type).
311
+ * The +12 accounts for the 12 fields declared in this class.
312
+ * No TSTRING fields, so no extra string length term.
313
+ */
314
+ size_t jsonObjectSize() const { return JSON_OBJECT_SIZE(noJsonFields + 12); }
315
+ #endif
316
+ };
317
+
318
+ } // namespace alteriom
319
+
320
+ #endif // ALTERIOM_CUSTOM_PACKAGE_TEMPLATE_HPP
@@ -520,7 +520,7 @@ class StatusPackage : public painlessmesh::plugin::BroadcastPackage {
520
520
  *
521
521
  * This is an extended version of StatusPackage that includes additional
522
522
  * mesh statistics, performance metrics, and alerting capabilities.
523
- * Type ID 203 is used to distinguish from the basic StatusPackage (202).
523
+ * Type ID 604 is used to distinguish from the basic StatusPackage (202).
524
524
  */
525
525
  class EnhancedStatusPackage : public painlessmesh::plugin::BroadcastPackage {
526
526
  public:
@@ -0,0 +1,208 @@
1
+ //************************************************************
2
+ // AlteriomPainlessMesh – MPPT Charge Controller Example
3
+ //
4
+ // This sketch shows how to use a custom package (MpptPackage)
5
+ // to broadcast solar charge-controller telemetry over a
6
+ // painlessMesh network.
7
+ //
8
+ // Hardware assumptions:
9
+ // - An MPPT charge controller connected via Serial / RS-485
10
+ // (e.g. Renegy, Epever, Victron BlueSolar, etc.)
11
+ // - ESP8266 or ESP32 running this firmware
12
+ //
13
+ // How it works:
14
+ // 1. Every 10 seconds the node reads the charge controller
15
+ // and broadcasts an MpptPackage to all mesh nodes.
16
+ // 2. Any node that receives an MpptPackage prints the values
17
+ // to Serial (bridge nodes can forward them to MQTT/HTTP).
18
+ // 3. A CommandPackage handler is included so a bridge node can
19
+ // request an immediate reading (command code 10).
20
+ //
21
+ // See alteriom_custom_package_template.hpp for a step-by-step
22
+ // guide to creating your own custom packages.
23
+ //************************************************************
24
+
25
+ #include "AlteriomPainlessMesh.h"
26
+ #include "alteriom_custom_package_template.hpp"
27
+ #include "alteriom_sensor_package.hpp" // for CommandPackage
28
+
29
+ #define MESH_PREFIX "AlteriomMesh"
30
+ #define MESH_PASSWORD "somethingSneaky"
31
+ #define MESH_PORT 5555
32
+
33
+ // How often to broadcast MPPT data (milliseconds)
34
+ #define MPPT_SEND_INTERVAL 10000
35
+
36
+ Scheduler userScheduler;
37
+ painlessMesh mesh;
38
+
39
+ using namespace alteriom;
40
+
41
+ // ---------------------------------------------------------------------------
42
+ // Forward declarations
43
+ // ---------------------------------------------------------------------------
44
+ void sendMpptData();
45
+ void handleIncomingPackage(uint32_t from, String& msg);
46
+ void handleCommandPackage(CommandPackage& cmd);
47
+ void newConnectionCallback(uint32_t nodeId);
48
+ void changedConnectionCallback();
49
+ void nodeTimeAdjustedCallback(int32_t offset);
50
+
51
+ // ---------------------------------------------------------------------------
52
+ // Scheduled tasks
53
+ // ---------------------------------------------------------------------------
54
+ Task taskSendMppt(MPPT_SEND_INTERVAL, TASK_FOREVER, &sendMpptData);
55
+
56
+ // ---------------------------------------------------------------------------
57
+ // Stub: read data from your MPPT controller
58
+ //
59
+ // Replace this function body with real hardware reads, e.g.:
60
+ // - Modbus RTU over RS-485 (using ModbusMaster library)
61
+ // - Vendor protocol over SoftwareSerial / HardwareSerial
62
+ // - I²C registers for controllers that support it
63
+ // ---------------------------------------------------------------------------
64
+ MpptPackage readMpptController() {
65
+ MpptPackage data;
66
+ data.from = mesh.getNodeId();
67
+ data.deviceId = mesh.getNodeId();
68
+ data.timestamp = mesh.getNodeTime() / 1000; // convert µs → s
69
+
70
+ // --- Replace these lines with real hardware reads ---
71
+ data.solarVoltage = 18.5f; // V (example: 18.5 V PV panel)
72
+ data.solarCurrent = 5.2f; // A
73
+ data.solarPower = 96; // W (= solarVoltage * solarCurrent)
74
+ data.batteryVoltage = 12.6f; // V (12 V lead-acid, ~80 % SOC)
75
+ data.batterySOC = 80; // %
76
+ data.loadVoltage = 12.4f; // V
77
+ data.loadCurrent = 2.1f; // A
78
+ data.chargeState = CHARGE_MPPT;
79
+ data.controllerTemp = 32; // °C
80
+ // ----------------------------------------------------
81
+
82
+ return data;
83
+ }
84
+
85
+ // ---------------------------------------------------------------------------
86
+ // Setup
87
+ // ---------------------------------------------------------------------------
88
+ void setup() {
89
+ Serial.begin(115200);
90
+
91
+ mesh.setDebugMsgTypes(ERROR | STARTUP | CONNECTION);
92
+ mesh.init(MESH_PREFIX, MESH_PASSWORD, &userScheduler, MESH_PORT);
93
+ mesh.onReceive(&handleIncomingPackage);
94
+ mesh.onNewConnection(&newConnectionCallback);
95
+ mesh.onChangedConnections(&changedConnectionCallback);
96
+ mesh.onNodeTimeAdjusted(&nodeTimeAdjustedCallback);
97
+
98
+ userScheduler.addTask(taskSendMppt);
99
+ taskSendMppt.enable();
100
+
101
+ Serial.println("MPPT node initialised");
102
+ }
103
+
104
+ // ---------------------------------------------------------------------------
105
+ // Loop
106
+ // ---------------------------------------------------------------------------
107
+ void loop() {
108
+ mesh.update();
109
+ }
110
+
111
+ // ---------------------------------------------------------------------------
112
+ // Broadcast MPPT telemetry
113
+ // ---------------------------------------------------------------------------
114
+ void sendMpptData() {
115
+ MpptPackage data = readMpptController();
116
+
117
+ // Serialise with ArduinoJson v7 API
118
+ JsonDocument doc;
119
+ JsonObject obj = doc.to<JsonObject>();
120
+ data.addTo(std::move(obj));
121
+
122
+ String msg;
123
+ serializeJson(doc, msg);
124
+
125
+ if (mesh.sendBroadcast(msg)) {
126
+ Serial.printf(
127
+ "MPPT sent: PV=%.1fV/%.1fA/%dW Bat=%.1fV/%d%% "
128
+ "Load=%.1fV/%.1fA State=%d Temp=%d°C\n",
129
+ data.solarVoltage, data.solarCurrent, data.solarPower,
130
+ data.batteryVoltage, data.batterySOC,
131
+ data.loadVoltage, data.loadCurrent,
132
+ data.chargeState, data.controllerTemp);
133
+ } else {
134
+ Serial.println("sendBroadcast failed – check mesh connectivity");
135
+ }
136
+ }
137
+
138
+ // ---------------------------------------------------------------------------
139
+ // Receive handler
140
+ // ---------------------------------------------------------------------------
141
+ void handleIncomingPackage(uint32_t from, String& msg) {
142
+ JsonDocument doc;
143
+ if (deserializeJson(doc, msg) != DeserializationError::Ok) {
144
+ Serial.printf("JSON parse error from %u\n", from);
145
+ return;
146
+ }
147
+
148
+ JsonObject obj = doc.as<JsonObject>();
149
+ uint16_t msgType = obj["type"];
150
+
151
+ switch (msgType) {
152
+ case 205: { // MpptPackage
153
+ MpptPackage received(obj);
154
+ Serial.printf(
155
+ "MPPT from %u: PV=%.1fV/%.1fA/%dW Bat=%.1fV/%d%% "
156
+ "Load=%.1fV/%.1fA State=%d Temp=%d°C\n",
157
+ received.from,
158
+ received.solarVoltage, received.solarCurrent, received.solarPower,
159
+ received.batteryVoltage, received.batterySOC,
160
+ received.loadVoltage, received.loadCurrent,
161
+ received.chargeState, received.controllerTemp);
162
+ break;
163
+ }
164
+
165
+ case 400: { // CommandPackage
166
+ CommandPackage cmd(obj);
167
+ if (cmd.dest == mesh.getNodeId()) {
168
+ handleCommandPackage(cmd);
169
+ }
170
+ break;
171
+ }
172
+
173
+ default:
174
+ // Silently ignore unknown types
175
+ break;
176
+ }
177
+ }
178
+
179
+ // ---------------------------------------------------------------------------
180
+ // Command handler
181
+ // ---------------------------------------------------------------------------
182
+ void handleCommandPackage(CommandPackage& cmd) {
183
+ switch (cmd.command) {
184
+ case 10: // Immediate MPPT data request
185
+ Serial.printf("Immediate data request from command %u\n", cmd.commandId);
186
+ sendMpptData();
187
+ break;
188
+
189
+ default:
190
+ Serial.printf("Unknown command %d\n", cmd.command);
191
+ break;
192
+ }
193
+ }
194
+
195
+ // ---------------------------------------------------------------------------
196
+ // Mesh callbacks
197
+ // ---------------------------------------------------------------------------
198
+ void newConnectionCallback(uint32_t nodeId) {
199
+ Serial.printf("New connection: nodeId = %u\n", nodeId);
200
+ }
201
+
202
+ void changedConnectionCallback() {
203
+ Serial.println("Connections changed");
204
+ }
205
+
206
+ void nodeTimeAdjustedCallback(int32_t offset) {
207
+ Serial.printf("Time adjusted: offset = %d µs\n", offset);
208
+ }
@@ -238,6 +238,23 @@ void setup() {
238
238
  mesh.onBridgeStatusChanged(&bridgeStatusCallback);
239
239
  mesh.onBridgeRoleChanged(&bridgeRoleCallback);
240
240
 
241
+ // Monitor bridge coordination (fires every ~30s per bridge)
242
+ mesh.onBridgeCoordination(
243
+ [](const painlessmesh::plugin::BridgeCoordinationPackage& pkg, uint32_t fromNode) {
244
+ Serial.printf("Bridge %u: priority=%d, load=%d%%\n",
245
+ fromNode, pkg.priority, pkg.load);
246
+ }
247
+ );
248
+
249
+ // Get notified when bridge state changes (new/updated/lost)
250
+ mesh.onBridgeCoordinationChanged(
251
+ [](const painlessmesh::plugin::BridgeCoordinationPackage& pkg, uint32_t fromNode,
252
+ TSTRING changeType) {
253
+ Serial.printf("Bridge %s: %u (role=%s)\n",
254
+ changeType.c_str(), fromNode, pkg.role.c_str());
255
+ }
256
+ );
257
+
241
258
  // Configure logging
242
259
  mesh.setDebugMsgTypes(ERROR | STARTUP | CONNECTION);
243
260
 
@@ -0,0 +1,54 @@
1
+ cmake_minimum_required(VERSION 3.10)
2
+ project(PCMeshNode)
3
+
4
+ set(CMAKE_CXX_STANDARD 14)
5
+ set(CMAKE_CXX_STANDARD_REQUIRED ON)
6
+ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
7
+
8
+ # Find Boost (compatible with different CMake versions and platforms)
9
+ FIND_PACKAGE(Boost 1.66 COMPONENTS system)
10
+
11
+ IF(Boost_FOUND)
12
+ INCLUDE_DIRECTORIES(${Boost_INCLUDE_DIRS})
13
+ LINK_DIRECTORIES(${Boost_LIBRARY_DIRS})
14
+ message(STATUS "Boost found: ${Boost_VERSION}")
15
+ message(STATUS "Boost include dirs: ${Boost_INCLUDE_DIRS}")
16
+ ELSE()
17
+ message(FATAL_ERROR "Boost not found! Please install Boost development libraries:\n"
18
+ " Ubuntu/Debian: sudo apt-get install libboost-dev libboost-system-dev\n"
19
+ " macOS: brew install boost\n"
20
+ " Windows: See https://www.boost.org/")
21
+ ENDIF()
22
+
23
+ # Set compiler flags
24
+ SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread")
25
+
26
+ # Add executable
27
+ add_executable(pc_mesh_node
28
+ pc_mesh_node.cpp
29
+ ../../test/catch/fake_serial.cpp
30
+ ../../src/scheduler.cpp
31
+ )
32
+
33
+ # Include directories
34
+ target_include_directories(pc_mesh_node PUBLIC
35
+ ../../test/include/
36
+ ../../test/boost/
37
+ ../../test/ArduinoJson/src/
38
+ ../../test/TaskScheduler/src/
39
+ ../../src/
40
+ )
41
+
42
+ # Link libraries
43
+ TARGET_LINK_LIBRARIES(pc_mesh_node ${Boost_LIBRARIES})
44
+
45
+ # Print instructions
46
+ message(STATUS "")
47
+ message(STATUS "===========================================")
48
+ message(STATUS "PC Mesh Node - Build Configuration")
49
+ message(STATUS "===========================================")
50
+ message(STATUS "Build with: cmake . && make")
51
+ message(STATUS "Run with: ./pc_mesh_node <bridge_ip> <bridge_port>")
52
+ message(STATUS "Example: ./pc_mesh_node 192.168.1.100 5555")
53
+ message(STATUS "===========================================")
54
+ message(STATUS "")