diff --git a/tasmota/settings.h b/tasmota/settings.h index b35f15ccc..25018bc84 100644 --- a/tasmota/settings.h +++ b/tasmota/settings.h @@ -744,10 +744,11 @@ typedef struct { uint8_t tcp_config; // F5F uint8_t light_step_pixels; // F60 - uint8_t free_f59[59]; // F61 - Decrement if adding new Setting variables just above and below + uint8_t free_f61[39]; // F61 - Decrement if adding new Setting variables just above and below // Only 32 bit boundary variables below + uint32_t eth_ipv4_address[5]; // F88 uint32_t energy_kWhtotal; // F9C SBitfield1 sbflag1; // FA0 TeleinfoCfg teleinfo; // FA4 @@ -834,11 +835,6 @@ struct XDRVMAILBOX { char *command; } XdrvMailbox; -#ifdef USE_SHUTTER -const uint8_t MAX_RULES_FLAG = 11; // Number of bits used in RulesBitfield (tricky I know...) -#else -const uint8_t MAX_RULES_FLAG = 9; // Number of bits used in RulesBitfield (tricky I know...) -#endif // USE_SHUTTER typedef union { // Restricted by MISRA-C Rule 18.4 but so useful... uint16_t data; // Allow bit manipulation struct { @@ -850,11 +846,11 @@ typedef union { // Restricted by MISRA-C Rule 18.4 bu uint16_t mqtt_disconnected : 1; uint16_t wifi_connected : 1; uint16_t wifi_disconnected : 1; + uint16_t eth_connected : 1; + uint16_t eth_disconnected : 1; uint16_t http_init : 1; uint16_t shutter_moved : 1; uint16_t shutter_moving : 1; - uint16_t spare11 : 1; - uint16_t spare12 : 1; uint16_t spare13 : 1; uint16_t spare14 : 1; uint16_t spare15 : 1; diff --git a/tasmota/xdrv_10_rules.ino b/tasmota/xdrv_10_rules.ino index c1dd8a64b..66fd23e41 100644 --- a/tasmota/xdrv_10_rules.ino +++ b/tasmota/xdrv_10_rules.ino @@ -960,32 +960,65 @@ void RulesEvery50ms(void) } } else if (TasmotaGlobal.rules_flag.data) { - uint16_t mask = 1; - for (uint32_t i = 0; i < MAX_RULES_FLAG; i++) { - if (TasmotaGlobal.rules_flag.data & mask) { - TasmotaGlobal.rules_flag.data ^= mask; - json_event[0] = '\0'; - switch (i) { - case 0: strncpy_P(json_event, PSTR("{\"System\":{\"Init\":1}}"), sizeof(json_event)); break; - case 1: strncpy_P(json_event, PSTR("{\"System\":{\"Boot\":1}}"), sizeof(json_event)); break; - case 2: snprintf_P(json_event, sizeof(json_event), PSTR("{\"Time\":{\"Initialized\":%d}}"), MinutesPastMidnight()); break; - case 3: snprintf_P(json_event, sizeof(json_event), PSTR("{\"Time\":{\"Set\":%d}}"), MinutesPastMidnight()); break; - case 4: strncpy_P(json_event, PSTR("{\"MQTT\":{\"Connected\":1}}"), sizeof(json_event)); break; - case 5: strncpy_P(json_event, PSTR("{\"MQTT\":{\"Disconnected\":1}}"), sizeof(json_event)); break; - case 6: strncpy_P(json_event, PSTR("{\"WIFI\":{\"Connected\":1}}"), sizeof(json_event)); break; - case 7: strncpy_P(json_event, PSTR("{\"WIFI\":{\"Disconnected\":1}}"), sizeof(json_event)); break; - case 8: strncpy_P(json_event, PSTR("{\"HTTP\":{\"Initialized\":1}}"), sizeof(json_event)); break; + json_event[0] = '\0'; + if (TasmotaGlobal.rules_flag.system_init) { + TasmotaGlobal.rules_flag.system_init = 0; + strncpy_P(json_event, PSTR("{\"System\":{\"Init\":1}}"), sizeof(json_event)); + } + else if (TasmotaGlobal.rules_flag.system_boot) { + TasmotaGlobal.rules_flag.system_boot = 0; + strncpy_P(json_event, PSTR("{\"System\":{\"Boot\":1}}"), sizeof(json_event)); + } + else if (TasmotaGlobal.rules_flag.time_init) { + TasmotaGlobal.rules_flag.time_init = 0; + snprintf_P(json_event, sizeof(json_event), PSTR("{\"Time\":{\"Initialized\":%d}}"), MinutesPastMidnight()); + } + else if (TasmotaGlobal.rules_flag.time_set) { + TasmotaGlobal.rules_flag.time_set = 0; + snprintf_P(json_event, sizeof(json_event), PSTR("{\"Time\":{\"Set\":%d}}"), MinutesPastMidnight()); + } + else if (TasmotaGlobal.rules_flag.mqtt_connected) { + TasmotaGlobal.rules_flag.mqtt_connected = 0; + strncpy_P(json_event, PSTR("{\"MQTT\":{\"Connected\":1}}"), sizeof(json_event)); + } + else if (TasmotaGlobal.rules_flag.mqtt_disconnected) { + TasmotaGlobal.rules_flag.mqtt_disconnected = 0; + strncpy_P(json_event, PSTR("{\"MQTT\":{\"Disconnected\":1}}"), sizeof(json_event)); + } + else if (TasmotaGlobal.rules_flag.wifi_connected) { + TasmotaGlobal.rules_flag.wifi_connected = 0; + strncpy_P(json_event, PSTR("{\"WIFI\":{\"Connected\":1}}"), sizeof(json_event)); + } + else if (TasmotaGlobal.rules_flag.wifi_disconnected) { + TasmotaGlobal.rules_flag.wifi_disconnected = 0; + strncpy_P(json_event, PSTR("{\"WIFI\":{\"Disconnected\":1}}"), sizeof(json_event)); + } +#if defined(ESP32) && CONFIG_IDF_TARGET_ESP32 && defined(USE_ETHERNET) + else if (TasmotaGlobal.rules_flag.eth_connected) { + TasmotaGlobal.rules_flag.eth_connected = 0; + strncpy_P(json_event, PSTR("{\"ETH\":{\"Connected\":1}}"), sizeof(json_event)); + } + else if (TasmotaGlobal.rules_flag.eth_disconnected) { + TasmotaGlobal.rules_flag.eth_disconnected = 0; + strncpy_P(json_event, PSTR("{\"ETH\":{\"Disconnected\":1}}"), sizeof(json_event)); + } +#endif + else if (TasmotaGlobal.rules_flag.http_init) { + TasmotaGlobal.rules_flag.http_init = 0; + strncpy_P(json_event, PSTR("{\"HTTP\":{\"Initialized\":1}}"), sizeof(json_event)); + } #ifdef USE_SHUTTER - case 9: strncpy_P(json_event, PSTR("{\"SHUTTER\":{\"Moved\":1}}"), sizeof(json_event)); break; - case 10: strncpy_P(json_event, PSTR("{\"SHUTTER\":{\"Moving\":1}}"), sizeof(json_event)); break; + else if (TasmotaGlobal.rules_flag.shutter_moved) { + TasmotaGlobal.rules_flag.shutter_moved = 0; + strncpy_P(json_event, PSTR("{\"SHUTTER\":{\"Moved\":1}}"), sizeof(json_event)); + } + else if (TasmotaGlobal.rules_flag.shutter_moving) { + TasmotaGlobal.rules_flag.shutter_moving = 0; + strncpy_P(json_event, PSTR("{\"SHUTTER\":{\"Moving\":1}}"), sizeof(json_event)); + } #endif // USE_SHUTTER - } - if (json_event[0]) { - RulesProcessEvent(json_event); - break; // Only service one event within 50mS - } - } - mask <<= 1; + if (json_event[0]) { + RulesProcessEvent(json_event); // Only service one event within 50mS } } } diff --git a/tasmota/xdrv_82_esp32_ethernet.ino b/tasmota/xdrv_82_esp32_ethernet.ino index 1deb646b5..d64d0a023 100644 --- a/tasmota/xdrv_82_esp32_ethernet.ino +++ b/tasmota/xdrv_82_esp32_ethernet.ino @@ -83,6 +83,7 @@ #include char eth_hostname[sizeof(TasmotaGlobal.hostname)]; +uint8_t eth_config_change; void EthernetEvent(WiFiEvent_t event) { switch (event) { @@ -97,14 +98,16 @@ void EthernetEvent(WiFiEvent_t event) { case ARDUINO_EVENT_ETH_GOT_IP: AddLog(LOG_LEVEL_DEBUG, PSTR("ETH: Mac %s, IPAddress %_I, Hostname %s"), ETH.macAddress().c_str(), (uint32_t)ETH.localIP(), eth_hostname); - Settings->ipv4_address[1] = (uint32_t)ETH.gatewayIP(); - Settings->ipv4_address[2] = (uint32_t)ETH.subnetMask(); - Settings->ipv4_address[3] = (uint32_t)ETH.dnsIP(); - Settings->ipv4_address[4] = (uint32_t)ETH.dnsIP(1); + Settings->eth_ipv4_address[1] = (uint32_t)ETH.gatewayIP(); + Settings->eth_ipv4_address[2] = (uint32_t)ETH.subnetMask(); + Settings->eth_ipv4_address[3] = (uint32_t)ETH.dnsIP(); + Settings->eth_ipv4_address[4] = (uint32_t)ETH.dnsIP(1); + TasmotaGlobal.rules_flag.eth_connected = 1; TasmotaGlobal.global_state.eth_down = 0; break; case ARDUINO_EVENT_ETH_DISCONNECTED: AddLog(LOG_LEVEL_INFO, PSTR("ETH: Disconnected")); + TasmotaGlobal.rules_flag.eth_disconnected = 1; TasmotaGlobal.global_state.eth_down = 1; break; case ARDUINO_EVENT_ETH_STOP: @@ -116,6 +119,11 @@ void EthernetEvent(WiFiEvent_t event) { } } +void EthernetSetIp(void) { + // IPAddress local_ip, IPAddress gateway, IPAddress subnet, IPAddress dns1, IPAddress dns2 + ETH.config(Settings->eth_ipv4_address[0], Settings->eth_ipv4_address[1], Settings->eth_ipv4_address[2], Settings->eth_ipv4_address[3], Settings->eth_ipv4_address[4]); // Set static IP +} + void EthernetInit(void) { if (!Settings->flag4.network_ethernet) { return; } if (!PinUsed(GPIO_ETH_PHY_MDC) && !PinUsed(GPIO_ETH_PHY_MDIO)) { @@ -123,6 +131,8 @@ void EthernetInit(void) { return; } + eth_config_change = 0; + if (WT32_ETH01 == TasmotaGlobal.module_type) { Settings->eth_address = 1; // EthAddress Settings->eth_type = ETH_PHY_LAN8720; // EthType @@ -140,7 +150,12 @@ void EthernetInit(void) { int eth_mdio = Pin(GPIO_ETH_PHY_MDIO); if (!ETH.begin(Settings->eth_address, eth_power, eth_mdc, eth_mdio, (eth_phy_type_t)Settings->eth_type, (eth_clock_mode_t)Settings->eth_clk_mode)) { AddLog(LOG_LEVEL_DEBUG, PSTR("ETH: Bad PHY type or init error")); + return; }; + + if (Settings->eth_ipv4_address[0]) { + EthernetSetIp(); // Set static IP + } } IPAddress EthernetLocalIP(void) { @@ -155,22 +170,38 @@ String EthernetMacAddress(void) { return ETH.macAddress(); } +void EthernetConfigChange(void) { + if (eth_config_change) { + eth_config_change--; + if (!eth_config_change) { + EthernetSetIp(); + } + } +} + /*********************************************************************************************\ * Commands \*********************************************************************************************/ -#define D_CMND_ETHADDRESS "EthAddress" -#define D_CMND_ETHTYPE "EthType" -#define D_CMND_ETHCLOCKMODE "EthClockMode" +#define D_CMND_ETHADDRESS "Address" +#define D_CMND_ETHTYPE "Type" +#define D_CMND_ETHCLOCKMODE "ClockMode" +#define D_CMND_ETHIPADDRESS D_CMND_IPADDRESS +#define D_CMND_ETHGATEWAY D_JSON_GATEWAY +#define D_CMND_ETHNETMASK D_JSON_SUBNETMASK +#define D_CMND_ETHDNS D_JSON_DNSSERVER -const char kEthernetCommands[] PROGMEM = "|" // No prefix - D_CMND_ETHERNET "|" D_CMND_ETHADDRESS "|" D_CMND_ETHTYPE "|" D_CMND_ETHCLOCKMODE; +const char kEthernetCommands[] PROGMEM = "Eth|" // Prefix + "ernet|" D_CMND_ETHADDRESS "|" D_CMND_ETHTYPE "|" D_CMND_ETHCLOCKMODE "|" + D_CMND_ETHIPADDRESS "|" D_CMND_ETHGATEWAY "|" D_CMND_ETHNETMASK "|" D_CMND_ETHDNS "|Ipconfig" ; void (* const EthernetCommand[])(void) PROGMEM = { - &CmndEthernet, &CmndEthAddress, &CmndEthType, &CmndEthClockMode }; + &CmndEthernet, &CmndEthAddress, &CmndEthType, &CmndEthClockMode, + &CmndEthSetIpConfig, &CmndEthSetIpConfig, &CmndEthSetIpConfig, &CmndEthSetIpConfig, &CmndEthIpConfig }; -void CmndEthernet(void) -{ +#define ETH_PARAM_OFFSET 4 // Offset of command index in above table of first CmndEthIpConfig + +void CmndEthernet(void) { if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 1)) { Settings->flag4.network_ethernet = XdrvMailbox.payload; TasmotaGlobal.restart_flag = 2; @@ -178,8 +209,7 @@ void CmndEthernet(void) ResponseCmndStateText(Settings->flag4.network_ethernet); } -void CmndEthAddress(void) -{ +void CmndEthAddress(void) { if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 31)) { Settings->eth_address = XdrvMailbox.payload; TasmotaGlobal.restart_flag = 2; @@ -187,8 +217,7 @@ void CmndEthAddress(void) ResponseCmndNumber(Settings->eth_address); } -void CmndEthType(void) -{ +void CmndEthType(void) { if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 2)) { Settings->eth_type = XdrvMailbox.payload; TasmotaGlobal.restart_flag = 2; @@ -196,8 +225,7 @@ void CmndEthType(void) ResponseCmndNumber(Settings->eth_type); } -void CmndEthClockMode(void) -{ +void CmndEthClockMode(void) { if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 3)) { Settings->eth_clk_mode = XdrvMailbox.payload; TasmotaGlobal.restart_flag = 2; @@ -205,6 +233,47 @@ void CmndEthClockMode(void) ResponseCmndNumber(Settings->eth_clk_mode); } +void CmndEthSetIpConfig(void) { + uint32_t param_id = XdrvMailbox.command_code -ETH_PARAM_OFFSET; + + char cmnd_idx[2] = { 0 }; + if (3 == param_id) { // EthDnsServer + if ((XdrvMailbox.index < 1) || (XdrvMailbox.index > 2)) { + XdrvMailbox.index = 1; + } + cmnd_idx[0] = '0' + XdrvMailbox.index; + param_id += XdrvMailbox.index -1; // EthDnsServer2 + } + + if (XdrvMailbox.data_len) { + uint32_t ipv4_address; + if (ParseIPv4(&ipv4_address, XdrvMailbox.data)) { + Settings->eth_ipv4_address[param_id] = ipv4_address; + eth_config_change = 2; + } + } + + char network_address[22] = { 0 }; + if (0 == param_id) { + if (!Settings->eth_ipv4_address[0]) { + ext_snprintf_P(network_address, sizeof(network_address), PSTR(" (%_I)"), (uint32_t)ETH.localIP()); + } + } + Response_P(PSTR("{\"%s%s\":\"%_I%s\"}"), XdrvMailbox.command, cmnd_idx, Settings->eth_ipv4_address[param_id], network_address); +} + +void CmndEthIpConfig(void) { + char network_address[22] = { 0 }; + if (!Settings->eth_ipv4_address[0]) { + ext_snprintf_P(network_address, sizeof(network_address), PSTR(" (%_I)"), (uint32_t)ETH.localIP()); + } + Response_P(PSTR("{\"EthIpAddress\":\"%_I%s\""), Settings->eth_ipv4_address[0], network_address); + ResponseAppend_P(PSTR(",\"EthGateway\":\"%_I\""), Settings->eth_ipv4_address[1]); + ResponseAppend_P(PSTR(",\"EthSubNetmask\":\"%_I\""), Settings->eth_ipv4_address[2]); + ResponseAppend_P(PSTR(",\"EthDnsServer1\":\"%_I\""), Settings->eth_ipv4_address[3]); + ResponseAppend_P(PSTR(",\"EthDnsServer2\":\"%_I\"}"), Settings->eth_ipv4_address[4]); +} + /*********************************************************************************************\ * Interface \*********************************************************************************************/ @@ -213,6 +282,9 @@ bool Xdrv82(uint8_t function) { bool result = false; switch (function) { + case FUNC_EVERY_SECOND: + EthernetConfigChange(); + break; case FUNC_COMMAND: result = DecodeCommand(kEthernetCommands, EthernetCommand); break;