Bluetooth Mesh OTA SDK

更新时间:
复制 MD 格式

This topic describes the iOS SDK solution for Bluetooth Mesh over-the-air (OTA) firmware upgrades.

Overview

Dependent SDK

Overview

Bluetooth Breeze SDK

The Breeze SDK is a mobile Bluetooth software development kit (SDK) that complies with standard specifications. It helps partners quickly integrate Bluetooth features into their mobile apps. Key features of the Breeze SDK include device discovery and connection, device communication, encrypted transmission, and large data transmission.

Log SDK

A basic dependent SDK. It provides unified client-side log printing, log level control, and modular log fencing.

API Channel SDK

Provides API channel capabilities and basic environment configuration information.

Usage instructions

Connect to the OTA upgrade network

Before you upgrade a mesh device, disconnect the app from the mesh network and stop Bluetooth scanning.

/// The following are the dependent SDK header files.
#import <ALSBluetoothGATT/ALSBluetoothGATTManager.h>
#import <ALSBluetoothBLE/ALSBLEDevice.h>
#import <IMLDeviceCenter/IMLDeviceCenter.h>
#import <ALBBluetoothMesh/ALBBluetoothMesh.h>

/// Disconnect from the current Bluetooth mesh network.
[[IMLMeshHelper sharedHelper] disconectMesh];

/// Stop Bluetooth scanning.
[self stopScan];

- (void)stopScan {
    IMSOTALogInfo(@"stopScan");
    [[ALSBluetoothGATTManager sharedInstance] stopScan:NO];
}

Initialize the upgrade

Connect to a device

Before you start the OTA update, establish a Bluetooth Generic Attribute Profile (GATT) connection with the target device.

- (void)connectDevice:(NSString *)mac timeOut:(NSTimeInterval)timeOut {
    IMSOTALogInfo(@"connectDevice:%@ timeOut:%@", mac, @(timeOut));
    [self reset];
    self.mac = mac;
    NSArray *deviceArray = [[ALSBluetoothGATTManager sharedInstance] getDeviceArray];
    if (deviceArray) {
        for (ALSBluetoothDevice *bleDevice in deviceArray)  {
            if ([bleDevice.macAddress isEqualToString:mac]) {
                if (![bleDevice isKindOfClass:[ALSBLEDevice class]]) {
                    [self startScan:timeOut];
                    return;
                }
                IMSOTALogInfo(@"BLEDevice connectDevice mac:%@", bleDevice.macAddress);
                self.bleDevice = bleDevice;
                self.bleDevice.delegate = self;
                [self connectDevice:bleDevice];
                [self.bleDevice setBLEDeviceDelegate:self];
            }
        }
        if (!self.bleDevice) {
            [self startScan:timeOut];
        }
    } else {
        [self startScan:timeOut];
    }
}


/// Method to start a Bluetooth GATT scan.
- (void)startScan:(NSTimeInterval)timeOut {
    IMSOTALogInfo(@"startScan:%@", @(timeOut) );
    [[ALSBluetoothGATTManager sharedInstance] addGATTDelegate:self];
    [[ALSBluetoothGATTManager sharedInstance] startScan:timeOut];
    ///self.isScanning = YES;
}



- (void)connectDevice:(ALSBluetoothDevice *)device {
    //IMSOTALogInfo(@"connectDevice:%@", device.macAddress);
    //// Start connecting to the target device.
    [[ALSBluetoothGATTManager sharedInstance] removeGATTDelegate:self];
    [[ALSBluetoothGATTManager sharedInstance] connectDevice:device];
    
    [self removeConnectTimer];
    //// Add a timer to prevent connection timeout.
    NSTimeInterval interval = 20.0;
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    self.connectTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
    dispatch_time_t start = dispatch_time(DISPATCH_TIME_NOW, (interval * NSEC_PER_SEC));
    dispatch_source_set_timer(self.connectTimer, start, DISPATCH_TIME_FOREVER, 0);
    __weak typeof(self) weakSelf = self;
    dispatch_source_set_event_handler(self.connectTimer, ^{
        __strong __typeof(weakSelf)self = weakSelf;
        ///IMSOTALogInfo(@"content device timeout:%@", self.bleDevice.macAddress);
        [self removeConnectTimer];
        
        ////TODO: Reaching this point means the connection timed out. You can prompt that the OTA update failed.
        [self stopScan];
    });
    dispatch_resume(self.connectTimer);;
}

- (void)removeConnectTimer {
    IMSOTALogInfo(@"removeConnectTimer");
    if (self.connectTimer) {
        dispatch_source_cancel(self.connectTimer);
        self.connectTimer = nil;
    }
}

Parameter description

Name

Type

Required

Default value

Description

mac

NSSting *

Yes

The MAC address of the target device.

timeOut

NSTimeInterval

Yes

The maximum connection wait time, in seconds.

To connect to a device and perform the OTA update, you must implement two delegates that are defined in the SDK.

