Subversion Repositories ESP8266_P1_Meter

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 raymond 1
#include "WiFiService.h"
2
#include "Json.h"
3
 
4
// Helper: log the actual station network configuration as seen by the core
5
// (independent from WiFiConnectParams / CredentialManager values).
6
static void logCurrentStaNetworkConfig() {
7
#if defined(ESP8266) || defined(ESP32)
8
    auto mode = WiFi.getMode();
9
    if (mode != WIFI_STA && mode != WIFI_AP_STA) {
10
        return;
11
    }
12
    log_info("[WiFi] Core STA config: IP=%s GW=%s SN=%s DNS1=%s DNS2=%s",
13
             WiFi.localIP().toString().c_str(),
14
             WiFi.gatewayIP().toString().c_str(),
15
             WiFi.subnetMask().toString().c_str(),
16
             WiFi.dnsIP(0).toString().c_str(),
17
             WiFi.dnsIP(1).toString().c_str());
18
#endif
19
}
20
 
21
#if defined(ESP32)
22
static inline void resetTaskWdtIfSubscribed() {
23
    if (esp_task_wdt_status(NULL) == ESP_OK) {
24
        esp_task_wdt_reset();
25
    }
26
}
27
#endif
28
 
29
void WiFiService::setTaskWdt(uint32_t timeout) {
30
#if defined(ESP32)
31
    #if ESP_ARDUINO_VERSION_MAJOR > 2
32
    esp_task_wdt_config_t twdt_config = {
33
        .timeout_ms = timeout,
34
        .idle_core_mask = (1 << portNUM_PROCESSORS) - 1,
35
        .trigger_panic = false,
36
    };
37
    ESP_ERROR_CHECK(esp_task_wdt_reconfigure(&twdt_config));
38
    #else
39
    ESP_ERROR_CHECK(esp_task_wdt_init(timeout / 1000, 0));
40
    #endif
41
    // The Arduino core already subscribes loopTask to the WDT.
42
    // Adding it again logs "task is already subscribed". Avoid duplicate add.
43
#elif defined(ESP8266)
44
    ESP.wdtDisable();
45
    ESP.wdtEnable(timeout);
46
#endif
47
}
48
 
49
WiFiScanResult WiFiService::scanNetworks() {
50
    WiFiScanResult result;
51
    int res = WiFi.scanComplete();
52
 
53
#ifdef WIFI_SCAN_RUNNING
54
    if (res == WIFI_SCAN_RUNNING) {
55
        result.reload = true;
56
        result.json = "{\"reload\":1}";
57
        return result;
58
    }
59
    if (res == WIFI_SCAN_FAILED) {
60
        WiFi.scanNetworks(true);
61
        result.reload = true;
62
        result.json = "{\"reload\":1}";
63
        return result;
64
    }
65
#else
66
    if (res == -2) {
67
        WiFi.scanNetworks(true);
68
        result.reload = true;
69
        result.json = "{\"reload\":1}";
70
        return result;
71
    }
72
    if (res == -1) {
73
        result.reload = true;
74
        result.json = "{\"reload\":1}";
75
        return result;
76
    }
77
#endif
78
 
79
    if (res >= 0) {
80
        CJSON::Json json_array;
81
        json_array.createArray();
82
        for (int i = 0; i < res; ++i) {
83
            CJSON::Json item;
84
            item.setNumber("strength", WiFi.RSSI(i));
85
            item.setString("ssid", WiFi.SSID(i));
86
#if defined(ESP8266)
87
            item.setString("security", AUTH_OPEN ? "none" : "enabled");
88
#elif defined(ESP32)
89
            item.setString("security", WIFI_AUTH_OPEN ? "none" : "enabled");
90
#endif
91
            json_array.add(item);
92
        }
93
        result.json = json_array.serialize();
94
        WiFi.scanDelete();
95
        WiFi.scanNetworks(true);
96
        return result;
97
    }
98
 
99
    result.reload = true;
100
    result.json = "{\"reload\":1}";
101
    return result;
102
}
103
 
