mirror of https://github.com/meshcore-dev/MeshCore
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
392 lines
12 KiB
392 lines
12 KiB
#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;
|
|
}
|
|
|
|
#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
|
|
|
|
#ifdef ESP32
|
|
#ifdef WIFI_SSID
|
|
#include <helpers/esp32/SerialWifiInterface.h>
|
|
SerialWifiInterface serial_interface;
|
|
#ifndef TCP_PORT
|
|
#define TCP_PORT 5000
|
|
#endif
|
|
#elif defined(BLE_PIN_CODE)
|
|
#include <helpers/esp32/SerialBLEInterface.h>
|
|
SerialBLEInterface serial_interface;
|
|
#elif defined(SERIAL_RX)
|
|
#include <helpers/ArduinoSerialInterface.h>
|
|
ArduinoSerialInterface serial_interface;
|
|
HardwareSerial companion_serial(1);
|
|
#else
|
|
#include <helpers/ArduinoSerialInterface.h>
|
|
ArduinoSerialInterface serial_interface;
|
|
#endif
|
|
#elif defined(RP2040_PLATFORM)
|
|
//#ifdef WIFI_SSID
|
|
// #include <helpers/rp2040/SerialWifiInterface.h>
|
|
// SerialWifiInterface serial_interface;
|
|
// #ifndef TCP_PORT
|
|
// #define TCP_PORT 5000
|
|
// #endif
|
|
// #elif defined(BLE_PIN_CODE)
|
|
// #include <helpers/rp2040/SerialBLEInterface.h>
|
|
// SerialBLEInterface serial_interface;
|
|
#if defined(SERIAL_RX)
|
|
#include <helpers/ArduinoSerialInterface.h>
|
|
ArduinoSerialInterface serial_interface;
|
|
HardwareSerial companion_serial(1);
|
|
#else
|
|
#include <helpers/ArduinoSerialInterface.h>
|
|
ArduinoSerialInterface serial_interface;
|
|
#endif
|
|
#elif defined(NRF52_PLATFORM)
|
|
#if defined(WITH_ETHERNET_COMPANION)
|
|
#include <SPI.h>
|
|
#include <helpers/SerialEthernetInterface.h>
|
|
SerialEthernetInterface serial_interface;
|
|
// Dedicated SPI for the W5100S on its own pins (SCK=3, MISO=29, MOSI=30).
|
|
// The radio remaps the global `SPI` to the LoRa pins (43/44/45) in
|
|
// std_init(), so the W5100S needs its own SPIM peripheral. SPIM2 is free
|
|
// (radio uses SPIM3, Wire uses TWIM0/1).
|
|
SPIClass eth_spi(NRF_SPIM2, 29, 3, 30); // (SPIM, MISO=29, SCK=3, MOSI=30)
|
|
uint8_t g_eth_mac[6] = {0}; // set in setup(), used in loop()
|
|
#ifndef TCP_PORT
|
|
#define TCP_PORT 5000
|
|
#endif
|
|
// Static IP (no DHCP): predictable address for Home Assistant AND avoids
|
|
// the multi-second blocking DHCP that reboot-loops the device on PoE.
|
|
// Override per network if needed (octets are comma-separated).
|
|
#ifndef ETH_STATIC_IP
|
|
#define ETH_STATIC_IP 192,168,1,50
|
|
#endif
|
|
#ifndef ETH_GATEWAY
|
|
#define ETH_GATEWAY 192,168,1,1
|
|
#endif
|
|
#ifndef ETH_SUBNET
|
|
#define ETH_SUBNET 255,255,255,0
|
|
#endif
|
|
#elif defined(BLE_PIN_CODE)
|
|
#include <helpers/nrf52/SerialBLEInterface.h>
|
|
SerialBLEInterface serial_interface;
|
|
#elif defined(ETHERNET_ENABLED)
|
|
#include <helpers/nrf52/SerialEthernetInterface.h>
|
|
SerialEthernetInterface serial_interface;
|
|
#else
|
|
#include <helpers/ArduinoSerialInterface.h>
|
|
ArduinoSerialInterface serial_interface;
|
|
#endif
|
|
#elif defined(STM32_PLATFORM)
|
|
#include <helpers/ArduinoSerialInterface.h>
|
|
ArduinoSerialInterface serial_interface;
|
|
#else
|
|
#error "need to define a serial interface"
|
|
#endif
|
|
|
|
/* GLOBAL OBJECTS */
|
|
#ifdef DISPLAY_CLASS
|
|
#include "UITask.h"
|
|
UITask ui_task(&board, &serial_interface);
|
|
#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
|
|
|
|
#if defined(WITH_ETHERNET_COMPANION)
|
|
// Direct W5100S register write via eth_spi (proven path). Common-register
|
|
// block addresses are fixed: GAR=0x0001, SUBR=0x0005, SHAR=0x0009, SIPR=0x000F.
|
|
static void eth_wr(uint16_t a, uint8_t v) {
|
|
eth_spi.beginTransaction(SPISettings(4000000, MSBFIRST, SPI_MODE0));
|
|
digitalWrite(26, LOW);
|
|
eth_spi.transfer(0xF0); eth_spi.transfer(a >> 8); eth_spi.transfer(a & 0xFF); eth_spi.transfer(v);
|
|
digitalWrite(26, HIGH);
|
|
eth_spi.endTransaction();
|
|
}
|
|
static void eth_write_netcfg(const uint8_t* mac) {
|
|
const uint8_t ip[4] = { ETH_STATIC_IP };
|
|
const uint8_t gw[4] = { ETH_GATEWAY };
|
|
const uint8_t sn[4] = { ETH_SUBNET };
|
|
for (int i = 0; i < 4; i++) eth_wr(0x0001 + i, gw[i]); // GAR
|
|
for (int i = 0; i < 4; i++) eth_wr(0x0005 + i, sn[i]); // SUBR
|
|
for (int i = 0; i < 6; i++) eth_wr(0x0009 + i, mac[i]); // SHAR
|
|
for (int i = 0; i < 4; i++) eth_wr(0x000F + i, ip[i]); // SIPR
|
|
}
|
|
#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
|
|
);
|
|
|
|
#if defined(WITH_ETHERNET_COMPANION)
|
|
{
|
|
// Bring up the W5100S (RAK13800) TCP/IP stack so the companion protocol is
|
|
// reachable over Ethernet (Home Assistant connects to this IP : TCP_PORT).
|
|
// Chip power + reset is handled in board.begin() (WITH_W5100S_POE: 3V3_EN +
|
|
// RST). The W5100S has its OWN SPI peripheral (eth_spi on SPIM2, pins
|
|
// SCK=3/MISO=29/MOSI=30, CS=26) — separate from the radio, which uses the
|
|
// global SPI on SPIM3 remapped to the LoRa pins. Derive a stable
|
|
// locally-administered MAC from the nRF52 device ID.
|
|
// Compute a stable locally-administered MAC from the nRF52 device ID.
|
|
// IMPORTANT: the W5100S/Ethernet library bring-up (W5100.init does a PHY
|
|
// soft-reset) is DEFERRED to loop() — see below. Doing it here in setup
|
|
// dipped the W5100S current during the marginal PoE cold-start window and
|
|
// collapsed the RAK19018 (Silvertel) converter → reboot loop. board.begin
|
|
// already has the W5100S drawing current (3V3_EN + RST + bit-bang reset),
|
|
// which latches the PoE converter just like the plain repeater build.
|
|
g_eth_mac[0] = 0x02; // locally administered, unicast
|
|
uint32_t id0 = NRF_FICR->DEVICEID[0];
|
|
uint32_t id1 = NRF_FICR->DEVICEID[1];
|
|
g_eth_mac[1] = (id0 >> 24) & 0xFF;
|
|
g_eth_mac[2] = (id0 >> 16) & 0xFF;
|
|
g_eth_mac[3] = (id0 >> 8) & 0xFF;
|
|
g_eth_mac[4] = (id0) & 0xFF;
|
|
g_eth_mac[5] = (id1) & 0xFF;
|
|
|
|
// Non-disruptive SPI setup here (no chip reset); the disruptive part — the
|
|
// lib's Ethernet.begin() / W5100.init() PHY soft-reset — is deferred to
|
|
// loop() (~6 s) so it can't collapse the marginal PoE supply at cold start.
|
|
eth_spi.begin();
|
|
Ethernet.init(eth_spi, 26);
|
|
Serial.println("Ethernet companion: bring-up deferred to loop()");
|
|
}
|
|
#elif defined(BLE_PIN_CODE)
|
|
serial_interface.begin(BLE_NAME_PREFIX, the_mesh.getNodePrefs()->node_name, the_mesh.getBLEPin());
|
|
the_mesh.startInterface(serial_interface);
|
|
#elif defined(ETHERNET_ENABLED)
|
|
Serial.print("Waiting for serial to connect...\n");
|
|
unsigned long timeout = millis();
|
|
while (!Serial) {
|
|
if ((millis() - timeout) < 5000) { delay(100); } else { break; }
|
|
}
|
|
Serial.println("Initializing Ethernet adapter...");
|
|
if (serial_interface.begin()) {
|
|
the_mesh.startInterface(serial_interface);
|
|
} else {
|
|
Serial.println("ETH: Init failed, continuing without Ethernet (mesh only)");
|
|
}
|
|
#else
|
|
serial_interface.begin(Serial);
|
|
the_mesh.startInterface(serial_interface);
|
|
#endif
|
|
#elif defined(RP2040_PLATFORM)
|
|
LittleFS.begin();
|
|
store.begin();
|
|
the_mesh.begin(
|
|
#ifdef DISPLAY_CLASS
|
|
disp != NULL
|
|
#else
|
|
false
|
|
#endif
|
|
);
|
|
|
|
//#ifdef WIFI_SSID
|
|
// WiFi.begin(WIFI_SSID, WIFI_PWD);
|
|
// serial_interface.begin(TCP_PORT);
|
|
// #elif defined(BLE_PIN_CODE)
|
|
// char dev_name[32+16];
|
|
// sprintf(dev_name, "%s%s", BLE_NAME_PREFIX, the_mesh.getNodeName());
|
|
// serial_interface.begin(dev_name, the_mesh.getBLEPin());
|
|
#if defined(SERIAL_RX)
|
|
companion_serial.setPins(SERIAL_RX, SERIAL_TX);
|
|
companion_serial.begin(115200);
|
|
serial_interface.begin(companion_serial);
|
|
#else
|
|
serial_interface.begin(Serial);
|
|
#endif
|
|
the_mesh.startInterface(serial_interface);
|
|
#elif defined(ESP32)
|
|
SPIFFS.begin(true);
|
|
store.begin();
|
|
the_mesh.begin(
|
|
#ifdef DISPLAY_CLASS
|
|
disp != NULL
|
|
#else
|
|
false
|
|
#endif
|
|
);
|
|
|
|
#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);
|
|
serial_interface.begin(TCP_PORT);
|
|
#elif defined(BLE_PIN_CODE)
|
|
serial_interface.begin(BLE_NAME_PREFIX, the_mesh.getNodePrefs()->node_name, the_mesh.getBLEPin());
|
|
#elif defined(SERIAL_RX)
|
|
companion_serial.setPins(SERIAL_RX, SERIAL_TX);
|
|
companion_serial.begin(115200);
|
|
serial_interface.begin(companion_serial);
|
|
#else
|
|
serial_interface.begin(Serial);
|
|
#endif
|
|
the_mesh.startInterface(serial_interface);
|
|
#else
|
|
#error "need to define filesystem"
|
|
#endif
|
|
|
|
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();
|
|
sensors.loop();
|
|
#ifdef DISPLAY_CLASS
|
|
ui_task.loop();
|
|
#endif
|
|
rtc_clock.tick();
|
|
#ifdef HAS_EXTERNAL_WATCHDOG
|
|
external_watchdog.loop();
|
|
#endif
|
|
|
|
#ifdef ETHERNET_ENABLED
|
|
serial_interface.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
|
|
|
|
#if defined(WITH_ETHERNET_COMPANION)
|
|
// Deferred Ethernet bring-up: only AFTER the device has booted and the PoE
|
|
// converter is solidly latched (~6 s). The W5100.init() PHY soft-reset would
|
|
// collapse the marginal PoE supply if done during setup() (reboot loop).
|
|
static bool _eth_up = false;
|
|
if (!_eth_up && millis() > 6000) {
|
|
IPAddress sip(ETH_STATIC_IP), sgw(ETH_GATEWAY), ssn(ETH_SUBNET);
|
|
Ethernet.begin(g_eth_mac, sip, sgw, sgw, ssn); // inits chip mode/sockets (PHY soft-reset)
|
|
serial_interface.begin(TCP_PORT); // start TCP server
|
|
delay(50);
|
|
eth_write_netcfg(g_eth_mac); // force IP/GW/SN/MAC (reliable here)
|
|
_eth_up = true;
|
|
IPAddress ip = Ethernet.localIP();
|
|
Serial.print("Ethernet up (deferred): ");
|
|
Serial.print(ip[0]); Serial.print('.'); Serial.print(ip[1]); Serial.print('.');
|
|
Serial.print(ip[2]); Serial.print('.'); Serial.print(ip[3]);
|
|
Serial.print(":"); Serial.println(TCP_PORT);
|
|
}
|
|
#endif
|
|
}
|
|
|