@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.
Files changed (80) hide show
  1. package/CHANGELOG.md +144 -0
  2. package/LICENSE +674 -0
  3. package/README.md +434 -0
  4. package/RELEASE_GUIDE.md +419 -0
  5. package/docs/DOCUMENTATION_MIGRATION_PLAN.md +176 -0
  6. package/docs/README.md +71 -0
  7. package/docs/alteriom/overview.md +508 -0
  8. package/docs/api/core-api.md +607 -0
  9. package/docs/architecture/mesh-architecture.md +379 -0
  10. package/docs/architecture/plugin-system.md +517 -0
  11. package/docs/getting-started/first-mesh.md +410 -0
  12. package/docs/getting-started/installation.md +275 -0
  13. package/docs/getting-started/quickstart.md +158 -0
  14. package/docs/improvements/README.md +69 -0
  15. package/docs/troubleshooting/common-issues.md +521 -0
  16. package/docs/troubleshooting/faq.md +473 -0
  17. package/docs/tutorials/basic-examples.md +718 -0
  18. package/docs/wiki/API-Reference.md +246 -0
  19. package/docs/wiki/Complete-Documentation.md +123 -0
  20. package/examples/alteriom/README.md +82 -0
  21. package/examples/alteriom/alteriom.ino +186 -0
  22. package/examples/alteriom/alteriom_sensor_node.ino +184 -0
  23. package/examples/alteriom/alteriom_sensor_package.hpp +128 -0
  24. package/examples/alteriom/improved_sensor_node.ino +246 -0
  25. package/examples/alteriom/platformio.ini +25 -0
  26. package/examples/basic/basic.ino +66 -0
  27. package/examples/basic/platformio.ini +25 -0
  28. package/examples/bridge/bridge.ino +51 -0
  29. package/examples/bridge/platformio.ini +25 -0
  30. package/examples/echoNode/echoNode.ino +33 -0
  31. package/examples/echoNode/platformio.ini +25 -0
  32. package/examples/logClient/logClient.ino +109 -0
  33. package/examples/logClient/platformio.ini +25 -0
  34. package/examples/logServer/logServer.ino +81 -0
  35. package/examples/logServer/platformio.ini +25 -0
  36. package/examples/mqttBridge/mqttBridge.ino +118 -0
  37. package/examples/mqttBridge/platformio.ini +26 -0
  38. package/examples/namedMesh/namedMesh.ino +97 -0
  39. package/examples/namedMesh/platformio.ini +25 -0
  40. package/examples/otaReceiver/otaReceiver.ino +79 -0
  41. package/examples/otaReceiver/platformio.ini +25 -0
  42. package/examples/otaSender/nodemcu32s_connections.JPG +0 -0
  43. package/examples/otaSender/otaSender.ino +151 -0
  44. package/examples/otaSender/platformio.ini +25 -0
  45. package/examples/startHere/platformio.ini +25 -0
  46. package/examples/startHere/startHere.ino +159 -0
  47. package/examples/webServer/platformio.ini +27 -0
  48. package/examples/webServer/webServer.ino +89 -0
  49. package/keywords.txt +49 -0
  50. package/library.json +34 -0
  51. package/library.properties +11 -0
  52. package/package.json +78 -0
  53. package/src/AlteriomPainlessMesh.h +98 -0
  54. package/src/arduino/wifi.hpp +365 -0
  55. package/src/boost/asynctcp.hpp +279 -0
  56. package/src/painlessMesh.h +70 -0
  57. package/src/painlessMeshSTA.cpp +236 -0
  58. package/src/painlessMeshSTA.h +58 -0
  59. package/src/painlessTaskOptions.h +4 -0
  60. package/src/painlessmesh/base64.hpp +111 -0
  61. package/src/painlessmesh/buffer.hpp +229 -0
  62. package/src/painlessmesh/callback.hpp +91 -0
  63. package/src/painlessmesh/configuration.hpp +77 -0
  64. package/src/painlessmesh/connection.hpp +192 -0
  65. package/src/painlessmesh/layout.hpp +188 -0
  66. package/src/painlessmesh/logger.hpp +158 -0
  67. package/src/painlessmesh/memory.hpp +120 -0
  68. package/src/painlessmesh/mesh.hpp +560 -0
  69. package/src/painlessmesh/metrics.hpp +323 -0
  70. package/src/painlessmesh/ntp.hpp +263 -0
  71. package/src/painlessmesh/ota.hpp +553 -0
  72. package/src/painlessmesh/plugin.hpp +188 -0
  73. package/src/painlessmesh/protocol.hpp +813 -0
  74. package/src/painlessmesh/router.hpp +322 -0
  75. package/src/painlessmesh/tcp.hpp +71 -0
  76. package/src/painlessmesh/validation.hpp +239 -0
  77. package/src/plugin/performance.hpp +214 -0
  78. package/src/plugin/remote.hpp +64 -0
  79. package/src/scheduler.cpp +10 -0
  80. package/src/wifi.cpp +2 -0
