Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion src/mesh/api/ServerAPI.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
#include "ServerAPI.h"
#include "Throttle.h"
#include "configuration.h"
#include <Arduino.h>

#define TCP_IDLE_TIMEOUT_MS (15 * 60 * 1000UL)

template <typename T>
ServerAPI<T>::ServerAPI(T &_client) : StreamAPI(&client), concurrency::OSThread("ServerAPI"), client(_client)
{
Expand All @@ -28,6 +31,12 @@ template <typename T> bool ServerAPI<T>::checkIsConnected()
template <class T> int32_t ServerAPI<T>::runOnce()
{
if (client.connected()) {
if (lastContactMsec > 0 && !Throttle::isWithinTimespanMs(lastContactMsec, TCP_IDLE_TIMEOUT_MS)) {
LOG_WARN("TCP connection timeout, no data for %lu ms", (unsigned long)(millis() - lastContactMsec));
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should lastContactMsec also be updated in cases other than inbound API data?

looks like:

  • For Ethernet TCP API, lastContactMsec is not updated by outbound traffic, socket-level ACKs, or link-level activity.
  • It advances only on inbound API protobuf frames from client.
  • So the new 15-minute timeout effectively enforces: client must send periodic API traffic (typically heartbeat) to stay connected.

close();
enabled = false;
return 0;
}
return StreamAPI::runOncePart();
} else {
LOG_INFO("Client dropped connection, suspend API service");
Expand All @@ -45,13 +54,19 @@ template <class T, class U> void APIServerPort<T, U>::init()

template <class T, class U> int32_t APIServerPort<T, U>::runOnce()
{
if (openAPI && !openAPI->isClientConnected()) {
LOG_INFO("Cleaning up disconnected TCP API client");
delete openAPI;
openAPI = nullptr;
}

#ifdef ARCH_ESP32
#if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 0)
auto client = U::accept();
#else
auto client = U::available();
#endif
#elif defined(ARCH_RP2040)
#elif defined(ARCH_RP2040) || defined(ARCH_NRF52)
auto client = U::accept();
#else
auto client = U::available();
Expand Down
3 changes: 3 additions & 0 deletions src/mesh/api/ServerAPI.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ template <class T> class ServerAPI : public StreamAPI, private concurrency::OSTh
/// override close to also shutdown the TCP link
virtual void close();

bool isClientConnected() { return client.connected(); }

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)
Expand Down Expand Up @@ -50,6 +52,7 @@ template <class T, class U> class APIServerPort : public U, private concurrency:

public:
explicit APIServerPort(int port);
~APIServerPort() { delete openAPI; }

void init();

Expand Down
9 changes: 9 additions & 0 deletions src/mesh/api/ethServerAPI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,15 @@ void initApiServer(int port)
}
}

void deInitApiServer()
{
if (apiPort) {
LOG_INFO("Deinit API server");
delete apiPort;
apiPort = nullptr;
}
}

ethServerAPI::ethServerAPI(EthernetClient &_client) : ServerAPI(_client)
{
LOG_INFO("Incoming ethernet connection");
Expand Down
1 change: 1 addition & 0 deletions src/mesh/api/ethServerAPI.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,5 @@ class ethServerPort : public APIServerPort<ethServerAPI, EthernetServer>
};

void initApiServer(int port = SERVER_API_DEFAULT_PORT);
void deInitApiServer();
#endif
58 changes: 58 additions & 0 deletions src/mesh/eth/ethClient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,64 @@ static Periodic *ethEvent;
static int32_t reconnectETH()
{
if (config.network.eth_enabled) {

// Detect W5100S chip reset by verifying the MAC address register.
// PoE power instability can brownout the W5100S while the MCU keeps running,
// causing all chip registers (MAC, IP, sockets) to revert to defaults.
uint8_t currentMac[6];
Ethernet.MACAddress(currentMac);

uint8_t expectedMac[6];
getMacAddr(expectedMac);
expectedMac[0] &= 0xfe;

if (memcmp(currentMac, expectedMac, 6) != 0) {
LOG_WARN("W5100S MAC mismatch (chip reset detected), reinitializing Ethernet");

syslog.disable();
#if !MESHTASTIC_EXCLUDE_SOCKETAPI
deInitApiServer();
#endif

#ifdef PIN_ETHERNET_RESET
pinMode(PIN_ETHERNET_RESET, OUTPUT);
digitalWrite(PIN_ETHERNET_RESET, LOW);
delay(100);
digitalWrite(PIN_ETHERNET_RESET, HIGH);
delay(100);
#endif

#ifdef RAK11310
ETH_SPI_PORT.setSCK(PIN_SPI0_SCK);
ETH_SPI_PORT.setTX(PIN_SPI0_MOSI);
ETH_SPI_PORT.setRX(PIN_SPI0_MISO);
ETH_SPI_PORT.begin();
#endif
Ethernet.init(ETH_SPI_PORT, PIN_ETHERNET_SS);

int status = 0;
if (config.network.address_mode == meshtastic_Config_NetworkConfig_AddressMode_DHCP) {
status = Ethernet.begin(expectedMac);
} else if (config.network.address_mode == meshtastic_Config_NetworkConfig_AddressMode_STATIC) {
Ethernet.begin(expectedMac, config.network.ipv4_config.ip, config.network.ipv4_config.dns,
config.network.ipv4_config.gateway, config.network.ipv4_config.subnet);
status = 1;
}

if (status == 0) {
LOG_ERROR("Ethernet re-initialization failed, will retry");
return 5000;
}

LOG_INFO("Ethernet reinitialized - IP %u.%u.%u.%u", Ethernet.localIP()[0], Ethernet.localIP()[1],
Ethernet.localIP()[2], Ethernet.localIP()[3]);

ethStartupComplete = false;
#ifndef DISABLE_NTP
ntp_renew = 0;
#endif
}

Ethernet.maintain();
if (!ethStartupComplete) {
// Start web server
Expand Down
Loading