diff --git a/src/appInterface.h b/src/appInterface.h index af28c968..fef97acb 100644 --- a/src/appInterface.h +++ b/src/appInterface.h @@ -8,9 +8,11 @@ #include "defines.h" #include "hm/hmSystem.h" -#if !defined(ETHERNET) +#if defined(ETHERNET) +#include "AsyncWebServer_ESP32_W5500.h" +#else #include "ESPAsyncWebServer.h" -#endif /* defined(ETHERNET) */ +#endif // abstract interface to App. Make members of App accessible from child class // like web or API without forward declaration @@ -20,10 +22,10 @@ class IApp { virtual bool saveSettings(bool stopFs) = 0; virtual bool readSettings(const char *path) = 0; virtual bool eraseSettings(bool eraseWifi) = 0; - #if !defined(ETHERNET) virtual bool getSavePending() = 0; virtual bool getLastSaveSucceed() = 0; virtual bool getShouldReboot() = 0; + #if !defined(ETHERNET) virtual void setOnUpdate() = 0; #endif /* defined(ETHERNET) */ virtual void setRebootFlag() = 0; diff --git a/src/eth/ahoyeth.cpp b/src/eth/ahoyeth.cpp new file mode 100644 index 00000000..bcdf6a19 --- /dev/null +++ b/src/eth/ahoyeth.cpp @@ -0,0 +1,243 @@ +//----------------------------------------------------------------------------- +// 2023 Ahoy, https://www.mikrocontroller.net/topic/525778 +// Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/ +//----------------------------------------------------------------------------- + +#if defined(ETHERNET) + +#if defined(ESP32) && defined(F) + #undef F + #define F(sl) (sl) +#endif +#include "ahoyeth.h" + + +//----------------------------------------------------------------------------- +ahoyeth::ahoyeth() +{ + // WiFi.onEvent(ESP32_W5500_event); +} + + +//----------------------------------------------------------------------------- +void ahoyeth::setup(settings_t *config, uint32_t *utcTimestamp, OnNetworkCB onNetworkCB, OnTimeCB onTimeCB) { + mConfig = config; + mUtcTimestamp = utcTimestamp; + mOnNetworkCB = onNetworkCB; + mOnTimeCB = onTimeCB; + + DPRINTLN(DBG_INFO, F("[ETH]: Register for events...")); + Serial.flush(); + WiFi.onEvent([this](WiFiEvent_t event, arduino_event_info_t info) -> void { this->onEthernetEvent(event, info); }); + + DPRINTLN(DBG_INFO, F("[ETH]: begin...")); + Serial.flush(); + ETH.begin(ETH_MISO_GPIO, ETH_MOSI_GPIO, ETH_SCK_GPIO, ETH_CS_PIN, ETH_INT_GPIO, ETH_SPI_CLOCK_MHZ, ETH_SPI_HOST); +} + + +//----------------------------------------------------------------------------- +bool ahoyeth::updateNtpTime(void) { + DPRINTLN(DBG_DEBUG, F(__FUNCTION__)); Serial.flush(); + Serial.printf("ETH.linkUp()=%s\n", ETH.linkUp() ? "up" : "down"); + Serial.print("ETH.localIP()="); + Serial.println(ETH.localIP()); + Serial.printf("Go on? %s\n", (!ETH.localIP()) ? "No..." : "Yes..."); + if (!ETH.localIP()) + return false; + + DPRINTLN(DBG_DEBUG, F("updateNtpTime: checking udp \"connection\"...")); Serial.flush(); + if (!mUdp.connected()) + { + DPRINTLN(DBG_DEBUG, F("updateNtpTime: About to (re)connect...")); Serial.flush(); + IPAddress timeServer; + if (!WiFi.hostByName(mConfig->ntp.addr, timeServer)) + return false; + + if (!mUdp.connect(timeServer, mConfig->ntp.port)) + return false; + + DPRINTLN(DBG_DEBUG, F("updateNtpTime: Connected...")); Serial.flush(); + mUdp.onPacket([this](AsyncUDPPacket packet) + { + DPRINTLN(DBG_DEBUG, F("updateNtpTime: about to handle ntp packet...")); Serial.flush(); + this->handleNTPPacket(packet); + }); + } + + DPRINTLN(DBG_DEBUG, F("updateNtpTime: prepare packet...")); Serial.flush(); + + // set all bytes in the buffer to 0 + memset(mUdpPacketBuffer, 0, NTP_PACKET_SIZE); + // Initialize values needed to form NTP request + // (see URL above for details on the packets) + + mUdpPacketBuffer[0] = 0b11100011; // LI, Version, Mode + mUdpPacketBuffer[1] = 0; // Stratum, or type of clock + mUdpPacketBuffer[2] = 6; // Polling Interval + mUdpPacketBuffer[3] = 0xEC; // Peer Clock Precision + + // 8 bytes of zero for Root Delay & Root Dispersion + mUdpPacketBuffer[12] = 49; + mUdpPacketBuffer[13] = 0x4E; + mUdpPacketBuffer[14] = 49; + mUdpPacketBuffer[15] = 52; + + //Send unicast + DPRINTLN(DBG_DEBUG, F("updateNtpTime: send packet...")); Serial.flush(); + mUdp.write(mUdpPacketBuffer, sizeof(mUdpPacketBuffer)); + + return true; +} + +//----------------------------------------------------------------------------- +void ahoyeth::handleNTPPacket(AsyncUDPPacket packet) { + char buf[80]; + + memcpy(buf, packet.data(), sizeof(buf)); + + unsigned long highWord = word(buf[40], buf[41]); + unsigned long lowWord = word(buf[42], buf[43]); + + // combine the four bytes (two words) into a long integer + // this is NTP time (seconds since Jan 1 1900): + unsigned long secsSince1900 = highWord << 16 | lowWord; + + *mUtcTimestamp = secsSince1900 - 2208988800UL; // UTC time + DPRINTLN(DBG_INFO, "[NTP]: " + ah::getDateTimeStr(*mUtcTimestamp) + " UTC"); + mOnTimeCB(true); +} + +//----------------------------------------------------------------------------- +void ahoyeth::welcome(String ip, String mode) { + DBGPRINTLN(F("\n\n--------------------------------")); + DBGPRINTLN(F("Welcome to AHOY!")); + DBGPRINT(F("\npoint your browser to http://")); + DBGPRINT(ip); + DBGPRINTLN(mode); + DBGPRINTLN(F("to configure your device")); + DBGPRINTLN(F("--------------------------------\n")); +} + +void ahoyeth::onEthernetEvent(WiFiEvent_t event, arduino_event_info_t info) +{ + AWS_LOG(F("[ETH]: Got event...")); + switch (event) + { +#if ( ( defined(ESP_ARDUINO_VERSION_MAJOR) && (ESP_ARDUINO_VERSION_MAJOR >= 2) ) && ( ARDUINO_ESP32_GIT_VER != 0x46d5afb1 ) ) + // For breaking core v2.0.0 + // Why so strange to define a breaking enum arduino_event_id_t in WiFiGeneric.h + // compared to the old system_event_id_t, now in tools/sdk/esp32/include/esp_event/include/esp_event_legacy.h + // You can preserve the old enum order and just adding new items to do no harm + case ARDUINO_EVENT_ETH_START: + AWS_LOG(F("\nETH Started")); + //set eth hostname here + if(String(mConfig->sys.deviceName) != "") + ETH.setHostname(mConfig->sys.deviceName); + else + ETH.setHostname("ESP32_W5500"); + break; + + case ARDUINO_EVENT_ETH_CONNECTED: + AWS_LOG(F("ETH Connected")); + break; + + case ARDUINO_EVENT_ETH_GOT_IP: + if (!ESP32_W5500_eth_connected) + { + AWS_LOG3(F("ETH MAC: "), ETH.macAddress(), F(", IPv4: "), ETH.localIP()); + + if (ETH.fullDuplex()) + { + AWS_LOG0(F("FULL_DUPLEX, ")); + } + else + { + AWS_LOG0(F("HALF_DUPLEX, ")); + } + + AWS_LOG1(ETH.linkSpeed(), F("Mbps")); + + ESP32_W5500_eth_connected = true; + mOnNetworkCB(true); + } + break; + + case ARDUINO_EVENT_ETH_DISCONNECTED: + AWS_LOG("ETH Disconnected"); + ESP32_W5500_eth_connected = false; + mUdp.close(); + mOnNetworkCB(false); + break; + + case ARDUINO_EVENT_ETH_STOP: + AWS_LOG("\nETH Stopped"); + ESP32_W5500_eth_connected = false; + mUdp.close(); + mOnNetworkCB(false); + break; + +#else + + // For old core v1.0.6- + // Core v2.0.0 defines a stupid enum arduino_event_id_t, breaking any code for ESP32_W5500 written for previous core + // Why so strange to define a breaking enum arduino_event_id_t in WiFiGeneric.h + // compared to the old system_event_id_t, now in tools/sdk/esp32/include/esp_event/include/esp_event_legacy.h + // You can preserve the old enum order and just adding new items to do no harm + case SYSTEM_EVENT_ETH_START: + AWS_LOG(F("\nETH Started")); + //set eth hostname here + if(String(mConfig->sys.deviceName) != "") + ETH.setHostname(mConfig->sys.deviceName); + else + ETH.setHostname("ESP32_W5500"); + break; + + case SYSTEM_EVENT_ETH_CONNECTED: + AWS_LOG(F("ETH Connected")); + break; + + case SYSTEM_EVENT_ETH_GOT_IP: + if (!ESP32_W5500_eth_connected) + { + AWS_LOG3(F("ETH MAC: "), ETH.macAddress(), F(", IPv4: "), ETH.localIP()); + + if (ETH.fullDuplex()) + { + AWS_LOG0(F("FULL_DUPLEX, ")); + } + else + { + AWS_LOG0(F("HALF_DUPLEX, ")); + } + + AWS_LOG1(ETH.linkSpeed(), F("Mbps")); + + ESP32_W5500_eth_connected = true; + mOnNetworkCB(true); + } + break; + + case SYSTEM_EVENT_ETH_DISCONNECTED: + AWS_LOG("ETH Disconnected"); + ESP32_W5500_eth_connected = false; + mUdp.close(); + mOnNetworkCB(false); + break; + + case SYSTEM_EVENT_ETH_STOP: + AWS_LOG("\nETH Stopped"); + ESP32_W5500_eth_connected = false; + mUdp.close(); + mOnNetworkCB(false); + break; +#endif + + default: + + break; + } + +} + +#endif /* defined(ETHERNET) */ \ No newline at end of file diff --git a/src/eth/ahoyeth.h b/src/eth/ahoyeth.h new file mode 100644 index 00000000..9f5123fb --- /dev/null +++ b/src/eth/ahoyeth.h @@ -0,0 +1,60 @@ +//----------------------------------------------------------------------------- +// 2023 Ahoy, https://www.mikrocontroller.net/topic/525778 +// Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/ +//----------------------------------------------------------------------------- + +#if defined(ETHERNET) +#ifndef __AHOYETH_H__ +#define __AHOYETH_H__ + +#include + +#include +#include +#include + +#include "../utils/dbg.h" +#include "../config/config.h" +#include "../config/settings.h" + +#include "AsyncWebServer_ESP32_W5500.h" + + +class app; + +#define NTP_PACKET_SIZE 48 + +class ahoyeth { + public: /* types */ + typedef std::function OnNetworkCB; + typedef std::function OnTimeCB; + + public: + ahoyeth(); + + void setup(settings_t *config, uint32_t *utcTimestamp, OnNetworkCB onNetworkCB, OnTimeCB onTimeCB); + bool updateNtpTime(void); + + private: + void setupEthernet(); + + void handleNTPPacket(AsyncUDPPacket packet); + + void welcome(String ip, String mode); + + void onEthernetEvent(WiFiEvent_t event, arduino_event_info_t info); + + private: + settings_t *mConfig; + + uint32_t *mUtcTimestamp; + AsyncUDP mUdp; // for time server + byte mUdpPacketBuffer[NTP_PACKET_SIZE]; // buffer to hold incoming and outgoing packets + + OnNetworkCB mOnNetworkCB; + OnTimeCB mOnTimeCB; + +}; + +#endif /*__AHOYETH_H__*/ +#endif /* defined(ETHERNET) */ \ No newline at end of file diff --git a/src/platformio.ini b/src/platformio.ini index 461b8063..eb88c6fb 100644 --- a/src/platformio.ini +++ b/src/platformio.ini @@ -152,7 +152,7 @@ lib_deps = https://github.com/JChristensen/Timezone olikraus/U8g2 zinggjm/GxEPD2@^1.5.0 -build_flags = -DETHERNET -DDEBUG_LEVEL=DBG_DEBUG -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_OOM -DDEBUG_ESP_PORT=Serial -std=gnu++14 +build_flags = -D ETHERNET -DDEBUG_LEVEL=DBG_DEBUG -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_OOM -DDEBUG_ESP_PORT=Serial -std=gnu++14 build_unflags = -std=gnu++11 build_type = debug monitor_filters = @@ -175,7 +175,7 @@ lib_deps = https://github.com/JChristensen/Timezone olikraus/U8g2 zinggjm/GxEPD2@^1.5.0 -build_flags = -DETHERNET -DRELEASE -DLOG_LOCAL_LEVEL=ESP_LOG_INFO -DDEBUG_LEVEL=DBG_DEBUG -std=gnu++14 +build_flags = -D ETHERNET -DRELEASE -DLOG_LOCAL_LEVEL=ESP_LOG_INFO -DDEBUG_LEVEL=DBG_DEBUG -std=gnu++14 build_unflags = -std=gnu++11 monitor_filters = ;default ; Remove typical terminal control codes from input diff --git a/src/web/RestApi.h b/src/web/RestApi.h index eab51247..ffdc4df9 100644 --- a/src/web/RestApi.h +++ b/src/web/RestApi.h @@ -18,9 +18,9 @@ #include "AsyncJson.h" #if defined(ETHERNET) #include "AsyncWebServer_ESP32_W5500.h" -#else /* defined(ETHERNET) */ +#else #include "ESPAsyncWebServer.h" -#endif /* defined(ETHERNET) */ +#endif #if defined(F) && defined(ESP32) #undef F diff --git a/src/web/web.h b/src/web/web.h index 7381c525..c083f505 100644 --- a/src/web/web.h +++ b/src/web/web.h @@ -188,13 +188,17 @@ class Web { if (final) { mUploadFp.close(); char pwd[PWD_LEN]; + #if !defined(ETHERNET) strncpy(pwd, mConfig->sys.stationPwd, PWD_LEN); // backup WiFi PWD + #endif if (!mApp->readSettings("/tmp.json")) { mUploadFail = true; DPRINTLN(DBG_ERROR, F("upload JSON error!")); } else { LittleFS.remove("/tmp.json"); + #if !defined(ETHERNET) strncpy(mConfig->sys.stationPwd, pwd, PWD_LEN); // restore WiFi PWD + #endif mApp->saveSettings(true); } if (!mUploadFail)