@@ -0,0 +1,279 @@
1
+ #ifndef _BOOST_ASYNCTCP_HPP_
2
+ #define _BOOST_ASYNCTCP_HPP_
3
+
4
+ #include <boost/array.hpp>
5
+ #include <boost/asio.hpp>
6
+ #include <iostream>
7
+
8
+ #ifndef TCP_MSS
9
+ #define TCP_MSS 1024
10
+ #endif
11
+
12
+ using boost::asio::ip::tcp;
13
+
14
+ #define ASYNC_WRITE_FLAG_COPY 0x01
15
+
16
+ class AsyncClient;
17
+
18
+ typedef std::function<void(void*, AsyncClient*)> AcConnectHandler;
19
+ typedef std::function<void(void*, AsyncClient*, size_t len, uint32_t time)>
20
+ AcAckHandler;
21
+ typedef std::function<void(void*, AsyncClient*, int8_t error)> AcErrorHandler;
22
+ typedef std::function<void(void*, AsyncClient*, void* data, size_t len)>
23
+ AcDataHandler;
24
+ // typedef std::function<void(void*, AsyncClient*, struct pbuf* pb)>
25
+ // AcPacketHandler;
26
+ typedef std::function<void(void*, AsyncClient*, uint32_t time)>
27
+ AcTimeoutHandler;
28
+
29
+ typedef boost::asio::ip::address IPAddress;
30
+
31
+ class AsyncClient {
32
+ public:
33
+ AsyncClient(boost::asio::io_context& io_service)
34
+ : _io_service(io_service), mSocket(_io_service) {}
35
+
36
+ bool connect(IPAddress ipaddress, uint16_t port) {
37
+ namespace ip = boost::asio::ip;
38
+ auto endpoint = ip::tcp::endpoint(ipaddress, port);
39
+
40
+ mSocket.async_connect(endpoint, [&](auto& ec) { this->handleConnect(ec); });
41
+ return true;
42
+ }
43
+
44
+ void initRead() {
45
+ mSocket.async_read_some(
46
+ boost::asio::buffer(mInputBuffer, TCP_MSS),
47
+ [&](auto& ec, auto len) { this->handleData(ec, len); });
48
+ }
49
+
50
+ size_t write(const void* data, size_t len,
51
+ size_t copy = ASYNC_WRITE_FLAG_COPY) {
52
+ if (writing) return 0;
53
+ writing = true;
54
+ if (copy == ASYNC_WRITE_FLAG_COPY) {
55
+ memcpy(mWriteBuffer, data, len);
56
+ mSocket.async_send(
57
+ boost::asio::buffer(mWriteBuffer, len),
58
+ [&](auto& ec, auto len) { this->handleWrite(ec, len); });
59
+ } else {
60
+ mSocket.async_send(
61
+ boost::asio::buffer(data, len),
62
+ [&](auto& ec, auto len) { this->handleWrite(ec, len); });
63
+ }
64
+ return len;
65
+ }
66
+
67
+ // Dummy functions for compatibility with ESPAsycnTCP
68
+ void send() {}
69
+ void setNoDelay(bool value = true) {}
70
+ void setRxTimeout(uint32_t timeout) {}
71
+ const char* errorToString(int8_t error) { return ""; }
72
+
73
+ // TODO: check if there is a better one
74
+ void abort() { this->close(true); }
75
+
76
+ void onConnect(AcConnectHandler cb, void* arg = 0) {
77
+ _connect_cb = cb;
78
+ _connect_cb_arg = arg;
79
+ }
80
+
81
+ void onDisconnect(AcConnectHandler cb, void* arg = 0) {
82
+ _discard_cb = cb;
83
+ _discard_cb_arg = arg;
84
+ }
85
+
86
+ void onAck(AcAckHandler cb, void* arg = 0) {
87
+ _sent_cb = cb;
88
+ _sent_cb_arg = arg;
89
+ }
90
+
91
+ void onError(AcErrorHandler cb, void* arg = 0) {
92
+ _error_cb = cb;
93
+ _error_cb_arg = arg;
94
+ }
95
+
96
+ void onData(AcDataHandler cb, void* arg = 0) {
97
+ _recv_cb = cb;
98
+ _recv_cb_arg = arg;
99
+ }
100
+
101
+ void onTimeout(AcTimeoutHandler cb, void* arg = 0) {
102
+ _timeout_cb = cb;
103
+ _timeout_cb_arg = arg;
104
+ }
105
+
106
+ void onPoll(AcConnectHandler cb, void* arg = 0) {
107
+ _poll_cb = cb;
108
+ _poll_cb_arg = arg;
109
+ }
110
+
111
+ bool connected() { return mSocket.is_open(); }
112
+
113
+ bool freeable() { return !this->connected(); }
114
+
115
+ void close(bool now = true) {
116
+ if (this->connected()) {
117
+ mSocket.close();
118
+ }
119
+ if (!disconnectCalled) {
120
+ disconnectCalled = true;
121
+ if (_discard_cb) {
122
+ _discard_cb(_discard_cb_arg, this);
123
+ }
124
+ }
125
+ }
126
+
127
+ ~AsyncClient() { close(true); }
128
+
129
+ size_t space() {
130
+ // This could be more intelligent, but simple and safe for now
131
+ if (writing) return 0;
132
+ return TCP_MSS;
133
+ }
134
+
135
+ bool canSend() { return this->space() > 0; }
136
+
137
+ size_t ack(size_t len) {
138
+ // Currently assumes that len is actually the size of the data that was sent
139
+ // Otherwise it will break
140
+ initRead();
141
+ return len;
142
+ }
143
+
144
+ tcp::socket& socket() { return mSocket; }
145
+
146
+ protected:
147
+ boost::asio::io_context& _io_service;
148
+ tcp::socket mSocket;
149
+
150
+ char mInputBuffer[TCP_MSS];
151
+ char mWriteBuffer[TCP_MSS];
152
+ bool writing = false;
153
+
154
+ bool disconnectCalled = false;
155
+
156
+ AcConnectHandler _connect_cb = 0;
157
+ void* _connect_cb_arg = 0;
158
+ AcConnectHandler _discard_cb = 0;
159
+ void* _discard_cb_arg = 0;
160
+ AcAckHandler _sent_cb = 0;
161
+ void* _sent_cb_arg = 0;
162
+ AcErrorHandler _error_cb = 0;
163
+ void* _error_cb_arg = 0;
164
+ AcDataHandler _recv_cb = 0;
165
+ void* _recv_cb_arg = 0;
166
+ AcTimeoutHandler _timeout_cb = 0;
167
+ void* _timeout_cb_arg = 0;
168
+ AcConnectHandler _poll_cb = 0;
169
+ void* _poll_cb_arg = 0;
170
+
171
+ void handleConnect(const boost::system::error_code& ec) {
172
+ if (!ec) {
173
+ if (_connect_cb) _connect_cb(_connect_cb_arg, this);
174
+
175
+ initRead();
176
+ } else {
177
+ handleError(ec);
178
+ if (this->connected()) close(true);
179
+ }
180
+ }
181
+
182
+ void handleData(const boost::system::error_code& ec, size_t len) {
183
+ if (disconnectCalled) return;
184
+
185
+ if (!ec) {
186
+ if (_recv_cb) {
187
+ _recv_cb(_recv_cb_arg, this, (void*)mInputBuffer, len);
188
+ }
189
+ } else {
190
+ handleError(ec);
191
+ close(true);
192
+ }
193
+ }
194
+
195
+ void handleWrite(const boost::system::error_code& ec, size_t len) {
196
+ if (disconnectCalled) return;
197
+
198
+ if (!ec) {
199
+ if (_sent_cb) {
200
+ // TODO send actual time
201
+ _sent_cb(_sent_cb_arg, this, len, 0);
202
+ }
203
+ writing = false;
204
+ } else {
205
+ handleError(ec);
206
+ close(true);
207
+ }
208
+ }
209
+
210
+ void handleError(const boost::system::error_code& ec) {
211
+ if (ec != boost::asio::error::eof && _error_cb) {
212
+ _error_cb(_error_cb_arg, this, ec.value());
213
+ }
214
+ }
215
+ };
216
+
217
+ class AsyncServer {
218
+ public:
219
+ AsyncServer(boost::asio::io_context& io_service, uint16_t port)
220
+ : _io_service(io_service),
221
+ // mSocket(io_service),
222
+ _port(port),
223
+ mAcceptor(io_service) {}
224
+
225
+ ~AsyncServer() { this->end(); }
226
+
227
+ void onClient(AcConnectHandler cb, void* arg = 0) {
228
+ _connect_cb = cb;
229
+ _connect_cb_arg = arg;
230
+ }
231
+
232
+ // begin() should do start_accept (see server2):w
233
+ void begin() {
234
+ mAcceptor.open(tcp::v4());
235
+ int one = 1;
236
+ setsockopt(mAcceptor.native_handle(), SOL_SOCKET,
237
+ SO_REUSEADDR | SO_REUSEPORT, &one, sizeof(one));
238
+ boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::tcp::v4(), _port);
239
+ mAcceptor.set_option(boost::asio::socket_base::reuse_address(true));
240
+ mAcceptor.set_option(boost::asio::ip::tcp::no_delay(true));
241
+ mAcceptor.set_option(boost::asio::socket_base::send_buffer_size(65536));
242
+ mAcceptor.set_option(boost::asio::socket_base::receive_buffer_size(65536));
243
+ mAcceptor.bind(endpoint);
244
+ mAcceptor.listen();
245
+ initAccept();
246
+ }
247
+
248
+ // Acceptor stop?
249
+ void end() { mAcceptor.close(); }
250
+
251
+ // Dummy function for compatibility with ESPAsycnTCP
252
+ void setNoDelay(bool value = true) {}
253
+
254
+ protected:
255
+ boost::asio::io_context& _io_service;
256
+ uint16_t _port;
257
+ tcp::acceptor mAcceptor;
258
+ AcConnectHandler _connect_cb = 0;
259
+ void* _connect_cb_arg = 0;
260
+
261
+ void initAccept() {
262
+ #if BOOST_VERSION >= 107000
263
+ AsyncClient* client = new AsyncClient(_io_service);
264
+ #else
265
+ AsyncClient* client = new AsyncClient(mAcceptor.get_io_service());
266
+ #endif
267
+ mAcceptor.async_accept(
268
+ client->socket(), [this, client](const boost::system::error_code& e) {
269
+ if (!e && this->_connect_cb) {
270
+ this->_connect_cb(this->_connect_cb_arg, client);
271
+ this->initAccept();
272
+ client->initRead();
273
+ } else
274
+ std::cout << "Error: " << e.message() << std::endl;
275
+ });
276
+ }
277
+ };
278
+
279
+ #endif
@@ -0,0 +1,70 @@
1
+ #ifndef _EASY_MESH_H_
2
+ #define _EASY_MESH_H_
3
+
4
+ #include "painlessTaskOptions.h"
5
+
6
+ #include <Arduino.h>
7
+ #include <functional>
8
+ #include <list>
9
+ #include <memory>
10
+ #include "painlessmesh/configuration.hpp"
11
+ #ifdef ESP32
12
+ #include <AsyncTCP.h>
13
+ #include <WiFi.h>
14
+ #if ESP_ARDUINO_VERSION_MAJOR >= 3
15
+ #include "esp_mac.h" // required for core 3.x - exposes esp_mac_type_t values
16
+ #endif
17
+ #elif defined(ESP8266)
18
+ #include <ESP8266WiFi.h>
19
+ #include <ESPAsyncTCP.h>
20
+ #endif // ESP32
21
+
22
+ #ifdef PAINLESSMESH_ENABLE_ARDUINO_WIFI
23
+ #include "painlessMeshSTA.h"
24
+
25
+ #include "arduino/wifi.hpp"
26
+ #endif
27
+
28
+ #ifdef PAINLESSMESH_ENABLE_OTA
29
+ #include "painlessmesh/ota.hpp"
30
+ #endif
31
+
32
+ // Include improvement modules when enabled
33
+ #ifdef PAINLESSMESH_ENABLE_VALIDATION
34
+ #include "painlessmesh/validation.hpp"
35
+ #endif
36
+
37
+ #ifdef PAINLESSMESH_ENABLE_METRICS
38
+ #include "painlessmesh/metrics.hpp"
39
+ #endif
40
+
41
+ #ifdef PAINLESSMESH_ENABLE_MEMORY_OPTIMIZATION
42
+ #include "painlessmesh/memory.hpp"
43
+ #endif
44
+
45
+ #include "painlessmesh/buffer.hpp"
46
+ #include "painlessmesh/layout.hpp"
47
+ #include "painlessmesh/logger.hpp"
48
+ #include "painlessmesh/mesh.hpp"
49
+ #include "painlessmesh/ntp.hpp"
50
+ #include "painlessmesh/plugin.hpp"
51
+ #include "painlessmesh/protocol.hpp"
52
+ #include "painlessmesh/router.hpp"
53
+ #include "painlessmesh/tcp.hpp"
54
+ using namespace painlessmesh::logger;
55
+
56
+ /*! \mainpage painlessMesh: A painless way to setup a mesh.
57
+ *
58
+ * painlessMesh is designed in a modular way, with many parent classes. The best
59
+ * place to get started with the documentation is to have a look at
60
+ * painlessmesh::wifi::Mesh (the main painlessMesh class is an alias (typedef)
61
+ * of the painlessmesh::wifi::Mesh class). Make sure to also explore the public
62
+ * member functions inherited from other classes, to get full information on the
63
+ * functions available to you.
64
+ */
65
+
66
+ #ifndef PAINLESSMESH_ENABLE_ARDUINO_WIFI
67
+ using painlessMesh = painlessmesh::Mesh<Connection>;
68
+ #endif
69
+
70
+ #endif // _EASY_MESH_H_
@@ -0,0 +1,236 @@
1
+ //
2
+ // painlessMeshSTA.cpp
3
+ //
4
+ //
5
+ // Created by Bill Gray on 7/26/16.
6
+ //
7
+ //
8
+ #include "painlessmesh/configuration.hpp"
9
+
10
+ #ifdef PAINLESSMESH_ENABLE_ARDUINO_WIFI
11
+
12
+ #include <Arduino.h>
13
+ #include <algorithm>
14
+ #include <memory>
15
+
16
+ #include "arduino/wifi.hpp"
17
+
18
+ #include "painlessmesh/layout.hpp"
19
+ #include "painlessmesh/tcp.hpp"
20
+
21
+ extern painlessmesh::logger::LogClass Log;
22
+
23
+ //***********************************************************************
24
+ // Calculate NodeID from a hardware MAC address
25
+ void ICACHE_FLASH_ATTR StationScan::init(painlessmesh::wifi::Mesh *pMesh,
26
+ TSTRING pssid, TSTRING ppassword,
27
+ uint16_t pport, uint8_t pchannel,
28
+ bool phidden) {
29
+ ssid = pssid;
30
+ password = ppassword;
31
+ mesh = pMesh;
32
+ port = pport;
33
+ channel = pchannel;
34
+ hidden = phidden;
35
+
36
+ task.set(SCAN_INTERVAL, TASK_FOREVER, [this]() { stationScan(); });
37
+ }
38
+
39
+ // Starts scan for APs whose name is Mesh SSID
40
+ void ICACHE_FLASH_ATTR StationScan::stationScan() {
41
+ using namespace painlessmesh::logger;
42
+ Log(CONNECTION, "stationScan(): %s\n", ssid.c_str());
43
+
44
+ #ifdef ESP32
45
+ WiFi.scanNetworks(true, hidden, false, 300U, channel);
46
+ #elif defined(ESP8266)
47
+ // WiFi.scanNetworksAsync([&](int networks) { this->scanComplete(); }, true);
48
+ // Try 600 times (60 seconds). If not completed after that, give up
49
+ asyncTask.set(100 * TASK_MILLISECOND, 600, [this]() {
50
+ auto num = WiFi.scanComplete();
51
+ if (num == WIFI_SCAN_FAILED || num > 0) {
52
+ this->asyncTask.disable();
53
+ this->scanComplete();
54
+ }
55
+ });
56
+ mesh->mScheduler->addTask(asyncTask);
57
+ asyncTask.enableDelayed();
58
+ WiFi.scanNetworks(true, hidden, channel);
59
+ #endif
60
+
61
+ task.delay(10 * SCAN_INTERVAL); // Scan should be completed by then and next
62
+ // step called. If not then we restart here.
63
+ return;
64
+ }
65
+
66
+ void ICACHE_FLASH_ATTR StationScan::scanComplete() {
67
+ using namespace painlessmesh::logger;
68
+ Log(CONNECTION, "scanComplete(): Scan finished\n");
69
+
70
+ aps.clear();
71
+ Log(CONNECTION, "scanComplete():-- > Cleared old APs.\n");
72
+
73
+ auto num = WiFi.scanComplete();
74
+ if (num == WIFI_SCAN_FAILED) {
75
+ Log(ERROR, "wifi scan failed. Retrying....\n");
76
+ task.forceNextIteration();
77
+ return;
78
+ } else if (num == WIFI_SCAN_RUNNING) {
79
+ Log(ERROR,
80
+ "scanComplete should never be called when scan is still running.\n");
81
+ return;
82
+ }
83
+
84
+ Log(CONNECTION, "scanComplete(): num = %d\n", num);
85
+
86
+ for (auto i = 0; i < num; ++i) {
87
+ WiFi_AP_Record_t record;
88
+ record.ssid = WiFi.SSID(i);
89
+
90
+ if (WiFi.channel(i) != mesh->_meshChannel) {
91
+ continue;
92
+ }
93
+
94
+ if (record.ssid != ssid) {
95
+ if (record.ssid.equals("") && mesh->_meshHidden) {
96
+ // Hidden mesh
97
+ record.ssid = ssid;
98
+ } else {
99
+ continue;
100
+ }
101
+ }
102
+
103
+ record.rssi = WiFi.RSSI(i);
104
+ if (record.rssi == 0) continue;
105
+
106
+ memcpy((void *)&record.bssid, (void *)WiFi.BSSID(i), sizeof(record.bssid));
107
+ aps.push_back(record);
108
+ Log(CONNECTION, "\tfound : %s, %ddBm\n", record.ssid.c_str(),
109
+ (int16_t)record.rssi);
110
+ }
111
+
112
+ Log(CONNECTION, "\tFound %d nodes\n", aps.size());
113
+
114
+ task.yield([this]() {
115
+ // Task filter all unknown
116
+ filterAPs();
117
+
118
+ lastAPs = aps;
119
+
120
+ // Next task is to sort by strength
121
+ task.yield([this] {
122
+ aps.sort([](WiFi_AP_Record_t a, WiFi_AP_Record_t b) {
123
+ return a.rssi > b.rssi;
124
+ });
125
+ // Next task is to connect to the top ap
126
+ task.yield([this]() { connectToAP(); });
127
+ });
128
+ });
129
+ }
130
+
131
+ void ICACHE_FLASH_ATTR StationScan::filterAPs() {
132
+ auto ap = aps.begin();
133
+ while (ap != aps.end()) {
134
+ auto apNodeId = painlessmesh::tcp::encodeNodeId(ap->bssid);
135
+ if (painlessmesh::router::findRoute<painlessmesh::Connection>(
136
+ (*mesh), apNodeId) != NULL) {
137
+ ap = aps.erase(ap);
138
+ } else {
139
+ ap++;
140
+ }
141
+ }
142
+ }
143
+
144
+ void ICACHE_FLASH_ATTR StationScan::requestIP(WiFi_AP_Record_t &ap) {
145
+ using namespace painlessmesh::logger;
146
+ Log(CONNECTION, "connectToAP(): Best AP is %u<---\n",
147
+ painlessmesh::tcp::encodeNodeId(ap.bssid));
148
+ WiFi.begin(ap.ssid.c_str(), password.c_str(), mesh->_meshChannel, ap.bssid);
149
+ return;
150
+ }
151
+
152
+ void ICACHE_FLASH_ATTR StationScan::connectToAP() {
153
+ using namespace painlessmesh;
154
+ using namespace painlessmesh::logger;
155
+ // Next task will be to rescan
156
+ task.setCallback([this]() { stationScan(); });
157
+
158
+ if (manual) {
159
+ if ((WiFi.SSID() == ssid) && WiFi.status() == WL_CONNECTED) {
160
+ Log(CONNECTION,
161
+ "connectToAP(): Already connected using manual connection. "
162
+ "Disabling scanning.\n");
163
+ task.disable();
164
+ return;
165
+ } else {
166
+ if (WiFi.status() == WL_CONNECTED) {
167
+ Log.remote("Close Sta because trying to connect manually\n");
168
+ mesh->closeConnectionSTA();
169
+ task.enableDelayed(10 * SCAN_INTERVAL);
170
+ return;
171
+ } else if (aps.empty() || !ssid.equals(aps.begin()->ssid)) {
172
+ task.enableDelayed(SCAN_INTERVAL);
173
+ return;
174
+ }
175
+ }
176
+ }
177
+ bool isRooted = layout::isRooted(mesh->asNodeTree());
178
+ if (aps.empty()) {
179
+ // No unknown nodes found
180
+ if (WiFi.status() == WL_CONNECTED &&
181
+ !(mesh->shouldContainRoot && !isRooted)) {
182
+ // if already connected -> scan slow
183
+ Log(CONNECTION,
184
+ "connectToAP(): Already connected, and no unknown nodes found: "
185
+ "scan rate set to slow\n");
186
+ task.delay(4 * SCAN_INTERVAL);
187
+ } else {
188
+ // else scan fast (SCAN_INTERVAL)
189
+ Log(CONNECTION,
190
+ "connectToAP(): No unknown nodes found scan rate set to "
191
+ "fast\n");
192
+ task.setInterval(0.5 * SCAN_INTERVAL);
193
+ }
194
+ mesh->stability += min(1000 - mesh->stability, (size_t)25);
195
+ } else {
196
+ if (WiFi.status() == WL_CONNECTED) {
197
+ // TODO: Use %u instead of String() here and below
198
+ // Also prob is always equal to stability, so we should use that directly
199
+ Log(CONNECTION,
200
+ "connectToAP(): Unknown nodes found. Current stability: %s\n",
201
+ String(mesh->stability).c_str());
202
+
203
+ int prob = mesh->stability;
204
+ if (!isRooted && random(0, 1000) < prob) {
205
+ Log(CONNECTION, "connectToAP(): Reconfigure network: %s\n",
206
+ String(prob).c_str());
207
+ // close STA connection, this will trigger station disconnect which
208
+ // will trigger connectToAP()
209
+ mesh->closeConnectionSTA();
210
+ mesh->stability = 0; // Discourage switching again
211
+ Log.remote("Close Sta to reconfigure network\n");
212
+ // wifiEventCB should be triggered before this delay runs out
213
+ // and reset the connecting
214
+ task.delay(4 * SCAN_INTERVAL);
215
+ } else {
216
+ if (!isRooted && mesh->shouldContainRoot)
217
+ // Increase scanning rate, because we want to find root
218
+ task.delay(0.5 * SCAN_INTERVAL);
219
+ else
220
+ task.delay(4 * SCAN_INTERVAL);
221
+ }
222
+ } else {
223
+ // Else try to connect to first
224
+ auto ap = aps.front();
225
+ aps.pop_front(); // drop bestAP from mesh list, so if doesn't work out,
226
+ // we can try the next one
227
+ requestIP(ap);
228
+ // Trying to connect, if that fails we will reconnect later
229
+ Log(CONNECTION,
230
+ "connectToAP(): Trying to connect, scan rate set to "
231
+ "4*normal\n");
232
+ task.delay(4 * SCAN_INTERVAL);
233
+ }
234
+ }
235
+ }
236
+ #endif
@@ -0,0 +1,58 @@
1
+ #ifndef _PAINLESS_MESH_STA_H_
2
+ #define _PAINLESS_MESH_STA_H_
3
+
4
+ #include "painlessmesh/configuration.hpp"
5
+
6
+ #include "painlessmesh/mesh.hpp"
7
+
8
+ #include <list>
9
+
10
+ typedef struct {
11
+ uint8_t bssid[6];
12
+ TSTRING ssid;
13
+ int8_t rssi;
14
+ } WiFi_AP_Record_t;
15
+
16
+ class StationScan {
17
+ public:
18
+ Task task; // Station scanning for connections
19
+
20
+ #ifdef ESP8266
21
+ Task asyncTask;
22
+ #endif
23
+
24
+ StationScan() {}
25
+ void init(painlessmesh::wifi::Mesh *pMesh, TSTRING ssid, TSTRING password,
26
+ uint16_t port, uint8_t channel, bool hidden);
27
+ void stationScan();
28
+ void scanComplete();
29
+ void filterAPs();
30
+ void connectToAP();
31
+ // This one will call the connectToAP next in the task and should be used
32
+ // instead of connectToAP
33
+ void yieldConnectToAP() {
34
+ task.yield([this]() { connectToAP(); });
35
+ }
36
+
37
+ /// Valid APs found during the last scan
38
+ std::list<WiFi_AP_Record_t> lastAPs;
39
+
40
+ protected:
41
+ TSTRING ssid;
42
+ TSTRING password;
43
+ painlessMesh *mesh;
44
+ uint16_t port;
45
+ uint8_t channel;
46
+ bool hidden;
47
+ std::list<WiFi_AP_Record_t> aps;
48
+
49
+ void requestIP(WiFi_AP_Record_t &ap);
50
+
51
+ // Manually configure network and ip
52
+ bool manual = false;
53
+ IPAddress manualIP = IPAddress(0, 0, 0, 0);
54
+
55
+ friend painlessMesh;
56
+ };
57
+
58
+ #endif
@@ -0,0 +1,4 @@
1
+ // The following two complie options are required for painlessMesh
2
+ #define _TASK_PRIORITY // Support for layered scheduling priority
3
+ #define _TASK_STD_FUNCTION // Support for std::function (ESP8266 and ESP32
4
+ // ONLY)