#pragma mark - ALSBluetoothGATTManagerDelegate
//// Callback for when a device is found.
- (void)onGenieDeviceFound:(ALSBluetoothDevice *)device {
    IMSOTALogInfo(@"onGenieDeviceFound:%@", device.macAddress);
    //if (!self.isScanning) {
    //    IMSOTALogInfo(@"onGenieDeviceFound:%@, isScanning == NO", device.macAddress);
    //}
    if ([device.macAddress isEqualToString:self.mac]) {
        if(![device isKindOfClass:[ALSBLEDevice class]]) {
            ////TODO: The device type is incorrect. Exit the OTA update directly.
            return;
        }
        /// The target device is found. Establish a Bluetooth GATT connection.
        self.bleDevice = device;
        self.bleDevice.delegate = self;
        [self connectDevice:device];
        [self.bleDevice setBLEDeviceDelegate:self];
        self.isScanning = NO;
        [self stopScan];
    }
//    if (!self.mac && [self.productId isEqualToString:[[NSNumber numberWithUnsignedInt:device.productId] stringValue]]) {
//        [self connectDevice:device.macAddress timeOut:kIMSOTAGATTContentTimeOut];
//    }
}

- (void)onScanStop:(NSArray<ALSBluetoothDevice *> *)deviceArray timeOut:(BOOL)timeOut {
    IMSOTALogInfo(@"onScanStop:%@", @(timeOut));
    if (self.isScanning) {
       ////TODO
    }
}

#pragma mark - ALSBluetoothDeviceDelegate

- (void)onGenieDeviceDisconnect:(ALSBluetoothDevice *)device {
    IMSOTALogInfo(@"onGenieDeviceDisconnect:%@", device.macAddress);
}

/// Callback for a successful device connection.
- (void)onGenieDeviceConnect:(ALSBluetoothDevice *)device {
    IMSOTALogInfo(@"onGenieDeviceConnect:%@", device.macAddress);
    if ([device.macAddress isEqualToString:self.bleDevice.macAddress]) {
        [self removeConnectTimer];
    }
}

//// Callback for a failed device connection.
- (void)onGenieDeviceFailToConnect:(ALSBluetoothDevice *)device Error:(NSError *)error {
    IMSOTALogInfo(@"onGenieDeviceFailToConnect:%@ error:%@", device.macAddress, error);
    if ([device.macAddress isEqualToString:self.bleDevice.macAddress]) {
        [self removeConnectTimer];
        ////NSError *error = [self createError:IMSOTAMeshConnectCodeFail];
        ////TODO
    }
}
 

//// Callback for the result of mutual authentication between the app and the device after a successful connection.
- (void)afterGenieDeviceAuth:(ALSBluetoothDevice *)device authSuc:(BOOL)authSuc error:(NSError *)error {
    IMSOTALogInfo(@"afterGenieDeviceAuth:%@ authSuc:%@ error:%@", device.macAddress, @(authSuc), error);
    [self removeConnectTimer];
    if (authSuc) {
        //// After successful connection and authentication, get the device firmware version.
        [self.bleDevice getDeviceVersion];
    } else {
        //// If mutual authentication fails after connection, the OTA process must also be considered stopped.
        ////NSError *error = [self createError:IMSOTAMeshConnectCodeFail];
    }
}

/// Callback for when the device firmware version is retrieved.
- (void)didGetDeviceVersion:(NSString *)version withError:(NSError *)error {
    IMSOTALogInfo(@"didGetDeviceVersion:%@ error: %@", version, error);
    [self removeConnectTimer];

    /// self.updateVersion is the version to which you want to upgrade the device.
    if ([version isEqualToString:self.updateVersion]) {
        IMSOTALogInfo(@"device version == updateVersion:%@", self.updateVersion);
        /// If the target version is the same as the current firmware version, no upgrade is needed.
        /// A prompt is required, and the OTA update must be stopped.
    } else {

        self.isOTAing = YES;
        [self startDownloadNewPackage];
    }
}

- (void)didDownloadingWithProgress:(double)progress
                       andFinished:(BOOL)isFinished
                         withError:(nullable NSError *)error {
    IMSOTALogInfo(@"didDownloadingWithProgress:%@ isFinished:%@ error:%@", @(progress), @(isFinished), error);
    if (error) {
        if (error.code == ALSOTAErrorTimeout) {
            /// OTA package download timed out.
            ///error = [self createError:IMSOTAMeshFailCodeTimeOut];
        } else if (error.code == ALSOTAErrorTypeDownloadInfoMissing) {
           /// Missing firmware information.
           // error = [self createError:IMSOTAMeshFailCodeGetFirmwareError];
        } else if (error.code == ALSOTAErrorTypePackageMissing) {
           /// The corresponding firmware package is not found in the cloud.
           /// error = [self createError:IMSOTAMeshFailCodeGetFirmwareError];
        } else {
        //// Unknown error.
           /// error = [self createError:IMSOTAMeshFailCodeUNKNOWN];
        }
        //// A failure occurred during the OTA package download.
    } else if (isFinished) {
        //// Start the OTA package upgrade.
        [self.bleDevice startOtaUpgrade];
    }
}

