本文介绍iOS SDK提供蓝牙Mesh OTA业务的APP端解决方案,提供了蓝牙Mesh设备固件升级的能力。
概述
依赖 SDK | 概述 |
蓝牙 Breeze SDK | Breeze SDK是按照规范实现的手机端蓝牙 SDK,方便合作厂商在手机端快速接入蓝牙功能。Breeze SDK 包含的主要功能有:设备发现连接,设备通信,加密传输,大数据传输等。 |
基础依赖SDK,提供客户端统一日志打印,日志等级控制,分模块日志隔离等能力。 | |
提供API通道能力,和基础环境配置信息。 |
使用说明
连接到OTA升级网络
在mesh设备升级前,需要保证当前APP断开Mesh网络并且停止蓝牙扫描。
///依赖到的sdk头文件如下
#import <ALSBluetoothGATT/ALSBluetoothGATTManager.h>
#import <ALSBluetoothBLE/ALSBLEDevice.h>
#import <IMLDeviceCenter/IMLDeviceCenter.h>
#import <ALBBluetoothMesh/ALBBluetoothMesh.h>
///断开当前的蓝牙mesh网络的连接
[[IMLMeshHelper sharedHelper] disconectMesh];
///停止蓝牙扫描
[self stopScan];
- (void)stopScan {
IMSOTALogInfo(@"stopScan");
[[ALSBluetoothGATTManager sharedInstance] stopScan:NO];
}
初始化升级
连接设备
在开始OTA之前,要跟目标设备建立蓝牙GATT连接。
- (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];
}
}
///开始蓝牙GATT扫描方法
- (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);
////开始真正连接目标设备
[[ALSBluetoothGATTManager sharedInstance] removeGATTDelegate:self];
[[ALSBluetoothGATTManager sharedInstance] connectDevice:device];
[self removeConnectTimer];
////增加Timer,以防止连接过程超时。
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,走到这里代表连接超时了,可以提示OTA失败
[self stopScan];
});
dispatch_resume(self.connectTimer);;
}
- (void)removeConnectTimer {
IMSOTALogInfo(@"removeConnectTimer");
if (self.connectTimer) {
dispatch_source_cancel(self.connectTimer);
self.connectTimer = nil;
}
}
参数说明
名称 | 类型 | 必填 | 默认值 | 描述 |
mac | NSSting * | 是 | 目标设备的Mac地址。 | |
timeOut | NSTimeInterval | 是 | 连接等待的最大时间,单位:S。 |
连接设备、设备OTA过程需要实现SDK中定义的2个Delegate。
#pragma mark - ALSBluetoothGATTManagerDelegate
////搜到设备的回调
- (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,设备类型错误,直接退出OTA
return;
}
///搜到目标设备,建立蓝牙GATT连接
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);
}
///设备连接成功回调
- (void)onGenieDeviceConnect:(ALSBluetoothDevice *)device {
IMSOTALogInfo(@"onGenieDeviceConnect:%@", device.macAddress);
if ([device.macAddress isEqualToString:self.bleDevice.macAddress]) {
[self removeConnectTimer];
}
}
////连接设备失败回调
- (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
}
}
////连接成功后,APP&设备双向认证结果回调
- (void)afterGenieDeviceAuth:(ALSBluetoothDevice *)device authSuc:(BOOL)authSuc error:(NSError *)error {
IMSOTALogInfo(@"afterGenieDeviceAuth:%@ authSuc:%@ error:%@", device.macAddress, @(authSuc), error);
[self removeConnectTimer];
if (authSuc) {
////连接认证成功后,去获取设备固件版本
[self.bleDevice getDeviceVersion];
} else {
////连接后,双向认证失败,也需要视作OTA流程终止
////NSError *error = [self createError:IMSOTAMeshConnectCodeFail];
}
}
///获取到设备固件版本号的回调
- (void)didGetDeviceVersion:(NSString *)version withError:(NSError *)error {
IMSOTALogInfo(@"didGetDeviceVersion:%@ error:%@", version, error);
[self removeConnectTimer];
///self.updateVersion为设备想要升级的版本
if ([version isEqualToString:self.updateVersion]) {
IMSOTALogInfo(@"device version == updateVersion:%@", self.updateVersion);
///要升级的版本跟当前固件版本一样,则不需要升级
///需要给出提示,并终止OTA
} 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包下载超时
///error = [self createError:IMSOTAMeshFailCodeTimeOut];
} else if (error.code == ALSOTAErrorTypeDownloadInfoMissing) {
///缺少固件信息
// error = [self createError:IMSOTAMeshFailCodeGetFirmwareError];
} else if (error.code == ALSOTAErrorTypePackageMissing) {
///云端没有相应固件包
/// error = [self createError:IMSOTAMeshFailCodeGetFirmwareError];
} else {
////未知错误
/// error = [self createError:IMSOTAMeshFailCodeUNKNOWN];
}
////OTA包下载过程中,发现失败
} else if (isFinished) {
////开始OTA包升级
[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) {
///OTA 过程超时
//error = [self createError:IMSOTAMeshFailCodeTimeOut];
} else if(error.code == ALSOTAErrorDisconnect) {
///OTA 过程连接断开
//error = [self createError:IMSOTAMeshFailCodeDisconnect];
} else if(error.code == ALSOTAErrorCheckFailed) {
/// OTA 固件检查失败
//error = [self createError:IMSOTAMeshFailCodeCheckFirmwareFail];
} else if(error.code == ALSOTAErrorTypePackageMissing) {
///OTA找不到固件包
//error = [self createError:IMSOTAMeshFailCodeGetFirmwareError];
} else if(error.code == ALSOTAErrorRefused) {
///设备拒绝升级
//error = [self createError:IMSOTAMeshFailCodeDeviceRefused];
} else if(error.code == ALSOTAErrorIsOTAing) {
///已经有一个OTA正在进行
//error = [self createError:IMSOTAMeshFailCodeIsDoing];
} else {
////未知错误
// error = [self createError:IMSOTAMeshFailCodeUNKNOWN];
}
////真正OTA过程时,发现失败,需要上报失败
[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(), ^{
////OTA成功,业务层告知用户
});
}
}
}
下载升级固件
在获取到设备的固件版本号,可以开始下载OTA包。方法入参需要依赖获取固件升级信息API接口。
- (void)startDownloadNewPackage {
IMSOTALogInfo(@"startDownloadNewPackage");
if (!self.bleDevice) {
///NSError *error = [self createError:IMSOTAMeshFailCodeNotConnect];
////通知OTA失败
return;
} else if (!(self.otaURL && self.md5)) {
///otaURL, md5,是调用云端api查询设备有待升级包时会返回.如果没有的话,无法OTA
return;
}
///OTA包的保存地址
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory,NSUserDomainMask,YES);
NSString *otaPath = [paths.firstObject stringByAppendingPathComponent:@"ble_ota"];
BOOL createPathOk = YES;
if (![[NSFileManager defaultManager] fileExistsAtPath:otaPath isDirectory:&createPathOk]) {
// 目录不存在先创建
[[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];
}
参数说明
名称 | 类型 | 必填 | 默认值 | 描述 |
otaURL | NSString * | 是 | ota包下载地址。 | |
md5 | NSString * | 是 | ota包的MD5值,下载完成后会做校验。 | |
updateVersion | NSString * | 是 | 要升级的OTA包版本。 |
以上参数,均可以在请求云端API/living/ota/firmware/file/get
获取 。请参见获取设备固件信息。
开始升级设备
在OTA包下载完成后,进入真正OTA包更新流程。
[self.bleDevice startOtaUpgrade];
停止升级设备
- (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:停止下载,停止OTA
}
文档内容是否对您有帮助?