Skip to content

Conversation

@jamesarich
Copy link
Collaborator

Pull Request: Implement ESP32 Unified OTA Trigger & Protobuf Updates

Summary

This PR implements the "Reboot OTA" trigger mechanism for ESP32 devices, enabling successful WiFi and BLE firmware updates using the latest Unified OTA Protocol. It also incorporates support for recent protobuf changes proposed for Meshtastic to enable a more secure and structured OTA handshake.

Key Changes

1. Protobuf & Service Layer

  • External Dependency: This implementation relies on the proposed changes in meshtastic/protobufs PR #829 (specifically field Display power plug icon when no battery #99 #102 ota_request in AdminMessage). Note: These .proto file changes are not included in this commit.
  • IMeshService.aidl: Added requestRebootOta(requestId, destNum, mode, hash) to the remote interface.
  • MeshService.kt: Implemented the service logic to construct and send the structured ota_request admin message.

2. ESP32 OTA Handler (Esp32OtaUpdateHandler.kt)

  • Pre-connection Trigger: The app now signals the device to enter OTA mode before attempting to connect to the OTA transport (port 3232 for WiFi).
  • Integrity Hashing: Calculates the 32-byte SHA256 hash of the .bin file and includes it in the trigger message. This allows the device to verify the incoming stream against a pre-authorized hash.
  • Robust Connection Retry: Added a retry loop (10 attempts for WiFi, 5 for BLE with a 2s delay) to ensure the app waits for the device to reboot and start its listener.
  • Dependency Injection: Injected ServiceRepository to allow the handler to communicate with the MeshService.

3. Utilities & Documentation

  • FirmwareHashUtil.kt: Added calculateSha256Bytes to support the 32-byte binary hash format required by the new protobufs.
  • README.md: Added a comprehensive guide to the :feature:firmware module with Mermaid sequence diagrams for ESP32 OTA, nRF52 DFU, and USB/UF2 flows.
  • walkthrough.md: Created a detailed walkthrough of the new ESP32 OTA flow for user and developer review.

Impact

  • ESP32 Devices: Enables reliable WiFi and BLE updates for Heltec V3 and other ESP32 devices which previously failed to connect because they weren't in OTA mode.
  • Security: Firmware integrity is now enforced by the device comparing the upload stream against the hash transmitted during the secure Admin trigger phase.

Verification

  • Protobuf Generation: Verified successful generation of AdminProtos.java with the new fields.
  • Service Logic: Confirmed the AdminMessage is correctly constructed and passed to the PacketHandler.
  • Flow Logic: Verified the sequence of events (Trigger -> Wait/Retry -> Connect -> Version Check -> Flash).

Related Issues

@github-actions github-actions bot added the enhancement New feature or request label Dec 29, 2025
@codecov
Copy link

codecov bot commented Dec 29, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 0.00%. Comparing base (7d60c20) to head (d577c3b).

Additional details and impacted files
@@          Coverage Diff          @@
##            main   #4095   +/-   ##
=====================================
  Coverage   0.00%   0.00%           
=====================================
  Files          2       2           
  Lines         19      19           
  Branches       7       7           
=====================================
  Misses        19      19           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@jamesarich jamesarich force-pushed the feat/firmware-ota branch 2 times, most recently from 54746f3 to 063b3df Compare December 30, 2025 18:00
@jamesarich
Copy link
Collaborator Author

@jake-b - tagging you here, this is the Android ota implementation (so far).

This commit introduces support for the ESP32 Unified Over-the-Air (OTA) update protocol, enabling firmware updates for ESP32-based devices over both BLE and WiFi.

Key changes include:

*   **ESP32 Unified OTA Protocol:**
    *   Adds `UnifiedOtaProtocol` interface and data classes for commands and responses.
    *   Implements `BleOtaTransport` for handling OTA updates via Bluetooth Low Energy.
    *   Implements `WifiOtaTransport` for handling OTA updates via TCP/IP.
    *   Includes `Esp32OtaUpdateHandler` to orchestrate the update flow for both transports.
    *   Adds `FirmwareHashUtil` for SHA-256 calculation and verification.

