@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.
- package/CHANGELOG.md +168 -0
- package/README.md +102 -63
- package/RELEASE_GUIDE.md +147 -8
- package/examples/alteriom/README.md +4 -4
- package/examples/alteriom/alteriom_custom_package_template.hpp +320 -0
- package/examples/alteriom/alteriom_sensor_package.hpp +1 -1
- package/examples/alteriom/mppt_example/alteriom_mppt_example.ino +208 -0
- package/examples/bridge_failover/bridge_failover.ino +17 -0
- package/examples/sendToInternet/CMakeLists.txt +54 -0
- package/examples/sendToInternet/PC_NODE_README.md +517 -0
- package/examples/sendToInternet/README.md +39 -1
- package/examples/sendToInternet/build.sh +153 -0
- package/examples/sendToInternet/mock_server_test.ino +361 -0
- package/examples/sendToInternet/pc_mesh_node.cpp +361 -0
- package/examples/tcpRetryConfig/README.md +110 -0
- package/examples/tcpRetryConfig/platformio.ini +26 -0
- package/examples/tcpRetryConfig/tcpRetryConfig.ino +154 -0
- package/keywords.txt +3 -0
- package/library.json +4 -1
- package/library.properties +1 -1
- package/package.json +3 -3
- package/src/AlteriomPainlessMesh.h +6 -14
- package/src/arduino/wifi.hpp +352 -114
- package/src/connection.cpp +10 -0
- package/src/painlessMesh.h +2 -15
- package/src/painlessTaskOptions.h +9 -0
- package/src/painlessmesh/buffer.hpp +4 -1
- package/src/painlessmesh/configuration.hpp +13 -2
- package/src/painlessmesh/connection.hpp +36 -21
- package/src/painlessmesh/gateway.hpp +0 -1061
- package/src/painlessmesh/mesh.hpp +102 -107
- package/src/painlessmesh/message_queue.hpp +25 -15
- package/src/painlessmesh/metrics.hpp +2 -262
- package/src/painlessmesh/plugin.hpp +27 -5
- package/src/painlessmesh/tcp.hpp +158 -29
- package/src/painlessmesh/validation.hpp +0 -143
- package/docs/README.md +0 -132
- package/docs/alteriom/overview.md +0 -531
- package/docs/api/core-api.md +0 -607
- package/docs/api/shared-gateway.md +0 -1207
- package/docs/architecture/mesh-architecture.md +0 -399
- package/docs/architecture/plugin-system.md +0 -517
- package/docs/getting-started/arduino-manual-install.md +0 -313
- package/docs/getting-started/first-mesh.md +0 -410
- package/docs/getting-started/installation.md +0 -275
- package/docs/getting-started/quickstart.md +0 -158
- package/docs/troubleshooting/common-issues.md +0 -679
- package/docs/troubleshooting/debugging.md +0 -455
- package/docs/troubleshooting/external-device-connection.md +0 -283
- package/docs/troubleshooting/faq.md +0 -574
- package/docs/tutorials/basic-examples.md +0 -718
package/src/painlessMesh.h
CHANGED
|
@@ -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
|
-
* @date
|
|
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
|
-
|
|
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
|
-
//
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
169
|
-
|
|
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()
|
|
174
|
-
//
|
|
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
|
|