iosmfi开发,iosmdm

iOS MFi外设配套App音频通讯实现小结

最近在做一款外设,搭配一款App,App中实现很多功能,集成了一套语音识别SDK,通过外设给App传输音频,通过SDK解析出具体的指令,去执行App中相关的功能。整体功能是这样,在iOS系统中,以上功能中有几个难点,很难解决或者是无法解决,只能避开。笼统的分以下几个点:

站在用户的角度思考问题,与客户深入沟通,找到宜春网站设计与宜春网站推广的解决方案,凭借多年的经验,让设计与互联网技术结合,创造个性化、用户体验好的作品,建站类型包括:网站建设、做网站、企业官网、英文网站、手机端网站、网站推广、域名申请雅安服务器托管、企业邮箱。业务覆盖宜春地区。

1).App在后台,外设能否拉起App

2).App被拉起之后,能否进行执行语音指令

3).App在后台,能否接收音频流,执行语音指令

上面这个问题是整个流程中最头疼的问题

1.首先要定义外设的形态,这里的外设可以是耳机,稍微变形一下可以是车载,录音笔,音响等。这里的外设就用耳机来代表。外设传输音频到App,无线场景下大部分都是采用蓝牙,蓝牙又分MFI认证的蓝牙设备和ble设备。

首先要说的是MFi认证的蓝牙设备,这类设备使用iAP协议同iPhone, iPad and iPod进行数据交互,传输性能好,稳定,但是成本比较大,设备需要苹果的官方检测很麻烦。当时查了一些资料,MFi认证的设备似乎是能解决上面说的几个问题。如图1所示,MFi认证的设备跟App商定好协议之后就能通过语音拉起App,但是这里有个坑,那段话我找不到了,意思是如果是usb模式的外设是可以直接拉起App,但是如果非usb模式的,拉起之前会出一个弹框,看图1 我们都以为这个alert是可选的,其实并不是,在某些模式下,必须是带alert的,如果拉起App需要Alert,让用户去确认,那也就没必要通过语音唤醒了,直接点击App好了,所以这条路基本就不考虑了。

但是外设是可以拉起Siri的,这个是固件需要支持的。这又为App打开了一条路,外设可以通过Siri打开App,这条路可行。但是Siri Shortcuts是iOS12之后才支持的。考虑低版本暂时没有想到好的解决方案。

苹果针对iap设备有一个background mode, 如图2 中的External accessory communication。设置了这个mode之后,在用户不主动杀死App,或者是系统不因为内存占用太高而杀死App,App理论上会一直在后台“活着”的,针对这个“活着”又是另一个话题了,这里的活着是假活的,也就是只能接收到来自外设传输的信息,所有其他的网络请求啥的都会被系统停止,在我的另一篇文章 iOS 后台机制探索 中讲到了这个,所以在后台这个模式只是给我们提供了一个入口,而且在接收到外设信息之后,系统只有10几秒的时候给App进行处理,具体多少秒,我没有实测,我在接收到消息之后,重连App中的socket连接进行数据发送这些操作都是能正常完成的。

所以总结以下,在通过MFi认证,使用iap进行数据交互的设备在我们这边的产品来看,能拉起App,但是拉起会出一个alert弹框 让用户确认,所以无法做到无用户参与的拉起App,如果想要通过外设去执行系统级别的操作,可以让外设接入Siri,可以通过Siri去打电话,接电话,查询天气等,同时在iOS12的系统上可以通过Siri打开应用。自家的App在设置了External accessory communication的background mode之后,能一定程度上保证App不被杀死,但是异常情况不可控,如果是iOS12 以上的系统可以通过Siri打开,但是iOS12以下的系统还是需要手动点击,先把App拉活了之后 再通过语音进行指令控制。但是就算设置了background mode之后,App也只能接收到来自iap的数据,无法完全保证所有的socket连接都不断,在每次接收到来自外设的数据时,还是需要本地做重连机制。

通过iap进行数据交互无论是产品定义还是功能实现都比较简单,因为苹果提供了很完整的文档,能实现啥不能实现啥都清清楚楚的展示出来了,而且iap数据传输带宽高,稳定性强,官方提供的demo直接就能用,需要App开发者要配置的东西很少,大部分的难点都在固件端,所以开发起来除了有后台的问题不好解决,其他都很愉快,但是如果是用ble进行音频传输就会有很多坑,下面一篇文章就是要说的不用iap进行这块的数据传输,改用ble的实现流程。