- (void)didUpgradingWithProgress:(double)progress
                     andFinished:(BOOL)isFinished
                       withError:(nullable NSError *)error {
    IMSOTALogInfo(@"didUpgradingWithProgress:%@ isFinished:%@ error:%@", @(progress), @(isFinished), error);
    if(error) {
        if(error.code == ALSOTAErrorTimeout) {
             /// The OTA process timed out.
            //error = [self createError:IMSOTAMeshFailCodeTimeOut];
        } else if(error.code == ALSOTAErrorDisconnect) {
            /// The connection was lost during the OTA process.
            //error = [self createError:IMSOTAMeshFailCodeDisconnect];
        } else if(error.code == ALSOTAErrorCheckFailed) {
            /// OTA firmware check failed.
            //error = [self createError:IMSOTAMeshFailCodeCheckFirmwareFail];
        } else if(error.code == ALSOTAErrorTypePackageMissing) {
            /// The OTA firmware package cannot be found.
            //error = [self createError:IMSOTAMeshFailCodeGetFirmwareError];
        } else if(error.code == ALSOTAErrorRefused) {
           /// The device rejected the upgrade.
            //error = [self createError:IMSOTAMeshFailCodeDeviceRefused];
        } else if(error.code == ALSOTAErrorIsOTAing) {
           /// An OTA update is already in progress.
            //error = [self createError:IMSOTAMeshFailCodeIsDoing];
        } else {
           //// Unknown error.
           // error = [self createError:IMSOTAMeshFailCodeUNKNOWN];
        }
         //// If a failure occurs during the actual OTA process, the failure must be reported.
        [self.bleDevice reportOtaProgressFeiyan:[@(progress * 100) stringValue] version:self.updateVersion iotId:self.iotId];
    } else {
        if (isFinished && progress == 100) {
            [self.bleDevice reportOtaProgressFeiyan:@"100" version:self.updateVersion iotId:self.iotId];
             
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                //// The OTA update is successful. The service layer notifies the user.
            });
        }
    }
}

Download the upgrade firmware

After you retrieve the device firmware version, start downloading the OTA package. The method's input parameters are determined by the Get firmware upgrade information API operation.

- (void)startDownloadNewPackage {
    IMSOTALogInfo(@"startDownloadNewPackage");
    if (!self.bleDevice) {
        ///NSError *error = [self createError:IMSOTAMeshFailCodeNotConnect];
        //// Notify that the OTA update failed.
        return;
    } else if (!(self.otaURL && self.md5)) {
        /// otaURL and md5 are returned when you call the cloud API to query for available upgrade packages for the device. If they are not returned, the OTA update cannot proceed.
        return;
    }
    
    /// The save path for the OTA package.
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory,NSUserDomainMask,YES);
    NSString *otaPath = [paths.firstObject stringByAppendingPathComponent:@"ble_ota"];
    BOOL createPathOk = YES;
    if (![[NSFileManager defaultManager] fileExistsAtPath:otaPath isDirectory:&createPathOk]) {
        // If the folder does not exist, create it first.
        [[NSFileManager defaultManager] createDirectoryAtPath:otaPath withIntermediateDirectories:YES attributes:nil error:nil];
    }
    IMSOTALogInfo(@"downloadNewPackageToDirectory:%@ \n otaUrl:%@ \n md5:%@ \n version:%@", otaPath, self.otaURL, self.md5, self.updateVersion);
    [self.bleDevice downloadNewPackageToDirectory:otaPath otaUrl:self.otaURL md5:self.md5 version:self.updateVersion];
}

Parameter description

NSString *

The download URL of the OTA package.

NSString *

The MD5 hash of the OTA package. A check is performed after the download is complete.

updateVersion

NSString *

Yes

The version of the OTA package for the upgrade.

You can obtain all the preceding parameters by calling the /living/ota/firmware/file/get cloud API. For more information, see Get device firmware information.

Start upgrading the device

After the OTA package is downloaded, the OTA update process begins.

[self.bleDevice startOtaUpgrade];

Stop upgrading the device

- (void)stopUpgrade {
    IMSOTALogInfo(@"stopUpgrade");
//    if (self.isOTAing) {
//        [self otaProgressReportFailed];
//    }
    [self removeConnectTimer];
    [[ALSBluetoothGATTManager sharedInstance] disconnectDevice:self.bleDevice];
    [[ALSBluetoothGATTManager sharedInstance] removeGATTDelegate:self];
    [[ALBBluetoothMesh sharedInstance] connectMesh];
    //[self reset];
    [self stopScan];
    //TODO: Stop the download and stop the OTA update.
    
}