diff --git a/build.sh b/build.sh index 6e2e8cfb..506f9a39 100755 --- a/build.sh +++ b/build.sh @@ -233,6 +233,7 @@ build_companion_firmwares() { build_wio_tracker_l1_firmwares() { build_firmware "WioTrackerL1_companion_radio_usb_settings" build_firmware "WioTrackerL1_companion_radio_ble_settings" + build_firmware "WioTrackerL1_companion_radio_dual_settings" } build_room_server_firmwares() { diff --git a/examples/companion_radio/main.cpp b/examples/companion_radio/main.cpp index 879d04be..f0c87504 100644 --- a/examples/companion_radio/main.cpp +++ b/examples/companion_radio/main.cpp @@ -71,7 +71,10 @@ static uint32_t _atoi(const char* sp) { ArduinoSerialInterface serial_interface; #endif #elif defined(NRF52_PLATFORM) - #ifdef BLE_PIN_CODE + #ifdef DUAL_SERIAL + #include + DualSerialInterface serial_interface; + #elif defined(BLE_PIN_CODE) #include SerialBLEInterface serial_interface; #else @@ -150,7 +153,9 @@ void setup() { #endif ); -#ifdef BLE_PIN_CODE +#ifdef DUAL_SERIAL + serial_interface.begin(BLE_NAME_PREFIX, the_mesh.getNodePrefs()->node_name, the_mesh.getBLEPin(), Serial); +#elif defined(BLE_PIN_CODE) serial_interface.begin(BLE_NAME_PREFIX, the_mesh.getNodePrefs()->node_name, the_mesh.getBLEPin()); #else serial_interface.begin(Serial); diff --git a/src/helpers/nrf52/DualSerialInterface.h b/src/helpers/nrf52/DualSerialInterface.h new file mode 100644 index 00000000..b7442040 --- /dev/null +++ b/src/helpers/nrf52/DualSerialInterface.h @@ -0,0 +1,49 @@ +#pragma once + +#include "../BaseSerialInterface.h" +#include "../ArduinoSerialInterface.h" +#include "SerialBLEInterface.h" + +// Wraps BLE + USB serial interfaces: BLE takes priority when connected, +// USB is always ready as a fallback. Both state machines run continuously. +class DualSerialInterface : public BaseSerialInterface { + SerialBLEInterface _ble; + ArduinoSerialInterface _usb; + uint8_t _ble_buf[MAX_FRAME_SIZE]; + uint8_t _usb_buf[MAX_FRAME_SIZE]; + +public: + void begin(const char* ble_prefix, char* node_name, uint32_t pin_code, Stream& usb_stream) { + _ble.begin(ble_prefix, node_name, pin_code); + _usb.begin(usb_stream); + } + + void enable() override { _ble.enable(); _usb.enable(); } + void disable() override { _ble.disable(); _usb.disable(); } + bool isEnabled() const override { return _ble.isEnabled() || _usb.isEnabled(); } + + // Reports BLE connection state (used for UI indicator and buzzer Auto mode) + bool isConnected() const override { return _ble.isConnected(); } + + bool isWriteBusy() const override { + return _ble.isConnected() ? _ble.isWriteBusy() : _usb.isWriteBusy(); + } + + size_t writeFrame(const uint8_t src[], size_t len) override { + return _ble.isConnected() ? _ble.writeFrame(src, len) : _usb.writeFrame(src, len); + } + + size_t checkRecvFrame(uint8_t dest[]) override { + // Always pump both state machines: BLE needs this for TX queue drain and + // advertising watchdog; USB needs it to drain its input buffer. + size_t ble_len = _ble.checkRecvFrame(_ble_buf); + size_t usb_len = _usb.checkRecvFrame(_usb_buf); + + if (_ble.isConnected()) { + if (ble_len > 0) { memcpy(dest, _ble_buf, ble_len); return ble_len; } + } else { + if (usb_len > 0) { memcpy(dest, _usb_buf, usb_len); return usb_len; } + } + return 0; + } +}; diff --git a/variants/wio-tracker-l1/platformio.ini b/variants/wio-tracker-l1/platformio.ini index 755bddc0..4edb67bd 100644 --- a/variants/wio-tracker-l1/platformio.ini +++ b/variants/wio-tracker-l1/platformio.ini @@ -135,3 +135,17 @@ extends = WioTrackerL1CompanionBLE build_flags = ${WioTrackerL1CompanionBLE.build_flags} -D UI_HAS_JOYSTICK_UPDOWN=1 extra_scripts = post:create-uf2.py + +[WioTrackerL1CompanionDual] +extends = WioTrackerL1CompanionBLE +build_flags = ${WioTrackerL1CompanionBLE.build_flags} + -D DUAL_SERIAL=1 + +[env:WioTrackerL1_companion_radio_dual] +extends = WioTrackerL1CompanionDual + +[env:WioTrackerL1_companion_radio_dual_settings] +extends = WioTrackerL1CompanionDual +build_flags = ${WioTrackerL1CompanionDual.build_flags} + -D UI_HAS_JOYSTICK_UPDOWN=1 +extra_scripts = post:create-uf2.py