emdashes begone (#10847)

This commit is contained in:
Tom
2026-07-01 19:01:27 -05:00
committed by GitHub
parent dee94e0758
commit 3becaf2d95
276 changed files with 1795 additions and 1793 deletions
+1 -1
View File
@@ -349,7 +349,7 @@ void cpuDeepSleep(uint32_t msecToWake)
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
#endif
// User shutdown (DELAY_FOREVER / portMAX_DELAY): no RTC timer — align with nRF52 system_off semantics.
// User shutdown (DELAY_FOREVER / portMAX_DELAY): no RTC timer - align with nRF52 system_off semantics.
if (msecToWake != portMAX_DELAY)
esp_sleep_enable_timer_wakeup(msecToWake * 1000ULL); // call expects usecs
esp_deep_sleep_start();
+1 -1
View File
@@ -413,7 +413,7 @@ bool NRF52Bluetooth::onPairingPasskey(uint16_t conn_handle, uint8_t const passke
"Bluetooth\nPIN\n[M]" + configuredPasskeyText.substr(0, 3) + " " + configuredPasskeyText.substr(3, 6);
// Use the pairing_pin notification type so the lockdown UI short-
// circuit (Screen.cpp updateUiFrame) allows the overlay through
// even on a locked device — see H13 audit fix. The banner content
// even on a locked device - see H13 audit fix. The banner content
// is the per-attempt ephemeral pair PIN, not operator content.
graphics::BannerOverlayOptions opts;
opts.message = ble_message.c_str();
+1 -1
View File
@@ -125,7 +125,7 @@ bool readRegister(uint8_t address, uint8_t reg, uint8_t &value)
twim->TXD.PTR = (uint32_t)®
twim->TXD.MAXCNT = 1;
// LASTTX_SUSPEND shortcut: peripheral suspends automatically after the TX
// byte, so EVENTS_SUSPENDED is set before we even poll — no clearing race.
// byte, so EVENTS_SUSPENDED is set before we even poll - no clearing race.
twim->SHORTS = TWIM_SHORTS_LASTTX_SUSPEND_Msk;
twim->TASKS_RESUME = 1;
twim->TASKS_STARTTX = 1;
+1 -1
View File
@@ -58,7 +58,7 @@ extern "C" void HardFault_Impl(uint32_t stack[])
// RAM contents, zero the DEK / KEK / ephemeralKEK so they aren't sitting
// in BSS for a fault dump to pick up. This is called from the asm naked
// HardFault_Handler entry above, so we're effectively in the chip's
// exception context — keep this strictly to in-RAM scrubbing, no flash
// exception context - keep this strictly to in-RAM scrubbing, no flash
// I/O, no logging.
#ifdef MESHTASTIC_ENCRYPTED_STORAGE
EncryptedStorage::secureWipeKeys();
+15 -15
View File
@@ -1,5 +1,5 @@
/**
* Arduino.h — Zephyr compatibility shim for nRF54L15
* Arduino.h - Zephyr compatibility shim for nRF54L15
*
* Provides the Arduino API surface expected by Meshtastic, backed by
* Zephyr primitives. Only the subset actually used by Meshtastic is
@@ -69,7 +69,7 @@ typedef uint16_t word;
#define bit(b) (1UL << (b))
#define lowByte(w) ((uint8_t)((w)&0xff))
#define highByte(w) ((uint8_t)((w) >> 8))
// word(h,l) — only define if not already defined (conflicts with typedef above)
// word(h,l) - only define if not already defined (conflicts with typedef above)
#undef word
#define word(h, l) ((uint16_t)(((h) << 8) | (l)))
@@ -121,11 +121,11 @@ static inline void interrupts() {}
static inline void noInterrupts() {}
#define digitalPinToInterrupt(p) (p)
// ── portMAX_DELAY — freertosinc.h also defines this; let it win ──────────────
// ── portMAX_DELAY - freertosinc.h also defines this; let it win ──────────────
// We intentionally do NOT define portMAX_DELAY here. freertosinc.h defines
// it for the FreeRTOS / Meshtastic threading layer and must not be overridden.
// ── Timing & system functions — declared with C linkage ──────────────────────
// ── Timing & system functions - declared with C linkage ──────────────────────
// buzz.cpp and others forward-declare delay() as extern "C"; keep linkage
// consistent by wrapping in extern "C" here.
#ifdef __cplusplus
@@ -146,7 +146,7 @@ void yield(void);
#include <cctype>
#include <cstdarg>
// ── C++ STL — include BEFORE defining any min/max helpers ───────────────────
// ── C++ STL - include BEFORE defining any min/max helpers ───────────────────
// Include algorithm first so its min/max templates are in scope.
// We must NOT define min/max as function-like macros: the C++ STL uses
// 3-argument versions (min(a,b,comp)) that the preprocessor would treat as
@@ -188,7 +188,7 @@ static inline long random(long lo, long hi)
return hi > lo ? lo + rand() % (hi - lo) : lo;
}
// ── GPIO — real Zephyr implementation (Phase 3) ──────────────────────────────
// ── GPIO - real Zephyr implementation (Phase 3) ──────────────────────────────
// Implemented in nrf54l15_arduino.cpp using Zephyr GPIO/SPI APIs.
// Pin numbering: P0.n = n, P1.n = 16+n, P2.n = 32+n
void pinMode(uint32_t pin, uint32_t mode);
@@ -206,12 +206,12 @@ static inline void analogWrite(uint32_t, uint32_t) {}
static inline void analogReadResolution(int) {}
static inline void analogWriteResolution(int) {}
// ── __WFI — provided by CMSIS core_cm33.h; do NOT redefine here ─────────────
// ── __WFI - provided by CMSIS core_cm33.h; do NOT redefine here ─────────────
// ── __FlashStringHelper — Arduino PROGMEM string class (no-op on Cortex-M) ──
// ── __FlashStringHelper - Arduino PROGMEM string class (no-op on Cortex-M) ──
class __FlashStringHelper;
// ── attachInterrupt / detachInterrupt — real Zephyr GPIO interrupt impl ──────
// ── attachInterrupt / detachInterrupt - real Zephyr GPIO interrupt impl ──────
typedef void (*voidFuncPtr)(void);
void attachInterrupt(uint32_t pin, voidFuncPtr cb, int mode);
void detachInterrupt(uint32_t pin);
@@ -685,7 +685,7 @@ class HardwareSerial : public Stream
String readStringUntil(char) { return String(); }
};
// Uart — nRF52 BSP alias for HardwareSerial (used by GPS.h when ARCH_NRF52)
// Uart - nRF52 BSP alias for HardwareSerial (used by GPS.h when ARCH_NRF52)
typedef HardwareSerial Uart;
extern HardwareSerial Serial;
@@ -715,7 +715,7 @@ static inline unsigned long pulseIn(uint8_t, uint8_t, unsigned long = 1000000UL)
return 0;
}
// ── strdup / stpcpy — POSIX extensions not in Zephyr newlib ─────────────────
// ── strdup / stpcpy - POSIX extensions not in Zephyr newlib ─────────────────
#ifndef strdup
static inline char *strdup(const char *s)
{
@@ -735,14 +735,14 @@ static inline char *stpcpy(char *dst, const char *src)
}
#endif
// ── strnstr — BSD extension not in Zephyr libc; defined in meshUtils.cpp ─────
// ── strnstr - BSD extension not in Zephyr libc; defined in meshUtils.cpp ─────
// Declare here so callers (GPS.cpp etc.) don't need ARCH_PORTDUINO.
#ifndef STRNSTR
#define STRNSTR
char *strnstr(const char *s, const char *find, size_t slen);
#endif
// ── strlcpy — BSD extension; implementation in nrf54l15_arduino.cpp ──────────
// ── strlcpy - BSD extension; implementation in nrf54l15_arduino.cpp ──────────
#ifndef HAVE_STRLCPY
#define HAVE_STRLCPY
#ifdef __cplusplus
@@ -754,7 +754,7 @@ size_t strlcpy(char *dst, const char *src, size_t size);
#endif
#endif
// ── setenv / getenv / tzset — Zephyr stubs for timezone support ──────────────
// ── setenv / getenv / tzset - Zephyr stubs for timezone support ──────────────
#include <stdlib.h>
static inline int setenv(const char *, const char *, int)
{
@@ -762,7 +762,7 @@ static inline int setenv(const char *, const char *, int)
}
static inline void tzset(void) {}
// ── dbgHeapFree / dbgHeapTotal — nRF52 BSP heap diagnostics ─────────────────
// ── dbgHeapFree / dbgHeapTotal - nRF52 BSP heap diagnostics ─────────────────
// Used by memGet.cpp when ARCH_NRF52 is defined. Return 0 for Phase 2.
static inline uint32_t dbgHeapFree(void)
{
+1 -1
View File
@@ -1,4 +1,4 @@
// IPAddress.h — stub for nRF54L15/Zephyr
// IPAddress.h - stub for nRF54L15/Zephyr
// MQTT.cpp includes this for IPv4 address representation.
// Phase 2: compile-only stub.
#pragma once
+3 -3
View File
@@ -1,4 +1,4 @@
// InternalFileSystem.cpp — Zephyr LittleFS backend for nRF54L15
// InternalFileSystem.cpp - Zephyr LittleFS backend for nRF54L15
//
// Implements Adafruit_LittleFS_Namespace used by FSCommon.h/cpp.
// Storage: 36 KB storage_partition in nRF54L15 internal RRAM (defined in
@@ -124,7 +124,7 @@ File InternalFileSystem::open(const char *path, const char *mode)
// Open as a regular file
fs_mode_t flags;
if (strcmp(mode, FILE_O_WRITE) == 0) {
// Truncate on write — unlink first to ensure a clean start
// Truncate on write - unlink first to ensure a clean start
fs_unlink(abs);
flags = FS_O_WRITE | FS_O_CREATE;
} else {
@@ -198,7 +198,7 @@ bool InternalFileSystem::rmdir_r(const char *path)
struct fs_dir_t dir;
fs_dir_t_init(&dir);
if (fs_opendir(&dir, abs) != 0) {
// Not a directory — try to delete as file
// Not a directory - try to delete as file
return fs_unlink(abs) == 0;
}
+2 -2
View File
@@ -1,9 +1,9 @@
// InternalFileSystem.h — Zephyr LittleFS backend for nRF54L15
// InternalFileSystem.h - Zephyr LittleFS backend for nRF54L15
//
// Implements the Adafruit InternalFileSystem API subset used by Meshtastic,
// backed by Zephyr's fs/littlefs on the storage_partition of the nRF54L15's
// internal RRAM. Partition size is taken from the DTS at compile time via
// FIXED_PARTITION_SIZE(storage_partition) — the DK overlay currently maps
// FIXED_PARTITION_SIZE(storage_partition) - the DK overlay currently maps
// ~700 KB into slot1 (see zephyr/boards/nrf54l15dk_nrf54l15_cpuapp.overlay).
//
// Mount point: /lfs
+1 -1
View File
@@ -1,4 +1,4 @@
// NRF52Bluetooth.h — stub for nRF54L15/Zephyr
// NRF52Bluetooth.h - stub for nRF54L15/Zephyr
// main.h includes this when ARCH_NRF52 is defined.
// Bluetooth is excluded (MESHTASTIC_EXCLUDE_BLUETOOTH=1); this satisfies the
// include chain without pulling in the nRF52 Bluefruit SDK.
+25 -25
View File
@@ -1,4 +1,4 @@
// NRF54L15Bluetooth.cpp — Zephyr BLE GATT peripheral for Meshtastic nRF54L15
// NRF54L15Bluetooth.cpp - Zephyr BLE GATT peripheral for Meshtastic nRF54L15
//
// GATT profile (identical UUIDs to the nRF52 / NimBLE implementations):
// Service: 6ba1b218-15a8-461f-9fa8-5dcae273eafd
@@ -37,7 +37,7 @@
#include <zephyr/sys/reboot.h>
// ── UUID definitions (little-endian per Bluetooth spec)
// ─────────────────────── Syntax: replace hyphens with commas, prefix 0x —
// ─────────────────────── Syntax: replace hyphens with commas, prefix 0x -
// matches BT_UUID_128_ENCODE doc.
#define MESH_SVC_UUID_VAL BT_UUID_128_ENCODE(0x6ba1b218, 0x15a8, 0x461f, 0x9fa8, 0x5dcae273eafd)
@@ -61,7 +61,7 @@ static K_MUTEX_DEFINE(ble_mutex);
// no active connection. Caller MUST bt_conn_unref() when done.
//
// Reading `active_conn` outside this lock races with disconnected_cb which can
// unref + null it on the BT RX thread — touching the freed pointer (even just
// unref + null it on the BT RX thread - touching the freed pointer (even just
// to bt_conn_ref it) is a use-after-free.
static struct bt_conn *acquire_active_conn()
{
@@ -77,7 +77,7 @@ static struct bt_conn *acquire_active_conn()
static bool bt_initialized = false; // bt_enable() called at most once
static bool ble_enabled = false; // set by setup(), cleared by shutdown()
// Forward declarations — BT_GATT_SERVICE_DEFINE(mesh_svc, ...) is below, but
// Forward declarations - BT_GATT_SERVICE_DEFINE(mesh_svc, ...) is below, but
// read_fromradio() (defined earlier) needs to reference the service to notify
// on fromNum after each non-empty read.
#define FROMNUM_ATTR_IDX 2
@@ -111,7 +111,7 @@ static void adv_restart_work_fn(struct k_work *work)
static uint16_t fromnum_ccc_val = 0;
static uint16_t logradio_ccc_val = 0;
// Scratch buffers — only one BLE operation at a time
// Scratch buffers - only one BLE operation at a time
static uint8_t fromRadioBytes[meshtastic_FromRadio_size];
static size_t fromRadioLen = 0;
static uint8_t toRadioBytes[meshtastic_ToRadio_size];
@@ -127,7 +127,7 @@ static uint32_t fromNumValue = 0;
// buffer under a mutex and let BleDeferredThread (running on the Meshtastic
// OSThread scheduler, 24 KB stack) do the actual call outside the lock.
//
// The mutex makes the producer/consumer handoff race-free — producer may
// The mutex makes the producer/consumer handoff race-free - producer may
// overwrite a pending buffer the consumer hasn't read yet (dropped packet),
// but partial reads / torn writes are impossible.
K_MUTEX_DEFINE(pendingToRadioMutex);
@@ -141,7 +141,7 @@ static bool pendingToRadio = false;
// LE Disconnection Complete event to the host: when iOS tears down the link
// (either explicitly by the user or via supervision timeout), the LL layer
// drops the connection but disconnected_cb never fires, active_conn stays
// non-null and advertising never restarts — the device vanishes from scans
// non-null and advertising never restarts - the device vanishes from scans
// until power cycle. Track the connected timestamp and the last time we
// observed ATT traffic; a long ATT idle on an "active" connection means we
// are zombied. A cold reboot is the only path that reliably recovers (any
@@ -228,7 +228,7 @@ static ssize_t write_toradio(struct bt_conn *conn, const struct bt_gatt_attr *at
return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
}
// Deduplicate — drop packet if identical to the last one we processed
// Deduplicate - drop packet if identical to the last one we processed
if (memcmp(lastToRadio, buf, len) != 0) {
memcpy(lastToRadio, buf, len);
if (len < MAX_TO_FROM_RADIO_SIZE) {
@@ -236,7 +236,7 @@ static ssize_t write_toradio(struct bt_conn *conn, const struct bt_gatt_attr *at
}
// Defer handleToRadio() to BleDeferredThread (24 KB stack).
// Running it here on bt_workq (6 KB) overflows during handleStartConfig.
// Always overwrite pending — we already dedup'd above via lastToRadio,
// Always overwrite pending - we already dedup'd above via lastToRadio,
// so any new write here is genuinely new data that must be delivered.
k_mutex_lock(&pendingToRadioMutex, K_FOREVER);
memcpy(pendingToRadioBuf, buf, len);
@@ -274,19 +274,19 @@ static ssize_t write_toradio(struct bt_conn *conn, const struct bt_gatt_attr *at
BT_GATT_SERVICE_DEFINE(mesh_svc, BT_GATT_PRIMARY_SERVICE(&mesh_svc_uuid.uuid),
// fromNum: READ | NOTIFY — packet-counter triggers phone to read fromRadio
// fromNum: READ | NOTIFY - packet-counter triggers phone to read fromRadio
BT_GATT_CHARACTERISTIC(&fromnum_uuid.uuid, BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY, MESH_PERM_READ,
read_fromnum, NULL, &fromNumValue),
BT_GATT_CCC(fromnum_ccc_changed, MESH_PERM_READ | MESH_PERM_WRITE),
// fromRadio: READ — phone polls this after receiving a fromNum notification
// fromRadio: READ - phone polls this after receiving a fromNum notification
BT_GATT_CHARACTERISTIC(&fromradio_uuid.uuid, BT_GATT_CHRC_READ, MESH_PERM_READ, read_fromradio, NULL,
NULL),
// toRadio: WRITE — phone sends protobuf packets to the device
// toRadio: WRITE - phone sends protobuf packets to the device
BT_GATT_CHARACTERISTIC(&toradio_uuid.uuid, BT_GATT_CHRC_WRITE, MESH_PERM_WRITE, NULL, write_toradio, NULL),
// logRadio: READ | NOTIFY | INDICATE — log stream to phone when connected
// logRadio: READ | NOTIFY | INDICATE - log stream to phone when connected
BT_GATT_CHARACTERISTIC(&logradio_uuid.uuid,
BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY | BT_GATT_CHRC_INDICATE, MESH_PERM_READ,
read_logradio, NULL, NULL),
@@ -308,7 +308,7 @@ BT_GATT_SERVICE_DEFINE(mesh_svc, BT_GATT_PRIMARY_SERVICE(&mesh_svc_uuid.uuid),
// the subsequent bt_le_ext_adv_delete() sends LE Remove Advertising Set
// (0x203c) which times out → kernel oops at hci_core.c:506.
//
// With CONFIG_BT_EXT_ADV=n the host uses pure legacy HCI commands — the same
// With CONFIG_BT_EXT_ADV=n the host uses pure legacy HCI commands - the same
// path Nordic NCS uses in all nRF54L15 examples (peripheral_uart,
// peripheral_lbs) and which is universally iOS-compatible. The legacy data
// payload is 31 bytes:
@@ -328,7 +328,7 @@ static void start_advertising()
// Legacy scan-response payload is 31 bytes total. Each AD entry costs 2
// bytes (length + type), leaving 29 bytes for the name. With
// CONFIG_BT_DEVICE_NAME_MAX=32 the name can exceed that — truncate and
// CONFIG_BT_DEVICE_NAME_MAX=32 the name can exceed that - truncate and
// mark as SHORTENED so bt_le_adv_start() doesn't reject the payload.
constexpr size_t LEGACY_SCAN_RSP_NAME_MAX = 31 - 2;
bool name_shortened = full_name_len > LEGACY_SCAN_RSP_NAME_MAX;
@@ -397,7 +397,7 @@ static void connected_cb(struct bt_conn *conn, uint8_t err)
meshtastic::BluetoothStatus newStatus(meshtastic::BluetoothStatus::ConnectionState::CONNECTED);
bluetoothStatus->updateStatus(&newStatus);
// nRF54L15-DK has no screen — cannot display a PIN to the user.
// nRF54L15-DK has no screen - cannot display a PIN to the user.
// Requesting BT_SECURITY_L2 causes the OS to show a pairing dialog that
// the user dismisses, triggering disconnect + advertising restart failure.
// Skip security negotiation; the Meshtastic app works over plain GATT.
@@ -428,7 +428,7 @@ static void disconnected_cb(struct bt_conn *conn, uint8_t reason)
meshtastic::BluetoothStatus newStatus(meshtastic::BluetoothStatus::ConnectionState::DISCONNECTED);
bluetoothStatus->updateStatus(&newStatus);
// Schedule advertising restart via work queue — NOT from this callback
// Schedule advertising restart via work queue - NOT from this callback
// directly. disconnected_cb runs on the BT RX thread; calling
// bt_le_adv_start() here would deadlock (see adv_restart_work comment above).
if (ble_enabled) {
@@ -442,7 +442,7 @@ static void security_changed_cb(struct bt_conn *conn, bt_security_t level, enum
if (err == BT_SECURITY_ERR_PIN_OR_KEY_MISSING) {
// Phone has a stale bond (device was wiped/reflashed). Unpair the stale
// entry so the phone re-pairs cleanly on the next connection attempt.
LOG_WARN("BLE stale bond detected (key missing) — unpairing");
LOG_WARN("BLE stale bond detected (key missing) - unpairing");
bt_unpair(BT_ID_DEFAULT, bt_conn_get_dst(conn));
bt_conn_disconnect(conn, BT_HCI_ERR_AUTH_FAIL);
} else if (err) {
@@ -523,7 +523,7 @@ void BluetoothPhoneAPI::onNowHasData(uint32_t fromRadioNum)
return;
// active_conn may be torn down on another thread while we're dispatching
// this notify — acquire under ble_mutex so disconnected_cb can't free the
// this notify - acquire under ble_mutex so disconnected_cb can't free the
// conn between the null check and bt_conn_ref.
struct bt_conn *conn = acquire_active_conn();
if (!conn)
@@ -606,7 +606,7 @@ class BleDeferredThread : public concurrency::OSThread
uint32_t now = k_uptime_get_32();
uint32_t att_idle = now - last_att_time_ms;
// Liveness probe — only when ATT has been quiet for a while.
// Liveness probe - only when ATT has been quiet for a while.
if (att_idle > IDLE_BEFORE_PROBE_MS && (now - last_probe_ms) >= PROBE_INTERVAL_MS &&
(fromnum_ccc_val & BT_GATT_CCC_NOTIFY)) {
last_probe_ms = now;
@@ -620,7 +620,7 @@ class BleDeferredThread : public concurrency::OSThread
}
bt_conn_unref(conn);
// Hard ceiling — last-resort reboot if probes miss the zombie.
// Hard ceiling - last-resort reboot if probes miss the zombie.
if (att_idle > HARD_WATCHDOG_MS && (now - connect_time_ms) > HARD_WATCHDOG_MS) {
LOG_WARN("BLE zombie (hard watchdog %us); rebooting", HARD_WATCHDOG_MS / 1000);
k_sleep(K_MSEC(50));
@@ -635,7 +635,7 @@ static BleDeferredThread *bleDeferredThread = nullptr;
// ── BT stack pre-initializer (call from main thread before OSThreads start) ──
//
// bt_enable() requires substantially more stack than a Meshtastic OSThread
// (PowerFSMThread) provides — calling it there causes a stack overflow.
// (PowerFSMThread) provides - calling it there causes a stack overflow.
// Call this from nrf54l15Setup() (main Zephyr thread, CONFIG_MAIN_STACK_SIZE)
// so that by the time NRF54L15Bluetooth::setup() runs from PowerFSMThread,
// bt_initialized is already true and bt_enable() is skipped.
@@ -653,7 +653,7 @@ void nrf54l15_bt_preinit()
// Phase 7: load bonding keys from LittleFS (/lfs/bt_settings).
// LittleFS is already mounted by fsInit() before nrf54l15Setup() runs.
// On first boot the file doesn't exist — settings_load() returns 0 (OK).
// On first boot the file doesn't exist - settings_load() returns 0 (OK).
// On subsequent boots, previously bonded peers are restored so the
// phone can reconnect without re-pairing.
err = settings_load();
@@ -697,7 +697,7 @@ static bool nrf54l15_bt_init_common()
bt_conn_auth_cb_register(&auth_cb);
bt_conn_auth_info_cb_register(&auth_info_cb);
// FIXED_PIN — register the configured passkey so the mobile app prompts
// FIXED_PIN - register the configured passkey so the mobile app prompts
// the user for that specific number instead of a random display-only PIN.
// RANDOM_PIN (and clamped NO_PIN) keeps the default behavior: Zephyr
// generates a fresh passkey on each pairing attempt and fires
@@ -797,7 +797,7 @@ void NRF54L15Bluetooth::sendLog(const uint8_t *logMessage, size_t length)
return;
}
// Send as notify regardless of whether client subscribed to NOTIFY or
// INDICATE — bt_gatt_indicate() requires a params struct with a callback;
// INDICATE - bt_gatt_indicate() requires a params struct with a callback;
// notify is simpler and the app accepts both. Change to indicate if
// compatibility issues arise.
bt_gatt_notify(conn, &mesh_svc.attrs[LOGRADIO_ATTR_IDX], logMessage, (uint16_t)length);
+1 -1
View File
@@ -1,4 +1,4 @@
// NRF54L15Bluetooth.h — Zephyr BLE backend for nRF54L15
// NRF54L15Bluetooth.h - Zephyr BLE backend for nRF54L15
//
// Implements the same interface as NRF52Bluetooth (same method names and
// signatures) so main.cpp and AdminModule can use nrf52Bluetooth pointer
+1 -1
View File
@@ -1,4 +1,4 @@
// Nrf52SaadcLock.h — stub for nRF54L15/Zephyr
// Nrf52SaadcLock.h - stub for nRF54L15/Zephyr
// Power.cpp includes this when ARCH_NRF52 is defined.
// Phase 2: compile-only stub.
#pragma once
+1 -1
View File
@@ -1,4 +1,4 @@
// Print.h — shim for nRF54L15/Zephyr
// Print.h - shim for nRF54L15/Zephyr
// Meshtastic includes <Print.h> separately; redirect to our Arduino.h shim.
#pragma once
#include "Arduino.h"
+4 -4
View File
@@ -1,5 +1,5 @@
/**
* SPI.h — Arduino SPI shim for Zephyr/nRF54L15
* SPI.h - Arduino SPI shim for Zephyr/nRF54L15
*
* Provides the Arduino SPIClass interface backed by Zephyr's SPI API. The
* backing controller is SPIM00 (HP domain, 3.0 V); the implementation in
@@ -7,7 +7,7 @@
* bus is configured in zephyr/boards/nrf54l15dk_nrf54l15_cpuapp.overlay.
* RadioLib uses ArduinoHal which calls transfer() byte-by-byte.
*
* CS pin is handled by RadioLib via digitalWrite() — hardware CS is not used.
* CS pin is handled by RadioLib via digitalWrite() - hardware CS is not used.
*/
#pragma once
@@ -25,7 +25,7 @@ struct SPISettings {
uint8_t bitOrder;
uint8_t dataMode;
// Arduino API allows `SPI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0))` — implicit form is intentional.
// Arduino API allows `SPI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0))` - implicit form is intentional.
// cppcheck-suppress noExplicitConstructor
SPISettings(uint32_t clock = 4000000, uint8_t bitOrder = MSBFIRST, uint8_t dataMode = SPI_MODE0)
: clock(clock), bitOrder(bitOrder), dataMode(dataMode)
@@ -46,7 +46,7 @@ class SPIClass
void setClockDivider(uint8_t div) {}
void setFrequency(uint32_t freq) {}
// Real Zephyr SPI implementation — defined in nrf54l15_arduino.cpp
// Real Zephyr SPI implementation - defined in nrf54l15_arduino.cpp
uint8_t transfer(uint8_t data);
uint16_t transfer16(uint16_t data);
void transfer(void *buf, size_t count);
+1 -1
View File
@@ -1,4 +1,4 @@
// Stream.h — shim for nRF54L15/Zephyr
// Stream.h - shim for nRF54L15/Zephyr
// StreamAPI.h and other Meshtastic headers include <Stream.h>.
// Redirect to our Arduino.h shim which defines the Stream base class.
#pragma once
+1 -1
View File
@@ -1,4 +1,4 @@
// Tone.h — shim for nRF54L15/Zephyr
// Tone.h - shim for nRF54L15/Zephyr
// Tone functions are stubbed in Arduino.h; this header satisfies direct includes.
#pragma once
#include "Arduino.h"
+1 -1
View File
@@ -1,4 +1,4 @@
// WProgram.h — shim for nRF54L15/Zephyr
// WProgram.h - shim for nRF54L15/Zephyr
// ArduinoThread (and other legacy Arduino libs) include <WProgram.h>.
// Redirect to our Arduino.h shim.
#pragma once
+4 -4
View File
@@ -1,4 +1,4 @@
// Wire.cpp — Arduino TwoWire backed by Zephyr i2c30 (TWIM30 hardware).
// Wire.cpp - Arduino TwoWire backed by Zephyr i2c30 (TWIM30 hardware).
//
// The pinctrl + clock-frequency are configured in
// zephyr/boards/nrf54l15dk_nrf54l15_cpuapp.overlay. Runtime begin()/setClock()
@@ -15,7 +15,7 @@
// Resolve the i2c30 node at compile time. If the overlay has not enabled
// i2c30, this evaluates to a NULL device pointer and every call short-circuits
// to a NACK return code — matching the prior compile-only stub behavior.
// to a NACK return code - matching the prior compile-only stub behavior.
#define I2C_NODE DT_NODELABEL(i2c30)
static const struct device *getI2CDevice()
@@ -49,7 +49,7 @@ void TwoWire::begin()
void TwoWire::begin(uint8_t /*sda*/, uint8_t /*scl*/)
{
// SDA/SCL fixed by overlay pinctrl — pin args ignored.
// SDA/SCL fixed by overlay pinctrl - pin args ignored.
begin();
}
@@ -98,7 +98,7 @@ void TwoWire::beginTransmission(uint8_t addr)
size_t TwoWire::write(uint8_t data)
{
if (txLen >= WIRE_BUFFER_LENGTH) {
return 0; // overflow — endTransmission() will return 1
return 0; // overflow - endTransmission() will return 1
}
txBuf[txLen++] = data;
return 1;
+5 -5
View File
@@ -1,10 +1,10 @@
/**
* Wire.h — Arduino TwoWire (I2C) shim for Zephyr / nRF54L15.
* Wire.h - Arduino TwoWire (I2C) shim for Zephyr / nRF54L15.
*
* Bus binding: the Zephyr device tree alias `i2c30` (TWIM30 hardware
* peripheral, HP domain, 3.0 V) is resolved at compile time via
* DEVICE_DT_GET in Wire.cpp. SDA/SCL pins are configured in the board
* overlay via pinctrl — `begin(sda, scl)` overloads are accepted for
* overlay via pinctrl - `begin(sda, scl)` overloads are accepted for
* Arduino API compatibility but the pin arguments are ignored.
*
* Buffer sizes are sized for the worst-case I2C consumer we plan to use
@@ -28,7 +28,7 @@ class TwoWire
TwoWire();
// ── Bus lifecycle ─────────────────────────────────────────────────
// begin() variants — pin arguments are accepted for API compatibility
// begin() variants - pin arguments are accepted for API compatibility
// but ignored: SDA/SCL are fixed by the Zephyr overlay pinctrl. freq
// is also fixed by overlay clock-frequency (use setClock() at runtime).
void begin();
@@ -63,7 +63,7 @@ class TwoWire
size_t readBytes(char *buf, size_t len) { return readBytes((uint8_t *)buf, len); }
void flush() {}
// Slave callbacks unsupported — peripheral-only stub.
// Slave callbacks unsupported - peripheral-only stub.
void onReceive(void (*)(int)) {}
void onRequest(void (*)(void)) {}
@@ -80,4 +80,4 @@ class TwoWire
};
extern TwoWire Wire;
extern TwoWire Wire1; // alias to Wire — only one I2C bus on this board
extern TwoWire Wire1; // alias to Wire - only one I2C bus on this board
+6 -6
View File
@@ -9,12 +9,12 @@
// optional subsystem (BLE, screen, I2C, GPS, buttons, telemetry, sensors,
// radio, CPU shutdown, ...) is wrapped in `#if HAS_FOO` so a given board
// only pays for the features it actually ships. On memory-tight MCUs this
// is not cosmetic — it's the difference between a binary that fits in
// is not cosmetic - it's the difference between a binary that fits in
// flash and one that doesn't, and between a build that links and one that
// drags in drivers for hardware the board doesn't have. Defaulting to 0
// here (rather than inheriting nRF52 defaults) is deliberate: the
// nRF54L15-DK is a bare dev kit with no screen, no I2C sensors, no GPS,
// no user buttons — so every HAS_* flag starts off and gets flipped on
// no user buttons - so every HAS_* flag starts off and gets flipped on
// explicitly by variants that add that hardware.
//
// Feature flags are also the cleanest way to absorb platform divergence
@@ -24,8 +24,8 @@
// to bring up the next board (flip the flags, don't patch call sites),
// and keeps the "does this platform support X?" question answerable by
// reading one file instead of grepping the tree. BLE in particular is
// deferred to Phase 2 on this port — the nRF54L15 uses MPSL/Zephyr BLE
// APIs rather than the Adafruit SoftDevice stack used by nRF52840 — so
// deferred to Phase 2 on this port - the nRF54L15 uses MPSL/Zephyr BLE
// APIs rather than the Adafruit SoftDevice stack used by nRF52840 - so
// while HAS_BLUETOOTH defaults to 1, the actual implementation lives in
// NRF54L15Bluetooth.cpp behind its own Zephyr Kconfig gates.
//
@@ -58,7 +58,7 @@
#define HAS_CPU_SHUTDOWN 0
#endif
// ADC reference — nRF54L15 SAADC uses VDD/4 internal ref by default
// ADC reference - nRF54L15 SAADC uses VDD/4 internal ref by default
#ifndef AREF_VOLTAGE
#define AREF_VOLTAGE 3.6
#endif
@@ -67,7 +67,7 @@
#endif
//
// HW_VENDOR — maps build-time define to HardwareModel enum.
// HW_VENDOR - maps build-time define to HardwareModel enum.
// PRIVATE_HW (255): the protobuf HardwareModel enum reserves DK / DIY boards
// without an SKU under this value; the nRF54L15-DK doesn't get a dedicated
// enum number. Variant manifest matches via custom_meshtastic_hw_model = 255.
+1 -1
View File
@@ -1,4 +1,4 @@
// bluefruit.h — stub for nRF54L15/Zephyr
// bluefruit.h - stub for nRF54L15/Zephyr
// NodeDB.cpp includes this when ARCH_NRF52 is defined.
// Bluetooth is excluded (MESHTASTIC_EXCLUDE_BLUETOOTH=1); this satisfies
// the include chain without pulling in the Adafruit Bluefruit SDK.
+5 -5
View File
@@ -1,5 +1,5 @@
/*
* main-nrf54l15.cpp — Platform entry points for Nordic nRF54L15
* main-nrf54l15.cpp - Platform entry points for Nordic nRF54L15
*
* Adapted from src/platform/nrf52/main-nrf52.cpp.
* SoftDevice, Adafruit BLE, and nRFCrypto are NOT available on nRF54L15.
@@ -42,7 +42,7 @@ void variant_nrf54l15LoopHook(void) {}
// ── PowerHAL ─────────────────────────────────────────────────────────────
bool powerHAL_isVBUSConnected()
{
// TODO(nrf54l15): nRF54L15 has a USB POWER peripheral — read USBREGSTATUS
// TODO(nrf54l15): nRF54L15 has a USB POWER peripheral - read USBREGSTATUS
return false;
}
@@ -165,7 +165,7 @@ void cpuDeepSleep(uint32_t msecToWake)
delay(msecToWake);
NVIC_SystemReset();
} else {
// System off equivalent — halt
// System off equivalent - halt
while (1) {
__WFI();
}
@@ -173,7 +173,7 @@ void cpuDeepSleep(uint32_t msecToWake)
}
// ── Setup / Loop ──────────────────────────────────────────────────────────
// Forward declaration — defined in NRF54L15Bluetooth.cpp
// Forward declaration - defined in NRF54L15Bluetooth.cpp
void nrf54l15_bt_preinit();
void nrf54l15Setup()
@@ -198,7 +198,7 @@ void nrf54l15Setup()
void nrf54l15Loop()
{
// First-call gate for the future WDT init — body will hold real init code, not just the bookkeeping flag.
// First-call gate for the future WDT init - body will hold real init code, not just the bookkeeping flag.
// cppcheck-suppress duplicateConditionalAssign
if (!watchdog_running) {
// TODO(nrf54l15): enable WDT here
+16 -16
View File
@@ -1,5 +1,5 @@
/**
* nrf54l15_arduino.cpp — Arduino shim implementations for Zephyr/nRF54L15
* nrf54l15_arduino.cpp - Arduino shim implementations for Zephyr/nRF54L15
*
* Provides concrete implementations for Print, HardwareSerial, GPIO, SPI,
* and String methods declared in Arduino.h / SPI.h.
@@ -22,7 +22,7 @@
#include "bluefruit.h"
BlueFruitClass Bluefruit;
// ── _fini stub — ARM newlib's __libc_fini_array references _fini, but ────────
// ── _fini stub - ARM newlib's __libc_fini_array references _fini, but ────────
// Zephyr startup doesn't provide it. Provide a weak no-op so the linker
// is satisfied when C++ global dtors or atexit() pull in __libc_fini_array.
extern "C" void __attribute__((weak)) _fini(void) {}
@@ -38,7 +38,7 @@ HardwareSerial Serial;
HardwareSerial Serial1;
HardwareSerial Serial2;
// ── Timing functions — C linkage to match extern "C" declarations ────────────
// ── Timing functions - C linkage to match extern "C" declarations ────────────
extern "C" uint32_t millis(void)
{
return (uint32_t)k_uptime_get_32();
@@ -60,7 +60,7 @@ extern "C" void yield(void)
k_yield();
}
// ── NVIC_SystemReset — wraps __NVIC_SystemReset from CMSIS core_cm33.h ───────
// ── NVIC_SystemReset - wraps __NVIC_SystemReset from CMSIS core_cm33.h ───────
// core_cm33.h has #define NVIC_SystemReset __NVIC_SystemReset, so undef it
// before defining our own implementation to prevent macro expansion collision.
#pragma push_macro("NVIC_SystemReset")
@@ -100,7 +100,7 @@ int Print::printf(const char *fmt, ...)
return n;
}
// ── strlcpy — BSD extension not in Zephyr newlib ────────────────────────────
// ── strlcpy - BSD extension not in Zephyr newlib ────────────────────────────
extern "C" size_t strlcpy(char *dst, const char *src, size_t size)
{
size_t len = strlen(src);
@@ -211,7 +211,7 @@ void String::replace(const String &from, const String &to)
{
if (from.isEmpty() || !_buf)
return;
// Simple O(n²) replace — fine for typical Meshtastic string lengths
// Simple O(n²) replace - fine for typical Meshtastic string lengths
String result;
const char *p = _buf;
while (*p) {
@@ -226,7 +226,7 @@ void String::replace(const String &from, const String &to)
}
// ═════════════════════════════════════════════════════════════════════════════
// GPIO — Real Zephyr implementation (Phase 3)
// GPIO - Real Zephyr implementation (Phase 3)
// Pin mapping: P0.n = n (0-15), P1.n = 16+n (16-31), P2.n = 32+n (32-47)
// ═════════════════════════════════════════════════════════════════════════════
@@ -269,7 +269,7 @@ void pinMode(uint32_t pin, uint32_t mode)
gpio_pin_configure(dev, zpin, flags);
}
// Bring-up diagnostics for the SX1262 wiring path. Off by default — enable by
// Bring-up diagnostics for the SX1262 wiring path. Off by default - enable by
// adding `-DNRF54L15_GPIO_DEBUG` to platformio.ini build_flags. Useful when
// validating CS/NRESET toggles after a wiring change, diagnosing a "stuck HIGH"
// BUSY before the first NRESET pulse, or tracing BUSY transitions during early
@@ -303,7 +303,7 @@ void digitalWrite(uint32_t pin, uint32_t value)
gpio_pin_t zpin;
const struct device *dev = _gpio_dev_for_pin(pin, &zpin);
if (!device_is_ready(dev)) {
// Genuine hardware/DTS misconfiguration — keep this regardless of the
// Genuine hardware/DTS misconfiguration - keep this regardless of the
// GPIO_DEBUG gate so it surfaces in production builds too.
printk("[GPIO] pin%u dev NOT READY\n", (unsigned)pin);
return;
@@ -351,7 +351,7 @@ int digitalRead(uint32_t pin)
return v;
}
// ─── attachInterrupt — supports up to NRF54L15_MAX_IRQS pins ────────────────
// ─── attachInterrupt - supports up to NRF54L15_MAX_IRQS pins ────────────────
#define NRF54L15_MAX_IRQS 8
struct _PinIrq {
@@ -432,12 +432,12 @@ void detachInterrupt(uint32_t pin)
}
// ═════════════════════════════════════════════════════════════════════════════
// SPI — Real Zephyr implementation using SPIM00 (HP domain, 3.0V)
// CS is handled by RadioLib via digitalWrite() — hardware CS not used.
// SPI - Real Zephyr implementation using SPIM00 (HP domain, 3.0V)
// CS is handled by RadioLib via digitalWrite() - hardware CS not used.
// Mode 0 (CPOL=0, CPHA=0), MSB first.
// ═════════════════════════════════════════════════════════════════════════════
// Use SPIM00 (HP domain, 3.0V) — SPIM20 is 1.8V LP domain, incompatible with SX1262.
// Use SPIM00 (HP domain, 3.0V) - SPIM20 is 1.8V LP domain, incompatible with SX1262.
// Lazy-init: DEVICE_DT_GET in global scope fails when the extern symbol is
// not visible in this translation unit. Use a function-local static instead.
static const struct device *_spi00(void)
@@ -463,13 +463,13 @@ static const struct device *_spi00(void)
// 1 MHz → prescaler = 128 > 126 → NRFX_ERROR_INVALID_PARAM → -EIO on every
// transfer. Minimum valid frequency is 2 MHz (prescaler = 64).
static const struct spi_config _spi00_cfg = {
.frequency = 2000000U, // 2 MHz — minimum valid for SPIM00 at 128 MHz base
.frequency = 2000000U, // 2 MHz - minimum valid for SPIM00 at 128 MHz base
.operation = SPI_OP_MODE_MASTER | SPI_WORD_SET(8) | SPI_TRANSFER_MSB,
.slave = 0,
.cs = {}, // CS = NULL → RadioLib handles CS via GPIO
};
// Static DMA buffers — stack-allocated bufs on nRF54L15 may not be reachable
// Static DMA buffers - stack-allocated bufs on nRF54L15 may not be reachable
// by SPIM20 EasyDMA. Static placement in .bss/.data is always in Global SRAM.
// rx_byte is pre-filled with 0xAA before every transfer so we can distinguish:
// 0xAA → DMA never wrote (EasyDMA can't reach the buffer)
@@ -510,7 +510,7 @@ uint8_t SPIClass::transfer(uint8_t data)
return _spi_rx_byte;
}
// Static DMA-safe buffers for transfer16 — same EasyDMA reachability concern
// Static DMA-safe buffers for transfer16 - same EasyDMA reachability concern
// applies as for the byte path: stack buffers from a caller thread may sit in
// per-thread RAM regions that EasyDMA cannot reach.
static uint8_t _spi_tx16[2] __attribute__((aligned(4)));
+4 -4
View File
@@ -1,5 +1,5 @@
/*
* nrf54l15_main.cpp — Zephyr entry point for Meshtastic nRF54L15 port
* nrf54l15_main.cpp - Zephyr entry point for Meshtastic nRF54L15 port
*
* Zephyr calls main() instead of Arduino's setup()/loop().
* This file provides the main() that bootstraps the Arduino-style
@@ -34,7 +34,7 @@ static struct crash_info saved_crash __attribute__((section(".noinit")));
// Override Zephyr's weak fatal handler to save crash info, then cold-reboot so
// main() can report the saved record on the next boot. We don't rely on
// CONFIG_RESET_ON_FATAL_ERROR (default off → k_fatal_halt would spin forever)
// — we issue sys_reboot() ourselves after flushing logs.
// - we issue sys_reboot() ourselves after flushing logs.
extern "C" void k_sys_fatal_error_handler(unsigned int reason, const struct arch_esf *esf)
{
saved_crash.magic = CRASH_MAGIC;
@@ -55,7 +55,7 @@ extern "C" void k_sys_fatal_error_handler(unsigned int reason, const struct arch
saved_crash.cfsr);
// Walk the failing thread's stack and print any word that looks like a
// Thumb code address (0x1000 — flash end, with the Thumb-mode low bit set).
// Thumb code address (0x1000 - flash end, with the Thumb-mode low bit set).
// The Cortex-M exception frame at PSP holds r0,r1,r2,r3,r12,lr,pc,xpsr
// (8 words); deeper words are the caller's saved frame, which gives a
// crude but useful poor-man's backtrace when CONFIG_DEBUG_COREDUMP is off.
@@ -64,7 +64,7 @@ extern "C" void k_sys_fatal_error_handler(unsigned int reason, const struct arch
// abort itself. Cheap (~150 B of code) and silent until a fault.
uint32_t psp;
__asm__ volatile("mrs %0, psp" : "=r"(psp));
printk("[nrf54l15] PSP=0x%08x — stack walk:\n", psp);
printk("[nrf54l15] PSP=0x%08x - stack walk:\n", psp);
// Validate PSP before dereferencing. Real faults frequently leave PSP
// pointing at corrupted/unmapped memory, and walking it blindly triggers a
// second fault inside this handler. Restrict to nRF54L15 SRAM (256 KB at
+1 -1
View File
@@ -1,4 +1,4 @@
// utility/bonding.h — stub for nRF54L15/Zephyr
// utility/bonding.h - stub for nRF54L15/Zephyr
// NodeDB.cpp includes this when ARCH_NRF52 is defined.
// Bluetooth is excluded; this stub satisfies the include chain.
#pragma once
+3 -3
View File
@@ -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;
+6 -6
View File
@@ -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 -1
View File
@@ -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.
+2 -2
View File
@@ -33,7 +33,7 @@
#define STM32WL_FLASH_BASE (FLASH_BASE)
/*
* LFS tunables — all of these are stored in the LFS superblock.
* LFS tunables - all of these are stored in the LFS superblock.
* Changing ANY of them is incompatible with the existing on-disk format;
* the filesystem will be detected as corrupted and reformatted on first boot.
*
@@ -43,7 +43,7 @@
* FLASH_END_ADDR is computed from FLASH_SIZE (read from the chip at link time).
*/
#define LFS_FLASH_TOTAL_SIZE \
(7 * STM32WL_PAGE_SIZE) /* 14 KiB — last 7 physical pages (FORMAT BREAK: reduced from 10 pages / 20 KiB) */
(7 * STM32WL_PAGE_SIZE) /* 14 KiB - last 7 physical pages (FORMAT BREAK: reduced from 10 pages / 20 KiB) */
#define LFS_BLOCK_SIZE (256) /* virtual block size (FORMAT BREAK if changed) */
#define LFS_FLASH_ADDR_END (FLASH_END_ADDR)
+1 -1
View File
@@ -393,7 +393,7 @@ void File::rewindDirectory(void)
_fs->_unlockFS();
}
// Default constructor — binds to the global InternalFS instance.
// Default constructor - binds to the global InternalFS instance.
// Allows File to be declared without an explicit filesystem argument,
// matching the API of ESP32/RP2040/Portduino File objects.
File::File() : File(InternalFS) {}
+1 -1
View File
@@ -23,7 +23,7 @@
#define BOOTLOADER_MAGIC 0xD00DB007UL
#define SYS_MEM_BASE 0x1FFF0000UL
// Placed in .noinit — not zeroed at startup, survives NVIC_SystemReset().
// Placed in .noinit - not zeroed at startup, survives NVIC_SystemReset().
__attribute__((section(".noinit"), used)) volatile uint32_t g_bootloaderMagic;
// Fires before main() / HAL_Init(). Must use only core Cortex-M registers.