@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,10 @@
1
+ #include "painlessmesh/connection.hpp"
2
+
3
+ namespace painlessmesh {
4
+ namespace tcp {
5
+
6
+ uint32_t lastScheduledDeletionTime = 0;
7
+ painlessmesh::buffer::temp_buffer_t shared_buffer;
8
+
9
+ } // namespace tcp
10
+ } // namespace painlessmesh
@@ -5,8 +5,8 @@
5
5
  * @file painlessMesh.h
6
6
  * @brief Main header file for Alteriom painlessMesh library
7
7
  *
8
- * @version 1.9.19
9
- * @date 2025-12-21
8
+ * @version 1.10.0
9
+ * @date 2026-08-12
10
10
  *
11
11
  * painlessMesh is a user-friendly library for creating mesh networks with
12
12
  * ESP8266 and ESP32 devices. This Alteriom fork includes additional packages
@@ -44,19 +44,6 @@
44
44
  #include "painlessmesh/ota.hpp"
45
45
  #endif
46
46
 
47
- // Include improvement modules when enabled
48
- #ifdef PAINLESSMESH_ENABLE_VALIDATION
49
- #include "painlessmesh/validation.hpp"
50
- #endif
51
-
52
- #ifdef PAINLESSMESH_ENABLE_METRICS
53
- #include "painlessmesh/metrics.hpp"
54
- #endif
55
-
56
- #ifdef PAINLESSMESH_ENABLE_MEMORY_OPTIMIZATION
57
- #include "painlessmesh/memory.hpp"
58
- #endif
59
-
60
47
  #include "painlessmesh/buffer.hpp"
61
48
  #include "painlessmesh/layout.hpp"
62
49
  #include "painlessmesh/logger.hpp"
@@ -2,6 +2,15 @@
2
2
  #define _TASK_PRIORITY // Support for layered scheduling priority
3
3
  #define _TASK_STD_FUNCTION // Support for std::function (ESP8266 and ESP32)
4
4
  // Required for painlessMesh lambda callbacks
5
+ #define _TASK_SELF_DESTRUCT // Scheduler-managed deletion of heap-allocated
6
+ // one-shot tasks. The Scheduler deletes the Task
7
+ // in execute(), after disable() has returned, so
8
+ // a task never has to delete itself from inside
9
+ // its own onDisable callback (which is a
10
+ // use-after-free: Task::disable() writes to the
11
+ // task object after onDisable returns). Used by
12
+ // scheduleAsyncClientDeletion() in
13
+ // connection.hpp. See issue #373.
5
14
 
6
15
  // NOTE: _TASK_THREAD_SAFE is currently DISABLED due to incompatibility
7
16
  // with _TASK_STD_FUNCTION in TaskScheduler v4.0.x