104
WiFiConnectResult WiFiService::connectWithParams(const WiFiConnectParams& params) {
105
    WiFiConnectResult result;
106
 
107
    if (strlen(params.creds.ssid)) {
108
        setTaskWdt(params.wdtLongTimeout);
109
        WiFi.mode(WIFI_AP_STA);
110
 
111
        if (!params.dhcp) {
112
            log_info("Manual config WiFi connection with IP: %s", params.creds.local_ip.toString().c_str());
113
            bool hasDns1 = params.creds.dns1 != IPAddress(0, 0, 0, 0);
114
            bool hasDns2 = params.creds.dns2 != IPAddress(0, 0, 0, 0);
115
            bool ok = false;
116
            if (hasDns1 && hasDns2) {
117
                ok = WiFi.config(params.creds.local_ip, params.creds.gateway, params.creds.subnet, params.creds.dns1, params.creds.dns2);
118
            } else if (hasDns1) {
119
                ok = WiFi.config(params.creds.local_ip, params.creds.gateway, params.creds.subnet, params.creds.dns1);
120
            } else {
121
                ok = WiFi.config(params.creds.local_ip, params.creds.gateway, params.creds.subnet);
122
            }
123
 
124
            if (!ok) {
125
                log_error("STA Failed to configure");
126
            }
127
        }        
128
 
129
        DBG_OUTPUT_PORT.print("\n\n\nConnecting to ");
130
        DBG_OUTPUT_PORT.println(params.creds.ssid);
131
        WiFi.begin(params.creds.ssid, params.password.c_str());
132
 
133
        uint32_t beginTime = millis();
134
        while (WiFi.status() != WL_CONNECTED) {
135
            delay(250);
136
            DBG_OUTPUT_PORT.print("*");
137
#if defined(ESP8266)
138
            ESP.wdtFeed();
139
#else
140
            resetTaskWdtIfSubscribed();
141
#endif
142
            if (millis() - beginTime > params.timeout) {
143
                result.status = 408;
144
                result.body = "<br><br>Connection timeout!<br>Check password or try to restart ESP.";
145
                setTaskWdt(params.wdtTimeout);
146
                return result;
147
            }
148
        }
149
 
150
        if (WiFi.status() == WL_CONNECTED) {
151
            result.ip = WiFi.localIP();
152
            result.connected = true;
153
 
154
            // Debug: print the actual STA configuration as seen by the core
155
            logCurrentStaNetworkConfig();
156
 
157
            DBG_OUTPUT_PORT.print("\nConnected to ");
158
            DBG_OUTPUT_PORT.print(params.creds.ssid);
159
            DBG_OUTPUT_PORT.print(". IP address: ");
160
            DBG_OUTPUT_PORT.println(result.ip);
161
            String serverLoc = F("http://");
162
            for (int i = 0; i < 4; i++) {
163
                if (i) serverLoc += ".";
164
                serverLoc += result.ip[i];
165
            }
166
            serverLoc += "/setup";
167
            String resp;
168
            resp  = "ESP successfully connected to ";
169
            resp += params.creds.ssid;
170
            resp += " WiFi network.";
171
 
172
            if (params.fromApClient) {
173
                // Case 1: request came from a client connected to the ESP AP.
174
                // We stay in WIFI_AP_STA, so we can still deliver this page over the AP and then let the user switch WiFi.
175
                resp += " <br><br><i>Note:<br>Disconnect your browser from ESP's access point and connect it to the new WiFi network.<br><br>IP address <a href='";
176
                resp += serverLoc;
177
                resp += "'>";
178
                resp += serverLoc;
179
                resp += "</a><br> Hostname <a href='http://";
180
                resp += params.host;
181
                resp += ".local/setup'>http://";
182
                resp += params.host;
183
                resp += ".local/setup</a></i><br><p style='text-align: center'>Do you want to proceed with a ESP restart right now?</p><div id='action-restart-required'></div>";
184
            } else {
185
                // Case 2: request came from a client already on the same WiFi as the ESP (pure SSID switch). 
186
                // After the switch this page will no longer reach the device until the client changes WiFi as well.
187
                resp += " <br><br><i>Note:<br>This setup page may stop communicating with the device due to the WiFi network change.<br>After you switch your PC/phone to the new WiFi network, open <a href='http://";
188
                resp += params.host;
189
                resp += ".local'>http://";
190
                resp += params.host;
191
                resp += ".local</a> (or the new IP: ";
192
                resp += result.ip.toString();
193
                resp += ") to reach the ESP again.</i><br><p style='text-align: center'>Do you want to proceed with a ESP restart right now?</p><div id='action-restart-required'></div>";
194
            }
195
            result.status = 200;
196
            result.body = resp;
197
            setTaskWdt(params.wdtTimeout);
198
            return result;
199
        }
200
    }
201
 
202
    setTaskWdt(params.wdtTimeout);
203
    result.status = 401;
204
    result.body = "Wrong credentials provided";
205
    return result;
206
}
207
 
