mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2026-05-04 13:05:52 +00:00
updates
This commit is contained in:
@@ -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;
|
|
||||||
}
|
}
|
||||||
@@ -26,8 +26,8 @@ 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;
|
||||||
@@ -66,36 +66,96 @@ void Network::begin() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Initialise WiFi - we only do this once, when the network service is started.
|
// 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.
|
// 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.
|
// 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.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
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
// 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
|
// arduino-esp32's WiFi.onEvent() simply appends to an internal callback list with no
|
||||||
// log a meaningful reason when its periodic poll notices we're no longer associated.
|
// de-duplication, so register the lambdas only once across the lifetime of this Network instance
|
||||||
// Also release the connect-pending guard so the next loop tick can issue a fresh WiFi.begin()
|
if (!wifi_events_registered_) {
|
||||||
WiFi.onEvent(
|
wifi_events_registered_ = true;
|
||||||
[this](WiFiEvent_t /*event*/, WiFiEventInfo_t info) {
|
|
||||||
last_disconnect_reason_ = info.wifi_sta_disconnected.reason;
|
// Defer Tx power setting until STA is actually started. Calling WiFi.setTxPower() before
|
||||||
wifi_connect_pending_ = false;
|
// 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.
|
||||||
ARDUINO_EVENT_WIFI_STA_DISCONNECTED);
|
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
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,12 +219,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
|
||||||
@@ -177,11 +238,13 @@ void Network::reconnect() {
|
|||||||
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;
|
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();
|
begin();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -196,16 +259,17 @@ void Network::loop() {
|
|||||||
lastConnectionAttempt_ = currentMillis;
|
lastConnectionAttempt_ = currentMillis;
|
||||||
|
|
||||||
// manage network interfaces
|
// manage network interfaces
|
||||||
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
|
// 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
|
||||||
@@ -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
|
// 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)) {
|
||||||
@@ -238,11 +306,15 @@ void Network::checkConnection() {
|
|||||||
if (reason == 0) {
|
if (reason == 0) {
|
||||||
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"
|
||||||
}
|
}
|
||||||
|
wifi_connect_pending_ = false;
|
||||||
LOG_INFO("WiFi connection lost (reason %u: %s)", reason, disconnectReason(reason));
|
LOG_INFO("WiFi connection lost (reason %u: %s)", reason, disconnectReason(reason));
|
||||||
} else {
|
} else if (network_iface_ == NetIface::ETHERNET) {
|
||||||
LOG_INFO("Ethernet connection lost");
|
LOG_INFO("Ethernet connection lost");
|
||||||
}
|
}
|
||||||
reconnect();
|
juststopped_ = true;
|
||||||
|
network_iface_ = NetIface::NONE;
|
||||||
|
network_ip_ = 0;
|
||||||
|
has_ipv6_ = false;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@@ -309,7 +381,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");
|
emsesp::EMSESP::logger().warning("Failed to set WiFi Tx Power!!");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@@ -419,53 +491,23 @@ 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
|
// exit if WiFi is 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_) {
|
if (WiFi.isConnected() || ssid_.isEmpty() || wifi_connect_pending_) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
WiFi.setHostname(hostname_.c_str()); // updates shared default_hostname buffer
|
wifi_connect_pending_ = true;
|
||||||
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
|
// LOG_DEBUG("WiFi connection with %s and %s", ssid_.c_str(), password_.c_str());
|
||||||
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
|
|
||||||
|
|
||||||
|
// 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
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -482,26 +524,11 @@ void Network::startEthernet() {
|
|||||||
|
|
||||||
#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
|
|
||||||
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
|
// 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) {
|
||||||
pinMode(eth_power_, OUTPUT);
|
pinMode(eth_power_, OUTPUT);
|
||||||
@@ -510,7 +537,22 @@ void Network::startEthernet() {
|
|||||||
digitalWrite(eth_power_, HIGH);
|
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_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.setHostname(hostname_.c_str()); // Push hostname to the ETH netif immediately after it's created
|
||||||
ETH.enableIPv6(true);
|
ETH.enableIPv6(true);
|
||||||
@@ -526,16 +568,18 @@ 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
|
// exit if already have a connection, unless in AP mode
|
||||||
// when in AP mode, it will always try and connect to the WiFi
|
// when in AP mode, it will always try and connect to the WiFi
|
||||||
if (network_ip_ != 0 && !(WiFi.getMode() & WIFI_AP)) {
|
// TODO what about if Eth drops and then comes back - we want to auto-switch?
|
||||||
// const esp_ip4_addr_t ip4 = {.addr = network_ip_};
|
// if (network_ip_ != 0 && !(WiFi.getMode() & WIFI_AP)) {
|
||||||
// LOG_DEBUG("Network already connected via IPv4: " IPSTR, IP2STR(&ip4));
|
// // for debugging only
|
||||||
return true;
|
// // const esp_ip4_addr_t ip4 = {.addr = network_ip_};
|
||||||
}
|
// // LOG_DEBUG("Network already connected via IPv4: " IPSTR, IP2STR(&ip4));
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
struct NetInfo {
|
struct NetInfo {
|
||||||
esp_ip4_addr_t ip;
|
esp_ip4_addr_t ip;
|
||||||
@@ -580,18 +624,17 @@ 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;
|
// 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 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;
|
||||||
@@ -612,8 +655,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,17 +666,20 @@ 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("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
|
#endif
|
||||||
return false; // no connection found yet
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// access point (soft-AP) and the captive portal
|
// access point (soft-AP) and the captive portal
|
||||||
|
|||||||
@@ -35,12 +35,12 @@
|
|||||||
|
|
||||||
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 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
|
// copied from Tasmota
|
||||||
#if CONFIG_IDF_TARGET_ESP32S2
|
#if CONFIG_IDF_TARGET_ESP32S2
|
||||||
@@ -79,9 +79,9 @@ namespace emsesp {
|
|||||||
// multiple ETH instances) -> ETHERNET. Anything else stays as NONE.
|
// multiple ETH instances) -> ETHERNET. Anything else stays as NONE.
|
||||||
enum class NetIface : uint8_t {
|
enum class NetIface : uint8_t {
|
||||||
NONE = 0,
|
NONE = 0,
|
||||||
WIFI,
|
WIFI, // 1
|
||||||
ETHERNET,
|
ETHERNET, // 2
|
||||||
AP,
|
AP, // 3
|
||||||
};
|
};
|
||||||
|
|
||||||
class Network {
|
class Network {
|
||||||
@@ -118,7 +118,7 @@ class Network {
|
|||||||
}
|
}
|
||||||
|
|
||||||
uint16_t getNetworkReconnects() {
|
uint16_t getNetworkReconnects() {
|
||||||
return connectcount_;
|
return reconnect_count_;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string getLocalIP() const;
|
std::string getLocalIP() const;
|
||||||
@@ -145,10 +145,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]);
|
||||||
@@ -165,7 +180,7 @@ 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;
|
||||||
@@ -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
|
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;
|
||||||
|
|
||||||
|
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
|
// Network and AP settings
|
||||||
bool enableMDNS_;
|
bool enableMDNS_;
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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");
|
||||||
|
|||||||
@@ -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,20 @@ 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) 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
|
// 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 +392,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 +431,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;
|
||||||
|
|||||||
@@ -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