Device Firmware Update
Device OTA updates the main firmware running on a supported AIBuds device. It is separate from Camera OTA, which updates the device's camera module.
The host app supplies a compatible local firmware package after completing its own update check, download, integrity verification, and device-model validation. The SDK transfers and installs that package, reports startup success, streams progress from 0.0 to 1.0, and returns the final upgrade result with the average transfer speed. Treat startHandler only as confirmation that the OTA task started; use completionHandler as the authoritative final result.
Device OTA delivery path
Validate the product input first, then let the SDK start, transfer, and complete the main-firmware update.
Prerequisites
- The device is connected and conforms to
DeviceOtaAPI. - Use
otaProtocolCapabilityto determine the supported protocol; do not infer it from the firmware filename. - For FitCloud Pro or Jieli, install and register the matching OTA plugin before connecting the device.
- Device battery is at least
otaBatteryLimitpercent. filePathpoints to the correct, complete firmware package for this device.- Keep the app active and connection stable until completion.
Implement with AI Assistance
Implement this workflow with AI
Use the official Update AIBuds Firmware skill to adapt this workflow to your app.
Read and follow https://docs-aibuds.github.io/skills/update-aibuds-firmware. Use it to implement Update AIBuds Firmware in this iOS project and verify the result.API Reference
Framework
AIBuds.xcframework
Import
- Swift
- Objective-C
import AIBuds
import AIBudsFoundation#import <AIBuds/AIBuds-Swift.h>
#import <AIBuds/AIBuds.h>Protocol
- Swift
- Objective-C
/// The protocol for device OTA upgrade API.
protocol DeviceOtaAPI: DeviceAPI {
/// The OTA protocol capability reported by the device.
/// Defaults to `.abmate` when the device does not report this capability.
var otaProtocolCapability: OtaProtocolCapability { get }
/// OTA battery limit, 0...100, unit: percent.
var otaBatteryLimit: Int { get }
/// Start OTA upgrade.
/// - Parameters:
/// - filePath: Upgrade file path.
/// - startHandler: Upgrade start callback.
/// - success: Whether the OTA task started successfully.
/// - error: Failure information, or `nil` if the task started.
/// - progressHandler: Upgrade progress callback.
/// - progress: Progress value in the range `0.0...1.0`.
/// - completionHandler: Final upgrade completion callback.
/// - success: Whether the upgrade succeeded.
/// - avgSpeed: Average transfer speed in kB/s.
/// - error: Failure information, or `nil` if the upgrade succeeded.
func startOta(
withFilePath filePath: String,
startHandler: AIBudsOtaStartCompletionHandler?,
progressHandler: AIBudsOtaProgressHandler?,
completionHandler: AIBudsOtaCompletionHandler?
)
/// Start OTA upgrade with an explicit transfer protocol configuration.
/// - Parameters:
/// - filePath: Upgrade file path.
/// - configuration: OTA protocol configuration.
/// - startHandler: Upgrade start callback.
/// - success: Whether the OTA task started successfully.
/// - error: Failure information, or `nil` if the task started.
/// - progressHandler: Upgrade progress callback.
/// - progress: Progress value in the range `0.0...1.0`.
/// - completionHandler: Final upgrade completion callback.
/// - success: Whether the upgrade succeeded.
/// - avgSpeed: Average transfer speed in kB/s.
/// - error: Failure information, or `nil` if the upgrade succeeded.
func startOta(
withFilePath filePath: String,
configuration: OtaConfiguration,
startHandler: AIBudsOtaStartCompletionHandler?,
progressHandler: AIBudsOtaProgressHandler?,
completionHandler: AIBudsOtaCompletionHandler?
)
}/// The protocol for device OTA upgrade API.
@protocol AIBudsDeviceOtaAPI <AIBudsDeviceAPI>
/// The OTA protocol capability reported by the device.
/// Defaults to `AIBudsOtaProtocolCapabilityAbmate` when the device does not report it.
@property(nonatomic, readonly) AIBudsOtaProtocolCapability otaProtocolCapability;
/// OTA battery limit, 0...100, unit: percent.
@property(nonatomic, readonly) NSInteger otaBatteryLimit;
/// Start OTA upgrade from a local firmware path.
///
/// - Parameters:
/// - filePath: The readable local firmware file path.
/// - startHandler: Called when the OTA start attempt completes.
/// - success: `YES` if the OTA task started; otherwise `NO`.
/// - error: Failure information, or `nil` if the task started.
/// - progressHandler: Called when OTA progress changes.
/// - progress: Progress in the range `0.0...1.0`.
/// - completionHandler: Called when the OTA operation finishes.
/// - success: `YES` if the upgrade succeeded; otherwise `NO`.
/// - avgSpeed: Average transfer speed in kB/s.
/// - error: Failure information, or `nil` if the upgrade succeeded.
- (void)startOtaWithFilePath:(NSString *_Nonnull)filePath
startHandler:(AIBudsOtaStartCompletionHandler _Nullable)startHandler
progressHandler:(AIBudsOtaProgressHandler _Nullable)progressHandler
completionHandler:(AIBudsOtaCompletionHandler _Nullable)completionHandler;
/// Start OTA upgrade with an explicit transfer protocol configuration.
///
/// - Parameters:
/// - filePath: The readable local firmware file path.
/// - configuration: The OTA protocol configuration required by the device.
/// - startHandler: Called when the OTA start attempt completes.
/// - success: `YES` if the OTA task started; otherwise `NO`.
/// - error: Failure information, or `nil` if the task started.
/// - progressHandler: Called when OTA progress changes.
/// - progress: Progress in the range `0.0...1.0`.
/// - completionHandler: Called when the OTA operation finishes.
/// - success: `YES` if the upgrade succeeded; otherwise `NO`.
/// - avgSpeed: Average transfer speed in kB/s.
/// - error: Failure information, or `nil` if the upgrade succeeded.
- (void)startOtaWithFilePath:(NSString *_Nonnull)filePath
configuration:(AIBudsOtaConfiguration *_Nonnull)configuration
startHandler:(AIBudsOtaStartCompletionHandler _Nullable)startHandler
progressHandler:(AIBudsOtaProgressHandler _Nullable)progressHandler
completionHandler:(AIBudsOtaCompletionHandler _Nullable)completionHandler;
@endSee otaProtocolCapability, otaBatteryLimit, and the startOta overloads in the API Reference.
Device Capability
Read otaProtocolCapability after the device is ready and use it to constrain protocol selection.
| Swift | Objective-C | Raw value | Supported protocol |
|---|---|---|---|
.none | AIBudsOtaProtocolCapabilityNone | -1 | No reported OTA support. |
.abmate | AIBudsOtaProtocolCapabilityAbmate | 0 | ABMate. This is also the fallback when the capability is not reported. |
.fitcloudPro | AIBudsOtaProtocolCapabilityFitcloudPro | 1 | FitCloud Pro. Requires the FitCloud Pro plugin. |
.abmateAndFitcloudPro | AIBudsOtaProtocolCapabilityAbmateAndFitcloudPro | 2 | ABMate and FitCloud Pro; present only the registered choices. |
.jieli | AIBudsOtaProtocolCapabilityJieli | 3 | Jieli single-bank OTA. Requires the Jieli plugin. |
OTA Configuration
OtaConfiguration selects the BLE OTA protocol used by the configured overload. Its otaProtocol property defaults to .abmate.
| Swift | Objective-C | Raw value | Meaning |
|---|---|---|---|
.abmate | AIBudsOtaProtocolKindAbmate | 0 | ABMate OTA protocol. |
.fitcloudPro | AIBudsOtaProtocolKindFitcloudPro | 1 | FitCloud Pro OTA protocol. |
.jieli | AIBudsOtaProtocolKindJieli | 2 | Jieli single-bank OTA protocol. |
Do not select a protocol by guessing from the firmware file. Use the protocol required by the connected device and product integration.
Optional OTA Plugins
FitCloud Pro and Jieli are separate CocoaPods subspecs. Register their plugins before connecting a device so the SDK can discover and subscribe to the required BLE characteristics. AIBudsSDK/AllInOne installs and registers both automatically.
pod 'AIBudsSDK/FitCloudProOTA'
pod 'AIBudsSDK/JieliOTA'import AIBuds
import AIBudsFitCloudProOTA
import AIBudsJieliOTA
AIBudsSDK.registerOtaPlugin(FitCloudProOtaSDK.otaPlugin)
AIBudsSDK.registerOtaPlugin(JieliOtaSDK.otaPlugin)For modular integration, register only the implementations your product ships. A later registration for the same OtaProtocolKind replaces the previous plugin. Use AIBudsSDK.otaPlugin(for:) to test availability or AIBudsSDK.removeOtaPlugin(for:) to unregister one.
Return Value
Neither overload returns a value directly. startHandler reports whether the OTA task started, progressHandler reports normalized progress, and completionHandler provides the authoritative final result and average transfer speed.
Usage Examples
- Swift
- Objective-C
guard let device = device as? DeviceOtaAPI else { return }
guard deviceBatteryPercent >= device.otaBatteryLimit else {
print("Charge the device before updating")
return
}
device.startOta(
withFilePath: firmwareURL.path,
startHandler: { success, error in
if !success { print(error?.localizedDescription ?? "OTA failed to start") }
},
progressHandler: { progress in
print("OTA: \(Int(progress * 100))%")
},
completionHandler: { success, averageSpeed, error in
print(
success
? "OTA completed at \(averageSpeed) kB/s"
: (error?.localizedDescription ?? "OTA failed"))
})id<AIBudsDeviceOtaAPI> device = (id<AIBudsDeviceOtaAPI>)self.device;
if (![device conformsToProtocol:@protocol(AIBudsDeviceOtaAPI)])
return;
[device startOtaWithFilePath:firmwareURL.path
startHandler:^(BOOL success, NSError *_Nullable error) {
if (!success)
NSLog(@"OTA failed to start: %@", error.localizedDescription);
}
progressHandler:^(CGFloat progress) {
NSLog(@"OTA: %.0f%%", progress * 100);
}
completionHandler:^(BOOL success, CGFloat averageSpeed, NSError *_Nullable error) {
if (success) {
NSLog(@"OTA completed at %.2f kB/s", averageSpeed);
} else {
NSLog(@"OTA failed: %@", error.localizedDescription);
}
}];Use an Explicit OTA Protocol
Use the configured overload only when your product integration knows which OTA protocol the connected device requires.
- Swift
- Objective-C
let configuration = OtaConfiguration()
configuration.otaProtocol = .fitcloudPro
device.startOta(
withFilePath: firmwareURL.path,
configuration: configuration,
startHandler: { success, error in
if !success {
print(error?.localizedDescription ?? "OTA failed to start")
}
},
progressHandler: { progress in
print("OTA: \(Int(progress * 100))%")
},
completionHandler: { success, averageSpeed, error in
print(
success
? "OTA completed at \(averageSpeed) kB/s"
: (error?.localizedDescription ?? "OTA failed"))
}
)AIBudsOtaConfiguration *configuration = [[AIBudsOtaConfiguration alloc] init];
configuration.otaProtocol = AIBudsOtaProtocolKindFitcloudPro;
[device startOtaWithFilePath:firmwareURL.path
configuration:configuration
startHandler:^(BOOL success, NSError *_Nullable error) {
if (!success)
NSLog(@"OTA failed to start: %@", error.localizedDescription);
}
progressHandler:^(CGFloat progress) {
NSLog(@"OTA: %.0f%%", progress * 100);
}
completionHandler:^(BOOL success, CGFloat averageSpeed, NSError *_Nullable error) {
if (success) {
NSLog(@"OTA completed at %.2f kB/s", averageSpeed);
} else {
NSLog(@"OTA failed: %@", error.localizedDescription);
}
}];Error Handling
OTA errors use AIBudsSDK.OtaErrorDomain and SdkOtaErrorCode.
| Codes | Typical condition |
|---|---|
unknown | The SDK cannot classify the error more narrowly. |
otaTaskAlreadyRunning | Another OTA task is already active. |
otaTaskCreateFailedDueToFileNotFound | The local firmware path does not exist. |
otaTaskStartFailedDueToFileReadError, otaTaskStartFailedDueToFileHandleCreateError | The package cannot be opened or read. |
otaTaskStartFailedDueToInvalidFileHashData | Firmware hash data is invalid. |
otaTaskStartFailedDueToGetOtaInfoError | Required OTA metadata cannot be obtained. |
otaTaskStartFailedDueToInvalidOffsetAddress, otaTaskStartFailedDueToInvalidBlockSize | Transfer metadata is invalid. |
otaTaskStartFailedDueToNotAllowUpdate | The device does not allow the update in its current state. |
otaTaskSendDataFailedDueToFileHandleIsNil, otaTaskSendDataFailedDueToSeekFileHandleFailed, otaTaskSendDataFailedDueToReadFileDataFailed, otaTaskSendDataFailedDueToOtaInfoIsNil | The SDK cannot continue reading or sending firmware data. |
otaTaskFailedDueToDeviceReportKeyMismatch, otaTaskFailedDueToDeviceReportCrcError, otaTaskFailedDueToDeviceReportSeqError, otaTaskFailedDueToDeviceReportDataLengthError | The device rejects transferred data or reports an integrity/sequence problem. |
otaTaskFailedDueToDeviceDisconnect, otaTaskFailedDueToTimeout | The device disconnects or the operation times out. |
Distinguish startHandler failure from a failure after transfer begins. Do not retry automatically with an unverified package; revalidate device model, firmware version, package integrity, battery, protocol selection, and connection first.
Best Practices
- Complete update discovery, download, signature or integrity verification, and device-model compatibility checks before calling the SDK.
- Check
otaBatteryLimitimmediately before starting, not only when presenting the update UI. - Prevent concurrent OTA, Camera OTA, Media File Import, or other long-running device operations.
- Dispatch UI updates to the main queue because callbacks may arrive on another queue.
- Treat
startHandleras task-start confirmation only; do not report upgrade success untilcompletionHandlersucceeds. - Keep the app active and the device connection stable through final completion, then verify the reported firmware version after reconnecting.
Notes
- Progress is normalized to
0.0...1.0; clamp UI presentation defensively without changing the SDK result. avgSpeedis reported in kB/s only by the final completion handler.OtaConfiguration.otaProtocoldefaults to.abmate; select.fitcloudProor.jielionly whenotaProtocolCapabilityand an installed plugin support it.- The SDK does not expose an OTA cancellation method. The Demo's Cancel button resets its local UI state and must not be documented as cancelling the SDK operation.