feat(sim): two-device messaging + repeater relay over a JS ether

Ports examples/simple_repeater to variants/sim/ (new sim_simple_repeater
native env + build_wasm_repeater.sh) and adds a JS "ether"
(variants/sim/web/mesh.html) that bridges two real companion_radio WASM
instances through a real simple_repeater instance in a strict A<->R<->B
topology (no direct A-B link), proving genuine relay routing rather than
a shortcut.

Also fixes multi-instance issues Phase 2's single-instance design never
surfaced: SimDisplayDriver's canvas context/id caching was keyed on a
single global instead of per-instance, and both wasm builds were missing
_malloc/_free/HEAPU8 runtime exports needed for the ether to poke bytes
into an instance's memory.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Jakub
2026-09-03 09:14:15 +02:00
co-authored by Claude Sonnet 5
parent 8f4c92a217
commit 9cfb58a60b
18 changed files with 1199 additions and 54 deletions
+76
View File
@@ -62,3 +62,79 @@ build_src_filter =
+<../variants/sim/thirdparty/cayennelpp/*.cpp>
+<../examples/companion_radio/*.cpp>
+<../examples/companion_radio/ui-new/*.cpp>
; Phase 3: native build of the real examples/simple_repeater app logic
; (MyMesh/no UI) against the same variants/sim/ interfaces as
; [env:sim_companion_radio] above. No DISPLAY_CLASS defined -- a repeater
; doesn't need one (examples/simple_repeater/main.cpp already gates its
; UITask/display use behind #ifdef DISPLAY_CLASS, same as every real
; headless-repeater hardware env does), which also means UITask.cpp/.h
; never need to be ported/compiled for this target at all.
;
; Everything else mirrors [env:sim_companion_radio] deliberately closely
; (same src/*.cpp list, same real hardware envs' own convention of sharing
; one broad src/helpers/*.cpp filter across companion_radio/simple_repeater/
; room_server -- see e.g. variants/ebyte_eora_s3/platformio.ini's
; [env:Ebyte_EoRa-S3] base + its "_Repeater" env only adding
; "+<../examples/simple_repeater>" on top) -- a few of those .cpp files
; (BaseChatMesh.cpp, ConfigSerializer.cpp, DeviceDiag.cpp) aren't actually
; reachable from simple_repeater's own code, but compiling them in unused is
; harmless and keeps this env a straightforward diff against the one above
; rather than a hand-pruned, easy-to-drift-out-of-sync list.
[env:sim_simple_repeater]
platform = native
build_type = debug
build_flags =
-std=c++17
-g
-funsigned-char
-D SIM_PLATFORM
-D MESH_DEBUG=0
; Real hardware repeater envs get this from [arduino_base] (which this
; from-scratch platform=native env deliberately doesn't extend -- see the
; header comment on [env:sim_companion_radio] above); set it explicitly so
; the repeater actually broadcasts its own identity on boot, same as a
; real one would.
-D ENABLE_ADVERT_ON_BOOT=1
-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_repeater
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_repeater/*.cpp>
; UITask.cpp uses real Arduino GPIO constants (HIGH/LOW/digitalRead(),
; gated on real #if PIN_USER_BTN/UI_HAS_JOYSTICK defines this env never
; sets) directly at file scope in its button-polling code, unconditionally
; -- unlike companion_radio's ui-new/UITask.cpp, it isn't written to
; compile cleanly with no display at all. Since DISPLAY_CLASS is
; deliberately undefined for this headless repeater env (main.cpp's own
; `#ifdef DISPLAY_CLASS` already skips constructing a UITask instance
; entirely), this translation unit is simply never needed -- exclude it
; rather than porting GPIO stubs into variants/sim/arduino/Arduino.h for
; code that would never run.
-<../examples/simple_repeater/UITask.cpp>