@alteriom/painlessmesh 1.6.1 → 1.7.2

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 (129) hide show
  1. package/CHANGELOG.md +380 -143
  2. package/LICENSE +674 -674
  3. package/README.md +477 -434
  4. package/RELEASE_GUIDE.md +504 -418
  5. package/docs/DOCUMENTATION_MIGRATION_PLAN.md +175 -175
  6. package/docs/IMPLEMENTATION_PLAN_MESH_TOPOLOGY.md +1062 -0
  7. package/docs/MESH_TOPOLOGY_GUIDE.md +992 -0
  8. package/docs/MESH_TOPOLOGY_PROGRESS.md +422 -0
  9. package/docs/MQTT_BRIDGE_COMMANDS.md +894 -0
  10. package/docs/MQTT_BRIDGE_IMPLEMENTATION_SUMMARY.md +324 -0
  11. package/docs/MQTT_COMMAND_SCHEMA_PROPOSAL.md +576 -0
  12. package/docs/MQTT_SCHEMA_COMPLIANCE.md +285 -0
  13. package/docs/MQTT_SCHEMA_PROPOSALS.md +446 -0
  14. package/docs/MQTT_SCHEMA_REVIEW.md +690 -0
  15. package/docs/OTA_COMMANDS_REFERENCE.md +554 -0
  16. package/docs/PHASE1_GUIDE.md +349 -0
  17. package/docs/PHASE2_GUIDE.md +543 -0
  18. package/docs/README.md +77 -70
  19. package/docs/SCHEMA_VALIDATION_CHECKLIST.md +222 -0
  20. package/docs/alteriom/overview.md +507 -507
  21. package/docs/api/core-api.md +606 -606
  22. package/docs/architecture/mesh-architecture.md +378 -378
  23. package/docs/architecture/plugin-system.md +516 -516
  24. package/docs/getting-started/first-mesh.md +409 -409
  25. package/docs/getting-started/installation.md +274 -274
  26. package/docs/getting-started/quickstart.md +157 -157
  27. package/docs/improvements/FEATURE_PROPOSALS.md +337 -0
  28. package/docs/improvements/PHASE1_IMPLEMENTATION.md +325 -0
  29. package/docs/improvements/PHASE2_IMPLEMENTATION.md +567 -0
  30. package/docs/improvements/README.md +86 -68
  31. package/docs/improvements/ota-and-status-enhancements.md +911 -0
  32. package/docs/improvements/ota-status-architecture-diagrams.md +658 -0
  33. package/docs/improvements/ota-status-quick-reference.md +284 -0
  34. package/docs/platformio-publishing.md +255 -0
  35. package/docs/platformio-setup-summary.md +121 -0
  36. package/docs/troubleshooting/common-issues.md +520 -520
  37. package/docs/troubleshooting/faq.md +472 -472
  38. package/docs/tutorials/basic-examples.md +717 -717
  39. package/docs/wiki/API-Reference.md +245 -245
  40. package/docs/wiki/Complete-Documentation.md +122 -122
  41. package/examples/alteriom/README.md +139 -81
  42. package/examples/alteriom/alteriom.ino +186 -185
  43. package/examples/alteriom/alteriom_sensor_package.hpp +240 -127
  44. package/examples/alteriom/platformio.ini +24 -24
  45. package/examples/alteriomImproved/alteriom_sensor_package.hpp +224 -0
  46. package/examples/{alteriom → alteriomImproved}/improved_sensor_node.ino +245 -245
  47. package/examples/alteriomImproved/platformio.ini +25 -0
  48. package/examples/alteriomPhase1/alteriom_sensor_package.hpp +224 -0
  49. package/examples/alteriomPhase1/phase1_features.ino +242 -0
  50. package/examples/alteriomPhase1/platformio.ini +25 -0
  51. package/examples/alteriomPhase2/alteriom_sensor_package.hpp +224 -0
  52. package/examples/alteriomPhase2/phase2_features.ino +186 -0
  53. package/examples/alteriomPhase2/platformio.ini +25 -0
  54. package/examples/{alteriom → alteriomSensorNode}/alteriom_sensor_node.ino +183 -183
  55. package/examples/alteriomSensorNode/alteriom_sensor_package.hpp +224 -0
  56. package/examples/alteriomSensorNode/platformio.ini +25 -0
  57. package/examples/basic/basic.ino +66 -66
  58. package/examples/basic/platformio.ini +25 -25
  59. package/examples/bridge/bridge.ino +51 -51
  60. package/examples/bridge/mesh_event_publisher.hpp +253 -0
  61. package/examples/bridge/mesh_topology_reporter.hpp +303 -0
  62. package/examples/bridge/mqtt_command_bridge.hpp +459 -0
  63. package/examples/bridge/mqtt_status_bridge.hpp +519 -0
  64. package/examples/bridge/platformio.ini +25 -25
  65. package/examples/echoNode/echoNode.ino +33 -33
  66. package/examples/echoNode/platformio.ini +25 -25
  67. package/examples/logClient/logClient.ino +109 -109
  68. package/examples/logClient/platformio.ini +25 -25
  69. package/examples/logServer/logServer.ino +81 -81
  70. package/examples/logServer/platformio.ini +25 -25
  71. package/examples/meshCommandNode/alteriom_sensor_package.hpp +235 -0
  72. package/examples/meshCommandNode/meshCommandNode.ino +263 -0
  73. package/examples/meshCommandNode/platformio.ini +25 -0
  74. package/examples/mqttBridge/mqttBridge.ino +118 -118
  75. package/examples/mqttBridge/platformio.ini +26 -26
  76. package/examples/mqttCommandBridge/alteriom_sensor_package.hpp +235 -0
  77. package/examples/mqttCommandBridge/mesh_event_publisher.hpp +253 -0
  78. package/examples/mqttCommandBridge/mesh_topology_reporter.hpp +303 -0
  79. package/examples/mqttCommandBridge/mqttCommandBridge.ino +252 -0
  80. package/examples/mqttCommandBridge/mqtt_command_bridge.hpp +459 -0
  81. package/examples/mqttCommandBridge/platformio.ini +26 -0
  82. package/examples/mqttStatusBridge/mqttStatusBridge.ino +216 -0
  83. package/examples/mqttStatusBridge/mqtt_status_bridge.hpp +522 -0
  84. package/examples/mqttStatusBridge/platformio.ini +26 -0
  85. package/examples/mqttTopologyTest/README.md +467 -0
  86. package/examples/mqttTopologyTest/mqttTopologyTest.ino +748 -0
  87. package/examples/mqttTopologyTest/platformio.ini +26 -0
  88. package/examples/namedMesh/namedMesh.ino +97 -97
  89. package/examples/namedMesh/platformio.ini +25 -25
  90. package/examples/otaReceiver/otaReceiver.ino +79 -79
  91. package/examples/otaReceiver/platformio.ini +25 -25
  92. package/examples/otaSender/otaSender.ino +160 -151
  93. package/examples/otaSender/platformio.ini +25 -25
  94. package/examples/startHere/platformio.ini +25 -25
  95. package/examples/startHere/startHere.ino +159 -159
  96. package/examples/webServer/platformio.ini +27 -27
  97. package/examples/webServer/webServer.ino +89 -89
  98. package/keywords.txt +48 -48
  99. package/library.json +55 -34
  100. package/library.properties +10 -10
  101. package/package.json +86 -78
  102. package/src/AlteriomPainlessMesh.h +97 -97
  103. package/src/arduino/wifi.hpp +365 -365
  104. package/src/boost/asynctcp.hpp +279 -279
  105. package/src/painlessMesh.h +70 -70
  106. package/src/painlessMeshSTA.cpp +236 -236
  107. package/src/painlessMeshSTA.h +58 -58
  108. package/src/painlessTaskOptions.h +4 -4
  109. package/src/painlessmesh/base64.hpp +111 -111
  110. package/src/painlessmesh/buffer.hpp +229 -229
  111. package/src/painlessmesh/callback.hpp +91 -91
  112. package/src/painlessmesh/configuration.hpp +77 -77
  113. package/src/painlessmesh/connection.hpp +192 -192
  114. package/src/painlessmesh/layout.hpp +188 -188
  115. package/src/painlessmesh/logger.hpp +158 -158
  116. package/src/painlessmesh/memory.hpp +119 -119
  117. package/src/painlessmesh/mesh.hpp +761 -560
  118. package/src/painlessmesh/metrics.hpp +322 -322
  119. package/src/painlessmesh/ntp.hpp +263 -263
  120. package/src/painlessmesh/ota.hpp +582 -553
  121. package/src/painlessmesh/plugin.hpp +188 -188
  122. package/src/painlessmesh/protocol.hpp +813 -813
  123. package/src/painlessmesh/router.hpp +322 -322
  124. package/src/painlessmesh/tcp.hpp +71 -71
  125. package/src/painlessmesh/validation.hpp +238 -238
  126. package/src/plugin/performance.hpp +214 -214
  127. package/src/plugin/remote.hpp +64 -64
  128. package/src/scheduler.cpp +10 -10
  129. package/src/wifi.cpp +2 -2
@@ -1,70 +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_
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_
@@ -1,236 +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
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