*   **Device & OTA Capability:**
    *   Adds `supportsUnifiedOta` flag to `BootloaderOtaQuirk` and `DeviceHardware` models.
    *   Updates `device_bootloader_ota_quirks.json` to enable unified OTA for all ESP32-S3 devices.
    *   The `isDfuCapable` property is renamed to `isOtaCapable` to reflect support for both Nordic DFU and the new ESP32 OTA protocol.

*   **UI & ViewModel Integration:**
    *   The `FirmwareUpdateViewModel` now routes updates to `Esp32OtaUpdateHandler` for ESP32 devices and the existing `OtaUpdateHandler` for others.
    *   Adds support for WiFi-based updates, including a new "WiFi OTA" method and icon.
    *   `SettingsViewModel` and `RadioConfig` are updated to check for general OTA capability.

Signed-off-by: James Rich <2199651+jamesarich@users.noreply.github.com>
This commit introduces a robust Over-the-Air (OTA) update mechanism for ESP32 devices, supporting both BLE and WiFi transports through the Unified OTA protocol.

The key change is the introduction of a pre-shared hash verification process. The app now calculates the firmware's SHA256 hash and sends it to the device via an `AdminMessage` to trigger the reboot into OTA mode. The device stores this hash and uses it to verify the integrity of the firmware stream, enhancing the security and reliability of the update.

Changes include:
- **`requestRebootOta`**: A new `AdminMessage` and `IMeshService` method to trigger a reboot into OTA mode (BLE or WiFi) and provide the firmware hash.
- **`Esp32OtaUpdateHandler`**: Reworked to orchestrate the new flow: it first triggers the reboot with the hash, then repeatedly attempts to connect to the device's OTA service.
- **`FirmwareHashUtil`**: Added a function to compute the SHA256 hash as a `ByteArray` for the new `requestRebootOta` message.
- **TCP Connection**: Improved error logging and handling in `TCPInterface`.
- **Documentation**: Updated `README.md` with sequence diagrams explaining the new ESP32 OTA flow alongside existing DFU and UF2 methods.

Signed-off-by: James Rich <2199651+jamesarich@users.noreply.github.com>
This commit refactors the `BleOtaTransport` to use the Nordic Semiconductor Kotlin-BLE-Library, replacing the legacy Android BluetoothGatt APIs.

Key changes:
- Replaces `BluetoothGattCallback` with coroutine-based flows from the Nordic library for connection, discovery, and notifications.
- Removes manual `suspendCancellableCoroutine` implementations in favor of the library's suspend functions for characteristic writes.
- Simplifies connection logic by leveraging the `CentralManager` to find and connect to peripherals.
- Removes the small delay after writes, as the new implementation uses `WriteType.WITH_RESPONSE`.
- Updates `Esp32OtaUpdateHandler` to pass the `CentralManager` and device address to the new `BleOtaTransport` constructor.

Signed-off-by: James Rich <2199651+jamesarich@users.noreply.github.com>
This refactors the firmware update feature by introducing a `FirmwareUpdateManager` and abstracting the logic for different update methods (Nordic DFU, ESP32 OTA, USB) into their own dedicated `FirmwareUpdateHandler` implementations.

Key changes:
- `FirmwareUpdateManager` now orchestrates the update process, selecting the appropriate handler based on the connection type and device hardware.
- The monolithic `UpdateHandler.kt` has been split into smaller, more focused classes:
    - `NordicDfuHandler`: Manages nRF52 OTA updates.
    - `UsbUpdateHandler`: Manages UF2 updates.
    - `Esp32OtaUpdateHandler`: Refactored to implement the common handler interface for BLE/WiFi updates.
    - `FirmwareRetriever`: Consolidates firmware downloading and extraction logic.
- A common `FirmwareUpdateHandler` interface is introduced to standardize the `startUpdate` method.
- `DfuManager` and `UsbManager` are created to encapsulate interactions with the Nordic DFU library and Android's UsbManager, respectively.
- The `FirmwareUpdateViewModel` is simplified to delegate update logic to the `FirmwareUpdateManager`, reducing its direct responsibilities.

