diff --git a/arch/esp32/esp32.ini b/arch/esp32/esp32.ini
index 1b57ffb8b..ba98a6b1f 100644
--- a/arch/esp32/esp32.ini
+++ b/arch/esp32/esp32.ini
@@ -33,8 +33,7 @@ lib_deps =
${environmental_base.lib_deps}
https://github.com/meshtastic/esp32_https_server.git#657509856ce97e9dddeffb89a559f544faefd5cd
h2zero/NimBLE-Arduino@^1.4.0
- arduino-libraries/NTPClient@^3.1.0
- https://github.com/lewisxhe/XPowersLib.git#84b7373faea3118b6c37954d52f98b8a337148d6
+ https://github.com/lewisxhe/XPowersLib.git#84b7373faea3118b6c37954d52f98b8a337148d6
lib_ignore =
segger_rtt
diff --git a/arch/esp32/esp32s3.ini b/arch/esp32/esp32s3.ini
index 286d9950e..f5338d9a9 100644
--- a/arch/esp32/esp32s3.ini
+++ b/arch/esp32/esp32s3.ini
@@ -33,7 +33,6 @@ lib_deps =
${environmental_base.lib_deps}
https://github.com/meshtastic/esp32_https_server.git#657509856ce97e9dddeffb89a559f544faefd5cd
h2zero/NimBLE-Arduino@^1.4.0
- arduino-libraries/NTPClient@^3.1.0
https://github.com/lewisxhe/XPowersLib.git#84b7373faea3118b6c37954d52f98b8a337148d6
lib_ignore =
diff --git a/platformio.ini b/platformio.ini
index 1f97cfb19..527bce0d0 100644
--- a/platformio.ini
+++ b/platformio.ini
@@ -79,6 +79,7 @@ build_src_filter = ${env.build_src_filter} -
[networking_base]
lib_deps =
knolleary/PubSubClient@^2.8
+ arduino-libraries/NTPClient@^3.1.0
meshtastic/json11@^1.0.2
; Common libs for environmental measurements in telemetry module
diff --git a/src/main.cpp b/src/main.cpp
index a783f023b..d2505d816 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -43,6 +43,7 @@
#endif
#if HAS_ETHERNET
+#include "mesh/eth/ethServerAPI.h"
#include "mqtt/MQTT.h"
#endif
diff --git a/src/mesh/eth/ethClient.cpp b/src/mesh/eth/ethClient.cpp
index 86e4e56c9..1bd8ea96e 100644
--- a/src/mesh/eth/ethClient.cpp
+++ b/src/mesh/eth/ethClient.cpp
@@ -1,15 +1,98 @@
#include "mesh/eth/ethClient.h"
#include "NodeDB.h"
+#include "RTC.h"
+#include "concurrency/Periodic.h"
#include
#include
#include "target_specific.h"
+#include "mesh/eth/ethServerAPI.h"
+#include "mqtt/MQTT.h"
+
+#ifndef DISABLE_NTP
+#include
+
+// NTP
+EthernetUDP ntpUDP;
+
+NTPClient timeClient(ntpUDP, config.network.ntp_server);
+
+uint32_t ntp_renew = 0;
+#endif
+
+// Stores our hostname
+char ourHost[16];
+
+bool ethStartupComplete = 0;
+
+using namespace concurrency;
+
+static Periodic *ethEvent;
+
+static int32_t reconnectETH()
+{
+ if (config.network.eth_enabled) {
+ Ethernet.maintain();
+ if (!ethStartupComplete) {
+ // Start web server
+ DEBUG_MSG("... Starting network services\n");
+
+ // // start mdns
+ // if (!MDNS.begin("Meshtastic")) {
+ // DEBUG_MSG("Error setting up MDNS responder!\n");
+ // } else {
+ // DEBUG_MSG("mDNS responder started\n");
+ // DEBUG_MSG("mDNS Host: Meshtastic.local\n");
+ // MDNS.addService("http", "tcp", 80);
+ // MDNS.addService("https", "tcp", 443);
+ // }
+
+ #ifndef DISABLE_NTP
+ DEBUG_MSG("Starting NTP time client\n");
+ timeClient.begin();
+ timeClient.setUpdateInterval(60 * 60); // Update once an hour
+ #endif
+
+ // initWebServer();
+ initApiServer();
+
+ ethStartupComplete = true;
+ }
+
+ // FIXME this is kinda yucky, instead we should just have an observable for 'wifireconnected'
+ if (mqtt && !mqtt->connected()) {
+ mqtt->reconnect();
+ }
+ }
+
+#ifndef DISABLE_NTP
+ if (isEthernetAvailable() && (ntp_renew < millis())) {
+ DEBUG_MSG("Updating NTP time\n");
+ if (timeClient.update()) {
+ DEBUG_MSG("NTP Request Success - Setting RTCQualityNTP if needed\n");
+
+ struct timeval tv;
+ tv.tv_sec = timeClient.getEpochTime();
+ tv.tv_usec = 0;
+
+ perhapsSetRTC(RTCQualityNTP, &tv);
+
+ ntp_renew = millis() + 43200 * 1000; // every 12 hours
+
+ } else {
+ DEBUG_MSG("NTP Update failed\n");
+ }
+ }
+#endif
+
+ return 5000; // every 5 seconds
+}
// Startup Ethernet
bool initEthernet()
{
- config.network.eth_enabled = true;
- config.network.eth_mode = Config_NetworkConfig_EthMode_DHCP;
+ // config.network.eth_enabled = true;
+ // config.network.eth_mode = Config_NetworkConfig_EthMode_DHCP;
if (config.network.eth_enabled) {
@@ -26,6 +109,8 @@ bool initEthernet()
int status = 0;
+ // createSSLCert();
+
getMacAddr(mac); // FIXME use the BLE MAC for now...
if (config.network.eth_mode == Config_NetworkConfig_EthMode_DHCP) {
@@ -56,6 +141,9 @@ bool initEthernet()
DEBUG_MSG("Gateway IP %u.%u.%u.%u\n",Ethernet.gatewayIP()[0], Ethernet.gatewayIP()[1], Ethernet.gatewayIP()[2], Ethernet.gatewayIP()[3]);
DEBUG_MSG("DNS Server IP %u.%u.%u.%u\n",Ethernet.dnsServerIP()[0], Ethernet.dnsServerIP()[1], Ethernet.dnsServerIP()[2], Ethernet.dnsServerIP()[3]);
}
+
+ ethEvent = new Periodic("ethConnect", reconnectETH);
+
return true;
} else {
diff --git a/src/mesh/eth/ethServerAPI.cpp b/src/mesh/eth/ethServerAPI.cpp
new file mode 100644
index 000000000..bb7dd927d
--- /dev/null
+++ b/src/mesh/eth/ethServerAPI.cpp
@@ -0,0 +1,82 @@
+#include "ethServerAPI.h"
+#include "configuration.h"
+#include
+
+static ethServerPort *apiPort;
+
+void initApiServer(int port)
+{
+ // Start API server on port 4403
+ if (!apiPort) {
+ apiPort = new ethServerPort(port);
+ DEBUG_MSG("API server listening on TCP port %d\n", port);
+ apiPort->init();
+ }
+}
+
+ethServerAPI::ethServerAPI(EthernetClient &_client) : StreamAPI(&client), client(_client)
+{
+ DEBUG_MSG("Incoming ethernet connection\n");
+}
+
+ethServerAPI::~ethServerAPI()
+{
+ client.stop();
+
+ // FIXME - delete this if the client dropps the connection!
+}
+
+/// override close to also shutdown the TCP link
+void ethServerAPI::close()
+{
+ client.stop(); // drop tcp connection
+ StreamAPI::close();
+}
+
+/// Check the current underlying physical link to see if the client is currently connected
+bool ethServerAPI::checkIsConnected()
+{
+ return client.connected();
+}
+
+int32_t ethServerAPI::runOnce()
+{
+ if (client.connected()) {
+ return StreamAPI::runOnce();
+ } else {
+ DEBUG_MSG("Client dropped connection, suspending API service\n");
+ enabled = false; // we no longer need to run
+ return 0;
+ }
+}
+
+/// If an api server is running, we try to spit out debug 'serial' characters there
+void ethServerPort::debugOut(char c)
+{
+ if (apiPort && apiPort->openAPI)
+ apiPort->openAPI->debugOut(c);
+}
+
+
+ethServerPort::ethServerPort(int port) : EthernetServer(port), concurrency::OSThread("ApiServer") {}
+
+void ethServerPort::init()
+{
+ begin();
+}
+
+int32_t ethServerPort::runOnce()
+{
+ auto client = available();
+ if (client) {
+ // Close any previous connection (see FIXME in header file)
+ if (openAPI) {
+ DEBUG_MSG("Force closing previous TCP connection\n");
+ delete openAPI;
+ }
+
+ openAPI = new ethServerAPI(client);
+ }
+
+ return 100; // only check occasionally for incoming connections
+}
diff --git a/src/mesh/eth/ethServerAPI.h b/src/mesh/eth/ethServerAPI.h
new file mode 100644
index 000000000..c92ab2f17
--- /dev/null
+++ b/src/mesh/eth/ethServerAPI.h
@@ -0,0 +1,58 @@
+#pragma once
+
+#include "StreamAPI.h"
+#include
+
+/**
+ * Provides both debug printing and, if the client starts sending protobufs to us, switches to send/receive protobufs
+ * (and starts dropping debug printing - FIXME, eventually those prints should be encapsulated in protobufs).
+ */
+class ethServerAPI : public StreamAPI
+{
+ private:
+ EthernetClient client;
+
+ public:
+ explicit ethServerAPI(EthernetClient &_client);
+
+ virtual ~ethServerAPI();
+
+ /// override close to also shutdown the TCP link
+ virtual void close();
+
+ protected:
+ /// We override this method to prevent publishing EVENT_SERIAL_CONNECTED/DISCONNECTED for wifi links (we want the board to
+ /// stay in the POWERED state to prevent disabling wifi)
+ virtual void onConnectionChanged(bool connected) override {}
+
+ virtual int32_t runOnce() override; // Check for dropped client connections
+
+ /// Check the current underlying physical link to see if the client is currently connected
+ virtual bool checkIsConnected() override;
+};
+
+/**
+ * Listens for incoming connections and does accepts and creates instances of WiFiServerAPI as needed
+ */
+class ethServerPort : public EthernetServer, private concurrency::OSThread
+{
+ /** The currently open port
+ *
+ * FIXME: We currently only allow one open TCP connection at a time, because we depend on the loop() call in this class to
+ * delegate to the worker. Once coroutines are implemented we can relax this restriction.
+ */
+ ethServerAPI *openAPI = NULL;
+
+ public:
+ explicit ethServerPort(int port);
+
+ void init();
+
+ /// If an api server is running, we try to spit out debug 'serial' characters there
+ static void debugOut(char c);
+
+ protected:
+ int32_t runOnce() override;
+};
+
+void initApiServer(int port=4403);
diff --git a/src/mqtt/MQTT.cpp b/src/mqtt/MQTT.cpp
index 9d6c68225..e77e82e54 100644
--- a/src/mqtt/MQTT.cpp
+++ b/src/mqtt/MQTT.cpp
@@ -107,6 +107,11 @@ MQTT::MQTT() : concurrency::OSThread("mqtt"), pubSub(mqttClient)
// preflightSleepObserver.observe(&preflightSleep);
}
+bool MQTT::connected()
+{
+ return pubSub.connected();
+}
+
void MQTT::reconnect()
{
if (wantsLink()) {
@@ -197,6 +202,7 @@ bool MQTT::wantsLink() const
#if HAS_ETHERNET
return hasChannel && (Ethernet.linkStatus() == LinkON);
#endif
+ return false;
}
int32_t MQTT::runOnce()
diff --git a/src/mqtt/MQTT.h b/src/mqtt/MQTT.h
index 4412e9526..c8381574c 100644
--- a/src/mqtt/MQTT.h
+++ b/src/mqtt/MQTT.h
@@ -48,6 +48,8 @@ class MQTT : private concurrency::OSThread
/** Attempt to connect to server if necessary
*/
void reconnect();
+
+ bool connected();
protected:
virtual int32_t runOnce() override;