You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
perf(deploy): replace esptool subprocess with espflash native crate #66
verify-flash and write-flash currently shell out to Python esptool. Cost breakdown for verifying a 2.4 MB ESP32-S3 image (~5.9 s total):
Phase
Estimated cost
Python interpreter startup
~1 s
Subprocess spawn + esptool init
~0.5 s
Connect + reset chip into bootloader
~1 s
SYNC handshake + stub flasher upload
~1 s
Baud rate change
~0.5 s
FLASH_MD5SUM execution (3 regions, 2.4 MB)
~1 s
Cleanup + reset back to app
~0.5 s
Actual MD5 work is ~1 s; the rest is process/Python overhead.
Proposed fix
Use the espflash crate — Rust-native ESP32 flasher protocol maintained by ESP-RS. Exposes SLIP protocol, stub flasher loading, and FLASH_MD5SUM natively.
espflash = { version = "4", default-features = false, features = ["serialport"] }
Reuse the daemon's existing serial port lease instead of spawning a subprocess.
Estimated win
First verify in a session: ~5.9 s → ~1.5–2 s
Subsequent verifies reusing the loaded stub flasher: <1 s
Effort
Medium. The espflash library API is less stable than the CLI — may need an adapter layer between fbuild's Esp32Deployer and espflash's Flasher type. Worth a spike to confirm feasibility before committing.
Context
Filed during the issue consolidation that closed tracker #18. See #18 for the original framing.
PR perf(deploy): use espflash crate for verify-flash (partial #66) #89 — verify-flash via espflash::Flasher behind FBUILD_USE_ESPFLASH_VERIFY=1 (default still esptool subprocess for safety). Native verify reuses the serial port through SharedSerialManager::preempt_for_deploy, computes MD5 against flasher.checksum_md5(offset, len), hard-resets on Match. Saves the ~1s Python interpreter startup + ~0.5s subprocess spawn per verify.
Remaining:write-flash migration. Deferred from PR #89 for two reasons: (1) needs a progress-callback bridge to the daemon's WebSocket log stream so users see upload progress, (2) stricter error recovery for partial writes / USB re-enumeration. Picking that up next.
Shipped the /ws/logs bridge in #111 (merged as e7aad0e). BroadcastLogLayer forwards tracing events to the existing BroadcastHub::log_tx channel, so the 10%-throttled native write-flash progress lines emitted by esp32_native::LoggingProgressBridge now stream live on /ws/logs. Zero-subscriber overhead is one atomic load; self-echo cycles are guarded by a module-path filter on handlers::websockets. Closes the "progress-callback bridge to the daemon WebSocket log stream" follow-up from the comment above — leaving only the stricter partial-write / USB re-enumeration error-recovery item on this issue.
Current main has crates/fbuild-deploy/src/esp32_native.rs implementing both native verify and write. The write path short-circuits on region failure, reports the failed region/offset/byte progress, returns a failed DeploymentResult instead of silently swallowing partial writes, and the daemon clears the trusted firmware hash on failed/partial deploys. The daemon also still runs the post-deploy USB re-enumeration poll for every non-VerifySkip result.
Important operational note: the native path is still deliberately opt-in. The daemon must be built with espflash-native, then enabled at runtime with FBUILD_USE_ESPFLASH_VERIFY=1 and/or FBUILD_USE_ESPFLASH_WRITE=1. The default remains the esptool subprocess path until hardware validation/default-flip policy is handled separately.
Closing #66 because the original esptool-to-espflash migration is implemented. Any remaining "make this the default after hardware benching across ESP32 families" work should be tracked as a separate validation/default-flip issue rather than keeping this implementation issue open.
Activity
spike(deploy): add espflash crate + feasibility module (partial #66)
spike(deploy): add espflash crate + feasibility module (partial #66) (#…
perf(deploy): use espflash crate for verify-flash (partial #66)
perf(deploy): use espflash crate for verify-flash (partial #66)
zackees commentedon Apr 18, 2026
Progress update — verify-flash migrated:
espflash = 4withdefault-features = false, features = ["serialport"]and a compile-onlyesp32_nativemodule documenting what a real migration needs.verify-flashviaespflash::FlasherbehindFBUILD_USE_ESPFLASH_VERIFY=1(default still esptool subprocess for safety). Native verify reuses the serial port throughSharedSerialManager::preempt_for_deploy, computes MD5 againstflasher.checksum_md5(offset, len), hard-resets onMatch. Saves the ~1s Python interpreter startup + ~0.5s subprocess spawn per verify.Remaining:
write-flashmigration. Deferred from PR #89 for two reasons: (1) needs a progress-callback bridge to the daemon's WebSocket log stream so users see upload progress, (2) stricter error recovery for partial writes / USB re-enumeration. Picking that up next.perf(deploy): use espflash crate for write-flash (partial #66)
perf(deploy): use espflash crate for write-flash (partial #66) (#106)
perf(daemon): bridge tracing events to /ws/logs (partial #66)
perf(daemon): bridge tracing events to /ws/logs (partial #66) (#111)
zackees commentedon Apr 18, 2026
Shipped the
/ws/logsbridge in #111 (merged ase7aad0e).BroadcastLogLayerforwardstracingevents to the existingBroadcastHub::log_txchannel, so the 10%-throttled nativewrite-flashprogress lines emitted byesp32_native::LoggingProgressBridgenow stream live on/ws/logs. Zero-subscriber overhead is one atomic load; self-echo cycles are guarded by a module-path filter onhandlers::websockets. Closes the "progress-callback bridge to the daemon WebSocket log stream" follow-up from the comment above — leaving only the stricter partial-write / USB re-enumeration error-recovery item on this issue.perf(daemon): session-trusted verify-skip for ESP32 warm redeploys
perf(daemon): session-trusted verify-skip for ESP32 warm redeploys (#116
perf(deploy): make espflash a build-time-optional cargo feature (#66)
perf(deploy): make espflash a build-time-optional cargo feature (#66)
zackees commentedon Apr 22, 2026
Current status (2026-04-22): closing this as code-complete.
What landed:
espflash = 4dependency behind the native feature.verify-flashpath viaespflash::Flasher, opt-in withFBUILD_USE_ESPFLASH_VERIFY=1.write-flashpath, opt-in withFBUILD_USE_ESPFLASH_WRITE=1, covering both full writes and selective region writes after verify mismatch./ws/logsbridge for the native write progress emitted throughtracing.Current
mainhascrates/fbuild-deploy/src/esp32_native.rsimplementing both native verify and write. The write path short-circuits on region failure, reports the failed region/offset/byte progress, returns a failedDeploymentResultinstead of silently swallowing partial writes, and the daemon clears the trusted firmware hash on failed/partial deploys. The daemon also still runs the post-deploy USB re-enumeration poll for every non-VerifySkipresult.Important operational note: the native path is still deliberately opt-in. The daemon must be built with
espflash-native, then enabled at runtime withFBUILD_USE_ESPFLASH_VERIFY=1and/orFBUILD_USE_ESPFLASH_WRITE=1. The default remains the esptool subprocess path until hardware validation/default-flip policy is handled separately.Local verification on
main(61b18fe8):Closing #66 because the original esptool-to-espflash migration is implemented. Any remaining "make this the default after hardware benching across ESP32 families" work should be tracked as a separate validation/default-flip issue rather than keeping this implementation issue open.