feat: Add firmware update module for Nordic nRF devices#3782
Conversation
This commit introduces a new feature module (`feature:firmware`) to handle Device Firmware Updates (DFU) for Nordic nRF52-based devices over Bluetooth LE.
- **Module & Dependencies**:
- Added the `:feature:firmware` module and integrated it into the main app.
- Included the Nordic DFU library (`no.nordicsemi.android:dfu:2.10.1`) and other necessary dependencies like Coil for image loading.
- **DFU Logic & ViewModel**:
- Implemented `FirmwareUpdateViewModel` to manage the entire update flow:
- Fetches stable or alpha releases from `FirmwareReleaseRepository`.
- Downloads the release artifact, showing progress.
- Extracts the correct device-specific firmware from the downloaded zip archive.
- Initiates the Nordic DFU process.
- Supports updating from a local file (`.zip`).
- Added a `FirmwareFileHandler` for downloading, extracting, and cleaning up firmware files.
- Implemented a `FirmwareDfuService` as required by the Nordic DFU library, including a notification channel.
- **UI & Navigation**:
- Created `FirmwareUpdateScreen` with a comprehensive UI to guide the user through the update process, showing different states (checking, downloading, updating, success, error).
- Added a `Firmware Update` entry in the device configuration screen, which is conditionally displayed for DFU-capable (nRF52) devices.
- Implemented `FirmwareRoutes` and integrated the navigation graph into the application.
- **Resources & Configuration**:
- Added extensive string resources for all steps and states of the firmware update process.
- Configured the Android Manifest with necessary permissions and the DFU service declaration.
Signed-off-by: James Rich <2199651+jamesarich@users.noreply.github.com>
| try { | ||
| // 1. Download | ||
| _state.value = FirmwareUpdateState.Downloading(0f) | ||
| val zipUrl = release.zipUrl.replace("esp32", "nrf52840") |
There was a problem hiding this comment.
Couple questions here :
- What if the URL doesn't contain "esp32"? It may silently continues with the wrong firmware
- What if "esp32" appears multiple times in the URL?
- This assumes the release structure for nrf52840 matches esp32, which may not be true
Users could flash completely wrong firmware to their devices
May need to add some defensive checks here
There was a problem hiding this comment.
May need to use the actual device hardware model to construct the correct firmware URL, and validate the firmware is for the correct hardware before flashing.
There was a problem hiding this comment.
I had some similar thought about how much do we need to hand-hold during the process.
| _state.value = FirmwareUpdateState.Downloading(0f) | ||
| val zipUrl = release.zipUrl.replace("esp32", "nrf52840") | ||
|
|
||
| val downloadedZip = |
There was a problem hiding this comment.
Few things here:
- No checksum/hash verification of downloaded firmware
- No signature validation
- No verification that the firmware file is actually for the target device
- A corrupted download or man-in-the-middle attack could brick the device
| * 3. Initiates the DFU process. | ||
| */ | ||
| @Suppress("TooGenericExceptionCaught") | ||
| fun startUpdate() { |
There was a problem hiding this comment.
Maybe add battery/RSSI validation before starting
| .setDeviceName(deviceHardware.displayName) | ||
| .setKeepBond(true) | ||
| .setZip(Uri.fromFile(firmwareFile)) | ||
| .setUnsafeExperimentalButtonlessServiceInSecureDfuEnabled(true) |
There was a problem hiding this comment.
name leads me to think this isn't prod ready, I haven't read through nordic yet so unsure of implications
There was a problem hiding this comment.
The docs are more about how this dfu triggering method is "unsafe" overall (on the device side).
| } | ||
| } | ||
|
|
||
| private fun cleanupTemporaryFiles() { |
There was a problem hiding this comment.
if app crashes during update, files remain
|
…itectures This commit replaces a hardcoded firmware URL replacement with a dynamic function, `getDeviceFirmwareUrl`, to correctly construct download URLs for various hardware architectures. Previously, the firmware download logic incorrectly assumed all non-ESP32 devices were `nrf52840`. The new implementation intelligently detects the architecture from the release URL and substitutes it with the target device's architecture. - Implemented `getDeviceFirmwareUrl` to handle multiple architectures like `esp32-s3`, `esp32-c3`, `esp32-c6`, `nrf52840`, `rp2040`, and `stm32`. - Updated `FirmwareUpdateViewModel` to use this function, ensuring the correct firmware package is downloaded based on the connected device's hardware. Signed-off-by: James Rich <2199651+jamesarich@users.noreply.github.com>
needs to be a .zip - probably should have some instructions along with warnings |
This commit enhances the firmware update process by adding download integrity checks and refining the firmware extraction logic to prevent mismatches. - Added a check to verify that the downloaded file size matches the `content-length` header, throwing an `IOException` on mismatch. - Improved the firmware extraction logic to prevent incorrect flashing due to partial name matches (e.g., preventing "tbeam" from matching "tbeam-s3"). - The new logic finds all potential firmware files for a device and selects the one with the shortest name as the best match. - A comment was added to note the current lack of checksums in the API. Signed-off-by: James Rich <2199651+jamesarich@users.noreply.github.com>
The `firmware_update_updating` and `firmware_update_downloading` string resources already included a percentage sign (`%`), and the view model was appending an additional one. This resulted in a double percentage sign (e.g., "Updating... 50%%") in the UI. This commit removes the redundant `%` from the string formatting in `FirmwareUpdateViewModel.kt` and the string resource definitions to correct the display. Signed-off-by: James Rich <2199651+jamesarich@users.noreply.github.com>
This commit introduces a confirmation dialog that appears before a firmware update can be initiated. The dialog warns the user about the potential risks associated with flashing new firmware and requires them to acknowledge these risks before proceeding. - Added an `AlertDialog` to `FirmwareUpdateScreen` to act as a disclaimer. - The "Update" button now triggers this disclaimer dialog instead of starting the update directly. - Added new string resources for the disclaimer's title and text content. Signed-off-by: James Rich <2199651+jamesarich@users.noreply.github.com>
…U improvements
This commit enhances the firmware update process by prioritizing direct downloads of device-specific firmware and refining the DFU (Device Firmware Update) configuration for better reliability.
- Implemented logic to first attempt downloading a specific firmware build for the target device, falling back to the full release zip only if the direct download fails.
- Adjusted DFU initiator settings for increased stability:
- Enabled `setForceScanningForNewAddressInLegacyDfu(true)` to improve device rediscovery.
- Set a 2-second scan timeout (`setScanTimeout`).
- Configured packet receipt notifications to reduce overhead.
- Disabled DFU session resumption (`disableResume`).
- Refactored `downloadFirmware` into a more generic `downloadFile` function.
- Added a `checkUrlExists` utility to verify direct download links before attempting to fetch them.
- Updated file cleanup logic to include the new direct download firmware file.
Signed-off-by: James Rich <2199651+jamesarich@users.noreply.github.com>
The firmware download URL was incorrect due to a missing `-ota` suffix in the filename. This change updates the filename construction in `FirmwareUpdateViewModel` to `firmware-$target-$version-ota.zip` to ensure the correct OTA firmware package is downloaded. Signed-off-by: James Rich <2199651+jamesarich@users.noreply.github.com>
This commit enhances the firmware update disclaimer dialog by adding a visual and textual element featuring "Chirpy," the Meshtastic mascot.
- Added a `Card` within the `DisclaimerDialog` to display an image of Chirpy and a related message.
- Introduced a new vector drawable for Chirpy (`core/ui/src/main/res/drawable/chirpy.xml`).
- Added new string resources for the disclaimer message ("Chirpy says, 'Keep your ladder handy!'") and for accessibility.
Signed-off-by: James Rich <2199651+jamesarich@users.noreply.github.com>
This commit refines the firmware update functionality by enforcing stricter conditions for its availability. - The firmware update option is now only displayed for nodes that are both DFU-capable and locally connected (`state.isLocal`). - The logic to determine DFU capability has been updated to rely on a `requiresDfu` property from the hardware model, replacing a check for the "nrf52" architecture. - The device address validation for DFU eligibility has been simplified. Signed-off-by: James Rich <2199651+jamesarich@users.noreply.github.com>
This commit refactors the `FirmwareFileHandler` to improve the management of temporary firmware files and enhance robustness. - **Centralized Temp Directory:** All temporary files for firmware updates are now stored in a dedicated `firmware_update` subdirectory within the cache, preventing clutter in the main cache directory. - **Robust Cleanup:** Implemented `deleteRecursively()` to reliably clean up all temporary files and the dedicated directory, such as after a crash. This replaces the previous method of deleting individual files. - **Lowercase URL Replacement:** Ensures that the target architecture in firmware URLs is always lowercase for consistency. - **Directory Creation:** The temporary directory is now proactively created before any file operations to prevent potential `FileNotFoundException` errors. Signed-off-by: James Rich <2199651+jamesarich@users.noreply.github.com>
This commit introduces a new feature module (
feature:firmware) to handle Device Firmware Updates (DFU) for Nordic nRF52-based devices over Bluetooth LE.Module & Dependencies:
:feature:firmwaremodule and integrated it into the main app.no.nordicsemi.android:dfu:2.10.1) and other necessary dependencies like Coil for image loading.DFU Logic & ViewModel:
FirmwareUpdateViewModelto manage the entire update flow: - Fetches stable or alpha releases fromFirmwareReleaseRepository. - Downloads the release artifact, showing progress. - Extracts the correct device-specific firmware from the downloaded zip archive. - Initiates the Nordic DFU process. - Supports updating from a local file (.zip).FirmwareFileHandlerfor downloading, extracting, and cleaning up firmware files.FirmwareDfuServiceas required by the Nordic DFU library, including a notification channel.UI & Navigation:
FirmwareUpdateScreenwith a comprehensive UI to guide the user through the update process, showing different states (checking, downloading, updating, success, error).Firmware Updateentry in the device configuration screen, which is conditionally displayed for DFU-capable (nRF52) devices.FirmwareRoutesand integrated the navigation graph into the application.Resources & Configuration:
Screen_recording_20251123_164232.mp4