* Fix RAK4631 Ethernet gateway API connection loss after W5100S brownout PoE power instability can brownout the W5100S while the nRF52 MCU keeps running, causing all chip registers (MAC, IP, sockets) to revert to defaults. The firmware had no mechanism to detect or recover from this. Changes: - Detect W5100S chip reset by periodically verifying MAC address register in reconnectETH(); on mismatch, perform full hardware reset and re-initialize Ethernet interface and services - Add deInitApiServer() for clean API server teardown during recovery - Add ~APIServerPort destructor to prevent memory leaks - Switch nRF52 from EthernetServer::available() to accept() to prevent the same connected client from being repeatedly re-reported - Add proactive dead-connection cleanup in APIServerPort::runOnce() - Add 15-minute TCP idle timeout to close half-open connections that consume limited W5100S hardware sockets Fixes meshtastic/firmware#6970 Made-with: Cursor * Log actual elapsed idle time instead of constant timeout value Address Copilot review comment: log millis() - lastContactMsec to show the real time since last client activity, rather than always logging the TCP_IDLE_TIMEOUT_MS constant. Made-with: Cursor * Update src/mesh/api/ServerAPI.h Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Stop UDP multicast handler during W5100S brownout recovery After a W5100S chip brownout, the udpHandler isRunning flag stays true while the underlying socket is dead. Without calling stop(), the subsequent start() no-ops and multicast is silently broken after recovery. Made-with: Cursor * Address Copilot review: recovery flags and timeout constant Move ethStartupComplete and ntp_renew reset to immediately after service teardown, before Ethernet.begin(). Previously, if DHCP failed the early return left ethStartupComplete=true, preventing service re-initialization on subsequent retries. Replace #define TCP_IDLE_TIMEOUT_MS with static constexpr uint32_t for type safety and better C++ practice. Made-with: Cursor --------- Co-authored-by: Ben Meadors <benmmeadors@gmail.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
98 lines
2.8 KiB
C++
98 lines
2.8 KiB
C++
#include "ServerAPI.h"
|
|
#include "Throttle.h"
|
|
#include "configuration.h"
|
|
#include <Arduino.h>
|
|
|
|
static constexpr uint32_t TCP_IDLE_TIMEOUT_MS = 15 * 60 * 1000UL;
|
|
|
|
template <typename T>
|
|
ServerAPI<T>::ServerAPI(T &_client) : StreamAPI(&client), concurrency::OSThread("ServerAPI"), client(_client)
|
|
{
|
|
LOG_INFO("Incoming API connection");
|
|
}
|
|
|
|
template <typename T> ServerAPI<T>::~ServerAPI()
|
|
{
|
|
client.stop();
|
|
}
|
|
|
|
template <typename T> void ServerAPI<T>::close()
|
|
{
|
|
client.stop(); // drop tcp connection
|
|
StreamAPI::close();
|
|
}
|
|
|
|
/// Check the current underlying physical link to see if the client is currently connected
|
|
template <typename T> bool ServerAPI<T>::checkIsConnected()
|
|
{
|
|
return client.connected();
|
|
}
|
|
|
|
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));
|
|
close();
|
|
enabled = false;
|
|
return 0;
|
|
}
|
|
return StreamAPI::runOncePart();
|
|
} else {
|
|
LOG_INFO("Client dropped connection, suspend API service");
|
|
close();
|
|
enabled = false; // we no longer need to run
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
template <class T, class U> APIServerPort<T, U>::APIServerPort(int port) : U(port), concurrency::OSThread("ApiServer") {}
|
|
|
|
template <class T, class U> void APIServerPort<T, U>::init()
|
|
{
|
|
U::begin();
|
|
}
|
|
|
|
template <class T, class U> int32_t APIServerPort<T, U>::runOnce()
|
|
{
|
|
// Clean up previous connection if its client already disconnected
|
|
if (openAPI && !openAPI->checkIsConnected()) {
|
|
openAPI.reset();
|
|
}
|
|
|
|
#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) || defined(ARCH_NRF52)
|
|
auto client = U::accept();
|
|
#else
|
|
auto client = U::available();
|
|
#endif
|
|
if (client) {
|
|
// Close any previous connection (see FIXME in header file)
|
|
if (openAPI) {
|
|
#if RAK_4631
|
|
// RAK13800 Ethernet requests periodically take more time
|
|
// This backoff addresses most cases keeping max wait < 1s
|
|
// Reconnections are delayed by full wait time
|
|
if (waitTime < 400) {
|
|
waitTime *= 2;
|
|
LOG_INFO("Previous TCP connection still open, try again in %dms", waitTime);
|
|
return waitTime;
|
|
}
|
|
#endif
|
|
LOG_INFO("Force close previous TCP connection");
|
|
openAPI.reset();
|
|
}
|
|
|
|
openAPI.reset(new T(client));
|
|
}
|
|
|
|
#if RAK_4631
|
|
waitTime = 100;
|
|
#endif
|
|
return 100; // only check occasionally for incoming connections
|
|
} |