The OTA DFU process generally involves four steps, executed using OTA commands from the
image and command groups: Update Request, Start, Complete, and Reset. For detailed
information on OTA commands, refer to chapter 2 BLE Document in MPLAB Harmony Wireless BLE.
This section also provides a walk through of the OTA DFU procedure. Below is a
step-by-step guide on integrating the OTA framework into an application and using the
OTA API.
When
the OTAManager discovers a peripheral, it calls the
bleConnecting(bleScanState:discoveredPeripherals:) method of
its delegate
object.
var peripherals = Array<Any>()
//delegate
func bleConnecting(bleScanState:Bool, discoveredPeripherals:[Any]) {
peripherals.removeAll()
for peripheral in discoveredPeripherals{
if peripheral is peripheralInfo{ //The peripheralInfo is a tuple. First element is BLE device name and second element is BLE RSSI. It is present inside the framework.
let peripheralInfo = peripheral as! peripheralInfo
peripherals.append(peripheralInfo)
}
}
}
Figure 1-5. Scan for
Peripheral
Pairing and connect the device. Once
the Bluetooth Low Energy is connected, OTAManager calls the
bleDidConnect(peripheralName:) method of its delegate
object.
bleOTA?.bleConnect(deviceName: "BLE_UART_D57D")
//delegate
func bleDidConnect(peripheralName: String) {
//handle the connection state
}
Figure 1-6. Connection Process
Prepare the OTA data for
transmission. Once the Image ID is verified, OTA client sends the update request
command to the server.
OTA_Cancel can be called to reload
the OTA image data.
Image ID: Confirm whether the updated image is valid
before starting DFU procedure, this ID is included in OTAU header of
.bin
file.
func loadOTAImage() {
let bundle = Bundle.main
let fileUrl = bundle.url(forResource: "ble_uart_OTA_v1.1.1.0_image1", withExtension: "bin")
if(fileUrl != nil){
do {
let file = try FileHandle(forReadingFrom: fileUrl!)
let dat = file.readDataToEndOfFile()
let image_version: [UInt8] = [0x01, 0x02, 0x03, 0x04]
//Verify image id
let (image_id, image_rev) = bleOTA?.OTAImageHeader(OTAImage: dat)
if(image_id != nil){
bleOTA?.OTA_SetData(format: "BIN", data: dat, version: image_version, completion: {error in
if(error == nil){
print("Check OTA Image header information. Success")
} else {
print("Failed to load OTA file.")
}
})
}
} catch { /* error handling here */
print("Can't open text file")
return
}
}
}
Figure 1-7. Read and Set OTA image
file
Delegate method
otaProgressUpdate(state:value:updateBytes:otaTime:)
will be called to notify the firmware
version.
func otaProgressUpdate(state: UInt8, value: [UInt8], updateBytes: UInt, otaTime: String) {
if state == OTA_State.UpdateRequest.rawValue{
if(value.count == 8){
print("Device Version : \(String(format: "%d.%d.%d.%d", value[3],value[2],value[1],value[0])")
print("Update Version : \(String(format: "%d.%d.%d.%d", value[7],value[6],value[5],value[4])")
}
}
}
Now the connected peripheral is ready for OTA DFU.
OTA DFU Start/Stop:
The OTA
Client sends the fragmented image to the OTA Server and waits for a response.
The OTA Server sends a notification to the OTA Client within 3 seconds after
receiving the data to avoid a timeout error. Call
SetOTADataTimeout to modify the timeout value. The data
transfer continues until the total length of transfer data equals the size of
the OTA image data.
The delegate method
otaProgressUpdate(state:value:updateBytes:otaTime:)
notifies the progress value during the
update.
//start the ota upgrade process
bleOTA?.OTA_Start()
//delegate
func otaProgressUpdate(state: UInt8, value: [UInt8], updateBytes: UInt, otaTime: String) {
if state == OTA_State.UpdateStart.rawValue{
if(value.count == 1){
print("ota progress value = \(value[0])")
}
}
}
Figure 1-8. OTA Update Process
OTA DFU Complete. OTA Server sends update complete command to OTA Client.
Delegate method
otaProgressUpdate(state:value:updateBytes:otaTime:)
will be called to notify that the firmware update has been completed.
func otaProgressUpdate(state: UInt8, value: [UInt8], updateBytes: UInt, otaTime: String) {
if state == OTA_State.UpdateComplete.rawValue{
print("Firmware upgrade: Success")
}
}
OTA DFU Error :
OTAManager calls this delegate method when a OTA Error occurs. An error
code is generated to indicate the root cause of an error.
/**
OTA Error code
- OTA_FeatureNotSupport: BLE OTA Service is not found
- BLE_ConnectionFail: BLE is disconnected
- BLE_AbnormalDisconnect: BLE connection is abnormally disconnected
- BleAdapter_PeripheralNotReady: Reserved
- OTA_Command_InvalidState: Responsed error code = Invalid State
- OTA_Command_NotSupported: Responsed error code = Command not supported
- OTA_Command_OperationFailed: Responsed error code = Operation fail
- OTA_Command_InvalidParameter: Responsed error code = Invalid parameters
- OTA_Command_UnspecifiedError: Responsed error code = Unknown error
- OTA_Data_Result_Error: Data error, Device timeout
- OTA_Data_Ack_Timeout: ACK timeout in data transmission
*/
public enum OTA_ErrorCode: UInt8 {
case OTA_FeatureNotSupport = 0
case BLE_ConnectionFail
case BLE_AbnormalDisconnect
case BleAdapter_PeripheralNotReady
case OTA_Command_InvalidState
case OTA_Command_NotSupported
case OTA_Command_OperationFailed
case OTA_Command_InvalidParameter
case OTA_Command_UnspecifiedError
case OTA_Request_NotSupported
case OTA_Request_OperationFailed
case OTA_Request_InvalidParameter
case OTA_Complete_ValidationFail
case OTA_Data_Result_Error
case OTA_Data_Ack_Timeout
case OTA_BT_OFF
}
func operationError(errorcode: UInt8, description: String) {
switch errorcode {
case OTA_ErrorCode.BLE_AbnormalDisconnect.rawValue:
print("BLE disconnect or BT Off")
case OTA_ErrorCode.OTA_Command_InvalidState.rawValue:
print("Invalid State")
case OTA_ErrorCode.OTA_Command_NotSupported.rawValue:
print("Command not supported")
case OTA_ErrorCode.OTA_Command_OperationFailed.rawValue:
print("Command operation failed")
case OTA_ErrorCode.OTA_Command_InvalidParameter.rawValue:
print("Invalid parameters")
case OTA_ErrorCode.OTA_Command_UnspecifiedError.rawValue:
print("Unspecified Error")
case OTA_ErrorCode.OTA_Request_NotSupported.rawValue:
print("Possible reasons:\n\n1.OTA header parameter error\n2.Target device doesn't support\n3.Host doesn't accept the request")
case OTA_ErrorCode.OTA_Request_OperationFailed.rawValue:
print("Target device doesn't support OTA DFU")
case OTA_ErrorCode.OTA_Request_InvalidParameter.rawValue:
print("Possible reasons:\n\n1.Size is 0 or too large\n2.Size is not 16-byte alignment(internal FW DFU only)")
case OTA_ErrorCode.OTA_Data_Result_Error.rawValue:
print("Possible reasons:\n\n1.Responding error or timeout from host MCU(Host OTA)\n2.Host stops DFU")
case OTA_ErrorCode.OTA_Data_Ack_Timeout.rawValue:
print("No ACK from target device in transmission")
case OTA_ErrorCode.OTA_Complete_ValidationFail.rawValue:
print("OTA image fails to pass validation")
case OTA_ErrorCode.OTA_BT_OFF.rawValue:
print("BLE disconnect or BT Off")
default:
print("Unknown")
}
}
}