replacing wifimanager

This one saves a lot of flash space.
Now, wifimanager is replaced with our own method for managing the wifi connections.
Also, the whole thing will run now even if there is no wifi. If wifi disappears, it automatically tries to reconnect in the background every few minutes.
Wifi settings now available at all times at /setup
If it cannot connect, an access point 'OpenEPaperLink' is created where you can set the wifi credentials (http://192.168.4.1/setup).
This commit is contained in:
Nic Limper
2023-07-19 15:00:52 +02:00
parent d6529cfdb3
commit 6db2f7f9bc
10 changed files with 547 additions and 47 deletions

View File

@@ -0,0 +1,52 @@
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0" />
<title>setup page - Open EPaper Link Access Point</title>
<link rel="stylesheet" href="main.css" type="text/css" />
<link rel="icon" type="image/vnd.icon" href="favicon.ico">
<style>
.window input {
width: 120px;
}
h1 {
padding-top: 20px;
}
option, select {
font-family: monospace, 'lucida console', 'Courier New', Courier;
}
</style>
</head>
<body>
<header>
<div class="logo">Open EPaper Link Access Point</div>
</header>
<div class="container">
<div class="window">
<h1>WiFi config</h1>
<pre>
SSID <input type="text" id="ssid"> <button id="listssid">find SSID</button>
Password <input type="password" id="pw">
</pre>
<p>Fill in fields below for static IP. Otherwise, leave empty.<br></p>
<pre>
Static IP <input type="text" id="ip">
Subnet mask <input type="text" id="mask">
Gateway <input type="text" id="gw">
DNS <input type="text" id="dns">
</pre>
<button id="connect">Save WiFi settings and reboot</button><br>
</div>
</div>
<script src="setup.js"></script>
</body>
</html>

View File

@@ -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 = '&#x231B;';
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 = "<h1>Wifi settings saved...</h1>Rebooting...<br>Wait a few seconds and then go to the <a href=\"" + url + "\">Access Point web page</a>.";
} else {
console.log('Error saving WiFi settings');
}
})
.catch(error => {
console.log('Network error:', error);
});
});

View File

@@ -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);

View File

@@ -0,0 +1,49 @@
#ifndef WIFI_MANAGER_H
#define WIFI_MANAGER_H
#include <WiFi.h>
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

View File

@@ -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

View File

@@ -1,7 +1,6 @@
#include <Arduino.h>
#include <WiFi.h>
#include <WiFiManager.h>
#include <time.h>
#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;

View File

@@ -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);

View File

@@ -2,6 +2,7 @@
#include <Arduino.h>
#include <FS.h>
#include <Preferences.h>
#include <sntp.h>
#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);

View File

@@ -6,12 +6,13 @@
#include <ESPAsyncWebServer.h>
#include <ESPmDNS.h>
#include <FS.h>
#include "storage.h"
#include <Preferences.h>
#include <WiFi.h>
#include "ArduinoJson.h"
#include "AsyncJson.h"
#include "LittleFS.h"
#include "SPIFFSEditor.h"
#include <WiFi.h>
#include <WiFiManager.h> // 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<wifi_power_t>(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<JsonObject>();
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<String>());
}
}
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");

View File

@@ -0,0 +1,266 @@
#include "WifiManager.h"
#include <Preferences.h>
#include <WiFi.h>
#include <esp_wifi.h>
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<const char *>(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<char *>(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;
}
}