-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathWiFiManager.cpp
760 lines (635 loc) · 23.4 KB
/
WiFiManager.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
/*
* WiFiManager.cpp - WiFi management Arduino library for ESP32.
*
* [email protected] 2023-2025
*
*/
#include <Arduino.h>
#include <WiFi.h>
#include <esp_http_server.h>
#include <ping/ping_sock.h>
#include <nvs_flash.h>
#include <nvs.h>
#include "WiFiManager.h"
#if defined(WFM_SHOW_LOG)
#define LOG_INF(format, ...) Serial.printf(format "\n", ##__VA_ARGS__)
#define LOG_WRN(format, ...) Serial.printf(format "\n", ##__VA_ARGS__)
#define LOG_ERR(format, ...) Serial.printf(format "\n", ##__VA_ARGS__)
#else
#define LOG_INF(format, ...)
#define LOG_WRN(format, ...)
#define LOG_ERR(format, ...)
#endif
// maximum size of a SSID name. 32 is IEEE standard. @warning limit is also hard coded in wifi_config_t. Never extend this value
#define MAX_SSID_SIZE 32
// maximum size of a WPA2 passkey. 64 is IEEE standard. @warning limit is also hard coded in wifi_config_t. Never extend this value
#define MAX_PSWD_SIZE 64
static char HostName[16] = ""; // Default Host name
static char AP_ssid[MAX_SSID_SIZE + 1] = ""; // Access Poin ssid
static char AP_pswd[MAX_PSWD_SIZE + 1] = ""; // Access Poin password
static char ST_ssid[MAX_SSID_SIZE + 1] = ""; // Router ssid
static char ST_pswd[MAX_PSWD_SIZE + 1] = ""; // Router password
// leave following blank for dhcp
static char ST_ip[16] = ""; // Static IP
static char ST_sn[16] = ""; // subnet normally 255.255.255.0
static char ST_gw[16] = ""; // gateway to internet, normally router IP
static char ST_dns1[16] = ""; // DNS Server, can be router IP (needed for SNTP)
static char ST_dns2[16] = ""; // alternative DNS Server, can be blank
#define START_WIFI_WAIT_SEC 15 // timeout WL_CONNECTED after board start
static bool APstarted = false; // internal flag AP state
static bool firstPingOK = false; // internal flag first successful connection
static callback_fn_t onConnect_cb = NULL; // pointer to callback function on first successful connection event
#pragma region "Configuration Portal"
#define WIFI_SCAN_LIST_SIZE 6
static uint8_t wifiScanListCnt = 0;
static char *wifiScanList = NULL;
static httpd_handle_t cfgPortalHttpServer = NULL;
static void startCfgPortalServer();
static void stopCfgPortalServer();
#pragma endregion
#pragma region "Ping"
#define PING_INTERVAL_SEC 30 // how often to check wifi status
static esp_ping_handle_t pingHandle = NULL;
static void startPing();
#pragma endregion
#pragma region "DNS server"
#if (WFM_AP_DNS_ENABLE)
#include <DNSServer.h>
static DNSServer *p_dnsServer = NULL;
static TaskHandle_t dnsServerHandle = NULL;
static void DnsServerTask(void *parameter) {
while (true) {
p_dnsServer->processNextRequest();
delay(10);
}
}
static void startDnsServer() {
const auto DNS_PORT = 53;
if (!dnsServerHandle) {
p_dnsServer = new DNSServer;
if (p_dnsServer) {
p_dnsServer->start(DNS_PORT, "*", WiFi.softAPIP()); // all DNS request
xTaskCreate(&DnsServerTask, "Dnstask", 1024 * 2, NULL, tskIDLE_PRIORITY + 1, &dnsServerHandle);
LOG_INF("startDnsServer");
}
}
}
static void stopDnsServer() {
if (dnsServerHandle) {
vTaskDelete(dnsServerHandle);
dnsServerHandle = NULL;
p_dnsServer->stop();
delete p_dnsServer;
p_dnsServer = NULL;
}
}
#endif
#pragma endregion
#pragma region "mDNS server"
#if WFM_ST_MDNS_ENABLE
#include <ESPmDNS.h>
static void setupMdnsHost() {
if (MDNS.begin(HostName)) {
// Add service to MDNS
MDNS.addService("http", "tcp", 80);
// MDNS.addService("ws", "udp", 83);
// MDNS.addService("ftp", "tcp", 21);
LOG_INF("mDNS service: http://%s.local", HostName);
} else
LOG_ERR("mDNS host: %s Failed", HostName);
}
#endif
#pragma endregion
static void loadWiFiAuthData() {
// Initialize NVS
esp_err_t err = nvs_flash_init();
if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) {
// NVS partition was truncated and needs to be erased
// Retry nvs_flash_init
nvs_flash_erase();
err = nvs_flash_init();
}
if (err != ESP_OK)
return;
nvs_handle_t nvs_handle;
size_t nvs_required_size;
err = nvs_open("wifiAuthData", NVS_READONLY, &nvs_handle);
if (err != ESP_OK) {
LOG_ERR("Error (%s) opening NVS handle!", esp_err_to_name(err));
} else {
if (ESP_OK == nvs_get_str(nvs_handle, "ssid", NULL, &nvs_required_size)) {
if (nvs_required_size <= sizeof(ST_ssid))
nvs_get_str(nvs_handle, "ssid", ST_ssid, &nvs_required_size);
}
if (ESP_OK == nvs_get_str(nvs_handle, "pswd", NULL, &nvs_required_size)) {
if (nvs_required_size <= sizeof(ST_pswd))
nvs_get_str(nvs_handle, "pswd", ST_pswd, &nvs_required_size);
}
if (ESP_OK == nvs_get_str(nvs_handle, "gateway", NULL, &nvs_required_size)) {
if (nvs_required_size <= sizeof(ST_gw))
nvs_get_str(nvs_handle, "gateway", ST_gw, &nvs_required_size);
}
nvs_close(nvs_handle);
}
}
static void saveWiFiAuthData() {
esp_err_t err;
nvs_handle_t nvs_handle;
err = nvs_open("wifiAuthData", NVS_READWRITE, &nvs_handle);
if (err != ESP_OK) {
LOG_ERR("Error (%s) opening NVS handle!", esp_err_to_name(err));
} else {
nvs_set_str(nvs_handle, "ssid", ST_ssid);
nvs_set_str(nvs_handle, "pswd", ST_pswd);
nvs_set_str(nvs_handle, "gateway", ST_gw);
nvs_commit(nvs_handle);
nvs_close(nvs_handle);
}
}
static void wifi_scan_clear() {
free(wifiScanList);
wifiScanList = NULL;
wifiScanListCnt = 0;
}
static void wifi_scan() {
wifi_scan_clear();
int16_t numNetworks = WiFi.scanNetworks();
if (numNetworks) {
if (numNetworks > WIFI_SCAN_LIST_SIZE)
numNetworks = WIFI_SCAN_LIST_SIZE;
wifiScanListCnt = numNetworks;
wifiScanList = (char *)malloc(wifiScanListCnt * sizeof(AP_ssid));
if (wifiScanList) {
char *pwifiScanList = wifiScanList;
for (uint8_t i = 0; i < wifiScanListCnt; ++i) {
memcpy(pwifiScanList, WiFi.SSID(i).c_str(), sizeof(AP_ssid));
pwifiScanList += sizeof(AP_ssid);
}
}
}
WiFi.scanDelete();
}
static void onWiFiEvent(WiFiEvent_t event) {
// callback to report on wifi events
if (event == ARDUINO_EVENT_WIFI_READY)
;
else if (event == ARDUINO_EVENT_WIFI_SCAN_DONE)
;
else if (event == ARDUINO_EVENT_WIFI_STA_START)
LOG_INF("Wifi event: STA started, connecting to: %s", ST_ssid);
else if (event == ARDUINO_EVENT_WIFI_STA_STOP)
LOG_INF("Wifi event: STA stopped %s", ST_ssid);
else if (event == ARDUINO_EVENT_WIFI_AP_START) {
if (!strcmp(WiFi.softAPSSID().c_str(), AP_ssid)) { // filter default AP "ESP_xxxxxx"
LOG_INF("Wifi event: AP_START: ssid: %s, use 'http://%s' to connect",
WiFi.softAPSSID().c_str(),
WiFi.softAPIP().toString().c_str());
APstarted = true;
#if (WFM_AP_DNS_ENABLE)
startDnsServer();
#endif
}
} else if (event == ARDUINO_EVENT_WIFI_AP_STOP) {
if (!strcmp(WiFi.softAPSSID().c_str(), AP_ssid)) {
LOG_INF("Wifi event: AP_STOP: %s", WiFi.softAPSSID().c_str());
APstarted = false;
#if (WFM_AP_DNS_ENABLE)
stopDnsServer();
#endif
}
} else if (event == ARDUINO_EVENT_WIFI_STA_GOT_IP)
LOG_INF("Wifi event: STA got IP, use 'http://%s' to connect", WiFi.localIP().toString().c_str());
else if (event == ARDUINO_EVENT_WIFI_STA_LOST_IP)
LOG_INF("Wifi event: STA lost IP");
else if (event == ARDUINO_EVENT_WIFI_AP_STAIPASSIGNED)
;
else if (event == ARDUINO_EVENT_WIFI_STA_CONNECTED)
LOG_INF("Wifi event: STA connection to %s", ST_ssid);
else if (event == ARDUINO_EVENT_WIFI_STA_DISCONNECTED)
LOG_INF("Wifi event: STA disconnected");
else if (event == ARDUINO_EVENT_WIFI_AP_STACONNECTED)
LOG_INF("Wifi event: AP client connection");
else if (event == ARDUINO_EVENT_WIFI_AP_STADISCONNECTED)
LOG_INF("Wifi event: AP client disconnection");
else
LOG_WRN("Wifi event: Unhandled event %d", event);
}
static bool setWifiAP() {
if (!APstarted) {
WiFi.mode(WIFI_AP_STA);
return WiFi.softAP(AP_ssid, AP_pswd, 1, 0, 1, false); // only 1 client
}
return false;
}
static bool setWifiSTA() {
if (strlen(ST_ssid)) {
if (!APstarted)
WiFi.mode(WIFI_STA);
if (strlen(ST_ip)) {
IPAddress _ip, _gw, _sn, _dns1, _dns2;
if (!_ip.fromString(ST_ip))
LOG_ERR("Failed to parse IP: %s", ST_ip);
else {
_ip.fromString(ST_ip);
_gw.fromString(ST_gw);
_sn.fromString(ST_sn);
if (strlen(ST_dns1)) {
_dns1.fromString(ST_dns1);
} else {
_dns1 = _gw;
}
_dns2.fromString(ST_dns2);
// set static ip
WiFi.config(_ip, _gw, _sn, _dns1); // need DNS for SNTP
LOG_INF("Wifi Station set static IP");
}
} else
LOG_INF("Wifi Station IP from DHCP");
WiFi.begin(ST_ssid, ST_pswd);
return true;
}
LOG_WRN("No Station SSID provided, use AP");
return false;
}
static bool startWifi(bool firstcall) {
// start wifi station (and wifi AP if allowed or station not defined)
if (firstcall) {
WiFi.mode(WIFI_OFF);
WiFi.persistent(false); // prevent the flash storage WiFi credentials
WiFi.setAutoReconnect(false); // Set whether module will attempt to reconnect to an access point in case it is disconnected
WiFi.softAPdisconnect(false); // kill rogue AP on startup
WiFi.disconnect(true);
WiFi.setHostname(HostName);
WiFi.onEvent(onWiFiEvent);
}
bool station = setWifiSTA();
if (station) {
LOG_INF("check WiFi status");
uint32_t startAttemptTime = millis();
// Stop trying on failure timeout, will try to reconnect later by ping
while (!WiFi.isConnected() && (millis() - startAttemptTime < START_WIFI_WAIT_SEC * 1000)) {
delay(500);
}
#if WFM_ST_MDNS_ENABLE
if (firstcall) {
setupMdnsHost();
}
#endif
}
if (!station || (firstcall && !WiFi.isConnected())) {
if (firstcall) {
wifi_scan();
}
setWifiAP();
startCfgPortalServer();
}
// start ping AFTER ALL configurations
if (station) {
startPing();
}
return WiFi.isConnected();
}
static void pingSuccess(esp_ping_handle_t hdl, void *args) {
if (APstarted) {
LOG_INF("pingSuccess: AP stop");
stopCfgPortalServer();
wifi_scan_clear();
WiFi.mode(WIFI_STA);
}
if (!firstPingOK) {
firstPingOK = true;
if (onConnect_cb)
onConnect_cb();
}
}
static void pingTimeout(esp_ping_handle_t hdl, void *args) {
LOG_WRN("Failed to ping gateway, restart wifi");
startWifi(false);
}
static void startPing() {
if (pingHandle) {
return;
}
IPAddress ipAddr = WiFi.gatewayIP();
ip_addr_t pingDest;
IP_ADDR4(&pingDest, ipAddr[0], ipAddr[1], ipAddr[2], ipAddr[3]);
esp_ping_config_t pingConfig = ESP_PING_DEFAULT_CONFIG();
pingConfig.target_addr = pingDest;
pingConfig.count = ESP_PING_COUNT_INFINITE;
pingConfig.interval_ms = PING_INTERVAL_SEC * 1000;
pingConfig.timeout_ms = 5000;
#if CONFIG_IDF_TARGET_ESP32S3
pingConfig.task_stack_size = 1024 * 6;
#else
pingConfig.task_stack_size = 1024 * 4;
#endif
pingConfig.task_prio = 1;
// set ping task callback functions
esp_ping_callbacks_t cbs;
cbs.on_ping_success = pingSuccess;
cbs.on_ping_timeout = pingTimeout;
cbs.on_ping_end = NULL;
cbs.cb_args = NULL;
esp_ping_new_session(&pingConfig, &cbs, &pingHandle);
esp_ping_start(pingHandle);
LOG_INF("Started ping monitoring");
}
static void stopPing() {
if (pingHandle) {
esp_ping_stop(pingHandle);
esp_ping_delete_session(pingHandle);
pingHandle = NULL;
}
}
const char head_chunk_html[] = R"rawliteral(<!DOCTYPE HTML><html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>WiFi settings</title>
<style>
input[type="text"] {width:250px;margin-bottom:8px;font-size:20px;}
input[type="submit"] {width:250px;height:60px;margin-bottom:8px;font-size:20px;}
select {width:50px;font-size:20px;}
body {text-align:center;font-size:15px;}
</style></head><body><br>)rawliteral";
const char end_chunk_html[] = R"rawliteral(</body></html>)rawliteral";
const char cfg_portal_body_begin[] = R"rawliteral(<form action="/" method="POST">
<input type="text" name="ssid" id="ssid" placeholder="SSID" required maxlength="32" style="width:200px;">
<select onchange="document.getElementById('ssid').value=this.options[this.selectedIndex].text;this.selectedIndex=0">
<option selected> </option>)rawliteral";
const char cfg_portal_body_end[] = R"rawliteral(</select><br>
<input type="text" name="pswd" placeholder="Pass" maxlength="64"><br>
<input type="submit" value="Check Connection">
</form>)rawliteral";
static esp_err_t indexHandler(httpd_req_t *req) {
httpd_resp_set_type(req, "text/html");
httpd_resp_sendstr_chunk(req, head_chunk_html);
httpd_resp_sendstr_chunk(req, cfg_portal_body_begin);
if (wifiScanList) {
char *pwifiScanList = wifiScanList;
for (uint8_t i = 0; i < wifiScanListCnt; ++i) {
httpd_resp_sendstr_chunk(req, "<option>");
httpd_resp_sendstr_chunk(req, pwifiScanList);
httpd_resp_sendstr_chunk(req, "</option>");
pwifiScanList += sizeof(AP_ssid);
}
}
httpd_resp_sendstr_chunk(req, cfg_portal_body_end);
httpd_resp_sendstr_chunk(req, end_chunk_html);
return httpd_resp_sendstr_chunk(req, NULL); // Send empty chunk to signal HTTP response completion
}
static esp_err_t cfgHandler(httpd_req_t *req) {
const char ssid_str[] = "ssid=";
const char pswd_str[] = "&pswd=";
const uint16_t buf_len =
(MAX_SSID_SIZE + MAX_PSWD_SIZE) * 3 // url encoded: '?' = "%3F"
+ sizeof(pswd_str) + sizeof(ssid_str)
+ 10; // gap
char buf[buf_len];
size_t total_len = req->content_len;
size_t received = 0;
size_t cur_len = 0;
if (total_len >= sizeof(buf)) {
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "content too long");
return ESP_FAIL;
}
while (cur_len < total_len) {
received = httpd_req_recv(req, buf + cur_len, total_len);
if (received <= 0) {
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to post control value");
return ESP_FAIL;
}
cur_len += received;
}
buf[total_len] = '\0';
char *ssid = strstr(buf, ssid_str);
char *pswd = strstr(buf, pswd_str);
if (ssid && pswd) {
char *pswd_decode = WiFiManagerClass::url_decode(pswd + sizeof(pswd_str) - 1);
*pswd = '\0';
char *ssid_decode = WiFiManagerClass::url_decode(ssid + sizeof(ssid_str) - 1);
size_t ssid_decode_len = strlen(ssid_decode);
size_t pswd_decode_len = strlen(pswd_decode);
if ((ssid_decode_len <= MAX_SSID_SIZE) && (pswd_decode_len <= MAX_PSWD_SIZE)) {
LOG_INF(R"~(Check connection to SSID="%s", Pass="%s")~", ssid_decode, pswd_decode);
stopPing();
WiFi.disconnect(true);
WiFi.begin(ssid_decode, pswd_decode);
uint32_t startAttemptTime = millis();
while (!WiFi.isConnected() && millis() - startAttemptTime < 5000) {
delay(500);
}
if (WiFi.isConnected()) {
httpd_resp_set_hdr(req, "Connection", "close");
httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
httpd_resp_set_type(req, "text/html");
httpd_resp_sendstr_chunk(req, head_chunk_html);
httpd_resp_sendstr_chunk(req, "Connection OK.<br>System Restart now...");
if (strlen(ST_ip)) {
snprintf(buf, sizeof(buf) - 1, R"~(<a href="http://%s"><br>Try http://%s later</a>)~", ST_ip, ST_ip);
httpd_resp_sendstr_chunk(req, buf);
}
httpd_resp_sendstr_chunk(req, end_chunk_html);
httpd_resp_sendstr_chunk(req, NULL);
memcpy(ST_ssid, ssid_decode, ssid_decode_len + 1);
memcpy(ST_pswd, pswd_decode, pswd_decode_len + 1);
memcpy(ST_gw, WiFi.gatewayIP().toString().c_str(), sizeof(ST_gw));
saveWiFiAuthData();
free(ssid_decode);
free(pswd_decode);
LOG_INF("restart");
Serial.flush();
delay(500);
ESP.restart();
return ESP_OK;
}
}
free(ssid_decode);
free(pswd_decode);
}
return indexHandler(req);
}
static void startCfgPortalServer() {
const uint8_t WEB_PORT = 80;
const uint8_t MAX_CLIENTS = 1;
if (cfgPortalHttpServer)
return;
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
#if CONFIG_IDF_TARGET_ESP32S3
config.stack_size = 1024 * 8;
#endif
config.server_port = WEB_PORT;
config.ctrl_port = WEB_PORT;
config.lru_purge_enable = true;
config.max_open_sockets = MAX_CLIENTS;
httpd_uri_t indexUri = {.uri = "/", .method = HTTP_GET, .handler = indexHandler, .user_ctx = NULL};
httpd_uri_t cfgUri = {.uri = "/", .method = HTTP_POST, .handler = cfgHandler, .user_ctx = NULL};
if (httpd_start(&cfgPortalHttpServer, &config) == ESP_OK) {
httpd_register_uri_handler(cfgPortalHttpServer, &indexUri);
httpd_register_uri_handler(cfgPortalHttpServer, &cfgUri);
LOG_INF("start cfgPortalHttpServer on port: %u", config.server_port);
} else {
LOG_ERR("Failed to start web server");
}
}
static void stopCfgPortalServer() {
if (cfgPortalHttpServer) {
httpd_stop(cfgPortalHttpServer);
cfgPortalHttpServer = NULL;
}
}
////////////////////////////////////////////////////////////
// WiFiManagerClass //
////////////////////////////////////////////////////////////
WiFiManagerClass::WiFiManagerClass() {
snprintf(AP_ssid, sizeof(AP_ssid), "ESP_%04X", (uint16_t)ESP.getEfuseMac());
snprintf(HostName, sizeof(HostName), "%s", AP_ssid);
loadWiFiAuthData();
}
/**
* Set static STAtion IP-address.
* @param ip Static IP
* @param subnet Subnet normally 255.255.255.0
* @param gateway Gateway to internet, if not set, the router's IP is used (192.168.0.1 as example)
* @param dns1 DNS Server, can be router IP (needed for SNTP)
* @param dns2 Alternative DNS Server, can be blank
*/
void WiFiManagerClass::setStaticIP(const char *ip, const char *subnet, const char *gateway, const char *dns1, const char *dns2) {
if (ip)
snprintf(ST_ip, sizeof(ST_ip), "%s", ip);
if (subnet)
snprintf(ST_sn, sizeof(ST_sn), "%s", subnet);
if (gateway)
snprintf(ST_gw, sizeof(ST_gw), "%s", gateway);
if (dns1)
snprintf(ST_dns1, sizeof(ST_dns1), "%s", dns1);
if (dns2)
snprintf(ST_dns2, sizeof(ST_dns2), "%s", dns2);
// restart if needed
if (pingHandle) {
stopPing();
startWifi(false);
}
}
/**
* Access Point configuration.
* @param ssidAP Access Point SSID (if null - use the default name "ESP_XXXX", where XXXX is the end MAC-address of the device)
* @param passwordAP Access Point password (if null - use the blank password)
*/
void WiFiManagerClass::configAP(const char *ssidAP, const char *passwordAP) {
if (ssidAP)
snprintf(AP_ssid, sizeof(AP_ssid), "%s", ssidAP);
if (passwordAP) {
size_t len = strlen(passwordAP);
if (len && (len >= 8)) {
snprintf(AP_pswd, sizeof(AP_pswd), "%s", passwordAP);
} else if (len) {
LOG_WRN("passwordAP must contain at least 8 characters. Apply blank password");
}
}
}
/**
* Start connect to saved Station or config new Access Point to set it
* @return true if STA is connected after wait timeout (will try to reconnect later by ping)
* @param hostname optional Hostname (if null - use the current Access Point SSID)
*/
bool WiFiManagerClass::start(const char *hostname) {
if (hostname)
snprintf(HostName, sizeof(HostName), "%s", hostname);
else
snprintf(HostName, sizeof(HostName), "%s", AP_ssid);
return startWifi(true);
}
/**
* is STAtion interface connected?
* @return true if STAtion is connected to an AP
*/
bool WiFiManagerClass::isConnected() {
return WiFi.isConnected();
}
/**
* Attach user callback function to first successful connection event
*/
void WiFiManagerClass::attachOnFirstConnect(callback_fn_t callback_fn) {
onConnect_cb = callback_fn;
}
/**
* Clean stored WiFi settings (ssid, password, gateway(router) IP)
*/
void WiFiManagerClass::cleanWiFiAuthData() {
memset(ST_ssid, 0, sizeof(ST_ssid));
memset(ST_pswd, 0, sizeof(ST_pswd));
memset(ST_gw, 0, sizeof(ST_gw));
saveWiFiAuthData();
};
/* Converts a hex character to its integer value */
static char from_hex(const char ch) {
return isdigit(ch) ? ch - '0' : tolower(ch) - 'a' + 10;
}
/* Converts an integer value to its hex character*/
static char to_hex(const char code) {
static const char hex[] = "0123456789abcdef";
return hex[code & 15];
}
/**
* Returns a url-encoded version of str.
* @param str from pointer
* @return pointer to string
* @warning be sure to free() the returned string after use
*/
char *WiFiManagerClass::url_encode(const char *str) {
char *buf = (char *)malloc(strlen(str) * 3 + 1);
char *pbuf = buf;
if (buf) {
while (*str) {
if (isalnum(*str) || *str == '-' || *str == '_' || *str == '.' || *str == '~')
*pbuf++ = *str;
else if (*str == ' ')
*pbuf++ = '+';
else
*pbuf++ = '%', *pbuf++ = to_hex(*str >> 4), *pbuf++ = to_hex(*str & 15);
str++;
}
*pbuf = '\0';
}
return buf;
}
/**
* Returns a url-decoded version of str.
* @param str from pointer
* @return pointer to string
* @warning be sure to free() the returned string after use
*/
char *WiFiManagerClass::url_decode(const char *str) {
char *buf = (char *)malloc(strlen(str) + 1);
char *pbuf = buf;
if (buf) {
while (*str) {
if (*str == '%') {
if (str[1] && str[2]) {
*pbuf++ = from_hex(str[1]) << 4 | from_hex(str[2]);
str += 2;
}
} else if (*str == '+') {
*pbuf++ = ' ';
} else {
*pbuf++ = *str;
}
str++;
}
*pbuf = '\0';
}
return buf;
}
/**
* Print memory usage statistics.
* @param caller pointer to description string
*/
void WiFiManagerClass::debugMemory(const char *caller) {
Serial.printf("%s > Free: heap %u, block: %u, pSRAM %u\n",
caller,
ESP.getFreeHeap(),
heap_caps_get_largest_free_block(MALLOC_CAP_INTERNAL),
ESP.getFreePsram());
}
WiFiManagerClass WiFiManager;