ios蓝牙时通过什么协议实现的

首先,你要了解你的目的是什么,一般的IOS蓝牙开发有以下三种目的:

1. IOS设备和IOS设备之间交互

好消息是:ios6.0可以把iPhone手机当从设备了,可以两台iPhone通过蓝牙通信传数据了,有点类似spp协议

坏消息是:我们需要的不是这种模式

这种模式通常用于两个使用IOS设备的土豪之间互相切磋游戏,玩个飞车什么的,需要注意的是

2. IOS设备与MFI认证设备交互

什么是MFI认证呢?意思是(Make For ipod/ipad/iphone),只有少数的硬件厂商才有苹果的MFI认证,如果你看到这,你的蓝牙设备还没设计,而且想发布在AppStore上,而且打算使用蓝牙4.0以下(4.0就不需要MFI了,福音),那你还是找个MFI认证的硬件开始做吧,我们公司已经有产品了,不可能重新设计,方案Pass了。

好消息是:如果你的蓝牙模块还没设计,打算在AppStore上发布,打算使用蓝牙4.0以下(IOS设备都兼容),那你就抓紧找MFI认证的蓝牙模块吧

坏消息是:我们不可能重新设计,所以舍弃

3. IOS设备与非IOS设备交互

这就是我们的现状了,根据这个现状,我们能分析出两种情况:

a. 我想做蓝牙4.0以下的,这样iphone4也能用了

好消息:确实可以兼容iphone4,但是由于苹果封闭,没有那个API给你调,苹果上也根本检索不到非IOS设备,所以你就需要越狱了,调用私有Api连接设备

坏消息:只有越狱的手机才能用,发布到AppStore是妄想

b. 我想做蓝牙4.0的,不全兼容也没事,我想发布在AppStore上

好消息:苹果开放了4.0的BLE通道,你可以用BLE通道通信,而且4.0设备也能检索到

坏消息:对IOS版本和IOS设备双重要求,肯定不能全兼容了

所以,根据你自己的实际情况,选择方案,现总结出以下几种方案:

1. 情景:蓝牙2.0,发布在AppStore上

答:使用MFI认证的蓝牙模块设计你的蓝牙产品

特点:不越狱就能用,IOS设备全兼容

2. 情景:蓝牙2.0,不用MFI

答:不能用苹果的Api了,使用私有Api连接设备

特点:只能越狱的手机才能用了,IOS设备全兼容

3. 情景:蓝牙4.0

答:使用BLE通道

特点:IOS设备不完全兼容,无需越狱,无需使用MFI,可发布在AppStore上

注意:一定要看你的蓝牙版本,这样才能选择方案,我们现在设备的版本是2.0,但是Boss想要发布在AppStore上,所以打算升级成4.0的,只有这么办了

iOS swift ble Bluetooth

1 前言

当前有越来越多的可穿戴设备使用了蓝牙4.0 BLE(Bluetooth Low Energy)。对于iOS开发而言,Apple之前专门推出CoreBluetooth的Framework来支持BLE的开发。对于硬件开发有了解的朋友应该知道,在之前使用低版本的蓝牙的设备,要连接到iOS设备上,需要注册MFI,拥有MFI协议才能进行相应的开发。如果大家关注我之前对LEGO EV3的研究,就可以发现,EV3是使用了蓝牙2.1,因此需要MFI协议来进行开发。

本文将一步一步讲解如何使用CoreBluetooth框架来与各种可穿戴设备进行通信,使用 小米手环 来进行基本的测试。

2 开发环境

1 Macbook Pro Mac OS X 10.10

2 Xcode 6.3.2

3 iPhone 5s v8.1

4 小米手环

3 基本流程

要开发蓝牙,需要对整个通讯过程有个基本了解。这里我摘录一些Apple官方的文档Core Bluetooth Programming Guide的图片来加以说明。这个文档其实对于开发的流程写的是非常的清楚,大家最好可以看一下。

3.1 可穿戴设备与iOS互联方式

从上面这幅图可以看到,我们的iOS设备是Central,用来接收数据和发送命令,而外设比如小米手环是Peripheral,向外传输数据和接收命令。我们要做的就是通过Central来连接Peripheral,然后实现数据的接收和控制指令的发送。在做到这一步之后,再根据具体的硬件,对接收到的数据进行parse解析。

3.2 可穿戴设备蓝牙的数据结构