208
WiFiStartResult WiFiService::startWiFi(CredentialManager* credentialManager, fs::FS* filesystem, const char* configFile, uint32_t timeout) {
209
    WiFiStartResult result;
210
    WiFi.mode(WIFI_STA);
211
    WiFiCredential* bestCred = nullptr;
212
 
213
    if (credentialManager) {
214
#ifdef ESP32
215
        credentialManager->loadFromNVS();
216
#else
217
        credentialManager->loadFromFS();
218
#endif        
219
        std::vector<WiFiCredential>* creds = credentialManager->getCredentials();
220
        if (creds && creds->size() > 0) {
221
            int networksFound = WiFi.scanNetworks();
222
            if (networksFound > 0) {
223
                int32_t bestRSSI = -200;
224
 
225
                for (int i = 0; i < networksFound; i++) {
226
                    String scannedSSID = WiFi.SSID(i);
227
                    int32_t scannedRSSI = WiFi.RSSI(i);
228
                    for (size_t j = 0; j < creds->size(); j++) {
229
                        if (strcmp((*creds)[j].ssid, scannedSSID.c_str()) == 0) {
230
                            if (scannedRSSI > bestRSSI) {
231
                                bestRSSI = scannedRSSI;
232
                                bestCred = &(*creds)[j];
233
                            }
234
                            break;
235
                        }
236
                    }
237
                }
238
                WiFi.scanDelete();
239
 
240
                if (bestCred != nullptr) {
241
                    if (bestCred->local_ip != IPAddress(0, 0, 0, 0)) {
242
                        log_info("Configuring static IP: %s, GW: %s, SN: %s",
243
                                 bestCred->local_ip.toString().c_str(),
244
                                 bestCred->gateway.toString().c_str(),
245
                                 bestCred->subnet.toString().c_str());
246
 
247
                        IPAddress dns1 = bestCred->dns1;
248
                        IPAddress dns2 = bestCred->dns2;
249
                        bool hasDns1 = dns1 != IPAddress(0, 0, 0, 0);
250
                        bool hasDns2 = dns2 != IPAddress(0, 0, 0, 0);
251
                        bool ok = false;
252
                        if (hasDns1 && hasDns2) {
253
                            ok = WiFi.config(bestCred->local_ip, bestCred->gateway, bestCred->subnet, dns1, dns2);
254
                        } else if (hasDns1) {
255
                            ok = WiFi.config(bestCred->local_ip, bestCred->gateway, bestCred->subnet, dns1);
256
                        } else {
257
                            ok = WiFi.config(bestCred->local_ip, bestCred->gateway, bestCred->subnet);
258
                        }
259
 
260
                        if (!ok) {
261
                            log_error("Failed to configure static IP");
262
                        }
263
                    }
264
 
265
                    if (credentialManager->getPassword(bestCred->ssid).length() > 0) {
266
                        log_info("Connecting to %s (RSSI: %d dBm)...", bestCred->ssid, bestRSSI);
267
                        WiFi.begin(bestCred->ssid, credentialManager->getPassword(bestCred->ssid).c_str());
268
 
269
                        int tryDelay = timeout / 10;
270
                        int numberOfTries = 10;
271
                        while (numberOfTries > 0) {
272
                            switch (WiFi.status()) {
273
                            case WL_NO_SSID_AVAIL:   log_debug("[WiFi] SSID not found"); break;
274
                            case WL_CONNECTION_LOST: log_debug("[WiFi] Connection was lost"); break;
275
                            case WL_SCAN_COMPLETED:  log_debug("[WiFi] Scan is completed"); break;
276
                            case WL_DISCONNECTED:    log_debug("[WiFi] WiFi is disconnected"); break;
277
                            case WL_CONNECT_FAILED:
278
                                log_debug("[WiFi] Failed - WiFi not connected!");
279
                                result.action = WiFiStartAction::StartAp;
280
                                return result;
281
                            case WL_CONNECTED:
282
                                log_debug("[WiFi] WiFi is connected!  IP address: %s", WiFi.localIP().toString().c_str());
283
                                // Debug: print the actual STA configuration as seen by the core
284
                                logCurrentStaNetworkConfig();
285
                                result.ip = WiFi.localIP();
286
                                result.action = WiFiStartAction::Connected;
287
                                return result;
288
                            default:
289
                                log_debug("[WiFi] WiFi Status: %d", WiFi.status());
290
                                break;
291
                            }
292
                            delay(tryDelay);
293
#if defined(ESP8266)
294
                            ESP.wdtFeed();
295
#else
296
                            resetTaskWdtIfSubscribed();
297
#endif
298
                            numberOfTries--;
299
                        }
300
 
301
                        log_debug("[WiFi] Failed to connect to WiFi!");
302
                        WiFi.disconnect();
303
                        result.action = WiFiStartAction::StartAp;
304
                        return result;
305
                    }
306
                }
307
            }
308
            result.action = WiFiStartAction::StartAp;
309
            return result;
310
        }
311
    }
312
 
313
    if (bestCred && strlen(bestCred->ssid)) {
314
        WiFi.setHostname(credentialManager->getHostname().c_str());
315
        WiFi.begin(bestCred->ssid, credentialManager->getPassword(bestCred->ssid).c_str());
316
        log_debug("Connecting to %s", bestCred->ssid);
317
 
318
        int tryDelay = timeout / 10;
319
        int numberOfTries = 10;
320
 
321
        while (true) {
322
            switch (WiFi.status()) {
323
            case WL_NO_SSID_AVAIL:   log_debug("[WiFi] SSID not found"); break;
324
            case WL_CONNECTION_LOST: log_debug("[WiFi] Connection was lost"); break;
325
            case WL_SCAN_COMPLETED:  log_debug("[WiFi] Scan is completed"); break;
326
            case WL_DISCONNECTED:    log_debug("[WiFi] WiFi is disconnected"); break;
327
            case WL_CONNECT_FAILED:
328
                log_debug("[WiFi] Failed - WiFi not connected!");
329
                result.action = WiFiStartAction::Failed;
330
                return result;
331
            case WL_CONNECTED:
332
                log_debug("[WiFi] WiFi is connected!  IP address: %s", WiFi.localIP().toString().c_str());
333
                // Debug: print the actual STA configuration as seen by the core
334
                logCurrentStaNetworkConfig();
335
                result.ip = WiFi.localIP();
336
                result.action = WiFiStartAction::Connected;
337
                return result;
338
            default:
339
                log_debug("[WiFi] WiFi Status: %d", WiFi.status());
340
                break;
341
            }
342
            delay(tryDelay);
343
#if defined(ESP8266)
344
            ESP.wdtFeed();
345
#else
346
            resetTaskWdtIfSubscribed();
347
#endif
348
            if (numberOfTries <= 0) {
349
                log_debug("[WiFi] Failed to connect to WiFi!");
350
                WiFi.disconnect();
351
                result.action = WiFiStartAction::Failed;
352
                return result;
353
            }
354
            else {
355
                numberOfTries--;
356
            }
357
        }
358
    }
359
 
360
    result.action = WiFiStartAction::StartAp;
361
    return result;
362
}
363
 
