Skip to content

Latest commit

 

History

History
42 lines (40 loc) · 26.3 KB

File metadata and controls

42 lines (40 loc) · 26.3 KB

SpawnWear Milestones

Historical record of significant ship dates. New entries go at the bottom; the README's "Recent Highlights" section calls out the last few.

Date Milestone Notes
2026-04-28 Repo scaffolded, OS architecture documented Initial commit, README + CLAUDE + BLE GATT scaffold mirroring NanoFrameTest1
2026-04-28 nanoFramework runtime flashed to first physical watch Watch MAC 1C:DB:D4:7B:03:0C. Final matched combo: runtime ESP32_S3_BLE 1.16.0.563 + stable 1.x class libraries with System.Net bumped to 1.11.50 (1.11.47 lagged the runtime's System.Net native v100.2.0.12 by one patch). Three runtimes tested (568, 567, 563); all have native v100.2.0.12. The 2.0-preview library line is currently ahead of every released runtime, so unusable.
2026-04-28 First SpawnWear deploy succeeded 11 assemblies, 153 KB total. Watch advertises as SpawnWear over BLE per Program.cs.
2026-04-28 FT3168 touch driver written + integrated SpawnWear/Drivers/Touch/Ft3168Driver.cs. Pure managed C# against System.Device.I2c. Probes device-id at boot + TouchEvent event fires on every touch.
2026-04-28 QSPI display contribution forks pushed LostBeard/nanoFramework.Graphics@feature/qspi-display-driver (managed: DisplayBusType enum + GraphicDriver extension + Co5300 driver project). LostBeard/nf-interpreter@feature/qspi-display-driver (native: Qspi_To_Display.cpp + DisplayInterface.h extension). Build-environment work to compile a custom firmware with the QSPI path enabled is in progress; PRs to upstream once verified end-to-end on the watch.
2026-05-03 Headless CLI deploy + capture loop landed tools/nf-deploy.cs drives DeploymentExecute over the wire protocol on COM9 by loading the VS-bundled debugger DLL via Assembly.LoadFrom + reflection. ~10s build-deploy-capture cycle, no VS GUI, no bootloader-mode dance. Replaces the slow nf-flash-full.bat esptool path for routine app re-deploys. See tools/README.md and Notes/flashing.md.
2026-05-03 FT3168 touch + helper-service BLE green end-to-end via CLI Watch advertises as SW-Skip-Tok (display skipped, touch OK). Ft3168Driver reports device id 0x03, WatchProfileService + DebugConsoleService + WifiConfigService GATT layouts attach cleanly, heartbeat loop runs. AXP2101 PMIC rails are NOT off (FT3168 responding requires VDD) - the earlier "rails off" hypothesis was based on observations from the broken nf-flash-full.bat deploy path.
2026-05-03 CO5300 display lit (solid red) Synced nf-interpreter/src/.../Native/nanoFramework_Graphics.cpp with the Graphics fork's bin/Release/Stubs/ so managed PE checksum (0xA11D435D) and runtime native checksum match. Custom nanoCLR rebuilt + bootloader-mode flashed via nf-flash-runtime-only.bat. Build #10 then deployed via tools/nf-deploy.cs and rendered solid red on the panel: [Display] D6 - Solid red flushed, status=OK. Phase 1 finish line.
2026-05-03 tools/nf-deploy.cs reads .nfproj allow-list Earlier the script blanket-scanned packages/*/lib/*.pe and over-deployed assemblies the user project did not reference. Those PEs still loaded into the runtime heap and starved the BLE host stack -> OutOfMemoryException at GattLocalCharacteristic::.ctor. Now the script parses the .nfproj <Reference Include=...> list and only includes those .pe files plus the project's own .pe. Result: SW-Skip-Tok advertises clean at RSSI -26 with touch + Wifi-only BLE on a 14-PE deploy. Display + BLE simultaneously still fight for the BLE host's internal-RAM heap; that's a follow-up tune (reduce char count, move buffers to PSRAM, or bump CONFIG_BT_NIMBLE_*).
2026-05-03 Phase 1 trifecta: display + touch + BLE all alive in one Main() Build #16 stripped helper services + System.Net + System.Device.Wifi to slip under the ~270 KB deploy ceiling, restored Graphics references, and put all three subsystems back in Program.cs. Result: panel renders solid red, FT3168 touch reports id 0x03, watch advertises SW-?-Tok externally at RSSI -26, heartbeat loop steady. ~10s deploy cycle via tools/nf-deploy.cs. WiFi provisioning + WatchProfile + DebugConsole GATT layouts will return alongside the heap-budget tune (CONFIG_BT_NIMBLE_* + the 270 KB deploy commit-phase fix).
2026-05-03 First-pixel root cause found + fixed: NativeInit was reading uninitialized BSS for the QSPI cmd byte The C# GraphicDriver class was extended with BusType + three Qspi* command/address fields, but the matching FIELD___xxx index defines never landed in nanoFramework_Graphics.h and the C++ NativeInitSpi never copied them into the DisplayInterfaceConfig struct. So qspi_send_register pulled cmd from uninitialized BSS (0xFF) instead of the descriptor's 0x02, and the CO5300 silently rejected every register write. The wire-level diagnostic that exposed it was CLR_Debug::Printf("cmd=0x%02X") - 30 lines of code that should have been added on day one. Fix is on LostBeard/nf-interpreter@feature/qspi-display-driver. Lesson preserved in Notes/co5300-quirks.md and feedback_native_field_index_must_be_read.md.
2026-05-03 Watchface V1: event-driven HH:MM:SS readout Replaced the heartbeat polling loop with an AutoResetEvent.WaitOne(timeout)-driven main loop modeled on waveshare-watch-rs's select3(timer, touch_int, button_int) pattern. New UI/SegmentFont.cs renders 7-segment digits via Bitmap.FillRectangle, no font resource needed. New UI/Watchface.cs does a full repaint on first frame and partial flush of just the digits region thereafter (~25 KB pushed per second versus 411 KB for full-frame). New Services/EventLoop.cs is the host loop with state-dependent tick budget (1 s idle, 16 ms while finger held).
2026-05-03 CO5300 alignment quirk hit + worked around The watchface V1 partial flush at (67, 219, 276, 64) left small slivers of the previous digit on each second-tick. Per Notes/co5300-quirks.md and the Hackaday comment thread by the rust port author, the CO5300 silently snaps any CASET/PASET address window that isn't x_start-even / x_end-odd. Surgical fix in Watchface.cs::Tick - round all four bounds before passing to Bitmap.Flush(x, y, w, h). Long-term fix is to bake the alignment into Qspi_To_Display.cpp::SetWindowX16bitsY16Bit so every managed caller gets it for free.
2026-05-03 Sleep / Wake / SetBrightness API added to nanoFramework.Graphics Three new public static extern methods on DisplayControl. Native checksum 0xA11D435D → 0x3C4F4B75. Native impls call g_DisplayDriver.PowerSave(SLEEP / NORMAL) (which run the descriptor's PowerModeSleep / PowerModeNormal arrays - on CO5300 those are MIPI DCS DISPOFF + SLPIN with the proper settling delays) and g_DisplayInterface.SendCommand(2, Brightness, level) for the byte-resolution brightness register. Both branches landed on LostBeard/nanoFramework.Graphics@feature/qspi-display-driver and LostBeard/nf-interpreter@feature/qspi-display-driver.
2026-05-03 Idle state machine + battery indicator + multi-screen navigation Active → Dim (15 s) → Sleep (30 s) state machine driven by time-since-last-touch; touch wakes the panel and triggers a full repaint. Battery indicator bar under the clock reads the AXP2101 fuel gauge, fill color shifts green / yellow / red with charge level. New IScreen interface + ScreenNavigator rotates between Watchface and a Stats screen (battery %, mV, uptime stacked) on a single-finger tap. BOOT button on GPIO0 force-sleeps the panel as a hardware shortcut; touch wakes it. Closes Phase 1 of the roadmap.
2026-05-03 CO5300 alignment quirk baked into firmware Long-term fix for the address-window stale-pixel bug. New AlignFlushBoundsForQspiPanels helper in nanoFramework_Graphics_nanoFramework_UI_Bitmap.cpp rounds the partial-flush rectangle (and source-bitmap origin) at the Bitmap.Flush(x, y, w, h) native entry point, so EVERY managed caller of partial flush gets correct pixels for free. Side effect: 1-px-wide elements at odd start coords render as 2-px-wide on all panels - acceptable for the framework's drawing primitives. Lands on LostBeard/nf-interpreter@feature/qspi-display-driver.
2026-05-03 PCF85063 RTC driver + watch face wall-clock time New Drivers/Rtc/Pcf85063Driver.cs reads / writes the I²C RTC at address 0x51. Watchface now renders RTC HH:MM:SS instead of uptime; falls back to uptime when the chip's oscillator-stop flag is set or the driver is missing. On first power-on the bring-up code seeds 2026-05-03 12:00:00 as a starting time so the screen has something to display. Phase 3 entry.
2026-05-03 -spawnwear.2 local NuGet packages Three local-feed packages (nanoFramework.Graphics, .Graphics.Core, .Graphics.Co5300) bumped to 2.0.0-spawnwear.2 to seal today's library work behind a clean version label. New tools/nf-graphics-repack.cs script rebuilds the three nupkgs from the LostBeard fork build outputs into D:\users\SpawnDevPackages\ in one shot. Anyone cloning SpawnWear gets matching API + native checksum from the local feed without manual file copies.
2026-05-04 WiFi connected + live HTTP screenshot pipeline Drivers/Wifi/WifiService.cs connects to the configured network via WifiNetworkHelper.ConnectDhcp(wifiAdapterId: 0). Services/HttpServer.cs exposes port 8080 with a /screenshot.bin route (RGB565 BE pixels prefixed with w=W h=H\n ASCII header) plus a JS-driven canvas viewer at /. Replaces the BOOT-button-base64-over-Debug.WriteLine screenshot path. Discovered 802.11 ax mixed-mode router incompatibility - SDN2 had to be switched to b/g/n + 20 MHz before the chip would auth (full writeup in Research/esp32s3-wifi-router-compatibility.md).
2026-05-04 FT3168 burst-read layout fix Touch coordinates were arriving as ~2305-3584 on a 410-wide panel because the burst-read decoder assumed a reserved gap byte after FingerNum at offset 0 - matching FT5xxx vendor samples. There is no gap on this silicon: X is at offsets [1,2], Y at [3,4]. Single-byte fix in Ft3168Driver.cs; full writeup in Research/ft3168-burst-read-layout.md.
2026-05-04 Android-quality launcher SHIPPED 3x3 grid of tiles with vertical-gradient backgrounds (16-band horizontal slices, 100% to 25% darkness), stepped quarter-circle corner masks for visible rounding, notification badges (red bubble with count, top-right of tile). Status bar adds a 4-bar WiFi staircase icon + collapsing-slot layout (BLE renders only when advertising; otherwise the slot collapses). Page dots replaced with Android-style pill (wide rounded rectangle for active screen, dim gray dots for others). New Program.cs first-paint fix calls _nav.Current.OnResume() before the event loop so the boot screen actually paints its tile body (status bar was rendering via Tick() but tiles only paint on OnResume). README hero shot at screenshots/launcher-2026-05-04.png captured live over WiFi via the new /screenshot.bin endpoint + tools/bin-to-png.cs converter.
2026-05-04 Watchface date label Below the HH:MM:SS digits, watchface now renders "MON MAY 04" in SmallFont 3x scale (~21x42 px) at soft white (180,180,180) so it doesn't compete with the bright 7-segment time. Date strip collapses cleanly when the RTC reports an invalid OS-flag time, so the watchface never displays 1970-01-01. Battery bar shifts down to make room.
2026-05-04 nf-interpreter deploy ceiling discovered nf-deploy silently corrupts the on-flash assembly table when total wire-protocol deploy >= ~290 KB. Reports 100% / Done; nf-attach then shows garbled assembly names like __StaticArrayInitTypeSize=10 instead of SpawnWear. Watch keeps responding to TCP but app code never runs. Likely root cause: missing mmap cache invalidation in Esp32FlashDriver_Write; full investigation in Research/nf-interpreter-deploy-ceiling.md. Mitigation: pre-flight ceiling guard added to tools/nf-deploy.cs (commit 958dd47) + standalone tools/check-deploy-size.cs. Fixed a related bug in nf-deploy.cs where the Reference allow-list regex was matching <Reference> tags inside XML comments, which had been silently shipping the BLE assembly even when "stripped" - that bug had hidden the real ceiling for hours.
2026-05-04 Docs/ Plans/ Research/ folders + nf-interpreter source-grounded design Three new top-level documentation folders matching the SpawnDev convention. Brought agent-memory feedback notes that were getting out of sync into the repo as canonical documentation. New design ground for Phase 8 SD-card-loadable apps: read the actual nanoFramework.Tools.DebugLibrary.Net + nf-interpreter source for Assembly.Load(byte[]) (corlib_native_System_Reflection_Assembly.cpp:277), AppDomain compile-out (NF_FEATURE_USE_APPDOMAINS:BOOL=OFF for ESP32), c_Flags_NeedReboot semantics (returns CLR_E_BUSY, not a forced reboot), and name-collision behavior (the byte[] load path does NOT dedupe via FindAssembly - both copies link). New tools/check-pe-header.cs parser inspects .pe headers pre-load, with verified field offsets (flags is at offset 16, not 12).
2026-05-05 Phase 3 service host scaffold + About app New SpawnWear/AppContracts/ folder with IServiceHost + IPowerService + IRtcService + IWifiService + ILogger interfaces (matches the SpawnWear.AppContracts v1 spec in Plans/app-contracts-v1.md). New Services/ServiceHost.cs concrete impl wraps the existing AXP2101 + PCF85063 + WifiService + Debug.WriteLine into a single contract surface. New UI/AboutScreen.cs consumes services through IServiceHost and renders 8 rows of live diagnostic info (build, wifi IP, SSID, battery %/mV, USB state, RTC date+time, free heap KB, uptime HH:MM:SS) - the canonical recovery surface that lives in core firmware so the user always has visibility into watch state even if the SD card is removed. Wired as screen index 4 in the navigator, ABOUT tile route on the launcher updated to TargetScreenIndex = 4 with a purple background, page-dot count bumped to 5. Total .pe sum 234 KB → 239 KB (well under the deploy ceiling).
2026-05-05 SpawnWear.Bridge RCL + SpawnWear.Companion PWA shipped Two new .NET 10 projects in this repo: SpawnWear.Bridge/ (Razor Class Library, browser-targeted) holds all watch-interaction code - ITransport abstraction, BleTransport (Web Bluetooth via SpawnDev.BlazorJS), BridgeClient with strongly-typed events for every BLE channel the watch emits (battery / IMU / RTC / button / wifi-status / wifi-scan / debug-log), WebRtcTransport stub for Phase 7 (SpawnDev.RTC). SpawnWear.Companion/ (Blazor WASM PWA) ships with six pages: Home (live battery / IMU / button / log cards), Stats (telemetry dashboard with notify-rate gauges), WiFi (setup form + scan results + status pill), Mirror (RGB565 BE → canvas via SpawnDev.BlazorJS typed Canvas, visibility-aware auto-refresh), Apps (drop .pe to /loadapp), Console (live debug log + command bar). Persistent StatusBar across all pages. Dark theme app-wide. Firmware-side CORS headers + OPTIONS preflight added to HttpServer so the PWA can fetch from a different origin. New SpawnWear.Bridge.Tests project (xUnit, net10.0): 24 wire-format regression tests + a BleUuidsParityTest that reads firmware + bridge BleUuids.cs at test time and asserts byte-for-byte parity. All BridgeClient interop runs through SpawnDev.BlazorJS typed wrappers - no raw JS, no IJSRuntime. Doc updates: SpawnWear.Bridge/README.md, Plans/companion-pwa.md, Docs/architecture.md, Docs/dev-loop.md, Apps.md. 21 commits (17e7870..b1de5f2).
2026-05-05 First dynamically-loaded SpawnWear app rendered on the watch Extended AppContracts with IDisplayBuffer + ISpawnApp (full lifecycle: OnCreate / OnResume / OnPause / OnDestroy / Tick / OnTap / Name). New UI/LoadedAppScreen.cs is an IScreen wrapper that adopts a loaded ISpawnApp instance, forwards lifecycle events, isolates app crashes with try/catch around every callback. New POST /loadapp HTTP endpoint reads body bytes, calls Assembly.Load(byte[]), scans for ISpawnApp implementer via reflection (no Activator on nanoFramework - uses GetConstructor(new Type[0]).Invoke(null)), instantiates + sets on LoadedAppScreen. CounterApp scratch project (CounterApp/CounterApp/) builds a 1.2 KB .pe referencing SpawnWear.exe + nanoFramework.Graphics.Core. POSTed via curl -X POST --data-binary @CounterApp.pe http://192.168.1.171:8080/loadapp returns OK: COUNTER; LoadedAppScreen renders "TAP TO COUNT" + big number + "LONG PRESS = HOME". Trimmed Stats + Settings screens to fit under the recalibrated 242 KB ceiling. New screenshots/counter-app-2026-05-05.png.
2026-05-04 Bridge.Tests 82 → 113: log-buffer + send-path + Phase 7a/7b interlock Three new xUnit test files in SpawnWear.Bridge.Tests driving the real production code paths via real TransportMessage bytes through FakeTransport / HookedFakeTransport. BridgeClientLogBufferTests.cs (8) pins the _recentLogLines ring buffer behind Console.razor's late-mount backfill. BridgeClientSendPathTests.cs (13) pins the wire bytes for every outbound BLE write the Companion makes. PairingWebRtcIntegrationTests.cs (10) is the new bridge: each test runs PairingFlow.PairAsync to produce a real PairingRecord, then runs WebRtcChallenge primitives against that record's stored keys (real Ed25519 via DotNetCrypto on both sides). Closes the gap pairing-only and webrtc-only suites do NOT cover - paired WatchPubKey IS the trust anchor for verifying that watch's WebRTC challenge response, paired OurPrivKey (PKCS8) round-trips back into a sign-capable handle, imposter watches fail, tampered nonce / signature both rejected, multi-watch trust isolation holds, re-pair revokes the old keypair. Mutation-tested: 4 prior-test mutations + 2 integration-test mutations break specific tests as predicted. 113/113 pass, 277 ms. Commits d662bd2, a314349, 70c55dc.
2026-06-20 microSD storage working: SDSPI + exFAT, alongside the display The watch's SDMMC controller is dead under nanoFramework - it accepts a command (start_cmd clears, no HLE) but never clocks it out (CMD8 sets neither command-done nor response-timeout), with every SDMMC / clock / GPIO / pad register byte-identical to a bare ESP-IDF app that mounts the same card. Root cause unlocated after an exhaustive register-level investigation (ruled out, each tested: clock source PLL+XTAL, HLE, clock-tree refcount, I2C0 state, pad-hold/SLP_SEL, CDETECT interlock, RTC-IO mux, PSRAM, TinyUSB, all config/components/constructors, bootloader). Bypassed it by running the SD over SDSPI (SD in SPI mode on the SPI peripheral, which works in nf - it drives the display): managed SDCardSpiParameters{spiBus=2} maps to native SPI3_HOST, CS GPIO17, pins CLK=2 / MOSI=1 / MISO=3. The CO5300 display keeps SPI2_HOST (QSPI), so the two buses never collide - board rule: when a dedicated controller fights, reach for the SPI peripheral. Enabled FF_FS_EXFAT in nf's ESP32-S3 ffconf.h and clamped the SDSPI clock to a conservative 400 kHz. Learned that SD "SPI mode" is optional/finicky: an old 960 MB card returned stable-garbage block reads (commands fine, bulk reads wrong) while a 128 GB exFAT card reads clean - a card-support issue, not the board. Also reverted a 2026-06-19 SD power workaround that had set the AXP2101 to ALDO1-only and blanked the AMOLED panel (it needs ALDO1+2+3). Verified on hardware: D:\ mounts, root browsable (12 dirs + 8 files), display + SD + WiFi + BLE + HTTP all running together. Full record in Research/sd-card-deep-dive-2026-06-19.md. Commits SpawnWear 0dbd260 / 0eb80f4, nf-interpreter (feature/qspi-display-driver) cc4f5658.
2026-06-20 Phase 3 system services complete: QMI8658 IMU + Logger; SD 4 MHz; Settings motion QMI8658 6-axis IMU driver (Drivers/Imu/Qmi8658Driver.cs): probe WHO_AM_I=0x05, accel +/-8 g 500 Hz + gyro +/-1024 dps 448 Hz, burst-read accel/gyro/temp. Register map + sensitivity scales taken from the vendor SensorLib QMI8658 source (each cited inline; the gyro hardware maxes at +/-1024 dps, no 2048 option). Verified on hardware: detected, accel gravity magnitude ~1.02 g (confirms the 4096 LSB/g scale), gyro + 35 degC temp read correctly. LoggerService (Services/LoggerService.cs): ILogger with a 64-line ring buffer (GetRecent backfill) + Debug.WriteLine mirror + optional BLE-notify sink; replaces the throwaway DebugLogger shim, created in Program.Main, wired into ServiceHost and to DebugConsoleService.Notify (notify-only, no double Debug print). SDSPI clock raised 400 kHz -> 4 MHz (Target_System_IO_FileSystem.c): 10x throughput, the 128 GB card lists its full directory tree clean; plus an internal warm-up retry (esp_vfs_fat_sdspi_mount up to 4x with a 40 ms settle, log only the final result) so the flaky first card-init no longer prints "mount failed" at boot. Settings screen gained a live MOTION row (dominant-axis orientation from the IMU, throttled ~1/8 ticks, flicker-free via ListView change-detection). With Power / RTC / WiFi / HTTP / Storage already shipped, Phase 3 (system services) is complete. Commits: SpawnWear 51abdc0 (IMU + Logger) + c9f7022 (Settings motion); nf-interpreter (feature/qspi-display-driver) cc4f5658 (exFAT) + ce4a7fc1 (4 MHz + warm-up retry).
2026-06-21 Phase 4 Settings: functional BLE + WiFi toggles The Settings screen now drives real hardware. BLE row: GattServiceProvider.Start/StopAdvertising (verified off/on). WiFi row: nanoFramework's WifiNetworkHelper cannot live-reconnect after a disconnect on ESP32 - ConnectDhcp is one-shot (2nd call throws InvalidOperationException), the helper's Reconnect() returns false (status unchanged, no exception), and ResetInstance is documented but not exposed in the shipped assembly (CS0117). Fix: bypass the helper and drive the low-level System.Device.Wifi.WifiAdapter directly - FindAllAdapters()[0].Disconnect() for OFF, adapter.Connect(ssid, WifiReconnectionKind.Automatic, password) for ON, then poll NetworkInterface for the DHCP-assigned IPv4. Verified on hardware: off -> on re-acquired 192.168.1.170. Boot connect stays on WifiNetworkHelper.ConnectDhcp (one-shot, fine for boot); only the runtime reconnect uses the adapter. WiFi OFF also stops the HTTP server, ON restarts it (binds IPAddress.Any). SettingsScreen rows: brightness, BLE, WiFi, live IMU motion, sleep, build. Commit SpawnWear d19be57.
2026-06-23 Phase 7 transport: watch ↔ Companion WebRTC, authenticated + multiplexed + proven end to end A full day of WebRTC bring-up taking the ESP32-S3 to a real application transport. DTLS interop with browsers + the SipSorcery fork: the watch is the DTLS client (a=setup:active) so it sends a small ClientHello (mbedTLS's server can't reassemble Chrome's fragmented ClientHello); CONFIG_DTLS_USE_ECDSA=1; mbedtls_ssl_set_hostname(ssl, NULL) for the mbedTLS 3.6 client cert-verify guard; non-blocking single-step handshake. Data channel: RFC 8832 SCTP stream parity (DTLS client=even), deliver BINARY + partial PPIDs to onmessage. Auth: mutual Ed25519 challenge with the real BLE-paired identities. Lock-free native interop (SpawnDev.WebRTC PeerConnection.cpp): GetState/TryReceive/GetLocalSdpLength read volatile/SPSC-ring data without the pump mutex, and Send writes a lock-free TX ring the pump drains - so a blocking native call never freezes nanoFramework's cooperatively-scheduled CLR (the UI stays smooth while streaming). Autonomous transport: retired the HTTP scaffold; WebRtcTransportService connects → stays → reconnects on its own, robust re-announce, graceful (sys.disconnect) + keepalive disconnect, Companion-link status icon. The channel bus (TransportBus): multiplexed named channels, OS sys lanes (battery/imu compact binary) + scoped isolated app.<id>.* lanes (MessagePack via a hand-rolled nanoFramework MsgPackWriter round-tripping to MessagePack-CSharp). Proven live: telemetry and a MessagePack app message flowing simultaneously over one link. Open-source: libpeer forked to LostBeard/libpeer branch spawnwear; mbedTLS reverts to stock (its only edits were dead server-path diagnostics). Removed the vestigial _DeployPad (fixed a layout-sensitive deploy fault). Architecture reference: Docs/transport.md. Commits SpawnWear 0b2948b..1af398b; nf-interpreter (feature/qspi-display-driver) lock-free interop; SpawnDev.RTC d34cca5.
2026-05-05 nf-interpreter deploy ceiling RESOLVED The ~290 KB managed-deploy ceiling (entry 2026-05-04 above) is fixed - the full 2.94 MB managed-deploy partition is usable. The tools/check-deploy-size.cs guard and the "strip-to-fit-under-the-ceiling" workarounds (BLE disabled, helper services trimmed) are obsolete; BLE + display + WiFi + services all deploy together. (Logged here in date order; recorded after the fact.)
2026-06-24 sys.files: SD card read/write over WebRTC, verified on hardware A sys.files lane on the transport bus exposes the SD card (D:\) for chunked read/write over the authenticated WebRTC link. Chunk size 480 bytes (native outbound clamp SW_TX_MSG_MAX=512). Put/get round-trips byte-identical, paginated ls, mkdir/rm of the loose-file app-dir model. Hardware-verified 2026-06-24.
2026-06-24 WebRTC answerer DTLS-role fix shipped to nuget.org Fixed the console/answerer DTLS unexpected_message(10) (two DTLS clients - watch offers setup:active, SipSorcery answerer hardcoded role=active). Fork now mirrors the offer per RFC 4145/5763. Shipped SpawnDev.SIPSorcery 10.0.7 + SpawnDev.RTC 1.1.11; SpawnWear references bumped to RTC 1.1.11. Hardware-verified (console connected to physical watch).
2026-06-25 26s I²C-contention freeze CLOSED + 387 KB clean deploy The whole-device cooperative-CLR freeze (~26s, unlocked shared I²C bus across main / WebRTC-IMU+battery / touch threads) is closed via BoardSetup.I2cLock serializing all driver transactions. Separately, deployed a 387 KB managed set cleanly - confirming the resolved deploy ceiling (entry 2026-05-05) in practice.