From e672679d6ec5c0c849b60b9c735f386a30c0bade Mon Sep 17 00:00:00 2001 From: liamcottle Date: Sun, 19 Jul 2026 23:28:01 +1200 Subject: [PATCH] refactored companion interfaces to allow for multiple active connection modes --- examples/companion_radio/AbstractUITask.h | 12 +- examples/companion_radio/main.cpp | 205 ++++++++---------- examples/companion_radio/ui-new/UITask.cpp | 8 +- examples/companion_radio/ui-new/UITask.h | 4 +- examples/companion_radio/ui-orig/UITask.h | 2 +- examples/companion_radio/ui-tiny/UITask.h | 4 +- src/helpers/BaseSerialInterface.h | 1 + src/helpers/MultiSerialInterface.h | 200 +++++++++++++++++ .../ethernet/SerialEthernetInterface.cpp | 140 ++++++++++++ .../ethernet/SerialEthernetInterface.h | 75 +++++++ .../ethernet/ch390/CH390EthernetInterface.cpp | 197 ++++------------- .../ethernet/ch390/CH390EthernetInterface.h | 72 +----- variants/thinknode_m7/platformio.ini | 6 + 13 files changed, 584 insertions(+), 342 deletions(-) create mode 100644 src/helpers/MultiSerialInterface.h create mode 100644 src/helpers/ethernet/SerialEthernetInterface.cpp create mode 100644 src/helpers/ethernet/SerialEthernetInterface.h diff --git a/examples/companion_radio/AbstractUITask.h b/examples/companion_radio/AbstractUITask.h index 0eee45ae..b25b1442 100644 --- a/examples/companion_radio/AbstractUITask.h +++ b/examples/companion_radio/AbstractUITask.h @@ -4,7 +4,7 @@ #include #include #include -#include +#include #include #ifdef PIN_BUZZER @@ -25,10 +25,10 @@ enum class UIEventType { class AbstractUITask { protected: mesh::MainBoard* _board; - BaseSerialInterface* _serial; + MultiSerialInterface* _interfaceManager; bool _connected; - AbstractUITask(mesh::MainBoard* board, BaseSerialInterface* serial) : _board(board), _serial(serial) { + AbstractUITask(mesh::MainBoard* board, MultiSerialInterface* interfaceManager) : _board(board), _interfaceManager(interfaceManager) { _connected = false; } @@ -36,9 +36,9 @@ public: void setHasConnection(bool connected) { _connected = connected; } bool hasConnection() const { return _connected; } uint16_t getBattMilliVolts() const { return _board->getBattMilliVolts(); } - bool isSerialEnabled() const { return _serial->isEnabled(); } - void enableSerial() { _serial->enable(); } - void disableSerial() { _serial->disable(); } + bool isBluetoothEnabled() const { return _interfaceManager->isBluetoothEnabled(); } + void enableBluetooth() { _interfaceManager->enableBluetooth(); } + void disableBluetooth() { _interfaceManager->disableBluetooth(); } virtual void msgRead(int msgcount) = 0; virtual void newMsg(uint8_t path_len, const char* from_name, const char* text, int msgcount) = 0; virtual void notify(UIEventType t = UIEventType::none) = 0; diff --git a/examples/companion_radio/main.cpp b/examples/companion_radio/main.cpp index b6b6d93c..7c67d90d 100644 --- a/examples/companion_radio/main.cpp +++ b/examples/companion_radio/main.cpp @@ -12,6 +12,67 @@ static uint32_t _atoi(const char* sp) { return n; } +// interface manager +#include +MultiSerialInterface interface_manager; + +// include bluetooth interface +#if defined(BLE_PIN_CODE) + #ifdef ESP32 + // include esp32 bluetooth interface + #include + SerialBLEInterface bluetooth_interface; + #elif defined(NRF52_PLATFORM) + // include nrf52 bluetooth interface + #include + SerialBLEInterface bluetooth_interface; + #else + #error "SerialBLEInterface is not defined for this platform" + #endif +#endif + +// include wifi interface +#ifdef WIFI_SSID + #ifndef TCP_PORT + #define TCP_PORT 5000 + #endif + #ifdef ESP32 + // include esp32 wifi interface + #include + SerialWifiInterface wifi_interface; + #else + #error "SerialWifiInterface is not defined for this platform" + #endif +#endif + +// include usb interface +#if defined(ENABLE_USB_INTERFACE) + #include + ArduinoSerialInterface usb_serial_interface; +#endif + +// include ethernet interface +#if defined(ETHERNET_ENABLED) + // todo refactor rak/nrf52 SerialEthernetInterface to new EthernetInterface + #if defined(NRF52_PLATFORM) + // include nrf52 ethernet interface + #include + SerialEthernetInterface ethernet_interface; + #else + // include ethernet interface + #include + ETHERNET_CLASS ethernet_interface; + #endif +#endif + +// include hardware serial interface +#if defined(SERIAL_RX) + #include + ArduinoSerialInterface hardware_serial_interface; + HardwareSerial companion_serial(1); +#endif + +// platform file system #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) #include #if defined(QSPIFLASH) @@ -34,67 +95,10 @@ static uint32_t _atoi(const char* sp) { DataStore store(SPIFFS, rtc_clock); #endif -#ifdef ESP32 - #ifdef WIFI_SSID - #include - SerialWifiInterface serial_interface; - #ifndef TCP_PORT - #define TCP_PORT 5000 - #endif - #elif defined(BLE_PIN_CODE) - #include - SerialBLEInterface serial_interface; - #elif defined(SERIAL_RX) - #include - ArduinoSerialInterface serial_interface; - HardwareSerial companion_serial(1); - #elif defined(ETHERNET_ENABLED) - #include - ETHERNET_CLASS serial_interface; - #else - #include - ArduinoSerialInterface serial_interface; - #endif -#elif defined(RP2040_PLATFORM) - //#ifdef WIFI_SSID - // #include - // SerialWifiInterface serial_interface; - // #ifndef TCP_PORT - // #define TCP_PORT 5000 - // #endif - // #elif defined(BLE_PIN_CODE) - // #include - // SerialBLEInterface serial_interface; - #if defined(SERIAL_RX) - #include - ArduinoSerialInterface serial_interface; - HardwareSerial companion_serial(1); - #else - #include - ArduinoSerialInterface serial_interface; - #endif -#elif defined(NRF52_PLATFORM) - #ifdef BLE_PIN_CODE - #include - SerialBLEInterface serial_interface; - #elif defined(ETHERNET_ENABLED) - #include - SerialEthernetInterface serial_interface; - #else - #include - ArduinoSerialInterface serial_interface; - #endif -#elif defined(STM32_PLATFORM) - #include - ArduinoSerialInterface serial_interface; -#else - #error "need to define a serial interface" -#endif - /* GLOBAL OBJECTS */ #ifdef DISPLAY_CLASS #include "UITask.h" - UITask ui_task(&board, &serial_interface); + UITask ui_task(&board, &interface_manager); #endif StdRNG fast_rng; @@ -164,26 +168,6 @@ void setup() { false #endif ); - -#ifdef BLE_PIN_CODE - serial_interface.begin(BLE_NAME_PREFIX, the_mesh.getNodePrefs()->node_name, the_mesh.getBLEPin()); - the_mesh.startInterface(serial_interface); -#elif defined(ETHERNET_ENABLED) - Serial.print("Waiting for serial to connect...\n"); - unsigned long timeout = millis(); - while (!Serial) { - if ((millis() - timeout) < 5000) { delay(100); } else { break; } - } - Serial.println("Initializing Ethernet adapter..."); - if (serial_interface.begin()) { - the_mesh.startInterface(serial_interface); - } else { - Serial.println("ETH: Init failed, continuing without Ethernet (mesh only)"); - } -#else - serial_interface.begin(Serial); - the_mesh.startInterface(serial_interface); -#endif #elif defined(RP2040_PLATFORM) LittleFS.begin(); store.begin(); @@ -194,22 +178,6 @@ void setup() { false #endif ); - - //#ifdef WIFI_SSID - // WiFi.begin(WIFI_SSID, WIFI_PWD); - // serial_interface.begin(TCP_PORT); - // #elif defined(BLE_PIN_CODE) - // char dev_name[32+16]; - // sprintf(dev_name, "%s%s", BLE_NAME_PREFIX, the_mesh.getNodeName()); - // serial_interface.begin(dev_name, the_mesh.getBLEPin()); - #if defined(SERIAL_RX) - companion_serial.setPins(SERIAL_RX, SERIAL_TX); - companion_serial.begin(115200); - serial_interface.begin(companion_serial); - #else - serial_interface.begin(Serial); - #endif - the_mesh.startInterface(serial_interface); #elif defined(ESP32) SPIFFS.begin(true); store.begin(); @@ -220,7 +188,17 @@ void setup() { false #endif ); +#else + #error "need to define filesystem" +#endif + +// add bluetooth interface +#if defined(BLE_PIN_CODE) + bluetooth_interface.begin(BLE_NAME_PREFIX, the_mesh.getNodePrefs()->node_name, the_mesh.getBLEPin()); + interface_manager.addInterface(InterfaceType::Bluetooth, &bluetooth_interface); +#endif +// add wifi interface #ifdef WIFI_SSID board.setInhibitSleep(true); // prevent sleep when WiFi is active WiFi.setAutoReconnect(true); @@ -236,23 +214,31 @@ void setup() { }); WiFi.begin(WIFI_SSID, WIFI_PWD); - serial_interface.begin(TCP_PORT); -#elif defined(BLE_PIN_CODE) - serial_interface.begin(BLE_NAME_PREFIX, the_mesh.getNodePrefs()->node_name, the_mesh.getBLEPin()); -#elif defined(SERIAL_RX) + wifi_interface.begin(TCP_PORT); + interface_manager.addInterface(InterfaceType::WiFi, &wifi_interface); +#endif + +// add usb interface +#if defined(ENABLE_USB_INTERFACE) + usb_serial_interface.begin(Serial); + interface_manager.addInterface(InterfaceType::USB, &usb_serial_interface); +#endif + +// add ethernet interface +#if defined(ETHERNET_ENABLED) + ethernet_interface.begin(); + interface_manager.addInterface(InterfaceType::Ethernet, ðernet_interface); +#endif + +// add hardware serial interface +#if defined(SERIAL_RX) companion_serial.setPins(SERIAL_RX, SERIAL_TX); companion_serial.begin(115200); - serial_interface.begin(companion_serial); -#elif defined(ETHERNET_ENABLED) - serial_interface.begin(); -#else - serial_interface.begin(Serial); -#endif - the_mesh.startInterface(serial_interface); -#else - #error "need to define filesystem" + hardware_serial_interface.begin(companion_serial); + interface_manager.addInterface(InterfaceType::HardwareSerial, &hardware_serial_interface); #endif + the_mesh.startInterface(interface_manager); sensors.begin(); #if ENV_INCLUDE_GPS == 1 @@ -268,6 +254,7 @@ void setup() { void loop() { the_mesh.loop(); + interface_manager.loop(); sensors.loop(); #ifdef DISPLAY_CLASS ui_task.loop(); @@ -277,10 +264,6 @@ void loop() { external_watchdog.loop(); #endif -#ifdef ETHERNET_ENABLED - serial_interface.loop(); -#endif - if (!the_mesh.hasPendingWork()) { #if defined(NRF52_PLATFORM) board.sleep(0); // nrf ignores seconds param, sleeps whenever possible diff --git a/examples/companion_radio/ui-new/UITask.cpp b/examples/companion_radio/ui-new/UITask.cpp index 50dc91d2..051f3b31 100644 --- a/examples/companion_radio/ui-new/UITask.cpp +++ b/examples/companion_radio/ui-new/UITask.cpp @@ -289,7 +289,7 @@ public: } else if (_page == HomePage::BLUETOOTH) { display.setColor(UIColor::corp_blue); display.drawXbm((display.width() - 32) / 2, 18, - _task->isSerialEnabled() ? bluetooth_on : bluetooth_off, + _task->isBluetoothEnabled() ? bluetooth_on : bluetooth_off, 32, 32); display.setColor(UIColor::secondary_txt); display.setTextSize(1); @@ -449,10 +449,10 @@ public: return true; } if (c == KEY_ENTER && _page == HomePage::BLUETOOTH) { - if (_task->isSerialEnabled()) { // toggle Bluetooth on/off - _task->disableSerial(); + if (_task->isBluetoothEnabled()) { // toggle Bluetooth on/off + _task->disableBluetooth(); } else { - _task->enableSerial(); + _task->enableBluetooth(); } return true; } diff --git a/examples/companion_radio/ui-new/UITask.h b/examples/companion_radio/ui-new/UITask.h index a77ad6e7..52d3ffa1 100644 --- a/examples/companion_radio/ui-new/UITask.h +++ b/examples/companion_radio/ui-new/UITask.h @@ -4,7 +4,7 @@ #include #include #include -#include +#include #include #include @@ -65,7 +65,7 @@ class UITask : public AbstractUITask { public: - UITask(mesh::MainBoard* board, BaseSerialInterface* serial) : AbstractUITask(board, serial), _display(NULL), _sensors(NULL) { + UITask(mesh::MainBoard* board, MultiSerialInterface* serial) : AbstractUITask(board, serial), _display(NULL), _sensors(NULL) { next_batt_chck = _next_refresh = 0; ui_started_at = 0; curr = NULL; diff --git a/examples/companion_radio/ui-orig/UITask.h b/examples/companion_radio/ui-orig/UITask.h index 60cd0d04..961c07a0 100644 --- a/examples/companion_radio/ui-orig/UITask.h +++ b/examples/companion_radio/ui-orig/UITask.h @@ -54,7 +54,7 @@ class UITask : public AbstractUITask { public: - UITask(mesh::MainBoard* board, BaseSerialInterface* serial) : AbstractUITask(board, serial), _display(NULL), _sensors(NULL) { + UITask(mesh::MainBoard* board, MultiSerialInterface* serial) : AbstractUITask(board, serial), _display(NULL), _sensors(NULL) { _next_refresh = 0; ui_started_at = 0; } diff --git a/examples/companion_radio/ui-tiny/UITask.h b/examples/companion_radio/ui-tiny/UITask.h index 344e48b9..dc689478 100644 --- a/examples/companion_radio/ui-tiny/UITask.h +++ b/examples/companion_radio/ui-tiny/UITask.h @@ -4,7 +4,7 @@ #include #include #include -#include +#include #include #include @@ -71,7 +71,7 @@ class UITask : public AbstractUITask { public: - UITask(mesh::MainBoard* board, BaseSerialInterface* serial) : AbstractUITask(board, serial), _display(NULL), _sensors(NULL) { + UITask(mesh::MainBoard* board, MultiSerialInterface* serial) : AbstractUITask(board, serial), _display(NULL), _sensors(NULL) { next_batt_chck = _next_refresh = 0; _cached_batt_mv = 0; ui_started_at = 0; diff --git a/src/helpers/BaseSerialInterface.h b/src/helpers/BaseSerialInterface.h index e9a3f2ab..23933fcb 100644 --- a/src/helpers/BaseSerialInterface.h +++ b/src/helpers/BaseSerialInterface.h @@ -14,6 +14,7 @@ public: virtual bool isEnabled() const = 0; virtual bool isConnected() const = 0; + virtual void loop() {}; virtual bool isWriteBusy() const = 0; virtual size_t writeFrame(const uint8_t src[], size_t len) = 0; diff --git a/src/helpers/MultiSerialInterface.h b/src/helpers/MultiSerialInterface.h new file mode 100644 index 00000000..f7742b24 --- /dev/null +++ b/src/helpers/MultiSerialInterface.h @@ -0,0 +1,200 @@ +#pragma once + +#include "BaseSerialInterface.h" + +#ifndef MAX_INTERFACES + // ble, usb, wifi, ethernet + #define MAX_INTERFACES 4 +#endif + +enum class InterfaceType : uint8_t { + NONE, + Bluetooth, + USB, + WiFi, + Ethernet, + HardwareSerial +}; + +class MultiSerialInterface : public BaseSerialInterface { +private: + + struct RegisteredInterface { + InterfaceType type = InterfaceType::NONE; + BaseSerialInterface* instance = nullptr; + }; + + bool _enabled = false; + RegisteredInterface _interfaces[MAX_INTERFACES] = {}; + +public: + bool addInterface(InterfaceType type, BaseSerialInterface* iface) { + // make sure an interface was provided + if(iface == nullptr){ + return false; + } + + // put it in the first free slot + for(int i = 0; i < MAX_INTERFACES; i++){ + if(_interfaces[i].instance == nullptr){ + _interfaces[i].instance = iface; + _interfaces[i].type = type; + return true; + } + } + + // no free slots available + return false; + } + + bool removeInterface(BaseSerialInterface* iface) { + // make sure an interface was provided + if(iface == nullptr){ + return false; + } + + // find and remove interface + for(int i = 0; i < MAX_INTERFACES; i++){ + if(_interfaces[i].instance == iface){ + _interfaces[i] = {}; + return true; + } + } + + // interface not found + return false; + } + + void enableBluetooth() { + for(auto iface : _interfaces){ + if(iface.instance && iface.type == InterfaceType::Bluetooth){ + iface.instance->enable(); + } + } + } + + void disableBluetooth() { + for(auto iface : _interfaces){ + if(iface.instance && iface.type == InterfaceType::Bluetooth){ + iface.instance->disable(); + } + } + } + + bool isBluetoothEnabled() { + for(auto iface : _interfaces){ + if(iface.instance && iface.type == InterfaceType::Bluetooth){ + return iface.instance->isEnabled(); + } + } + return false; + } + + // enable all interfaces + void enable() override { + _enabled = true; + for(auto iface : _interfaces){ + if(iface.instance){ + iface.instance->enable(); + } + } + } + + // disable all interfaces + void disable() override { + _enabled = false; + for(auto iface : _interfaces){ + if(iface.instance){ + iface.instance->disable(); + } + } + } + + bool isEnabled() const override { + return _enabled; + } + + bool isConnected() const override { + // not connected when disabled + if(!_enabled){ + return false; + } + + // check if any interface is connected + for(auto iface : _interfaces){ + if(iface.instance && iface.instance->isConnected()) { + return true; + } + } + + // nothing connected + return false; + } + + // loop all interfaces + void loop() override { + for(auto iface : _interfaces){ + if(iface.instance){ + iface.instance->loop(); + } + } + } + + bool isWriteBusy() const override { + // not busy when disabled + if(!_enabled){ + return false; + } + + // check if any interface is busy + for(auto iface : _interfaces){ + if(iface.instance && iface.instance->isEnabled() && iface.instance->isWriteBusy()){ + return true; + } + } + + // nothing busy + return false; + } + + size_t writeFrame(const uint8_t src[], size_t len) override { + // don't write when disabled or nothing provided + if(!_enabled || len == 0){ + return 0; + } + + // write frame to all enabled interfaces + bool allSuccessful = true; + for(auto iface : _interfaces){ + if(iface.instance && iface.instance->isEnabled()){ + if(iface.instance->writeFrame(src, len) != len){ + allSuccessful = false; + } + } + } + + // report success if all writes completed successfully + return allSuccessful ? len : 0; + } + + size_t checkRecvFrame(uint8_t dest[]) override { + // don't read when disabled + if(!_enabled){ + return 0; + } + + // try to read a frame from any enabled interface + for(auto iface : _interfaces){ + if(iface.instance && iface.instance->isEnabled()){ + size_t frameSize = iface.instance->checkRecvFrame(dest); + if(frameSize > 0){ + return frameSize; + } + } + } + + // no frame received + return 0; + } + +}; diff --git a/src/helpers/ethernet/SerialEthernetInterface.cpp b/src/helpers/ethernet/SerialEthernetInterface.cpp new file mode 100644 index 00000000..22bcabc1 --- /dev/null +++ b/src/helpers/ethernet/SerialEthernetInterface.cpp @@ -0,0 +1,140 @@ +#include "SerialEthernetInterface.h" + +#define RECV_STATE_IDLE 0 +#define RECV_STATE_HDR_FOUND 1 +#define RECV_STATE_LEN1_FOUND 2 +#define RECV_STATE_LEN2_FOUND 3 + +bool SerialEthernetInterface::begin() { + return true; +} + +void SerialEthernetInterface::enable() { + if (_isEnabled) return; + _isEnabled = true; + clearBuffers(); +} + +void SerialEthernetInterface::disable() { + _isEnabled = false; +} + +size_t SerialEthernetInterface::writeFrame(const uint8_t src[], size_t len) { + if (len > MAX_FRAME_SIZE) { + ETHERNET_DEBUG_PRINTLN("writeFrame(), frame too big, len=%d\n", len); + return 0; + } + + if (isConnected() && len > 0) { + if (send_queue_len >= FRAME_QUEUE_SIZE) { + ETHERNET_DEBUG_PRINTLN("writeFrame(), send_queue is full!"); + return 0; + } + + send_queue[send_queue_len].len = len; // add to send queue + memcpy(send_queue[send_queue_len].buf, src, len); + send_queue_len++; + + return len; + } + return 0; +} + +bool SerialEthernetInterface::isWriteBusy() const { + return false; +} + +void SerialEthernetInterface::onClientConnected() { + _state = RECV_STATE_IDLE; + _frame_len = 0; + _rx_len = 0; +} + +size_t SerialEthernetInterface::checkRecvFrame(uint8_t dest[]) { + + if (isConnected()) { + if (send_queue_len > 0) { // first, check send queue + + _last_write = millis(); + int len = send_queue[0].len; + +#if ETHERNET_RAW_LINE + ETHERNET_DEBUG_PRINTLN("TX line len=%d", len); + client.write(send_queue[0].buf, len); + client.write("\r\n", 2); +#else + uint8_t pkt[3+len]; // use same header as serial interface so client can delimit frames + pkt[0] = '>'; + pkt[1] = (len & 0xFF); // LSB + pkt[2] = (len >> 8); // MSB + memcpy(&pkt[3], send_queue[0].buf, send_queue[0].len); + ETHERNET_DEBUG_PRINTLN("Sending frame len=%d", len); + #if ETHERNET_DEBUG_LOGGING && ARDUINO + ETHERNET_DEBUG_PRINTLN("TX frame len=%d", len); + #endif + write(pkt, 3 + len); +#endif + send_queue_len--; + for (int i = 0; i < send_queue_len; i++) { // delete top item from queue + send_queue[i] = send_queue[i + 1]; + } + } else { + while (available()) { + int c = read(); + if (c < 0) break; + +#if ETHERNET_RAW_LINE + if (c == '\r' || c == '\n') { + if (_rx_len == 0) { + continue; + } + uint16_t out_len = _rx_len; + if (out_len > MAX_FRAME_SIZE) out_len = MAX_FRAME_SIZE; + memcpy(dest, _rx_buf, out_len); + _rx_len = 0; + return out_len; + } + if (_rx_len < MAX_FRAME_SIZE) { + _rx_buf[_rx_len] = (uint8_t)c; + _rx_len++; + } +#else + switch (_state) { + case RECV_STATE_IDLE: + if (c == '<') { + _state = RECV_STATE_HDR_FOUND; + } + break; + case RECV_STATE_HDR_FOUND: + _frame_len = (uint8_t)c; + _state = RECV_STATE_LEN1_FOUND; + break; + case RECV_STATE_LEN1_FOUND: + _frame_len |= ((uint16_t)c) << 8; + _rx_len = 0; + _state = _frame_len > 0 ? RECV_STATE_LEN2_FOUND : RECV_STATE_IDLE; + break; + default: + if (_rx_len < MAX_FRAME_SIZE) { + _rx_buf[_rx_len] = (uint8_t)c; + } + _rx_len++; + if (_rx_len >= _frame_len) { + if (_frame_len > MAX_FRAME_SIZE) { + _frame_len = MAX_FRAME_SIZE; + } + #if ETHERNET_DEBUG_LOGGING && ARDUINO + ETHERNET_DEBUG_PRINTLN("RX frame len=%d", _frame_len); + #endif + memcpy(dest, _rx_buf, _frame_len); + _state = RECV_STATE_IDLE; + return _frame_len; + } + } +#endif + } + } + } + + return 0; +} diff --git a/src/helpers/ethernet/SerialEthernetInterface.h b/src/helpers/ethernet/SerialEthernetInterface.h new file mode 100644 index 00000000..46789f71 --- /dev/null +++ b/src/helpers/ethernet/SerialEthernetInterface.h @@ -0,0 +1,75 @@ +#pragma once + +#include "../BaseSerialInterface.h" + +#ifndef ETHERNET_TCP_PORT + #define ETHERNET_TCP_PORT 5000 +#endif +// define ETHERNET_RAW_LINE=1 to use raw line-based CLI instead of framed packets + +class SerialEthernetInterface : public BaseSerialInterface { + bool _isEnabled; + unsigned long _last_write; + uint8_t _state; + uint16_t _frame_len; + uint16_t _rx_len; + uint8_t _rx_buf[MAX_FRAME_SIZE]; + + struct Frame { + uint8_t len; + uint8_t buf[MAX_FRAME_SIZE]; + }; + + #define FRAME_QUEUE_SIZE 4 + int send_queue_len; + Frame send_queue[FRAME_QUEUE_SIZE]; + + void clearBuffers() { + send_queue_len = 0; + _state = 0; + _frame_len = 0; + _rx_len = 0; + } + + protected: + + public: + SerialEthernetInterface() { + _isEnabled = false; + _last_write = 0; + send_queue_len = 0; + _state = 0; + _frame_len = 0; + _rx_len = 0; + } + bool begin(); + + void onClientConnected(); + + // BaseSerialInterface methods + void enable() override; + void disable() override; + bool isEnabled() const override { return _isEnabled; } + + bool isConnected() const override; + bool isWriteBusy() const override; + + size_t writeFrame(const uint8_t src[], size_t len) override; + size_t checkRecvFrame(uint8_t dest[]) override; + + virtual int available() = 0; + virtual int read() = 0; + virtual size_t write(const uint8_t *buf, size_t size) = 0; +}; + + +#if ETHERNET_DEBUG_LOGGING && ARDUINO + #include + #define ETHERNET_DEBUG_PRINT(F, ...) Serial.printf("ETH: " F, ##__VA_ARGS__) + #define ETHERNET_DEBUG_PRINTLN(F, ...) Serial.printf("ETH: " F "\n", ##__VA_ARGS__) + #define ETHERNET_DEBUG_PRINT_IP(name, ip) Serial.printf("ETH: " name ": %u.%u.%u.%u" "\n", ip[0], ip[1], ip[2], ip[3]) +#else + #define ETHERNET_DEBUG_PRINT(...) {} + #define ETHERNET_DEBUG_PRINTLN(...) {} + #define ETHERNET_DEBUG_PRINT_IP(...) {} +#endif diff --git a/src/helpers/ethernet/ch390/CH390EthernetInterface.cpp b/src/helpers/ethernet/ch390/CH390EthernetInterface.cpp index f7ee5503..7f696245 100644 --- a/src/helpers/ethernet/ch390/CH390EthernetInterface.cpp +++ b/src/helpers/ethernet/ch390/CH390EthernetInterface.cpp @@ -1,12 +1,33 @@ #include "CH390EthernetInterface.h" -#define RECV_STATE_IDLE 0 -#define RECV_STATE_HDR_FOUND 1 -#define RECV_STATE_LEN1_FOUND 2 -#define RECV_STATE_LEN2_FOUND 3 +void onWiFiEvent(WiFiEvent_t event) { + switch(event){ + case ARDUINO_EVENT_ETH_START: + ETHERNET_DEBUG_PRINTLN("Ethernet Started"); + break; + case ARDUINO_EVENT_ETH_CONNECTED: + ETHERNET_DEBUG_PRINTLN("Ethernet Connected"); + break; + case ARDUINO_EVENT_ETH_DISCONNECTED: + ETHERNET_DEBUG_PRINTLN("Ethernet Disconnected"); + break; + case ARDUINO_EVENT_ETH_GOT_IP: + ETHERNET_DEBUG_PRINTLN("Ethernet Got IP"); + ETHERNET_DEBUG_PRINT_IP("IP Address", CH390.localIP()); + ETHERNET_DEBUG_PRINT_IP("Subnet Mask", CH390.subnetMask()); + ETHERNET_DEBUG_PRINT_IP("Gateway", CH390.gatewayIP()); + ETHERNET_DEBUG_PRINT_IP("DNS", CH390.dnsIP()); + ETHERNET_DEBUG_PRINTLN("MAC Address: %s", CH390.macAddress().c_str()); + break; + default: + break; + } +} bool CH390EthernetInterface::begin() { - ETHERNET_DEBUG_PRINTLN("Ethernet initializing"); + + // listen to ethernet events + WiFi.onEvent(onWiFiEvent); // Init CH390 ch390_config_t config = CH390_DEFAULT_CONFIG(); @@ -29,179 +50,45 @@ bool CH390EthernetInterface::begin() { #endif // Start Server - server.begin(); + server.begin(ETHERNET_TCP_PORT); ETHERNET_DEBUG_PRINTLN("listening on TCP port: %d", ETHERNET_TCP_PORT); return true; } -void CH390EthernetInterface::enable() { - if (_isEnabled) return; - _isEnabled = true; - clearBuffers(); +int CH390EthernetInterface::available() { + return client.available(); } -void CH390EthernetInterface::disable() { - _isEnabled = false; +int CH390EthernetInterface::read() { + return client.read(); } -size_t CH390EthernetInterface::writeFrame(const uint8_t src[], size_t len) { - if (len > MAX_FRAME_SIZE) { - ETHERNET_DEBUG_PRINTLN("writeFrame(), frame too big, len=%d\n", len); - return 0; - } - - if (deviceConnected && len > 0) { - if (send_queue_len >= FRAME_QUEUE_SIZE) { - ETHERNET_DEBUG_PRINTLN("writeFrame(), send_queue is full!"); - return 0; - } - - send_queue[send_queue_len].len = len; // add to send queue - memcpy(send_queue[send_queue_len].buf, src, len); - send_queue_len++; - - return len; - } - return 0; +size_t CH390EthernetInterface::write(const uint8_t *buf, size_t size) { + return client.write(buf, size); } -bool CH390EthernetInterface::isWriteBusy() const { - return false; +bool CH390EthernetInterface::isConnected() const { + return _isConnected; } -size_t CH390EthernetInterface::checkRecvFrame(uint8_t dest[]) { +void CH390EthernetInterface::loop() { + if (server.hasClient()) { auto newClient = server.available(); if (newClient) { - IPAddress new_ip = newClient.remoteIP(); - uint16_t new_port = newClient.remotePort(); - ETHERNET_DEBUG_PRINTLN( - "New client accepted %u.%u.%u.%u:%u", - new_ip[0], new_ip[1], new_ip[2], new_ip[3], new_port); - - deviceConnected = false; + IPAddress remoteIp = newClient.remoteIP(); + uint16_t remotePort = newClient.remotePort(); + ETHERNET_DEBUG_PRINTLN("New client accepted %u.%u.%u.%u:%u", remoteIp[0], remoteIp[1], remoteIp[2], remoteIp[3], remotePort); if (client) { ETHERNET_DEBUG_PRINTLN("Closing previous client"); client.stop(); } - _state = RECV_STATE_IDLE; - _frame_len = 0; - _rx_len = 0; client = newClient; - ETHERNET_DEBUG_PRINTLN("Switched to new client"); + onClientConnected(); } } - if (client.connected()) { - if (!deviceConnected) { - ETHERNET_DEBUG_PRINTLN( - "Got connection %u.%u.%u.%u:%u", - client.remoteIP()[0], - client.remoteIP()[1], - client.remoteIP()[2], - client.remoteIP()[3], - client.remotePort()); - deviceConnected = true; - } - } else { - if (deviceConnected) { - deviceConnected = false; - ETHERNET_DEBUG_PRINTLN("Disconnected"); - } - } + _isConnected = client.connected(); - if (deviceConnected) { - if (send_queue_len > 0) { // first, check send queue - - _last_write = millis(); - int len = send_queue[0].len; - -#if ETHERNET_RAW_LINE - ETHERNET_DEBUG_PRINTLN("TX line len=%d", len); - client.write(send_queue[0].buf, len); - client.write("\r\n", 2); -#else - uint8_t pkt[3+len]; // use same header as serial interface so client can delimit frames - pkt[0] = '>'; - pkt[1] = (len & 0xFF); // LSB - pkt[2] = (len >> 8); // MSB - memcpy(&pkt[3], send_queue[0].buf, send_queue[0].len); - ETHERNET_DEBUG_PRINTLN("Sending frame len=%d", len); - #if ETHERNET_DEBUG_LOGGING && ARDUINO - ETHERNET_DEBUG_PRINTLN("TX frame len=%d", len); - #endif - client.write(pkt, 3 + len); -#endif - send_queue_len--; - for (int i = 0; i < send_queue_len; i++) { // delete top item from queue - send_queue[i] = send_queue[i + 1]; - } - } else { - while (client.available()) { - int c = client.read(); - if (c < 0) break; - -#if ETHERNET_RAW_LINE - if (c == '\r' || c == '\n') { - if (_rx_len == 0) { - continue; - } - uint16_t out_len = _rx_len; - if (out_len > MAX_FRAME_SIZE) out_len = MAX_FRAME_SIZE; - memcpy(dest, _rx_buf, out_len); - _rx_len = 0; - return out_len; - } - if (_rx_len < MAX_FRAME_SIZE) { - _rx_buf[_rx_len] = (uint8_t)c; - _rx_len++; - } -#else - switch (_state) { - case RECV_STATE_IDLE: - if (c == '<') { - _state = RECV_STATE_HDR_FOUND; - } - break; - case RECV_STATE_HDR_FOUND: - _frame_len = (uint8_t)c; - _state = RECV_STATE_LEN1_FOUND; - break; - case RECV_STATE_LEN1_FOUND: - _frame_len |= ((uint16_t)c) << 8; - _rx_len = 0; - _state = _frame_len > 0 ? RECV_STATE_LEN2_FOUND : RECV_STATE_IDLE; - break; - default: - if (_rx_len < MAX_FRAME_SIZE) { - _rx_buf[_rx_len] = (uint8_t)c; - } - _rx_len++; - if (_rx_len >= _frame_len) { - if (_frame_len > MAX_FRAME_SIZE) { - _frame_len = MAX_FRAME_SIZE; - } - #if ETHERNET_DEBUG_LOGGING && ARDUINO - ETHERNET_DEBUG_PRINTLN("RX frame len=%d", _frame_len); - #endif - memcpy(dest, _rx_buf, _frame_len); - _state = RECV_STATE_IDLE; - return _frame_len; - } - } -#endif - } - } - } - - return 0; -} - -bool CH390EthernetInterface::isConnected() const { - return deviceConnected; -} - -void CH390EthernetInterface::loop() { - } diff --git a/src/helpers/ethernet/ch390/CH390EthernetInterface.h b/src/helpers/ethernet/ch390/CH390EthernetInterface.h index d638319b..09c3fc23 100644 --- a/src/helpers/ethernet/ch390/CH390EthernetInterface.h +++ b/src/helpers/ethernet/ch390/CH390EthernetInterface.h @@ -1,80 +1,30 @@ #pragma once -#include "../../BaseSerialInterface.h" +#include "../SerialEthernetInterface.h" #include #include #include #include #include -#ifndef ETHERNET_TCP_PORT - #define ETHERNET_TCP_PORT 5000 -#endif -// define ETHERNET_RAW_LINE=1 to use raw line-based CLI instead of framed packets - -class CH390EthernetInterface : public BaseSerialInterface { - bool deviceConnected; - bool _isEnabled; - unsigned long _last_write; - uint8_t _state; - uint16_t _frame_len; - uint16_t _rx_len; - uint8_t _rx_buf[MAX_FRAME_SIZE]; - +class CH390EthernetInterface : public SerialEthernetInterface { + + bool _isConnected; WiFiServer server; WiFiClient client; - struct Frame { - uint8_t len; - uint8_t buf[MAX_FRAME_SIZE]; - }; - - #define FRAME_QUEUE_SIZE 4 - int send_queue_len; - Frame send_queue[FRAME_QUEUE_SIZE]; - - void clearBuffers() { - send_queue_len = 0; - _state = 0; - _frame_len = 0; - _rx_len = 0; - } - - protected: - public: - CH390EthernetInterface() : server(ETHERNET_TCP_PORT) { - deviceConnected = false; - _isEnabled = false; - _last_write = 0; - send_queue_len = 0; - _state = 0; - _frame_len = 0; - _rx_len = 0; + CH390EthernetInterface(){ + _isConnected = false; } + bool begin(); - void loop(); + void loop() override; // BaseSerialInterface methods - void enable() override; - void disable() override; - bool isEnabled() const override { return _isEnabled; } - bool isConnected() const override; - bool isWriteBusy() const override; - size_t writeFrame(const uint8_t src[], size_t len) override; - size_t checkRecvFrame(uint8_t dest[]) override; + int available() override; + int read() override; + size_t write(const uint8_t *buf, size_t size) override; }; - - -#if ETHERNET_DEBUG_LOGGING && ARDUINO - #include - #define ETHERNET_DEBUG_PRINT(F, ...) Serial.printf("ETH: " F, ##__VA_ARGS__) - #define ETHERNET_DEBUG_PRINTLN(F, ...) Serial.printf("ETH: " F "\n", ##__VA_ARGS__) - #define ETHERNET_DEBUG_PRINT_IP(name, ip) Serial.printf(name ": %u.%u.%u.%u" "\n", ip[0], ip[1], ip[2], ip[3]) -#else - #define ETHERNET_DEBUG_PRINT(...) {} - #define ETHERNET_DEBUG_PRINTLN(...) {} - #define ETHERNET_DEBUG_PRINT_IP(...) {} -#endif diff --git a/variants/thinknode_m7/platformio.ini b/variants/thinknode_m7/platformio.ini index 43ff61c3..2ff6fc0a 100644 --- a/variants/thinknode_m7/platformio.ini +++ b/variants/thinknode_m7/platformio.ini @@ -1,6 +1,8 @@ [ThinkNode_M7] extends = esp32_base board = thinknode_m7 +board_upload.flash_size = 8MB +board_build.partitions = default_8MB.csv build_flags = ${esp32_base.build_flags} -I src/helpers/esp32 -I variants/thinknode_m7 @@ -46,6 +48,7 @@ build_flags = -D ETH_INT_PIN=45 -D ETHERNET_DEBUG_LOGGING=1 build_src_filter = + + + lib_deps = https://github.com/liamcottle/ESP32-CH390.git#47b401f1de546118b03c18b5689dedec45871f2d @@ -88,6 +91,7 @@ lib_deps = extends = ThinkNode_M7 build_flags = ${ThinkNode_M7.build_flags} + ${ThinkNode_M7_ethernet.build_flags} -I examples/companion_radio/ui-orig -D DISPLAY_CLASS=NullDisplayDriver -D MAX_CONTACTS=350 @@ -97,6 +101,7 @@ build_flags = ; -D BLE_DEBUG_LOGGING=1 ; -D MESH_PACKET_LOGGING=1 build_src_filter = ${ThinkNode_M7.build_src_filter} + ${ThinkNode_M7_ethernet.build_src_filter} + + + @@ -104,6 +109,7 @@ build_src_filter = ${ThinkNode_M7.build_src_filter} +<../examples/companion_radio/ui-orig/*.cpp> lib_deps = ${ThinkNode_M7.lib_deps} + ${ThinkNode_M7_ethernet.lib_deps} densaugeo/base64 @ ~1.4.0 [env:ThinkNode_M7_companion_radio_usb]