@alteriom/painlessmesh 1.8.9 → 1.8.11
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 +59 -0
- package/docs/releases/RELEASE_CHECKLIST_1.8.10.md +331 -0
- package/docs/releases/RELEASE_CHECKLIST_1.8.9.md +207 -0
- package/docs/releases/RELEASE_NOTES_1.8.10.md +239 -0
- package/docs/releases/RELEASE_NOTES_1.8.9.md +213 -0
- package/docs/releases/RELEASE_SUMMARY_1.8.10.md +193 -0
- package/examples/bridge_failover/README.md +50 -2
- package/examples/bridge_failover/bridge_failover.ino +4 -0
- package/library.json +1 -1
- package/library.properties +1 -1
- package/package.json +1 -1
- package/src/AlteriomPainlessMesh.h +2 -2
- package/src/arduino/wifi.hpp +75 -4
- package/src/boost/asynctcp.hpp +5 -0
- package/src/painlessMesh.h +2 -2
- package/src/painlessmesh/buffer.hpp +3 -3
- package/src/painlessmesh/logger.hpp +9 -4
- package/src/painlessmesh/mesh.hpp +9 -5
- package/src/painlessmesh/message_queue.hpp +3 -3
- package/src/painlessmesh/ntp.hpp +2 -2
- package/src/painlessmesh/protocol.hpp +1 -1
- package/src/painlessmesh/router.hpp +4 -4
|
@@ -233,6 +233,10 @@ mesh.enableBridgeFailover(true);
|
|
|
233
233
|
// Set election timeout (milliseconds)
|
|
234
234
|
mesh.setElectionTimeout(5000);
|
|
235
235
|
|
|
236
|
+
// Set minimum RSSI for isolated bridge elections (default: -80 dBm)
|
|
237
|
+
// Prevents nodes with poor signal from becoming bridges when isolated
|
|
238
|
+
mesh.setMinimumBridgeRSSI(-80);
|
|
239
|
+
|
|
236
240
|
// Set bridge status broadcast interval
|
|
237
241
|
mesh.setBridgeStatusInterval(30000);
|
|
238
242
|
|
|
@@ -357,6 +361,37 @@ bool amBridge = mesh.isBridge();
|
|
|
357
361
|
- Works reliably with all network types including mobile hotspots and tethering
|
|
358
362
|
- Local IP check is more reliable than gateway IP, which may not be available on all network types
|
|
359
363
|
|
|
364
|
+
### Isolated Node with Poor Signal Attempting Bridge
|
|
365
|
+
|
|
366
|
+
**Symptoms**: Node logs "Election Failed: Insufficient Signal Quality" or has -85+ dBm RSSI
|
|
367
|
+
|
|
368
|
+
**Root Cause**: Node cannot see the mesh (isolated) and has poor router signal below minimum threshold
|
|
369
|
+
|
|
370
|
+
**What's Happening**:
|
|
371
|
+
1. Node comes online and doesn't detect existing bridge
|
|
372
|
+
2. Triggers election where it's the only candidate
|
|
373
|
+
3. Has poor router RSSI (e.g., -87 dBm, below -80 dBm threshold)
|
|
374
|
+
4. Election automatically rejected to prevent unreliable bridge
|
|
375
|
+
5. Node remains as regular node until mesh connection or better signal
|
|
376
|
+
|
|
377
|
+
**Expected Behavior** (v1.8.10+):
|
|
378
|
+
```
|
|
379
|
+
=== Evaluating Election ===
|
|
380
|
+
evaluateElection(): 1 candidates
|
|
381
|
+
=== Election Failed: Insufficient Signal Quality ===
|
|
382
|
+
Single candidate with RSSI -87 dBm (minimum required: -80 dBm)
|
|
383
|
+
Node is isolated from mesh with poor router signal
|
|
384
|
+
Rejecting election to prevent unreliable bridge
|
|
385
|
+
Recommendation: Move closer to router or wait for mesh connection
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
**Solutions**:
|
|
389
|
+
- **Best**: Move node closer to router for better signal strength
|
|
390
|
+
- **Wait**: Node will join existing bridge once mesh connection established
|
|
391
|
+
- **Adjust threshold**: `mesh.setMinimumBridgeRSSI(-85)` to relax requirement (not recommended)
|
|
392
|
+
- **Check existing bridge**: Ensure another node with good signal is already bridge
|
|
393
|
+
- **Verify mesh**: Ensure nodes can communicate with each other
|
|
394
|
+
|
|
360
395
|
### Election Doesn't Start
|
|
361
396
|
|
|
362
397
|
**Symptoms**: Bridge fails but no election occurs
|
|
@@ -381,10 +416,23 @@ bool amBridge = mesh.isBridge();
|
|
|
381
416
|
|
|
382
417
|
**Symptoms**: Node with weak signal becomes bridge
|
|
383
418
|
|
|
384
|
-
**
|
|
419
|
+
**Root Cause (Fixed in v1.8.10+)**:
|
|
420
|
+
When a node with poor router signal (e.g., -87 dBm) comes online and cannot see the existing bridge, it may trigger an election where it's the only candidate. Previously, it could win by default despite inadequate signal strength.
|
|
421
|
+
|
|
422
|
+
**Solution (Automatic)**:
|
|
423
|
+
Starting in v1.8.10, the election system enforces a minimum RSSI threshold (-80 dBm by default) for single-candidate elections. This prevents isolated nodes with poor signal from becoming bridges.
|
|
424
|
+
|
|
425
|
+
**Behavior**:
|
|
426
|
+
- **Single candidate with poor RSSI**: Election fails with warning message
|
|
427
|
+
- **Multiple candidates**: Best RSSI wins regardless of threshold (mesh is connected)
|
|
428
|
+
- **Single candidate with good RSSI**: Election proceeds normally
|
|
429
|
+
|
|
430
|
+
**Manual Solutions**:
|
|
431
|
+
- Update to painlessMesh v1.8.10 or later
|
|
432
|
+
- Adjust minimum RSSI threshold: `mesh.setMinimumBridgeRSSI(-75)` (stricter)
|
|
385
433
|
- Verify RSSI values in election logs
|
|
386
434
|
- Check router positioning and interference
|
|
387
|
-
-
|
|
435
|
+
- Move nodes closer to router for better signal
|
|
388
436
|
- Review signal strength readings
|
|
389
437
|
|
|
390
438
|
### Frequent Re-elections
|
|
@@ -136,6 +136,10 @@ void setup() {
|
|
|
136
136
|
mesh.setRouterCredentials(ROUTER_SSID, ROUTER_PASSWORD);
|
|
137
137
|
mesh.enableBridgeFailover(true);
|
|
138
138
|
mesh.setElectionTimeout(5000); // 5 second election window
|
|
139
|
+
|
|
140
|
+
// Optional: Set minimum RSSI for isolated bridge elections (default: -80 dBm)
|
|
141
|
+
// This prevents nodes with poor signal from becoming bridges when isolated
|
|
142
|
+
// mesh.setMinimumBridgeRSSI(-80); // Uncomment to customize threshold
|
|
139
143
|
}
|
|
140
144
|
|
|
141
145
|
// Register callbacks
|
package/library.json
CHANGED
package/library.properties
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
name=Alteriom PainlessMesh
|
|
2
|
-
version=1.8.
|
|
2
|
+
version=1.8.11
|
|
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.8.
|
|
3
|
+
"version": "1.8.11",
|
|
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",
|
|
@@ -29,10 +29,10 @@
|
|
|
29
29
|
/**
|
|
30
30
|
* @brief AlteriomPainlessMesh library version information
|
|
31
31
|
*/
|
|
32
|
-
#define ALTERIOM_PAINLESS_MESH_VERSION "1.8.
|
|
32
|
+
#define ALTERIOM_PAINLESS_MESH_VERSION "1.8.11"
|
|
33
33
|
#define ALTERIOM_PAINLESS_MESH_VERSION_MAJOR 1
|
|
34
34
|
#define ALTERIOM_PAINLESS_MESH_VERSION_MINOR 8
|
|
35
|
-
#define ALTERIOM_PAINLESS_MESH_VERSION_PATCH
|
|
35
|
+
#define ALTERIOM_PAINLESS_MESH_VERSION_PATCH 11
|
|
36
36
|
|
|
37
37
|
/**
|
|
38
38
|
* @brief Library description and usage information
|
package/src/arduino/wifi.hpp
CHANGED
|
@@ -534,6 +534,21 @@ class Mesh : public painlessmesh::Mesh<Connection> {
|
|
|
534
534
|
electionTimeoutMs = timeoutMs;
|
|
535
535
|
}
|
|
536
536
|
|
|
537
|
+
/**
|
|
538
|
+
* Set the minimum RSSI required for bridge election
|
|
539
|
+
*
|
|
540
|
+
* Prevents nodes with poor router signal from becoming bridges in isolated
|
|
541
|
+
* elections. When a node is the only candidate, it must meet this threshold.
|
|
542
|
+
* When multiple candidates exist, the best RSSI wins regardless of threshold.
|
|
543
|
+
*
|
|
544
|
+
* @param minRSSI Minimum RSSI in dBm (default: -80 dBm, range: -100 to -30)
|
|
545
|
+
*/
|
|
546
|
+
void setMinimumBridgeRSSI(int8_t minRSSI) {
|
|
547
|
+
if (minRSSI < -100) minRSSI = -100;
|
|
548
|
+
if (minRSSI > -30) minRSSI = -30;
|
|
549
|
+
minimumBridgeRSSI = minRSSI;
|
|
550
|
+
}
|
|
551
|
+
|
|
537
552
|
/**
|
|
538
553
|
* Set callback for when this node's bridge role changes
|
|
539
554
|
*
|
|
@@ -789,10 +804,43 @@ class Mesh : public painlessmesh::Mesh<Connection> {
|
|
|
789
804
|
this->sendBridgeStatus();
|
|
790
805
|
});
|
|
791
806
|
|
|
792
|
-
// Also
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
807
|
+
// Also send bridge status when new nodes connect so they can discover the bridge immediately
|
|
808
|
+
// Send directly to the new node to ensure delivery, independent of time sync
|
|
809
|
+
// Using changedConnectionCallbacks instead of newConnectionCallbacks ensures routing is ready
|
|
810
|
+
this->changedConnectionCallbacks.push_back([this](uint32_t nodeId) {
|
|
811
|
+
Log(CONNECTION, "Node %u connection changed, sending bridge status directly\n", nodeId);
|
|
812
|
+
|
|
813
|
+
// Small delay to ensure connection is fully stable, then send directly to the new node
|
|
814
|
+
// This avoids issues with time sync blocking broadcast messages
|
|
815
|
+
this->addTask(500, TASK_ONCE, [this, nodeId]() {
|
|
816
|
+
// Create bridge status message
|
|
817
|
+
JsonDocument doc;
|
|
818
|
+
JsonObject obj = doc.to<JsonObject>();
|
|
819
|
+
|
|
820
|
+
obj["type"] = 610; // BRIDGE_STATUS type
|
|
821
|
+
obj["from"] = this->nodeId;
|
|
822
|
+
obj["routing"] = 1; // SINGLE routing (direct to node)
|
|
823
|
+
obj["dest"] = nodeId;
|
|
824
|
+
obj["timestamp"] = this->getNodeTime();
|
|
825
|
+
|
|
826
|
+
bool hasInternet = (WiFi.status() == WL_CONNECTED) &&
|
|
827
|
+
(WiFi.localIP() != IPAddress(0, 0, 0, 0));
|
|
828
|
+
obj["internetConnected"] = hasInternet;
|
|
829
|
+
obj["routerRSSI"] = WiFi.RSSI();
|
|
830
|
+
obj["routerChannel"] = WiFi.channel();
|
|
831
|
+
obj["uptime"] = millis();
|
|
832
|
+
obj["gatewayIP"] = WiFi.gatewayIP().toString();
|
|
833
|
+
obj["message_type"] = 610;
|
|
834
|
+
|
|
835
|
+
String msg;
|
|
836
|
+
serializeJson(doc, msg);
|
|
837
|
+
|
|
838
|
+
Log(CONNECTION, "Sending bridge status directly to node %u (Internet: %s)\n",
|
|
839
|
+
nodeId, hasInternet ? "YES" : "NO");
|
|
840
|
+
|
|
841
|
+
// Send directly to the new node using routing table (which is now ready)
|
|
842
|
+
this->sendSingle(nodeId, msg);
|
|
843
|
+
});
|
|
796
844
|
});
|
|
797
845
|
|
|
798
846
|
Log(STARTUP, "Bridge status broadcast enabled (interval: %d ms)\n",
|
|
@@ -1077,6 +1125,28 @@ class Mesh : public painlessmesh::Mesh<Connection> {
|
|
|
1077
1125
|
return;
|
|
1078
1126
|
}
|
|
1079
1127
|
|
|
1128
|
+
// Validate RSSI threshold for single-candidate elections
|
|
1129
|
+
// When only one candidate exists, it indicates the node is isolated from the mesh.
|
|
1130
|
+
// In this case, require minimum signal quality to prevent poor connections.
|
|
1131
|
+
// When multiple candidates exist, the mesh is connected and best RSSI wins.
|
|
1132
|
+
if (electionCandidates.size() == 1 && winner->routerRSSI < minimumBridgeRSSI) {
|
|
1133
|
+
Log(CONNECTION, "=== Election Failed: Insufficient Signal Quality ===\n");
|
|
1134
|
+
Log(CONNECTION, " Single candidate with RSSI %d dBm (minimum required: %d dBm)\n",
|
|
1135
|
+
winner->routerRSSI, minimumBridgeRSSI);
|
|
1136
|
+
Log(CONNECTION, " Node is isolated from mesh with poor router signal\n");
|
|
1137
|
+
Log(CONNECTION, " Rejecting election to prevent unreliable bridge\n");
|
|
1138
|
+
Log(CONNECTION, " Recommendation: Move closer to router or wait for mesh connection\n");
|
|
1139
|
+
|
|
1140
|
+
electionState = ELECTION_IDLE;
|
|
1141
|
+
electionCandidates.clear();
|
|
1142
|
+
|
|
1143
|
+
// Notify via callback that election failed
|
|
1144
|
+
if (bridgeRoleChangedCallback) {
|
|
1145
|
+
bridgeRoleChangedCallback(false, "Insufficient signal quality for isolated bridge");
|
|
1146
|
+
}
|
|
1147
|
+
return;
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1080
1150
|
Log(CONNECTION, "=== Election Winner: Node %u ===\n", winner->nodeId);
|
|
1081
1151
|
Log(CONNECTION, " Router RSSI: %d dBm\n", winner->routerRSSI);
|
|
1082
1152
|
Log(CONNECTION, " Uptime: %u ms\n", winner->uptime);
|
|
@@ -1381,6 +1451,7 @@ class Mesh : public painlessmesh::Mesh<Connection> {
|
|
|
1381
1451
|
TSTRING routerSSID = "";
|
|
1382
1452
|
TSTRING routerPassword = "";
|
|
1383
1453
|
uint32_t electionTimeoutMs = 5000; // Default 5 seconds
|
|
1454
|
+
int8_t minimumBridgeRSSI = -80; // Default -80 dBm minimum for isolated elections
|
|
1384
1455
|
uint32_t lastRoleChangeTime = 0;
|
|
1385
1456
|
ElectionState electionState = ELECTION_IDLE;
|
|
1386
1457
|
uint32_t electionDeadline = 0;
|
package/src/boost/asynctcp.hpp
CHANGED
|
@@ -233,8 +233,13 @@ class AsyncServer {
|
|
|
233
233
|
void begin() {
|
|
234
234
|
mAcceptor.open(tcp::v4());
|
|
235
235
|
int one = 1;
|
|
236
|
+
#ifdef _WIN32
|
|
237
|
+
setsockopt(mAcceptor.native_handle(), SOL_SOCKET,
|
|
238
|
+
SO_REUSEADDR | SO_REUSEPORT, reinterpret_cast<const char*>(&one), sizeof(one));
|
|
239
|
+
#else
|
|
236
240
|
setsockopt(mAcceptor.native_handle(), SOL_SOCKET,
|
|
237
241
|
SO_REUSEADDR | SO_REUSEPORT, &one, sizeof(one));
|
|
242
|
+
#endif
|
|
238
243
|
boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::tcp::v4(), _port);
|
|
239
244
|
mAcceptor.set_option(boost::asio::socket_base::reuse_address(true));
|
|
240
245
|
mAcceptor.set_option(boost::asio::ip::tcp::no_delay(true));
|
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.8.
|
|
9
|
-
* @date 2025-11-
|
|
8
|
+
* @version 1.8.11
|
|
9
|
+
* @date 2025-11-18
|
|
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
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#ifndef _PAINLESS_MESH_BUFFER_HPP_
|
|
1
|
+
#ifndef _PAINLESS_MESH_BUFFER_HPP_
|
|
2
2
|
#define _PAINLESS_MESH_BUFFER_HPP_
|
|
3
3
|
|
|
4
4
|
#include <list>
|
|
@@ -37,7 +37,7 @@ 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
|
+
auto read_len = (std::min)(len, buf.length);
|
|
41
41
|
memcpy(buf.buffer, data_ptr, read_len);
|
|
42
42
|
buf.buffer[read_len] = '\0';
|
|
43
43
|
auto newBuffer = T(buf.buffer);
|
|
@@ -179,7 +179,7 @@ class SentBuffer {
|
|
|
179
179
|
else
|
|
180
180
|
// String.toCharArray automatically turns the last character into
|
|
181
181
|
// a \0, we need the extra space to deal with that annoyance
|
|
182
|
-
return std::min(buffer_length - 1, msg->length() + 1);
|
|
182
|
+
return (std::min)(buffer_length - 1, msg->length() + 1);
|
|
183
183
|
}
|
|
184
184
|
|
|
185
185
|
/**
|
|
@@ -10,6 +10,11 @@ namespace logger {
|
|
|
10
10
|
|
|
11
11
|
#define REMOTE_QUEUE_SIZE 10
|
|
12
12
|
|
|
13
|
+
// Windows defines ERROR as a macro, undefine it to avoid conflicts
|
|
14
|
+
#ifdef ERROR
|
|
15
|
+
#undef ERROR
|
|
16
|
+
#endif
|
|
17
|
+
|
|
13
18
|
typedef enum {
|
|
14
19
|
ERROR = 1 << 0,
|
|
15
20
|
STARTUP = 1 << 1,
|
|
@@ -132,7 +137,7 @@ class LogClass {
|
|
|
132
137
|
|
|
133
138
|
vsnprintf(str, 200, format, args);
|
|
134
139
|
|
|
135
|
-
remote_queue.push_back(std::pair<
|
|
140
|
+
remote_queue.push_back(std::pair<uint32_t, TSTRING>(remote_uuid, str));
|
|
136
141
|
++remote_uuid;
|
|
137
142
|
if (remote_queue.size() > REMOTE_QUEUE_SIZE) {
|
|
138
143
|
// No place to store the reason, but do signify the queue is full,
|
|
@@ -142,15 +147,15 @@ class LogClass {
|
|
|
142
147
|
}
|
|
143
148
|
}
|
|
144
149
|
|
|
145
|
-
std::list<std::pair<
|
|
150
|
+
std::list<std::pair<uint32_t, TSTRING>> &get_remote_queue() {
|
|
146
151
|
return remote_queue;
|
|
147
152
|
}
|
|
148
153
|
|
|
149
154
|
private:
|
|
150
155
|
uint16_t types = 0;
|
|
151
156
|
char str[200];
|
|
152
|
-
std::list<std::pair<
|
|
153
|
-
|
|
157
|
+
std::list<std::pair<uint32_t, TSTRING>> remote_queue;
|
|
158
|
+
uint32_t remote_uuid;
|
|
154
159
|
};
|
|
155
160
|
|
|
156
161
|
} // namespace logger
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#ifndef _PAINLESS_MESH_MESH_HPP_
|
|
1
|
+
#ifndef _PAINLESS_MESH_MESH_HPP_
|
|
2
2
|
#define _PAINLESS_MESH_MESH_HPP_
|
|
3
3
|
|
|
4
4
|
#include <vector>
|
|
@@ -428,7 +428,7 @@ class Mesh : public ntp::MeshTime, public plugin::PackageHandler<T> {
|
|
|
428
428
|
/** Set a callback routine for any messages that are addressed to this node.
|
|
429
429
|
*
|
|
430
430
|
* Every time this node receives a message, this callback routine will the
|
|
431
|
-
* called.
|
|
431
|
+
* called. “from†is the id of the original sender of the message, and “msgâ€
|
|
432
432
|
* is a string that contains the message. The message can be anything. A
|
|
433
433
|
* JSON, some other text string, or binary data.
|
|
434
434
|
*
|
|
@@ -1861,7 +1861,7 @@ class Mesh : public ntp::MeshTime, public plugin::PackageHandler<T> {
|
|
|
1861
1861
|
* // === painlessMesh Diagnostics ===
|
|
1862
1862
|
* // Mode: Regular Node
|
|
1863
1863
|
* // Mesh: ProductionMesh (25 nodes)
|
|
1864
|
-
* // Bridge: 123456789 (RSSI: -42 dBm, Internet:
|
|
1864
|
+
* // Bridge: 123456789 (RSSI: -42 dBm, Internet: ✓)
|
|
1865
1865
|
* // Queue: 3 messages (2 CRITICAL, 1 NORMAL)
|
|
1866
1866
|
* // Uptime: 02:15:33
|
|
1867
1867
|
* // Last Election: 00:45:12 ago (Winner: 123456789)
|
|
@@ -1889,7 +1889,7 @@ class Mesh : public ntp::MeshTime, public plugin::PackageHandler<T> {
|
|
|
1889
1889
|
if (primaryBridge != nullptr) {
|
|
1890
1890
|
report += "Bridge: " + TSTRING(std::to_string(primaryBridge->nodeId).c_str());
|
|
1891
1891
|
report += " (RSSI: " + TSTRING(std::to_string(primaryBridge->routerRSSI).c_str()) + " dBm";
|
|
1892
|
-
report += ", Internet: " + TSTRING(primaryBridge->internetConnected ? "
|
|
1892
|
+
report += ", Internet: " + TSTRING(primaryBridge->internetConnected ? "✓" : "✗") + ")\n";
|
|
1893
1893
|
} else {
|
|
1894
1894
|
report += "Bridge: None available\n";
|
|
1895
1895
|
}
|
|
@@ -1960,6 +1960,7 @@ class Mesh : public ntp::MeshTime, public plugin::PackageHandler<T> {
|
|
|
1960
1960
|
isExternalScheduler = true;
|
|
1961
1961
|
}
|
|
1962
1962
|
|
|
1963
|
+
public: // Windows MSVC: router functions need access
|
|
1963
1964
|
void startTimeSync(std::shared_ptr<T> conn) {
|
|
1964
1965
|
using namespace logger;
|
|
1965
1966
|
Log(S_TIME, "startTimeSync(): from %u with %u\n", this->nodeId,
|
|
@@ -2033,6 +2034,7 @@ class Mesh : public ntp::MeshTime, public plugin::PackageHandler<T> {
|
|
|
2033
2034
|
[](const std::shared_ptr<T> &conn) { return !conn->connected(); });
|
|
2034
2035
|
}
|
|
2035
2036
|
|
|
2037
|
+
public: // Windows MSVC: TCP lambdas need access to droppedConnectionCallbacks
|
|
2036
2038
|
// Callback functions
|
|
2037
2039
|
callback::List<uint32_t> newConnectionCallbacks;
|
|
2038
2040
|
callback::List<uint32_t, bool> droppedConnectionCallbacks;
|
|
@@ -2056,6 +2058,8 @@ class Mesh : public ntp::MeshTime, public plugin::PackageHandler<T> {
|
|
|
2056
2058
|
|
|
2057
2059
|
Scheduler *mScheduler;
|
|
2058
2060
|
|
|
2061
|
+
public: // Windows MSVC: lambdas in friend functions need public access
|
|
2062
|
+
|
|
2059
2063
|
/**
|
|
2060
2064
|
* Wrapper function for ESP32 semaphore function
|
|
2061
2065
|
*
|
|
@@ -2285,7 +2289,7 @@ class Connection : public painlessmesh::layout::Neighbour,
|
|
|
2285
2289
|
quality -= (80 + rssi); // e.g., -90 dBm = penalty of 10
|
|
2286
2290
|
}
|
|
2287
2291
|
|
|
2288
|
-
return std::max(0, std::min(100, quality));
|
|
2292
|
+
return (std::max)(0, (std::min)(100, quality));
|
|
2289
2293
|
}
|
|
2290
2294
|
|
|
2291
2295
|
/**
|
|
@@ -143,8 +143,8 @@ public:
|
|
|
143
143
|
// Check for state change
|
|
144
144
|
checkStateChange();
|
|
145
145
|
|
|
146
|
-
Log(logger::GENERAL, "MessageQueue: Enqueued message #%u (priority=%d, size=%
|
|
147
|
-
msgId, priority, messages.size(), maxQueueSize);
|
|
146
|
+
Log(logger::GENERAL, "MessageQueue: Enqueued message #%u (priority=%d, size=%zu/%zu)\n",
|
|
147
|
+
msgId, priority, messages.size(), (size_t)maxQueueSize);
|
|
148
148
|
|
|
149
149
|
return msgId;
|
|
150
150
|
}
|
|
@@ -167,7 +167,7 @@ public:
|
|
|
167
167
|
updatePriorityStats();
|
|
168
168
|
checkStateChange();
|
|
169
169
|
|
|
170
|
-
Log(logger::GENERAL, "MessageQueue: Removed message #%u (size=%
|
|
170
|
+
Log(logger::GENERAL, "MessageQueue: Removed message #%u (size=%zu)\n",
|
|
171
171
|
messageId, messages.size());
|
|
172
172
|
return true;
|
|
173
173
|
}
|
package/src/painlessmesh/ntp.hpp
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#ifndef _PAINLESS_MESH_NTP_HPP_
|
|
1
|
+
#ifndef _PAINLESS_MESH_NTP_HPP_
|
|
2
2
|
#define _PAINLESS_MESH_NTP_HPP_
|
|
3
3
|
|
|
4
4
|
#ifndef TIME_SYNC_INTERVAL
|
|
@@ -42,7 +42,7 @@ class MeshTime {
|
|
|
42
42
|
*/
|
|
43
43
|
uint32_t getNodeTime() { return micros() + timeOffset; }
|
|
44
44
|
|
|
45
|
-
|
|
45
|
+
public: // Windows MSVC: friend functions need access to timeOffset
|
|
46
46
|
uint32_t timeOffset = 0;
|
|
47
47
|
};
|
|
48
48
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#ifndef _PAINLESS_MESH_ROUTER_HPP_
|
|
1
|
+
#ifndef _PAINLESS_MESH_ROUTER_HPP_
|
|
2
2
|
#define _PAINLESS_MESH_ROUTER_HPP_
|
|
3
3
|
|
|
4
4
|
#include <algorithm>
|
|
@@ -235,14 +235,14 @@ void routePackage(layout::Layout<T> layout, std::shared_ptr<T> connection,
|
|
|
235
235
|
// Base capacity: message length + overhead for JSON structure
|
|
236
236
|
// Each nesting level adds overhead for pointers and metadata
|
|
237
237
|
size_t calculatedCapacity = pkg.length() +
|
|
238
|
-
JSON_OBJECT_SIZE(10) * std::max(nestingDepth, size_t(1)) +
|
|
238
|
+
JSON_OBJECT_SIZE(10) * (std::max)(nestingDepth, size_t(1)) +
|
|
239
239
|
512; // Additional buffer for strings and padding
|
|
240
240
|
#endif
|
|
241
241
|
|
|
242
242
|
// Cap at 8KB for safety on ESP8266 (which has ~80KB total heap)
|
|
243
243
|
// Messages larger than this should be rejected
|
|
244
244
|
constexpr size_t MAX_MESSAGE_CAPACITY = 8192;
|
|
245
|
-
size_t capacity = std::min(calculatedCapacity, MAX_MESSAGE_CAPACITY);
|
|
245
|
+
size_t capacity = (std::min)(calculatedCapacity, MAX_MESSAGE_CAPACITY);
|
|
246
246
|
|
|
247
247
|
auto variant = std::make_shared<protocol::Variant>(pkg, capacity);
|
|
248
248
|
|
|
@@ -330,7 +330,7 @@ void handleNodeSync(T& mesh, protocol::NodeTree newTree,
|
|
|
330
330
|
[&mesh, nodeId]() { mesh.changedConnectionCallbacks.execute(nodeId); });
|
|
331
331
|
} else {
|
|
332
332
|
conn->nodeSyncTask.delay();
|
|
333
|
-
mesh.stability += std::min(1000 - mesh.stability, (size_t)25);
|
|
333
|
+
mesh.stability += (std::min)(1000 - mesh.stability, (size_t)25);
|
|
334
334
|
}
|
|
335
335
|
}
|
|
336
336
|
|