Add transport mode notes and config updates

This commit is contained in:
James L
2026-03-14 12:18:37 -04:00
parent d8c925769d
commit 79cb2d49e8
6 changed files with 120 additions and 5 deletions

View File

@@ -1,7 +1,10 @@
// Copyright (C) 2026, Boundary Mode Extension
// Based on microReticulum_Firmware by Mark Qvist
//
// BoundaryConfig.h — Captive-portal web configuration for Boundary Mode.
// BoundaryConfig.h — Captive-portal web configuration for the legacy
// "Boundary Mode" path. This should be renamed to "Transport Mode"
// together with the rest of the boundary-mode terminology. In this fork,
// transport/boundary mode is the only intended mode of operation.
// When triggered (first boot with no config, or button hold >5s),
// the device starts a WiFi AP with a web form for all settings:
// WiFi STA credentials, TCP backbone params, LoRa radio params,
@@ -59,6 +62,16 @@ static const char* const BW_OPTIONS_LABELS[] PROGMEM = {
};
static const int BW_OPTIONS_COUNT = sizeof(BW_OPTIONS_HZ) / sizeof(BW_OPTIONS_HZ[0]);
static uint8_t config_default_display_rotation() {
#if BOARD_MODEL == BOARD_LORA32_V2_1 || BOARD_MODEL == BOARD_TBEAM || BOARD_MODEL == BOARD_RAK4631
return 0;
#elif BOARD_MODEL == BOARD_HELTEC32_V2 || BOARD_MODEL == BOARD_HELTEC32_V3 || BOARD_MODEL == BOARD_HELTEC32_V4 || BOARD_MODEL == BOARD_HELTEC_T114 || BOARD_MODEL == BOARD_TBEAM_S_V1
return 1;
#else
return 3;
#endif
}
// ─── HTML Page Generation ────────────────────────────────────────────────────
static void config_send_html() {
@@ -304,6 +317,11 @@ static void config_send_html() {
cur_blank = EEPROM.read(eeprom_addr(ADDR_CONF_DBLK));
}
uint8_t cur_rotation = EEPROM.read(eeprom_addr(ADDR_CONF_DROT));
if (cur_rotation > 3) {
cur_rotation = config_default_display_rotation();
}
static const uint8_t blank_vals[] = { 0, 1, 5, 10, 30, 60 };
static const char* blank_labels[] = { "Never", "1 minute", "5 minutes", "10 minutes", "30 minutes", "60 minutes" };
static const int blank_count = 6;
@@ -320,6 +338,23 @@ static void config_send_html() {
html += F("</select>");
html += F("<p class='note'>Turn off display after inactivity to save power</p>");
html += F("<label>Display Orientation</label><select name='disp_rot'>");
html += F("<option value='0'");
if (cur_rotation == 0) html += F(" selected");
html += F(">Landscape</option>");
html += F("<option value='1'");
if (cur_rotation == 1) html += F(" selected");
html += F(">Portrait</option>");
html += F("<option value='2'");
if (cur_rotation == 2) html += F(" selected");
html += F(">Landscape Flipped</option>");
html += F("<option value='3'");
if (cur_rotation == 3) html += F(" selected");
html += F(">Portrait Flipped</option>");
html += F("</select>");
html += F("<p class='note'>Choose the orientation that matches your OLED mounting. "
"Landscape modes place the two status panes side by side; portrait modes stack them.</p>");
// ── Submit ──
html += F(
"<button type='submit'>Save &amp; Reboot</button>"
@@ -353,6 +388,14 @@ static void config_handle_save() {
// Set WiFi mode to STA
EEPROM.write(eeprom_addr(ADDR_CONF_WIFI), WR_WIFI_STA);
// Boundary mode always uses DHCP on the STA interface. Clear the legacy
// static IP and netmask slots so stale values from older firmware or tools
// cannot force a persistent static address.
for (int i = 0; i < 4; i++) {
EEPROM.write(config_addr(ADDR_CONF_IP + i), 0x00);
EEPROM.write(config_addr(ADDR_CONF_NM + i), 0x00);
}
// ── WiFi enable setting ──
boundary_state.wifi_enabled = (config_server->arg("wifi_en").toInt() == 1);
@@ -370,6 +413,12 @@ static void config_handle_save() {
eeprom_update(eeprom_addr(ADDR_CONF_DBLK), blank_val);
}
int display_rotation = config_server->arg("disp_rot").toInt();
if (display_rotation < 0 || display_rotation > 3) {
display_rotation = config_default_display_rotation();
}
eeprom_update(eeprom_addr(ADDR_CONF_DROT), (uint8_t)display_rotation);
// ── TCP backbone settings ──
boundary_state.tcp_mode = (uint8_t)config_server->arg("tcp_mode").toInt(); // 0=disabled, 1=client
if (boundary_state.tcp_mode > 1) boundary_state.tcp_mode = 0;
@@ -481,6 +530,14 @@ static void config_handle_redirect() {
// ─── Check if config is needed ───────────────────────────────────────────────
bool boundary_needs_config() {
// If the RTNode app marker is missing, this node was either never
// configured by RTNode or was flashed from a different firmware family
// such as stock RNode. Force the portal so RTNode can claim and rewrite
// its persisted settings explicitly.
if (!boundary_app_marker_valid()) {
return true;
}
// Check if WiFi SSID is configured
char ssid[33];
for (int i = 0; i < 32; i++) {

View File

@@ -1,9 +1,11 @@
// Copyright (C) 2026, Boundary Mode Extension
// Based on microReticulum_Firmware by Mark Qvist
//
// BoundaryMode.h — Configuration and runtime state for the Boundary Mode
// firmware variant. This header defines the WiFi backbone connection
// parameters and boundary-specific operational settings.
// BoundaryMode.h — Configuration and runtime state for the legacy
// "Boundary Mode" firmware variant. Going forward this should be renamed
// "Transport Mode". It is the only intended operating mode for this fork;
// the old multi-mode split is kept only for compatibility while the codebase
// is being simplified.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
@@ -15,8 +17,19 @@
#ifdef BOUNDARY_MODE
// Compatibility alias for the planned rename from Boundary Mode to
// Transport Mode. New code should prefer the transport terminology even
// while the legacy BOUNDARY_MODE compile-time flag still exists.
#ifndef TRANSPORT_MODE
#define TRANSPORT_MODE 1
#endif
// ─── Boundary Mode Configuration ────────────────────────────────────────────
//
// NOTE: "Boundary Mode" is the legacy name. This should be relabeled
// "Transport Mode" once the remaining non-transport code paths are removed.
// In practice this is the only supported mode in this firmware branch.
//
// The boundary node operates with TWO RNS interfaces:
//
// 1. LoRaInterface (MODE_GATEWAY) — radio side, handles LoRa mesh
@@ -72,10 +85,16 @@
#define ADDR_CONF_IFAC_EN 0xD6 // IFAC enable flag (1 byte, 0x73 = enabled)
#define ADDR_CONF_IFAC_NAME 0xD7 // Network name (33 bytes, null-terminated)
#define ADDR_CONF_IFAC_PASS 0xF8 // Passphrase (33 bytes, null-terminated)
// Total: 0x119 (281 bytes — extends beyond 256-byte CONFIG area into
#define ADDR_CONF_APP_MARKER0 0x119 // RTNode app marker byte 0
#define ADDR_CONF_APP_MARKER1 0x11A // RTNode app marker byte 1
#define ADDR_CONF_APP_VERSION 0x11B // RTNode app config version
// Total: 0x11C (284 bytes — extends beyond 256-byte CONFIG area into
// unused EEPROM gap; safe on ESP32 where EEPROM starts at 824)
#define BOUNDARY_ENABLE_BYTE 0x73
#define BOUNDARY_APP_MARKER0 0x52
#define BOUNDARY_APP_MARKER1 0x54
#define BOUNDARY_APP_VERSION 0x01
// ─── Boundary Mode Runtime State ─────────────────────────────────────────────
#ifndef BOUNDARY_STATE_DEFINED
@@ -115,6 +134,24 @@ extern BoundaryState boundary_state;
// ─── Boundary Mode EEPROM Load/Save ─────────────────────────────────────────
inline bool boundary_app_marker_valid() {
return EEPROM.read(config_addr(ADDR_CONF_APP_MARKER0)) == BOUNDARY_APP_MARKER0 &&
EEPROM.read(config_addr(ADDR_CONF_APP_MARKER1)) == BOUNDARY_APP_MARKER1;
}
inline bool boundary_app_version_matches() {
return boundary_app_marker_valid() &&
EEPROM.read(config_addr(ADDR_CONF_APP_VERSION)) == BOUNDARY_APP_VERSION;
}
inline void boundary_clear_app_marker() {
EEPROM.write(config_addr(ADDR_CONF_APP_MARKER0), 0xFF);
EEPROM.write(config_addr(ADDR_CONF_APP_MARKER1), 0xFF);
EEPROM.write(config_addr(ADDR_CONF_APP_VERSION), 0xFF);
EEPROM.write(config_addr(ADDR_CONF_BMODE), 0xFF);
EEPROM.commit();
}
inline void boundary_load_config() {
// Check if boundary mode is configured
uint8_t bmode = EEPROM.read(config_addr(ADDR_CONF_BMODE));
@@ -258,6 +295,9 @@ inline void boundary_save_config() {
EEPROM.write(config_addr(ADDR_CONF_IFAC_PASS + i), boundary_state.ifac_passphrase[i]);
}
EEPROM.write(config_addr(ADDR_CONF_IFAC_PASS + 32), 0x00);
EEPROM.write(config_addr(ADDR_CONF_APP_MARKER0), BOUNDARY_APP_MARKER0);
EEPROM.write(config_addr(ADDR_CONF_APP_MARKER1), BOUNDARY_APP_MARKER1);
EEPROM.write(config_addr(ADDR_CONF_APP_VERSION), BOUNDARY_APP_VERSION);
EEPROM.commit();
}

View File

@@ -28,6 +28,9 @@
#include "Utilities.h"
// CBA Boundary Mode
// NOTE: Boundary Mode is the legacy name. This firmware branch intends to
// converge on a single Transport Mode, with the BOUNDARY_MODE symbol kept
// temporarily as a compatibility shim during cleanup.
#ifdef BOUNDARY_MODE
#include "BoundaryMode.h"
#include "TcpInterface.h"
@@ -525,6 +528,7 @@ void setup() {
// Enter config mode if: first boot with no config, OR button-triggered reboot,
// OR bootloop detected
bool app_marker_missing = !boundary_app_marker_valid();
bool need_config = boundary_needs_config();
bool config_requested = (boundary_config_request == BOUNDARY_CONFIG_MAGIC);
bool skip_config = (boundary_skip_config == BOUNDARY_SKIP_MAGIC);
@@ -543,6 +547,9 @@ void setup() {
Serial.println("[Boundary] Entering config portal due to bootloop recovery");
} else if (config_requested) {
Serial.println("[Boundary] Config mode requested via button hold");
} else if (app_marker_missing) {
Serial.println("[Boundary] RTNode app marker missing — previous firmware was not RTNode or config is unclaimed");
Serial.println("[Boundary] Starting config portal to migrate settings into RTNode");
} else {
Serial.println("[Boundary] No configuration found — starting config portal");
}
@@ -1734,6 +1741,9 @@ void serial_callback(uint8_t sbyte) {
eeprom_conf_save();
} else if (command == CMD_CONF_DELETE) {
eeprom_conf_delete();
#ifdef BOUNDARY_MODE
boundary_clear_app_marker();
#endif
} else if (command == CMD_FB_EXT) {
#if HAS_DISPLAY == true
if (sbyte == 0xFF) {

BIN
Release/rnode_firmware_heltec32v3.bin Normal file → Executable file

Binary file not shown.

View File

@@ -78,6 +78,13 @@ void wifi_remote_start_ap() {
void wifi_remote_start_sta() {
WiFi.mode(WIFI_STA);
#ifdef BOUNDARY_MODE
// Boundary mode does not expose static STA addressing in its config flow.
// Always return the station interface to DHCP so stale legacy EEPROM data
// cannot pin the node to an unintended address.
WiFi.config(INADDR_NONE, INADDR_NONE, INADDR_NONE);
#else
uint8_t ip[4]; bool ip_ok = true;
for (uint8_t i = 0; i < 4; i++) { ip[i] = EEPROM.read(config_addr(ADDR_CONF_IP+i)); }
if (ip[0]==0x00 && ip[1]==0x00 && ip[2]==0x00 && ip[3]==0x00) { ip_ok = false; }
@@ -97,6 +104,7 @@ void wifi_remote_start_sta() {
IPAddress dns2(1, 1, 1, 1);
WiFi.config(sta_ip, sta_gw, sta_nm, dns1, dns2);
}
#endif
delay(100);
if (wr_ssid[0] != 0x00) {