Signed-off-by: James Rich <2199651+jamesarich@users.noreply.github.com>
This commit introduces several improvements to the firmware update process:

- Adds a "Verifying" step after the update completes to wait for the device to reconnect, ensuring a successful flash.
- Displays transfer speed (KiB/s) and ETA during both DFU and OTA firmware uploads.
- Shows more detailed status messages during the OTA process, such as "Erasing flash...".
- Includes a pre-flight check to prevent updates if the device battery level is below 10%.

Signed-off-by: James Rich <2199651+jamesarich@users.noreply.github.com>
This commit refactors the firmware update screen for a more dynamic and user-friendly experience.

Key changes include:
- A redesigned layout using `SharedTransitionLayout` for smooth animated transitions between different update states (e.g., ready, downloading, updating).
- The introduction of a "Local File" option in the release type selector, allowing users to flash firmware from a file on their device.
- An improved device info card that now includes a pulsing animation during the update process.
- UI components are now more distinctly separated, with device info at the top and state-specific content animated in the main view.
- Release notes are now always visible for remote releases, removing the collapsible section.

Signed-off-by: James Rich <2199651+jamesarich@users.noreply.github.com>
This commit introduces several improvements and refactorings to the firmware update feature.

Key changes include:
- Refactored the `FirmwareUpdateScreen` to use a `FirmwareUpdateActions` data class, simplifying the function signatures of composables by grouping related event handlers.
- Extracted UI logic into a new `FirmwareUpdateMainColumn` composable for better structure and readability.
- Replaced magic numbers with named constants for animations, timeouts, buffer sizes, and other values across the firmware update feature, enhancing maintainability.
- Broke down the complex `update` function in `Esp32OtaUpdateHandler` into smaller, more manageable private functions (`obtainFirmwareFile`, `connectToDevice`, `executeOtaSequence`).
- Suppressed `TooGenericExceptionCaught` warnings where broad exceptions are intentionally caught and logged as part of the update process error handling.
- Replaced `throw IllegalStateException` with `error()` for contract violations.

Signed-off-by: James Rich <2199651+jamesarich@users.noreply.github.com>
This commit refactors the firmware update feature to improve user experience and provide better localization.

**Key Changes:**

*   **UI/UX Enhancements:**
    *   Introduces a `ProgressState` data class to provide richer feedback during updates, including status messages, progress, and detailed text (e.g., transfer speed).
    *   Simplifies animations on the update screen, removing complex shared element transitions in favor of `animateContentSize` for a cleaner look.
    *   Improves the layout and presentation of progress indicators, status messages, and device information.
    *   Enhances the "Chirpy" card and disclaimer dialog.

*   **Localization:**
    *   Adds and utilizes numerous localized strings for all stages of the update process, including downloading, processing, errors, and success states.
    *   Localizes the DFU notification channel name and description.

*   **Code Refinements:**
    *   Makes OTA protocol callbacks in `UnifiedOtaProtocol` suspend functions to allow for string resource retrieval.
    *   Updates various update handlers (`UsbUpdateHandler`, `NordicDfuHandler`, `Esp32OtaUpdateHandler`) to use the new `ProgressState` and localized strings.
    *   Reduces the TCP connection retry limit in `TCPInterface` from `Int.MAX_VALUE` to 20 to prevent indefinite retries.

Signed-off-by: James Rich <2199651+jamesarich@users.noreply.github.com>
This commit enhances the Nordic DFU (Device Firmware Update) process by providing more granular feedback to the user and improving the overall reliability of the update.

Key changes include:
- **Expanded DFU State Reporting**: Added new states (`Connecting`, `EnablingDfuMode`, `Validating`, `Disconnecting`) to offer a more detailed view of the DFU progress.
- **DFU Logging**: Implemented a `DfuLogListener` to capture and log events from the Nordic DFU library, aiding in debugging.
- **Improved Post-Update Verification**: Refactored the verification logic to wait for the device to fully reconnect and sync its node information before declaring the update a success.
- **DFU Parameter Tuning**:
    - Increased the DFU scan timeout to 5 seconds.
    - Enabled bond restoration (`setRestoreBond(true)`).
    - Explicitly enabled packet receipt notifications.
    - Added a preparation delay for the data object (`setPrepareDataObjectDelay`).