364
 
365
bool WiFiService::startAccessPoint(WiFiConnectParams& params, IPAddress& outIp) {    
366
    delay(100);
367
    WiFi.mode(WIFI_AP);
368
 
369
    IPAddress apIP = params.creds.local_ip != IPAddress(0,0,0,0) ? params.creds.local_ip : IPAddress(192, 168, 4, 1);
370
    IPAddress gateway = params.creds.gateway != IPAddress(0,0,0,0) ? params.creds.gateway : IPAddress(192, 168, 4, 1);
371
    IPAddress netmask = params.creds.subnet != IPAddress(0,0,0,0) ? params.creds.subnet : IPAddress(255, 255, 255, 0);
372
 
373
    // Configure AP IP parameters
374
    WiFi.softAPConfig(apIP, gateway, netmask);
375
 
376
    if (!WiFi.softAP(params.creds.ssid, params.password.c_str())) {
377
        log_error("Captive portal failed to start: WiFi.softAP() failed!");
378
        return false;
379
    }
380
    outIp = WiFi.softAPIP();
381
    return true;
382
}
383
 
384
bool WiFiService::startMDNSResponder(DNSServer*& dnsServer, const String& host, uint16_t port, const IPAddress& serverIp) {
385
    if (dnsServer) {
386
        delete dnsServer;
387
        dnsServer = nullptr;
388
    }
389
    dnsServer = new DNSServer();
390
    dnsServer->setErrorReplyCode(DNSReplyCode::NoError);
391
    if (!dnsServer->start(53, "*", serverIp)) {
392
        log_error("Captive portal failed to start: no sockets for DNS server available!");
393
        return false;
394
    }
395
 
396
    if (!MDNS.begin(host.c_str())) {
397
        log_error("MDNS responder not started");
398
        return false;
399
    } else {
400
        log_debug("MDNS responder started %s (AP mode)", host.c_str());
401
        MDNS.addService("http", "tcp", port);
402
        MDNS.setInstanceName("esp-fs-webserver");
403
    }
404
    return true;
405
}
406
 
407
bool WiFiService::startMDNSOnly(const String& host, uint16_t port) {
408
    if (!MDNS.begin(host.c_str())) {
409
        log_error("MDNS responder not started");
410
        return false;
411
    }
412
 
413
    log_debug("MDNS responder started %s (STA mode)", host.c_str());
414
    MDNS.addService("http", "tcp", port);
415
    MDNS.setInstanceName("esp-fs-webserver");
416
    return true;
417
}