#include // needed for PlatformIO #include #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 MultiSerialInterface interface_manager; // include bluetooth interface #if defined(BLE_PIN_CODE) #ifdef ESP32 // include esp32 bluetooth interface #include SerialBLEInterface bluetooth_interface; #elif defined(NRF52_PLATFORM) // include nrf52 bluetooth interface #include SerialBLEInterface bluetooth_interface; #elif defined(RP2040_PLATFORM) // include rp2040 (Pico W / CYW43) bluetooth interface #include 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 #ifndef WIFI_RETRY_INTERVAL #if defined(RP2040_PLATFORM) #define WIFI_RETRY_INTERVAL 30000 // each attempt blocks loop(), so retry less often #else #define WIFI_RETRY_INTERVAL 10000 // millis between reconnect attempts #endif #endif #ifndef WIFI_RETRY_TIMEOUT #define WIFI_RETRY_TIMEOUT 5000 // RP2040: cap on how long one join may block loop() #endif #if defined(ESP32) || defined(RP2040_PLATFORM) #include SerialWifiInterface wifi_interface; #else #error "SerialWifiInterface is not defined for this platform" #endif #endif // include usb interface #if defined(ENABLE_USB_INTERFACE) #include ArduinoSerialInterface usb_serial_interface; #endif // include ethernet interface #if defined(ETHERNET_ENABLED) #include ETHERNET_CLASS ethernet_interface; #endif // include hardware serial interface #if defined(SERIAL_RX) #include ArduinoSerialInterface hardware_serial_interface; HardwareSerial companion_serial(1); #endif // platform file system #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) #include #if defined(QSPIFLASH) #include DataStore store(InternalFS, QSPIFlash, rtc_clock); #else #if defined(EXTRAFS) #include CustomLFS ExtraFS(0xD4000, 0x19000, 128); DataStore store(InternalFS, ExtraFS, rtc_clock); #else DataStore store(InternalFS, rtc_clock); #endif #endif #elif defined(RP2040_PLATFORM) #include DataStore store(LittleFS, rtc_clock); #elif defined(ESP32) #include 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 */ #ifdef WIFI_SSID bool wifi_needs_reconnect = false; unsigned long last_wifi_reconnect_attempt = 0; char wifi_ssid[33] = WIFI_SSID; // replaced by stored prefs at boot, if set char wifi_pwd[64] = WIFI_PWD; bool wifi_was_connected = false; bool wifi_enabled = false; // set at boot from prefs; false also when the effective SSID is blank #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 // stored credentials win over the build-time ones ('set wifi.ssid ' over USB serial). // they are taken as a pair, so 'set wifi.ssid' alone gives an empty password, not a // silent fallback to the build-time password of a different network. Copied out of prefs // so 'set wifi.*' edits only take effect on reboot, as their replies promise. // (No NULL-for-open-network: the RP2040 core does strlen() on the password unguarded.) if (the_mesh.getNodePrefs()->wifi_ssid[0]) { strcpy(wifi_ssid, the_mesh.getNodePrefs()->wifi_ssid); strcpy(wifi_pwd, the_mesh.getNodePrefs()->wifi_pwd); } // only start wifi if enabled and ssid is not empty wifi_enabled = the_mesh.getNodePrefs()->wifi_enabled; if (wifi_enabled && wifi_ssid[0]) { #if defined(ESP32) 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; } }); #endif WIFI_DEBUG_PRINTLN("connecting to %s", wifi_ssid); #if defined(RP2040_PLATFORM) // ponytail: the join itself blocks inside the core (CYW43::begin busy-waits for the // association), so every attempt stalls the mesh loop. beginNoBlock() only skips the // extra DHCP wait. Give the first connect a full window, then bound the retries below. // Upgrade path if the stall ever matters: run WiFi on core1. WiFi.beginNoBlock(wifi_ssid, wifi_pwd); last_wifi_reconnect_attempt = millis(); // let DHCP finish before the poll can retry #else WiFi.begin(wifi_ssid, wifi_pwd); #endif wifi_interface.begin(TCP_PORT); interface_manager.addInterface(InterfaceType::WiFi, &wifi_interface); } else { WIFI_DEBUG_PRINTLN("wifi disabled"); } #endif // add usb interface #if defined(ENABLE_USB_INTERFACE) usb_serial_interface.begin(Serial); 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 } #ifdef WIFI_SSID if (wifi_enabled) { // RP2040 has no WiFi event callbacks, so poll the link state instead #if defined(RP2040_PLATFORM) wifi_needs_reconnect = (WiFi.status() != WL_CONNECTED); if (wifi_was_connected == wifi_needs_reconnect) { // link state changed wifi_was_connected = !wifi_needs_reconnect; if (wifi_was_connected) { WIFI_DEBUG_PRINTLN("connected, listening on %s:%d", WiFi.localIP().toString().c_str(), TCP_PORT); } else { WIFI_DEBUG_PRINTLN("link lost"); } } #endif // Safely attempt to reconnect if flagged. On RP2040 each attempt blocks the mesh loop // for up to WIFI_RETRY_TIMEOUT, so retry less often and cap how long a join may stall. if (wifi_needs_reconnect && (millis() - last_wifi_reconnect_attempt > WIFI_RETRY_INTERVAL)) { WIFI_DEBUG_PRINTLN("Attempting manual WiFi reconnect to %s (status %d)...", wifi_ssid, WiFi.status()); #if defined(RP2040_PLATFORM) WiFi.setTimeout(WIFI_RETRY_TIMEOUT); WiFi.beginNoBlock(wifi_ssid, wifi_pwd); // no reconnect() on this platform #else WiFi.disconnect(); WiFi.reconnect(); #endif last_wifi_reconnect_attempt = millis(); } } #endif }