#include #include #include #include #include #include WebServer server(80); DNSServer dnsServer; const byte DNS_PORT = 53; #ifdef BLYNK_USE_SPIFFS #include "SPIFFS.h" #else const char* config_form = R"html( WiFi setup

)html"; #endif static const char serverUpdateForm[] PROGMEM = R"(
)"; void restartMCU() { ESP.restart(); while(1) {}; } void eraseMcuConfig() { // Erase ESP32 NVS int err; //err=nvs_flash_init(); //BLYNK_LOG2("nvs_flash_init: ", err ? String(err) : "Success"); err=nvs_flash_erase(); BLYNK_LOG2("nvs_flash_erase: ", err ? String(err) : "Success"); } void getWiFiName(char* buff, size_t len, bool withPrefix = true) { const uint64_t chipId = ESP.getEfuseMac(); uint32_t unique = 0; for (int i=0; i<4; i++) { unique = BlynkCRC32(&chipId, sizeof(chipId), unique); } unique &= 0xFFFFF; if (withPrefix) { snprintf(buff, len, "Blynk %s-%05X", BLYNK_DEVICE_NAME, unique); } else { snprintf(buff, len, "%s-%05X", BLYNK_DEVICE_NAME, unique); } } void enterConfigMode() { char ssidBuff[64]; getWiFiName(ssidBuff, sizeof(ssidBuff)); WiFi.mode(WIFI_OFF); delay(100); WiFi.mode(WIFI_AP); delay(2000); WiFi.softAPConfig(WIFI_AP_IP, WIFI_AP_IP, WIFI_AP_Subnet); WiFi.softAP(ssidBuff); delay(500); IPAddress myIP = WiFi.softAPIP(); DEBUG_PRINT(String("AP SSID: ") + ssidBuff); DEBUG_PRINT(String("AP IP: ") + myIP[0] + "." + myIP[1] + "." + myIP[2] + "." + myIP[3]); // Set up DNS Server dnsServer.setTTL(300); // Time-to-live 300s dnsServer.setErrorReplyCode(DNSReplyCode::ServerFailure); // Return code for non-accessible domains #ifdef WIFI_CAPTIVE_PORTAL_ENABLE dnsServer.start(DNS_PORT, "*", WiFi.softAPIP()); // Point all to our IP server.onNotFound(handleRoot); #else dnsServer.start(DNS_PORT, CONFIG_AP_URL, WiFi.softAPIP()); DEBUG_PRINT(String("AP URL: ") + CONFIG_AP_URL); #endif server.on("/update", HTTP_GET, []() { server.sendHeader("Connection", "close"); server.send(200, "text/html", serverUpdateForm); }); server.on("/update", HTTP_POST, []() { server.sendHeader("Connection", "close"); if (!Update.hasError()) { server.send(200, "text/plain", "OK"); } else { server.send(500, "text/plain", "FAIL"); } delay(1000); restartMCU(); }, []() { HTTPUpload& upload = server.upload(); if (upload.status == UPLOAD_FILE_START) { DEBUG_PRINT(String("Update: ") + upload.filename); //WiFiUDP::stop(); if (!Update.begin(UPDATE_SIZE_UNKNOWN)) { //start with max available size Update.printError(BLYNK_PRINT); } } else if (upload.status == UPLOAD_FILE_WRITE) { /* flashing firmware to ESP*/ if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) { Update.printError(BLYNK_PRINT); } BLYNK_PRINT.print("."); } else if (upload.status == UPLOAD_FILE_END) { BLYNK_PRINT.println(); DEBUG_PRINT("Finishing..."); if (Update.end(true)) { //true to set the size to the current progress DEBUG_PRINT("Update Success. Rebooting"); } else { Update.printError(BLYNK_PRINT); } } }); server.on("/config", []() { DEBUG_PRINT("Applying configuration..."); String ssid = server.arg("ssid"); String ssidManual = server.arg("ssidManual"); String pass = server.arg("pass"); if (ssidManual != "") { ssid = ssidManual; } String token = server.arg("blynk"); String host = server.arg("host"); String port = server.arg("port_ssl"); String ip = server.arg("ip"); String mask = server.arg("mask"); String gw = server.arg("gw"); String dns = server.arg("dns"); String dns2 = server.arg("dns2"); bool save = server.arg("save").toInt(); String content; DEBUG_PRINT(String("WiFi SSID: ") + ssid + " Pass: " + pass); DEBUG_PRINT(String("Blynk cloud: ") + token + " @ " + host + ":" + port); if (token.length() == 32 && ssid.length() > 0) { configStore.setFlag(CONFIG_FLAG_VALID, false); CopyString(ssid, configStore.wifiSSID); CopyString(pass, configStore.wifiPass); CopyString(token, configStore.cloudToken); if (host.length()) { CopyString(host, configStore.cloudHost); } if (port.length()) { configStore.cloudPort = port.toInt(); } IPAddress addr; if (ip.length() && addr.fromString(ip)) { configStore.staticIP = addr; configStore.setFlag(CONFIG_FLAG_STATIC_IP, true); } else { configStore.setFlag(CONFIG_FLAG_STATIC_IP, false); } if (mask.length() && addr.fromString(mask)) { configStore.staticMask = addr; } if (gw.length() && addr.fromString(gw)) { configStore.staticGW = addr; } if (dns.length() && addr.fromString(dns)) { configStore.staticDNS = addr; } if (dns2.length() && addr.fromString(dns2)) { configStore.staticDNS2 = addr; } if (save) { configStore.setFlag(CONFIG_FLAG_VALID, true); config_save(); content = R"json({"status":"ok","msg":"Configuration saved"})json"; } else { content = R"json({"status":"ok","msg":"Trying to connect..."})json"; } server.send(200, "application/json", content); BlynkState::set(MODE_SWITCH_TO_STA); } else { DEBUG_PRINT("Configuration invalid"); content = R"json({"status":"error","msg":"Configuration invalid"})json"; server.send(500, "application/json", content); } }); server.on("/board_info.json", []() { DEBUG_PRINT("Sending board info..."); const char* tmpl = BLYNK_TEMPLATE_ID; char ssidBuff[64]; getWiFiName(ssidBuff, sizeof(ssidBuff)); char buff[512]; snprintf(buff, sizeof(buff), R"json({"board":"%s","tmpl_id":"%s","fw_type":"%s","fw_ver":"%s","ssid":"%s","bssid":"%s","last_error":%d,"wifi_scan":true,"static_ip":true})json", BLYNK_DEVICE_NAME, tmpl ? tmpl : "Unknown", BLYNK_FIRMWARE_TYPE, BLYNK_FIRMWARE_VERSION, ssidBuff, WiFi.softAPmacAddress().c_str(), configStore.last_error ); server.send(200, "application/json", buff); }); server.on("/wifi_scan.json", []() { DEBUG_PRINT("Scanning networks..."); int wifi_nets = WiFi.scanNetworks(true, true); const uint32_t t = millis(); while (wifi_nets < 0 && millis() - t < 20000) { delay(20); wifi_nets = WiFi.scanComplete(); } DEBUG_PRINT(String("Found networks: ") + wifi_nets); if (wifi_nets > 0) { // Sort networks int indices[wifi_nets]; for (int i = 0; i < wifi_nets; i++) { indices[i] = i; } for (int i = 0; i < wifi_nets; i++) { for (int j = i + 1; j < wifi_nets; j++) { if (WiFi.RSSI(indices[j]) > WiFi.RSSI(indices[i])) { std::swap(indices[i], indices[j]); } } } wifi_nets = BlynkMin(15, wifi_nets); // Show top 15 networks // TODO: skip empty names String result = "[\n"; char buff[256]; for (int i = 0; i < wifi_nets; i++){ int id = indices[i]; const char* sec; switch (WiFi.encryptionType(id)) { case WIFI_AUTH_WEP: sec = "WEP"; break; case WIFI_AUTH_WPA_PSK: sec = "WPA/PSK"; break; case WIFI_AUTH_WPA2_PSK: sec = "WPA2/PSK"; break; case WIFI_AUTH_WPA_WPA2_PSK: sec = "WPA/WPA2/PSK"; break; case WIFI_AUTH_OPEN: sec = "OPEN"; break; default: sec = "unknown"; break; } snprintf(buff, sizeof(buff), R"json( {"ssid":"%s","bssid":"%s","rssi":%i,"sec":"%s","ch":%i})json", WiFi.SSID(id).c_str(), WiFi.BSSIDstr(id).c_str(), WiFi.RSSI(id), sec, WiFi.channel(id) ); result += buff; if (i != wifi_nets-1) result += ",\n"; } server.send(200, "application/json", result + "\n]"); } else { server.send(200, "application/json", "[]"); } }); server.on("/reset", []() { BlynkState::set(MODE_RESET_CONFIG); server.send(200, "application/json", R"json({"status":"ok","msg":"Configuration reset"})json"); }); server.on("/reboot", []() { restartMCU(); }); #ifdef BLYNK_USE_SPIFFS if (SPIFFS.begin()) { server.serveStatic("/img/favicon.png", SPIFFS, "/img/favicon.png"); server.serveStatic("/img/logo.png", SPIFFS, "/img/logo.png"); server.serveStatic("/", SPIFFS, "/index.html"); } else { DEBUG_PRINT("Webpage: No SPIFFS"); } #endif server.begin(); while (BlynkState::is(MODE_WAIT_CONFIG) || BlynkState::is(MODE_CONFIGURING)) { delay(10); dnsServer.processNextRequest(); server.handleClient(); app_loop(); if (BlynkState::is(MODE_WAIT_CONFIG) && WiFi.softAPgetStationNum() > 0) { BlynkState::set(MODE_CONFIGURING); } else if (BlynkState::is(MODE_CONFIGURING) && WiFi.softAPgetStationNum() == 0) { BlynkState::set(MODE_WAIT_CONFIG); } } server.stop(); #ifdef BLYNK_USE_SPIFFS SPIFFS.end(); #endif } void enterConnectNet() { BlynkState::set(MODE_CONNECTING_NET); DEBUG_PRINT(String("Connecting to WiFi: ") + configStore.wifiSSID); char ssidBuff[64]; getWiFiName(ssidBuff, sizeof(ssidBuff)); String hostname(ssidBuff); hostname.replace(" ", "-"); WiFi.setHostname(hostname.c_str()); if (configStore.getFlag(CONFIG_FLAG_STATIC_IP)) { if (!WiFi.config(configStore.staticIP, configStore.staticGW, configStore.staticMask, configStore.staticDNS, configStore.staticDNS2) ) { DEBUG_PRINT("Failed to configure Static IP"); config_set_last_error(BLYNK_PROV_ERR_CONFIG); BlynkState::set(MODE_ERROR); return; } } WiFi.begin(configStore.wifiSSID, configStore.wifiPass); unsigned long timeoutMs = millis() + WIFI_NET_CONNECT_TIMEOUT; while ((timeoutMs > millis()) && (WiFi.status() != WL_CONNECTED)) { delay(10); app_loop(); if (!BlynkState::is(MODE_CONNECTING_NET)) { WiFi.disconnect(); return; } } if (WiFi.status() == WL_CONNECTED) { IPAddress localip = WiFi.localIP(); if (configStore.getFlag(CONFIG_FLAG_STATIC_IP)) { BLYNK_LOG_IP("Using Static IP: ", localip); } else { BLYNK_LOG_IP("Using Dynamic IP: ", localip); } BlynkState::set(MODE_CONNECTING_CLOUD); } else { config_set_last_error(BLYNK_PROV_ERR_NETWORK); BlynkState::set(MODE_ERROR); } } void enterConnectCloud() { BlynkState::set(MODE_CONNECTING_CLOUD); Blynk.config(configStore.cloudToken, configStore.cloudHost, configStore.cloudPort); Blynk.connect(0); unsigned long timeoutMs = millis() + WIFI_CLOUD_CONNECT_TIMEOUT; while ((timeoutMs > millis()) && (!Blynk.isTokenInvalid()) && (Blynk.connected() == false)) { delay(10); Blynk.run(); app_loop(); if (!BlynkState::is(MODE_CONNECTING_CLOUD)) { Blynk.disconnect(); return; } } if (millis() > timeoutMs) { DEBUG_PRINT("Timeout"); } if (Blynk.isTokenInvalid()) { config_set_last_error(BLYNK_PROV_ERR_TOKEN); BlynkState::set(MODE_WAIT_CONFIG); } else if (Blynk.connected()) { BlynkState::set(MODE_RUNNING); if (!configStore.getFlag(CONFIG_FLAG_VALID)) { configStore.last_error = BLYNK_PROV_ERR_NONE; configStore.setFlag(CONFIG_FLAG_VALID, true); config_save(); } } else { config_set_last_error(BLYNK_PROV_ERR_CLOUD); BlynkState::set(MODE_ERROR); } } void enterSwitchToSTA() { BlynkState::set(MODE_SWITCH_TO_STA); DEBUG_PRINT("Switching to STA..."); delay(1000); WiFi.mode(WIFI_OFF); delay(100); WiFi.mode(WIFI_STA); BlynkState::set(MODE_CONNECTING_NET); } void enterError() { BlynkState::set(MODE_ERROR); unsigned long timeoutMs = millis() + 10000; while (timeoutMs > millis() || g_buttonPressed) { delay(10); app_loop(); if (!BlynkState::is(MODE_ERROR)) { return; } } DEBUG_PRINT("Restarting after error."); delay(10); restartMCU(); }