mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2026-09-13 13:44:10 +00:00
power save - Reduce EMS-ESP energy consumption #3213
This commit is contained in:
@@ -129,6 +129,7 @@
|
|||||||
"eth_power": 15,
|
"eth_power": 15,
|
||||||
"eth_phy_addr": 0,
|
"eth_phy_addr": 0,
|
||||||
"eth_clock_mode": 1,
|
"eth_clock_mode": 1,
|
||||||
|
"eth_10mbit": false,
|
||||||
"modbus_enabled": false,
|
"modbus_enabled": false,
|
||||||
"modbus_port": 502,
|
"modbus_port": 502,
|
||||||
"modbus_max_clients": 10,
|
"modbus_max_clients": 10,
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ export interface Settings {
|
|||||||
eth_power: number;
|
eth_power: number;
|
||||||
eth_phy_addr: number;
|
eth_phy_addr: number;
|
||||||
eth_clock_mode: number;
|
eth_clock_mode: number;
|
||||||
|
eth_10mbit: boolean;
|
||||||
platform: string;
|
platform: string;
|
||||||
modbus_enabled: boolean;
|
modbus_enabled: boolean;
|
||||||
modbus_port: number;
|
modbus_port: number;
|
||||||
|
|||||||
@@ -820,6 +820,18 @@ const ApplicationSettings = () => {
|
|||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
)}
|
)}
|
||||||
|
{data.phy_type !== 0 && (
|
||||||
|
<BlockFormControlLabel
|
||||||
|
control={
|
||||||
|
<Checkbox
|
||||||
|
checked={data.eth_10mbit}
|
||||||
|
onChange={updateFormValue}
|
||||||
|
name="eth_10mbit"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
label="Force 10Mbit half-duplex"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<Grid container spacing={2} rowSpacing={0}>
|
<Grid container spacing={2} rowSpacing={0}>
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ let settings = {
|
|||||||
eth_power: 15,
|
eth_power: 15,
|
||||||
eth_phy_addr: 0,
|
eth_phy_addr: 0,
|
||||||
eth_clock_mode: 1,
|
eth_clock_mode: 1,
|
||||||
|
eth_10mbit: false,
|
||||||
led_type: 0,
|
led_type: 0,
|
||||||
platform: 'ESP32',
|
platform: 'ESP32',
|
||||||
modbus_enabled: false,
|
modbus_enabled: false,
|
||||||
|
|||||||
@@ -1346,3 +1346,5 @@ eraseap
|
|||||||
roomtempoffset
|
roomtempoffset
|
||||||
dhcpc
|
dhcpc
|
||||||
wifioff
|
wifioff
|
||||||
|
autonegotiation
|
||||||
|
Mbit
|
||||||
@@ -28,4 +28,7 @@ void setup() {
|
|||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
application.loop();
|
application.loop();
|
||||||
|
#ifndef EMSESP_STANDALONE
|
||||||
|
delay(1); // block for a tick so the idle task gets to run and the core can clock-gate
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|||||||
+95
-31
@@ -57,6 +57,7 @@ void Network::begin() {
|
|||||||
eth_power_ = settings.eth_power;
|
eth_power_ = settings.eth_power;
|
||||||
eth_phy_addr_ = settings.eth_phy_addr;
|
eth_phy_addr_ = settings.eth_phy_addr;
|
||||||
eth_clock_mode_ = settings.eth_clock_mode;
|
eth_clock_mode_ = settings.eth_clock_mode;
|
||||||
|
eth_10mbit_ = settings.eth_10mbit;
|
||||||
});
|
});
|
||||||
|
|
||||||
// get Access Point settings
|
// get Access Point settings
|
||||||
@@ -78,39 +79,16 @@ void Network::begin() {
|
|||||||
|
|
||||||
phase_ = initialPhase();
|
phase_ = initialPhase();
|
||||||
|
|
||||||
// Initialise WiFi once when the Network service starts.
|
startWiFiRadio();
|
||||||
// persistent(false) keeps credentials in RAM so we don't auto-join leftover NVS config.
|
|
||||||
WiFi.persistent(false);
|
|
||||||
WiFi.setAutoReconnect(false);
|
|
||||||
WiFi.mode(WIFI_STA);
|
|
||||||
|
|
||||||
// Keep the radio up. disconnect(wifioff=true) maps to STA.end() -> esp_wifi_stop()
|
#if CONFIG_IDF_TARGET_ESP32
|
||||||
if (WiFi.STA.started()) {
|
// Ethernet is switched off but the board still has a PHY wired up. Hold its power/oscillator
|
||||||
WiFi.disconnect(false, true); // drop leftover association, wipe RAM config
|
// enable low so an unused LAN8720 isn't left clocked and burning current for the whole uptime.
|
||||||
|
if (phy_type_ == PHY_type::PHY_TYPE_NONE && eth_power_ != -1) {
|
||||||
|
pinMode(eth_power_, OUTPUT);
|
||||||
|
digitalWrite(eth_power_, LOW);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
WiFi.setScanMethod(WIFI_ALL_CHANNEL_SCAN);
|
|
||||||
WiFi.setHostname(hostname_.c_str()); // updates shared default_hostname buffer
|
|
||||||
WiFi.enableSTA(true); // no-op if already STA; recreates netif after a full off
|
|
||||||
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
|
|
||||||
// WiFi.setScanMethod(WIFI_ALL_CHANNEL_SCAN); // default is FAST_SCAN
|
|
||||||
// WiFi.setSortMethod(WIFI_CONNECT_AP_BY_SIGNAL); // is default, no need to set
|
|
||||||
|
|
||||||
// avoid duplicate registration, so register the lambdas only once across the lifetime of this Network instance
|
// avoid duplicate registration, so register the lambdas only once across the lifetime of this Network instance
|
||||||
if (!wifi_events_registered_) {
|
if (!wifi_events_registered_) {
|
||||||
@@ -326,6 +304,10 @@ void Network::checkConnection() {
|
|||||||
network_iface_ = NetIface::NONE;
|
network_iface_ = NetIface::NONE;
|
||||||
if (lost_iface == NetIface::ETHERNET) {
|
if (lost_iface == NetIface::ETHERNET) {
|
||||||
LOG_WARNING("Ethernet connection lost");
|
LOG_WARNING("Ethernet connection lost");
|
||||||
|
// restore the radio now so the WiFi and AP fallbacks have something to work with
|
||||||
|
if (wifi_radio_off_) {
|
||||||
|
startWiFiRadio();
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
begin();
|
begin();
|
||||||
@@ -502,6 +484,59 @@ const char * Network::disconnectReason([[maybe_unused]] uint8_t code) {
|
|||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Bring up the STA interface and apply all the radio-level settings to it
|
||||||
|
// Safe to call repeatedly - enableSTA() is a no-op when the STA is already running, and recreates the netif after stopWiFiRadio() has torn it down
|
||||||
|
void Network::startWiFiRadio() {
|
||||||
|
#ifndef EMSESP_STANDALONE
|
||||||
|
WiFi.persistent(false); // keep credentials in RAM so we don't auto-join leftover NVS config
|
||||||
|
WiFi.setAutoReconnect(false);
|
||||||
|
WiFi.mode(WIFI_STA);
|
||||||
|
|
||||||
|
// Keep the radio up. disconnect(wifioff=true) maps to STA.end() -> esp_wifi_stop()
|
||||||
|
if (WiFi.STA.started()) {
|
||||||
|
WiFi.disconnect(false, true); // drop leftover association, wipe RAM config
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.setHostname(hostname_.c_str()); // updates shared default_hostname buffer
|
||||||
|
WiFi.enableSTA(true); // no-op if already STA; recreates netif after a full off
|
||||||
|
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
|
||||||
|
} else {
|
||||||
|
WiFi.setSleep(true); // WIFI_PS_MIN_MODEM
|
||||||
|
}
|
||||||
|
|
||||||
|
wifi_radio_off_ = false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// Power down the WiFi radio completely (esp_wifi_stop), used when Ethernet is carrying the traffic.
|
||||||
|
// An idle STA still keeps the receiver powered, which on an Ethernet gateway is a pure waste of heat.
|
||||||
|
void Network::stopWiFiRadio() {
|
||||||
|
#ifndef EMSESP_STANDALONE
|
||||||
|
if (wifi_radio_off_) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
WiFi.mode(WIFI_OFF);
|
||||||
|
wifi_radio_off_ = true;
|
||||||
|
wifi_connect_pending_ = false;
|
||||||
|
LOG_INFO("Powering down WiFi radio, Ethernet is active");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
// WiFi management
|
// WiFi management
|
||||||
void Network::startWIFI() {
|
void Network::startWIFI() {
|
||||||
#ifndef EMSESP_STANDALONE
|
#ifndef EMSESP_STANDALONE
|
||||||
@@ -510,6 +545,11 @@ void Network::startWIFI() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// the radio was powered down while Ethernet was up, so bring it back before trying to associate
|
||||||
|
if (wifi_radio_off_) {
|
||||||
|
startWiFiRadio();
|
||||||
|
}
|
||||||
|
|
||||||
// exit if WiFi or Ethernet is already connected, or if we have no SSID or another Wifi.begin() is already in progress
|
// exit if WiFi or Ethernet is already connected, or if we have no SSID or another Wifi.begin() is already in progress
|
||||||
if (WiFi.isConnected() || ssid_.isEmpty() || ethernet_connected() || wifi_connect_pending_) {
|
if (WiFi.isConnected() || ssid_.isEmpty() || ethernet_connected() || wifi_connect_pending_) {
|
||||||
return;
|
return;
|
||||||
@@ -610,6 +650,17 @@ void Network::startEthernet() {
|
|||||||
eth_hostname_handler_registered_ = true;
|
eth_hostname_handler_registered_ = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Forcing 10BASE-T roughly halves the PHY's power draw, which on these boards is usually the
|
||||||
|
// hottest component. All three must be set before ETH.begin(), and in this order - the speed
|
||||||
|
// and duplex setters are ignored while autonegotiation is still on. Half duplex is deliberate:
|
||||||
|
// a switch port that can't negotiate falls back to parallel detection, which is always half,
|
||||||
|
// so forcing full here would be a duplex mismatch. Opt-in only for that reason.
|
||||||
|
if (eth_10mbit_) {
|
||||||
|
ETH.setAutoNegotiation(false);
|
||||||
|
ETH.setLinkSpeed(10);
|
||||||
|
ETH.setFullDuplex(false);
|
||||||
|
}
|
||||||
|
|
||||||
if (ETH.begin(type, eth_phy_addr_, 23, 18, eth_power_, (eth_clock_mode_t)eth_clock_mode_)) {
|
if (ETH.begin(type, eth_phy_addr_, 23, 18, eth_power_, (eth_clock_mode_t)eth_clock_mode_)) {
|
||||||
// LOG_DEBUG("Ethernet module found - initialising");
|
// LOG_DEBUG("Ethernet module found - initialising");
|
||||||
ethernet_started_ = true;
|
ethernet_started_ = true;
|
||||||
@@ -739,6 +790,14 @@ void Network::findNetworks() {
|
|||||||
stopAP();
|
stopAP();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ethernet is carrying the traffic so the STA is dead weight - shut the radio down.
|
||||||
|
// It is brought back by checkConnection() when the link drops, or by startWIFI() if we
|
||||||
|
// fall through to the WiFi phase. Do this before startmDNS() so we don't register on a
|
||||||
|
// netif we are about to destroy.
|
||||||
|
if (network_iface_ == NetIface::ETHERNET) {
|
||||||
|
stopWiFiRadio();
|
||||||
|
}
|
||||||
|
|
||||||
// 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;
|
||||||
@@ -823,6 +882,11 @@ void Network::startAP() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// the AP needs the radio, which may have been powered down while Ethernet was up
|
||||||
|
if (wifi_radio_off_) {
|
||||||
|
startWiFiRadio();
|
||||||
|
}
|
||||||
|
|
||||||
if (!WiFi.softAPConfig(ap_localIP_, ap_gatewayIP_, ap_subnetMask_)) {
|
if (!WiFi.softAPConfig(ap_localIP_, ap_gatewayIP_, ap_subnetMask_)) {
|
||||||
LOG_DEBUG("softAPConfig failed");
|
LOG_DEBUG("softAPConfig failed");
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -186,6 +186,8 @@ class Network {
|
|||||||
void startAP();
|
void startAP();
|
||||||
void startWIFI();
|
void startWIFI();
|
||||||
void startEthernet();
|
void startEthernet();
|
||||||
|
void startWiFiRadio();
|
||||||
|
void stopWiFiRadio();
|
||||||
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();
|
||||||
@@ -216,6 +218,7 @@ class Network {
|
|||||||
bool ethernet_started_ = false; // ETH.begin() succeeded; the driver runs for the lifetime of the boot
|
bool ethernet_started_ = false; // ETH.begin() succeeded; the driver runs for the lifetime of the boot
|
||||||
bool wifi_ever_connected_ = false; // set true once we've successfully obtained an IP
|
bool wifi_ever_connected_ = false; // set true once we've successfully obtained an IP
|
||||||
bool ethernet_ever_connected_ = false; // set true once we've successfully obtained an IP
|
bool ethernet_ever_connected_ = false; // set true once we've successfully obtained an IP
|
||||||
|
bool wifi_radio_off_ = false; // STA powered down because Ethernet is carrying the traffic
|
||||||
|
|
||||||
// Network and AP settings
|
// Network and AP settings
|
||||||
bool enableMDNS_;
|
bool enableMDNS_;
|
||||||
@@ -236,6 +239,7 @@ class Network {
|
|||||||
int8_t eth_power_;
|
int8_t eth_power_;
|
||||||
uint8_t eth_phy_addr_;
|
uint8_t eth_phy_addr_;
|
||||||
uint8_t eth_clock_mode_;
|
uint8_t eth_clock_mode_;
|
||||||
|
bool eth_10mbit_;
|
||||||
|
|
||||||
// AP settings
|
// AP settings
|
||||||
uint8_t ap_provisionMode_;
|
uint8_t ap_provisionMode_;
|
||||||
|
|||||||
@@ -79,6 +79,7 @@ void WebSettings::read(WebSettings & settings, JsonObject root) {
|
|||||||
root["eth_power"] = settings.eth_power;
|
root["eth_power"] = settings.eth_power;
|
||||||
root["eth_phy_addr"] = settings.eth_phy_addr;
|
root["eth_phy_addr"] = settings.eth_phy_addr;
|
||||||
root["eth_clock_mode"] = settings.eth_clock_mode;
|
root["eth_clock_mode"] = settings.eth_clock_mode;
|
||||||
|
root["eth_10mbit"] = settings.eth_10mbit;
|
||||||
root["modbus_enabled"] = settings.modbus_enabled;
|
root["modbus_enabled"] = settings.modbus_enabled;
|
||||||
root["modbus_port"] = settings.modbus_port;
|
root["modbus_port"] = settings.modbus_port;
|
||||||
root["modbus_max_clients"] = settings.modbus_max_clients;
|
root["modbus_max_clients"] = settings.modbus_max_clients;
|
||||||
@@ -120,6 +121,7 @@ StateUpdateResult WebSettings::update(JsonObject root, WebSettings & settings) {
|
|||||||
settings.eth_power = root["eth_power"];
|
settings.eth_power = root["eth_power"];
|
||||||
settings.eth_phy_addr = root["eth_phy_addr"];
|
settings.eth_phy_addr = root["eth_phy_addr"];
|
||||||
settings.eth_clock_mode = root["eth_clock_mode"];
|
settings.eth_clock_mode = root["eth_clock_mode"];
|
||||||
|
settings.eth_10mbit = root["eth_10mbit"];
|
||||||
settings.led_type = root["led_type"]; // 1 = RGB-LED
|
settings.led_type = root["led_type"]; // 1 = RGB-LED
|
||||||
|
|
||||||
reset_flags();
|
reset_flags();
|
||||||
@@ -145,6 +147,7 @@ StateUpdateResult WebSettings::update(JsonObject root, WebSettings & settings) {
|
|||||||
if (settings.phy_type != PHY_type::PHY_TYPE_NONE) {
|
if (settings.phy_type != PHY_type::PHY_TYPE_NONE) {
|
||||||
check_flag(original_settings.eth_power, settings.eth_power, ChangeFlags::RESTART);
|
check_flag(original_settings.eth_power, settings.eth_power, ChangeFlags::RESTART);
|
||||||
check_flag(original_settings.eth_clock_mode, settings.eth_clock_mode, ChangeFlags::RESTART);
|
check_flag(original_settings.eth_clock_mode, settings.eth_clock_mode, ChangeFlags::RESTART);
|
||||||
|
check_flag(original_settings.eth_10mbit, settings.eth_10mbit, ChangeFlags::RESTART);
|
||||||
if (settings.eth_power != -1) { // Ethernet Power -1 means disabled
|
if (settings.eth_power != -1) { // Ethernet Power -1 means disabled
|
||||||
EMSESP::system_.remove_gpio(settings.eth_power, true);
|
EMSESP::system_.remove_gpio(settings.eth_power, true);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -119,6 +119,7 @@ class WebSettings {
|
|||||||
int8_t eth_power; // -1 means disabled
|
int8_t eth_power; // -1 means disabled
|
||||||
uint8_t eth_phy_addr;
|
uint8_t eth_phy_addr;
|
||||||
uint8_t eth_clock_mode;
|
uint8_t eth_clock_mode;
|
||||||
|
bool eth_10mbit; // force 10BASE-T half-duplex, no auto-negotiation
|
||||||
|
|
||||||
bool developer_mode; // developer mode
|
bool developer_mode; // developer mode
|
||||||
bool disable_reset; // disable reset
|
bool disable_reset; // disable reset
|
||||||
|
|||||||
Reference in New Issue
Block a user