diff --git a/ESP32_AP-Flasher/data/www/setup.html b/ESP32_AP-Flasher/data/www/setup.html new file mode 100644 index 00000000..1e0f6eff --- /dev/null +++ b/ESP32_AP-Flasher/data/www/setup.html @@ -0,0 +1,52 @@ + + + + + + + + setup page - Open EPaper Link Access Point + + + + + + +
+ +
+ +
+
+

WiFi config

+
+SSID             
+Password        
+            
+

Fill in fields below for static IP. Otherwise, leave empty.

+
+Static IP       
+Subnet mask     
+Gateway         
+DNS             
+            
+ +
+ +
+
+ + + + + \ No newline at end of file diff --git a/ESP32_AP-Flasher/data/www/setup.js b/ESP32_AP-Flasher/data/www/setup.js new file mode 100644 index 00000000..914b092f --- /dev/null +++ b/ESP32_AP-Flasher/data/www/setup.js @@ -0,0 +1,87 @@ +const $ = document.querySelector.bind(document); + +window.addEventListener("load", function () { + fetch("/get_wifi_config") + .then(response => response.json()) + .then(data => { + $('#ssid').value = data.ssid || ""; + $('#pw').value = data.pw || ""; + $('#ip').value = data.ip || ""; + $('#mask').value = data.mask || ""; + $('#gw').value = data.gw || ""; + $('#dns').value = data.dns || ""; + }); +}); + +function pad(text, count) { + let t = text + ""; + return t.padEnd(count, "\u00A0").slice(0, count); +} + +$('#listssid').addEventListener('click', () => { + document.body.style.cursor = 'progress'; + $('#listssid').innerHTML = '⌛'; + getSsidList(); +}); + +function getSsidList() { + fetch("/get_ssid_list") + .then(response => response.json()) + .then(data => { + if (data.scanstatus < 0) { + setTimeout(getSsidList, 3000); + return; + } else { + const select = document.createElement('select'); + select.id = 'ssid'; + + data.networks.forEach(network => { + if (network.ssid) { + const option = document.createElement('option'); + option.value = network.ssid; + console.log(network); + option.text = pad(network.rssi, 5) + pad(network.ssid, 24); + select.appendChild(option); + } + }); + + let ssidval = $('#ssid').value; + $('#ssid').replaceWith(select); + $('#ssid').value = ssidval; + } + document.body.style.cursor = 'default'; + $('#listssid').innerHTML = 'find SSID'; + }); +}; + +$('#connect').addEventListener('click', () => { + const data = { + ssid: $('#ssid').value, + pw: $('#pw').value, + ip: $('#ip').value, + mask: $('#mask').value, + gw: $('#gw').value, + dns: $('#dns').value + }; + + fetch('/save_wifi_config', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(data) + }) + .then(response => { + if (response.ok) { + console.log('WiFi settings saved successfully'); + let url = "/"; + if ($('#ip').value) url = "http://" + $('#ip').value + "/"; + $('.window').innerHTML = "

Wifi settings saved...

Rebooting...
Wait a few seconds and then go to the Access Point web page."; + } else { + console.log('Error saving WiFi settings'); + } + }) + .catch(error => { + console.log('Network error:', error); + }); +}); diff --git a/ESP32_AP-Flasher/include/web.h b/ESP32_AP-Flasher/include/web.h index 87d5f27e..bc7c6214 100644 --- a/ESP32_AP-Flasher/include/web.h +++ b/ESP32_AP-Flasher/include/web.h @@ -6,7 +6,7 @@ void init_web(); void doImageUpload(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final); void doJsonUpload(AsyncWebServerRequest *request); -extern void webSocketSendProcess(void *parameter); +extern void networkProcess(void *parameter); void wsLog(String text); void wsErr(String text); void wsSendTaginfo(uint8_t *mac, uint8_t syncMode); diff --git a/ESP32_AP-Flasher/include/wifimanager.h b/ESP32_AP-Flasher/include/wifimanager.h new file mode 100644 index 00000000..7aaafba7 --- /dev/null +++ b/ESP32_AP-Flasher/include/wifimanager.h @@ -0,0 +1,49 @@ +#ifndef WIFI_MANAGER_H +#define WIFI_MANAGER_H + +#include + +enum WifiStatus { + NOINIT, + WAIT_CONNECTING, + CONNECTED, + WAIT_RECONNECT, + AP +}; + +class WifiManager { + private: + bool _connected; + bool _savewhensuccessfull; + int _reconnectIntervalCheck; + int _retryIntervalCheck; + int _connectionTimeout; + String _ssid; + String _pass; + unsigned long _nextReconnectCheck; + bool _APstarted; + + const int SERIAL_BUFFER_SIZE = 64; + char serialBuffer[64]; + int serialIndex = 0; + + String WiFi_SSID(); + String WiFi_psk(); + + bool waitForConnection(); + void pollSerial(); + + public: + WifiManager(); + + WifiStatus wifiStatus; + static uint8_t apClients; + bool connectToWifi(); + bool connectToWifi(String ssid, String pass, bool savewhensuccessfull); + + void startManagementServer(); + void poll(); + static void WiFiEvent(WiFiEvent_t event); +}; + +#endif diff --git a/ESP32_AP-Flasher/platformio.ini b/ESP32_AP-Flasher/platformio.ini index 0d389d03..d49c50bb 100644 --- a/ESP32_AP-Flasher/platformio.ini +++ b/ESP32_AP-Flasher/platformio.ini @@ -13,7 +13,6 @@ platform = espressif32 framework = arduino lib_deps = https://github.com/me-no-dev/ESPAsyncWebServer - https://github.com/tzapu/WiFiManager.git#feature_asyncwebserver bblanchon/ArduinoJson bodmer/TFT_eSPI https://github.com/Bodmer/TJpg_Decoder.git diff --git a/ESP32_AP-Flasher/src/main.cpp b/ESP32_AP-Flasher/src/main.cpp index 3e077e54..d1768446 100644 --- a/ESP32_AP-Flasher/src/main.cpp +++ b/ESP32_AP-Flasher/src/main.cpp @@ -1,7 +1,6 @@ #include #include -#include #include #include "storage.h" @@ -74,7 +73,7 @@ void setup() { Serial.begin(115200); Serial.print(">\n"); - pinTest(); + // pinTest(); #ifdef BOARD_HAS_PSRAM if (!psramInit()) { Serial.printf("This build of the AP expects PSRAM, but we couldn't find/init any. Something is terribly wrong here! System halted."); @@ -135,7 +134,7 @@ void setup() { loadDB("/current/tagDB.json"); // tagDBOwner = xSemaphoreCreateMutex(); xTaskCreate(APTask, "AP Process", 6000, NULL, 2, NULL); - xTaskCreate(webSocketSendProcess, "ws", 2000, NULL, configMAX_PRIORITIES - 10, NULL); + xTaskCreate(networkProcess, "Wifi", 2000, NULL, configMAX_PRIORITIES - 10, NULL); vTaskDelay(10 / portTICK_PERIOD_MS); config.runStatus = RUNSTATUS_INIT; diff --git a/ESP32_AP-Flasher/src/makeimage.cpp b/ESP32_AP-Flasher/src/makeimage.cpp index 4399e8b0..e01bbb85 100644 --- a/ESP32_AP-Flasher/src/makeimage.cpp +++ b/ESP32_AP-Flasher/src/makeimage.cpp @@ -91,6 +91,7 @@ uint8_t *spr2color(TFT_eSprite &spr, imgParam &imageParams, size_t *buffer_size, uint8_t *buffer = (uint8_t*) malloc(*buffer_size); if (!buffer) { Serial.println("Failed to allocate buffer"); + Serial.println("Maximum Continuous Heap Space: " + String(heap_caps_get_largest_free_block(MALLOC_CAP_DEFAULT))); return nullptr; } memset(buffer, 0, *buffer_size); diff --git a/ESP32_AP-Flasher/src/system.cpp b/ESP32_AP-Flasher/src/system.cpp index 78763469..d73abd3a 100644 --- a/ESP32_AP-Flasher/src/system.cpp +++ b/ESP32_AP-Flasher/src/system.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include "storage.h" @@ -16,7 +17,7 @@ void initTime(void* parameter) { sntp_set_sync_interval(300 * 1000); configTzTime(config.timeZone, "nl.pool.ntp.org", "europe.pool.ntp.org", "time.nist.gov"); struct tm timeinfo; - while (true) { + while (millis() < 30000) { if (!getLocalTime(&timeinfo)) { Serial.println("Waiting for valid time from NTP-server"); vTaskDelay(1000 / portTICK_PERIOD_MS); diff --git a/ESP32_AP-Flasher/src/web.cpp b/ESP32_AP-Flasher/src/web.cpp index 4a40cc11..dc419db6 100644 --- a/ESP32_AP-Flasher/src/web.cpp +++ b/ESP32_AP-Flasher/src/web.cpp @@ -6,12 +6,13 @@ #include #include #include -#include "storage.h" +#include +#include + +#include "ArduinoJson.h" +#include "AsyncJson.h" #include "LittleFS.h" #include "SPIFFSEditor.h" -#include -#include // https://github.com/tzapu/WiFiManager/tree/feature_asyncwebserver - #include "commstructs.h" #include "language.h" #include "leds.h" @@ -19,8 +20,10 @@ #include "ota.h" #include "serialap.h" #include "settings.h" +#include "storage.h" #include "tag_db.h" #include "udp.h" +#include "wifimanager.h" extern uint8_t data_to_send[]; @@ -28,38 +31,17 @@ extern uint8_t data_to_send[]; // const char *http_password = "admin"; AsyncWebServer server(80); AsyncWebSocket ws("/ws"); +WifiManager wm; SemaphoreHandle_t wsMutex; +uint32_t lastssidscan = 0; -void webSocketSendProcess(void *parameter) { +void networkProcess(void *parameter) { wsMutex = xSemaphoreCreateMutex(); while (true) { ws.cleanupClients(); - vTaskDelay(1000 / portTICK_PERIOD_MS); - } -} - -void onEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len) { -#ifdef HAS_RGB_LED - shortBlink(CRGB::BlueViolet); -#endif - switch (type) { - case WS_EVT_CONNECT: - ets_printf("ws[%s][%u] connect\n", server->url(), client->id()); - // client->ping(); - break; - case WS_EVT_DISCONNECT: - ets_printf("ws[%s][%u] disconnect: %u\n", server->url(), client->id()); - break; - case WS_EVT_ERROR: - ets_printf("WS Error received :(\n\n"); - // ets_printf("ws[%s][%u] error(%u): %s\n", server->url(), client->id(), *((uint16_t *)arg), (char *)data); - break; - case WS_EVT_PONG: - ets_printf("ws[%s][%u] pong[%u]: %s\n", server->url(), client->id(), len, (len) ? (char *)data : ""); - break; - case WS_EVT_DATA: - break; + wm.poll(); + vTaskDelay(50 / portTICK_PERIOD_MS); } } @@ -185,22 +167,13 @@ void init_web() { Storage.begin(); WiFi.mode(WIFI_STA); - WiFiManager wm; - bool res; WiFi.setTxPower(static_cast(config.wifiPower)); - wm.setWiFiAutoReconnect(true); - res = wm.autoConnect("OpenEPaperLink Setup"); - if (!res) { - Serial.println("Failed to connect"); - ESP.restart(); - } - Serial.print("Connected! IP address: "); - Serial.println(WiFi.localIP()); + + wm.connectToWifi(); // server.addHandler(new SPIFFSEditor(*contentFS, http_username, http_password)); server.addHandler(new SPIFFSEditor(*contentFS)); - ws.onEvent(onEvent); server.addHandler(&ws); server.on("/reboot", HTTP_POST, [](AsyncWebServerRequest *request) { @@ -386,6 +359,79 @@ void init_web() { request->send(200, "text/plain", "Ok, saved"); }); + // setup + + server.on("/setup", HTTP_GET, [](AsyncWebServerRequest *request) { + request->send(*contentFS, "/www/setup.html"); + }); + + server.on("/get_wifi_config", HTTP_GET, [](AsyncWebServerRequest *request) { + Preferences preferences; + AsyncResponseStream *response = request->beginResponseStream("application/json"); + StaticJsonDocument<250> doc; + preferences.begin("wifi", false); + const char *keys[] = {"ssid", "pw", "ip", "mask", "gw", "dns"}; + const size_t numKeys = sizeof(keys) / sizeof(keys[0]); + for (size_t i = 0; i < numKeys; i++) { + doc[keys[i]] = preferences.getString(keys[i], ""); + } + serializeJson(doc, *response); + request->send(response); + }); + + server.on("/get_ssid_list", HTTP_GET, [](AsyncWebServerRequest *request) { + AsyncResponseStream *response = request->beginResponseStream("application/json"); + DynamicJsonDocument doc(5000); + + doc["scanstatus"] = WiFi.scanComplete(); + JsonArray networks = doc.createNestedArray("networks"); + for (int i = 0; i < (WiFi.scanComplete() > 50 ? 50 : WiFi.scanComplete()); ++i) { + if (WiFi.SSID(i) != "") { + JsonObject network = networks.createNestedObject(); + network["ssid"] = WiFi.SSID(i); + network["ch"] = WiFi.channel(i); + network["rssi"] = WiFi.RSSI(i); + network["enc"] = WiFi.encryptionType(i); + } + } + if (WiFi.scanComplete() != -1 && (WiFi.scanComplete() == -2 || millis() - lastssidscan > 30000)) { + WiFi.scanDelete(); + Serial.println("start scanning"); + WiFi.scanNetworks(true, true); + lastssidscan = millis(); + } + + serializeJson(doc, *response); + request->send(response); + }); + + AsyncCallbackJsonWebHandler *handler = new AsyncCallbackJsonWebHandler("/save_wifi_config", [](AsyncWebServerRequest *request, JsonVariant &json) { + const JsonObject &jsonObj = json.as(); + Preferences preferences; + preferences.begin("wifi", false); + const char *keys[] = {"ssid", "pw", "ip", "mask", "gw", "dns"}; + const size_t numKeys = sizeof(keys) / sizeof(keys[0]); + for (size_t i = 0; i < numKeys; i++) { + String key = keys[i]; + if (jsonObj.containsKey(key)) { + preferences.putString(key.c_str(), jsonObj[key].as()); + } + } + preferences.end(); + Serial.println("config saved"); + request->send(200, "text/plain", "Ok, saved"); + + ws.enable(false); + refreshAllPending(); + saveDB("/current/tagDB.json"); + ws.closeAll(); + delay(100); + ESP.restart(); + }); + server.addHandler(handler); + + // end of setup + server.on("/backup_db", HTTP_GET, [](AsyncWebServerRequest *request) { saveDB("/current/tagDB.json"); File file = contentFS->open("/current/tagDB.json", "r"); diff --git a/ESP32_AP-Flasher/src/wifimanager.cpp b/ESP32_AP-Flasher/src/wifimanager.cpp new file mode 100644 index 00000000..9cdb24e9 --- /dev/null +++ b/ESP32_AP-Flasher/src/wifimanager.cpp @@ -0,0 +1,266 @@ +#include "WifiManager.h" + +#include +#include +#include + +uint8_t WifiManager::apClients = 0; + +WifiManager::WifiManager() { + _reconnectIntervalCheck = 5000; + _retryIntervalCheck = 5 * 60000; + _connectionTimeout = 15000; + + _nextReconnectCheck = 0; + _connected = false; + _savewhensuccessfull = false; + _ssid = ""; + _APstarted = false; + wifiStatus = NOINIT; + + WiFi.onEvent(WiFiEvent); +} + +void WifiManager::poll() { + if (wifiStatus == AP && millis() > _nextReconnectCheck && _ssid!="") { + if (apClients == 0) { + Serial.println("Attempting to reconnect to WiFi."); + _APstarted = false; + wifiStatus = NOINIT; + connectToWifi(); + } else { + _nextReconnectCheck = millis() + _retryIntervalCheck; + } + } + + if (wifiStatus == CONNECTED && millis() > _nextReconnectCheck) { + if (WiFi.status() != WL_CONNECTED) { + _connected = false; + Serial.println("WiFi connection lost. Attempting to reconnect."); + WiFi.reconnect(); + waitForConnection(); + } else { + _nextReconnectCheck = millis() + _reconnectIntervalCheck; + } + } + + pollSerial(); +} + +bool WifiManager::connectToWifi() { + Preferences preferences; + preferences.begin("wifi", false); + _ssid = preferences.getString("ssid", WiFi_SSID()); + _pass = preferences.getString("pw", WiFi_psk()); + if (_ssid == "") { + Serial.println("No connection information specified"); + startManagementServer(); + return false; + } + Serial.println("Stored ssid: " + String(_ssid)); + + String ip = preferences.getString("ip", ""); + String mask = preferences.getString("mask", ""); + String gw = preferences.getString("gw", ""); + String dns = preferences.getString("dns", ""); + + if (ip.length() > 0) { + IPAddress staticIP, subnetMask, gatewayIP, dnsIP; + if (staticIP.fromString(ip) && subnetMask.fromString(mask) && gatewayIP.fromString(gw)) { + if (dns.length() > 0) dnsIP.fromString(dns); + WiFi.config(staticIP, gatewayIP, subnetMask, dnsIP); + Serial.println("Setting static IP"); + } + } + + _connected = connectToWifi(_ssid, _pass, false); + return _connected; +} + +bool WifiManager::connectToWifi(String ssid, String pass, bool savewhensuccessfull) { + _ssid = ssid; + _pass = pass; + _savewhensuccessfull = savewhensuccessfull; + + _APstarted = false; + WiFi.disconnect(false, true); + WiFi.mode(WIFI_STA); + WiFi.setSleep(WIFI_PS_NONE); + + Serial.println("Connecting to WiFi..."); + + WiFi.persistent(savewhensuccessfull); + WiFi.begin(_ssid.c_str(), _pass.c_str()); + _connected = waitForConnection(); + return _connected; +} + +bool WifiManager::waitForConnection() { + unsigned long timeout = millis() + _connectionTimeout; + wifiStatus = WAIT_CONNECTING; + + while (WiFi.status() != WL_CONNECTED) { + if (millis() > timeout) { + Serial.println("Unable to connect to WIFI"); + startManagementServer(); + return false; + } + vTaskDelay(250 / portTICK_PERIOD_MS); + } + + if (_savewhensuccessfull) { + Preferences preferences; + preferences.begin("wifi", false); + preferences.putString("ssid", _ssid); + preferences.putString("pass", _pass); + preferences.end(); + _savewhensuccessfull = false; + } + WiFi.setAutoReconnect(true); + WiFi.persistent(true); + IPAddress IP = WiFi.localIP(); + Serial.printf("Connected! IP Address: %s\n", IP.toString().c_str()); + _nextReconnectCheck = millis() + _reconnectIntervalCheck; + wifiStatus = CONNECTED; + return true; +} + +void WifiManager::startManagementServer() { + if (!_APstarted) { + Serial.println("Starting configuration AP, ssid OpenEPaperLink"); + WiFi.mode(WIFI_AP); + WiFi.softAP("OpenEPaperLink", "", 1, false); + WiFi.softAPsetHostname("OpenEPaperLink"); + IPAddress IP = WiFi.softAPIP(); + Serial.printf("IP Address: %s\n", IP.toString().c_str()); + _APstarted = true; + _nextReconnectCheck = millis() + _retryIntervalCheck; + wifiStatus = AP; + } +} + +String WifiManager::WiFi_SSID() { + wifi_config_t conf; + esp_wifi_get_config(WIFI_IF_STA, &conf); + return String(reinterpret_cast(conf.sta.ssid)); +} + +String WifiManager::WiFi_psk() { + if (WiFiGenericClass::getMode() == WIFI_MODE_NULL) { + return String(); + } + wifi_config_t conf; + esp_wifi_get_config(WIFI_IF_STA, &conf); + return String(reinterpret_cast(conf.sta.password)); +} + +void WifiManager::pollSerial() { + while (Serial.available() > 0) { + char receivedChar = Serial.read(); + + if (receivedChar == 27) { + memset(serialBuffer, 0, sizeof(serialBuffer)); + serialIndex = 0; + Serial.println(); + continue; + } + + if (receivedChar == 8) { + if (serialIndex > 0) { + serialIndex--; + serialBuffer[serialIndex] = '\0'; + Serial.print("\r"); + Serial.print(serialBuffer); + } + continue; + } + if (receivedChar == '\r') { + continue; + } + + if (receivedChar == '\n') { + serialBuffer[serialIndex] = '\0'; + String command = String(serialBuffer); + + if (command.startsWith("ssid ")) { + _ssid = command.substring(5); + Serial.println("\rSSID set to: " + _ssid); + } else if (command.startsWith("pass ")) { + _pass = command.substring(5); + Serial.println("\rPassword set to: " + _pass); + } else if (command.startsWith("connect")) { + connectToWifi(_ssid, _pass, true); + } + memset(serialBuffer, 0, sizeof(serialBuffer)); + serialIndex = 0; + } else { + if (serialIndex < SERIAL_BUFFER_SIZE - 1) { + serialBuffer[serialIndex] = receivedChar; + serialIndex++; + Serial.print("\r"); + Serial.print(serialBuffer); + } + } + } +} + +// temporary write some more debug info +void WifiManager::WiFiEvent(WiFiEvent_t event) { + Serial.printf("[WiFi-event %d] ", event); + + switch (event) { + case ARDUINO_EVENT_WIFI_STA_CONNECTED: + Serial.println("Connected to access point"); + break; + case ARDUINO_EVENT_WIFI_STA_DISCONNECTED: + Serial.println("Disconnected from WiFi access point"); + break; + case ARDUINO_EVENT_WIFI_STA_AUTHMODE_CHANGE: + Serial.println("Authentication mode of access point has changed"); + break; + case ARDUINO_EVENT_WIFI_STA_GOT_IP: + Serial.print("Obtained IP address: "); + Serial.println(WiFi.localIP()); + break; + case ARDUINO_EVENT_WIFI_STA_LOST_IP: + Serial.println("Lost IP address and IP address is reset to 0"); + break; + + case ARDUINO_EVENT_WIFI_AP_START: + Serial.println("WiFi access point started"); + break; + case ARDUINO_EVENT_WIFI_AP_STOP: + Serial.println("WiFi access point stopped"); + break; + case ARDUINO_EVENT_WIFI_AP_STACONNECTED: + apClients++; + Serial.println("Client connected"); + break; + case ARDUINO_EVENT_WIFI_AP_STADISCONNECTED: + apClients--; + Serial.println("Client disconnected"); + break; + case ARDUINO_EVENT_WIFI_AP_STAIPASSIGNED: + Serial.println("Assigned IP address to client"); + break; + + case ARDUINO_EVENT_ETH_START: + Serial.println("Ethernet started"); + break; + case ARDUINO_EVENT_ETH_STOP: + Serial.println("Ethernet stopped"); + break; + case ARDUINO_EVENT_ETH_CONNECTED: + Serial.println("Ethernet connected"); + break; + case ARDUINO_EVENT_ETH_DISCONNECTED: + Serial.println("Ethernet disconnected"); + break; + case ARDUINO_EVENT_ETH_GOT_IP: + Serial.println("Obtained IP address"); + break; + default: + Serial.println(); + break; + } +}