- **UI Enhancements**: Added new string resources to display the more detailed DFU statuses to the user.
- **Code Refinements**: Introduced constants for calculations in `Esp32OtaUpdateHandler` and cleaned up method visibility.

Signed-off-by: James Rich <2199651+jamesarich@users.noreply.github.com>
Updates the `infoUrl` for several nrf52-based devices (NANO_G2_ULTRA, RAK4631, NOMADSTAR_METEOR_PRO) to point to the official Meshtastic documentation for updating the bootloader.

Signed-off-by: James Rich <2199651+jamesarich@users.noreply.github.com>
Correctly sets both `otaHash` and `rebootOtaMode` when requesting an OTA reboot via the remote admin interface. Previously, only the hash was set, and the mode was set on an unnested builder.
Refactor the Over-the-Air (OTA) update process to use a sealed class `OtaHandshakeStatus` for status updates instead of a generic `String`. This improves type safety and makes the states more explicit.

*   Introduce `OtaHandshakeStatus.Erasing` to represent the device's flash erasing state.
*   Update `UnifiedOtaProtocol`, `BleOtaTransport`, and `WifiOtaTransport` to use the new `onHandshakeStatus` callback.
*   Add a new string resource `firmware_update_erasing` for the UI.
*   Update `Esp32OtaUpdateHandler` to handle the new `Erasing` status and display the corresponding message.
Refactored `FirmwareHashUtil` to expose a public `bytesToHex` utility function. This removes the private `ByteArray.toHexString()` extension function.

Updated callers in `Esp32OtaUpdateHandler` and within `FirmwareHashUtil` itself to use the new public method for converting SHA-256 byte arrays to hexadecimal strings.
Moved the DFU progress and state observation logic from the deleted `DfuManager` to `NordicDfuHandler`. This consolidates all Nordic DFU-related responsibilities into a single class.

The `FirmwareUpdateManager` now exposes the DFU progress flow directly from `NordicDfuHandler`.
This commit introduces Over-the-Air (OTA) firmware update capabilities for ESP32 devices via WiFi.

Key changes include:
- Adding a file picker for `.bin` files in the firmware update screen.
- Correcting the port parameter usage in the `WifiOtaTransport` to use the instance's port.
- Implementing the `Esp32OtaUpdateHandler` to facilitate the WiFi update process.

Signed-off-by: James Rich <2199651+jamesarich@users.noreply.github.com>
Refactors the firmware update screen to use a single file picker launcher (`getFileLauncher`) instead of separate launchers for ZIP, UF2, and BIN files.

The file type (`MIME type`) is now determined dynamically based on the selected update method (BLE/WiFi or USB), simplifying the code and removing redundant launchers.

Signed-off-by: James Rich <2199651+jamesarich@users.noreply.github.com>
Applied Kotlin formatting to `FirmwareUpdateScreen.kt` and `WifiOtaTransport.kt`. This addresses code style without changing functionality.

Signed-off-by: James Rich <2199651+jamesarich@users.noreply.github.com>
- Refined OTA handshake loop to handle multiple ERASING responses and terminate on OK.
- Simplified OTA sequence by removing sendVersion and reboot commands, aligning with the automatic reboot behavior.
- Updated BleOtaTransport and WifiOtaTransport to handle ACK and OK during stream more robustly.
- Ensured consistency with the latest iOS implementation (Jake's code).

Signed-off-by: James Rich <2199651+jamesarich@users.noreply.github.com>
This commit refactors the handling of Over-the-Air (OTA) reboot requests by moving the logic from `MeshService` to `MeshActionHandler`.

A new `handleRequestRebootOta` method is introduced in `MeshActionHandler` to construct and send the `AdminMessage` for OTA reboots. `MeshService` now delegates the call to this new handler, centralizing the action logic.

Signed-off-by: James Rich <2199651+jamesarich@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request needs-review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants