mirror of
https://github.com/liquidraver/ZephCore.git
synced 2026-10-15 17:19:55 +00:00
Server roles (slice 7) - CommonCLI split into upstream's shape; region CLI moved into CommonCLI; ClientACL, RegionMap, TransportKeyStore and both server meshes in upstream shape over an Arduino compat shim (helpers/compat: Arduino.h, Stream/Print, Serial, SHA256, FILESYSTEM/File over Zephyr fs) - room server processAck port; `get acl` prints as upstream - room: default name Room-XXXX, display/input rotation, live radio UI refresh Event loops (slice 7b) - repeater and room server share server_main_common with a ServerRole descriptor; shared event bits in src/mesh_events.h - room server deadline-driven (idle wakes 2.2/s -> 0.08/s); companion loop() runs on housekeeping (closes the lazy contact-flush gap) - `get loop.wakes` in debug builds (CONFIG_ZEPHCORE_LOOP_WAKE_STATS) Companion transports and WiFi (slice 8) - upstream MultiSerialInterface: BLE, USB/UART and TCP are each a BaseSerialInterface, every connected transport gets every frame; CompanionSerial holds a lossless reply; shared frame_txq for BLE/TCP - SerialCompanionTransport and LinuxTCPTransport replaced by TcpCompanionTransport; ZephyrBLE split (gatt layout, DFU) - WiFi companion (upstream wifi.ssid/pwd/enabled CLI, TCP port 5000) on boards declaring `capabilities: wifi` in zephcore.yml: the PSRAM ESP32-S3 boards, plus Heltec V3 (140 contacts), Wireless Tracker (130) and both C6 boards (280); ESP32-S3 without PSRAM gets bounded WiFi buffers and a measured 72 KB heap; PSRAM boards' internal heap 72 KB - TCP queues: own depth (ZEPHCORE_TCP_QUEUE_SIZE=8), RX waits instead of dropping - RAM: companion ClientACL dropped; main/BT-TX/TCP stacks sized from measured peaks (-7 KB nRF companions, -9 KB ESP32 companions) - ESP32 WiFi stopped working under ~200 frames/s unicast. Fixed by: patches/zephyr/0017 never block the WiFi task in the RX callback; patches/zephyr/0018 lock shared_multi_heap (heap corruption with the WiFi heap in PSRAM); patches/modules/hal_espressif/0001 backport of hal_espressif af97fb02df (PSRAM realloc) Storage and prefs (slice 9) - prefs are now prefs.json through upstream's ConfigSerializer (ported verbatim), with upstream's key names for shared fields and ZephCore's own under "zc"; on first boot the binary new_prefs / repeater prefs is migrated and kept, so a downgrade still boots with the old settings - helpers/PrefsCodec: both legacy binary layouts (companion 272 B, server 311 B) as Zephyr-free codecs; sanitizeNodePrefs is the only validator - fix: server prefs could be replaced by a truncated file when a write failed; identity, prefs and channel writes go through one checked atomic replace (ZephyrFsUtil) - fix: a corrupt cad_auto byte turned adaptive CAD off instead of the default on - fix: a deliberate 300 s GPS duty on a repeater was rewritten to 48 h on every boot (that migration now runs on legacy files only) - one identity loader for all roles (IdentityFile); an unusable key file is always parked as .bad before regenerating - server store no longer stamps repeater defaults on room server and observer; companion first-boot volume policy moved into the store (adoptVolume) - ZephyrDataStore 1435 -> ~850 lines
206 lines
4.5 KiB
C++
206 lines
4.5 KiB
C++
#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;
|
|
bool anyConnected = false; // ZEPHCORE
|
|
for(auto iface : _interfaces){
|
|
// ZEPHCORE: connected ones only. An enabled but idle BLE/WiFi interface
|
|
// returns 0, which failed every write, and the caller retries lossless
|
|
// replies on 0.
|
|
if(iface.instance && iface.instance->isEnabled() && iface.instance->isConnected()){
|
|
anyConnected = true; // ZEPHCORE
|
|
if(iface.instance->writeFrame(src, len) != len){
|
|
allSuccessful = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// report success if all writes completed successfully
|
|
return (allSuccessful && anyConnected) ? len : 0; // ZEPHCORE: 0 when nobody is listening
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
};
|