Files
HaloKeymind/examples/simple_room_server/main.cpp
T
mikecarper b847b46ef7 Retry identity startup and harden replay state and permissions
Read identities up to three times before allowing replacement, publish only complete reads, and require new startup keys to be saved with bounded retries.

Use authoritative replay-file metadata throughout persistence and clock recovery. Validate setperm input before narrowing or mutating roles, and add production-path regression coverage.
2026-09-15 17:33:50 -07:00

265 lines
7.1 KiB
C++

#include <Arduino.h> // needed for PlatformIO
#include <Mesh.h>
#include <helpers/IdentityGeneration.h>
#if MESH_PACKET_LOGGING
#include <helpers/SerialPacketLog.h>
#endif
#include "MyMesh.h"
#if defined(ESP32_PLATFORM)
#include <helpers/ESP32TrueRandom.h>
#endif
#ifdef ETHERNET_ENABLED
#define ETHERNET_CLI_BANNER "MeshCore Room Server CLI"
#include <helpers/nrf52/EthernetCLI.h>
#endif
#ifdef DISPLAY_CLASS
#include "UITask.h"
#ifdef DISPLAY_TOUCH_TOGGLE
static UITask ui_task(board, display);
#else
static UITask ui_task(display);
#endif
static bool display_ready = false;
#endif
StdRNG fast_rng;
SimpleMeshTables tables;
MyMesh the_mesh(board, radio_driver, *new ArduinoMillis(), fast_rng, rtc_clock, tables);
#include "../InfrastructureWireless.h"
void halt() {
while (1) ;
}
static char command[MAX_POST_TEXT_LEN+1];
#ifdef ETHERNET_ENABLED
static char ethernet_command[MAX_POST_TEXT_LEN+1];
#endif
// For power saving
unsigned long POWERSAVING_FIRSTSLEEP_SECS = 120; // The first sleep (if enabled) from boot
void setup() {
mesh::wireless::control().begin(infrastructure_wireless);
mesh::prepareUsbLoggingPort();
Serial.begin(115200);
#if MESH_ESP32_USB_CONSOLE_COOPERATIVE
mesh::beginUsbLoggingPort();
#endif
#if MESH_PACKET_LOGGING
mesh::serialLogBegin();
#endif
delay(1000);
board.begin();
#ifdef HAS_EXTERNAL_WATCHDOG
external_watchdog.begin();
#endif
#ifdef DISPLAY_CLASS
display_ready = display.begin();
if (display_ready) {
display.turnOff(); // Stay dark until the saved display policy is loaded.
}
#endif
int radioinit_attempts = 0;
while (!radio_init()) {
++radioinit_attempts;
MESH_DEBUG_PRINTLN("Radio init failed! (attempt %d)", radioinit_attempts);
if (radioinit_attempts >= 3) {
MESH_DEBUG_PRINTLN("Radio init failed 3x - rebooting");
board.reboot();
}
delay(500);
}
fast_rng.begin(radio_driver.getRngSeed());
FILESYSTEM* fs;
#if defined(NRF52_PLATFORM)
InternalFS.begin();
fs = &InternalFS;
IdentityStore store(InternalFS, "");
#elif defined(RP2040_PLATFORM)
LittleFS.begin();
fs = &LittleFS;
IdentityStore store(LittleFS, "/identity");
store.begin();
#elif defined(ESP32)
SPIFFS.begin(true);
fs = &SPIFFS;
IdentityStore store(SPIFFS, "/identity");
#else
#error "need to define filesystem"
#endif
const bool needs_identity = !store.load("_main", the_mesh.self_id)
|| mesh::hasReservedIdentityPrefix(the_mesh.self_id);
bool identity_ready = true;
if (needs_identity) {
identity_ready = mesh::generateUsableLocalIdentity(the_mesh.self_id, radio_new_identity);
if (identity_ready) identity_ready = store.saveWithRetry("_main", the_mesh.self_id);
}
#if defined(ESP32_PLATFORM)
mesh::discardESP32TrueRandom();
#endif
if (!identity_ready) {
MESH_DEBUG_PRINTLN("Identity generation or persistence failed after retries; rebooting");
board.reboot();
return;
}
Stream& console = mesh::usbConsolePort();
console.print("Room ID: ");
mesh::Utils::printHex(console, the_mesh.self_id.pub_key, PUB_KEY_SIZE); console.println();
command[0] = 0;
#ifdef ETHERNET_ENABLED
ethernet_command[0] = 0;
#endif
sensors.begin();
the_mesh.begin(fs);
#ifdef DISPLAY_CLASS
if (display_ready) {
#ifdef WITH_MQTT_BRIDGE
ui_task.setObserverPrefs(the_mesh.getObserverPrefs());
#endif
#ifdef DISPLAY_ACTIVITY_DASHBOARD
ui_task.setActivityWindow(the_mesh.getActivityWindow());
#endif
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
board.onBootComplete();
}
void loop() {
mesh::wireless::control().service(millis());
#if defined(NRF52_PLATFORM)
board.feedWatchdog(the_mesh.getNodePrefs()->system_watchdog_enabled != 0);
#endif
bool usb_ready = true;
#if MESH_ESP32_USB_CONSOLE_COOPERATIVE
mesh::serviceUsbLoggingPort();
mesh::serviceUsbTerminalPort();
if (mesh::takeUsbTerminalSessionReset()) {
command[0] = 0;
the_mesh.cancelPendingSerialOutput();
}
usb_ready = mesh::tryCompleteUsbTerminalSessionReset();
usb_ready = usb_ready && !the_mesh.hasPendingSerialOutput()
&& mesh::canAcceptUsbConsoleCommand();
#endif
Stream& console = mesh::usbConsolePort();
int len = strlen(command);
while (usb_ready && console.available() && len < sizeof(command)-1) {
char c = console.read();
if (c != '\n') {
command[len++] = c;
command[len] = 0;
}
console.print(c);
}
if (len == sizeof(command)-1) { // command buffer full
command[sizeof(command)-1] = '\r';
}
if (len > 0 && command[len - 1] == '\r') { // received complete line
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.handleUsbCommand(command, reply);
}
#else
the_mesh.handleUsbCommand(command, reply);
#endif
if (reply[0]) {
console.printf(" -> %s\r\n", reply);
}
command[0] = 0; // reset command buffer
}
#ifdef ETHERNET_ENABLED
ethernet_loop_maintain();
if (ethernet_take_session_reset() || !ethernet_client.connected()) {
the_mesh.cancelLocalOutput(ethernet_client);
ethernet_command[0] = 0;
}
if (!the_mesh.hasPendingLocalOutput() && ethernet_read_line(ethernet_command, sizeof(ethernet_command))) {
char reply[160];
reply[0] = 0;
if (!ethernet_handle_command(ethernet_command, reply)) {
the_mesh.handleLocalCommand(ethernet_command, reply, ethernet_client);
}
ethernet_send_reply(reply);
ethernet_command[0] = 0;
}
#endif
the_mesh.loop();
sensors.loop();
#ifdef DISPLAY_CLASS
if (display_ready) ui_task.loop();
#endif
rtc_clock.tick();
#if MESH_ESP32_USB_CONSOLE_COOPERATIVE
mesh::serviceUsbTerminalPort();
#endif
#ifdef TBEAM_1W
board.updateFanControl();
#endif
#ifdef HAS_EXTERNAL_WATCHDOG
external_watchdog.loop();
#endif
bool can_power_save = the_mesh.getNodePrefs()->powersaving_enabled
&& !board.isUsbDataConnected()
&& !mesh::wireless::control().pending();
#if defined(MOMENTARY_BUTTON_WAKE_FROM_SLEEP) \
&& MOMENTARY_BUTTON_WAKE_FROM_SLEEP \
&& defined(PIN_USER_BTN) && defined(DISPLAY_CLASS)
can_power_save = can_power_save && !user_btn.needsPolling();
#endif
if (can_power_save) {
uint32_t sleep_secs = the_mesh.getPowerSaveSleepSeconds(30);
#ifdef HAS_EXTERNAL_WATCHDOG
if (sleep_secs > 0) external_watchdog.feed();
#endif
#if defined(NRF52_PLATFORM)
if (sleep_secs > 0) {
board.sleep(0); // nrf ignores seconds param, sleeps whenever possible
}
#else
if (sleep_secs > 0 && the_mesh.millisHasNowPassed(POWERSAVING_FIRSTSLEEP_SECS * 1000)) { // To check if it is time to sleep
board.sleep(sleep_secs); // Sleep. Wake up for scheduled jobs or when receiving a LoRa packet
}
#endif
}
#if defined(ESP32_PLATFORM)
if (!can_power_save && the_mesh.getNodePrefs()->powersaving_enabled) {
delay(1); // Keep USB and the button wake interval serviced while idle.
}
#endif
}