From c022b8696b1f4f381fe57fd4f67e512573d513c9 Mon Sep 17 00:00:00 2001 From: Thomas Basler Date: Tue, 19 Jul 2022 21:53:04 +0200 Subject: [PATCH] Implemented serveal global network functions which are independent of the medium (wifi / ethernet) --- include/MqttSettings.h | 4 +- include/NetworkSettings.h | 46 +++++++ src/MqttHassPublishing.cpp | 2 +- src/MqttPublishing.cpp | 3 +- src/MqttSettings.cpp | 15 ++- src/NetworkSettings.cpp | 242 ++++++++++++++++++++++++++++++++++--- src/WebApi_network.cpp | 12 +- src/WebApi_sysstatus.cpp | 3 +- src/main.cpp | 2 +- 9 files changed, 290 insertions(+), 39 deletions(-) diff --git a/include/MqttSettings.h b/include/MqttSettings.h index 407380bb..5d17312b 100644 --- a/include/MqttSettings.h +++ b/include/MqttSettings.h @@ -1,10 +1,10 @@ // SPDX-License-Identifier: GPL-2.0-or-later #pragma once +#include "NetworkSettings.h" #include #include #include -#include #include class MqttSettingsClass { @@ -19,7 +19,7 @@ public: String getPrefix(); private: - void WiFiEvent(WiFiEvent_t event); + void NetworkEvent(network_event event); void onMqttDisconnect(AsyncMqttClientDisconnectReason reason); void onMqttConnect(bool sessionPresent); diff --git a/include/NetworkSettings.h b/include/NetworkSettings.h index 78165a1f..6c6fae8d 100644 --- a/include/NetworkSettings.h +++ b/include/NetworkSettings.h @@ -4,6 +4,37 @@ #include #include #include +#include + +enum class network_mode { + WiFi, + Ethernet, + Undefined +}; + +enum class network_event { + NETWORK_UNKNOWN, + NETWORK_START, + NETWORK_STOP, + NETWORK_CONNECTED, + NETWORK_DISCONNECTED, + NETWORK_GOT_IP, + NETWORK_LOST_IP, + NETWORK_EVENT_MAX +}; + +typedef std::function NetworkEventCb; + +typedef struct NetworkEventCbList { + NetworkEventCb cb; + network_event event; + + NetworkEventCbList() + : cb(NULL) + , event(network_event::NETWORK_UNKNOWN) + { + } +} NetworkEventCbList_t; class NetworkSettingsClass { public: @@ -14,6 +45,18 @@ public: void enableAdminMode(); String getApName(); + IPAddress localIP(); + IPAddress subnetMask(); + IPAddress gatewayIP(); + IPAddress dnsIP(uint8_t dns_no = 0); + String macAddress(); + const char* getHostname(); + bool isConnected(); + network_mode NetworkMode(); + + bool onEvent(NetworkEventCb cbEvent, network_event event = network_event::NETWORK_EVENT_MAX); + void raiseEvent(network_event event); + private: void setHostname(); void setStaticIp(); @@ -30,6 +73,9 @@ private: IPAddress apNetmask; std::unique_ptr dnsServer; bool dnsServerStatus = false; + network_mode _networkMode = network_mode::Undefined; + bool _ethConnected = false; + std::vector _cbEventList; }; extern NetworkSettingsClass NetworkSettings; \ No newline at end of file diff --git a/src/MqttHassPublishing.cpp b/src/MqttHassPublishing.cpp index adce730a..0a5cd79d 100644 --- a/src/MqttHassPublishing.cpp +++ b/src/MqttHassPublishing.cpp @@ -6,7 +6,7 @@ #include "ArduinoJson.h" #include "MqttPublishing.h" #include "MqttSettings.h" -#include "WiFiSettings.h" +#include "NetworkSettings.h" MqttHassPublishingClass MqttHassPublishing; diff --git a/src/MqttPublishing.cpp b/src/MqttPublishing.cpp index 768492c0..cb724b4b 100644 --- a/src/MqttPublishing.cpp +++ b/src/MqttPublishing.cpp @@ -4,6 +4,7 @@ */ #include "MqttPublishing.h" #include "MqttSettings.h" +#include "NetworkSettings.h" MqttPublishingClass MqttPublishing; @@ -21,7 +22,7 @@ void MqttPublishingClass::loop() if (millis() - _lastPublish > (config.Mqtt_PublishInterval * 1000)) { MqttSettings.publish("dtu/uptime", String(millis() / 1000)); - MqttSettings.publish("dtu/ip", WiFi.localIP().toString()); + MqttSettings.publish("dtu/ip", NetworkSettings.localIP().toString()); // Loop all inverters for (uint8_t i = 0; i < Hoymiles.getNumInverters(); i++) { diff --git a/src/MqttSettings.cpp b/src/MqttSettings.cpp index c5460885..242b068f 100644 --- a/src/MqttSettings.cpp +++ b/src/MqttSettings.cpp @@ -7,22 +7,21 @@ #include "NetworkSettings.h" #include #include -#include MqttSettingsClass::MqttSettingsClass() : mqttClient() { } -void MqttSettingsClass::WiFiEvent(WiFiEvent_t event) +void MqttSettingsClass::NetworkEvent(network_event event) { switch (event) { - case SYSTEM_EVENT_STA_GOT_IP: - Serial.println(F("WiFi connected")); + case network_event::NETWORK_GOT_IP: + Serial.println(F("Network connected")); performConnect(); break; - case SYSTEM_EVENT_STA_DISCONNECTED: - Serial.println(F("WiFi lost connection")); + case network_event::NETWORK_DISCONNECTED: + Serial.println(F("Network lost connection")); mqttReconnectTimer.detach(); // ensure we don't reconnect to MQTT while reconnecting to Wi-Fi break; } @@ -45,7 +44,7 @@ void MqttSettingsClass::onMqttDisconnect(AsyncMqttClientDisconnectReason reason) void MqttSettingsClass::performConnect() { - if (WiFi.isConnected() && Configuration.get().Mqtt_Enabled) { + if (NetworkSettings.isConnected() && Configuration.get().Mqtt_Enabled) { Serial.println(F("Connecting to MQTT...")); CONFIG_T& config = Configuration.get(); mqttClient.setServer(config.Mqtt_Hostname, config.Mqtt_Port); @@ -103,7 +102,7 @@ void MqttSettingsClass::publishHass(String subtopic, String payload) void MqttSettingsClass::init() { using namespace std::placeholders; - WiFi.onEvent(std::bind(&MqttSettingsClass::WiFiEvent, this, _1)); + NetworkSettings.onEvent(std::bind(&MqttSettingsClass::NetworkEvent, this, _1)); mqttClient.onConnect(std::bind(&MqttSettingsClass::onMqttConnect, this, _1)); mqttClient.onDisconnect(std::bind(&MqttSettingsClass::onMqttDisconnect, this, _1)); diff --git a/src/NetworkSettings.cpp b/src/NetworkSettings.cpp index 21551ddd..d2a39b7b 100644 --- a/src/NetworkSettings.cpp +++ b/src/NetworkSettings.cpp @@ -31,36 +31,83 @@ void NetworkSettingsClass::NetworkEvent(WiFiEvent_t event) #ifdef OPENDTU_ETHERNET case ARDUINO_EVENT_ETH_START: Serial.println("ETH start"); - ETH.setHostname("esp32-ethernet"); + if (_networkMode == network_mode::Ethernet) { + raiseEvent(network_event::NETWORK_START); + } break; case ARDUINO_EVENT_ETH_STOP: Serial.println("ETH stop"); + if (_networkMode == network_mode::Ethernet) { + raiseEvent(network_event::NETWORK_STOP); + } break; case ARDUINO_EVENT_ETH_CONNECTED: Serial.println("ETH connected"); - ETH.config(INADDR_NONE, INADDR_NONE, INADDR_NONE, INADDR_NONE); + _ethConnected = true; + raiseEvent(network_event::NETWORK_CONNECTED); break; case ARDUINO_EVENT_ETH_GOT_IP: Serial.printf("ETH got IP: %s\n", ETH.localIP().toString().c_str()); + if (_networkMode == network_mode::Ethernet) { + raiseEvent(network_event::NETWORK_GOT_IP); + } break; case ARDUINO_EVENT_ETH_DISCONNECTED: Serial.println("ETH disconnected"); + _ethConnected = false; + if (_networkMode == network_mode::Ethernet) { + raiseEvent(network_event::NETWORK_DISCONNECTED); + } break; #endif case ARDUINO_EVENT_WIFI_STA_CONNECTED: Serial.println("WiFi connected"); + if (_networkMode == network_mode::WiFi) { + raiseEvent(network_event::NETWORK_CONNECTED); + } break; case ARDUINO_EVENT_WIFI_STA_DISCONNECTED: Serial.println("WiFi disconnected"); + if (_networkMode == network_mode::WiFi) { + WiFi.reconnect(); + raiseEvent(network_event::NETWORK_DISCONNECTED); + } break; case ARDUINO_EVENT_WIFI_STA_GOT_IP: Serial.printf("WiFi got ip: %s\n", WiFi.localIP().toString().c_str()); + if (_networkMode == network_mode::WiFi) { + raiseEvent(network_event::NETWORK_GOT_IP); + } break; default: Serial.printf("Event: %d\n", event); } } +bool NetworkSettingsClass::onEvent(NetworkEventCb cbEvent, network_event event) +{ + if (!cbEvent) { + return pdFALSE; + } + NetworkEventCbList_t newEventHandler; + newEventHandler.cb = cbEvent; + newEventHandler.event = event; + _cbEventList.push_back(newEventHandler); + return true; +} + +void NetworkSettingsClass::raiseEvent(network_event event) +{ + for (uint32_t i = 0; i < _cbEventList.size(); i++) { + NetworkEventCbList_t entry = _cbEventList[i]; + if (entry.cb) { + if (entry.event == event || entry.event == network_event::NETWORK_EVENT_MAX) { + entry.cb(event); + } + } + } +} + void NetworkSettingsClass::setupMode() { if (adminEnabled) { @@ -74,7 +121,11 @@ void NetworkSettingsClass::setupMode() } else { dnsServer->stop(); dnsServerStatus = false; - WiFi.mode(WIFI_STA); + if (_networkMode == network_mode::WiFi) { + WiFi.mode(WIFI_STA); + } else { + WiFi.mode(WIFI_MODE_NULL); + } } #ifdef OPENDTU_ETHERNET ETH.begin(); @@ -99,6 +150,26 @@ String NetworkSettingsClass::getApName() void NetworkSettingsClass::loop() { +#ifdef OPENDTU_ETHERNET + if (_ethConnected) { + if (_networkMode != network_mode::Ethernet) { + // Do stuff when switching to Ethernet mode + Serial.println(F("Switch to Ethernet mode")); + _networkMode = network_mode::Ethernet; + WiFi.mode(WIFI_MODE_NULL); + setStaticIp(); + setHostname(); + } + } else +#endif + if (_networkMode != network_mode::WiFi) { + // Do stuff when switching to Ethernet mode + Serial.println(F("Switch to WiFi mode")); + _networkMode = network_mode::WiFi; + enableAdminMode(); + applyConfig(); + } + if (millis() - lastTimerCall > 1000) { adminTimeoutCounter++; connectTimeoutTimer++; @@ -106,8 +177,8 @@ void NetworkSettingsClass::loop() lastTimerCall = millis(); } if (adminEnabled) { - // Don't disable the admin mode when WiFi is not available - if (WiFi.status() != WL_CONNECTED) { + // Don't disable the admin mode when network is not available + if (!isConnected()) { adminTimeoutCounter = 0; } // If WiFi is connected to AP for more than ADMIN_TIMEOUT @@ -120,7 +191,7 @@ void NetworkSettingsClass::loop() // It's nearly not possible to use the internal AP if the // WiFi is searching for an AP. So disable searching afer // WIFI_RECONNECT_TIMEOUT and repeat after WIFI_RECONNECT_REDO_TIMEOUT - if (WiFi.status() == WL_CONNECTED) { + if (isConnected()) { connectTimeoutTimer = 0; connectRedoTimer = 0; } else { @@ -170,11 +241,22 @@ void NetworkSettingsClass::setHostname() { Serial.print(F("Setting Hostname... ")); if (strcmp(Configuration.get().WiFi_Hostname, "")) { - if (WiFi.hostname(Configuration.get().WiFi_Hostname)) { - Serial.println(F("done")); - } else { - Serial.println(F("failed")); + if (_networkMode == network_mode::WiFi) { + if (WiFi.hostname(Configuration.get().WiFi_Hostname)) { + Serial.println(F("done")); + } else { + Serial.println(F("failed")); + } } +#ifdef OPENDTU_ETHERNET + else if (_networkMode == network_mode::Ethernet) { + if (ETH.setHostname(Configuration.get().WiFi_Hostname)) { + Serial.println(F("done")); + } else { + Serial.println(F("failed")); + } + } +#endif } else { Serial.println(F("failed (Hostname empty)")); } @@ -182,16 +264,138 @@ void NetworkSettingsClass::setHostname() void NetworkSettingsClass::setStaticIp() { - if (!Configuration.get().WiFi_Dhcp) { - Serial.print(F("Configuring WiFi STA static IP... ")); - WiFi.config( - IPAddress(Configuration.get().WiFi_Ip), - IPAddress(Configuration.get().WiFi_Gateway), - IPAddress(Configuration.get().WiFi_Netmask), - IPAddress(Configuration.get().WiFi_Dns1), - IPAddress(Configuration.get().WiFi_Dns2)); - Serial.println(F("done")); + if (_networkMode == network_mode::WiFi) { + if (!Configuration.get().WiFi_Dhcp) { + Serial.print(F("Configuring WiFi STA static IP... ")); + WiFi.config( + IPAddress(Configuration.get().WiFi_Ip), + IPAddress(Configuration.get().WiFi_Gateway), + IPAddress(Configuration.get().WiFi_Netmask), + IPAddress(Configuration.get().WiFi_Dns1), + IPAddress(Configuration.get().WiFi_Dns2)); + Serial.println(F("done")); + } + } +#ifdef OPENDTU_ETHERNET + else if (_networkMode == network_mode::Ethernet) { + if (Configuration.get().WiFi_Dhcp) { + ETH.config(INADDR_NONE, INADDR_NONE, INADDR_NONE, INADDR_NONE); + } else { + Serial.print(F("Configuring Ethernet static IP... ")); + ETH.config( + IPAddress(Configuration.get().WiFi_Ip), + IPAddress(Configuration.get().WiFi_Gateway), + IPAddress(Configuration.get().WiFi_Netmask), + IPAddress(Configuration.get().WiFi_Dns1), + IPAddress(Configuration.get().WiFi_Dns2)); + Serial.println(F("done")); + } + } +#endif +} + +IPAddress NetworkSettingsClass::localIP() +{ + switch (_networkMode) { +#ifdef OPENDTU_ETHERNET + case network_mode::Ethernet: + return ETH.localIP(); + break; +#endif + case network_mode::WiFi: + return WiFi.localIP(); + break; + default: + return INADDR_NONE; } } +IPAddress NetworkSettingsClass::subnetMask() +{ + switch (_networkMode) { +#ifdef OPENDTU_ETHERNET + case network_mode::Ethernet: + return ETH.subnetMask(); + break; +#endif + case network_mode::WiFi: + return WiFi.subnetMask(); + break; + default: + return IPAddress(255, 255, 255, 0); + } +} + +IPAddress NetworkSettingsClass::gatewayIP() +{ + switch (_networkMode) { +#ifdef OPENDTU_ETHERNET + case network_mode::Ethernet: + return ETH.gatewayIP(); + break; +#endif + case network_mode::WiFi: + return WiFi.gatewayIP(); + break; + default: + return INADDR_NONE; + } +} + +IPAddress NetworkSettingsClass::dnsIP(uint8_t dns_no) +{ + switch (_networkMode) { +#ifdef OPENDTU_ETHERNET + case network_mode::Ethernet: + return ETH.dnsIP(dns_no); + break; +#endif + case network_mode::WiFi: + return WiFi.dnsIP(dns_no); + break; + default: + return INADDR_NONE; + } +} + +String NetworkSettingsClass::macAddress() +{ + switch (_networkMode) { +#ifdef OPENDTU_ETHERNET + case network_mode::Ethernet: + return ETH.macAddress(); + break; +#endif + case network_mode::WiFi: + return WiFi.macAddress(); + break; + default: + return String(""); + } +} + +const char* NetworkSettingsClass::getHostname() +{ +#ifdef OPENDTU_ETHERNET + if (_networkMode == network_mode::Ethernet) { + return ETH.getHostname(); + } +#endif + return WiFi.getHostname(); +} + +bool NetworkSettingsClass::isConnected() +{ +#ifndef OPENDTU_ETHERNET + return WiFi.localIP()[0] != 0; +#else + return WiFi.localIP()[0] != 0 || ETH.localIP()[0] != 0; +#endif +} + +network_mode NetworkSettingsClass::NetworkMode() +{ + return _networkMode; +} + NetworkSettingsClass NetworkSettings; \ No newline at end of file diff --git a/src/WebApi_network.cpp b/src/WebApi_network.cpp index 15dfe419..e5925f7d 100644 --- a/src/WebApi_network.cpp +++ b/src/WebApi_network.cpp @@ -31,12 +31,12 @@ void WebApiNetworkClass::onNetworkStatus(AsyncWebServerRequest* request) root[F("sta_status")] = ((WiFi.getMode() & WIFI_STA) != 0); root[F("sta_ssid")] = WiFi.SSID(); - root[F("sta_ip")] = WiFi.localIP().toString(); - root[F("sta_netmask")] = WiFi.subnetMask().toString(); - root[F("sta_gateway")] = WiFi.gatewayIP().toString(); - root[F("sta_dns1")] = WiFi.dnsIP(0).toString(); - root[F("sta_dns2")] = WiFi.dnsIP(1).toString(); - root[F("sta_mac")] = WiFi.macAddress(); + root[F("sta_ip")] = NetworkSettings.localIP().toString(); + root[F("sta_netmask")] = NetworkSettings.subnetMask().toString(); + root[F("sta_gateway")] = NetworkSettings.gatewayIP().toString(); + root[F("sta_dns1")] = NetworkSettings.dnsIP(0).toString(); + root[F("sta_dns2")] = NetworkSettings.dnsIP(1).toString(); + root[F("sta_mac")] = NetworkSettings.macAddress(); root[F("sta_rssi")] = WiFi.RSSI(); root[F("ap_status")] = ((WiFi.getMode() & WIFI_AP) != 0); root[F("ap_ssid")] = NetworkSettings.getApName(); diff --git a/src/WebApi_sysstatus.cpp b/src/WebApi_sysstatus.cpp index 1e6e4867..61acaf93 100644 --- a/src/WebApi_sysstatus.cpp +++ b/src/WebApi_sysstatus.cpp @@ -6,6 +6,7 @@ #include "ArduinoJson.h" #include "AsyncJson.h" #include "Configuration.h" +#include "NetworkSettings.h" #include #include @@ -31,7 +32,7 @@ void WebApiSysstatusClass::onSystemStatus(AsyncWebServerRequest* request) AsyncJsonResponse* response = new AsyncJsonResponse(); JsonObject root = response->getRoot(); - root[F("hostname")] = WiFi.getHostname(); + root[F("hostname")] = NetworkSettings.getHostname(); root[F("sdkversion")] = ESP.getSdkVersion(); root[F("cpufreq")] = ESP.getCpuFreqMHz(); diff --git a/src/main.cpp b/src/main.cpp index 11fe7578..5c69e2ef 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -54,7 +54,7 @@ void setup() Serial.println(F("done")); // Initialize WiFi - Serial.print(F("Initialize WiFi... ")); + Serial.print(F("Initialize Network... ")); NetworkSettings.init(); Serial.println(F("done")); NetworkSettings.applyConfig();