iOS之蓝牙开发技术


随说 : 本文结合自己做的这个项目来整理开发思路,这一篇着重 <蓝牙> 交互模块
前期准备 :

  1. 硬件商家提供的电路板
  2. 硬件商家提供的电路板通讯协议

看懂通讯协议

我模拟了一份通讯协议的资料,凑合着看吧
当然,实际情况的指令会多的多

lanyaxieyi

序号 : 指令的序号(没啥意义)
指令名称 : 就是给指令定义一个名字,一般可以作为储存 本地的Key
指令 : 实际操作电脑版接收的数据
验证指令 : 根据一个验证算法得出的数据.
备注 : 告诉你这个是干嘛的.

连接设备

将电路板通电,然后它就处于可被检测状态.
作为App来说,首先需要检测iphone是否处于蓝牙开启状态

1、创建Central管理器

1
2
3
4
5
6
7
8
9
10
// 创建Central管理器,这里我弄成单例
+(LinBlueToothEngine *)shareInstance
{
static dispatch_once_t pred = 0;
__strong static LinBlueToothEngine *_sharedObject = nil;
dispatch_once(&pred, ^{
_sharedObject = [[LinBlueToothEngine alloc] init];
});
return _sharedObject;
}

2、检测蓝牙状态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 当创建Central管理器成功后,系统会自动调用该方法
// 该方法的作用是检测蓝牙开启状态
- (void)centralManagerDidUpdateState:(CBCentralManager *)central
{

// 具体又几种状态的,这里忽略
switch ([central state])
{
case CBCentralManagerStatePoweredOn:
_state = @"正常打开了";
_bluetoothPowerOn = YES;
// 注意这句, 调用CoreBluetooth框架 CBCentralManager类的
//scanForPeripheralsWithServices: options: 方法
[self.centralManager scanForPeripheralsWithServices:nil options:nil];
break;
case CBCentralManagerStateUnknown:
default:
;
}
NSLog(@"蓝牙此时状态: %@", _state);
}

确保蓝牙打开,才能扫描设备

3、扫描设备

1
2
3
4
5
6
7
8
9
//搜索成功的回调
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI{ //BLUETOOTH_DEVICE_NAME 为设备名的宏定义
if ([peripheral.name isEqualToString:BLUETOOTH_DEVICE_NAME]) {
// 保存peripheral
self.deviceModel.peripheral = peripheral;
//执行搜索成功的block
!self.scanningToAroundYES ? : self.scanningToAroundYES(peripheral.name);
}
}

4、连接蓝牙设备

1
2
3
4
//连接蓝牙设备
-(void)startconnectService{
[self.centralManager connectPeripheral:self.deviceModel.peripheral options:@{CBConnectPeripheralOptionNotifyOnConnectionKey :@YES}];
}

5、连接成功

1
2
3
4
5
6
7
8
9
//外设连接成功
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral{
if ([peripheral.name isEqualToString:BLUETOOTH_DEVICE_NAME]) {
if (peripheral == self.deviceModel.peripheral) {
self.deviceModel.peripheral.delegate = self;
[self.deviceModel.peripheral discoverServices:nil];
}
}
}

6、发现外设

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//发现外设的service
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error{
if (error){
!self.connentionFailure ? :self.connentionFailure();
return;
}
if ([peripheral.name isEqualToString:BLUETOOTH_DEVICE_NAME]){
for (CBService *service in peripheral.services){
if ([service.UUID isEqual:[CBUUID UUIDWithString:BLUETOOTH_CBUUID]]){
[peripheral discoverCharacteristics:nil forService:service];
}
}
}
}

7、发现service的特征

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 外设发现service的特征
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error{
if (error) {
return;
}
if ([peripheral.name isEqualToString:BLUETOOTH_DEVICE_NAME]){
if (peripheral == self.deviceModel.peripheral) {
for (CBCharacteristic *characteristic in service.characteristics) {
if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:WRITE_CHARACTERISTIC]]) {
NSLog(@"写入特征");
self.deviceModel.characteristcs = characteristic;
}else if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:READ_CHARACTERISTIC]]){
NSLog(@"通知特征");
[self.deviceModel.peripheral setNotifyValue:YES forCharacteristic:characteristic];
}
}
}
!self.connectionSuccess ? : self.connectionSuccess ();
}
}

8、传输数据

characteristic 有几种属性
Read : 只读
Write Without Response : 写数据不接收回执
Write : 只写
Notify : 通知(不需要对方回复)
Indicate : 通知(需要回复)

这个项目中,只用到了write 与 notify

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//发送指令
- (IBAction)sendCommand:(id)sender {
NSString *command = @"02******";
//将16进制的字符串转16位Data
NSData *data = [command stringHexToBytesData];
[self.blueToothEngine sendWriteData:data];
}

- (void)sendWriteData:(NSData *)data{
[self.deviceModel.peripheral writeValue:data forCharacteristic:self.deviceModel.characteristcs type:CBCharacteristicWriteWithResponse];
}

//接收电路板回来的信息
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{
if (error) {
return;
}
if ([peripheral.name isEqualToString:BLUETOOTH_DEVICE_NAME]) {
if (peripheral == self.deviceModel.peripheral) {
//解析数据
NSString *resultString = [[NSString stringWithHexData:characteristic.value] stringUpperCase];
//将数据返回页面
!self.dataReportingBluetooth ? : self.dataReportingBluetooth(resultString);
}
}
}

蓝牙硬件版本升级

最近给产品做了DFU升级
其实DFU升级是app端和驱动这边共同完成的,这个协议是自己定义的,所以每个公司的升级都不一样,但从根本上看都是一样的,就是把升级包发给固件,然后进行升级,下面就来简单的说下我们这边的步骤。

1、发送升级指令

就是给固件发送一个指令,告诉固件,进入升级模式,当然在这之前有的公司会先发送一个查询固件的版本,然后对比当前版本和最新版,看是否需要升级,如果需要,再发送升级指令。这时,固件一般会断开蓝牙,切换成升级模式。

2、发送升级包

当固件切换完模式以后,需要重新连接蓝牙;这一步就是将升级包发送给固件了,由于蓝牙每次发送数据大小的限制,升级包一般都要分多次发送,每次发送一部分。

3、校验重启

当整个升级包都发送给固件以后,固件就会进行升级,升级完成之后固件就可以重启,把模式切换回正常使用模式了。对此,整个升级就完成了,还是前面说的,升级是和驱动这边共同完成的,双方定义如何升级,但总的都是获取升级包,发送给固件,固件进行升级这样。

注意:有的公司会把升级包放到服务器,也有的公司会在更新App时将固件升级包放到App里,毕竟固件升级的频率还是很低的,能不升级则尽量不要升级。

项目示例地址