@@ -37,7 +37,10 @@ class ReceiveBuffer {
37
37
  do {
38
38
  auto len = strnlen(data_ptr, length);
39
39
  do {
40
- auto read_len = (std::min)(len, buf.length);
40
+ // Reserve one byte for the '\0' terminator below: read_len may be at
41
+ // most buf.length - 1, otherwise buf.buffer[read_len] writes one
42
+ // byte past the end of the buffer (caught by the ASan CI job).
43
+ auto read_len = (std::min)(len, buf.length - 1);
41
44
  memcpy(buf.buffer, data_ptr, read_len);
42
45
  buf.buffer[read_len] = '\0';
43
46
  auto newBuffer = T(buf.buffer);
@@ -26,10 +26,21 @@
26
26
  // Enable OTA support
27
27
  #define PAINLESSMESH_ENABLE_OTA
28
28
 
29
- // Minimum free memory, besides here all packets in queue are discarded.
29
+ // NOTE: `MIN_FREE_MEMORY` and `MAX_MESSAGE_QUEUE` are kept as deprecated
30
+ // no-op compatibility macros. The library does not read either macro:
31
+ // the auto-flushing message queue they were meant to tune never landed
32
+ // (see #385, PR #383 review). `MessageQueue`
33
+ // (`painlessmesh/message_queue.hpp`) is a manual priority buffer with
34
+ // its own per-instance `maxSize` argument. Their historical default
35
+ // values are preserved so downstream code that referenced them keeps
36
+ // its prior behavior.
37
+ #ifndef MIN_FREE_MEMORY
30
38
  #define MIN_FREE_MEMORY 4000
31
- // MAX number of unsent messages in queue. Newer messages are discarded
39
+ #endif
40
+
41
+ #ifndef MAX_MESSAGE_QUEUE
32
42
  #define MAX_MESSAGE_QUEUE 50
43
+ #endif
33
44
 
34
45
  #define NODE_TIMEOUT 10 * TASK_SECOND
35
46
  #define SCAN_INTERVAL 30 * TASK_SECOND // AP scan period in ms
@@ -42,10 +42,10 @@ static const uint32_t TCP_CLIENT_DELETION_SPACING_MS = 1000; // 1000ms spacing b
42
42
  // - The scheduler never runs tasks concurrently within the same mesh instance
43
43
  // - All mesh operations (including deletion callbacks) execute in the same thread
44
44
  // - Even when multiple tasks are ready, they execute one-at-a-time via scheduler->execute()
45
- static uint32_t lastScheduledDeletionTime = 0; // Timestamp of last deletion scheduled/executed (milliseconds)
45
+ extern uint32_t lastScheduledDeletionTime; // Timestamp of last deletion scheduled/executed (milliseconds)
46
46
 
47
47
  // Shared buffer for reading/writing to the buffer
48
- static painlessmesh::buffer::temp_buffer_t shared_buffer;
48
+ extern painlessmesh::buffer::temp_buffer_t shared_buffer;
49
49
 
50
50
  /**
51
51
  * Schedule deletion of an AsyncClient with proper spacing to prevent concurrent cleanups
@@ -116,25 +116,25 @@ inline void scheduleAsyncClientDeletion(Scheduler* scheduler, AsyncClient* clien
116
116
  Log(CONNECTION, "%s: Scheduling AsyncClient deletion in %u ms (spaced from previous deletions)\n",
117
117
  logPrefix, actualDelay);
118
118
 
119
- // Schedule the deletion task
120
- // Note: Task object is intentionally leaked to keep implementation simple
121
- // This is acceptable because:
122
- // 1. Connections are long-lived, destructor calls are infrequent
123
- // 2. Task object is small (~32-64 bytes) vs preventing critical heap corruption
124
- // 3. In typical deployments, memory impact is negligible (few KB over months)
125
- // 4. Alternative cleanup patterns would add significant complexity
119
+ // Schedule the deletion task with self-cleanup
126
120
  Task* cleanupTask = new Task(actualDelay * TASK_MILLISECOND, TASK_ONCE, [client, logPrefix]() {
127
121
  using namespace logger;
128
122
  Log(CONNECTION, "%s: Deferred cleanup of AsyncClient executing now\n", logPrefix);
129
-
130
- // Note: lastScheduledDeletionTime is updated at scheduling time (before this task runs), not here
131
- // This ensures consistent spacing based on when deletions were scheduled, preventing
132
- // the race condition where execution-time updates could "rewind" the timestamp
133
- // and cause subsequent deletions to be scheduled too close together
134
-
135
123
  delete client;
136
124
  });
137
-
125
+
126
+ // Task cleanup (issue #373): the task must NOT delete itself from inside
127
+ // its own onDisable callback. TaskScheduler's Task::disable() keeps
128
+ // writing to the task object (iScheduler->iCurrent) after onDisable
129
+ // returns, so a self-delete there is a guaranteed use-after-free — this
130
+ // was crashing nodes on every connection teardown.
131
+ // Instead we mark the task self-destructing (_TASK_SELF_DESTRUCT, enabled
132
+ // in painlessTaskOptions.h): after the single iteration completes, the
133
+ // Scheduler itself disables the task, unlinks it from its chain, and
134
+ // deletes it — all from within Scheduler::execute(), safely outside the
135
+ // disable() call stack.
136
+ cleanupTask->setSelfDestruct(true);
137
+
138
138
  scheduler->addTask(*cleanupTask);
139
139
  cleanupTask->enableDelayed();
140
140
  }
@@ -165,13 +165,28 @@ class BufferedConnection
165
165
  using namespace logger;
166
166
  Log.remote("~BufferedConnection");
167
167
  this->close();
168
- if (!client->freeable()) {
169
- client->close(true);
170
- }
168
+ // Always call client->close() here, unconditionally - do NOT guard this
169
+ // behind client->freeable(). freeable() can return true while _pcb is
170
+ // still a valid, non-null pointer (e.g. pcb->state == CLOSED but not yet
171
+ // reclaimed by lwIP). If we skip close() in that case, _pcb stays
172
+ // non-null for the entire TCP_CLIENT_CLEANUP_DELAY_MS+ deferred-deletion
173
+ // window below - during which lwIP's own internal timers (e.g. TIME_WAIT
174
+ // expiry) can silently free/recycle that pcb without ever notifying
175
+ // AsyncClient (the tcp_err callback only fires on abnormal termination,
176
+ // not on routine timer-driven pcb reclamation). The deferred delete then
177
+ // finds a stale-but-non-null _pcb and tries to close/free it a second
178
+ // time, corrupting the heap (observed as heap_caps_free/memp_free
179
+ // assertion failures and wild-pointer crashes inside tcp_arg(), all
180
+ // several seconds after the connection actually died).
181
+ // close() internally handles "nothing to do" safely (AsyncTCP checks
182
+ // _pcb/*pcb before touching lwIP state), and reliably nulls _pcb via its
183
+ // synchronous tcpip_api_call round-trip - so calling it unconditionally
184
+ // here, right at destruction time, closes this exposure window entirely.
185
+ client->close();
171
186
  // Note: client->abort() removed - calling it before deferred deletion
172
187
  // can leave the client in an inconsistent state where AsyncTCP is still
173
- // trying to clean up the aborted connection. The close() and close(true)
174
- // calls above are sufficient for connection termination.
188
+ // trying to clean up the aborted connection. The close() call above is
189
+ // sufficient for connection termination.
175
190
  // See: AsyncTCP best practices - abort() should only be called immediately
176
191
  // before delete, not before a deferred deletion.
177
192