Files
MeshCore-Solo/variants/sim/platformio.ini
T
JakubandClaude Sonnet 5 b2cf459460 fix(sim): text-width/render bugs, splash version, wasm-fetch error, battery lag
Code-review pass over the buzzer/sim commits turned up several real bugs,
plus two issues found afterward from manual browser testing:

Rendering (SimDisplayDriverCanvas, variants/sim/SimDisplayDriver.h + target.cpp):
- getTextWidth() measured UTF-8 BYTES (strlen()*6), not codepoints. Since
  b067e95b stopped stripping accents, any accented string now measures
  double its real width -- mis-centred titles, premature ellipsis/marquee,
  badges pushed off-screen. Now uses the real MiscFixedRenderer measurement
  (miscFixedTextWidth()), same as SH1106Display/SSD1306Display.
- Added the matching getCodepointWidth() override (O(1) single-glyph
  advance), same pattern as SSD1306Display.
- isSingleFont() was left at the base class's `false`, though this backend
  only ever renders MiscFixed -- UITask.cpp's status-bar indicator height
  keys off this (`lh-2` vs `lh`), so the sim drew it 2px taller than a real
  board.
- print() blitted the full 128x64 canvas on every call (dozens per frame,
  60fps) -- now tracks a dirty bounding box and only clears/blits the
  region actually touched.

Web Audio (buzzer bridge, index.html + mesh.html):
- No AudioContext.resume() -- a context created (or later suspended) in the
  'suspended' state (Safari/Firefox, or any browser backgrounding the tab)
  stayed silent forever. Now resumed on every gesture.
- linearRampToValueAtTime with no anchoring setValueAtTime interpolates
  from the LAST scheduled event, not "now" -- so the anti-click ramps could
  effectively snap instead of fading. Fixed with cancelScheduledValues +
  setValueAtTime(current) before each ramp.
- mesh.html: a gesture only armed the clicked instance's audio. Click A,
  send A->B, and B (the one actually meant to beep on receipt) stayed
  silent. Now any gesture arms both A and B.
- RTTTL rests (freq=0, still "playing") now explicitly hold pitch and drop
  gain instead of it happening to work by coincidence.

Misc: sim_test_get_num_contacts() was missing the g_sim_ready gate every
other sim_test_* hook has, so it could return a bogus negative count before
setup() finishes seeding num_contacts.

Splash screen missing "Solo <version>" bar: variants/sim never defined
FIRMWARE_SOLO_BUILD (every real Solo board does), so SplashScreen silently
skipped that whole line -- the sim looked like a plain non-Solo companion
build. Added -D FIRMWARE_SOLO_BUILD=1 to platformio.ini and build_wasm.sh.
Verified on a real canvas screenshot: "MESHCORE 1.17.1 / 19 Aug 2026 /
Solo v1.27".

Wasm-fetch error message: "failed to start: RuntimeError: Aborted(both
async and sync fetching of the wasm failed)" is Emscripten's own opaque
message for the single most common real cause -- the page opened via
file://...index.html instead of served over http(s) (fetch() on a local
file is blocked by CORS in both Chrome and Safari, confirmed by reproducing
the exact same error/stack via file://). Both harnesses now detect
location.protocol === 'file:' and show an actionable message with the
one-line fix instead of the raw stack trace.

Battery-set latency: SimMainBoard's battery value is an exact, instantaneous
JS-set integer (see sim_battery_set_mv()), but UITask's battery-check code
polls it every 8s and runs it through an EMA (alpha=0.2) meant to smooth a
REAL board's noisy ADC -- so a value typed into the demo UI could take tens
of seconds to visibly settle. SIM_PLATFORM now checks every 250ms and skips
the EMA (nothing to smooth), since the reading is already clean. Measured
on real canvas pixels: indicator update now lands within one screen-refresh
cycle instead of up to 8s+.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-03 19:32:23 +02:00

207 lines
8.4 KiB
INI

; variants/sim -- native (host) build of the real companion_radio app logic
; (MyMesh/UITask/DataStore), for Phase 1 of the in-house browser-sim project
; (see the plan doc / project memory for the full Emscripten roadmap this is
; step one of). No Emscripten here yet -- plain g++, a terminal ASCII-art
; "display", stdin for input. Deliberately the easiest possible target so the
; from-scratch port can be debugged without WASM complexity on top.
;
; Not `extends`-ing arduino_base/esp32_base etc: those pull in RadioLib, Wire,
; SPI and a real Arduino framework, none of which this build wants -- it's
; written against platform=native from scratch, closer in spirit to
; [env:native]/[env:native_kiss_modem] below than to a real board env.
[env:sim_companion_radio]
platform = native
build_type = debug
build_flags =
-std=c++17
-g
; ARM toolchains (every real board target) default `char` to UNSIGNED;
; x86/ARM64 host compilers default it to signed. The UI's KEY_* codes
; (src/helpers/ui/UIScreen.h) go up to 0xF3 and get compared/stored as
; plain `char` throughout (KeyboardWidget.h, PopupMenu.h, UITask.cpp) --
; without this flag those comparisons are silently always-false on a
; signed-char host (a negative char never equals a positive KEY_* int
; literal), which would make keyboard input compile cleanly but never
; actually navigate anything.
-funsigned-char
-D SIM_PLATFORM
-D MESH_DEBUG=0
-D DISPLAY_CLASS=SimDisplayDriver
; No real buzzer pin -- this is a dummy sentinel, purely to activate the
; #ifdef PIN_BUZZER guards in UITask.h/.cpp/SoundNotifier.h/main.cpp
; unchanged (real boards set this to their actual GPIO pin number the same
; way). genericBuzzer's own #ifdef SIM_PLATFORM branch (src/helpers/ui/
; buzzer.cpp) never touches a real pin, so the value itself is unused.
-D PIN_BUZZER=0
; Every real board running this same ui-new/ tree builds as a Solo config
; (see e.g. solo/heltec_v3/platformio.ini) -- without this, SplashScreen
; (UITask.cpp) skips the whole "Solo <version>" bar under the big MeshCore
; version digits, and the splash silently looks like a plain non-Solo
; companion build instead of what it actually is.
-D FIRMWARE_SOLO_BUILD=1
-D MAX_CONTACTS=100
-D MAX_GROUP_CHANNELS=8
-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/companion_radio
-I examples/companion_radio/ui-new
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>
+<../src/helpers/ui/buzzer.cpp>
+<../lib/ed25519/*.c>
+<../variants/sim/*.cpp>
+<../variants/sim/thirdparty/crypto/*.cpp>
+<../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>
; 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>