mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2026-05-04 21:15:52 +00:00
updates
This commit is contained in:
@@ -72,12 +72,6 @@ StateUpdateResult NetworkSettings::update(JsonObject root, NetworkSettings & set
|
||||
settings.staticIPConfig = false;
|
||||
}
|
||||
|
||||
// see if we need to inform the user of a restart
|
||||
// if tx power, enableCORS, CORSOrigin, ssid changes, we need to 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;
|
||||
// always best to do a restart after changing network settings
|
||||
return StateUpdateResult::CHANGED_RESTART;
|
||||
}
|
||||
@@ -26,8 +26,8 @@ uuid::log::Logger Network::logger_{F_(network), uuid::log::Facility::KERN};
|
||||
|
||||
void Network::begin() {
|
||||
#ifndef EMSESP_STANDALONE
|
||||
// pull Network settings and store locally on stack
|
||||
EMSESP::esp32React.getNetworkSettingsService()->read([&](auto & settings) {
|
||||
// pull all settings and store locally
|
||||
EMSESP::esp32React.getNetworkSettingsService()->read([&](NetworkSettings & settings) {
|
||||
enableMDNS_ = settings.enableMDNS;
|
||||
staticIPConfig_ = settings.staticIPConfig;
|
||||
localIP_ = settings.localIP;
|
||||
@@ -66,36 +66,96 @@ void Network::begin() {
|
||||
});
|
||||
|
||||
// Initialise WiFi - we only do this once, when the network service is started.
|
||||
|
||||
// We want the device to come up in opmode=0 (WIFI_OFF), which is not the default after a flash erase.
|
||||
// Persistence is true by default, so this WiFi.mode() call writes opmode=0 to NVS for future boots.
|
||||
WiFi.mode(WIFI_OFF);
|
||||
// WiFi.mode(WIFI_OFF);
|
||||
|
||||
// if Wifi is disabled, with no SSID, stop here
|
||||
if (ssid_.isEmpty()) {
|
||||
WiFi.mode(WIFI_OFF);
|
||||
return;
|
||||
}
|
||||
|
||||
// From here on, mode changes stay in RAM only and don't touch NVS
|
||||
WiFi.persistent(false);
|
||||
WiFi.setAutoReconnect(false);
|
||||
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
|
||||
}
|
||||
|
||||
wifi_connect_pending_ = false; // set before begin() so the event handlers can race-clear it safely
|
||||
|
||||
// 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.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
|
||||
// log a meaningful reason when its periodic poll notices we're no longer associated.
|
||||
// Also release the connect-pending guard so the next loop tick can issue a fresh WiFi.begin()
|
||||
WiFi.onEvent(
|
||||
[this](WiFiEvent_t /*event*/, WiFiEventInfo_t info) {
|
||||
last_disconnect_reason_ = info.wifi_sta_disconnected.reason;
|
||||
wifi_connect_pending_ = false;
|
||||
},
|
||||
ARDUINO_EVENT_WIFI_STA_DISCONNECTED);
|
||||
// arduino-esp32's WiFi.onEvent() simply appends to an internal callback list with no
|
||||
// de-duplication, so register the lambdas only once across the lifetime of this Network instance
|
||||
if (!wifi_events_registered_) {
|
||||
wifi_events_registered_ = true;
|
||||
|
||||
// Defer Tx power setting until STA is actually started. Calling WiFi.setTxPower() before
|
||||
// WIFI_EVENT_STA_START fires would fail with "Neither AP or STA has been started" because
|
||||
// WiFi.STA.started() only flips after esp_wifi_start() raises the event asynchronously.
|
||||
WiFi.onEvent(
|
||||
[this](WiFiEvent_t /*event*/, WiFiEventInfo_t /*info*/) {
|
||||
#ifdef BOARD_C3_MINI_V1
|
||||
// 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.
|
||||
// Also release the connect-pending guard so the next loop tick can issue a fresh WiFi.begin().
|
||||
// The first STA_DISCONNECTED after boot is suppressed because arduino-esp32 hard-codes a
|
||||
// "retry once on first_connect" inside its own STA event handler (see STA.cpp), so a
|
||||
// transient initial AUTH_FAIL / NO_AP_FOUND / etc. is automatically retried and almost
|
||||
// always succeeds; logging it as a WARNING is misleading noise.
|
||||
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,
|
||||
// and latch wifi_ever_connected_ so future disconnects log as warnings
|
||||
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
|
||||
}
|
||||
|
||||
@@ -159,12 +219,13 @@ uint8_t Network::getStationNum() const {
|
||||
// disconnect all WiFi, Eth and AP
|
||||
// so we can starts searching again to reconnect
|
||||
void Network::reconnect() {
|
||||
LOG_DEBUG("Disconnecting all networks");
|
||||
LOG_DEBUG("Reconnecting all networks");
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
// disconnect WiFi
|
||||
if (wifi_connected()) {
|
||||
WiFi.disconnect(true);
|
||||
WiFi.disconnect(true, true);
|
||||
WiFi.mode(WIFI_STA); // reset mode
|
||||
}
|
||||
|
||||
// disconnect AP
|
||||
@@ -177,11 +238,13 @@ void Network::reconnect() {
|
||||
network_ip_ = 0;
|
||||
network_iface_ = NetIface::NONE;
|
||||
has_ipv6_ = false;
|
||||
juststopped_ = true;
|
||||
juststopped_ = false;
|
||||
wifi_connect_pending_ = false;
|
||||
last_disconnect_reason_ = 0;
|
||||
connect_retry_ = 0;
|
||||
reconnect_count_ = 0;
|
||||
|
||||
// reload the network settings, as this could be called from the console
|
||||
// reload the network settings and apply them
|
||||
begin();
|
||||
}
|
||||
|
||||
@@ -196,16 +259,17 @@ void Network::loop() {
|
||||
lastConnectionAttempt_ = currentMillis;
|
||||
|
||||
// manage network interfaces
|
||||
startAP(); // Captive Portal (AP)
|
||||
startWIFI(); // WiFi
|
||||
startEthernet(); // Ethernet
|
||||
startWIFI(); // WiFi
|
||||
startAP(); // Captive Portal (AP)
|
||||
|
||||
// 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) {
|
||||
checkConnection();
|
||||
}
|
||||
findNetworks(); // detect new connections
|
||||
|
||||
findNetworks(); // detect any new network connections
|
||||
}
|
||||
|
||||
// process DNS requests for the captive portal while the soft-AP is up
|
||||
@@ -219,6 +283,10 @@ void Network::loop() {
|
||||
// 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
|
||||
void Network::checkConnection() {
|
||||
if (network_iface_ == NetIface::NONE) {
|
||||
return;
|
||||
}
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
bool still_up = false;
|
||||
for (esp_netif_t * netif = esp_netif_next_unsafe(NULL); netif != NULL; netif = esp_netif_next_unsafe(netif)) {
|
||||
@@ -238,11 +306,15 @@ void Network::checkConnection() {
|
||||
if (reason == 0) {
|
||||
reason = WIFI_REASON_UNSPECIFIED; // event hasn't fired yet (or was cleared); avoid logging "0"
|
||||
}
|
||||
wifi_connect_pending_ = false;
|
||||
LOG_INFO("WiFi connection lost (reason %u: %s)", reason, disconnectReason(reason));
|
||||
} else {
|
||||
} else if (network_iface_ == NetIface::ETHERNET) {
|
||||
LOG_INFO("Ethernet connection lost");
|
||||
}
|
||||
reconnect();
|
||||
juststopped_ = true;
|
||||
network_iface_ = NetIface::NONE;
|
||||
network_ip_ = 0;
|
||||
has_ipv6_ = false;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -309,7 +381,7 @@ void Network::setWiFiPower(uint8_t tx_power) {
|
||||
#endif
|
||||
|
||||
if (!WiFi.setTxPower(p)) {
|
||||
emsesp::EMSESP::logger().warning("Failed to set WiFi Tx Power");
|
||||
emsesp::EMSESP::logger().warning("Failed to set WiFi Tx Power!!");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -419,53 +491,23 @@ const char * Network::disconnectReason(uint8_t code) {
|
||||
// WiFi management
|
||||
void Network::startWIFI() {
|
||||
#ifndef EMSESP_STANDALONE
|
||||
// Abort if already connected, or if we have no SSID or another Wifi.begin() is already in progress
|
||||
if (WiFi.isConnected() || ssid_.length() == 0 || wifi_connect_pending_) {
|
||||
// exit if WiFi is already connected, or if we have no SSID or another Wifi.begin() is already in progress
|
||||
if (WiFi.isConnected() || ssid_.isEmpty() || wifi_connect_pending_) {
|
||||
return;
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
wifi_connect_pending_ = true;
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// attempt to connect to the network
|
||||
uint8_t bssid[6];
|
||||
wl_status_t status;
|
||||
wifi_connect_pending_ = true; // set before begin() so the event handlers can race-clear it safely
|
||||
// LOG_DEBUG("WiFi connection with %s and %s", ssid_.c_str(), password_.c_str());
|
||||
|
||||
// attempt to connect to the wifi network
|
||||
// the event handlers handle error handling and retries
|
||||
uint8_t bssid[6];
|
||||
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 {
|
||||
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
|
||||
}
|
||||
|
||||
@@ -482,26 +524,11 @@ void Network::startEthernet() {
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
|
||||
// no ethernet present or wifi takes precedence
|
||||
if (phy_type_ == PHY_type::PHY_TYPE_NONE || (ssid_.length() > 0)) {
|
||||
// no ethernet present
|
||||
if (phy_type_ == PHY_type::PHY_TYPE_NONE) {
|
||||
return;
|
||||
}
|
||||
|
||||
// configure Ethernet
|
||||
int mdc = 23; // Pin# of the I²C clock signal for the Ethernet PHY - hardcoded
|
||||
int mdio = 18; // Pin# of the I²C IO signal for the Ethernet PHY - hardcoded
|
||||
uint8_t phy_addr = eth_phy_addr_; // I²C-address of Ethernet PHY (0 or 1 for LAN8720, 31 for TLK110)
|
||||
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
|
||||
if (eth_power_ != -1) {
|
||||
pinMode(eth_power_, OUTPUT);
|
||||
@@ -510,7 +537,22 @@ void Network::startEthernet() {
|
||||
digitalWrite(eth_power_, HIGH);
|
||||
}
|
||||
|
||||
if (ETH.begin(type, phy_addr, mdc, mdio, power, clock_mode)) {
|
||||
// call to ETH.begin(type, phy_addr, mdc, mdio, power, clock_mode)
|
||||
// mdc = 23 = Pin# of the I²C clock signal for the Ethernet PHY - hardcoded
|
||||
// mdio = 18 = Pin# of the I²C IO signal for the Ethernet PHY - hardcoded
|
||||
// phy_addr = eth_phy_addr_ = I²C-address of Ethernet PHY (0 or 1 for LAN8720, 31 for TLK110)
|
||||
// power = eth_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 started");
|
||||
eth_started_ = true; // mark up; do not re-enter this block until reboot / explicit teardown
|
||||
ETH.setHostname(hostname_.c_str()); // Push hostname to the ETH netif immediately after it's created
|
||||
ETH.enableIPv6(true);
|
||||
@@ -526,16 +568,18 @@ void Network::startEthernet() {
|
||||
|
||||
// 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
|
||||
bool Network::findNetworks() {
|
||||
void Network::findNetworks() {
|
||||
#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;
|
||||
}
|
||||
// TODO what about if Eth drops and then comes back - we want to auto-switch?
|
||||
// if (network_ip_ != 0 && !(WiFi.getMode() & WIFI_AP)) {
|
||||
// // for debugging only
|
||||
// // const esp_ip4_addr_t ip4 = {.addr = network_ip_};
|
||||
// // LOG_DEBUG("Network already connected via IPv4: " IPSTR, IP2STR(&ip4));
|
||||
// return;
|
||||
// }
|
||||
|
||||
struct NetInfo {
|
||||
esp_ip4_addr_t ip;
|
||||
@@ -580,18 +624,17 @@ bool Network::findNetworks() {
|
||||
strlcpy(info.desc, desc, sizeof(info.desc));
|
||||
}
|
||||
info.has_ipv6 = (esp_netif_get_ip6_linklocal(netif, &info.ip6) == ESP_OK);
|
||||
best_iface = candidate;
|
||||
|
||||
best_iface = candidate;
|
||||
if (best_iface == NetIface::ETHERNET) {
|
||||
break; // top priority, can't be beaten by anything later in the list
|
||||
}
|
||||
}
|
||||
|
||||
auto previous_iface = NetIface::NONE;
|
||||
// LOG_DEBUG("best_iface: %d, network_iface_: %d", best_iface, network_iface_);
|
||||
|
||||
// if we have a connection and it's a new one, set it up
|
||||
if (best_iface != NetIface::NONE && best_iface != previous_iface) {
|
||||
previous_iface = network_iface_; // save the previous interface for comparison next time
|
||||
if (best_iface != NetIface::NONE && best_iface != network_iface_) {
|
||||
network_ip_ = info.ip.addr;
|
||||
network_iface_ = iface_from_desc(info.desc); // "sta"/"ap"/"eth*"
|
||||
has_ipv6_ = info.has_ipv6;
|
||||
@@ -612,8 +655,7 @@ bool Network::findNetworks() {
|
||||
// count the number of restarts (for Wifi and Eth)
|
||||
if (juststopped_) {
|
||||
juststopped_ = false;
|
||||
connectcount_++;
|
||||
LOG_DEBUG("Network re-connection count %d", connectcount_);
|
||||
reconnect_count_++;
|
||||
}
|
||||
|
||||
// start mDNS for any real network interface (skip the SoftAP since the captive portal handles its own DNS)
|
||||
@@ -624,17 +666,20 @@ bool Network::findNetworks() {
|
||||
// fetch the versions.json file from emsesp.org
|
||||
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("No active network interfaces found yet (retry #%d)", connect_retry_);
|
||||
}
|
||||
|
||||
// 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
|
||||
return false; // no connection found yet
|
||||
return;
|
||||
}
|
||||
|
||||
// access point (soft-AP) and the captive portal
|
||||
|
||||
@@ -35,12 +35,12 @@
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
#define NETWORK_RECONNECTION_DELAY_SHORT 3000 // 3 seconds
|
||||
#define NETWORK_RECONNECTION_DELAY_LONG 60000 // 60 seconds
|
||||
#define NETWORK_RECONNECTION_DELAY_SHORT 5000 // 5 seconds
|
||||
#define NETWORK_RECONNECTION_DELAY_LONG 60000 // 1 minute
|
||||
|
||||
#define MAX_NETWORK_RECONNECTION_ATTEMPTS 3 // maximum number of network reconnection attempts
|
||||
#define MAX_NETWORK_RECONNECTION_ATTEMPTS 4 // maximum number of network reconnection attempts before going to AP fallback
|
||||
|
||||
#define DNS_PORT 53
|
||||
#define DNS_PORT 53 // dns server port for captive portal
|
||||
|
||||
// copied from Tasmota
|
||||
#if CONFIG_IDF_TARGET_ESP32S2
|
||||
@@ -79,9 +79,9 @@ namespace emsesp {
|
||||
// multiple ETH instances) -> ETHERNET. Anything else stays as NONE.
|
||||
enum class NetIface : uint8_t {
|
||||
NONE = 0,
|
||||
WIFI,
|
||||
ETHERNET,
|
||||
AP,
|
||||
WIFI, // 1
|
||||
ETHERNET, // 2
|
||||
AP, // 3
|
||||
};
|
||||
|
||||
class Network {
|
||||
@@ -118,7 +118,7 @@ class Network {
|
||||
}
|
||||
|
||||
uint16_t getNetworkReconnects() {
|
||||
return connectcount_;
|
||||
return reconnect_count_;
|
||||
}
|
||||
|
||||
std::string getLocalIP() const;
|
||||
@@ -145,10 +145,25 @@ class Network {
|
||||
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:
|
||||
static uuid::log::Logger logger_;
|
||||
|
||||
bool findNetworks();
|
||||
void findNetworks();
|
||||
void checkConnection();
|
||||
void startmDNS() const;
|
||||
bool formatBSSID(const String & bssid, uint8_t (&mac)[6]);
|
||||
@@ -165,7 +180,7 @@ class Network {
|
||||
#endif
|
||||
|
||||
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;
|
||||
NetIface network_iface_ = NetIface::NONE;
|
||||
bool has_ipv6_ = false;
|
||||
@@ -173,7 +188,11 @@ class Network {
|
||||
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;
|
||||
uint16_t connect_retry_ = 0; // number of network re-connection attempts
|
||||
volatile bool wifi_connect_pending_ = false;
|
||||
|
||||
volatile bool wifi_connect_pending_ = false;
|
||||
|
||||
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; used to silence the harmless first-attempt disconnect emitted by arduino-esp32's built-in retry-once behaviour
|
||||
|
||||
// Network and AP settings
|
||||
bool enableMDNS_;
|
||||
|
||||
@@ -742,6 +742,8 @@ void System::button_OnClick(PButton & b) {
|
||||
}
|
||||
|
||||
// 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) {
|
||||
LOG_NOTICE("Button pressed - double click - wifi reconnect to AP");
|
||||
#ifndef EMSESP_STANDALONE
|
||||
@@ -1307,19 +1309,19 @@ void System::show_system(uuid::console::Shell & shell) {
|
||||
shell.println("Network:");
|
||||
switch (WiFi.status()) {
|
||||
case WL_IDLE_STATUS:
|
||||
shell.printfln(" Status: Idle");
|
||||
shell.printfln(" WiFi Status: Idle");
|
||||
break;
|
||||
|
||||
case WL_NO_SSID_AVAIL:
|
||||
shell.printfln(" Status: Network not found");
|
||||
shell.printfln(" WiFi Status: Network not found");
|
||||
break;
|
||||
|
||||
case WL_SCAN_COMPLETED:
|
||||
shell.printfln(" Status: Network scan complete");
|
||||
shell.printfln(" WiFi Status: Network scan complete");
|
||||
break;
|
||||
|
||||
case WL_CONNECTED:
|
||||
shell.printfln(" Status: WiFi connected");
|
||||
shell.printfln(" WiFi Status: Connected");
|
||||
shell.printfln(" SSID: %s", WiFi.SSID().c_str());
|
||||
shell.printfln(" BSSID: %s", WiFi.BSSIDstr().c_str());
|
||||
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.println();
|
||||
|
||||
// show AP is connected
|
||||
if (EMSESP::network_.ap_connected()) {
|
||||
shell.printfln(" AP Status: connected");
|
||||
}
|
||||
|
||||
shell.println();
|
||||
shell.println("Syslog:");
|
||||
if (!syslog_enabled_) {
|
||||
shell.printfln(" Syslog: disabled");
|
||||
|
||||
@@ -359,7 +359,6 @@ StateUpdateResult WebSettings::update(JsonObject root, WebSettings & settings) {
|
||||
// either via the Web UI or via the Console
|
||||
void WebSettingsService::onUpdate() {
|
||||
// skip if we're restarting anyway
|
||||
|
||||
if (WebSettings::has_flags(WebSettings::ChangeFlags::RESTART)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -369,6 +369,20 @@ void WebStatusService::getVersions(JsonObject root) {
|
||||
#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) before
|
||||
// HTTPClient::begin() does the hostByName() lookup. Without this delay the very first fetch races
|
||||
// with the link-up event and arduino-esp32 logs a noisy "DNS Failed ... error '-54'".
|
||||
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
|
||||
// runs on the main loop task, which has a much bigger stack than AsyncTCP needed for https
|
||||
void WebStatusService::loop() {
|
||||
@@ -378,8 +392,6 @@ void WebStatusService::loop() {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO handle a network re-connect to fetch the values again (set versions_next_fetch_ms_ to 1)
|
||||
|
||||
// 0 = idle, nothing scheduled
|
||||
if (versions_next_fetch_ms_ == 0) {
|
||||
return;
|
||||
@@ -419,7 +431,7 @@ bool WebStatusService::refresh_versions_cache() {
|
||||
int httpCode = http.GET();
|
||||
if (httpCode != HTTP_CODE_OK) {
|
||||
#if defined(EMSESP_DEBUG)
|
||||
EMSESP::logger().debug("versions.json: HTTP %d", httpCode);
|
||||
EMSESP::logger().debug("versions.json: HTTP error code %d", httpCode);
|
||||
#endif
|
||||
http.end();
|
||||
return false;
|
||||
|
||||
@@ -30,10 +30,11 @@ class WebStatusService {
|
||||
return versions_cache_valid_;
|
||||
}
|
||||
|
||||
// refresh the versions.json cache
|
||||
void schedule_versions_refresh() {
|
||||
versions_next_fetch_ms_ = 1;
|
||||
}
|
||||
// schedule a refresh of the versions.json cache. Defers the fetch by
|
||||
// VERSIONS_INITIAL_FETCH_DELAY_MS so the network stack (DHCP, default netif, DNS server)
|
||||
// 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
|
||||
|
||||
@@ -71,8 +72,9 @@ class WebStatusService {
|
||||
|
||||
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_RETRY_INTERVAL_MS = 5UL * 60UL * 1000UL; // 5 min after failure
|
||||
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_INITIAL_FETCH_DELAY_MS = 5UL * 1000UL; // 5 s after a link comes up
|
||||
};
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
Reference in New Issue
Block a user