mirror of https://github.com/meshcore-dev/MeshCore
Browse Source
SerialEthernetInterface lived in src/helpers/, which the base build_src_filter compiles for EVERY variant via `+<helpers/*.cpp>`. Its `#include <RAK13800_W5100S.h>` then broke all non-Ethernet RAK4631 builds (repeater, room server, companion usb/ble, ...) with "RAK13800_W5100S.h: No such file or directory", since that library is only a dependency of the Ethernet env. Move it into src/helpers/nrf52/ (a subdirectory the base filter does NOT glob), matching how SerialBLEInterface is handled, and include it explicitly only in the Ethernet env via `+<helpers/nrf52/SerialEthernetInterface.cpp>`. Now every RAK4631 variant builds, and only the Ethernet build pulls in the W5100S library.pull/2679/head
5 changed files with 264 additions and 584 deletions
@ -1,151 +0,0 @@ |
|||
#include "SerialEthernetInterface.h" |
|||
|
|||
void SerialEthernetInterface::begin(int port) { |
|||
// Ethernet hardware (Ethernet.init/begin) is brought up in setup();
|
|||
// here we only start the TCP server.
|
|||
server = new EthernetServer(port); |
|||
server->begin(); |
|||
} |
|||
|
|||
void SerialEthernetInterface::enable() { |
|||
if (_isEnabled) return; |
|||
_isEnabled = true; |
|||
send_queue_len = 0; |
|||
} |
|||
|
|||
void SerialEthernetInterface::disable() { |
|||
_isEnabled = false; |
|||
} |
|||
|
|||
size_t SerialEthernetInterface::writeFrame(const uint8_t src[], size_t len) { |
|||
if (len > MAX_FRAME_SIZE) { |
|||
ETH_DEBUG_PRINTLN("writeFrame(): frame too big, len=%d", (int)len); |
|||
return 0; |
|||
} |
|||
if (!_connected || len == 0) return 0; |
|||
|
|||
if (send_queue_len >= ETH_FRAME_QUEUE_SIZE) { |
|||
ETH_DEBUG_PRINTLN("writeFrame(): send_queue full (dropping code=0x%02x)", src[0]); |
|||
return 0; |
|||
} |
|||
|
|||
// PUSH codes (>= 0x80) go to all clients; command responses go to the
|
|||
// client that issued the most recent command.
|
|||
int8_t target = (src[0] >= 0x80) ? -1 : (int8_t)_last_rx; |
|||
|
|||
ETH_DEBUG_PRINTLN("TX code=0x%02x len=%d -> %s", src[0], (int)len, |
|||
target < 0 ? "all" : (target == 0 ? "slot0" : target == 1 ? "slot1" : "slot2")); |
|||
|
|||
send_queue[send_queue_len].target = target; |
|||
send_queue[send_queue_len].len = (uint8_t)len; |
|||
memcpy(send_queue[send_queue_len].buf, src, len); |
|||
send_queue_len++; |
|||
return len; |
|||
} |
|||
|
|||
size_t SerialEthernetInterface::checkRecvFrame(uint8_t dest[]) { |
|||
if (server == NULL) return 0; |
|||
|
|||
// ---- accept a new connection into a free slot --------------------------
|
|||
// accept() returns each new connection once and maintains the listen socket,
|
|||
// so it must be called every loop.
|
|||
EthernetClient nc = server->accept(); |
|||
if (nc) { |
|||
int slot = -1; |
|||
for (int i = 0; i < MAX_ETH_CLIENTS; i++) { |
|||
if (!clients[i].connected()) { slot = i; break; } |
|||
} |
|||
if (slot >= 0) { |
|||
clients[slot].stop(); // free any lingering socket in this slot
|
|||
clients[slot] = nc; |
|||
rx_header[slot].type = 0; |
|||
rx_header[slot].length = 0; |
|||
ETH_DEBUG_PRINTLN("Got connection (slot %d)", slot); |
|||
} else { |
|||
nc.stop(); // all slots busy — reject
|
|||
ETH_DEBUG_PRINTLN("Rejected connection (all %d slots busy)", MAX_ETH_CLIENTS); |
|||
} |
|||
} |
|||
|
|||
// ---- refresh connected state, free dropped sockets ---------------------
|
|||
bool any = false; |
|||
for (int i = 0; i < MAX_ETH_CLIENTS; i++) { |
|||
if (clients[i].connected()) { |
|||
any = true; |
|||
} else if (rx_header[i].type || rx_header[i].length) { |
|||
// a client that was active just dropped — reset its parse state
|
|||
rx_header[i].type = 0; |
|||
rx_header[i].length = 0; |
|||
clients[i].stop(); |
|||
ETH_DEBUG_PRINTLN("Disconnected (slot %d)", i); |
|||
} |
|||
} |
|||
_connected = any; |
|||
|
|||
// ---- drain the outbound queue ------------------------------------------
|
|||
while (send_queue_len > 0) { |
|||
Frame &f = send_queue[0]; |
|||
uint8_t pkt[3 + MAX_FRAME_SIZE]; |
|||
pkt[0] = '>'; |
|||
pkt[1] = (f.len & 0xFF); |
|||
pkt[2] = (f.len >> 8); |
|||
memcpy(&pkt[3], f.buf, f.len); |
|||
|
|||
if (f.target < 0) { // broadcast (push)
|
|||
for (int i = 0; i < MAX_ETH_CLIENTS; i++) { |
|||
if (clients[i].connected()) clients[i].write(pkt, 3 + f.len); |
|||
} |
|||
} else if (f.target < MAX_ETH_CLIENTS && clients[f.target].connected()) { |
|||
clients[f.target].write(pkt, 3 + f.len); // response to the requester
|
|||
} |
|||
|
|||
send_queue_len--; |
|||
for (int i = 0; i < send_queue_len; i++) send_queue[i] = send_queue[i + 1]; |
|||
} |
|||
|
|||
// ---- read ONE inbound frame (round-robin across clients) ---------------
|
|||
for (int k = 0; k < MAX_ETH_CLIENTS; k++) { |
|||
int i = (_rr + k) % MAX_ETH_CLIENTS; |
|||
EthernetClient &c = clients[i]; |
|||
if (!c.connected()) continue; |
|||
|
|||
// frame header = [type][len_lo][len_hi]
|
|||
if (rx_header[i].type == 0 || rx_header[i].length == 0) { |
|||
if (c.available() >= 3) { |
|||
c.readBytes(&rx_header[i].type, 1); |
|||
c.readBytes((uint8_t *)&rx_header[i].length, 2); |
|||
} |
|||
} |
|||
|
|||
if (rx_header[i].type != 0 && rx_header[i].length != 0) { |
|||
int avail = c.available(); |
|||
int frame_type = rx_header[i].type; |
|||
int frame_length = rx_header[i].length; |
|||
|
|||
if (frame_length > avail) continue; // wait for the rest
|
|||
|
|||
if (frame_length > MAX_FRAME_SIZE || frame_type != '<') { |
|||
// oversized or unexpected type — discard
|
|||
while (frame_length > 0) { |
|||
uint8_t skip[1]; |
|||
int n = c.read(skip, 1); |
|||
if (n <= 0) break; |
|||
frame_length -= n; |
|||
} |
|||
rx_header[i].type = 0; |
|||
rx_header[i].length = 0; |
|||
continue; |
|||
} |
|||
|
|||
c.readBytes(dest, frame_length); |
|||
rx_header[i].type = 0; |
|||
rx_header[i].length = 0; |
|||
_last_rx = i; // route responses back here
|
|||
_rr = (i + 1) % MAX_ETH_CLIENTS; // fairness
|
|||
ETH_DEBUG_PRINTLN("RX[%d] cmd=0x%02x len=%d", i, dest[0], frame_length); |
|||
return frame_length; |
|||
} |
|||
} |
|||
|
|||
return 0; |
|||
} |
|||
@ -1,78 +0,0 @@ |
|||
#pragma once |
|||
|
|||
#include "BaseSerialInterface.h" |
|||
#include <RAK13800_W5100S.h> |
|||
|
|||
// Multi-client TCP companion interface over a W5100S Ethernet module (RAK13800).
|
|||
// Lets several clients (e.g. Home Assistant AND the phone app) stay connected
|
|||
// at once — the single-client model had them kicking each other off the one
|
|||
// socket, causing an endless reconnect loop.
|
|||
//
|
|||
// Routing of outbound frames (the companion protocol isn't natively
|
|||
// multi-client, so we route by frame code):
|
|||
// - PUSH frames (code >= 0x80, e.g. LoRa-RX log, adverts) -> ALL clients
|
|||
// - command RESPONSES (code < 0x80) -> the client
|
|||
// that issued
|
|||
// the last command
|
|||
//
|
|||
// Ethernet hardware (Ethernet.init/begin) is brought up outside this class.
|
|||
|
|||
#ifndef MAX_ETH_CLIENTS |
|||
#define MAX_ETH_CLIENTS 3 // W5100S has 4 sockets: up to 3 clients + 1 listen
|
|||
#endif |
|||
|
|||
class SerialEthernetInterface : public BaseSerialInterface { |
|||
bool _isEnabled; |
|||
bool _connected; // true if at least one client is connected
|
|||
|
|||
EthernetServer* server; |
|||
EthernetClient clients[MAX_ETH_CLIENTS]; |
|||
|
|||
struct FrameHeader { uint8_t type; uint16_t length; }; |
|||
FrameHeader rx_header[MAX_ETH_CLIENTS]; // per-client inbound parse state
|
|||
|
|||
struct Frame { |
|||
int8_t target; // -1 = broadcast, else client index
|
|||
uint8_t len; |
|||
uint8_t buf[MAX_FRAME_SIZE]; |
|||
}; |
|||
|
|||
#define ETH_FRAME_QUEUE_SIZE 16 |
|||
int send_queue_len; |
|||
Frame send_queue[ETH_FRAME_QUEUE_SIZE]; |
|||
|
|||
int _last_rx; // client index of the most recent inbound command
|
|||
int _rr; // round-robin cursor for fair inbound polling
|
|||
|
|||
public: |
|||
SerialEthernetInterface() : server(NULL) { |
|||
_isEnabled = false; |
|||
_connected = false; |
|||
send_queue_len = 0; |
|||
_last_rx = -1; |
|||
_rr = 0; |
|||
for (int i = 0; i < MAX_ETH_CLIENTS; i++) { rx_header[i].type = 0; rx_header[i].length = 0; } |
|||
} |
|||
|
|||
void begin(int port); |
|||
|
|||
// BaseSerialInterface methods
|
|||
void enable() override; |
|||
void disable() override; |
|||
bool isEnabled() const override { return _isEnabled; } |
|||
|
|||
bool isConnected() const override { return _connected; } |
|||
bool isWriteBusy() const override { return false; } |
|||
|
|||
size_t writeFrame(const uint8_t src[], size_t len) override; |
|||
size_t checkRecvFrame(uint8_t dest[]) override; |
|||
}; |
|||
|
|||
#if ETH_DEBUG_LOGGING && ARDUINO |
|||
#include <Arduino.h> |
|||
#define ETH_DEBUG_PRINT(F, ...) Serial.printf("ETH: " F, ##__VA_ARGS__) |
|||
#define ETH_DEBUG_PRINTLN(F, ...) Serial.printf("ETH: " F "\n", ##__VA_ARGS__) |
|||
#else |
|||
#define ETH_DEBUG_PRINT(...) {} |
|||
#define ETH_DEBUG_PRINTLN(...) {} |
|||
#endif |
|||
Loading…
Reference in new issue