emdashes begone (#10847)
This commit is contained in:
276 files changed
+1795
-1793
No files matched your search
@@ -174,7 +174,7 @@ void getMacAddr(uint8_t *dmac)
|
||||
// the same kind of stable, host-derived identifier that the BlueZ
|
||||
// path provides on Linux. If en0 isn't found or has no MAC, dmac is
|
||||
// left untouched and the caller's "Blank MAC Address not allowed!"
|
||||
// check will still fire — preserving existing behavior for users
|
||||
// check will still fire - preserving existing behavior for users
|
||||
// who deliberately rely on --hwid or YAML override.
|
||||
struct ifaddrs *ifap = nullptr;
|
||||
if (getifaddrs(&ifap) == 0) {
|
||||
@@ -548,7 +548,7 @@ void portduinoSetup()
|
||||
// Pass the full buffer size (9 = 8 chars + null) to getSerialString,
|
||||
// not 8. The function treats `len` as buffer size and reserves one
|
||||
// slot for the null terminator, so passing 8 produced a 7-char serial
|
||||
// and broke the `strlen(serial) == 8` check below — masked on Linux
|
||||
// and broke the `strlen(serial) == 8` check below - masked on Linux
|
||||
// by the BlueZ HCI MAC fallback in getMacAddr(), but on macOS (where
|
||||
// the BlueZ path is __linux__-guarded) it left mac_address empty and
|
||||
// meshtasticd refused to start.
|
||||
@@ -1170,7 +1170,7 @@ bool MAC_from_string(std::string mac_str, uint8_t *dmac)
|
||||
std::string exec(const char *cmd)
|
||||
{ // https://stackoverflow.com/a/478960
|
||||
#ifdef ARCH_PORTDUINO_WASM
|
||||
(void)cmd; // no shell/popen in the browser — shell-outs degrade to empty
|
||||
(void)cmd; // no shell/popen in the browser - shell-outs degrade to empty
|
||||
return "";
|
||||
#endif
|
||||
std::array<char, 128> buffer;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# ARCH_PORTDUINO_WASM — meshtasticd in WebAssembly (LoRa over WebUSB)
|
||||
# ARCH_PORTDUINO_WASM - meshtasticd in WebAssembly (LoRa over WebUSB)
|
||||
|
||||
Builds the full portduino firmware (`setup()`/`loop()`) to WebAssembly with
|
||||
Emscripten, so a real Meshtastic node runs in a browser tab (or headless Node)
|
||||
and drives a LoRa radio over **WebUSB** through a CH341 USB-to-SPI bridge — the
|
||||
and drives a LoRa radio over **WebUSB** through a CH341 USB-to-SPI bridge - the
|
||||
same `Ch341Hal` path the desktop `meshtasticd` uses, with the libusb backend
|
||||
swapped for a WebUSB one. The desktop/native portduino build is untouched.
|
||||
|
||||
@@ -11,7 +11,7 @@ swapped for a WebUSB one. The desktop/native portduino build is untouched.
|
||||
| file | role |
|
||||
| -------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `portduino_glue_wasm.cpp` | LoRa config (MeshToad default + `wasm_set_lora_*` setters, no YAML), VFS mount, region/MAC helpers, and the `wasm_api_*` PhoneAPI bridge |
|
||||
| `portduino_main_wasm.cpp` | `wasm_setup()` / `wasm_loop_once()` — JS drives the cooperative loop |
|
||||
| `portduino_main_wasm.cpp` | `wasm_setup()` / `wasm_loop_once()` - JS drives the cooperative loop |
|
||||
| `libpinedio_webusb.c` | WebUSB libpinedio backend (sync C ↔ async WebUSB via Asyncify `EM_ASYNC_JS`) |
|
||||
| `include/libpinedio-usb.h` | the 12-fn libpinedio API the backend implements |
|
||||
| `stubs/` | `argp.h` shim + jsoncpp serializer stub (MQTT-only, excluded) |
|
||||
@@ -28,7 +28,7 @@ This is a normal PlatformIO env (`[env:native-wasm]`) built with the
|
||||
[meshtastic/platform-wasm](https://github.com/meshtastic/platform-wasm) platform
|
||||
(emcc/em++), exactly like any other board target.
|
||||
|
||||
Prereq: an **Emscripten SDK** on `PATH` — `source <emsdk>/emsdk_env.sh` (or
|
||||
Prereq: an **Emscripten SDK** on `PATH` - `source <emsdk>/emsdk_env.sh` (or
|
||||
`export EMSDK=<path>`) so the platform builder can locate `emcc`.
|
||||
|
||||
```sh
|
||||
@@ -63,7 +63,7 @@ pump();
|
||||
```
|
||||
|
||||
**API control:** feed a `ToRadio` protobuf with `wasm_api_to_radio(ptr,len)` and
|
||||
drain `FromRadio` with `wasm_api_from_radio(out,max)` — the firmware's own
|
||||
drain `FromRadio` with `wasm_api_from_radio(out,max)` - the firmware's own
|
||||
`PhoneAPI`, unframed. The official `@meshtastic/core` SDK drives it through a
|
||||
~40-line in-process transport (see the `meshtasticd-wasm-node` repo, which hosts
|
||||
the dev server, the SDK-UI page, the headless node-usb runner, and the TCP :4403
|
||||
@@ -71,7 +71,7 @@ bridge for the Python CLI). WebUSB is Chromium-only.
|
||||
|
||||
**Reboot:** the firmware can't restart itself in wasm, so a reboot (admin/phone
|
||||
command, factory reset, or the 60 s stuck-TX watchdog) hands off to the host. In
|
||||
a browser it calls `location.reload()` — NodeDB state survives via IDBFS, so the
|
||||
a browser it calls `location.reload()` - NodeDB state survives via IDBFS, so the
|
||||
node comes back with the same identity. Headless, provide a `Module.onReboot`
|
||||
callback to handle it (re-instantiate the module, `process.exit()` for a
|
||||
supervisor to restart, etc.); without one it just logs and keeps running.
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
//
|
||||
// Same API surface as the upstream libpinedio-usb.h that the firmware's
|
||||
// Ch341Hal (src/platform/portduino/USBHal.h) compiles against, but WITHOUT the
|
||||
// libusb / pthread dependencies — the implementation (libpinedio_webusb.c)
|
||||
// libusb / pthread dependencies - the implementation (libpinedio_webusb.c)
|
||||
// forwards to a JS WebUSB bridge via Emscripten. The struct keeps only the
|
||||
// fields Ch341Hal actually touches (serial_number, product_string, in_error,
|
||||
// options[]); GPIO/CS state now lives on the JS side.
|
||||
|
||||
@@ -33,9 +33,9 @@
|
||||
extern "C" EMSCRIPTEN_KEEPALIVE void wasm_fs_sync()
|
||||
{
|
||||
// Coalesce: IDBFS syncfs is async, and overlapping syncs warn "2 FS.syncfs
|
||||
// operations in flight". Never run two at once — if one is in flight, mark a
|
||||
// operations in flight". Never run two at once - if one is in flight, mark a
|
||||
// pending re-sync and let the running one chain it when it finishes.
|
||||
// NOTE: loose != / == and string ops only — clang-format mangles !== and
|
||||
// NOTE: loose != / == and string ops only - clang-format mangles !== and
|
||||
// /regex/ literals inside EM_ASM JS (splitting them into invalid tokens).
|
||||
EM_ASM({
|
||||
try {
|
||||
@@ -78,10 +78,10 @@ void wasm_fs_mount()
|
||||
portduinoVFS->mountpoint("/meshdata");
|
||||
}
|
||||
|
||||
// Resolve a per-instance MAC address (12 uppercase hex chars, no colons — the
|
||||
// Resolve a per-instance MAC address (12 uppercase hex chars, no colons - the
|
||||
// MAC_from_string format getMacAddr() expects). The lower 4 bytes become the
|
||||
// 32-bit NodeNum (pickNewNodeNum: mac[2..5]), so this is the node's identity on
|
||||
// the mesh — every browser node MUST get a distinct one or they collide on the
|
||||
// the mesh - every browser node MUST get a distinct one or they collide on the
|
||||
// same NodeNum. Priority:
|
||||
// 1. MESH_MAC env (headless determinism / parity tests, e.g. DEAD00C0FFEE),
|
||||
// 2. a value persisted in the /meshdata tree (survives reload/restart),
|
||||
@@ -235,16 +235,16 @@ void wasm_config_apply()
|
||||
// (lora) region path exactly (AdminModule.cpp:904-937): validate -> first-region
|
||||
// keygen + enable tx -> initRegion() -> reloadConfig (resetRadioConfig +
|
||||
// reconfigure observer recomputes the carrier freq + saveToDisk). A region change
|
||||
// is reconfigure-only — no reboot. Returns 0 on success, -1 if validation fails.
|
||||
// is reconfigure-only - no reboot. Returns 0 on success, -1 if validation fails.
|
||||
// `region` is a meshtastic_Config_LoRaConfig_RegionCode enum value.
|
||||
// Re-entrancy guard. The node is single-threaded + Asyncify: while setup()/loop()
|
||||
// is suspended inside a WebUSB transfer, the JS event loop is free, so a stray DOM
|
||||
// or timer callback could re-enter a wasm_* entry point — starting a second
|
||||
// or timer callback could re-enter a wasm_* entry point - starting a second
|
||||
// Asyncify unwind ("async operation already in flight" abort) or clobbering shared
|
||||
// PhoneAPI state. The host MUST call these only BETWEEN wasm_loop_once() ticks; the
|
||||
// flag (set around setup()/loop() in portduino_main_wasm.cpp) lets the entry points
|
||||
// reject a mid-tick call rather than corrupt/abort. The JS-side queue is the real
|
||||
// fix — this is just the safety net the design otherwise lacked.
|
||||
// fix - this is just the safety net the design otherwise lacked.
|
||||
extern "C" volatile bool g_wasm_in_firmware = false;
|
||||
|
||||
extern "C" EMSCRIPTEN_KEEPALIVE int wasm_set_region(int region)
|
||||
@@ -285,11 +285,11 @@ extern "C" EMSCRIPTEN_KEEPALIVE int wasm_set_region(int region)
|
||||
// those servers are excluded here, so we expose PhoneAPI directly to JS instead.
|
||||
// A JS-side transport (browser in-process, or a headless HTTP/TCP bridge) feeds
|
||||
// ToRadio protobufs in via wasm_api_to_radio() and drains FromRadio protobufs
|
||||
// out via wasm_api_from_radio() — exactly the unframed contract the device HTTP
|
||||
// out via wasm_api_from_radio() - exactly the unframed contract the device HTTP
|
||||
// API uses. PhoneAPI is abstract on checkIsConnected(); in-process we're always
|
||||
// connected. We poll available() rather than push from onNowHasData(), to keep
|
||||
// all API calls OUT of the loop's Asyncify suspend (the JS pump drains only
|
||||
// between wasm_loop_once() ticks — never re-entering wasm mid-SPI).
|
||||
// between wasm_loop_once() ticks - never re-entering wasm mid-SPI).
|
||||
class WasmPhoneAPI : public PhoneAPI
|
||||
{
|
||||
public:
|
||||
@@ -300,7 +300,7 @@ static WasmPhoneAPI *g_wasmPhone = nullptr;
|
||||
|
||||
// Lazily construct after MeshService exists (PhoneAPI's ctor observes
|
||||
// service->fromNumChanged). The first API call happens post-setup(), so service
|
||||
// is live by then — no wasm_setup() ordering change needed.
|
||||
// is live by then - no wasm_setup() ordering change needed.
|
||||
static WasmPhoneAPI *phone()
|
||||
{
|
||||
if (!g_wasmPhone && service)
|
||||
@@ -310,7 +310,7 @@ static WasmPhoneAPI *phone()
|
||||
|
||||
// JS -> device: hand one serialized ToRadio protobuf (UNFRAMED) to the node.
|
||||
// The SDK's want_config_id, text messages, admin, etc. all arrive here. Returns
|
||||
// 1 if accepted, 0 if rejected (e.g. per-portnum throttle — not a transport error).
|
||||
// 1 if accepted, 0 if rejected (e.g. per-portnum throttle - not a transport error).
|
||||
extern "C" EMSCRIPTEN_KEEPALIVE int wasm_api_to_radio(const uint8_t *buf, size_t len)
|
||||
{
|
||||
if (g_wasm_in_firmware)
|
||||
@@ -350,7 +350,7 @@ extern "C" EMSCRIPTEN_KEEPALIVE int wasm_api_is_connected()
|
||||
// delay()/yield() normally come from framework linux/LinuxCommon.cpp (excluded:
|
||||
// it's POSIX/threads). We provide them via Asyncify's emscripten_sleep so
|
||||
// init-time blocking yields to the browser event loop instead of freezing the
|
||||
// tab — exactly the behavior we want at runtime. C++ linkage to match Arduino.h.
|
||||
// tab - exactly the behavior we want at runtime. C++ linkage to match Arduino.h.
|
||||
void delay(unsigned long ms)
|
||||
{
|
||||
emscripten_sleep(ms);
|
||||
@@ -378,9 +378,9 @@ void randomSeed(unsigned long seed)
|
||||
// Restart the node. There is no in-process restart in wasm, so we hand off to
|
||||
// the JS host: a browser reloads the tab (NodeDB state survives via IDBFS, so it
|
||||
// comes back as the same node); a headless host calls Module.onReboot() if it
|
||||
// provided one (re-instantiate or exit as it sees fit), otherwise we just log —
|
||||
// provided one (re-instantiate or exit as it sees fit), otherwise we just log -
|
||||
// the caller (Power::reboot) already let modules persist via the reboot
|
||||
// observers. NOTE: loose !=/== and double-quoted strings only — clang-format
|
||||
// observers. NOTE: loose !=/== and double-quoted strings only - clang-format
|
||||
// mangles !== and /regex/ literals inside EM_ASM JS into invalid runtime tokens.
|
||||
void reboot()
|
||||
{
|
||||
@@ -403,7 +403,7 @@ void reboot()
|
||||
});
|
||||
}
|
||||
|
||||
// tone()/noTone() — no buzzer in the browser (from linux/LinuxCommon.cpp, excluded).
|
||||
// tone()/noTone() - no buzzer in the browser (from linux/LinuxCommon.cpp, excluded).
|
||||
void tone(unsigned char, unsigned int, unsigned long) {}
|
||||
void noTone(unsigned char) {}
|
||||
void delayMicroseconds(unsigned int us)
|
||||
@@ -421,6 +421,6 @@ bool shouldWakeOnReceivedMessage()
|
||||
return false;
|
||||
}
|
||||
|
||||
// TCP phone/API server is excluded in the wasm build — no-op stubs so main.cpp links.
|
||||
// TCP phone/API server is excluded in the wasm build - no-op stubs so main.cpp links.
|
||||
void initApiServer(int) {}
|
||||
void deInitApiServer() {}
|
||||
@@ -1,6 +1,6 @@
|
||||
// WASM replacement for framework-portduino's cores/portduino/main.cpp.
|
||||
// The original uses argp (CLI parse), ftw/nftw (FS walk) and an internal
|
||||
// while(1){ loop(); usleep(loopDelay); } pump — none of which fit the browser.
|
||||
// while(1){ loop(); usleep(loopDelay); } pump - none of which fit the browser.
|
||||
//
|
||||
// Here: run the firmware setup() once from main(), then RETURN. JavaScript
|
||||
// drives the cooperative scheduler by calling wasm_loop_once() on a timer,
|
||||
@@ -26,8 +26,8 @@ extern void portduinoSetup();
|
||||
extern void wasm_fs_mount(); // points portduinoVFS at /meshdata (portduino_glue_wasm.cpp)
|
||||
|
||||
// Re-entrancy guard (defined in portduino_glue_wasm.cpp). True while the firmware
|
||||
// is executing setup()/loop() — including while it is Asyncify-suspended inside a
|
||||
// WebUSB transfer — so the wasm_* API/region entry points reject a mid-tick
|
||||
// is executing setup()/loop() - including while it is Asyncify-suspended inside a
|
||||
// WebUSB transfer - so the wasm_* API/region entry points reject a mid-tick
|
||||
// re-entry from JS instead of corrupting state or aborting Asyncify. The host is
|
||||
// expected to call them only between ticks; this is the safety net.
|
||||
extern "C" volatile bool g_wasm_in_firmware;
|
||||
@@ -48,7 +48,7 @@ extern "C" EMSCRIPTEN_KEEPALIVE void wasm_setup()
|
||||
//
|
||||
// If you want the exact next-delay, expose it: have a small in-tree change make
|
||||
// loop() stash mainController.runOrDelay() in a global, or just poll fast (e.g.
|
||||
// JS setTimeout(_, 5)) — RX latency is bounded by pollMissedIrqs() each tick.
|
||||
// JS setTimeout(_, 5)) - RX latency is bounded by pollMissedIrqs() each tick.
|
||||
extern "C" EMSCRIPTEN_KEEPALIVE int wasm_loop_once()
|
||||
{
|
||||
g_wasm_in_firmware = true;
|
||||
@@ -62,6 +62,6 @@ extern "C" EMSCRIPTEN_KEEPALIVE int wasm_loop_once()
|
||||
int main()
|
||||
{
|
||||
// INVOKE_RUN=0: the page calls wasm_setup() after wiring up Module.ch341.
|
||||
printf("[meshnode] wasm loaded — set Module.ch341, then call wasm_setup()\n");
|
||||
printf("[meshnode] wasm loaded - set Module.ch341, then call wasm_setup()\n");
|
||||
return 0;
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
// Minimal stub of glibc <argp.h> for the wasm build.
|
||||
//
|
||||
// framework-portduino's Arduino.h includes <argp.h> unconditionally, and
|
||||
// PortduinoGlue.cpp DEFINES argp option tables / a parse_opt callback — but
|
||||
// PortduinoGlue.cpp DEFINES argp option tables / a parse_opt callback - but
|
||||
// nothing in the wasm build CALLS argp_parse (that lived in the framework
|
||||
// main.cpp we replaced with portduino_main_wasm.cpp). So only the types and
|
||||
// macros are needed to compile; no argp functions are linked.
|
||||
|
||||
Reference in New Issue
Block a user