mirror of
https://github.com/MarekZegare4/MeshCore-Solo.git
synced 2026-09-14 23:26:38 +00:00
feat(sim): add variants/sim/ — real companion_radio firmware on native + Emscripten
New board variant compiling the unmodified MyMesh/UITask/DataStore app logic against real mesh::Radio/MainBoard/RTCClock/RNG interfaces, for running the actual firmware outside embedded hardware: - Native (plain g++, platform = native): ASCII-art display over stdout, stdin-driven input, local-disk-backed DataStore/IdentityStore. - Emscripten/WASM (variants/sim/build_wasm.sh, since PlatformIO's native platform force-overrides any CC/CXX toolchain override back to system clang++): canvas-backed display, IDBFS-backed persistence across page reloads, JS-callable input via sim_enqueue_key(), emscripten_set_main_loop. Real rweather/Crypto (AES128/SHA256/Ed25519) vendored unmodified and proven working on both targets. variants/sim/web/index.html is a bare verification harness, not the polished website embed.
This commit is contained in:
@@ -0,0 +1,112 @@
|
||||
// Process/runtime entry point for the sim build. Real Arduino cores provide
|
||||
// their own main() (call setup() once, then loop() forever); platform=native
|
||||
// has no such core, so this is that main() for both Phase 1's native target
|
||||
// and Phase 2's Emscripten target -- the two are different enough (a native
|
||||
// process owns its own loop and a real stdin tty; a wasm module in a browser
|
||||
// tab must hand control back to the browser's event loop between ticks, and
|
||||
// has no stdin at all) that they get fully separate #ifdef __EMSCRIPTEN__
|
||||
// branches below rather than one branch trying to cover both.
|
||||
#include <Arduino.h>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
|
||||
// Defined by examples/companion_radio/main.cpp (Arduino sketch convention:
|
||||
// no header declares these, every board's own entry point just forward-
|
||||
// declares and calls them).
|
||||
void setup();
|
||||
void loop();
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
// ---------------------------------------------------------------------
|
||||
// Phase 2: Emscripten/browser entry point.
|
||||
#include <emscripten.h>
|
||||
#include "SimFS.h" // sim_fs_mount_idbfs()
|
||||
|
||||
// One tick of the real app's cooperative loop -- identical body to
|
||||
// examples/companion_radio/main.cpp's own loop() (the_mesh.loop();
|
||||
// sensors.loop(); ui_task.loop(); rtc_clock.tick(); ...), so this wrapper
|
||||
// adds nothing of its own; it exists only because emscripten_set_main_loop
|
||||
// wants a void(void) function pointer, and passing `loop` directly would
|
||||
// also work here, but naming it makes the call below self-documenting.
|
||||
static void sim_main_loop_tick() {
|
||||
loop();
|
||||
}
|
||||
|
||||
// Called back from JS (see SimFS.h's sim_fs_mount_idbfs()) once the async
|
||||
// IndexedDB -> MEMFS pull finishes. This is where the app's boot sequence
|
||||
// actually starts -- deliberately NOT in main() itself, since main() must
|
||||
// return (or hand off via emscripten_exit_with_live_runtime()) long before
|
||||
// this callback ever fires.
|
||||
extern "C" EMSCRIPTEN_KEEPALIVE void sim_idbfs_ready() {
|
||||
setup();
|
||||
// fps=0, simulate_infinite_loop=1: let the browser's own
|
||||
// requestAnimationFrame cadence drive ticks (Emscripten's documented
|
||||
// recommendation for anything drawing to a <canvas>) rather than a fixed
|
||||
// interval -- ties the sim's tick rate to the actual display refresh, and
|
||||
// it's throttled/paused for free by the browser when the tab is hidden or
|
||||
// backgrounded, which a manual setInterval(..., fixed_ms) wouldn't get.
|
||||
emscripten_set_main_loop(sim_main_loop_tick, 0, 1);
|
||||
}
|
||||
|
||||
int main() {
|
||||
printf("MeshCore sim (wasm) starting -- mounting IDBFS at /sim_data...\n");
|
||||
// "/sim_data" must match the relative "./sim_data" DataStore store(sim_fs,
|
||||
// ...) in examples/companion_radio/main.cpp resolves to -- see the long
|
||||
// comment on sim_fs_mount_idbfs() in SimFS.h for why those are the same
|
||||
// filesystem node under Emscripten's default cwd ("/").
|
||||
sim_fs_mount_idbfs("/sim_data");
|
||||
// Keep the runtime alive after main() returns instead of tearing it down
|
||||
// (the default for a `main()` that returns under Emscripten) -- the real
|
||||
// boot sequence hasn't happened yet, it's waiting on sim_idbfs_ready()
|
||||
// above, an async JS callback that fires well after main() itself is done.
|
||||
emscripten_exit_with_live_runtime();
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else
|
||||
// ---------------------------------------------------------------------
|
||||
// Phase 1: native (host process) entry point -- unchanged from the
|
||||
// original Phase 1 implementation. Puts the terminal into raw/
|
||||
// non-canonical mode so UITask.cpp's SIM_PLATFORM stdin-poll branch (see
|
||||
// UITask::loop()) receives individual keystrokes immediately instead of
|
||||
// once per Enter-terminated line.
|
||||
#include <unistd.h>
|
||||
#include <termios.h>
|
||||
|
||||
static struct termios g_orig_termios;
|
||||
static bool g_termios_saved = false;
|
||||
|
||||
static void restoreTerminal() {
|
||||
if (g_termios_saved) tcsetattr(STDIN_FILENO, TCSANOW, &g_orig_termios);
|
||||
}
|
||||
|
||||
static void setupTerminal() {
|
||||
if (!isatty(STDIN_FILENO)) return; // piped/redirected stdin -- leave alone
|
||||
if (tcgetattr(STDIN_FILENO, &g_orig_termios) != 0) return;
|
||||
g_termios_saved = true;
|
||||
atexit(restoreTerminal);
|
||||
|
||||
struct termios raw = g_orig_termios;
|
||||
raw.c_lflag &= ~(ICANON | ECHO); // no line buffering, no local echo
|
||||
raw.c_cc[VMIN] = 0;
|
||||
raw.c_cc[VTIME] = 0;
|
||||
tcsetattr(STDIN_FILENO, TCSANOW, &raw);
|
||||
}
|
||||
|
||||
int main() {
|
||||
setupTerminal();
|
||||
printf("MeshCore sim (native) -- Ctrl-C to quit.\n");
|
||||
printf("Keys: arrows/WASD move, Enter/Space select, Esc/Backspace cancel, n/p next/prev.\n\n");
|
||||
|
||||
setup();
|
||||
for (;;) {
|
||||
loop();
|
||||
// Real boards spin their loop() as fast as the hardware allows too;
|
||||
// this just keeps a native process from pegging a CPU core at 100%
|
||||
// for no benefit -- 1ms is well under any UI timing this app cares
|
||||
// about (refresh/animation intervals are tens-to-hundreds of ms).
|
||||
usleep(1000);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif // __EMSCRIPTEN__
|
||||
Reference in New Issue
Block a user