mirror of
https://github.com/MarekZegare4/MeshCore-Solo.git
synced 2026-11-01 10:21:59 +00:00
refactor(companion): the app's links through upstream's MultiSerialInterface
- Bluetooth + USB (+ WiFi/Ethernet/UART per build) in one interface_manager,
as upstream; frames go to every enabled link, USB is read while BT is
connected. DUAL_SERIAL now means ENABLE_USB_INTERFACE.
- WiFi interface with upstream's wifi_* prefs and set/get wifi.* CLI.
- isClientConnected moved into ArduinoSerialInterface (DTR on nRF52 and
ESP32 native CDC).
- UI uses isBluetoothEnabled/enableBluetooth/disableBluetooth; turning BT
off leaves USB and the other links on.
- DualSerialInterface removed.
- Sim build gains DynamicConfigSerializer.cpp (broken since bd64c203).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -22,6 +22,17 @@ bool ArduinoSerialInterface::isConnected() const {
|
||||
return true; // no way of knowing, so assume yes
|
||||
}
|
||||
|
||||
bool ArduinoSerialInterface::isClientConnected() const {
|
||||
#if defined(NRF52_PLATFORM) || (defined(ARDUINO_USB_MODE) && ARDUINO_USB_MODE == 0 && defined(ARDUINO_USB_CDC_ON_BOOT) && ARDUINO_USB_CDC_ON_BOOT == 1)
|
||||
// (bool)Serial: the host has the CDC port open (DTR) -- a serial monitor
|
||||
// counts too, charging alone doesn't.
|
||||
return _serial == (Stream*)&Serial && (bool)Serial;
|
||||
#else
|
||||
// A UART bridge: (bool)Serial is just "begun", always true.
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool ArduinoSerialInterface::isWriteBusy() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -27,6 +27,9 @@ public:
|
||||
bool isEnabled() const override { return _isEnabled; }
|
||||
|
||||
bool isConnected() const override;
|
||||
// A host holding the port open: native USB tells (DTR), a UART bridge
|
||||
// can't, so it never counts as an app being there.
|
||||
bool isClientConnected() const override;
|
||||
|
||||
bool isWriteBusy() const override;
|
||||
size_t writeFrame(const uint8_t src[], size_t len) override;
|
||||
|
||||
@@ -14,6 +14,7 @@ public:
|
||||
virtual bool isEnabled() const = 0;
|
||||
|
||||
virtual bool isConnected() const = 0;
|
||||
virtual void loop() {};
|
||||
// Returns true when a BLE companion app is connected. Default delegates to
|
||||
// isConnected(); override in dual-interface wrappers that always report
|
||||
// isConnected()=true but still need to distinguish BLE from USB state.
|
||||
|
||||
@@ -131,6 +131,38 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
// A companion app on Bluetooth (the pairing PIN, the BT indicator).
|
||||
bool isBLEConnected() const override {
|
||||
for(auto iface : _interfaces){
|
||||
if(iface.instance && iface.type == InterfaceType::Bluetooth && iface.instance->isEnabled() && iface.instance->isConnected()){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// A companion app on any of them (each says how it knows).
|
||||
bool isClientConnected() const override {
|
||||
if(!_enabled){
|
||||
return false;
|
||||
}
|
||||
for(auto iface : _interfaces){
|
||||
if(iface.instance && iface.instance->isEnabled() && iface.instance->isClientConnected()){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool hasPendingFrames() const override {
|
||||
for(auto iface : _interfaces){
|
||||
if(iface.instance && iface.instance->isEnabled() && iface.instance->hasPendingFrames()){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// loop all interfaces
|
||||
void loop() override {
|
||||
for(auto iface : _interfaces){
|
||||
|
||||
@@ -1,91 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "../BaseSerialInterface.h"
|
||||
#include "../ArduinoSerialInterface.h"
|
||||
#include "SerialBLEInterface.h"
|
||||
|
||||
// ESP32 counterpart of helpers/nrf52/DualSerialInterface.h: wraps BLE + USB
|
||||
// serial so one build serves both companion transports. BLE takes priority when
|
||||
// connected, USB is always ready as a fallback.
|
||||
// enable()/disable() control BLE only — USB is always on.
|
||||
// BLE state machine is only pumped when BLE is enabled; USB is not read while
|
||||
// BLE is connected.
|
||||
class DualSerialInterface : public BaseSerialInterface {
|
||||
SerialBLEInterface _ble;
|
||||
ArduinoSerialInterface _usb;
|
||||
uint8_t _ble_buf[MAX_FRAME_SIZE];
|
||||
uint8_t _usb_buf[MAX_FRAME_SIZE];
|
||||
bool _ble_enabled;
|
||||
bool _ble_was_connected;
|
||||
|
||||
public:
|
||||
DualSerialInterface() : _ble_enabled(false), _ble_was_connected(false) {}
|
||||
|
||||
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);
|
||||
_usb.enable(); // USB is always on
|
||||
}
|
||||
|
||||
void enable() override { _ble.enable(); _ble_enabled = true; }
|
||||
void disable() override { _ble.disable(); _ble_enabled = false; }
|
||||
bool isEnabled() const override { return _ble_enabled; }
|
||||
|
||||
// Always true — USB is always available as fallback, so the mesh can send.
|
||||
bool isConnected() const override { return true; }
|
||||
// True only when a BLE companion app is paired and connected.
|
||||
bool isBLEConnected() const override { return _ble_enabled && _ble.isConnected(); }
|
||||
#if defined(ARDUINO_USB_MODE) && ARDUINO_USB_MODE == 0 && defined(ARDUINO_USB_CDC_ON_BOOT) && ARDUINO_USB_CDC_ON_BOOT == 1
|
||||
// Native USB CDC (e.g. Heltec V4): Serial's bool operator reflects whether
|
||||
// the host actually has the port open, same DTR-style signal the nRF52
|
||||
// version reads. Counts as a connected client same as BLE, matching that
|
||||
// version's behaviour.
|
||||
bool isClientConnected() const override { return isBLEConnected() || (bool)Serial; }
|
||||
#else
|
||||
// BLE only, unlike the nRF52 version, which also counts a USB host holding the
|
||||
// CDC port open. These boards reach USB through a UART bridge rather than
|
||||
// native CDC, so there is no DTR to read: (bool)Serial is a plain "is the
|
||||
// peripheral initialised", i.e. always true once begin() has run, and
|
||||
// ArduinoSerialInterface::isConnected() likewise hardcodes true. Reporting
|
||||
// that as a connected client would permanently convince the UI an app is
|
||||
// watching the device and stop it waking the display for new messages.
|
||||
bool isClientConnected() const override { return isBLEConnected(); }
|
||||
#endif
|
||||
|
||||
// USB can't tell without reading it.
|
||||
bool hasPendingFrames() const override {
|
||||
return (_ble_enabled && _ble.isConnected()) ? _ble.hasPendingFrames() : true;
|
||||
}
|
||||
|
||||
bool isWriteBusy() const override {
|
||||
return (_ble_enabled && _ble.isConnected()) ? _ble.isWriteBusy() : _usb.isWriteBusy();
|
||||
}
|
||||
|
||||
size_t writeFrame(const uint8_t src[], size_t len) override {
|
||||
return (_ble_enabled && _ble.isConnected()) ? _ble.writeFrame(src, len) : _usb.writeFrame(src, len);
|
||||
}
|
||||
|
||||
size_t checkRecvFrame(uint8_t dest[]) override {
|
||||
if (_ble_enabled) {
|
||||
size_t ble_len = _ble.checkRecvFrame(_ble_buf);
|
||||
bool ble_now = _ble.isConnected();
|
||||
|
||||
if (ble_now) {
|
||||
_ble_was_connected = true;
|
||||
if (ble_len > 0) { memcpy(dest, _ble_buf, ble_len); return ble_len; }
|
||||
return 0; // BLE active — don't read USB to keep its state machine clean
|
||||
}
|
||||
|
||||
if (_ble_was_connected) {
|
||||
// BLE just disconnected — reset USB state machine so stale partial frames don't block it
|
||||
_ble_was_connected = false;
|
||||
_usb.enable();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
size_t usb_len = _usb.checkRecvFrame(_usb_buf);
|
||||
if (usb_len > 0) { memcpy(dest, _usb_buf, usb_len); return usb_len; }
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
@@ -1,72 +0,0 @@
|
||||
#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.
|
||||
// enable()/disable() control BLE only — USB is always on.
|
||||
// BLE state machine is only pumped when BLE is enabled; USB is not read while BLE is connected.
|
||||
class DualSerialInterface : public BaseSerialInterface {
|
||||
SerialBLEInterface _ble;
|
||||
ArduinoSerialInterface _usb;
|
||||
uint8_t _ble_buf[MAX_FRAME_SIZE];
|
||||
uint8_t _usb_buf[MAX_FRAME_SIZE];
|
||||
bool _ble_enabled;
|
||||
bool _ble_was_connected;
|
||||
|
||||
public:
|
||||
DualSerialInterface() : _ble_enabled(false), _ble_was_connected(false) {}
|
||||
|
||||
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);
|
||||
_usb.enable(); // USB is always on
|
||||
}
|
||||
|
||||
void enable() override { _ble.enable(); _ble_enabled = true; }
|
||||
void disable() override { _ble.disable(); _ble_enabled = false; }
|
||||
bool isEnabled() const override { return _ble_enabled; }
|
||||
|
||||
// Always true — USB is always available as fallback, so the mesh can send.
|
||||
bool isConnected() const override { return true; }
|
||||
// True only when a BLE companion app is paired and connected.
|
||||
bool isBLEConnected() const override { return _ble_enabled && _ble.isConnected(); }
|
||||
// A companion app is actually connected if BLE is bonded OR a host holds the
|
||||
// USB-CDC port open. (bool)Serial == tud_cdc_n_connected() (DTR asserted) — a
|
||||
// serial monitor counts too, but charging-only (no host) does not.
|
||||
bool isClientConnected() const override { return isBLEConnected() || (bool)Serial; }
|
||||
|
||||
bool isWriteBusy() const override {
|
||||
return (_ble_enabled && _ble.isConnected()) ? _ble.isWriteBusy() : _usb.isWriteBusy();
|
||||
}
|
||||
|
||||
size_t writeFrame(const uint8_t src[], size_t len) override {
|
||||
return (_ble_enabled && _ble.isConnected()) ? _ble.writeFrame(src, len) : _usb.writeFrame(src, len);
|
||||
}
|
||||
|
||||
size_t checkRecvFrame(uint8_t dest[]) override {
|
||||
if (_ble_enabled) {
|
||||
size_t ble_len = _ble.checkRecvFrame(_ble_buf);
|
||||
bool ble_now = _ble.isConnected();
|
||||
|
||||
if (ble_now) {
|
||||
_ble_was_connected = true;
|
||||
if (ble_len > 0) { memcpy(dest, _ble_buf, ble_len); return ble_len; }
|
||||
return 0; // BLE active — don't read USB to keep its state machine clean
|
||||
}
|
||||
|
||||
if (_ble_was_connected) {
|
||||
// BLE just disconnected — reset USB state machine so stale partial frames don't block it
|
||||
_ble_was_connected = false;
|
||||
_usb.enable();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
size_t usb_len = _usb.checkRecvFrame(_usb_buf);
|
||||
if (usb_len > 0) { memcpy(dest, _usb_buf, usb_len); return usb_len; }
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user