MeshCore/examples/simple_repeater/main.cpp
Ryan Gregg 88892de864 Fix Ethernet init checking hardwareStatus before begin() and deduplicate CLI code
The Ethernet retry loop in repeater and room server checked
hardwareStatus() and linkStatus() before calling Ethernet.begin(),
which always returned EthernetNoHardware since hardware detection
only happens during begin(). Extract shared Ethernet CLI code into
EthernetCLI.h to prevent future divergence. Also fix time_t type
mismatch in companion radio Ethernet init.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 10:48:42 -07:00

186 lines
4.8 KiB
C++

#include <Arduino.h> // needed for PlatformIO
#include <Mesh.h>
#include "MyMesh.h"
#ifdef DISPLAY_CLASS
#include "UITask.h"
static UITask ui_task(display);
#endif
#ifdef ETHERNET_ENABLED
#define ETHERNET_CLI_BANNER "MeshCore Repeater CLI"
#include <helpers/nrf52/EthernetCLI.h>
#endif
StdRNG fast_rng;
SimpleMeshTables tables;
MyMesh the_mesh(board, radio_driver, *new ArduinoMillis(), fast_rng, rtc_clock, tables);
void halt() {
while (1) ;
}
static char command[160];
#ifdef ETHERNET_ENABLED
static char ethernet_command[160];
#endif
// For power saving
unsigned long lastActive = 0; // mark last active time
unsigned long nextSleepinSecs = 120; // next sleep in seconds. The first sleep (if enabled) is after 2 minutes from boot
void setup() {
Serial.begin(115200);
delay(1000);
board.begin();
#if defined(MESH_DEBUG) && defined(NRF52_PLATFORM)
// give some extra time for serial to settle so
// boot debug messages can be seen on terminal
delay(5000);
#endif
// For power saving
lastActive = millis(); // mark last active time since boot
#ifdef DISPLAY_CLASS
if (display.begin()) {
display.startFrame();
display.setCursor(0, 0);
display.print("Please wait...");
display.endFrame();
}
#endif
if (!radio_init()) {
MESH_DEBUG_PRINTLN("Radio init failed!");
halt();
}
fast_rng.begin(radio_get_rng_seed());
FILESYSTEM* fs;
#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM)
InternalFS.begin();
fs = &InternalFS;
IdentityStore store(InternalFS, "");
#elif defined(ESP32)
SPIFFS.begin(true);
fs = &SPIFFS;
IdentityStore store(SPIFFS, "/identity");
#elif defined(RP2040_PLATFORM)
LittleFS.begin();
fs = &LittleFS;
IdentityStore store(LittleFS, "/identity");
store.begin();
#else
#error "need to define filesystem"
#endif
if (!store.load("_main", the_mesh.self_id)) {
MESH_DEBUG_PRINTLN("Generating new keypair");
the_mesh.self_id = radio_new_identity(); // create new random identity
int count = 0;
while (count < 10 && (the_mesh.self_id.pub_key[0] == 0x00 || the_mesh.self_id.pub_key[0] == 0xFF)) { // reserved id hashes
the_mesh.self_id = radio_new_identity(); count++;
}
store.save("_main", the_mesh.self_id);
}
Serial.print("Repeater ID: ");
mesh::Utils::printHex(Serial, the_mesh.self_id.pub_key, PUB_KEY_SIZE); Serial.println();
command[0] = 0;
#ifdef ETHERNET_ENABLED
ethernet_command[0] = 0;
#endif
sensors.begin();
the_mesh.begin(fs);
#ifdef DISPLAY_CLASS
ui_task.begin(the_mesh.getNodePrefs(), FIRMWARE_BUILD_DATE, FIRMWARE_VERSION);
#endif
#ifdef ETHERNET_ENABLED
ethernet_start_task();
#endif
// send out initial zero hop Advertisement to the mesh
#if ENABLE_ADVERT_ON_BOOT == 1
the_mesh.sendSelfAdvertisement(16000, false);
#endif
}
void loop() {
// Handle Serial CLI
int len = strlen(command);
while (Serial.available() && len < sizeof(command)-1) {
char c = Serial.read();
if (c != '\n') {
command[len++] = c;
command[len] = 0;
Serial.print(c);
}
if (c == '\r') break;
}
if (len == sizeof(command)-1) { // command buffer full
command[sizeof(command)-1] = '\r';
}
if (len > 0 && command[len - 1] == '\r') { // received complete line
Serial.print('\n');
command[len - 1] = 0; // replace newline with C string null terminator
char reply[160];
reply[0] = 0;
#ifdef ETHERNET_ENABLED
if (!ethernet_handle_command(command, reply)) {
the_mesh.handleCommand(0, command, reply);
}
#else
the_mesh.handleCommand(0, command, reply); // NOTE: there is no sender_timestamp via serial!
#endif
if (reply[0]) {
Serial.print(" -> "); Serial.println(reply);
}
command[0] = 0; // reset command buffer
}
#ifdef ETHERNET_ENABLED
ethernet_loop_maintain();
if (ethernet_read_line(ethernet_command, sizeof(ethernet_command))) {
char reply[160];
reply[0] = 0;
if (!ethernet_handle_command(ethernet_command, reply)) {
the_mesh.handleCommand(0, ethernet_command, reply);
}
ethernet_send_reply(reply);
ethernet_command[0] = 0;
}
#endif
the_mesh.loop();
sensors.loop();
#ifdef DISPLAY_CLASS
ui_task.loop();
#endif
rtc_clock.tick();
if (the_mesh.getNodePrefs()->powersaving_enabled && !the_mesh.hasPendingWork()) {
#if defined(NRF52_PLATFORM)
board.sleep(1800); // nrf ignores seconds param, sleeps whenever possible
#else
if (the_mesh.millisHasNowPassed(lastActive + nextSleepinSecs * 1000)) { // To check if it is time to sleep
board.sleep(1800); // To sleep. Wake up after 30 minutes or when receiving a LoRa packet
lastActive = millis();
nextSleepinSecs = 5; // Default: To work for 5s and sleep again
} else {
nextSleepinSecs += 5; // When there is pending work, to work another 5s
}
#endif
}
}