mirror of
https://github.com/mikecarper/MeshCore.git
synced 2026-09-06 21:24:24 +00:00
The contact sync streams up to MAX_CONTACTS frames back to back, but ArduinoSerialInterface never checked whether the stream could take them: isWriteBusy() returned false unconditionally, so MyMesh's pacing gate had no effect, and writeFrame() called write() without looking at availableForWrite(). On ESP32 (HWCDC, 256 byte TX ring) a host that stalls for a moment makes the driver drop queued bytes silently, which tears a frame in half - and since the framing is length prefixed with no checksum and no resync marker, the client stays desynchronised for the rest of the session. Reported as 'the app disconnects while syncing contacts' on devices with a large contact list. Add opt-in flow control: report busy until a whole frame fits, and drop frames as a unit instead of tearing them. Enable it for the companion USB interface and give HWCDC a bigger TX buffer with a short write timeout, mirroring what kiss_modem already does. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
303 lines
8.4 KiB
C++
303 lines
8.4 KiB
C++
#include <Arduino.h> // needed for PlatformIO
|
|
#include <Mesh.h>
|
|
#include "MyMesh.h"
|
|
|
|
// Believe it or not, this std C function is busted on some platforms!
|
|
static uint32_t _atoi(const char* sp) {
|
|
uint32_t n = 0;
|
|
while (*sp && *sp >= '0' && *sp <= '9') {
|
|
n *= 10;
|
|
n += (*sp++ - '0');
|
|
}
|
|
return n;
|
|
}
|
|
|
|
// interface manager
|
|
#include <helpers/MultiSerialInterface.h>
|
|
MultiSerialInterface interface_manager;
|
|
|
|
// include bluetooth interface
|
|
#if defined(BLE_PIN_CODE)
|
|
#ifdef ESP32
|
|
// include esp32 bluetooth interface
|
|
#include <helpers/esp32/SerialBLEInterface.h>
|
|
SerialBLEInterface bluetooth_interface;
|
|
#elif defined(NRF52_PLATFORM)
|
|
// include nrf52 bluetooth interface
|
|
#include <helpers/nrf52/SerialBLEInterface.h>
|
|
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 <helpers/esp32/SerialWifiInterface.h>
|
|
SerialWifiInterface wifi_interface;
|
|
#else
|
|
#error "SerialWifiInterface is not defined for this platform"
|
|
#endif
|
|
#endif
|
|
|
|
// include usb interface
|
|
#if defined(ENABLE_USB_INTERFACE)
|
|
#include <helpers/ArduinoSerialInterface.h>
|
|
ArduinoSerialInterface usb_serial_interface;
|
|
#ifndef USB_CLIENT_IDLE_TIMEOUT
|
|
// how long a USB client is still considered present after its last frame,
|
|
// for targets which cannot report DTR (see setConnectedCheck below)
|
|
#define USB_CLIENT_IDLE_TIMEOUT (10*60*1000UL)
|
|
#endif
|
|
#endif
|
|
|
|
// include ethernet interface
|
|
#if defined(ETHERNET_ENABLED)
|
|
#include <helpers/ethernet/EthernetInterface.h>
|
|
ETHERNET_CLASS ethernet_interface;
|
|
#endif
|
|
|
|
// include hardware serial interface
|
|
#if defined(SERIAL_RX)
|
|
#include <helpers/ArduinoSerialInterface.h>
|
|
ArduinoSerialInterface hardware_serial_interface;
|
|
HardwareSerial companion_serial(1);
|
|
#endif
|
|
|
|
// platform file system
|
|
#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM)
|
|
#include <InternalFileSystem.h>
|
|
#if defined(QSPIFLASH)
|
|
#include <CustomLFS_QSPIFlash.h>
|
|
DataStore store(InternalFS, QSPIFlash, rtc_clock);
|
|
#else
|
|
#if defined(EXTRAFS)
|
|
#include <CustomLFS.h>
|
|
CustomLFS ExtraFS(0xD4000, 0x19000, 128);
|
|
DataStore store(InternalFS, ExtraFS, rtc_clock);
|
|
#else
|
|
DataStore store(InternalFS, rtc_clock);
|
|
#endif
|
|
#endif
|
|
#elif defined(RP2040_PLATFORM)
|
|
#include <LittleFS.h>
|
|
DataStore store(LittleFS, rtc_clock);
|
|
#elif defined(ESP32)
|
|
#include <SPIFFS.h>
|
|
DataStore store(SPIFFS, rtc_clock);
|
|
#endif
|
|
|
|
/* GLOBAL OBJECTS */
|
|
#ifdef DISPLAY_CLASS
|
|
#include "UITask.h"
|
|
UITask ui_task(&board, &interface_manager);
|
|
#endif
|
|
|
|
StdRNG fast_rng;
|
|
SimpleMeshTables tables;
|
|
MyMesh the_mesh(radio_driver, fast_rng, rtc_clock, tables, store
|
|
#ifdef DISPLAY_CLASS
|
|
, &ui_task
|
|
#endif
|
|
);
|
|
|
|
/* END GLOBAL OBJECTS */
|
|
|
|
void halt() {
|
|
while (1) ;
|
|
}
|
|
|
|
/* WIFI RECONNECT TRACKERS */
|
|
#if defined(ESP32) && defined(WIFI_SSID)
|
|
bool wifi_needs_reconnect = false;
|
|
unsigned long last_wifi_reconnect_attempt = 0;
|
|
#endif
|
|
|
|
void setup() {
|
|
Serial.begin(115200);
|
|
board.begin();
|
|
|
|
#ifdef HAS_EXTERNAL_WATCHDOG
|
|
external_watchdog.begin();
|
|
#endif
|
|
|
|
#ifdef DISPLAY_CLASS
|
|
DisplayDriver* disp = NULL;
|
|
if (display.begin()) {
|
|
disp = &display;
|
|
disp->startFrame();
|
|
#ifdef ST7789
|
|
disp->setTextSize(2);
|
|
#endif
|
|
disp->drawTextCentered(disp->width() / 2, 28, "Loading...");
|
|
disp->endFrame();
|
|
}
|
|
#endif
|
|
|
|
if (!radio_init()) { halt(); }
|
|
|
|
fast_rng.begin(radio_driver.getRngSeed());
|
|
|
|
#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM)
|
|
InternalFS.begin();
|
|
#if defined(QSPIFLASH)
|
|
if (!QSPIFlash.begin()) {
|
|
// debug output might not be available at this point, might be too early. maybe should fall back to InternalFS here?
|
|
MESH_DEBUG_PRINTLN("CustomLFS_QSPIFlash: failed to initialize");
|
|
} else {
|
|
MESH_DEBUG_PRINTLN("CustomLFS_QSPIFlash: initialized successfully");
|
|
}
|
|
#else
|
|
#if defined(EXTRAFS)
|
|
ExtraFS.begin();
|
|
#endif
|
|
#endif
|
|
store.begin();
|
|
the_mesh.begin(
|
|
#ifdef DISPLAY_CLASS
|
|
disp != NULL
|
|
#else
|
|
false
|
|
#endif
|
|
);
|
|
#elif defined(RP2040_PLATFORM)
|
|
LittleFS.begin();
|
|
store.begin();
|
|
the_mesh.begin(
|
|
#ifdef DISPLAY_CLASS
|
|
disp != NULL
|
|
#else
|
|
false
|
|
#endif
|
|
);
|
|
#elif defined(ESP32)
|
|
SPIFFS.begin(true);
|
|
store.begin();
|
|
the_mesh.begin(
|
|
#ifdef DISPLAY_CLASS
|
|
disp != NULL
|
|
#else
|
|
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);
|
|
|
|
WiFi.onEvent([](WiFiEvent_t event, WiFiEventInfo_t info){
|
|
if (event == ARDUINO_EVENT_WIFI_STA_DISCONNECTED) {
|
|
WIFI_DEBUG_PRINTLN("WiFi disconnected. Flagging for reconnect...");
|
|
wifi_needs_reconnect = true;
|
|
} else if (event == ARDUINO_EVENT_WIFI_STA_GOT_IP) {
|
|
WIFI_DEBUG_PRINTLN("WiFi connected successfully!");
|
|
wifi_needs_reconnect = false;
|
|
}
|
|
});
|
|
|
|
WiFi.begin(WIFI_SSID, WIFI_PWD);
|
|
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);
|
|
// keep frames intact and pace the contact stream when the host is slow
|
|
usb_serial_interface.enableFlowControl(true);
|
|
#if defined(ESP32) && defined(ARDUINO_USB_MODE) && ARDUINO_USB_MODE == 1
|
|
// a 256 byte TX buffer overflows during a contact sync, and write() blocks
|
|
// up to tx_timeout_ms per call against a stalled host (same reasoning as
|
|
// the kiss_modem tuning)
|
|
Serial.setTxBufferSize(4096);
|
|
Serial.setTxTimeoutMs(5);
|
|
// The ESP32 USB-Serial-JTAG peripheral (HWCDC) has no DTR concept at all:
|
|
// (bool)Serial only tells us the host has enumerated the device, which is
|
|
// already true when the cable is plugged into a powered port. Fall back to
|
|
// activity: a real client has to send us frames.
|
|
usb_serial_interface.setConnectedCheck([]() {
|
|
uint32_t last = usb_serial_interface.getLastFrameMillis();
|
|
return (bool)Serial && last != 0 && (millis() - last) < USB_CLIENT_IDLE_TIMEOUT;
|
|
});
|
|
#elif (defined(ESP32) && defined(ARDUINO_USB_CDC_ON_BOOT) && ARDUINO_USB_CDC_ON_BOOT) \
|
|
|| defined(NRF52_PLATFORM) || defined(RP2040_PLATFORM)
|
|
// native USB-CDC (TinyUSB): (bool)Serial reflects DTR, ie. the host really
|
|
// has the port open. A classic ESP32 behind a UART bridge has no such
|
|
// signal and keeps the assume-connected default.
|
|
usb_serial_interface.setConnectedCheck([]() { return (bool)Serial; });
|
|
#endif
|
|
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);
|
|
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
|
|
the_mesh.applyGpsPrefs();
|
|
#endif
|
|
|
|
#ifdef DISPLAY_CLASS
|
|
ui_task.begin(disp, &sensors, the_mesh.getNodePrefs()); // still want to pass this in as dependency, as prefs might be moved
|
|
#endif
|
|
|
|
board.onBootComplete();
|
|
}
|
|
|
|
void loop() {
|
|
the_mesh.loop();
|
|
interface_manager.loop();
|
|
sensors.loop();
|
|
#ifdef DISPLAY_CLASS
|
|
ui_task.loop();
|
|
#endif
|
|
rtc_clock.tick();
|
|
#ifdef HAS_EXTERNAL_WATCHDOG
|
|
external_watchdog.loop();
|
|
#endif
|
|
|
|
if (!the_mesh.hasPendingWork()) {
|
|
#if defined(NRF52_PLATFORM)
|
|
board.sleep(0); // nrf ignores seconds param, sleeps whenever possible
|
|
#endif
|
|
}
|
|
|
|
#if defined(ESP32) && defined(WIFI_SSID)
|
|
// Safely attempt to reconnect every 10 seconds if flagged
|
|
if (wifi_needs_reconnect && (millis() - last_wifi_reconnect_attempt > 10000)) {
|
|
WIFI_DEBUG_PRINTLN("Attempting manual WiFi reconnect...");
|
|
WiFi.disconnect();
|
|
WiFi.reconnect();
|
|
last_wifi_reconnect_attempt = millis();
|
|
}
|
|
#endif
|
|
}
|