mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2026-05-04 21:15:52 +00:00
18
interface/pnpm-lock.yaml
generated
18
interface/pnpm-lock.yaml
generated
@@ -1151,8 +1151,8 @@ packages:
|
|||||||
base64-js@1.5.1:
|
base64-js@1.5.1:
|
||||||
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
|
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
|
||||||
|
|
||||||
baseline-browser-mapping@2.10.25:
|
baseline-browser-mapping@2.10.27:
|
||||||
resolution: {integrity: sha512-QO/VHsXCQdnzADMfmkeOPvHdIAkoB7i0/rGjINPJEetLx75hNttVWGQ/jycHUDP9zZ9rupbm60WRxcwViB0MiA==}
|
resolution: {integrity: sha512-zEs/ufmZoUd7WftKpKyXaT6RFxpQ5Qm9xytKRHvJfxFV9DFJkZph9RvJ1LcOUi0Z1ZVijMte65JbILeV+8QQEA==}
|
||||||
engines: {node: '>=6.0.0'}
|
engines: {node: '>=6.0.0'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
@@ -2993,9 +2993,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==}
|
resolution: {integrity: sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==}
|
||||||
deprecated: 'Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility'
|
deprecated: 'Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility'
|
||||||
|
|
||||||
stack-trace@1.0.0-pre2:
|
stack-trace@1.0.0:
|
||||||
resolution: {integrity: sha512-2ztBJRek8IVofG9DBJqdy2N5kulaacX30Nz7xmkYF6ale9WBVmIy6mFBchvGX7Vx/MyjBhx+Rcxqrj+dbOnQ6A==}
|
resolution: {integrity: sha512-H6D7134xi6qONvh7ZHKgviXf+rd3vhGBSvebPZCaUkd8zvQ+7PtDw6CljPTe4cXWNf2IKZGNqw6VJXSb9IgBpA==}
|
||||||
engines: {node: '>=16'}
|
engines: {node: '>=20.0.0'}
|
||||||
|
|
||||||
strict-uri-encode@1.1.0:
|
strict-uri-encode@1.1.0:
|
||||||
resolution: {integrity: sha512-R3f198pcvnB+5IpnBlRkphuE9n46WyVl8I39W/ZUTZLz4nqSP/oLYUrcnJrw462Ds8he4YKMov2efsTIw1BDGQ==}
|
resolution: {integrity: sha512-R3f198pcvnB+5IpnBlRkphuE9n46WyVl8I39W/ZUTZLz4nqSP/oLYUrcnJrw462Ds8he4YKMov2efsTIw1BDGQ==}
|
||||||
@@ -4279,7 +4279,7 @@ snapshots:
|
|||||||
|
|
||||||
base64-js@1.5.1: {}
|
base64-js@1.5.1: {}
|
||||||
|
|
||||||
baseline-browser-mapping@2.10.25: {}
|
baseline-browser-mapping@2.10.27: {}
|
||||||
|
|
||||||
bin-build@3.0.0:
|
bin-build@3.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -4340,7 +4340,7 @@ snapshots:
|
|||||||
|
|
||||||
browserslist@4.28.2:
|
browserslist@4.28.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
baseline-browser-mapping: 2.10.25
|
baseline-browser-mapping: 2.10.27
|
||||||
caniuse-lite: 1.0.30001791
|
caniuse-lite: 1.0.30001791
|
||||||
electron-to-chromium: 1.5.349
|
electron-to-chromium: 1.5.349
|
||||||
node-releases: 2.0.38
|
node-releases: 2.0.38
|
||||||
@@ -6178,7 +6178,7 @@ snapshots:
|
|||||||
|
|
||||||
stable@0.1.8: {}
|
stable@0.1.8: {}
|
||||||
|
|
||||||
stack-trace@1.0.0-pre2: {}
|
stack-trace@1.0.0: {}
|
||||||
|
|
||||||
strict-uri-encode@1.1.0: {}
|
strict-uri-encode@1.1.0: {}
|
||||||
|
|
||||||
@@ -6420,7 +6420,7 @@ snapshots:
|
|||||||
node-html-parser: 6.1.13
|
node-html-parser: 6.1.13
|
||||||
simple-code-frame: 1.3.0
|
simple-code-frame: 1.3.0
|
||||||
source-map: 0.7.6
|
source-map: 0.7.6
|
||||||
stack-trace: 1.0.0-pre2
|
stack-trace: 1.0.0
|
||||||
vite: 8.0.10(@types/node@25.6.0)(esbuild@0.27.4)(terser@5.46.2)
|
vite: 8.0.10(@types/node@25.6.0)(esbuild@0.27.4)(terser@5.46.2)
|
||||||
|
|
||||||
vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.4)(terser@5.46.2):
|
vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.4)(terser@5.46.2):
|
||||||
|
|||||||
@@ -173,7 +173,7 @@ const NetworkSettings = () => {
|
|||||||
<ValidatedTextField
|
<ValidatedTextField
|
||||||
fieldErrors={fieldErrors || {}}
|
fieldErrors={fieldErrors || {}}
|
||||||
name="ssid"
|
name="ssid"
|
||||||
label={'SSID (' + LL.NETWORK_BLANK_SSID() + ')'}
|
label="SSID"
|
||||||
fullWidth
|
fullWidth
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
value={data.ssid}
|
value={data.ssid}
|
||||||
|
|||||||
@@ -261,7 +261,6 @@ const cz: Translation = {
|
|||||||
SCAN_AGAIN: 'Skenovat znovu',
|
SCAN_AGAIN: 'Skenovat znovu',
|
||||||
NETWORK_SCANNER: 'Síťový skener',
|
NETWORK_SCANNER: 'Síťový skener',
|
||||||
NETWORK_NO_WIFI: 'Nenalezeny žádné WiFi sítě',
|
NETWORK_NO_WIFI: 'Nenalezeny žádné WiFi sítě',
|
||||||
NETWORK_BLANK_SSID: 'ponechte prázdné pro deaktivaci WiFi a povolení ETH',
|
|
||||||
NETWORK_BLANK_BSSID: 'ponechte prázdné pokud použijete jen SSID',
|
NETWORK_BLANK_BSSID: 'ponechte prázdné pokud použijete jen SSID',
|
||||||
TX_POWER: 'Vysílací výkon',
|
TX_POWER: 'Vysílací výkon',
|
||||||
HOSTNAME: 'Název hostitele',
|
HOSTNAME: 'Název hostitele',
|
||||||
|
|||||||
@@ -261,7 +261,6 @@ const de: Translation = {
|
|||||||
SCAN_AGAIN: 'Erneute Suche',
|
SCAN_AGAIN: 'Erneute Suche',
|
||||||
NETWORK_SCANNER: 'Netzwerksuche',
|
NETWORK_SCANNER: 'Netzwerksuche',
|
||||||
NETWORK_NO_WIFI: 'Keine WiFi-Netzwerke gefunden',
|
NETWORK_NO_WIFI: 'Keine WiFi-Netzwerke gefunden',
|
||||||
NETWORK_BLANK_SSID: 'Freilassen, um WiFi zu deaktivieren und ETH zu aktivieren.',
|
|
||||||
NETWORK_BLANK_BSSID: 'Freilassen, um nur SSID für die Verbindung zu nutzen.',
|
NETWORK_BLANK_BSSID: 'Freilassen, um nur SSID für die Verbindung zu nutzen.',
|
||||||
TX_POWER: 'Tx Leistung',
|
TX_POWER: 'Tx Leistung',
|
||||||
HOSTNAME: 'Hostname',
|
HOSTNAME: 'Hostname',
|
||||||
|
|||||||
@@ -261,7 +261,6 @@ const en: Translation = {
|
|||||||
SCAN_AGAIN: 'Scan again',
|
SCAN_AGAIN: 'Scan again',
|
||||||
NETWORK_SCANNER: 'Network Scanner',
|
NETWORK_SCANNER: 'Network Scanner',
|
||||||
NETWORK_NO_WIFI: 'No WiFi networks found',
|
NETWORK_NO_WIFI: 'No WiFi networks found',
|
||||||
NETWORK_BLANK_SSID: 'leave blank to disable WiFi and enable ETH',
|
|
||||||
NETWORK_BLANK_BSSID: 'leave blank to use only SSID',
|
NETWORK_BLANK_BSSID: 'leave blank to use only SSID',
|
||||||
TX_POWER: 'Tx Power',
|
TX_POWER: 'Tx Power',
|
||||||
HOSTNAME: 'Hostname',
|
HOSTNAME: 'Hostname',
|
||||||
|
|||||||
@@ -261,7 +261,6 @@ const fr: Translation = {
|
|||||||
SCAN_AGAIN: 'Rescanner',
|
SCAN_AGAIN: 'Rescanner',
|
||||||
NETWORK_SCANNER: 'Scan réseau',
|
NETWORK_SCANNER: 'Scan réseau',
|
||||||
NETWORK_NO_WIFI: 'Pas de réseau WiFi trouvé',
|
NETWORK_NO_WIFI: 'Pas de réseau WiFi trouvé',
|
||||||
NETWORK_BLANK_SSID: 'laisser vide pour désactiver le WiFi',
|
|
||||||
NETWORK_BLANK_BSSID: 'laisser vide pour utiliser uniquement le SSID',
|
NETWORK_BLANK_BSSID: 'laisser vide pour utiliser uniquement le SSID',
|
||||||
TX_POWER: 'Puissance Tx',
|
TX_POWER: 'Puissance Tx',
|
||||||
HOSTNAME: "Nom d'hôte",
|
HOSTNAME: "Nom d'hôte",
|
||||||
|
|||||||
@@ -261,7 +261,6 @@ const it: Translation = {
|
|||||||
SCAN_AGAIN: 'Scansiona ancora',
|
SCAN_AGAIN: 'Scansiona ancora',
|
||||||
NETWORK_SCANNER: 'Scansione Rete',
|
NETWORK_SCANNER: 'Scansione Rete',
|
||||||
NETWORK_NO_WIFI: 'Nessuana rete WiFi trovata',
|
NETWORK_NO_WIFI: 'Nessuana rete WiFi trovata',
|
||||||
NETWORK_BLANK_SSID: 'lasciare vuoto per disattivare WiFi',
|
|
||||||
NETWORK_BLANK_BSSID: 'lasciare vuoto per usare solo SSID',
|
NETWORK_BLANK_BSSID: 'lasciare vuoto per usare solo SSID',
|
||||||
TX_POWER: 'Potenza Tx',
|
TX_POWER: 'Potenza Tx',
|
||||||
HOSTNAME: 'Nome ospite',
|
HOSTNAME: 'Nome ospite',
|
||||||
|
|||||||
@@ -261,7 +261,6 @@ const nl: Translation = {
|
|||||||
SCAN_AGAIN: 'Opnieuw scannen',
|
SCAN_AGAIN: 'Opnieuw scannen',
|
||||||
NETWORK_SCANNER: 'Netwerk Scannen',
|
NETWORK_SCANNER: 'Netwerk Scannen',
|
||||||
NETWORK_NO_WIFI: 'Geen WiFi netwerken gevonden',
|
NETWORK_NO_WIFI: 'Geen WiFi netwerken gevonden',
|
||||||
NETWORK_BLANK_SSID: 'laat leeg om WiFi uit te schakelen',
|
|
||||||
NETWORK_BLANK_BSSID: 'laat leeg om alleen SSID te bebruiken',
|
NETWORK_BLANK_BSSID: 'laat leeg om alleen SSID te bebruiken',
|
||||||
TX_POWER: 'Tx Vermogen',
|
TX_POWER: 'Tx Vermogen',
|
||||||
HOSTNAME: 'Hostnaam',
|
HOSTNAME: 'Hostnaam',
|
||||||
|
|||||||
@@ -261,7 +261,6 @@ const no: Translation = {
|
|||||||
SCAN_AGAIN: 'Søk igjen',
|
SCAN_AGAIN: 'Søk igjen',
|
||||||
NETWORK_SCANNER: 'Nettverk Scanner',
|
NETWORK_SCANNER: 'Nettverk Scanner',
|
||||||
NETWORK_NO_WIFI: 'Ingen trådløse nett funnet',
|
NETWORK_NO_WIFI: 'Ingen trådløse nett funnet',
|
||||||
NETWORK_BLANK_SSID: 'la feltet være blankt for å deaktivisere trådløst nettverk',
|
|
||||||
NETWORK_BLANK_BSSID: 'la feltet være blankt for å bruke kun SSID',
|
NETWORK_BLANK_BSSID: 'la feltet være blankt for å bruke kun SSID',
|
||||||
TX_POWER: 'Tx Effekt',
|
TX_POWER: 'Tx Effekt',
|
||||||
HOSTNAME: 'Hostname',
|
HOSTNAME: 'Hostname',
|
||||||
|
|||||||
@@ -261,7 +261,6 @@ const pl: BaseTranslation = {
|
|||||||
SCAN_AGAIN: 'Skanuj ponownie',
|
SCAN_AGAIN: 'Skanuj ponownie',
|
||||||
NETWORK_SCANNER: 'Skaner sieci WiFi',
|
NETWORK_SCANNER: 'Skaner sieci WiFi',
|
||||||
NETWORK_NO_WIFI: 'Brak sieci WiFi w zasięgu',
|
NETWORK_NO_WIFI: 'Brak sieci WiFi w zasięgu',
|
||||||
NETWORK_BLANK_SSID: 'pozostaw puste aby wyłączyć WiFi i włączyć ETH',
|
|
||||||
NETWORK_BLANK_BSSID: 'pozostaw puste aby używać tylko SSID',
|
NETWORK_BLANK_BSSID: 'pozostaw puste aby używać tylko SSID',
|
||||||
TX_POWER: 'Moc nadawania',
|
TX_POWER: 'Moc nadawania',
|
||||||
HOSTNAME: 'Nazwa w sieci',
|
HOSTNAME: 'Nazwa w sieci',
|
||||||
|
|||||||
@@ -261,7 +261,6 @@ const sk: Translation = {
|
|||||||
SCAN_AGAIN: 'Skenovať znova',
|
SCAN_AGAIN: 'Skenovať znova',
|
||||||
NETWORK_SCANNER: 'Sieťový skener',
|
NETWORK_SCANNER: 'Sieťový skener',
|
||||||
NETWORK_NO_WIFI: 'WiFi siete nenájdené',
|
NETWORK_NO_WIFI: 'WiFi siete nenájdené',
|
||||||
NETWORK_BLANK_SSID: 'nechajte prázdne, ak chcete zakázať WiFi a povoliť ETH',
|
|
||||||
NETWORK_BLANK_BSSID: 'ponechajte prázdne, ak chcete používať iba SSID',
|
NETWORK_BLANK_BSSID: 'ponechajte prázdne, ak chcete používať iba SSID',
|
||||||
TX_POWER: 'Tx výkon',
|
TX_POWER: 'Tx výkon',
|
||||||
HOSTNAME: 'Hostname',
|
HOSTNAME: 'Hostname',
|
||||||
|
|||||||
@@ -261,7 +261,6 @@ const sv: Translation = {
|
|||||||
SCAN_AGAIN: 'Sök igen',
|
SCAN_AGAIN: 'Sök igen',
|
||||||
NETWORK_SCANNER: 'Hittade nätverk',
|
NETWORK_SCANNER: 'Hittade nätverk',
|
||||||
NETWORK_NO_WIFI: 'Inga WiFi-nätverk hittades',
|
NETWORK_NO_WIFI: 'Inga WiFi-nätverk hittades',
|
||||||
NETWORK_BLANK_SSID: 'lämna blankt för att inaktivera WiFi',
|
|
||||||
NETWORK_BLANK_BSSID: 'lämna blankt för att bara använda SSID',
|
NETWORK_BLANK_BSSID: 'lämna blankt för att bara använda SSID',
|
||||||
TX_POWER: 'Tx effekt',
|
TX_POWER: 'Tx effekt',
|
||||||
HOSTNAME: 'Värdnamn',
|
HOSTNAME: 'Värdnamn',
|
||||||
|
|||||||
@@ -261,7 +261,6 @@ const tr: Translation = {
|
|||||||
SCAN_AGAIN: 'Tekrar tara',
|
SCAN_AGAIN: 'Tekrar tara',
|
||||||
NETWORK_SCANNER: 'Ağ Tarayıcısı',
|
NETWORK_SCANNER: 'Ağ Tarayıcısı',
|
||||||
NETWORK_NO_WIFI: 'Hiçbir Kablosuz Ağ bulunamadı',
|
NETWORK_NO_WIFI: 'Hiçbir Kablosuz Ağ bulunamadı',
|
||||||
NETWORK_BLANK_SSID: 'Kablosuz ağı devre dışı bırakmak için boş bırakın',
|
|
||||||
NETWORK_BLANK_BSSID: 'sadece SSID kullanmak için boş bırakın',
|
NETWORK_BLANK_BSSID: 'sadece SSID kullanmak için boş bırakın',
|
||||||
TX_POWER: 'Aktarım gücü',
|
TX_POWER: 'Aktarım gücü',
|
||||||
HOSTNAME: 'Ana Makine Adı',
|
HOSTNAME: 'Ana Makine Adı',
|
||||||
|
|||||||
@@ -72,12 +72,6 @@ StateUpdateResult NetworkSettings::update(JsonObject root, NetworkSettings & set
|
|||||||
settings.staticIPConfig = false;
|
settings.staticIPConfig = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// see if we need to inform the user of a restart
|
// always best to do a restart after changing network settings
|
||||||
// if tx power, enableCORS, CORSOrigin, ssid changes, we need to restart
|
return StateUpdateResult::CHANGED_RESTART;
|
||||||
if (tx_power != settings.tx_power || enableCORS != settings.enableCORS || CORSOrigin != settings.CORSOrigin
|
|
||||||
|| (ssid != settings.ssid && settings.ssid.isEmpty())) {
|
|
||||||
return StateUpdateResult::CHANGED_RESTART; // tell WebUI that a restart is needed
|
|
||||||
}
|
|
||||||
|
|
||||||
return StateUpdateResult::CHANGED;
|
|
||||||
}
|
}
|
||||||
@@ -51,7 +51,7 @@ void NetworkStatus::networkStatus(AsyncWebServerRequest * request) {
|
|||||||
root["ssid"] = WiFi.SSID();
|
root["ssid"] = WiFi.SSID();
|
||||||
root["bssid"] = WiFi.BSSIDstr();
|
root["bssid"] = WiFi.BSSIDstr();
|
||||||
root["channel"] = WiFi.channel();
|
root["channel"] = WiFi.channel();
|
||||||
root["reconnect_count"] = emsesp::EMSESP::network_.getWifiReconnects();
|
root["reconnect_count"] = emsesp::EMSESP::network_.getNetworkReconnects();
|
||||||
root["subnet_mask"] = WiFi.subnetMask().toString();
|
root["subnet_mask"] = WiFi.subnetMask().toString();
|
||||||
|
|
||||||
if (WiFi.gatewayIP() != INADDR_NONE) {
|
if (WiFi.gatewayIP() != INADDR_NONE) {
|
||||||
|
|||||||
@@ -20,14 +20,21 @@
|
|||||||
|
|
||||||
#include "emsesp.h"
|
#include "emsesp.h"
|
||||||
|
|
||||||
|
#ifndef NETWORK_FALLBACK_AP_SSID
|
||||||
|
#define NETWORK_FALLBACK_AP_SSID "ems-esp"
|
||||||
|
#endif
|
||||||
|
#ifndef NETWORK_FALLBACK_AP_PASSWORD
|
||||||
|
#define NETWORK_FALLBACK_AP_PASSWORD "ems-esp-neo"
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace emsesp {
|
namespace emsesp {
|
||||||
|
|
||||||
uuid::log::Logger Network::logger_{F_(network), uuid::log::Facility::KERN};
|
uuid::log::Logger Network::logger_{F_(network), uuid::log::Facility::KERN};
|
||||||
|
|
||||||
void Network::begin() {
|
void Network::begin() {
|
||||||
#ifndef EMSESP_STANDALONE
|
#ifndef EMSESP_STANDALONE
|
||||||
// pull Network settings and store locally on stack
|
// pull all settings and store locally
|
||||||
EMSESP::esp32React.getNetworkSettingsService()->read([&](auto & settings) {
|
EMSESP::esp32React.getNetworkSettingsService()->read([&](NetworkSettings & settings) {
|
||||||
enableMDNS_ = settings.enableMDNS;
|
enableMDNS_ = settings.enableMDNS;
|
||||||
staticIPConfig_ = settings.staticIPConfig;
|
staticIPConfig_ = settings.staticIPConfig;
|
||||||
localIP_ = settings.localIP;
|
localIP_ = settings.localIP;
|
||||||
@@ -65,37 +72,81 @@ void Network::begin() {
|
|||||||
ap_subnetMask_ = settings.subnetMask;
|
ap_subnetMask_ = settings.subnetMask;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Initialise WiFi - we only do this once, when the network service is started.
|
// set before begin() so the event handlers can race-clear it safely
|
||||||
// We want the device to come up in opmode=0 (WIFI_OFF), which is not the default after a flash erase.
|
wifi_connect_pending_ = false;
|
||||||
// Persistence is true by default, so this WiFi.mode() call writes opmode=0 to NVS for future boots.
|
ethernet_connect_pending_ = false;
|
||||||
WiFi.mode(WIFI_OFF);
|
|
||||||
|
|
||||||
// From here on, mode changes stay in RAM only and don't touch NVS
|
phase_ = initialPhase();
|
||||||
|
|
||||||
|
// Initialise WiFi once when the Network service starts
|
||||||
WiFi.persistent(false);
|
WiFi.persistent(false);
|
||||||
WiFi.setAutoReconnect(false);
|
WiFi.setAutoReconnect(false);
|
||||||
WiFi.mode(WIFI_STA);
|
WiFi.mode(WIFI_STA);
|
||||||
|
WiFi.disconnect(true); // wipe old settings in NVS
|
||||||
|
WiFi.setHostname(hostname_.c_str()); // updates shared default_hostname buffer
|
||||||
|
WiFi.enableSTA(true); // creates the STA netif
|
||||||
|
WiFi.STA.setHostname(hostname_.c_str()); // pushes to esp_netif_set_hostname
|
||||||
|
WiFi.enableIPv6(true);
|
||||||
|
if (staticIPConfig_) {
|
||||||
|
WiFi.config(localIP_, gatewayIP_, subnetMask_, dnsIP1_, dnsIP2_); // configure for static IP
|
||||||
|
}
|
||||||
|
|
||||||
|
// www.esp32.com/viewtopic.php?t=12055
|
||||||
|
if (bandwidth20_) {
|
||||||
|
esp_wifi_set_bandwidth(static_cast<wifi_interface_t>(ESP_IF_WIFI_STA), WIFI_BW_HT20);
|
||||||
|
} else {
|
||||||
|
esp_wifi_set_bandwidth(static_cast<wifi_interface_t>(ESP_IF_WIFI_STA), WIFI_BW_HT40);
|
||||||
|
}
|
||||||
|
if (nosleep_) {
|
||||||
|
WiFi.setSleep(false); // turn off sleep - WIFI_PS_NONE
|
||||||
|
}
|
||||||
|
|
||||||
// scan settings give connect issues since arduino 2.0.14 and arduino 3.x.x with some wifi systems
|
// scan settings give connect issues since arduino 2.0.14 and arduino 3.x.x with some wifi systems
|
||||||
// WiFi.setScanMethod(WIFI_ALL_CHANNEL_SCAN); // default is FAST_SCAN
|
// WiFi.setScanMethod(WIFI_ALL_CHANNEL_SCAN); // default is FAST_SCAN
|
||||||
// WiFi.setSortMethod(WIFI_CONNECT_AP_BY_SIGNAL); // is default, no need to set
|
// WiFi.setSortMethod(WIFI_CONNECT_AP_BY_SIGNAL); // is default, no need to set
|
||||||
|
|
||||||
// capture the WIFI_REASON_* code on every STA disconnect event so check_connection() can
|
// avoid duplicate registration, so register the lambdas only once across the lifetime of this Network instance
|
||||||
// log a meaningful reason when its periodic poll notices we're no longer associated.
|
if (!wifi_events_registered_) {
|
||||||
// Also release the connect-pending guard so the next loop tick can issue a fresh WiFi.begin()
|
wifi_events_registered_ = true;
|
||||||
WiFi.onEvent(
|
|
||||||
[this](WiFiEvent_t /*event*/, WiFiEventInfo_t info) {
|
// Defer Tx power setting until STA is actually started
|
||||||
last_disconnect_reason_ = info.wifi_sta_disconnected.reason;
|
WiFi.onEvent(
|
||||||
wifi_connect_pending_ = false;
|
[this](WiFiEvent_t /*event*/, WiFiEventInfo_t /*info*/) {
|
||||||
},
|
#ifdef BOARD_C3_MINI_V1
|
||||||
ARDUINO_EVENT_WIFI_STA_DISCONNECTED);
|
// always hardcode Tx power for Wemos C3 Mini v1
|
||||||
|
// v1 needs this value, see https://github.com/emsesp/EMS-ESP32/pull/620#discussion_r993173979
|
||||||
|
// https://www.wemos.cc/en/latest/c3/c3_mini_1_0_0.html#about-wifi
|
||||||
|
WiFi.setTxPower(WIFI_POWER_8_5dBm);
|
||||||
|
#else
|
||||||
|
setWiFiPower(tx_power_);
|
||||||
|
#endif
|
||||||
|
},
|
||||||
|
ARDUINO_EVENT_WIFI_STA_START);
|
||||||
|
|
||||||
|
// capture the WIFI_REASON_* code on every STA disconnect event so check_connection() can
|
||||||
|
// log a meaningful reason when its periodic poll notices we're no longer associated.
|
||||||
|
WiFi.onEvent(
|
||||||
|
[this](WiFiEvent_t /*event*/, WiFiEventInfo_t info) {
|
||||||
|
last_disconnect_reason_ = info.wifi_sta_disconnected.reason;
|
||||||
|
wifi_connect_pending_ = false;
|
||||||
|
if (wifi_ever_connected_) {
|
||||||
|
LOG_WARNING("WiFi disconnected (reason: %s)", disconnectReason(last_disconnect_reason_));
|
||||||
|
} else {
|
||||||
|
LOG_WARNING("WiFi initial connect attempt failed (reason: %s)", disconnectReason(last_disconnect_reason_));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ARDUINO_EVENT_WIFI_STA_DISCONNECTED);
|
||||||
|
|
||||||
|
// clear the saved reason and the connect-pending guard on a fresh STA association,
|
||||||
|
WiFi.onEvent(
|
||||||
|
[this](WiFiEvent_t /*event*/, WiFiEventInfo_t /*info*/) {
|
||||||
|
last_disconnect_reason_ = 0;
|
||||||
|
wifi_connect_pending_ = false;
|
||||||
|
wifi_ever_connected_ = true;
|
||||||
|
},
|
||||||
|
ARDUINO_EVENT_WIFI_STA_GOT_IP);
|
||||||
|
}
|
||||||
|
|
||||||
// clear the saved reason and the connect-pending guard on a fresh STA association
|
|
||||||
WiFi.onEvent(
|
|
||||||
[this](WiFiEvent_t /*event*/, WiFiEventInfo_t /*info*/) {
|
|
||||||
last_disconnect_reason_ = 0;
|
|
||||||
wifi_connect_pending_ = false;
|
|
||||||
},
|
|
||||||
ARDUINO_EVENT_WIFI_STA_GOT_IP);
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -147,7 +198,7 @@ std::string Network::getMacAddress() const {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the number of stations connected to the AP
|
// get the number of sessions connected to the AP
|
||||||
uint8_t Network::getStationNum() const {
|
uint8_t Network::getStationNum() const {
|
||||||
#ifndef EMSESP_STANDALONE
|
#ifndef EMSESP_STANDALONE
|
||||||
return network_iface_ == NetIface::AP ? WiFi.softAPgetStationNum() : 0;
|
return network_iface_ == NetIface::AP ? WiFi.softAPgetStationNum() : 0;
|
||||||
@@ -159,12 +210,13 @@ uint8_t Network::getStationNum() const {
|
|||||||
// disconnect all WiFi, Eth and AP
|
// disconnect all WiFi, Eth and AP
|
||||||
// so we can starts searching again to reconnect
|
// so we can starts searching again to reconnect
|
||||||
void Network::reconnect() {
|
void Network::reconnect() {
|
||||||
LOG_DEBUG("Disconnecting all networks");
|
LOG_DEBUG("Reconnecting all networks");
|
||||||
|
|
||||||
#ifndef EMSESP_STANDALONE
|
#ifndef EMSESP_STANDALONE
|
||||||
// disconnect WiFi
|
// disconnect WiFi
|
||||||
if (wifi_connected()) {
|
if (wifi_connected()) {
|
||||||
WiFi.disconnect(true);
|
WiFi.disconnect(true, true);
|
||||||
|
WiFi.mode(WIFI_STA); // reset mode
|
||||||
}
|
}
|
||||||
|
|
||||||
// disconnect AP
|
// disconnect AP
|
||||||
@@ -174,17 +226,33 @@ void Network::reconnect() {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// reset network state
|
// reset network state
|
||||||
network_ip_ = 0;
|
network_ip_ = 0;
|
||||||
network_iface_ = NetIface::NONE;
|
network_iface_ = NetIface::NONE;
|
||||||
has_ipv6_ = false;
|
has_ipv6_ = false;
|
||||||
juststopped_ = true;
|
juststopped_ = false;
|
||||||
wifi_connect_pending_ = false;
|
wifi_connect_pending_ = false;
|
||||||
last_disconnect_reason_ = 0;
|
ethernet_connect_pending_ = false;
|
||||||
|
last_disconnect_reason_ = 0;
|
||||||
|
connect_retry_ = 0;
|
||||||
|
reconnect_count_ = 0;
|
||||||
|
phase_ = NetPhase::ETHERNET; // begin() will refine this once settings are reloaded
|
||||||
|
|
||||||
// reload the network settings, as this could be called from the console
|
// reload the network settings and apply them
|
||||||
begin();
|
begin();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// pick the first phase that has the hardware/config to even be attempted on this device.
|
||||||
|
// boards without an Ethernet PHY skip straight to WIFI; without a configured SSID, straight to AP
|
||||||
|
NetPhase Network::initialPhase() const {
|
||||||
|
if (phy_type_ != PHY_type::PHY_TYPE_NONE) {
|
||||||
|
return NetPhase::ETHERNET;
|
||||||
|
}
|
||||||
|
if (!ssid_.isEmpty()) {
|
||||||
|
return NetPhase::WIFI;
|
||||||
|
}
|
||||||
|
return NetPhase::AP;
|
||||||
|
}
|
||||||
|
|
||||||
// network loop, looking for new and disconnecting networks
|
// network loop, looking for new and disconnecting networks
|
||||||
void Network::loop() {
|
void Network::loop() {
|
||||||
#ifndef EMSESP_STANDALONE
|
#ifndef EMSESP_STANDALONE
|
||||||
@@ -195,17 +263,17 @@ void Network::loop() {
|
|||||||
if (!lastConnectionAttempt_ || static_cast<uint32_t>(currentMillis - lastConnectionAttempt_) >= reconnectDelay) {
|
if (!lastConnectionAttempt_ || static_cast<uint32_t>(currentMillis - lastConnectionAttempt_) >= reconnectDelay) {
|
||||||
lastConnectionAttempt_ = currentMillis;
|
lastConnectionAttempt_ = currentMillis;
|
||||||
|
|
||||||
// manage network interfaces
|
// manage the network interfaces: Ethernet, WiFi and AP in that order
|
||||||
startAP(); // Captive Portal (AP)
|
|
||||||
startWIFI(); // WiFi
|
|
||||||
startEthernet(); // Ethernet
|
startEthernet(); // Ethernet
|
||||||
|
startWIFI(); // WiFi
|
||||||
|
startAP(); // Captive Portal (AP)
|
||||||
|
|
||||||
// already have a connection: verify it's still alive
|
// already have a connection: verify it's still alive
|
||||||
// or trigger if the WiFi handshaked failed on the WiFi.begin() call
|
|
||||||
if (network_ip_ != 0 || last_disconnect_reason_ != 0) {
|
if (network_ip_ != 0 || last_disconnect_reason_ != 0) {
|
||||||
checkConnection();
|
checkConnection();
|
||||||
}
|
}
|
||||||
findNetworks(); // detect new connections
|
|
||||||
|
findNetworks(); // detect any new network connections
|
||||||
}
|
}
|
||||||
|
|
||||||
// process DNS requests for the captive portal while the soft-AP is up
|
// process DNS requests for the captive portal while the soft-AP is up
|
||||||
@@ -216,9 +284,12 @@ void Network::loop() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Re-validate the currently active connection
|
// Re-validate the currently active connection
|
||||||
// if a netif is no longer up or has lost its IP (cable unplugged, AP gone, DHCP lease lost, ...) we drop our state so
|
// if a netif is no longer up or has lost its IP (cable unplugged, AP gone, DHCP lease lost, ...) we drop our state so find_networks() can pick up a new one
|
||||||
// find_networks() can pick up a new one
|
|
||||||
void Network::checkConnection() {
|
void Network::checkConnection() {
|
||||||
|
if (network_iface_ == NetIface::NONE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
#ifndef EMSESP_STANDALONE
|
#ifndef EMSESP_STANDALONE
|
||||||
bool still_up = false;
|
bool still_up = false;
|
||||||
for (esp_netif_t * netif = esp_netif_next_unsafe(NULL); netif != NULL; netif = esp_netif_next_unsafe(netif)) {
|
for (esp_netif_t * netif = esp_netif_next_unsafe(NULL); netif != NULL; netif = esp_netif_next_unsafe(netif)) {
|
||||||
@@ -239,10 +310,17 @@ void Network::checkConnection() {
|
|||||||
reason = WIFI_REASON_UNSPECIFIED; // event hasn't fired yet (or was cleared); avoid logging "0"
|
reason = WIFI_REASON_UNSPECIFIED; // event hasn't fired yet (or was cleared); avoid logging "0"
|
||||||
}
|
}
|
||||||
LOG_INFO("WiFi connection lost (reason %u: %s)", reason, disconnectReason(reason));
|
LOG_INFO("WiFi connection lost (reason %u: %s)", reason, disconnectReason(reason));
|
||||||
} else {
|
wifi_connect_pending_ = false;
|
||||||
|
} else if (network_iface_ == NetIface::ETHERNET) {
|
||||||
LOG_INFO("Ethernet connection lost");
|
LOG_INFO("Ethernet connection lost");
|
||||||
|
ethernet_connect_pending_ = false;
|
||||||
}
|
}
|
||||||
reconnect();
|
juststopped_ = true;
|
||||||
|
network_iface_ = NetIface::NONE;
|
||||||
|
network_ip_ = 0;
|
||||||
|
has_ipv6_ = false;
|
||||||
|
connect_retry_ = 0;
|
||||||
|
phase_ = initialPhase();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@@ -250,12 +328,12 @@ void Network::checkConnection() {
|
|||||||
// set the WiFi TxPower based on the RSSI (signal strength), picking the lowest value
|
// set the WiFi TxPower based on the RSSI (signal strength), picking the lowest value
|
||||||
// code is based of RSSI (signal strength) and copied from Tasmota's WiFiSetTXpowerBasedOnRssi() which is copied ESPEasy's ESPEasyWifi.SetWiFiTXpower() function
|
// code is based of RSSI (signal strength) and copied from Tasmota's WiFiSetTXpowerBasedOnRssi() which is copied ESPEasy's ESPEasyWifi.SetWiFiTXpower() function
|
||||||
// Range ESP32 : 2dBm - 20dBm
|
// Range ESP32 : 2dBm - 20dBm
|
||||||
// 802.11b - wifi1
|
// 802.11b - wifi1
|
||||||
// 802.11a - wifi2
|
// 802.11a - wifi2
|
||||||
// 802.11g - wifi3
|
// 802.11g - wifi3
|
||||||
// 802.11n - wifi4
|
// 802.11n - wifi4
|
||||||
// 802.11ac - wifi5
|
// 802.11ac - wifi5
|
||||||
// 802.11ax - wifi6
|
// 802.11ax - wifi6
|
||||||
// tx_power is the Tx power to set, 0 for auto
|
// tx_power is the Tx power to set, 0 for auto
|
||||||
void Network::setWiFiPower(uint8_t tx_power) {
|
void Network::setWiFiPower(uint8_t tx_power) {
|
||||||
#ifndef EMSESP_STANDALONE
|
#ifndef EMSESP_STANDALONE
|
||||||
@@ -269,7 +347,7 @@ void Network::setWiFiPower(uint8_t tx_power) {
|
|||||||
int max_tx_pwr = MAX_TX_PWR_DBM_n; // assume wifi4
|
int max_tx_pwr = MAX_TX_PWR_DBM_n; // assume wifi4
|
||||||
int threshold = WIFI_SENSITIVITY_n + 120; // Margin in dBm * 10 on top of threshold
|
int threshold = WIFI_SENSITIVITY_n + 120; // Margin in dBm * 10 on top of threshold
|
||||||
|
|
||||||
// Assume AP sends with max set by ETSI standard.
|
// Assume AP sends with max set by ETSI standard
|
||||||
// 2.4 GHz: 100 mWatt (20 dBm)
|
// 2.4 GHz: 100 mWatt (20 dBm)
|
||||||
// US and some other countries allow 1000 mW (30 dBm)
|
// US and some other countries allow 1000 mW (30 dBm)
|
||||||
int rssi = WiFi.RSSI() * 10;
|
int rssi = WiFi.RSSI() * 10;
|
||||||
@@ -309,7 +387,7 @@ void Network::setWiFiPower(uint8_t tx_power) {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (!WiFi.setTxPower(p)) {
|
if (!WiFi.setTxPower(p)) {
|
||||||
emsesp::EMSESP::logger().warning("Failed to set WiFi Tx Power");
|
LOG_DEBUG("Failed to set WiFi Tx Power");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@@ -419,88 +497,59 @@ const char * Network::disconnectReason(uint8_t code) {
|
|||||||
// WiFi management
|
// WiFi management
|
||||||
void Network::startWIFI() {
|
void Network::startWIFI() {
|
||||||
#ifndef EMSESP_STANDALONE
|
#ifndef EMSESP_STANDALONE
|
||||||
// Abort if already connected, or if we have no SSID or another Wifi.begin() is already in progress
|
// only run during the WIFI phase; ETHERNET phase keeps WiFi quiet, AP phase has its own bring-up
|
||||||
if (WiFi.isConnected() || ssid_.length() == 0 || wifi_connect_pending_) {
|
if (phase_ != NetPhase::WIFI) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
WiFi.setHostname(hostname_.c_str()); // updates shared default_hostname buffer
|
// exit if WiFi or Ethernet is already connected, or if we have no SSID or another Wifi.begin() is already in progress
|
||||||
WiFi.enableSTA(true); // creates the STA netif
|
if (WiFi.isConnected() || ssid_.isEmpty() || ethernet_connected() || wifi_connect_pending_) {
|
||||||
WiFi.STA.setHostname(hostname_.c_str()); // pushes to esp_netif_set_hostname
|
return;
|
||||||
WiFi.enableIPv6(true);
|
|
||||||
if (staticIPConfig_) {
|
|
||||||
WiFi.config(localIP_, gatewayIP_, subnetMask_, dnsIP1_, dnsIP2_); // configure for static IP
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// www.esp32.com/viewtopic.php?t=12055
|
wifi_connect_pending_ = true;
|
||||||
if (bandwidth20_) {
|
|
||||||
esp_wifi_set_bandwidth(static_cast<wifi_interface_t>(ESP_IF_WIFI_STA), WIFI_BW_HT20);
|
|
||||||
} else {
|
|
||||||
esp_wifi_set_bandwidth(static_cast<wifi_interface_t>(ESP_IF_WIFI_STA), WIFI_BW_HT40);
|
|
||||||
}
|
|
||||||
if (nosleep_) {
|
|
||||||
WiFi.setSleep(false); // turn off sleep - WIFI_PS_NONE
|
|
||||||
}
|
|
||||||
|
|
||||||
// attempt to connect to the network
|
LOG_DEBUG("WiFi connection with %s and %s", ssid_.c_str(), password_.c_str());
|
||||||
uint8_t bssid[6];
|
|
||||||
wl_status_t status;
|
|
||||||
wifi_connect_pending_ = true; // set before begin() so the event handlers can race-clear it safely
|
|
||||||
|
|
||||||
|
// attempt to connect to the wifi network
|
||||||
|
// the event handlers handle error handling and retries
|
||||||
|
uint8_t bssid[6];
|
||||||
if (formatBSSID(bssid_, bssid)) {
|
if (formatBSSID(bssid_, bssid)) {
|
||||||
status = WiFi.begin(ssid_.c_str(), password_.c_str(), 0, bssid);
|
WiFi.begin(ssid_.c_str(), password_.c_str(), 0, bssid);
|
||||||
} else {
|
} else {
|
||||||
status = WiFi.begin(ssid_.c_str(), password_.c_str());
|
WiFi.begin(ssid_.c_str(), password_.c_str());
|
||||||
}
|
}
|
||||||
if (status == WL_CONNECT_FAILED) {
|
|
||||||
wifi_connect_pending_ = false; // begin() didn't actually start anything, allow next tick to retry
|
|
||||||
LOG_ERROR("WiFi connection failed (code %d)", status);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef BOARD_C3_MINI_V1
|
|
||||||
// always hardcode Tx power for Wemos CS Mini v1
|
|
||||||
// v1 needs this value, see https://github.com/emsesp/EMS-ESP32/pull/620#discussion_r993173979
|
|
||||||
// https://www.wemos.cc/en/latest/c3/c3_mini_1_0_0.html#about-wifi
|
|
||||||
WiFi.setTxPower(WIFI_POWER_8_5dBm);
|
|
||||||
#else
|
|
||||||
setWiFiPower(tx_power_);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ethernet management
|
// Ethernet management
|
||||||
// Brings up the ETH driver / netif exactly once. After ETH.begin() returns true the driver
|
// Brings up the ETH driver / netif exactly once. After ETH.begin() returns true the driver
|
||||||
// continues to run autonomously (link negotiation, DHCP, etc); the loop must NOT call ETH.begin()
|
// continues to run autonomously (link negotiation, DHCP, etc)
|
||||||
// again on every iteration because that thrashes the netif and can prevent DHCP from completing
|
|
||||||
void Network::startEthernet() {
|
void Network::startEthernet() {
|
||||||
#if CONFIG_IDF_TARGET_ESP32
|
#if CONFIG_IDF_TARGET_ESP32
|
||||||
|
// only run during the ETHERNET phase; once we've given up on Ethernet, the driver is left
|
||||||
|
// running in case the link comes up later, but we no longer try to (re)start it here
|
||||||
|
if (phase_ != NetPhase::ETHERNET) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// already up and running, nothing to do
|
// already up and running, nothing to do
|
||||||
if (eth_started_) {
|
if (ethernet_connect_pending_ || ethernet_connected()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef EMSESP_STANDALONE
|
#ifndef EMSESP_STANDALONE
|
||||||
|
|
||||||
// no ethernet present or wifi takes precedence
|
// no ethernet present
|
||||||
if (phy_type_ == PHY_type::PHY_TYPE_NONE || (ssid_.length() > 0)) {
|
if (phy_type_ == PHY_type::PHY_TYPE_NONE) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// configure Ethernet
|
// disabled Ethernet for boards with only 4MB flash and no PSRAM
|
||||||
int mdc = 23; // Pin# of the I²C clock signal for the Ethernet PHY - hardcoded
|
if (ESP.getFlashChipSize() < 4194304 && !ESP.getPsramSize()) { // 4MB
|
||||||
int mdio = 18; // Pin# of the I²C IO signal for the Ethernet PHY - hardcoded
|
LOG_NOTICE("Ethernet disabled for boards with only 4MB flash");
|
||||||
uint8_t phy_addr = eth_phy_addr_; // I²C-address of Ethernet PHY (0 or 1 for LAN8720, 31 for TLK110)
|
return;
|
||||||
int8_t power = eth_power_; // Pin# of the enable signal for the external crystal oscillator (-1 to disable for internal APLL source)
|
}
|
||||||
eth_phy_type_t type = (phy_type_ == PHY_type::PHY_TYPE_LAN8720) ? ETH_PHY_LAN8720
|
|
||||||
: (phy_type_ == PHY_type::PHY_TYPE_TLK110) ? ETH_PHY_TLK110
|
|
||||||
: ETH_PHY_RTL8201; // Type of the Ethernet PHY (LAN8720 or TLK110)
|
|
||||||
// clock mode:
|
|
||||||
// ETH_CLOCK_GPIO0_IN = 0 RMII clock input to GPIO0
|
|
||||||
// ETH_CLOCK_GPIO0_OUT = 1 RMII clock output from GPIO0
|
|
||||||
// ETH_CLOCK_GPIO16_OUT = 2 RMII clock output from GPIO16
|
|
||||||
// ETH_CLOCK_GPIO17_OUT = 3 RMII clock output from GPIO17, for 50hz inverted clock
|
|
||||||
auto clock_mode = (eth_clock_mode_t)eth_clock_mode_;
|
|
||||||
|
|
||||||
// reset power and add a delay as ETH doesn't not always start up correctly after a warm boot
|
// reset power and add a delay as ETH doesn't not always start up correctly after a warm boot
|
||||||
if (eth_power_ != -1) {
|
if (eth_power_ != -1) {
|
||||||
@@ -510,15 +559,29 @@ void Network::startEthernet() {
|
|||||||
digitalWrite(eth_power_, HIGH);
|
digitalWrite(eth_power_, HIGH);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ETH.begin(type, phy_addr, mdc, mdio, power, clock_mode)) {
|
// mdc = 23 = Pin# of the I²C clock signal for the Ethernet PHY
|
||||||
eth_started_ = true; // mark up; do not re-enter this block until reboot / explicit teardown
|
// mdio = 18 = Pin# of the I²C IO signal for the Ethernet PHY
|
||||||
|
// phy_addr = I²C-address of Ethernet PHY (0 or 1 for LAN8720, 31 for TLK110)
|
||||||
|
// power = Pin# of the enable signal for the external crystal oscillator (-1 to disable for internal APLL source)
|
||||||
|
// type = Type of the Ethernet PHY (LAN8720 or TLK110)
|
||||||
|
// clock_mode =
|
||||||
|
// ETH_CLOCK_GPIO0_IN = 0 RMII clock input to GPIO0
|
||||||
|
// ETH_CLOCK_GPIO0_OUT = 1 RMII clock output from GPIO0
|
||||||
|
// ETH_CLOCK_GPIO16_OUT = 2 RMII clock output from GPIO16
|
||||||
|
// ETH_CLOCK_GPIO17_OUT = 3 RMII clock output from GPIO17, for 50hz inverted clock
|
||||||
|
eth_phy_type_t type = (phy_type_ == PHY_type::PHY_TYPE_LAN8720) ? ETH_PHY_LAN8720
|
||||||
|
: (phy_type_ == PHY_type::PHY_TYPE_TLK110) ? ETH_PHY_TLK110
|
||||||
|
: ETH_PHY_RTL8201; // Type of the Ethernet PHY (LAN8720 or TLK110)
|
||||||
|
if (ETH.begin(type, eth_phy_addr_, 23, 18, eth_power_, (eth_clock_mode_t)eth_clock_mode_)) {
|
||||||
|
LOG_DEBUG("Ethernet module found - starting");
|
||||||
|
ethernet_connect_pending_ = true;
|
||||||
ETH.setHostname(hostname_.c_str()); // Push hostname to the ETH netif immediately after it's created
|
ETH.setHostname(hostname_.c_str()); // Push hostname to the ETH netif immediately after it's created
|
||||||
ETH.enableIPv6(true);
|
ETH.enableIPv6(true);
|
||||||
if (staticIPConfig_) {
|
if (staticIPConfig_) {
|
||||||
ETH.config(localIP_, gatewayIP_, subnetMask_, dnsIP1_, dnsIP2_);
|
ETH.config(localIP_, gatewayIP_, subnetMask_, dnsIP1_, dnsIP2_);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
LOG_ERROR("Failed to start Ethernet");
|
LOG_ERROR("Failed to start Ethernet module");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
@@ -526,17 +589,9 @@ void Network::startEthernet() {
|
|||||||
|
|
||||||
// check if the network is connected and set network_ip_ / network_iface_ / has_ipv6_
|
// check if the network is connected and set network_ip_ / network_iface_ / has_ipv6_
|
||||||
// Iterates over every esp-netif that exists, prioritizing Ethernet > WiFi > AP
|
// Iterates over every esp-netif that exists, prioritizing Ethernet > WiFi > AP
|
||||||
bool Network::findNetworks() {
|
void Network::findNetworks() {
|
||||||
#ifndef EMSESP_STANDALONE
|
#ifndef EMSESP_STANDALONE
|
||||||
|
|
||||||
// exit if already have a connection, unless in AP mode
|
|
||||||
// when in AP mode, it will always try and connect to the WiFi
|
|
||||||
if (network_ip_ != 0 && !(WiFi.getMode() & WIFI_AP)) {
|
|
||||||
// const esp_ip4_addr_t ip4 = {.addr = network_ip_};
|
|
||||||
// LOG_DEBUG("Network already connected via IPv4: " IPSTR, IP2STR(&ip4));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct NetInfo {
|
struct NetInfo {
|
||||||
esp_ip4_addr_t ip;
|
esp_ip4_addr_t ip;
|
||||||
esp_ip6_addr_t ip6;
|
esp_ip6_addr_t ip6;
|
||||||
@@ -580,23 +635,31 @@ bool Network::findNetworks() {
|
|||||||
strlcpy(info.desc, desc, sizeof(info.desc));
|
strlcpy(info.desc, desc, sizeof(info.desc));
|
||||||
}
|
}
|
||||||
info.has_ipv6 = (esp_netif_get_ip6_linklocal(netif, &info.ip6) == ESP_OK);
|
info.has_ipv6 = (esp_netif_get_ip6_linklocal(netif, &info.ip6) == ESP_OK);
|
||||||
best_iface = candidate;
|
|
||||||
|
|
||||||
|
best_iface = candidate;
|
||||||
if (best_iface == NetIface::ETHERNET) {
|
if (best_iface == NetIface::ETHERNET) {
|
||||||
break; // top priority, can't be beaten by anything later in the list
|
break; // top priority, can't be beaten by anything later in the list
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto previous_iface = NetIface::NONE;
|
|
||||||
|
|
||||||
// if we have a connection and it's a new one, set it up
|
// if we have a connection and it's a new one, set it up
|
||||||
if (best_iface != NetIface::NONE && best_iface != previous_iface) {
|
if (best_iface != NetIface::NONE && best_iface != network_iface_) {
|
||||||
previous_iface = network_iface_; // save the previous interface for comparison next time
|
|
||||||
network_ip_ = info.ip.addr;
|
network_ip_ = info.ip.addr;
|
||||||
network_iface_ = iface_from_desc(info.desc); // "sta"/"ap"/"eth*"
|
network_iface_ = iface_from_desc(info.desc); // "sta"/"ap"/"eth*"
|
||||||
has_ipv6_ = info.has_ipv6;
|
has_ipv6_ = info.has_ipv6;
|
||||||
connect_retry_ = 0;
|
connect_retry_ = 0;
|
||||||
|
|
||||||
|
// sync the phase to the interface that actually came up. ETH can come up late
|
||||||
|
// (e.g. cable plugged in after we'd already moved on to WiFi/AP) and we want the
|
||||||
|
// next disconnect-driven retry cycle to start from the right place.
|
||||||
|
if (network_iface_ == NetIface::ETHERNET) {
|
||||||
|
phase_ = NetPhase::ETHERNET;
|
||||||
|
} else if (network_iface_ == NetIface::WIFI) {
|
||||||
|
phase_ = NetPhase::WIFI;
|
||||||
|
} else if (network_iface_ == NetIface::AP) {
|
||||||
|
phase_ = NetPhase::AP;
|
||||||
|
}
|
||||||
|
|
||||||
LOG_INFO("Network connected via %s (IP: " IPSTR ")",
|
LOG_INFO("Network connected via %s (IP: " IPSTR ")",
|
||||||
network_iface_ == NetIface::ETHERNET ? "Ethernet"
|
network_iface_ == NetIface::ETHERNET ? "Ethernet"
|
||||||
: network_iface_ == NetIface::WIFI ? "WiFi"
|
: network_iface_ == NetIface::WIFI ? "WiFi"
|
||||||
@@ -612,8 +675,7 @@ bool Network::findNetworks() {
|
|||||||
// count the number of restarts (for Wifi and Eth)
|
// count the number of restarts (for Wifi and Eth)
|
||||||
if (juststopped_) {
|
if (juststopped_) {
|
||||||
juststopped_ = false;
|
juststopped_ = false;
|
||||||
connectcount_++;
|
reconnect_count_++;
|
||||||
LOG_DEBUG("Network re-connection count %d", connectcount_);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// start mDNS for any real network interface (skip the SoftAP since the captive portal handles its own DNS)
|
// start mDNS for any real network interface (skip the SoftAP since the captive portal handles its own DNS)
|
||||||
@@ -624,44 +686,94 @@ bool Network::findNetworks() {
|
|||||||
// fetch the versions.json file from emsesp.org
|
// fetch the versions.json file from emsesp.org
|
||||||
EMSESP::webStatusService.schedule_versions_refresh();
|
EMSESP::webStatusService.schedule_versions_refresh();
|
||||||
|
|
||||||
return true; // we have a network connection
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// fallback, reset network state if nothing found
|
||||||
|
if (best_iface == NetIface::NONE) {
|
||||||
|
network_ip_ = 0;
|
||||||
|
network_iface_ = NetIface::NONE;
|
||||||
|
has_ipv6_ = false;
|
||||||
|
connect_retry_++;
|
||||||
|
LOG_DEBUG("Looking for network interfaces (retry #%d, phase=%s)",
|
||||||
|
connect_retry_,
|
||||||
|
phase_ == NetPhase::ETHERNET ? "Ethernet"
|
||||||
|
: phase_ == NetPhase::WIFI ? "WiFi"
|
||||||
|
: "AP");
|
||||||
|
|
||||||
|
// give up on this interface and try the next one. ETHERNET -> WIFI (or straight to AP if no SSID configured); WIFI -> AP.
|
||||||
|
if (connect_retry_ >= MAX_NETWORK_RECONNECTION_ATTEMPTS && phase_ != NetPhase::AP) {
|
||||||
|
if (phase_ == NetPhase::ETHERNET) {
|
||||||
|
if (ssid_.isEmpty()) {
|
||||||
|
LOG_WARNING("Ethernet failed to connect after %u attempts, falling back to AP", connect_retry_);
|
||||||
|
phase_ = NetPhase::AP;
|
||||||
|
} else {
|
||||||
|
LOG_WARNING("Ethernet failed to connect after %u attempts, switching to WiFi", connect_retry_);
|
||||||
|
phase_ = NetPhase::WIFI;
|
||||||
|
}
|
||||||
|
ethernet_connect_pending_ = false;
|
||||||
|
} else if (phase_ == NetPhase::WIFI) {
|
||||||
|
LOG_WARNING("WiFi failed to connect after %u attempts, falling back to AP", connect_retry_);
|
||||||
|
phase_ = NetPhase::AP;
|
||||||
|
wifi_connect_pending_ = false;
|
||||||
|
WiFi.disconnect(true);
|
||||||
|
}
|
||||||
|
connect_retry_ = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// fallback
|
|
||||||
network_ip_ = 0;
|
|
||||||
network_iface_ = NetIface::NONE;
|
|
||||||
has_ipv6_ = false;
|
|
||||||
connect_retry_++;
|
|
||||||
LOG_DEBUG("No active network interfaces found yet, re-connection count %d", connect_retry_);
|
|
||||||
#endif
|
#endif
|
||||||
return false; // no connection found yet
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// access point (soft-AP) and the captive portal
|
// access point (soft-AP) and the captive portal
|
||||||
void Network::startAP() {
|
void Network::startAP() {
|
||||||
#ifndef EMSESP_STANDALONE
|
#ifndef EMSESP_STANDALONE
|
||||||
// Only start AP as a fallback if the Network has failed
|
// only start AP as a fallback once both the Ethernet and WiFi phases have given up
|
||||||
if (connect_retry_ < MAX_NETWORK_RECONNECTION_ATTEMPTS) {
|
if (phase_ != NetPhase::AP) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// don't start the soft-AP if it is disabled, or Ethernet has taken over or we have a real WiFi connection or it's already running
|
// Don't start AP if wired/Wi-Fi STA is serving the network
|
||||||
if (ap_provisionMode_ == AP_MODE_NEVER || network_connected() || WiFi.getMode() & WIFI_AP) {
|
if (ap_provisionMode_ == AP_MODE_NEVER || network_connected()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
WiFi.softAPenableIPv6(); // force IPv6, same as for STA - fixes https://github.com/emsesp/EMS-ESP32/issues/1922
|
// Captive-portal DNS is already bound to the softAP interface
|
||||||
WiFi.softAPConfig(ap_localIP_, ap_gatewayIP_, ap_subnetMask_);
|
if (ap_dnsServer_) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!WiFi.softAPConfig(ap_localIP_, ap_gatewayIP_, ap_subnetMask_)) {
|
||||||
|
LOG_DEBUG("softAPConfig failed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
esp_wifi_set_bandwidth(static_cast<wifi_interface_t>(ESP_IF_WIFI_AP), WIFI_BW_HT20);
|
esp_wifi_set_bandwidth(static_cast<wifi_interface_t>(ESP_IF_WIFI_AP), WIFI_BW_HT20);
|
||||||
WiFi.softAP(ap_ssid_.c_str(), ap_password_.c_str(), ap_channel_, ap_ssid_hidden_, ap_max_clients_);
|
|
||||||
|
// WiFi.softAPenableIPv6();
|
||||||
|
|
||||||
|
if (!WiFi.softAP(ap_ssid_.c_str(), ap_password_.c_str(), ap_channel_, ap_ssid_hidden_, ap_max_clients_)) {
|
||||||
|
LOG_ERROR("softAP failed; check SSID/password in AP settings");
|
||||||
|
WiFi.softAPdisconnect(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
#if CONFIG_IDF_TARGET_ESP32C3
|
#if CONFIG_IDF_TARGET_ESP32C3
|
||||||
WiFi.setTxPower(WIFI_POWER_8_5dBm); // https://www.wemos.cc/en/latest/c3/c3_mini_1_0_0.html#about-wifi
|
WiFi.setTxPower(WIFI_POWER_8_5dBm); // https://www.wemos.cc/en/latest/c3/c3_mini_1_0_0.html#about-wifi
|
||||||
#endif
|
#endif
|
||||||
const IPAddress apIp = WiFi.softAPIP();
|
const IPAddress apIp = WiFi.softAPIP();
|
||||||
|
if (static_cast<uint32_t>(apIp) == 0) {
|
||||||
|
LOG_DEBUG("SoftAP has no IPv4 yet; skipping captive-portal DNS for now.");
|
||||||
|
WiFi.softAPdisconnect(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
LOG_INFO("Starting Access Point with captive portal on %u.%u.%u.%u", apIp[0], apIp[1], apIp[2], apIp[3]);
|
LOG_INFO("Starting Access Point with captive portal on %u.%u.%u.%u", apIp[0], apIp[1], apIp[2], apIp[3]);
|
||||||
|
|
||||||
// start DNS server for Captive Portal
|
|
||||||
ap_dnsServer_ = new DNSServer;
|
ap_dnsServer_ = new DNSServer;
|
||||||
|
if (!ap_dnsServer_) {
|
||||||
|
LOG_DEBUG("Out of memory starting captive-portal DNSServer");
|
||||||
|
WiFi.softAPdisconnect(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
ap_dnsServer_->start(DNS_PORT, "*", apIp);
|
ap_dnsServer_->start(DNS_PORT, "*", apIp);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,12 +35,17 @@
|
|||||||
|
|
||||||
namespace emsesp {
|
namespace emsesp {
|
||||||
|
|
||||||
#define NETWORK_RECONNECTION_DELAY_SHORT 3000 // 3 seconds
|
#define NETWORK_RECONNECTION_DELAY_SHORT 5000 // 5 seconds
|
||||||
#define NETWORK_RECONNECTION_DELAY_LONG 60000 // 60 seconds
|
|
||||||
|
|
||||||
#define MAX_NETWORK_RECONNECTION_ATTEMPTS 3 // maximum number of network reconnection attempts
|
#ifndef EMSESP_DEBUG
|
||||||
|
#define NETWORK_RECONNECTION_DELAY_LONG 60000 // 1 minute
|
||||||
|
#else
|
||||||
|
#define NETWORK_RECONNECTION_DELAY_LONG 10000 // 10 seconds - for debugging
|
||||||
|
#endif
|
||||||
|
|
||||||
#define DNS_PORT 53
|
#define MAX_NETWORK_RECONNECTION_ATTEMPTS 3 // maximum number of network reconnection attempts before going to AP fallback
|
||||||
|
|
||||||
|
#define DNS_PORT 53 // dns server port for captive portal
|
||||||
|
|
||||||
// copied from Tasmota
|
// copied from Tasmota
|
||||||
#if CONFIG_IDF_TARGET_ESP32S2
|
#if CONFIG_IDF_TARGET_ESP32S2
|
||||||
@@ -75,13 +80,18 @@ namespace emsesp {
|
|||||||
|
|
||||||
// which physical interface we are currently using for the active network connection.
|
// which physical interface we are currently using for the active network connection.
|
||||||
// Mapped from the esp-netif description string returned by esp_netif_get_desc(): "sta" -> WIFI,
|
// Mapped from the esp-netif description string returned by esp_netif_get_desc(): "sta" -> WIFI,
|
||||||
// "ap" -> AP, "eth"/"eth1"/"eth2"/... (arduino-esp32 v3.x suffixes ETH netifs because it supports
|
// "ap" -> AP, "eth"/"eth1"/"eth2"/...
|
||||||
// multiple ETH instances) -> ETHERNET. Anything else stays as NONE.
|
|
||||||
enum class NetIface : uint8_t {
|
enum class NetIface : uint8_t {
|
||||||
NONE = 0,
|
NONE = 0, // 0
|
||||||
WIFI,
|
WIFI, // 1
|
||||||
ETHERNET,
|
ETHERNET, // 2
|
||||||
AP,
|
AP, // 3
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class NetPhase : uint8_t {
|
||||||
|
ETHERNET = 0,
|
||||||
|
WIFI = 1,
|
||||||
|
AP = 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
class Network {
|
class Network {
|
||||||
@@ -89,10 +99,6 @@ class Network {
|
|||||||
void begin();
|
void begin();
|
||||||
void loop();
|
void loop();
|
||||||
|
|
||||||
uint16_t getWifiReconnects() const {
|
|
||||||
return connectcount_;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t network_ip() const {
|
uint32_t network_ip() const {
|
||||||
return network_ip_;
|
return network_ip_;
|
||||||
}
|
}
|
||||||
@@ -121,8 +127,8 @@ class Network {
|
|||||||
return has_ipv6_ && (network_iface_ == NetIface::WIFI || network_iface_ == NetIface::ETHERNET);
|
return has_ipv6_ && (network_iface_ == NetIface::WIFI || network_iface_ == NetIface::ETHERNET);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t getWifiReconnects() {
|
uint16_t getNetworkReconnects() {
|
||||||
return connectcount_;
|
return reconnect_count_;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string getLocalIP() const;
|
std::string getLocalIP() const;
|
||||||
@@ -149,10 +155,25 @@ class Network {
|
|||||||
return NetIface::NONE;
|
return NetIface::NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char * network_iface_to_string(NetIface iface) {
|
||||||
|
switch (iface) {
|
||||||
|
case NetIface::WIFI:
|
||||||
|
return "WiFi";
|
||||||
|
case NetIface::ETHERNET:
|
||||||
|
return "Ethernet";
|
||||||
|
case NetIface::AP:
|
||||||
|
return "AP";
|
||||||
|
case NetIface::NONE:
|
||||||
|
default:
|
||||||
|
return "None";
|
||||||
|
}
|
||||||
|
return "unknown";
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static uuid::log::Logger logger_;
|
static uuid::log::Logger logger_;
|
||||||
|
|
||||||
bool findNetworks();
|
void findNetworks();
|
||||||
void checkConnection();
|
void checkConnection();
|
||||||
void startmDNS() const;
|
void startmDNS() const;
|
||||||
bool formatBSSID(const String & bssid, uint8_t (&mac)[6]);
|
bool formatBSSID(const String & bssid, uint8_t (&mac)[6]);
|
||||||
@@ -162,6 +183,7 @@ class Network {
|
|||||||
void setWiFiPower(uint8_t tx_power);
|
void setWiFiPower(uint8_t tx_power);
|
||||||
const char * disconnectReason(uint8_t code);
|
const char * disconnectReason(uint8_t code);
|
||||||
void stopAP();
|
void stopAP();
|
||||||
|
NetPhase initialPhase() const;
|
||||||
|
|
||||||
#if defined(__clang__)
|
#if defined(__clang__)
|
||||||
#pragma clang diagnostic push
|
#pragma clang diagnostic push
|
||||||
@@ -169,15 +191,21 @@ class Network {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
unsigned long lastConnectionAttempt_ = 0;
|
unsigned long lastConnectionAttempt_ = 0;
|
||||||
uint16_t connectcount_ = 0; // number of network reconnects
|
uint16_t reconnect_count_ = 0; // number of network reconnects
|
||||||
uint32_t network_ip_ = 0;
|
uint32_t network_ip_ = 0;
|
||||||
NetIface network_iface_ = NetIface::NONE;
|
NetIface network_iface_ = NetIface::NONE;
|
||||||
bool has_ipv6_ = false;
|
bool has_ipv6_ = false;
|
||||||
bool juststopped_ = false;
|
bool juststopped_ = false;
|
||||||
bool eth_started_ = false; // true after ETH.begin() has succeeded once; prevents repeated re-init while DHCP is still running
|
|
||||||
volatile uint8_t last_disconnect_reason_ = 0;
|
volatile uint8_t last_disconnect_reason_ = 0;
|
||||||
uint16_t connect_retry_ = 0; // number of network re-connection attempts
|
uint16_t connect_retry_ = 0; // number of network re-connection attempts
|
||||||
volatile bool wifi_connect_pending_ = false;
|
|
||||||
|
volatile bool wifi_connect_pending_ = false;
|
||||||
|
volatile bool ethernet_connect_pending_ = false;
|
||||||
|
|
||||||
|
NetPhase phase_ = NetPhase::ETHERNET;
|
||||||
|
|
||||||
|
bool wifi_events_registered_ = false; // ensure WiFi.onEvent() handlers are registered only once across begin()/reconnect() cycles
|
||||||
|
bool wifi_ever_connected_ = false; // set true once we've successfully obtained an IP
|
||||||
|
|
||||||
// Network and AP settings
|
// Network and AP settings
|
||||||
bool enableMDNS_;
|
bool enableMDNS_;
|
||||||
@@ -216,7 +244,7 @@ class Network {
|
|||||||
|
|
||||||
// for the captive portal in AP mode
|
// for the captive portal in AP mode
|
||||||
#ifndef EMSESP_STANDALONE
|
#ifndef EMSESP_STANDALONE
|
||||||
DNSServer * ap_dnsServer_;
|
DNSServer * ap_dnsServer_ = nullptr;
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -742,6 +742,8 @@ void System::button_OnClick(PButton & b) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// button double click
|
// button double click
|
||||||
|
// reconnect to AP by removing the SSID from the network settings
|
||||||
|
// note: in v3.9 this is normal behaviour to fallback to AP if the Wifi or Ethernet connection fails
|
||||||
void System::button_OnDblClick(PButton & b) {
|
void System::button_OnDblClick(PButton & b) {
|
||||||
LOG_NOTICE("Button pressed - double click - wifi reconnect to AP");
|
LOG_NOTICE("Button pressed - double click - wifi reconnect to AP");
|
||||||
#ifndef EMSESP_STANDALONE
|
#ifndef EMSESP_STANDALONE
|
||||||
@@ -998,7 +1000,7 @@ void System::heartbeat_json(JsonObject output) {
|
|||||||
int8_t rssi = WiFi.RSSI();
|
int8_t rssi = WiFi.RSSI();
|
||||||
output["rssi"] = rssi;
|
output["rssi"] = rssi;
|
||||||
output["wifistrength"] = wifi_quality(rssi);
|
output["wifistrength"] = wifi_quality(rssi);
|
||||||
output["wifireconnects"] = EMSESP::network_.getWifiReconnects();
|
output["wifireconnects"] = EMSESP::network_.getNetworkReconnects();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -1307,19 +1309,19 @@ void System::show_system(uuid::console::Shell & shell) {
|
|||||||
shell.println("Network:");
|
shell.println("Network:");
|
||||||
switch (WiFi.status()) {
|
switch (WiFi.status()) {
|
||||||
case WL_IDLE_STATUS:
|
case WL_IDLE_STATUS:
|
||||||
shell.printfln(" Status: Idle");
|
shell.printfln(" WiFi Status: Idle");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case WL_NO_SSID_AVAIL:
|
case WL_NO_SSID_AVAIL:
|
||||||
shell.printfln(" Status: Network not found");
|
shell.printfln(" WiFi Status: Network not found");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case WL_SCAN_COMPLETED:
|
case WL_SCAN_COMPLETED:
|
||||||
shell.printfln(" Status: Network scan complete");
|
shell.printfln(" WiFi Status: Network scan complete");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case WL_CONNECTED:
|
case WL_CONNECTED:
|
||||||
shell.printfln(" Status: WiFi connected");
|
shell.printfln(" WiFi Status: Connected");
|
||||||
shell.printfln(" SSID: %s", WiFi.SSID().c_str());
|
shell.printfln(" SSID: %s", WiFi.SSID().c_str());
|
||||||
shell.printfln(" BSSID: %s", WiFi.BSSIDstr().c_str());
|
shell.printfln(" BSSID: %s", WiFi.BSSIDstr().c_str());
|
||||||
shell.printfln(" RSSI: %d dBm (%d %%)", WiFi.RSSI(), wifi_quality(WiFi.RSSI()));
|
shell.printfln(" RSSI: %d dBm (%d %%)", WiFi.RSSI(), wifi_quality(WiFi.RSSI()));
|
||||||
@@ -1367,8 +1369,13 @@ void System::show_system(uuid::console::Shell & shell) {
|
|||||||
shell.printfln(" IPv6 address: %s", uuid::printable_to_string(ETH.linkLocalIPv6()).c_str());
|
shell.printfln(" IPv6 address: %s", uuid::printable_to_string(ETH.linkLocalIPv6()).c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
shell.println();
|
|
||||||
|
|
||||||
|
// show AP is connected
|
||||||
|
if (EMSESP::network_.ap_connected()) {
|
||||||
|
shell.printfln(" AP Status: connected");
|
||||||
|
}
|
||||||
|
|
||||||
|
shell.println();
|
||||||
shell.println("Syslog:");
|
shell.println("Syslog:");
|
||||||
if (!syslog_enabled_) {
|
if (!syslog_enabled_) {
|
||||||
shell.printfln(" Syslog: disabled");
|
shell.printfln(" Syslog: disabled");
|
||||||
@@ -2451,7 +2458,7 @@ bool System::command_info(const char * value, const int8_t id, JsonObject output
|
|||||||
node["network"] = "WiFi";
|
node["network"] = "WiFi";
|
||||||
node["hostname"] = WiFi.getHostname();
|
node["hostname"] = WiFi.getHostname();
|
||||||
node["RSSI"] = WiFi.RSSI();
|
node["RSSI"] = WiFi.RSSI();
|
||||||
node["WIFIReconnects"] = EMSESP::network_.getWifiReconnects();
|
node["WIFIReconnects"] = EMSESP::network_.getNetworkReconnects();
|
||||||
// node["MAC"] = WiFi.macAddress();
|
// node["MAC"] = WiFi.macAddress();
|
||||||
// node["IPv4 address"] = uuid::printable_to_string(WiFi.localIP()) + "/" + uuid::printable_to_string(WiFi.subnetMask());
|
// node["IPv4 address"] = uuid::printable_to_string(WiFi.localIP()) + "/" + uuid::printable_to_string(WiFi.subnetMask());
|
||||||
// node["IPv4 gateway"] = uuid::printable_to_string(WiFi.gatewayIP());
|
// node["IPv4 gateway"] = uuid::printable_to_string(WiFi.gatewayIP());
|
||||||
|
|||||||
@@ -359,7 +359,6 @@ StateUpdateResult WebSettings::update(JsonObject root, WebSettings & settings) {
|
|||||||
// either via the Web UI or via the Console
|
// either via the Web UI or via the Console
|
||||||
void WebSettingsService::onUpdate() {
|
void WebSettingsService::onUpdate() {
|
||||||
// skip if we're restarting anyway
|
// skip if we're restarting anyway
|
||||||
|
|
||||||
if (WebSettings::has_flags(WebSettings::ChangeFlags::RESTART)) {
|
if (WebSettings::has_flags(WebSettings::ChangeFlags::RESTART)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -369,6 +369,18 @@ void WebStatusService::getVersions(JsonObject root) {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// schedule the next versions.json fetch a few seconds out so the network stack has time to settle
|
||||||
|
// (DHCP completion, default-netif assignment and DNS server propagation through lwip)
|
||||||
|
void WebStatusService::schedule_versions_refresh() {
|
||||||
|
#ifndef EMSESP_STANDALONE
|
||||||
|
uint32_t next = uuid::get_uptime() + VERSIONS_INITIAL_FETCH_DELAY_MS;
|
||||||
|
if (next == 0) {
|
||||||
|
next = 1; // 0 is the "idle" sentinel — never let the wrap land there
|
||||||
|
}
|
||||||
|
versions_next_fetch_ms_ = next;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
// periodic refresh (1 hour) of the cached versions.json
|
// periodic refresh (1 hour) of the cached versions.json
|
||||||
// runs on the main loop task, which has a much bigger stack than AsyncTCP needed for https
|
// runs on the main loop task, which has a much bigger stack than AsyncTCP needed for https
|
||||||
void WebStatusService::loop() {
|
void WebStatusService::loop() {
|
||||||
@@ -378,8 +390,6 @@ void WebStatusService::loop() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO handle a network re-connect to fetch the values again (set versions_next_fetch_ms_ to 1)
|
|
||||||
|
|
||||||
// 0 = idle, nothing scheduled
|
// 0 = idle, nothing scheduled
|
||||||
if (versions_next_fetch_ms_ == 0) {
|
if (versions_next_fetch_ms_ == 0) {
|
||||||
return;
|
return;
|
||||||
@@ -419,7 +429,7 @@ bool WebStatusService::refresh_versions_cache() {
|
|||||||
int httpCode = http.GET();
|
int httpCode = http.GET();
|
||||||
if (httpCode != HTTP_CODE_OK) {
|
if (httpCode != HTTP_CODE_OK) {
|
||||||
#if defined(EMSESP_DEBUG)
|
#if defined(EMSESP_DEBUG)
|
||||||
EMSESP::logger().debug("versions.json: HTTP %d", httpCode);
|
EMSESP::logger().debug("versions.json: HTTP error code %d", httpCode);
|
||||||
#endif
|
#endif
|
||||||
http.end();
|
http.end();
|
||||||
return false;
|
return false;
|
||||||
@@ -453,7 +463,7 @@ bool WebStatusService::refresh_versions_cache() {
|
|||||||
|
|
||||||
versions_cache_valid_ = true;
|
versions_cache_valid_ = true;
|
||||||
#if defined(EMSESP_DEBUG)
|
#if defined(EMSESP_DEBUG)
|
||||||
EMSESP::logger().debug("versions.json: refreshed (stable=%s dev=%s), current=%s",
|
EMSESP::logger().debug("versions.json: fetched stable=%s, dev=%s, current=%s",
|
||||||
versions_stable_.version.c_str(),
|
versions_stable_.version.c_str(),
|
||||||
versions_dev_.version.c_str(),
|
versions_dev_.version.c_str(),
|
||||||
current_version_s.c_str());
|
current_version_s.c_str());
|
||||||
|
|||||||
@@ -30,10 +30,11 @@ class WebStatusService {
|
|||||||
return versions_cache_valid_;
|
return versions_cache_valid_;
|
||||||
}
|
}
|
||||||
|
|
||||||
// refresh the versions.json cache
|
// schedule a refresh of the versions.json cache. Defers the fetch by
|
||||||
void schedule_versions_refresh() {
|
// VERSIONS_INITIAL_FETCH_DELAY_MS so the network stack (DHCP, default netif, DNS server)
|
||||||
versions_next_fetch_ms_ = 1;
|
// has time to settle after the link first comes up — otherwise hostByName() can fail
|
||||||
}
|
// immediately on boot with a noisy "DNS Failed ... error '-54'".
|
||||||
|
void schedule_versions_refresh();
|
||||||
|
|
||||||
bool current_upgradeable() const; // true if a newer version is available
|
bool current_upgradeable() const; // true if a newer version is available
|
||||||
|
|
||||||
@@ -71,8 +72,9 @@ class WebStatusService {
|
|||||||
|
|
||||||
bool refresh_versions_cache(); // does the actual HTTPS fetch + parse, returns true on success
|
bool refresh_versions_cache(); // does the actual HTTPS fetch + parse, returns true on success
|
||||||
|
|
||||||
static constexpr uint32_t VERSIONS_REFRESH_INTERVAL_MS = 60UL * 60UL * 1000UL; // 1 hour on success
|
static constexpr uint32_t VERSIONS_REFRESH_INTERVAL_MS = 60UL * 60UL * 1000UL; // 1 hour on success
|
||||||
static constexpr uint32_t VERSIONS_RETRY_INTERVAL_MS = 5UL * 60UL * 1000UL; // 5 min after failure
|
static constexpr uint32_t VERSIONS_RETRY_INTERVAL_MS = 5UL * 60UL * 1000UL; // 5 min after failure
|
||||||
|
static constexpr uint32_t VERSIONS_INITIAL_FETCH_DELAY_MS = 5UL * 1000UL; // 5 s after a link comes up
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace emsesp
|
} // namespace emsesp
|
||||||
|
|||||||
Reference in New Issue
Block a user