这里用的是心率设备来做说明,每个外设Peripheral都有对应的服务Service,比如这里是心率Service。一个外设可以有不止一个s、Service。每个service里面可以有多个属性Characteristic,比如这里有两个Characteristic,一个是用来测量心率,一个是用来定位位置。

那么很关键的一点是每个Service,每个Characteristic都是用UUID来确定的。UUID就是每个Service或Characteristic的identifier。

大家可以在iPhone上下载LightBlue这个应用。可以在这里查看一些设备的UUID。

在实际使用中,我们都是要通过UUID来获取数据。这点非常重要。

在CoreBluetooth中,其具体的数据结构图如下:

4 Step-By-Step 上手BLE开发

4.1 Step 1 创建CBCentralManager

从名字上大家可以很清楚的知道,这个类是用来管理BLE的。我们也就是通过这个类来实现连接。

先创建一个:

@property (nonatomic,strong) CBCentralManager *centralManager;

dispatch_queue_t centralQueue = dispatch_queue_create("com.manmanlai", DISPATCH_QUEUE_SERIAL);

self.centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:centralQueue];

然后关键在于CBCentralManagerDelegate的使用。这个之后再讲。

4.2 Step 2 寻找CBPeripheral外设

有了CBCentralManager,接下来就是寻找CBPeripheral外设,方法很简单:

[self.centralManager scanForPeripheralsWithServices:@[] options:nil];

这里的Service就是对应的UUID,如果为空,这scan所有service。

4.3 Step 3 连接CBPeripheral

在上一步中,如果找到了设备,则CBCentralManager的delegate会调用下面的方法:

- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI

{

NSLog(@"name:%@",peripheral);

if (!peripheral || !peripheral.name || ([peripheral.name isEqualToString:@""])) {

return;

}

if (!self.peripheral || (self.peripheral.state == CBPeripheralStateDisconnected)) {

self.peripheral = peripheral;

self.peripheral.delegate = self;

NSLog(@"connect peripheral");

[self.centralManager connectPeripheral:peripheral options:nil];

}

}

我们在这里创建了一个CBPeripheral的对象,然后直接连接

CBPeripheral的对象也需要设置delegate.

4.4 Step 4 寻找Service

如果Peripheral连接成功的话,就会调用delegate的方法:

- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral

{

if (!peripheral) {

return;

}

[self.centralManager stopScan];

NSLog(@"peripheral did connect");

[self.peripheral discoverServices:nil];

}

我们这里先停止Scan,然后让Peripheral外设寻找其Service。

4.5 Step 5 寻找Characteristic

找到Service后会调用下面的方法:

- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error

{

NSArray *services = nil;

if (peripheral != self.peripheral) {

NSLog(@"Wrong Peripheral.\n");

return ;

}

if (error != nil) {

NSLog(@"Error %@\n", error);

return ;

}

services = [peripheral services];

if (!services || ![services count]) {

NSLog(@"No Services");

return ;

}

for (CBService *service in services) {

NSLog(@"service:%@",service.UUID);

[peripheral discoverCharacteristics:nil forService:service];

}

}

我们根据找到的service寻找其对应的Characteristic。

4.6 Step 6 找到Characteristic后读取数据

找到Characteristic后会调用下面的delegate方法:

- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error

{

NSLog(@"characteristics:%@",[service characteristics]);

NSArray *characteristics = [service characteristics];

if (peripheral != self.peripheral) {

NSLog(@"Wrong Peripheral.\n");

return ;

}

if (error != nil) {

NSLog(@"Error %@\n", error);

return ;

}

self.characteristic = [characteristics firstObject];

//[self.peripheral readValueForCharacteristic:self.characteristic];

[self.peripheral setNotifyValue:YES forCharacteristic:self.characteristic];

这里我们可以使用readValueForCharacteristic:来读取数据。如果数据是不断更新的,则可以使用setNotifyValue:forCharacteristic:来实现只要有新数据,就获取。

4.7 Step 7 处理数据

读到数据后会调用delegate方法:

- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error

{

NSData *data = characteristic.value;

// Parse data ...

}

4.8 Step 8 向设备写数据

这个很简单,只要使用:

[self.peripheral writeValue:data forCharacteristic:self.characteristic type:CBCharacteristicWriteWithResponse];

data是NSData类型。


本文名称:iosmfi开发,iosmdm
文章位置:http://azwzsj.com/article/hosshj.html