diff --git a/docs/_assets/repeater_web_panel_app.png b/docs/_assets/repeater_web_panel_app.png new file mode 100644 index 00000000..c5edae05 Binary files /dev/null and b/docs/_assets/repeater_web_panel_app.png differ diff --git a/docs/_assets/repeater_web_panel_overview.png b/docs/_assets/repeater_web_panel_overview.png deleted file mode 100644 index a06a5849..00000000 Binary files a/docs/_assets/repeater_web_panel_overview.png and /dev/null differ diff --git a/docs/_assets/repeater_web_panel_stats.png b/docs/_assets/repeater_web_panel_stats.png new file mode 100644 index 00000000..3f0d41ca Binary files /dev/null and b/docs/_assets/repeater_web_panel_stats.png differ diff --git a/docs/boards.md b/docs/boards.md index 52980b3b..843882f5 100644 --- a/docs/boards.md +++ b/docs/boards.md @@ -86,48 +86,48 @@ If you are deciding between otherwise similar boards, these target-level setting This table includes all repeater MQTT targets currently defined in `variants/eastmesh_mqtt/platformio.ini`. -| Target | MCU | RAM | PSRAM | Flash | LoRa | Display | GPS | SD | -| ------------------------ | ------- | ------ | ----- | ----- | ------ | --------------- | --- | -- | -| Ebyte_EoRa-S3 | ESP32S3 | 512 KB | 2 MB | 4 MB | SX1262 | OLED (SSD1306) | | | -| Generic_E22_sx1262 | ESP32 | 520 KB | No | 4 MB | SX1262 | None | | | -| Generic_E22_sx1268 | ESP32 | 520 KB | No | 4 MB | SX1268 | None | | | -| Heltec_ct62 | ESP32C3 | 400 KB | No | 4 MB | SX1262 | None | | | -| Heltec_E213 | ESP32S3 | 512 KB | 8 MB | 16 MB | SX1262 | E-paper (2.13") | | | -| Heltec_E290 | ESP32S3 | 512 KB | 8 MB | 16 MB | SX1262 | E-paper (2.9") | | | -| Heltec_T190 | ESP32S3 | 512 KB | 8 MB | 16 MB | SX1262 | TFT (ST7789) | | | -| heltec_tracker_v2 | ESP32S3 | 512 KB | No | 8 MB | SX1262 | TFT (ST7735) | ✅ | | -| Heltec_v2 | ESP32 | 520 KB | No | 8 MB | SX1276 | OLED (SSD1306) | | | -| Heltec_v3 | ESP32S3 | 512 KB | No | 8 MB | SX1262 | OLED (SSD1306) | ✅ | | -| heltec_v4 | ESP32S3 | 512 KB | 2 MB | 16 MB | SX1262 | OLED (SSD1306) | ✅ | | -| heltec_v4_tft | ESP32S3 | 512 KB | 2 MB | 16 MB | SX1262 | TFT (ST7789) | ✅ | | -| Heltec_Wireless_Paper | ESP32S3 | 512 KB | No | 8 MB | SX1262 | E-paper (2.13") | | | -| Heltec_Wireless_Tracker | ESP32S3 | 512 KB | No | 8 MB | SX1262 | TFT (ST7735) | ✅ | | -| Heltec_WSL3 | ESP32S3 | 512 KB | No | 8 MB | SX1262 | None | ✅ | | -| LilyGo_T3S3_sx1262 | ESP32S3 | 512 KB | 2 MB | 4 MB | SX1262 | OLED (SSD1306) | | 🧪 | -| LilyGo_T3S3_sx1276 | ESP32S3 | 512 KB | 2 MB | 4 MB | SX1276 | OLED (SSD1306) | | 🧪 | -| LilyGo_TBeam_1W | ESP32S3 | 512 KB | 8 MB | 16 MB | SX1262 | OLED (SH1106) | ✅ | ✅ | -| LilyGo_TDeck | ESP32S3 | 512 KB | No | 16 MB | SX1262 | TFT (ST7789) | ✅ | 🧪 | -| LilyGo_Tlora_C6 | ESP32C6 | 512 KB | No | 4 MB | SX1262 | None | | | -| LilyGo_TLora_V2_1_1_6 | ESP32 | 520 KB | No | 4 MB | SX1276 | OLED (SSD1306) | ✅ | | -| M5Stack_Unit_C6L | ESP32C6 | 512 KB | No | 4 MB | SX1262 | None | ✅ | | -| Meshadventurer_sx1262 | ESP32 | 520 KB | No | 4 MB | SX1262 | OLED (SSD1306) | ✅ | | -| Meshadventurer_sx1268 | ESP32 | 520 KB | No | 4 MB | SX1268 | OLED (SSD1306) | ✅ | | -| Meshimi | ESP32C6 | 512 KB | No | 4 MB | SX1262 | None | | | -| nibble_screen_connect | ESP32S3 | 512 KB | No | 4 MB | SX1262 | OLED (SSD1306) | | | -| RAK_3112 | ESP32S3 | 512 KB | No | 8 MB | SX1262 | None | ✅ | | -| Station_G2 | ESP32S3 | 512 KB | 8 MB | 16 MB | SX1262 | OLED (SH1106) | ✅ | | -| Station_G2_logging | ESP32S3 | 512 KB | 8 MB | 16 MB | SX1262 | OLED (SH1106) | ✅ | | -| T_Beam_S3_Supreme_SX1262 | ESP32S3 | 512 KB | 8 MB | 8 MB | SX1262 | OLED (SH1106) | ✅ | ✅ | -| Tbeam_SX1262 | ESP32 | 520 KB | No | 4 MB | SX1262 | OLED (SSD1306) | ✅ | | -| Tbeam_SX1276 | ESP32 | 520 KB | No | 4 MB | SX1276 | OLED (SSD1306) | ✅ | | -| Tenstar_C3_sx1262 | ESP32C3 | 400 KB | No | 4 MB | SX1262 | None | | | -| Tenstar_C3_sx1268 | ESP32C3 | 400 KB | No | 4 MB | SX1268 | None | | | -| ThinkNode_M2 | ESP32S3 | 512 KB | No | 4 MB | SX1262 | OLED (SH1106) | | | -| ThinkNode_M5 | ESP32S3 | 512 KB | No | 4 MB | SX1262 | E-paper (GxEPD) | ✅ | | -| WHY2025_badge | ESP32C6 | 512 KB | No | 4 MB | SX1262 | None | | | -| Xiao_C3 | ESP32C3 | 400 KB | No | 4 MB | SX1262 | None | ✅ | | -| Xiao_C6 | ESP32C6 | 512 KB | No | 4 MB | SX1262 | None | | | -| Xiao_S3_WIO | ESP32S3 | 512 KB | No | 8 MB | SX1262 | None | ✅ | | +| Target | MCU | RAM | PSRAM | Flash | LoRa | Display | GPS | SD | +| ------------------------ | ------- | ------ | ----- | ----- | ------ | --------------- | --- | --- | +| Ebyte_EoRa-S3 | ESP32S3 | 512 KB | 2 MB | 4 MB | SX1262 | OLED (SSD1306) | | | +| Generic_E22_sx1262 | ESP32 | 520 KB | No | 4 MB | SX1262 | None | | | +| Generic_E22_sx1268 | ESP32 | 520 KB | No | 4 MB | SX1268 | None | | | +| Heltec_ct62 | ESP32C3 | 400 KB | No | 4 MB | SX1262 | None | | | +| Heltec_E213 | ESP32S3 | 512 KB | 8 MB | 16 MB | SX1262 | E-paper (2.13") | | | +| Heltec_E290 | ESP32S3 | 512 KB | 8 MB | 16 MB | SX1262 | E-paper (2.9") | | | +| Heltec_T190 | ESP32S3 | 512 KB | 8 MB | 16 MB | SX1262 | TFT (ST7789) | | | +| heltec_tracker_v2 | ESP32S3 | 512 KB | No | 8 MB | SX1262 | TFT (ST7735) | ✅ | | +| Heltec_v2 | ESP32 | 520 KB | No | 8 MB | SX1276 | OLED (SSD1306) | | | +| Heltec_v3 | ESP32S3 | 512 KB | No | 8 MB | SX1262 | OLED (SSD1306) | ✅ | | +| heltec_v4 | ESP32S3 | 512 KB | 2 MB | 16 MB | SX1262 | OLED (SSD1306) | ✅ | | +| heltec_v4_tft | ESP32S3 | 512 KB | 2 MB | 16 MB | SX1262 | TFT (ST7789) | ✅ | | +| Heltec_Wireless_Paper | ESP32S3 | 512 KB | No | 8 MB | SX1262 | E-paper (2.13") | | | +| Heltec_Wireless_Tracker | ESP32S3 | 512 KB | No | 8 MB | SX1262 | TFT (ST7735) | ✅ | | +| Heltec_WSL3 | ESP32S3 | 512 KB | No | 8 MB | SX1262 | None | ✅ | | +| LilyGo_T3S3_sx1262 | ESP32S3 | 512 KB | 2 MB | 4 MB | SX1262 | OLED (SSD1306) | | 🧪 | +| LilyGo_T3S3_sx1276 | ESP32S3 | 512 KB | 2 MB | 4 MB | SX1276 | OLED (SSD1306) | | 🧪 | +| LilyGo_TBeam_1W | ESP32S3 | 512 KB | 8 MB | 16 MB | SX1262 | OLED (SH1106) | ✅ | ✅ | +| LilyGo_TDeck | ESP32S3 | 512 KB | No | 16 MB | SX1262 | TFT (ST7789) | ✅ | 🧪 | +| LilyGo_Tlora_C6 | ESP32C6 | 512 KB | No | 4 MB | SX1262 | None | | | +| LilyGo_TLora_V2_1_1_6 | ESP32 | 520 KB | No | 4 MB | SX1276 | OLED (SSD1306) | ✅ | | +| M5Stack_Unit_C6L | ESP32C6 | 512 KB | No | 4 MB | SX1262 | None | ✅ | | +| Meshadventurer_sx1262 | ESP32 | 520 KB | No | 4 MB | SX1262 | OLED (SSD1306) | ✅ | | +| Meshadventurer_sx1268 | ESP32 | 520 KB | No | 4 MB | SX1268 | OLED (SSD1306) | ✅ | | +| Meshimi | ESP32C6 | 512 KB | No | 4 MB | SX1262 | None | | | +| nibble_screen_connect | ESP32S3 | 512 KB | No | 4 MB | SX1262 | OLED (SSD1306) | | | +| RAK_3112 | ESP32S3 | 512 KB | No | 8 MB | SX1262 | None | ✅ | | +| Station_G2 | ESP32S3 | 512 KB | 8 MB | 16 MB | SX1262 | OLED (SH1106) | ✅ | | +| Station_G2_logging | ESP32S3 | 512 KB | 8 MB | 16 MB | SX1262 | OLED (SH1106) | ✅ | | +| T_Beam_S3_Supreme_SX1262 | ESP32S3 | 512 KB | 8 MB | 8 MB | SX1262 | OLED (SH1106) | ✅ | ✅ | +| Tbeam_SX1262 | ESP32 | 520 KB | No | 4 MB | SX1262 | OLED (SSD1306) | ✅ | | +| Tbeam_SX1276 | ESP32 | 520 KB | No | 4 MB | SX1276 | OLED (SSD1306) | ✅ | | +| Tenstar_C3_sx1262 | ESP32C3 | 400 KB | No | 4 MB | SX1262 | None | | | +| Tenstar_C3_sx1268 | ESP32C3 | 400 KB | No | 4 MB | SX1268 | None | | | +| ThinkNode_M2 | ESP32S3 | 512 KB | No | 4 MB | SX1262 | OLED (SH1106) | | | +| ThinkNode_M5 | ESP32S3 | 512 KB | No | 4 MB | SX1262 | E-paper (GxEPD) | ✅ | | +| WHY2025_badge | ESP32C6 | 512 KB | No | 4 MB | SX1262 | None | | | +| Xiao_C3 | ESP32C3 | 400 KB | No | 4 MB | SX1262 | None | ✅ | | +| Xiao_C6 | ESP32C6 | 512 KB | No | 4 MB | SX1262 | None | | | +| Xiao_S3_WIO | ESP32S3 | 512 KB | No | 8 MB | SX1262 | None | ✅ | | Note: all current `repeater_mqtt` boards support the local web panel except `Xiao_C3`, where it is disabled to preserve limited board resources. diff --git a/docs/web-panel.md b/docs/web-panel.md index 88096481..e8659229 100644 --- a/docs/web-panel.md +++ b/docs/web-panel.md @@ -26,9 +26,15 @@ Operational guidance: ## Screenshot Overview -The layout below reflects the current panel structure for the repeater web UI. +The screenshots below show the current split between the lighter `/app` admin page and the dedicated `/stats` status page. -![Repeater web panel overview](./_assets/repeater_web_panel_overview.png) +### `/app` + +![Repeater web panel `/app` overview](./_assets/repeater_web_panel_app.png) + +### `/stats` + +![Repeater web panel `/stats` overview](./_assets/repeater_web_panel_stats.png) ## Requirements diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index dd68eecb..8becb028 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -2132,35 +2132,96 @@ bool MyMesh::formatWebStatsSummaryJson(char* reply, size_t reply_size) { #else char wifi_ssid[48]; char wifi_status[20]; + char wifi_state[24]; char wifi_ip[20]; + char wifi_signal[16]; char wifi_powersave[12]; escapeJsonString(network.getWifiSSID()[0] ? network.getWifiSSID() : "-", wifi_ssid, sizeof(wifi_ssid)); escapeJsonString(network.getWifiPowerSave(), wifi_powersave, sizeof(wifi_powersave)); int wifi_rssi = 0; + int wifi_quality = 0; + int wifi_code = 0; #if defined(ESP32) if (network.getWifiSSID()[0] == 0) { strncpy(wifi_status, "unconfigured", sizeof(wifi_status) - 1); wifi_status[sizeof(wifi_status) - 1] = 0; + strncpy(wifi_state, "unconfigured", sizeof(wifi_state) - 1); + wifi_state[sizeof(wifi_state) - 1] = 0; strncpy(wifi_ip, "--", sizeof(wifi_ip) - 1); wifi_ip[sizeof(wifi_ip) - 1] = 0; + strncpy(wifi_signal, "--", sizeof(wifi_signal) - 1); + wifi_signal[sizeof(wifi_signal) - 1] = 0; } else if (network.isWifiConnected()) { strncpy(wifi_status, "connected", sizeof(wifi_status) - 1); wifi_status[sizeof(wifi_status) - 1] = 0; + strncpy(wifi_state, "connected", sizeof(wifi_state) - 1); + wifi_state[sizeof(wifi_state) - 1] = 0; String ip = WiFi.localIP().toString(); escapeJsonString(ip.c_str(), wifi_ip, sizeof(wifi_ip)); wifi_rssi = WiFi.RSSI(); + wifi_code = static_cast(WiFi.status()); + if (wifi_rssi <= -100) { + wifi_quality = 0; + strncpy(wifi_signal, "poor", sizeof(wifi_signal) - 1); + } else if (wifi_rssi >= -50) { + wifi_quality = 100; + strncpy(wifi_signal, "excellent", sizeof(wifi_signal) - 1); + } else { + wifi_quality = 2 * (wifi_rssi + 100); + if (wifi_rssi >= -60) { + strncpy(wifi_signal, "excellent", sizeof(wifi_signal) - 1); + } else if (wifi_rssi >= -67) { + strncpy(wifi_signal, "good", sizeof(wifi_signal) - 1); + } else if (wifi_rssi >= -75) { + strncpy(wifi_signal, "fair", sizeof(wifi_signal) - 1); + } else { + strncpy(wifi_signal, "poor", sizeof(wifi_signal) - 1); + } + } + wifi_signal[sizeof(wifi_signal) - 1] = 0; } else { strncpy(wifi_status, "connecting", sizeof(wifi_status) - 1); wifi_status[sizeof(wifi_status) - 1] = 0; + wifi_code = static_cast(WiFi.status()); + switch (WiFi.status()) { + case WL_IDLE_STATUS: + strncpy(wifi_state, "idle", sizeof(wifi_state) - 1); + break; + case WL_NO_SSID_AVAIL: + strncpy(wifi_state, "no_ssid", sizeof(wifi_state) - 1); + break; + case WL_SCAN_COMPLETED: + strncpy(wifi_state, "scan_completed", sizeof(wifi_state) - 1); + break; + case WL_CONNECT_FAILED: + strncpy(wifi_state, "connect_failed", sizeof(wifi_state) - 1); + break; + case WL_CONNECTION_LOST: + strncpy(wifi_state, "connection_lost", sizeof(wifi_state) - 1); + break; + case WL_DISCONNECTED: + strncpy(wifi_state, "disconnected", sizeof(wifi_state) - 1); + break; + default: + strncpy(wifi_state, "unknown", sizeof(wifi_state) - 1); + break; + } + wifi_state[sizeof(wifi_state) - 1] = 0; strncpy(wifi_ip, "--", sizeof(wifi_ip) - 1); wifi_ip[sizeof(wifi_ip) - 1] = 0; + strncpy(wifi_signal, "--", sizeof(wifi_signal) - 1); + wifi_signal[sizeof(wifi_signal) - 1] = 0; } #else strncpy(wifi_status, "unsupported", sizeof(wifi_status) - 1); wifi_status[sizeof(wifi_status) - 1] = 0; + strncpy(wifi_state, "unsupported", sizeof(wifi_state) - 1); + wifi_state[sizeof(wifi_state) - 1] = 0; strncpy(wifi_ip, "--", sizeof(wifi_ip) - 1); wifi_ip[sizeof(wifi_ip) - 1] = 0; + strncpy(wifi_signal, "--", sizeof(wifi_signal) - 1); + wifi_signal[sizeof(wifi_signal) - 1] = 0; #endif const int battery_pct = board.getBatteryPercent(); @@ -2189,7 +2250,7 @@ bool MyMesh::formatWebStatsSummaryJson(char* reply, size_t reply_size) { "\"packets\":{\"recv\":%u,\"sent\":%u,\"flood_tx\":%u,\"direct_tx\":%u,\"flood_rx\":%u,\"direct_rx\":%u," "\"recv_errors\":%u,\"direct_dups\":%u,\"flood_dups\":%u,\"neighbors\":%u}," "\"memory\":{\"heap_free\":%u,\"heap_min\":%u,\"heap_max\":%u,\"psram_free\":%u,\"psram_min\":%u,\"psram_max\":%u}," - "\"wifi\":{\"ssid\":\"%s\",\"status\":\"%s\",\"connected\":%s,\"ip\":\"%s\",\"rssi\":%d,\"powersave\":\"%s\"}," + "\"wifi\":{\"ssid\":\"%s\",\"status\":\"%s\",\"connected\":%s,\"state\":\"%s\",\"code\":%d,\"ip\":\"%s\",\"rssi\":%d,\"quality\":%d,\"signal\":\"%s\",\"powersave\":\"%s\"}," "\"services\":{\"mqtt_connected\":%s,\"web_enabled\":%s,\"web_panel_up\":%s,\"web_auth\":\"%s\"," "\"archive_available\":%s}", (_stats_history.isEnabled() && _stats_history.isRecentHistoryAvailable()) ? "true" : "false", @@ -2241,8 +2302,12 @@ bool MyMesh::formatWebStatsSummaryJson(char* reply, size_t reply_size) { wifi_ssid, wifi_status, network.isWifiConnected() ? "true" : "false", + wifi_state, + wifi_code, wifi_ip, wifi_rssi, + wifi_quality, + wifi_signal, wifi_powersave, mqtt_connected ? "true" : "false", web.isWebEnabled() ? "true" : "false",