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.
Move is_connected() to the header as an inline method that returns a
cached bool field. The previous implementation called
wifi_sta_connect_status_() on every invocation, which makes SDK calls
on ESP8266 (wifi_station_get_connect_status) and RP2040
(cyw43_wifi_link_status + WiFi.status), preventing inlining and adding
overhead for the many callers that check it every loop iteration
(network::is_connected, API server, MQTT, status sensor, etc.).
The cached state is updated once per loop() after wifi_loop_() processes
platform events. Internal call sites that need a live SDK query
(STA_CONNECTED loss detection, RP2040 can_proceed) use the new
is_connected_() private method directly.