feat(sim): port examples/simple_room_server to variants/sim/

Mirrors the simple_repeater sim port exactly: headless (no DISPLAY_CLASS,
UITask.cpp excluded from the build), new sim_simple_room_server native env
plus build_wasm_room_server.sh, own SimFS root ./sim_data_room so its
identity storage can't collide with the companion or repeater instances on
the same page/cwd.

Verified beyond "it compiles": ran the native binary and confirmed a real
_main.id identity file gets persisted through the actual SimFS/IdentityStore
path, same as the other two sim targets.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Jakub
2026-09-03 09:36:23 +02:00
co-authored by Claude Sonnet 5
parent 9cfb58a60b
commit 288a7d856b
4 changed files with 235 additions and 0 deletions
+4
View File
@@ -787,6 +787,8 @@ bool MyMesh::formatFileSystem() {
return LittleFS.format();
#elif defined(ESP32)
return SPIFFS.format();
#elif defined(SIM_PLATFORM)
return _fs->format();
#else
#error "need to implement file system erase"
return false;
@@ -852,6 +854,8 @@ void MyMesh::saveIdentity(const mesh::LocalIdentity &new_id) {
IdentityStore store(*_fs, "/identity");
#elif defined(RP2040_PLATFORM)
IdentityStore store(*_fs, "/identity");
#elif defined(SIM_PLATFORM)
IdentityStore store(*_fs, "/identity");
#else
#error "need to define saveIdentity()"
#endif
+29
View File
@@ -21,6 +21,18 @@ void halt() {
while (1) ;
}
#if defined(SIM_PLATFORM) && defined(__EMSCRIPTEN__)
#include <emscripten.h>
// Same "has setup() actually finished" readiness flag as
// examples/companion_radio/main.cpp / examples/simple_repeater/main.cpp --
// see those files' comments for why this is needed instead of a fixed
// post-ready delay.
static bool g_sim_ready = false;
extern "C" EMSCRIPTEN_KEEPALIVE int sim_is_ready() {
return g_sim_ready ? 1 : 0;
}
#endif
static char command[MAX_POST_TEXT_LEN+1];
#ifdef ETHERNET_ENABLED
static char ethernet_command[MAX_POST_TEXT_LEN+1];
@@ -28,7 +40,12 @@ static char ethernet_command[MAX_POST_TEXT_LEN+1];
void setup() {
Serial.begin(115200);
#ifndef SIM_PLATFORM
// Skip this one-shot boot pause in the sim: under Emscripten it would
// synchronously block the browser's single JS thread for a full second
// (see examples/simple_repeater/main.cpp's setup() for the same guard).
delay(1000);
#endif
board.begin();
@@ -63,6 +80,15 @@ void setup() {
SPIFFS.begin(true);
fs = &SPIFFS;
IdentityStore store(SPIFFS, "/identity");
#elif defined(SIM_PLATFORM)
// Own root, distinct from both examples/companion_radio/main.cpp's
// "./sim_data" and examples/simple_repeater/main.cpp's
// "./sim_data_repeater" -- so a room-server instance's identity can never
// collide with either, even sharing a cwd (native) or a page (wasm).
static SimFS sim_fs("./sim_data_room");
fs = &sim_fs;
IdentityStore store(sim_fs, "/identity");
store.begin();
#else
#error "need to define filesystem"
#endif
@@ -101,6 +127,9 @@ void setup() {
#endif
board.onBootComplete();
#if defined(SIM_PLATFORM) && defined(__EMSCRIPTEN__)
g_sim_ready = true;
#endif
}
void loop() {
+149
View File
@@ -0,0 +1,149 @@
#!/usr/bin/env bash
# Emscripten build script for the examples/simple_room_server sim -- sibling
# of build_wasm.sh (companion_radio) and build_wasm_repeater.sh
# (simple_repeater), NOT a modification of either.
#
# Differences from build_wasm_repeater.sh, all deliberate:
# - examples/simple_room_server/{main,MyMesh}.cpp instead of
# examples/simple_repeater/{main,MyMesh}.cpp -- UITask.cpp is likewise
# excluded (not added to SRCS at all) since no -DDISPLAY_CLASS is
# defined here, matching every real headless room-server env.
# - -DSIM_FS_ROOT=\"/sim_data_room\" -- must match the "./sim_data_room"
# SimFS root examples/simple_room_server/main.cpp's own SIM_PLATFORM
# branch constructs (see variants/sim/sim_main.cpp), kept DIFFERENT from
# both companion_radio's "/sim_data" and simple_repeater's
# "/sim_data_repeater" so identities can never collide on one page.
# - -sEXPORT_NAME=MeshCoreSimRoomServer -- its own MODULARIZE factory
# name, distinct from MeshCoreSim/MeshCoreSimRepeater.
# - Output lands under web/build/room_server/.
#
# Usage:
# variants/sim/build_wasm_room_server.sh # release-ish build (-O2)
# variants/sim/build_wasm_room_server.sh debug # -O0 -g
#
# Same emsdk 6.0.9 requirement as build_wasm.sh -- see that script's header
# comment for the install command if variants/sim/tools/emsdk/ is missing.
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
EMSDK_DIR="$SCRIPT_DIR/tools/emsdk"
EMXX="$EMSDK_DIR/upstream/emscripten/em++"
OUT_DIR="$SCRIPT_DIR/web/build/room_server"
if [ ! -x "$EMXX" ]; then
echo "error: em++ not found at $EMXX" >&2
echo "Install it first:" >&2
echo " cd $EMSDK_DIR && python3 ./emsdk.py install 6.0.9 && python3 ./emsdk.py activate 6.0.9" >&2
exit 1
fi
BUILD_MODE="${1:-release}"
if [ "$BUILD_MODE" = "debug" ]; then
OPT_FLAGS=(-O0 -g)
else
OPT_FLAGS=(-O2)
fi
mkdir -p "$OUT_DIR"
cd "$REPO_ROOT"
# Same list as variants/sim/platformio.ini's [env:sim_simple_room_server]
# build_src_filter, spelled as real paths.
SRCS=(
src/Dispatcher.cpp
src/Identity.cpp
src/Mesh.cpp
src/Packet.cpp
src/Utils.cpp
src/helpers/AdvertDataHelpers.cpp
src/helpers/BaseChatMesh.cpp
src/helpers/ClientACL.cpp
src/helpers/CommonCLI.cpp
src/helpers/ConfigSerializer.cpp
src/helpers/DeviceDiag.cpp
src/helpers/IdentityStore.cpp
src/helpers/RegionMap.cpp
src/helpers/StaticPoolPacketManager.cpp
src/helpers/TransportKeyStore.cpp
src/helpers/TxtDataHelpers.cpp
lib/ed25519/add_scalar.c
lib/ed25519/fe.c
lib/ed25519/ge.c
lib/ed25519/key_exchange.c
lib/ed25519/keypair.c
lib/ed25519/sc.c
lib/ed25519/seed.c
lib/ed25519/sha512.c
lib/ed25519/sign.c
lib/ed25519/verify.c
variants/sim/sim_main.cpp
variants/sim/target.cpp
variants/sim/thirdparty/crypto/AES128.cpp
variants/sim/thirdparty/crypto/AESCommon.cpp
variants/sim/thirdparty/crypto/BigNumberUtil.cpp
variants/sim/thirdparty/crypto/BlockCipher.cpp
variants/sim/thirdparty/crypto/Crypto.cpp
variants/sim/thirdparty/crypto/Curve25519.cpp
variants/sim/thirdparty/crypto/Ed25519.cpp
variants/sim/thirdparty/crypto/Hash.cpp
variants/sim/thirdparty/crypto/rng_stub.cpp
variants/sim/thirdparty/crypto/SHA256.cpp
variants/sim/thirdparty/crypto/SHA512.cpp
variants/sim/thirdparty/cayennelpp/CayenneLPP.cpp
variants/sim/thirdparty/cayennelpp/CayenneLPPPolyline.cpp
examples/simple_room_server/main.cpp
examples/simple_room_server/MyMesh.cpp
)
INCLUDES=(
-Ivariants/sim/arduino
-Ivariants/sim
-Ivariants/sim/thirdparty/crypto
-Ivariants/sim/thirdparty/cayennelpp
-Ivariants/sim/thirdparty/arduinojson
-Ilib/ed25519
-Isrc
-Iexamples/simple_room_server
)
DEFINES=(
-DSIM_PLATFORM
-DMESH_DEBUG=0
-DENABLE_ADVERT_ON_BOOT=1
-DADVERT_NAME="\"Sim Room\""
-DSIM_FS_ROOT="\"/sim_data_room\""
)
# -funsigned-char: same reasoning as build_wasm.sh/build_wasm_repeater.sh --
# keep it uniform across every sim TU even though this app has no UI either.
COMMON_FLAGS=(-std=c++17 -funsigned-char "${OPT_FLAGS[@]}" "${DEFINES[@]}" "${INCLUDES[@]}")
# Mirror each source's own directory under obj/ -- same macOS APFS
# case-insensitive sha512.o/SHA512.o collision reason as the other two
# build scripts (same source tree).
OBJ_DIR="$OUT_DIR/obj"
rm -rf "$OBJ_DIR"
OBJS=()
for src in "${SRCS[@]}"; do
obj="$OBJ_DIR/${src%.*}.o"
mkdir -p "$(dirname "$obj")"
"$EMXX" -c "${COMMON_FLAGS[@]}" "$src" -o "$obj"
OBJS+=("$obj")
done
"$EMXX" \
"${OBJS[@]}" \
-lidbfs.js \
-sALLOW_MEMORY_GROWTH=1 \
-sFORCE_FILESYSTEM=1 \
-sMODULARIZE=1 \
-sEXPORT_NAME=MeshCoreSimRoomServer \
-sENVIRONMENT=web \
-sEXIT_RUNTIME=0 \
-sEXPORTED_RUNTIME_METHODS=FS,ccall,cwrap,HEAPU8 \
-sEXPORTED_FUNCTIONS=_main,_malloc,_free \
-o "$OUT_DIR/meshcore_sim_room_server.js"
echo ""
echo "Built: $OUT_DIR/meshcore_sim_room_server.js (+ .wasm alongside it)"
+53
View File
@@ -138,3 +138,56 @@ build_src_filter =
; rather than porting GPIO stubs into variants/sim/arduino/Arduino.h for
; code that would never run.
-<../examples/simple_repeater/UITask.cpp>
; Headless native build of the real examples/simple_room_server app logic
; against the same variants/sim/ interfaces -- sibling of
; [env:sim_simple_repeater] above, same reasoning throughout (no
; DISPLAY_CLASS, so UITask.cpp is never needed here either -- unlike
; simple_repeater's, this one's UITask.cpp actually WOULD compile fine
; headless (its digitalRead(PIN_USER_BTN) call is itself gated behind
; #ifdef PIN_USER_BTN, which this env never defines), but there's still no
; reason to compile a translation unit that's never linked in, so it's
; excluded the same way).
[env:sim_simple_room_server]
platform = native
build_type = debug
build_flags =
-std=c++17
-g
-funsigned-char
-D SIM_PLATFORM
-D MESH_DEBUG=0
-D ENABLE_ADVERT_ON_BOOT=1
-D ADVERT_NAME='"Sim Room"'
-I variants/sim/arduino
-I variants/sim
-I variants/sim/thirdparty/crypto
-I variants/sim/thirdparty/cayennelpp
-I variants/sim/thirdparty/arduinojson
-I lib/ed25519
-I src
-I examples/simple_room_server
build_src_filter =
-<*>
+<../src/Dispatcher.cpp>
+<../src/Identity.cpp>
+<../src/Mesh.cpp>
+<../src/Packet.cpp>
+<../src/Utils.cpp>
+<../src/helpers/AdvertDataHelpers.cpp>
+<../src/helpers/BaseChatMesh.cpp>
+<../src/helpers/ClientACL.cpp>
+<../src/helpers/CommonCLI.cpp>
+<../src/helpers/ConfigSerializer.cpp>
+<../src/helpers/DeviceDiag.cpp>
+<../src/helpers/IdentityStore.cpp>
+<../src/helpers/RegionMap.cpp>
+<../src/helpers/StaticPoolPacketManager.cpp>
+<../src/helpers/TransportKeyStore.cpp>
+<../src/helpers/TxtDataHelpers.cpp>
+<../lib/ed25519/*.c>
+<../variants/sim/*.cpp>
+<../variants/sim/thirdparty/crypto/*.cpp>
+<../variants/sim/thirdparty/cayennelpp/*.cpp>
+<../examples/simple_room_server/*.cpp>
-<../examples/simple_room_server/UITask.cpp>