@alteriom/painlessmesh 1.6.1
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 +144 -0
- package/LICENSE +674 -0
- package/README.md +434 -0
- package/RELEASE_GUIDE.md +419 -0
- package/docs/DOCUMENTATION_MIGRATION_PLAN.md +176 -0
- package/docs/README.md +71 -0
- package/docs/alteriom/overview.md +508 -0
- package/docs/api/core-api.md +607 -0
- package/docs/architecture/mesh-architecture.md +379 -0
- package/docs/architecture/plugin-system.md +517 -0
- package/docs/getting-started/first-mesh.md +410 -0
- package/docs/getting-started/installation.md +275 -0
- package/docs/getting-started/quickstart.md +158 -0
- package/docs/improvements/README.md +69 -0
- package/docs/troubleshooting/common-issues.md +521 -0
- package/docs/troubleshooting/faq.md +473 -0
- package/docs/tutorials/basic-examples.md +718 -0
- package/docs/wiki/API-Reference.md +246 -0
- package/docs/wiki/Complete-Documentation.md +123 -0
- package/examples/alteriom/README.md +82 -0
- package/examples/alteriom/alteriom.ino +186 -0
- package/examples/alteriom/alteriom_sensor_node.ino +184 -0
- package/examples/alteriom/alteriom_sensor_package.hpp +128 -0
- package/examples/alteriom/improved_sensor_node.ino +246 -0
- package/examples/alteriom/platformio.ini +25 -0
- package/examples/basic/basic.ino +66 -0
- package/examples/basic/platformio.ini +25 -0
- package/examples/bridge/bridge.ino +51 -0
- package/examples/bridge/platformio.ini +25 -0
- package/examples/echoNode/echoNode.ino +33 -0
- package/examples/echoNode/platformio.ini +25 -0
- package/examples/logClient/logClient.ino +109 -0
- package/examples/logClient/platformio.ini +25 -0
- package/examples/logServer/logServer.ino +81 -0
- package/examples/logServer/platformio.ini +25 -0
- package/examples/mqttBridge/mqttBridge.ino +118 -0
- package/examples/mqttBridge/platformio.ini +26 -0
- package/examples/namedMesh/namedMesh.ino +97 -0
- package/examples/namedMesh/platformio.ini +25 -0
- package/examples/otaReceiver/otaReceiver.ino +79 -0
- package/examples/otaReceiver/platformio.ini +25 -0
- package/examples/otaSender/nodemcu32s_connections.JPG +0 -0
- package/examples/otaSender/otaSender.ino +151 -0
- package/examples/otaSender/platformio.ini +25 -0
- package/examples/startHere/platformio.ini +25 -0
- package/examples/startHere/startHere.ino +159 -0
- package/examples/webServer/platformio.ini +27 -0
- package/examples/webServer/webServer.ino +89 -0
- package/keywords.txt +49 -0
- package/library.json +34 -0
- package/library.properties +11 -0
- package/package.json +78 -0
- package/src/AlteriomPainlessMesh.h +98 -0
- package/src/arduino/wifi.hpp +365 -0
- package/src/boost/asynctcp.hpp +279 -0
- package/src/painlessMesh.h +70 -0
- package/src/painlessMeshSTA.cpp +236 -0
- package/src/painlessMeshSTA.h +58 -0
- package/src/painlessTaskOptions.h +4 -0
- package/src/painlessmesh/base64.hpp +111 -0
- package/src/painlessmesh/buffer.hpp +229 -0
- package/src/painlessmesh/callback.hpp +91 -0
- package/src/painlessmesh/configuration.hpp +77 -0
- package/src/painlessmesh/connection.hpp +192 -0
- package/src/painlessmesh/layout.hpp +188 -0
- package/src/painlessmesh/logger.hpp +158 -0
- package/src/painlessmesh/memory.hpp +120 -0
- package/src/painlessmesh/mesh.hpp +560 -0
- package/src/painlessmesh/metrics.hpp +323 -0
- package/src/painlessmesh/ntp.hpp +263 -0
- package/src/painlessmesh/ota.hpp +553 -0
- package/src/painlessmesh/plugin.hpp +188 -0
- package/src/painlessmesh/protocol.hpp +813 -0
- package/src/painlessmesh/router.hpp +322 -0
- package/src/painlessmesh/tcp.hpp +71 -0
- package/src/painlessmesh/validation.hpp +239 -0
- package/src/plugin/performance.hpp +214 -0
- package/src/plugin/remote.hpp +64 -0
- package/src/scheduler.cpp +10 -0
- package/src/wifi.cpp +2 -0
|
@@ -0,0 +1,560 @@
|
|
|
1
|
+
#ifndef _PAINLESS_MESH_MESH_HPP_
|
|
2
|
+
#define _PAINLESS_MESH_MESH_HPP_
|
|
3
|
+
|
|
4
|
+
#include "painlessmesh/configuration.hpp"
|
|
5
|
+
|
|
6
|
+
#include "painlessmesh/connection.hpp"
|
|
7
|
+
#include "painlessmesh/logger.hpp"
|
|
8
|
+
#include "painlessmesh/ntp.hpp"
|
|
9
|
+
#include "painlessmesh/plugin.hpp"
|
|
10
|
+
#include "painlessmesh/protocol.hpp"
|
|
11
|
+
#include "painlessmesh/tcp.hpp"
|
|
12
|
+
|
|
13
|
+
#ifdef PAINLESSMESH_ENABLE_OTA
|
|
14
|
+
#include "painlessmesh/ota.hpp"
|
|
15
|
+
#endif
|
|
16
|
+
|
|
17
|
+
namespace painlessmesh {
|
|
18
|
+
typedef std::function<void(uint32_t nodeId)> newConnectionCallback_t;
|
|
19
|
+
typedef std::function<void(uint32_t nodeId)> droppedConnectionCallback_t;
|
|
20
|
+
typedef std::function<void(uint32_t from, TSTRING &msg)> receivedCallback_t;
|
|
21
|
+
typedef std::function<void()> changedConnectionsCallback_t;
|
|
22
|
+
typedef std::function<void(int32_t offset)> nodeTimeAdjustedCallback_t;
|
|
23
|
+
typedef std::function<void(uint32_t nodeId, int32_t delay)> nodeDelayCallback_t;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Main api class for the mesh
|
|
27
|
+
*
|
|
28
|
+
* Brings all the functions together except for the WiFi functions
|
|
29
|
+
*/
|
|
30
|
+
template <class T>
|
|
31
|
+
class Mesh : public ntp::MeshTime, public plugin::PackageHandler<T> {
|
|
32
|
+
public:
|
|
33
|
+
void init(uint32_t id) {
|
|
34
|
+
using namespace logger;
|
|
35
|
+
if (!isExternalScheduler) {
|
|
36
|
+
mScheduler = new Scheduler();
|
|
37
|
+
}
|
|
38
|
+
this->nodeId = id;
|
|
39
|
+
|
|
40
|
+
#ifdef ESP32
|
|
41
|
+
xSemaphore = xSemaphoreCreateMutex();
|
|
42
|
+
#endif
|
|
43
|
+
|
|
44
|
+
// Add package handlers
|
|
45
|
+
this->callbackList = painlessmesh::ntp::addPackageCallback(
|
|
46
|
+
std::move(this->callbackList), (*this));
|
|
47
|
+
this->callbackList = painlessmesh::router::addPackageCallback(
|
|
48
|
+
std::move(this->callbackList), (*this));
|
|
49
|
+
|
|
50
|
+
this->changedConnectionCallbacks.push_back([this](uint32_t nodeId) {
|
|
51
|
+
Log(MESH_STATUS, "Changed connections in neighbour %u\n", nodeId);
|
|
52
|
+
if (nodeId != 0) layout::syncLayout<T>((*this), nodeId);
|
|
53
|
+
});
|
|
54
|
+
this->droppedConnectionCallbacks.push_back([this](uint32_t nodeId,
|
|
55
|
+
bool station) {
|
|
56
|
+
Log(MESH_STATUS, "Dropped connection %u, station %d\n", nodeId, station);
|
|
57
|
+
this->eraseClosedConnections();
|
|
58
|
+
});
|
|
59
|
+
this->newConnectionCallbacks.push_back([](uint32_t nodeId) {
|
|
60
|
+
Log(MESH_STATUS, "New connection %u\n", nodeId);
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
void init(Scheduler *scheduler, uint32_t id) {
|
|
65
|
+
this->setScheduler(scheduler);
|
|
66
|
+
this->init(id);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
#ifdef PAINLESSMESH_ENABLE_OTA
|
|
70
|
+
std::shared_ptr<Task> offerOTA(painlessmesh::plugin::ota::Announce announce) {
|
|
71
|
+
auto announceTask = this->addTask(TASK_SECOND * 60, 60, [this, announce]() {
|
|
72
|
+
this->sendPackage(&announce);
|
|
73
|
+
});
|
|
74
|
+
return announceTask;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
std::shared_ptr<Task> offerOTA(TSTRING role, TSTRING hardware, TSTRING md5,
|
|
78
|
+
size_t noPart, bool forced = false) {
|
|
79
|
+
painlessmesh::plugin::ota::Announce announce;
|
|
80
|
+
announce.md5 = md5;
|
|
81
|
+
announce.role = role;
|
|
82
|
+
announce.hardware = hardware;
|
|
83
|
+
announce.from = this->nodeId;
|
|
84
|
+
announce.noPart = noPart;
|
|
85
|
+
announce.forced = forced;
|
|
86
|
+
return offerOTA(announce);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
void initOTASend(
|
|
90
|
+
painlessmesh::plugin::ota::otaDataPacketCallbackType_t callback,
|
|
91
|
+
size_t otaPartSize) {
|
|
92
|
+
painlessmesh::plugin::ota::addSendPackageCallback(
|
|
93
|
+
*this->mScheduler, (*this), callback, otaPartSize);
|
|
94
|
+
}
|
|
95
|
+
void initOTAReceive(TSTRING role = "",
|
|
96
|
+
std::function<void(int, int)> progress_cb = NULL) {
|
|
97
|
+
painlessmesh::plugin::ota::addReceivePackageCallback(
|
|
98
|
+
*this->mScheduler, (*this), role, progress_cb);
|
|
99
|
+
}
|
|
100
|
+
#endif
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Set the node as an root/master node for the mesh
|
|
104
|
+
*
|
|
105
|
+
* This is an optional setting that can speed up mesh formation.
|
|
106
|
+
* At most one node in the mesh should be a root, or you could
|
|
107
|
+
* end up with multiple subMeshes.
|
|
108
|
+
*
|
|
109
|
+
* We recommend any AP_ONLY nodes (e.g. a bridgeNode) to be set
|
|
110
|
+
* as a root node.
|
|
111
|
+
*
|
|
112
|
+
* If one node is root, then it is also recommended to call
|
|
113
|
+
* painlessMesh::setContainsRoot() on all the nodes in the mesh.
|
|
114
|
+
*/
|
|
115
|
+
void setRoot(bool on = true) { this->root = on; };
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* The mesh should contains a root node
|
|
119
|
+
*
|
|
120
|
+
* This will cause the mesh to restructure more quickly around the root node.
|
|
121
|
+
* Note that this could have adverse effects if set, while there is no root
|
|
122
|
+
* node present. Also see painlessMesh::setRoot().
|
|
123
|
+
*/
|
|
124
|
+
void setContainsRoot(bool on = true) { shouldContainRoot = on; };
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Check whether this node is a root node.
|
|
128
|
+
*/
|
|
129
|
+
bool isRoot() { return this->root; };
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Change the internal log level
|
|
133
|
+
*/
|
|
134
|
+
void setDebugMsgTypes(uint16_t types) { Log.setLogLevel(types); }
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Disconnect and stop this node
|
|
138
|
+
*/
|
|
139
|
+
void stop() {
|
|
140
|
+
using namespace logger;
|
|
141
|
+
// Close all connections
|
|
142
|
+
while (this->subs.size() > 0) {
|
|
143
|
+
auto conn = this->subs.begin();
|
|
144
|
+
(*conn)->close();
|
|
145
|
+
this->eraseClosedConnections();
|
|
146
|
+
}
|
|
147
|
+
plugin::PackageHandler<T>::stop();
|
|
148
|
+
|
|
149
|
+
newConnectionCallbacks.clear();
|
|
150
|
+
droppedConnectionCallbacks.clear();
|
|
151
|
+
changedConnectionCallbacks.clear();
|
|
152
|
+
|
|
153
|
+
if (!isExternalScheduler) {
|
|
154
|
+
delete mScheduler;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/** Perform crucial maintenance task
|
|
159
|
+
*
|
|
160
|
+
* Add this to your loop() function. This routine runs various maintenance
|
|
161
|
+
* tasks.
|
|
162
|
+
*/
|
|
163
|
+
void update(void) {
|
|
164
|
+
if (semaphoreTake()) {
|
|
165
|
+
// Check if something is executed (returns false)
|
|
166
|
+
if (!mScheduler->execute())
|
|
167
|
+
Log(logger::GENERAL, "update(): Scheduler executed a task\n");
|
|
168
|
+
semaphoreGive();
|
|
169
|
+
}
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/** Send message to a specific node
|
|
174
|
+
*
|
|
175
|
+
* @param destId The nodeId of the node to send it to.
|
|
176
|
+
* @param msg The message to send
|
|
177
|
+
*
|
|
178
|
+
* @return true if everything works, false if not.
|
|
179
|
+
*/
|
|
180
|
+
bool sendSingle(uint32_t destId, TSTRING msg) {
|
|
181
|
+
Log(logger::COMMUNICATION, "sendSingle(): dest=%u msg=%s\n", destId,
|
|
182
|
+
msg.c_str());
|
|
183
|
+
auto single = painlessmesh::protocol::Single(this->nodeId, destId, msg);
|
|
184
|
+
return painlessmesh::router::send<T>(single, (*this));
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/** Broadcast a message to every node on the mesh network.
|
|
188
|
+
*
|
|
189
|
+
* @param includeSelf Send message to myself as well. Default is false.
|
|
190
|
+
*
|
|
191
|
+
* @return true if everything works, false if not
|
|
192
|
+
*/
|
|
193
|
+
bool sendBroadcast(TSTRING msg, bool includeSelf = false) {
|
|
194
|
+
using namespace logger;
|
|
195
|
+
Log(COMMUNICATION, "sendBroadcast(): msg=%s\n", msg.c_str());
|
|
196
|
+
painlessmesh::protocol::Broadcast pkg(this->nodeId, 0, msg);
|
|
197
|
+
auto success = router::broadcast<protocol::Broadcast, T>(pkg, (*this), 0);
|
|
198
|
+
if (includeSelf) {
|
|
199
|
+
protocol::Variant var(pkg);
|
|
200
|
+
this->callbackList.execute(var.type(), var, NULL, 0);
|
|
201
|
+
}
|
|
202
|
+
if (success > 0) return true;
|
|
203
|
+
return false;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/** Sends a node a packet to measure network trip delay to that node.
|
|
207
|
+
*
|
|
208
|
+
* After calling this function, user program have to wait to the response in
|
|
209
|
+
* the form of a callback specified by onNodeDelayReceived().
|
|
210
|
+
*
|
|
211
|
+
* @return true if nodeId is connected to the mesh, false otherwise
|
|
212
|
+
*/
|
|
213
|
+
bool startDelayMeas(uint32_t id) {
|
|
214
|
+
using namespace logger;
|
|
215
|
+
Log(S_TIME, "startDelayMeas(): NodeId %u\n", id);
|
|
216
|
+
auto conn = painlessmesh::router::findRoute<T>((*this), id);
|
|
217
|
+
if (!conn) return false;
|
|
218
|
+
return router::send<protocol::TimeDelay, T>(
|
|
219
|
+
protocol::TimeDelay(this->nodeId, id, this->getNodeTime()), conn);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/** Set a callback routine for any messages that are addressed to this node.
|
|
223
|
+
*
|
|
224
|
+
* Every time this node receives a message, this callback routine will the
|
|
225
|
+
* called. “from” is the id of the original sender of the message, and “msg”
|
|
226
|
+
* is a string that contains the message. The message can be anything. A
|
|
227
|
+
* JSON, some other text string, or binary data.
|
|
228
|
+
*
|
|
229
|
+
* \code
|
|
230
|
+
* mesh.onReceive([](auto nodeId, auto msg) {
|
|
231
|
+
* // Do something with the message
|
|
232
|
+
* Serial.println(msg);
|
|
233
|
+
* });
|
|
234
|
+
* \endcode
|
|
235
|
+
*/
|
|
236
|
+
void onReceive(receivedCallback_t onReceive) {
|
|
237
|
+
using namespace painlessmesh;
|
|
238
|
+
this->callbackList.onPackage(
|
|
239
|
+
protocol::SINGLE,
|
|
240
|
+
[onReceive](protocol::Variant &variant, std::shared_ptr<T>, uint32_t) {
|
|
241
|
+
auto pkg = variant.to<protocol::Single>();
|
|
242
|
+
onReceive(pkg.from, pkg.msg);
|
|
243
|
+
return false;
|
|
244
|
+
});
|
|
245
|
+
this->callbackList.onPackage(
|
|
246
|
+
protocol::BROADCAST,
|
|
247
|
+
[onReceive](protocol::Variant &variant, std::shared_ptr<T>, uint32_t) {
|
|
248
|
+
auto pkg = variant.to<protocol::Broadcast>();
|
|
249
|
+
onReceive(pkg.from, pkg.msg);
|
|
250
|
+
return false;
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/** Callback that gets called every time the local node makes a new
|
|
255
|
+
* connection.
|
|
256
|
+
*
|
|
257
|
+
* \code
|
|
258
|
+
* mesh.onNewConnection([](auto nodeId) {
|
|
259
|
+
* // Do something with the event
|
|
260
|
+
* Serial.println(String(nodeId));
|
|
261
|
+
* });
|
|
262
|
+
* \endcode
|
|
263
|
+
*/
|
|
264
|
+
void onNewConnection(newConnectionCallback_t onNewConnection) {
|
|
265
|
+
Log(logger::GENERAL, "onNewConnection():\n");
|
|
266
|
+
newConnectionCallbacks.push_back([onNewConnection](uint32_t nodeId) {
|
|
267
|
+
if (nodeId != 0) onNewConnection(nodeId);
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/** Callback that gets called every time the local node drops a connection.
|
|
272
|
+
*
|
|
273
|
+
* \code
|
|
274
|
+
* mesh.onDroppedConnection([](auto nodeId) {
|
|
275
|
+
* // Do something with the event
|
|
276
|
+
* Serial.println(String(nodeId));
|
|
277
|
+
* });
|
|
278
|
+
* \endcode
|
|
279
|
+
*/
|
|
280
|
+
void onDroppedConnection(droppedConnectionCallback_t onDroppedConnection) {
|
|
281
|
+
droppedConnectionCallbacks.push_back(
|
|
282
|
+
[onDroppedConnection](uint32_t nodeId, bool station) {
|
|
283
|
+
if (nodeId != 0) onDroppedConnection(nodeId);
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/** Callback that gets called every time the layout of the mesh changes
|
|
288
|
+
*
|
|
289
|
+
* \code
|
|
290
|
+
* mesh.onChangedConnections([]() {
|
|
291
|
+
* // Do something with the event
|
|
292
|
+
* });
|
|
293
|
+
* \endcode
|
|
294
|
+
*/
|
|
295
|
+
void onChangedConnections(changedConnectionsCallback_t onChangedConnections) {
|
|
296
|
+
Log(logger::GENERAL, "onChangedConnections():\n");
|
|
297
|
+
changedConnectionCallbacks.push_back(
|
|
298
|
+
[onChangedConnections](uint32_t nodeId) {
|
|
299
|
+
if (nodeId != 0) onChangedConnections();
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/** Callback that gets called every time node time gets adjusted
|
|
304
|
+
*
|
|
305
|
+
* Node time is automatically kept in sync in the mesh. This gets called
|
|
306
|
+
* whenever the time is to far out of sync with the rest of the mesh and gets
|
|
307
|
+
* adjusted.
|
|
308
|
+
*
|
|
309
|
+
* \code
|
|
310
|
+
* mesh.onNodeTimeAdjusted([](auto offset) {
|
|
311
|
+
* // Do something with the event
|
|
312
|
+
* Serial.println(String(offset));
|
|
313
|
+
* });
|
|
314
|
+
* \endcode
|
|
315
|
+
*/
|
|
316
|
+
void onNodeTimeAdjusted(nodeTimeAdjustedCallback_t onTimeAdjusted) {
|
|
317
|
+
Log(logger::GENERAL, "onNodeTimeAdjusted():\n");
|
|
318
|
+
nodeTimeAdjustedCallback = onTimeAdjusted;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/** Callback that gets called when a delay measurement is received.
|
|
322
|
+
*
|
|
323
|
+
* This fires when a time delay masurement response is received, after a
|
|
324
|
+
* request was sent.
|
|
325
|
+
*
|
|
326
|
+
* \code
|
|
327
|
+
* mesh.onNodeDelayReceived([](auto nodeId, auto delay) {
|
|
328
|
+
* // Do something with the event
|
|
329
|
+
* Serial.println(String(delay));
|
|
330
|
+
* });
|
|
331
|
+
* \endcode
|
|
332
|
+
*/
|
|
333
|
+
void onNodeDelayReceived(nodeDelayCallback_t onDelayReceived) {
|
|
334
|
+
Log(logger::GENERAL, "onNodeDelayReceived():\n");
|
|
335
|
+
nodeDelayReceivedCallback = onDelayReceived;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Are we connected/know a route to the given node?
|
|
340
|
+
*
|
|
341
|
+
* @param nodeId The nodeId we are looking for
|
|
342
|
+
*/
|
|
343
|
+
bool isConnected(uint32_t nodeId) {
|
|
344
|
+
return painlessmesh::router::findRoute<T>((*this), nodeId) != NULL;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/** Get a list of all known nodes.
|
|
348
|
+
*
|
|
349
|
+
* This includes nodes that are both directly and indirectly connected to the
|
|
350
|
+
* current node.
|
|
351
|
+
*/
|
|
352
|
+
std::list<uint32_t> getNodeList(bool includeSelf = false) {
|
|
353
|
+
return painlessmesh::layout::asList(this->asNodeTree(), includeSelf);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Return a json representation of the current mesh layout
|
|
358
|
+
*/
|
|
359
|
+
inline TSTRING subConnectionJson(bool pretty = false) {
|
|
360
|
+
return this->asNodeTree().toString(pretty);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
inline std::shared_ptr<Task> addTask(unsigned long aInterval,
|
|
364
|
+
long aIterations,
|
|
365
|
+
std::function<void()> aCallback) {
|
|
366
|
+
return plugin::PackageHandler<T>::addTask((*this->mScheduler), aInterval,
|
|
367
|
+
aIterations, aCallback);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
inline std::shared_ptr<Task> addTask(std::function<void()> aCallback) {
|
|
371
|
+
return plugin::PackageHandler<T>::addTask((*this->mScheduler), aCallback);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
~Mesh() {
|
|
375
|
+
this->stop();
|
|
376
|
+
if (!isExternalScheduler) delete mScheduler;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
protected:
|
|
380
|
+
void setScheduler(Scheduler *baseScheduler) {
|
|
381
|
+
this->mScheduler = baseScheduler;
|
|
382
|
+
isExternalScheduler = true;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
void startTimeSync(std::shared_ptr<T> conn) {
|
|
386
|
+
using namespace logger;
|
|
387
|
+
Log(S_TIME, "startTimeSync(): from %u with %u\n", this->nodeId,
|
|
388
|
+
conn->nodeId);
|
|
389
|
+
painlessmesh::protocol::TimeSync timeSync;
|
|
390
|
+
if (ntp::adopt(this->asNodeTree(), (*conn))) {
|
|
391
|
+
timeSync = painlessmesh::protocol::TimeSync(this->nodeId, conn->nodeId,
|
|
392
|
+
this->getNodeTime());
|
|
393
|
+
Log(S_TIME, "startTimeSync(): Requesting time from %u\n", conn->nodeId);
|
|
394
|
+
} else {
|
|
395
|
+
timeSync = painlessmesh::protocol::TimeSync(this->nodeId, conn->nodeId);
|
|
396
|
+
Log(S_TIME, "startTimeSync(): Requesting %u to adopt our time\n",
|
|
397
|
+
conn->nodeId);
|
|
398
|
+
}
|
|
399
|
+
router::send<protocol::TimeSync, T>(timeSync, conn, true);
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
bool closeConnectionSTA() {
|
|
403
|
+
auto connection = this->subs.begin();
|
|
404
|
+
while (connection != this->subs.end()) {
|
|
405
|
+
if ((*connection)->station) {
|
|
406
|
+
// We found the STA connection, close it
|
|
407
|
+
(*connection)->close();
|
|
408
|
+
return true;
|
|
409
|
+
}
|
|
410
|
+
++connection;
|
|
411
|
+
}
|
|
412
|
+
return false;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
void eraseClosedConnections() {
|
|
416
|
+
using namespace logger;
|
|
417
|
+
Log(CONNECTION, "eraseClosedConnections():\n");
|
|
418
|
+
this->subs.remove_if(
|
|
419
|
+
[](const std::shared_ptr<T> &conn) { return !conn->connected(); });
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
// Callback functions
|
|
423
|
+
callback::List<uint32_t> newConnectionCallbacks;
|
|
424
|
+
callback::List<uint32_t, bool> droppedConnectionCallbacks;
|
|
425
|
+
callback::List<uint32_t> changedConnectionCallbacks;
|
|
426
|
+
nodeTimeAdjustedCallback_t nodeTimeAdjustedCallback;
|
|
427
|
+
nodeDelayCallback_t nodeDelayReceivedCallback;
|
|
428
|
+
#ifdef ESP32
|
|
429
|
+
SemaphoreHandle_t xSemaphore = NULL;
|
|
430
|
+
#endif
|
|
431
|
+
|
|
432
|
+
bool isExternalScheduler = false;
|
|
433
|
+
|
|
434
|
+
/// Is the node a root node
|
|
435
|
+
bool shouldContainRoot;
|
|
436
|
+
|
|
437
|
+
Scheduler *mScheduler;
|
|
438
|
+
|
|
439
|
+
/**
|
|
440
|
+
* Wrapper function for ESP32 semaphore function
|
|
441
|
+
*
|
|
442
|
+
* Waits for the semaphore to be available and then returns true
|
|
443
|
+
*
|
|
444
|
+
* Always return true on ESP8266
|
|
445
|
+
*/
|
|
446
|
+
bool semaphoreTake() {
|
|
447
|
+
#ifdef ESP32
|
|
448
|
+
return xSemaphoreTake(xSemaphore, (TickType_t)10) == pdTRUE;
|
|
449
|
+
#else
|
|
450
|
+
return true;
|
|
451
|
+
#endif
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
/**
|
|
455
|
+
* Wrapper function for ESP32 semaphore give function
|
|
456
|
+
*
|
|
457
|
+
* Does nothing on ESP8266 hardware
|
|
458
|
+
*/
|
|
459
|
+
void semaphoreGive() {
|
|
460
|
+
#ifdef ESP32
|
|
461
|
+
xSemaphoreGive(xSemaphore);
|
|
462
|
+
#endif
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
friend T;
|
|
466
|
+
friend void onDataCb(void *, AsyncClient *, void *, size_t);
|
|
467
|
+
friend void tcpSentCb(void *, AsyncClient *, size_t, uint32_t);
|
|
468
|
+
friend void meshRecvCb(void *, AsyncClient *, void *, size_t);
|
|
469
|
+
friend void painlessmesh::ntp::handleTimeSync<Mesh, T>(
|
|
470
|
+
Mesh &, painlessmesh::protocol::TimeSync, std::shared_ptr<T>, uint32_t);
|
|
471
|
+
friend void painlessmesh::ntp::handleTimeDelay<Mesh, T>(
|
|
472
|
+
Mesh &, painlessmesh::protocol::TimeDelay, std::shared_ptr<T>, uint32_t);
|
|
473
|
+
friend void painlessmesh::router::handleNodeSync<Mesh, T>(
|
|
474
|
+
Mesh &, protocol::NodeTree, std::shared_ptr<T> conn);
|
|
475
|
+
friend void painlessmesh::tcp::initServer<T, Mesh>(AsyncServer &, Mesh &);
|
|
476
|
+
friend void painlessmesh::tcp::connect<T, Mesh>(AsyncClient &, IPAddress,
|
|
477
|
+
uint16_t, Mesh &);
|
|
478
|
+
};
|
|
479
|
+
|
|
480
|
+
class Connection : public painlessmesh::layout::Neighbour,
|
|
481
|
+
public painlessmesh::tcp::BufferedConnection {
|
|
482
|
+
public:
|
|
483
|
+
Mesh<Connection> *mesh;
|
|
484
|
+
bool station = true;
|
|
485
|
+
bool newConnection = true;
|
|
486
|
+
|
|
487
|
+
Task timeSyncTask;
|
|
488
|
+
Task nodeSyncTask;
|
|
489
|
+
Task timeOutTask;
|
|
490
|
+
|
|
491
|
+
Connection(AsyncClient *client, Mesh<painlessmesh::Connection> *mesh,
|
|
492
|
+
bool station)
|
|
493
|
+
: painlessmesh::tcp::BufferedConnection(client),
|
|
494
|
+
mesh(mesh),
|
|
495
|
+
station(station) {}
|
|
496
|
+
|
|
497
|
+
void initTasks() {
|
|
498
|
+
auto self = this->shared_from_this();
|
|
499
|
+
auto mesh = this->mesh;
|
|
500
|
+
this->onReceive([self](const TSTRING &str) {
|
|
501
|
+
auto variant = painlessmesh::protocol::Variant(str);
|
|
502
|
+
|
|
503
|
+
router::routePackage<painlessmesh::Connection>(
|
|
504
|
+
(*self->mesh), self->shared_from_this(), str,
|
|
505
|
+
self->mesh->callbackList, self->mesh->getNodeTime());
|
|
506
|
+
});
|
|
507
|
+
|
|
508
|
+
this->onDisconnect([mesh, self]() {
|
|
509
|
+
Log.remote("id:%u AsyncClient disconnect\n", self->nodeId);
|
|
510
|
+
self->timeSyncTask.setCallback(NULL);
|
|
511
|
+
self->timeSyncTask.disable();
|
|
512
|
+
self->nodeSyncTask.setCallback(NULL);
|
|
513
|
+
self->nodeSyncTask.disable();
|
|
514
|
+
self->timeOutTask.setCallback(NULL);
|
|
515
|
+
self->timeOutTask.disable();
|
|
516
|
+
auto nodeId = self->nodeId;
|
|
517
|
+
auto station = self->station;
|
|
518
|
+
mesh->addTask([mesh, nodeId, station]() {
|
|
519
|
+
mesh->changedConnectionCallbacks.execute(nodeId);
|
|
520
|
+
mesh->droppedConnectionCallbacks.execute(nodeId, station);
|
|
521
|
+
});
|
|
522
|
+
self->clear();
|
|
523
|
+
});
|
|
524
|
+
|
|
525
|
+
using namespace logger;
|
|
526
|
+
|
|
527
|
+
timeOutTask.set(NODE_TIMEOUT, TASK_ONCE, [self]() {
|
|
528
|
+
Log(CONNECTION, "Time out reached\n");
|
|
529
|
+
Log.remote("id:%u TimeOut\n", self->nodeId);
|
|
530
|
+
self->close();
|
|
531
|
+
});
|
|
532
|
+
mesh->mScheduler->addTask(timeOutTask);
|
|
533
|
+
|
|
534
|
+
this->nodeSyncTask.set(TASK_MINUTE, TASK_FOREVER, [self]() {
|
|
535
|
+
Log(SYNC, "nodeSyncTask(): request with %u\n", self->nodeId);
|
|
536
|
+
router::send<protocol::NodeSyncRequest, Connection>(
|
|
537
|
+
self->request(self->mesh->asNodeTree()), self);
|
|
538
|
+
self->timeOutTask.disable();
|
|
539
|
+
self->timeOutTask.restartDelayed();
|
|
540
|
+
});
|
|
541
|
+
|
|
542
|
+
mesh->mScheduler->addTask(this->nodeSyncTask);
|
|
543
|
+
if (station)
|
|
544
|
+
this->nodeSyncTask.enable();
|
|
545
|
+
else
|
|
546
|
+
this->nodeSyncTask.enableDelayed(10 * TASK_SECOND);
|
|
547
|
+
|
|
548
|
+
Log(CONNECTION, "painlessmesh::Connection: New connection established.\n");
|
|
549
|
+
this->initialize(mesh->mScheduler);
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
bool addMessage(const TSTRING &msg, bool priority = false) {
|
|
553
|
+
return this->write(msg, priority);
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
protected:
|
|
557
|
+
std::shared_ptr<Connection> shared_from_this() { return shared_from(this); }
|
|
558
|
+
};
|
|
559
|
+
}; // namespace painlessmesh
|
|
560
|
+
#endif
|