diff --git a/examples/companion_radio/main.cpp b/examples/companion_radio/main.cpp index eff9efca4..9988bb769 100644 --- a/examples/companion_radio/main.cpp +++ b/examples/companion_radio/main.cpp @@ -43,7 +43,8 @@ static uint32_t _atoi(const char* sp) { #endif #elif defined(BLE_PIN_CODE) #include - SerialBLEInterface serial_interface; + #include + SerialDualInterface serial_interface; #elif defined(SERIAL_RX) #include ArduinoSerialInterface serial_interface; @@ -73,7 +74,8 @@ static uint32_t _atoi(const char* sp) { #elif defined(NRF52_PLATFORM) #ifdef BLE_PIN_CODE #include - SerialBLEInterface serial_interface; + #include + SerialDualInterface serial_interface; #else #include ArduinoSerialInterface serial_interface; @@ -151,7 +153,7 @@ void setup() { ); #ifdef BLE_PIN_CODE - serial_interface.begin(BLE_NAME_PREFIX, the_mesh.getNodePrefs()->node_name, the_mesh.getBLEPin()); + serial_interface.begin(BLE_NAME_PREFIX, the_mesh.getNodePrefs()->node_name, the_mesh.getBLEPin(), Serial); #else serial_interface.begin(Serial); #endif @@ -198,7 +200,7 @@ 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()); + serial_interface.begin(BLE_NAME_PREFIX, the_mesh.getNodePrefs()->node_name, the_mesh.getBLEPin(), Serial); #elif defined(SERIAL_RX) companion_serial.setPins(SERIAL_RX, SERIAL_TX); companion_serial.begin(115200); diff --git a/examples/companion_radio/ui-new/UITask.cpp b/examples/companion_radio/ui-new/UITask.cpp index 265532be0..9ce10dcc9 100644 --- a/examples/companion_radio/ui-new/UITask.cpp +++ b/examples/companion_radio/ui-new/UITask.cpp @@ -220,7 +220,7 @@ class HomeScreen : public UIScreen { display.setTextSize(1); display.drawTextCentered(display.width() / 2, 43, "< Connected >"); - } else if (the_mesh.getBLEPin() != 0) { // BT pin + } else if (the_mesh.getBLEPin() != 0 && _task->isSerialEnabled()) { // BT pin display.setColor(DisplayDriver::RED); display.setTextSize(2); sprintf(tmp, "Pin:%d", the_mesh.getBLEPin()); diff --git a/src/helpers/ArduinoSerialInterface.cpp b/src/helpers/ArduinoSerialInterface.cpp index a01fa5866..2412edc7d 100644 --- a/src/helpers/ArduinoSerialInterface.cpp +++ b/src/helpers/ArduinoSerialInterface.cpp @@ -13,8 +13,12 @@ void ArduinoSerialInterface::disable() { _isEnabled = false; } -bool ArduinoSerialInterface::isConnected() const { - return true; // no way of knowing, so assume yes +bool ArduinoSerialInterface::isConnected() const { +#if defined(NRF52_PLATFORM) || defined(ESP32_PLATFORM) + return Serial; // operator bool() checks connection (USB CDC DTR, UART init) +#else + return true; // platforms without operator bool() on Serial have no way of knowing, so assume yes +#endif } bool ArduinoSerialInterface::isWriteBusy() const { diff --git a/src/helpers/SerialDualInterface.h b/src/helpers/SerialDualInterface.h new file mode 100644 index 000000000..d35bb1264 --- /dev/null +++ b/src/helpers/SerialDualInterface.h @@ -0,0 +1,73 @@ +#pragma once + +#include "ArduinoSerialInterface.h" + +template +class SerialDualInterface : public BaseSerialInterface { + BLEInterface _ble; + ArduinoSerialInterface _usb; + bool _bleWasConnected; + +public: + SerialDualInterface() : _bleWasConnected(false) { } + + void begin(const char* prefix, char* name, uint32_t pin_code, Stream& serial) { + _ble.begin(prefix, name, pin_code); + _usb.begin(serial); + } + + // enable/disable only control BLE, USB is always available + void enable() override { + _ble.enable(); + } + + void disable() override { + _ble.disable(); + } + + bool isEnabled() const override { + return _ble.isEnabled() || _usb.isEnabled(); + } + + bool isConnected() const override { + return _ble.isConnected() || _usb.isConnected(); + } + + bool isWriteBusy() const override { + if (_ble.isConnected()) return _ble.isWriteBusy(); + return _usb.isWriteBusy(); + } + + size_t writeFrame(const uint8_t src[], size_t len) override { + if (_ble.isConnected()) return _ble.writeFrame(src, len); + return _usb.writeFrame(src, len); + } + + size_t checkRecvFrame(uint8_t dest[]) override { + // Always call BLE first, it needs polling for send queue draining + // and advertising watchdog, even when USB is the active transport + size_t n = _ble.checkRecvFrame(dest); + + bool bleNow = _ble.isConnected(); + if (bleNow != _bleWasConnected) { + if (bleNow) { + // BLE just connected, wait for any pending USB write to finish + while (_usb.isWriteBusy()) { } + _usb.disable(); + } else { + // BLE just disconnected, re-enable USB (resets state machine, + // discarding any stale bytes from the previous session) + _usb.enable(); + } + _bleWasConnected = bleNow; + } + + if (n > 0) return n; // got a BLE frame + + // Only check USB when BLE is not connected + if (!bleNow) { + return _usb.checkRecvFrame(dest); + } + return 0; + } +};