Add network status and multi STA WiFi configuration
This commit is contained in:
@@ -33,6 +33,7 @@ const char INDEX_HTML[] PROGMEM = R"rawliteral(
|
||||
.v{font-weight:650}.good{color:var(--good)}.warn{color:var(--warn)}.bad{color:var(--bad)}.muted{color:var(--muted)}
|
||||
.bar{height:10px;background:#0e141c;border-radius:999px;overflow:hidden;border:1px solid var(--line)}.fill{height:100%;background:linear-gradient(90deg,var(--good),#9be15d);width:0%}
|
||||
button{width:100%;border:0;border-radius:12px;padding:12px;background:var(--btn);color:var(--text);font-weight:700;font-size:15px}
|
||||
input{width:100%;border:1px solid var(--line);border-radius:12px;padding:12px;background:#0f151d;color:var(--text);font-size:15px}
|
||||
button.on{background:#17462a;color:#dfffea}button.off{background:#46202a;color:#ffe2e8}.relayBtns{display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-top:12px}
|
||||
.tempList,.alarmList{display:grid;gap:8px;margin-top:12px}.item{display:flex;justify-content:space-between;gap:8px;background:#0f151d;border:1px solid var(--line);border-radius:12px;padding:10px}
|
||||
@media(max-width:760px){.span6,.span4{grid-column:span 12}.big{font-size:36px}.wrap{padding:12px}}
|
||||
@@ -76,6 +77,23 @@ const char INDEX_HTML[] PROGMEM = R"rawliteral(
|
||||
<div class="tempList" id="relays"></div>
|
||||
</section>
|
||||
|
||||
<section class="card span12">
|
||||
<div class="label">WiFi</div>
|
||||
<div class="sub">AP stays available at 192.168.4.1. STA networks are tried in order.</div>
|
||||
<div class="tempList" style="margin-top:12px">
|
||||
<input id="w1s" placeholder="Network 1 SSID">
|
||||
<input id="w1p" placeholder="Network 1 password" type="password">
|
||||
<input id="w2s" placeholder="Network 2 SSID">
|
||||
<input id="w2p" placeholder="Network 2 password" type="password">
|
||||
<input id="w3s" placeholder="Network 3 SSID">
|
||||
<input id="w3p" placeholder="Network 3 password" type="password">
|
||||
<button onclick="saveWifi()">Save WiFi</button>
|
||||
<button onclick="connectWifi()">Connect Now</button>
|
||||
</div>
|
||||
<div class="row"><span class="muted">STA</span><span class="v" id="wifiSta">--</span></div>
|
||||
<div class="row"><span class="muted">STA IP</span><span class="v" id="wifiIp">--</span></div>
|
||||
</section>
|
||||
|
||||
<section class="card span12">
|
||||
<div class="label">Alarms / System</div>
|
||||
<div class="alarmList" id="alarms"></div>
|
||||
@@ -136,9 +154,43 @@ function render(data){
|
||||
<div class="item"><span>${k.replaceAll("_"," ")}</span><strong class="${clsAlarm(alarms[k])}">${alarms[k]?"ALARM":"OK"}</strong></div>`).join("");
|
||||
|
||||
const s=data.system||{};
|
||||
const n=data.network||{};
|
||||
$("wifiSta").textContent=n.sta_connected ? (n.sta_ssid||"connected") : "Not connected";
|
||||
$("wifiIp").textContent=n.sta_ip||"--";
|
||||
$("fw").textContent=(s.firmware_name||"--")+" "+(s.firmware_version||"");
|
||||
$("uptime").textContent=(s.uptime_seconds||0)+" sec";
|
||||
}
|
||||
async function loadWifiConfig(){
|
||||
try{
|
||||
const r=await fetch("/config/wifi",{cache:"no-store"});
|
||||
const d=await r.json();
|
||||
const nets=d.wifi?.networks||[];
|
||||
for(let i=0;i<3;i++){
|
||||
const n=nets[i]||{};
|
||||
const s=$("w"+(i+1)+"s");
|
||||
if(s) s.value=n.ssid||"";
|
||||
}
|
||||
}catch(e){}
|
||||
}
|
||||
async function saveWifi(){
|
||||
const networks=[];
|
||||
for(let i=1;i<=3;i++){
|
||||
const ssid=$("w"+i+"s").value.trim();
|
||||
const password=$("w"+i+"p").value;
|
||||
if(ssid) networks.push({ssid,password});
|
||||
}
|
||||
await fetch("/config/wifi",{
|
||||
method:"POST",
|
||||
headers:{"Content-Type":"application/json"},
|
||||
body:JSON.stringify({networks})
|
||||
});
|
||||
await loadWifiConfig();
|
||||
await load();
|
||||
}
|
||||
async function connectWifi(){
|
||||
await fetch("/wifi/connect",{method:"POST"});
|
||||
await load();
|
||||
}
|
||||
async function load(){
|
||||
try{
|
||||
const r=await fetch("/status",{cache:"no-store"});
|
||||
@@ -149,6 +201,7 @@ async function load(){
|
||||
}
|
||||
}
|
||||
load();
|
||||
loadWifiConfig();
|
||||
setInterval(load,3000);
|
||||
</script>
|
||||
</body>
|
||||
@@ -161,20 +214,55 @@ HardwareSerial DashboardSerial(2);
|
||||
String uartLineBuffer;
|
||||
|
||||
Preferences wifiPrefs;
|
||||
String staSsid = "";
|
||||
String staPassword = "";
|
||||
|
||||
const int MAX_WIFI_NETWORKS = 3;
|
||||
String staSsids[MAX_WIFI_NETWORKS];
|
||||
String staPasswords[MAX_WIFI_NETWORKS];
|
||||
int wifiNetworkCount = 0;
|
||||
String activeStaSsid = "";
|
||||
|
||||
void loadWifiConfig() {
|
||||
wifiPrefs.begin("wifi", true);
|
||||
staSsid = wifiPrefs.getString("ssid", "");
|
||||
staPassword = wifiPrefs.getString("password", "");
|
||||
|
||||
wifiNetworkCount = wifiPrefs.getInt("count", 0);
|
||||
|
||||
if (wifiNetworkCount < 0) wifiNetworkCount = 0;
|
||||
if (wifiNetworkCount > MAX_WIFI_NETWORKS) wifiNetworkCount = MAX_WIFI_NETWORKS;
|
||||
|
||||
for (int i = 0; i < MAX_WIFI_NETWORKS; i++) {
|
||||
staSsids[i] = wifiPrefs.getString(("ssid" + String(i)).c_str(), "");
|
||||
staPasswords[i] = wifiPrefs.getString(("pass" + String(i)).c_str(), "");
|
||||
}
|
||||
|
||||
// Migration path from old single-network config
|
||||
if (wifiNetworkCount == 0) {
|
||||
String oldSsid = wifiPrefs.getString("ssid", "");
|
||||
String oldPassword = wifiPrefs.getString("password", "");
|
||||
|
||||
if (oldSsid.length() > 0) {
|
||||
staSsids[0] = oldSsid;
|
||||
staPasswords[0] = oldPassword;
|
||||
wifiNetworkCount = 1;
|
||||
}
|
||||
}
|
||||
|
||||
wifiPrefs.end();
|
||||
}
|
||||
|
||||
void saveWifiConfig() {
|
||||
wifiPrefs.begin("wifi", false);
|
||||
wifiPrefs.putString("ssid", staSsid);
|
||||
wifiPrefs.putString("password", staPassword);
|
||||
|
||||
wifiPrefs.putInt("count", wifiNetworkCount);
|
||||
|
||||
for (int i = 0; i < MAX_WIFI_NETWORKS; i++) {
|
||||
wifiPrefs.putString(("ssid" + String(i)).c_str(), staSsids[i]);
|
||||
wifiPrefs.putString(("pass" + String(i)).c_str(), staPasswords[i]);
|
||||
}
|
||||
|
||||
// Keep legacy keys updated for easier debugging
|
||||
wifiPrefs.putString("ssid", wifiNetworkCount > 0 ? staSsids[0] : "");
|
||||
wifiPrefs.putString("password", wifiNetworkCount > 0 ? staPasswords[0] : "");
|
||||
|
||||
wifiPrefs.end();
|
||||
}
|
||||
|
||||
@@ -182,44 +270,67 @@ void clearWifiConfig() {
|
||||
wifiPrefs.begin("wifi", false);
|
||||
wifiPrefs.clear();
|
||||
wifiPrefs.end();
|
||||
staSsid = "";
|
||||
staPassword = "";
|
||||
|
||||
wifiNetworkCount = 0;
|
||||
activeStaSsid = "";
|
||||
|
||||
for (int i = 0; i < MAX_WIFI_NETWORKS; i++) {
|
||||
staSsids[i] = "";
|
||||
staPasswords[i] = "";
|
||||
}
|
||||
}
|
||||
|
||||
void connectStaWifi() {
|
||||
if (staSsid.length() == 0) {
|
||||
if (wifiNetworkCount <= 0) {
|
||||
Serial.println("STA WiFi not configured.");
|
||||
return;
|
||||
}
|
||||
|
||||
Serial.print("Connecting STA WiFi to: ");
|
||||
Serial.println(staSsid);
|
||||
WiFi.disconnect(false);
|
||||
activeStaSsid = "";
|
||||
|
||||
WiFi.begin(staSsid.c_str(), staPassword.c_str());
|
||||
for (int i = 0; i < wifiNetworkCount; i++) {
|
||||
if (staSsids[i].length() == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
unsigned long start = millis();
|
||||
while (WiFi.status() != WL_CONNECTED && millis() - start < 10000) {
|
||||
delay(500);
|
||||
Serial.print(".");
|
||||
Serial.print("Connecting STA WiFi to: ");
|
||||
Serial.println(staSsids[i]);
|
||||
|
||||
WiFi.begin(staSsids[i].c_str(), staPasswords[i].c_str());
|
||||
|
||||
unsigned long start = millis();
|
||||
while (WiFi.status() != WL_CONNECTED && millis() - start < 10000) {
|
||||
delay(500);
|
||||
Serial.print(".");
|
||||
}
|
||||
|
||||
Serial.println();
|
||||
|
||||
if (WiFi.status() == WL_CONNECTED) {
|
||||
activeStaSsid = staSsids[i];
|
||||
Serial.println("STA WiFi connected");
|
||||
Serial.print("STA SSID: ");
|
||||
Serial.println(activeStaSsid);
|
||||
Serial.print("STA IP: ");
|
||||
Serial.println(WiFi.localIP());
|
||||
return;
|
||||
}
|
||||
|
||||
Serial.println("STA WiFi attempt failed.");
|
||||
}
|
||||
|
||||
Serial.println();
|
||||
|
||||
if (WiFi.status() == WL_CONNECTED) {
|
||||
Serial.println("STA WiFi connected");
|
||||
Serial.print("STA IP: ");
|
||||
Serial.println(WiFi.localIP());
|
||||
} else {
|
||||
Serial.println("STA WiFi failed. AP remains available.");
|
||||
}
|
||||
Serial.println("All STA WiFi attempts failed. AP remains available.");
|
||||
}
|
||||
|
||||
void printNetworkStatus() {
|
||||
Serial.println("Network:");
|
||||
Serial.print(" AP IP: ");
|
||||
Serial.println(WiFi.softAPIP());
|
||||
Serial.print(" STA SSID: ");
|
||||
Serial.println(staSsid.length() ? staSsid : "(not configured)");
|
||||
Serial.print(" Saved STA Networks: ");
|
||||
Serial.println(wifiNetworkCount);
|
||||
Serial.print(" Active STA SSID: ");
|
||||
Serial.println(activeStaSsid.length() ? activeStaSsid : "(none)");
|
||||
Serial.print(" STA Connected: ");
|
||||
Serial.println(WiFi.status() == WL_CONNECTED ? "true" : "false");
|
||||
if (WiFi.status() == WL_CONNECTED) {
|
||||
@@ -323,6 +434,13 @@ void buildStatusDocument(JsonDocument& doc) {
|
||||
JsonObject network = doc.createNestedObject("network");
|
||||
network["wifi_enabled"] = true;
|
||||
network["uart_connected"] = true;
|
||||
network["ap_enabled"] = true;
|
||||
network["ap_ip"] = WiFi.softAPIP().toString();
|
||||
network["sta_enabled"] = wifiNetworkCount > 0;
|
||||
network["sta_connected"] = WiFi.status() == WL_CONNECTED;
|
||||
network["sta_ssid"] = activeStaSsid;
|
||||
network["sta_ip"] = WiFi.status() == WL_CONNECTED ? WiFi.localIP().toString() : "";
|
||||
network["saved_network_count"] = wifiNetworkCount;
|
||||
|
||||
JsonObject alarmObj = doc.createNestedObject("alarms");
|
||||
alarmObj["low_soc"] = alarms.lowSoc;
|
||||
@@ -613,12 +731,20 @@ void handleUartMessage(const String& line) {
|
||||
|
||||
JsonObject wifi = response.createNestedObject("wifi");
|
||||
wifi["ap_enabled"] = true;
|
||||
wifi["sta_enabled"] = staSsid.length() > 0;
|
||||
wifi["ssid"] = staSsid;
|
||||
wifi["password_set"] = staPassword.length() > 0;
|
||||
wifi["sta_enabled"] = wifiNetworkCount > 0;
|
||||
wifi["network_count"] = wifiNetworkCount;
|
||||
wifi["active_ssid"] = activeStaSsid;
|
||||
wifi["sta_connected"] = WiFi.status() == WL_CONNECTED;
|
||||
wifi["ap_ip"] = WiFi.softAPIP().toString();
|
||||
|
||||
JsonArray networks = wifi.createNestedArray("networks");
|
||||
for (int i = 0; i < wifiNetworkCount; i++) {
|
||||
JsonObject network = networks.createNestedObject();
|
||||
network["index"] = i + 1;
|
||||
network["ssid"] = staSsids[i];
|
||||
network["password_set"] = staPasswords[i].length() > 0;
|
||||
}
|
||||
|
||||
if (WiFi.status() == WL_CONNECTED) {
|
||||
wifi["sta_ip"] = WiFi.localIP().toString();
|
||||
} else {
|
||||
@@ -631,12 +757,34 @@ void handleUartMessage(const String& line) {
|
||||
}
|
||||
|
||||
if (strcmp(type, "config_wifi") == 0) {
|
||||
if (doc["ssid"].is<String>()) {
|
||||
staSsid = doc["ssid"].as<String>();
|
||||
}
|
||||
JsonArray networks = doc["networks"].as<JsonArray>();
|
||||
|
||||
if (doc["password"].is<String>()) {
|
||||
staPassword = doc["password"].as<String>();
|
||||
if (!networks.isNull()) {
|
||||
wifiNetworkCount = 0;
|
||||
|
||||
for (JsonObject network : networks) {
|
||||
if (wifiNetworkCount >= MAX_WIFI_NETWORKS) break;
|
||||
|
||||
String ssid = network["ssid"] | "";
|
||||
String password = network["password"] | "";
|
||||
ssid.trim();
|
||||
|
||||
if (ssid.length() == 0) continue;
|
||||
|
||||
staSsids[wifiNetworkCount] = ssid;
|
||||
staPasswords[wifiNetworkCount] = password;
|
||||
wifiNetworkCount++;
|
||||
}
|
||||
} else {
|
||||
if (doc["ssid"].is<String>()) {
|
||||
staSsids[0] = doc["ssid"].as<String>();
|
||||
if (wifiNetworkCount < 1) wifiNetworkCount = 1;
|
||||
}
|
||||
|
||||
if (doc["password"].is<String>()) {
|
||||
staPasswords[0] = doc["password"].as<String>();
|
||||
if (wifiNetworkCount < 1) wifiNetworkCount = 1;
|
||||
}
|
||||
}
|
||||
|
||||
saveWifiConfig();
|
||||
@@ -644,8 +792,7 @@ void handleUartMessage(const String& line) {
|
||||
DynamicJsonDocument response(512);
|
||||
response["type"] = "wifi_config_response";
|
||||
response["ok"] = true;
|
||||
response["ssid"] = staSsid;
|
||||
response["password_set"] = staPassword.length() > 0;
|
||||
response["network_count"] = wifiNetworkCount;
|
||||
|
||||
serializeJson(response, DashboardSerial);
|
||||
DashboardSerial.println();
|
||||
@@ -810,15 +957,40 @@ void handleDebugSerial() {
|
||||
}
|
||||
|
||||
if (command.startsWith("wifi ssid ")) {
|
||||
staSsid = command.substring(10);
|
||||
Serial.println("OK WiFi SSID updated");
|
||||
staSsids[0] = command.substring(10);
|
||||
if (wifiNetworkCount < 1) wifiNetworkCount = 1;
|
||||
Serial.println("OK WiFi SSID 1 updated");
|
||||
Serial.println("Run: wifi save");
|
||||
return;
|
||||
}
|
||||
|
||||
if (command.startsWith("wifi pass ")) {
|
||||
staPassword = command.substring(10);
|
||||
Serial.println("OK WiFi password updated");
|
||||
staPasswords[0] = command.substring(10);
|
||||
if (wifiNetworkCount < 1) wifiNetworkCount = 1;
|
||||
Serial.println("OK WiFi password 1 updated");
|
||||
Serial.println("Run: wifi save");
|
||||
return;
|
||||
}
|
||||
|
||||
if (command.startsWith("wifi add ")) {
|
||||
String rest = command.substring(9);
|
||||
int split = rest.indexOf('|');
|
||||
|
||||
if (split < 0) {
|
||||
Serial.println("Invalid command. Use: wifi add SSID|PASSWORD");
|
||||
return;
|
||||
}
|
||||
|
||||
if (wifiNetworkCount >= MAX_WIFI_NETWORKS) {
|
||||
Serial.println("WiFi network list full");
|
||||
return;
|
||||
}
|
||||
|
||||
staSsids[wifiNetworkCount] = rest.substring(0, split);
|
||||
staPasswords[wifiNetworkCount] = rest.substring(split + 1);
|
||||
wifiNetworkCount++;
|
||||
|
||||
Serial.println("OK WiFi network added");
|
||||
Serial.println("Run: wifi save");
|
||||
return;
|
||||
}
|
||||
@@ -1114,16 +1286,16 @@ void handleFactoryResetConfig() {
|
||||
|
||||
|
||||
void sendWifiConfigJson() {
|
||||
DynamicJsonDocument doc(1024);
|
||||
DynamicJsonDocument doc(2048);
|
||||
|
||||
doc["type"] = "wifi_config_response";
|
||||
doc["ok"] = true;
|
||||
|
||||
JsonObject wifi = doc.createNestedObject("wifi");
|
||||
wifi["ap_enabled"] = true;
|
||||
wifi["sta_enabled"] = staSsid.length() > 0;
|
||||
wifi["ssid"] = staSsid;
|
||||
wifi["password_set"] = staPassword.length() > 0;
|
||||
wifi["sta_enabled"] = wifiNetworkCount > 0;
|
||||
wifi["network_count"] = wifiNetworkCount;
|
||||
wifi["active_ssid"] = activeStaSsid;
|
||||
wifi["sta_connected"] = WiFi.status() == WL_CONNECTED;
|
||||
wifi["ap_ip"] = WiFi.softAPIP().toString();
|
||||
|
||||
@@ -1133,6 +1305,14 @@ void sendWifiConfigJson() {
|
||||
wifi["sta_ip"] = "";
|
||||
}
|
||||
|
||||
JsonArray networks = wifi.createNestedArray("networks");
|
||||
for (int i = 0; i < wifiNetworkCount; i++) {
|
||||
JsonObject network = networks.createNestedObject();
|
||||
network["index"] = i + 1;
|
||||
network["ssid"] = staSsids[i];
|
||||
network["password_set"] = staPasswords[i].length() > 0;
|
||||
}
|
||||
|
||||
String output;
|
||||
serializeJson(doc, output);
|
||||
server.send(200, "application/json", output);
|
||||
@@ -1143,7 +1323,7 @@ void handleGetWifiConfig() {
|
||||
}
|
||||
|
||||
void handleUpdateWifiConfig() {
|
||||
DynamicJsonDocument doc(1024);
|
||||
DynamicJsonDocument doc(2048);
|
||||
DeserializationError error = deserializeJson(doc, server.arg("plain"));
|
||||
|
||||
if (error) {
|
||||
@@ -1151,12 +1331,42 @@ void handleUpdateWifiConfig() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (doc["ssid"].is<String>()) {
|
||||
staSsid = doc["ssid"].as<String>();
|
||||
}
|
||||
JsonArray networks = doc["networks"].as<JsonArray>();
|
||||
|
||||
if (doc["password"].is<String>()) {
|
||||
staPassword = doc["password"].as<String>();
|
||||
if (!networks.isNull()) {
|
||||
wifiNetworkCount = 0;
|
||||
|
||||
for (JsonObject network : networks) {
|
||||
if (wifiNetworkCount >= MAX_WIFI_NETWORKS) break;
|
||||
|
||||
String ssid = network["ssid"] | "";
|
||||
String password = network["password"] | "";
|
||||
|
||||
ssid.trim();
|
||||
|
||||
if (ssid.length() == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
staSsids[wifiNetworkCount] = ssid;
|
||||
staPasswords[wifiNetworkCount] = password;
|
||||
wifiNetworkCount++;
|
||||
}
|
||||
|
||||
for (int i = wifiNetworkCount; i < MAX_WIFI_NETWORKS; i++) {
|
||||
staSsids[i] = "";
|
||||
staPasswords[i] = "";
|
||||
}
|
||||
} else {
|
||||
if (doc["ssid"].is<String>()) {
|
||||
staSsids[0] = doc["ssid"].as<String>();
|
||||
if (wifiNetworkCount < 1) wifiNetworkCount = 1;
|
||||
}
|
||||
|
||||
if (doc["password"].is<String>()) {
|
||||
staPasswords[0] = doc["password"].as<String>();
|
||||
if (wifiNetworkCount < 1) wifiNetworkCount = 1;
|
||||
}
|
||||
}
|
||||
|
||||
saveWifiConfig();
|
||||
|
||||
Reference in New Issue
Block a user