Enable instant wake on socket activity for RP2040 using ARM WFE/SEV
hardware instructions, matching ESP8266's esp_delay/esp_schedule pattern.
Previously, RP2040 used plain delay() in the main loop which could not
be interrupted by incoming socket data. Now LWIP recv/accept callbacks
call socket_wake() which sets a flag and sends a hardware event (__sev)
to wake the main loop from __wfe() sleep immediately.
The implementation uses a one-shot pico-sdk timer alarm for timeout
(same pattern as ESP8266's os_timer_arm) combined with __wfe() for
true hardware sleep between events.
Move signed bin creation from post_build script to upload_using_platformio.
The post_build AddPostAction only runs during build, not during nobuild
upload, so the file was missing when PlatformIO tried to upload.
Now create firmware.bin.signed before calling PlatformIO upload.
- Fix _wait_for_serial_port to use get_serial_ports() instead of
os.access() so it works on Windows COM ports
- Snapshot serial ports before upload and wait for new ports to appear,
preventing false matches on pre-existing serial devices
- Auto-select only newly appeared ports after mass storage upload
- Guard against ZeroDivisionError if UF2 file is empty
Add auto-detection of RP2040 BOOTSEL mass storage volumes (RPI-RP2) on
macOS, Linux, and Windows. Show detected volumes as upload targets with
a progress bar for UF2 file copy. Display helpful BOOTSEL instructions
when no RP2040 device is found.
- Add get_rp2040_mass_storage_volumes() to detect mounted RPI-RP2 volumes
- Add PortType.MASS_STORAGE and upload_using_uf2_copy() with progress bar
- Move ProgressBar to helpers.py for shared use
- Wait for USB-CDC serial port after upload for log output
- Auto-select single serial port for logs after mass storage upload
- Create firmware.bin.signed in post_build to fix nobuild upload target
- Show BOOTSEL tip when only OTA options are available
The pre-sleep scan of all monitored sockets was added to preserve
select() semantics by checking for pending data before sleeping.
However, this is unnecessary with the FreeRTOS task notification
approach:
- xTaskNotifyGive from the lwip callback persists until consumed
by ulTaskNotifyTake, so notifications received while the task
is running (not sleeping) are not lost.
- The only case the scan caught was intentionally undrained sockets
(e.g., API's MAX_MESSAGES_PER_LOOP=5 throttle). Adding up to
16ms (loop_interval) latency before re-checking undrained data
is the desired behavior — waking immediately would defeat the
purpose of the throttle which exists to let other components run.
This removes N volatile cross-core reads (one per monitored socket)
from every loop iteration.
Each platform defines exactly one OTA backend subclass (marked final),
so using concrete types in unique_ptr eliminates virtual dispatch overhead.
- Remove OTABackend base class - no longer needed since all consumers
use concrete types directly
- Move make_ota_backend() declaration to each concrete backend header
with concrete return type
- Add ota_backend_factory.h convenience header for consumers
- Use concrete unique_ptr types in ota_esphome, http_request, and
web_server consumers
Each platform defines exactly one OTA backend subclass (marked final),
so using concrete types in unique_ptr eliminates virtual dispatch overhead.
- Move make_ota_backend() declaration from base header to each concrete
backend header with concrete return type
- Add ota_backend_factory.h convenience header for consumers
- Use concrete unique_ptr types in ota_esphome, http_request, and
web_server consumers
When only one API protocol is configured (plaintext-only or noise-only),
use the concrete frame helper type in unique_ptr instead of the base
class. Since both APIPlaintextFrameHelper and APINoiseFrameHelper are
marked final, the compiler can devirtualize all virtual calls
(read_packet, write_protobuf_packet, loop, etc.), eliminating vtable
dispatch overhead in the hot APIConnection::loop() path.
When both protocols are enabled (encryption key set with plaintext
fallback), the polymorphic base pointer is used as before.