@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,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
|