iOS App 连接外设的几种方式

来源:互联网 发布:儿童围棋软件 编辑:程序博客网 时间:2024/04/27 02:05

原创作者: Max_Marry

文章地址: http://www.jianshu.com/p/852bf92c5c92

随着近年来车联网和物联网的兴起,智能家居和智能硬件的逐步火热,越来越多的 App 被用来跟硬件设备进行来连接,获取硬件相关信息用以展示或者发送指令控制硬件来提供服务。

iOS App 连接外设的常用方式可以分为三大类:

网络端口

建立 Socket 使用 TCP/IP 协议族进行通信,天然支持多通道,想要几个通道就建几个 Socket 就可以。它主要有三种方式,

Wi-Fi 连接

优点:

  • 简单,不需要集成 MFi 芯片

  • 只要对应的硬件有无线网卡,然后将手机和硬件连接到同一个局域网中就可以使用 Socket 通过网络协议通信

缺点:

  • 无线连接信号容易受到干扰,不太稳定,容易断开

  • 如果硬件使用的场合没有公共 Wi-Fi,就需要手机自建热点共享,硬件进行热点接入,操作步骤较多,对用户来说学习使用成本较高,并且热点共享要求手机本身的数据移动网络是稳定的,在没有移动数据网络信号的地方,热点无法建立。

USB 热点共享

这个其实跟 Wi-Fi 中的热点共享非常类似,也不需要集成 MFi 芯片。

优点:

  • USB 热点共享,走的是有线,不容易受到干扰,更稳定,

  • iPhone 可以边使用可以边充电

缺点:

  • 操作步骤比较复杂,需要先打开个人热点共享

NCM

就是把 USB 端口虚拟成标准的网络端口,然后手机和外设就能通过有线网络直连了,可以理解成手机和外设通过一跟网线连起来了,然后就可以用 Socket 通过 TCP,UDP 进行通信了。

优点:

  • 有线连接,非常稳定,带宽足够

  • 不依赖移动网络信号

缺点:

  • 需要集成 MFi 芯片并进行 MFi 认证,有一定门槛

  • 除了 CarPlay,其他跟 iPhone 连接的外设都不能使用 NCM 的方式跟 iPhone 上的 App 进行连接和通信

EAP

EAP 全拼是 External Accessory Protocol,即外部设备协议。这个是苹果推荐使用的外设连接方式。需要外设集成 MFi 芯片进行 MFi 认证。手机端开发相对简单,只要集成 iOS 系统提供的一个框架 ExternalAccessory.framework,并且在 info.plist 中配置好协议字符串 (Supported external accessory protocols),当 iOS 设备通过 USB 或者蓝牙连接到对应硬件时,iOS 系统会把符合 MFi 认证要求的外设抽象成了一个流对象,App 通过指定的协议字符串来创建一个 EASession 类的实例来访问到该流对象,就能通过 NSInputStream 和 NSOutputStream 跟硬件件进行通信了。

  • EASession 模式:它的带宽相对较低,但是允许同时通过多个协议字符串创建多个会话,也就是说直接支持多通道

  • Native Transport 模式:它的带宽足够大,理论值是 100MB 以上,但是不支持多通道,如果业务层需要支持多数据通道的话需要 App 自己进行通道的复用与拆分,并且 Native Transport 需要 iPhone 工作在 USB Host 模式,硬件需要支持 USB 模式切换

关于如何使用 EAP 跟外部设备进行通信,可以参考苹果官方的 Demo (https://developer.apple.com/library/content/samplecode/EADemo/Introduction/Intro.html)进行入门和学习。

BLE

BLE 即低功耗蓝牙,是 iOS7.0 以后才支持的连接方式。

  • 优点:不需要集成 MFi 芯片做认证,功耗低,手机端开发也相对简单,集成 iOS 系统提供的 CoreBluetooth.framework 即可

  • 缺点:带宽很低,一般适合于只需要传输少量数据的场景。比如各种智能硬件,像智能水杯,智能体重计,运动手环等,都是采用这种连接方式

总结一下,图中带 MFi 字样的表示该连接方式需要硬件集成 MFi 芯片,做 MFi 认证。关于苹果的 MFi 认证,对 iOS 开发中来说其实是一个比较陌生并且繁琐的 Topic,原因如下:

  • 网上鲜有资料,Google 基本上查不到。 因为 MFi 认证是由硬件生产商主导进行的,苹果首先对硬件生产商的实力 (质量,信誉,生产规模) 有很苛刻的要求,满足要求的才有进行 MFi 认证的资格。满足 MFi 认证资格要求的硬件生产商,提交了 MFi 产品计划后才能得到苹果 MFi 开发的官方文档,这个文档是带水印的,不允许外泄

  • MFi认证周期很长,过程也很复杂

  • 苹果官方沟通渠道很窄,电话打不通,邮件回复不及时

关于 MFi 申请的一些事情

什么是 MFi 认证?

苹果 MFi 认证,是苹果公司(Apple Inc.)对其授权配件厂商生产的外置配件的一种标识使用许可,是 Apple 公司 “Made for iOS” 的英文缩写。

为什么要做 MFi 认证?

  • 从苹果角度来看,为了更好的巩固苹果的生态圈,只有集成了有
    MFi 芯片,才能跟 iPhone、iPod,iPad 进行连接通信。而只有经过了 MFi 认证的企业才能批量购买 MFi 芯片,并且都 MFi 芯片的供销链条都有很严格的监督管理,所以这样苹果可以严格控制只有那些满足苹果规范和要求的外设才能加入到苹果生态圈。

  • 从生产厂商来看,经过苹果官方授权,配件产品能完美兼容苹果智能设备。提交 MFi 认证过程中,硬件设备需要经过苹果要求的
    ATS 自测以及苹果的严格测试,产品质量更有保证。消费者也更加信任经过了 MFi 认证授权的配件;最后成功获得 MFi 授权这也成为技术与质量实力的一种标志,因为 MFi 认证通过率仅 2%,其中大部分企业因为申请资格不符合直接被拒绝。

  • 从 iOS 开发人员来看,MFi 认证是由硬件生产商主导进行申请的,是苹果对外设配件的一种认证和授权。但是很多外设跟苹果进行连接,并不只是跟 iOS 设备硬件或者 iOS 系统配合就可以完成对应的功能 (比如充电、CarPlay、播放 iPod 音乐 (A2DP)、接听蓝牙电话 (HPF) 或者提供 GPS 输入源等)。很多时候为了实现特定的需求,需要由 iOS App 的配合,由 iOS App 跟对应外设进行连接和通信,传输相关的控制命令对外设进行控制,或者传输相关的外设数据进行展示。iOS App 跟外设的连接方式有网络、EAP 和 BLE,其中 EAP 是苹果官方推荐的跟外设连接的方式。只有经过 MFi 认证的外设才能使用 EAP 跟 App 进行通信。

如何做 MFi 认证?

MFi 认证的流程比较复杂,可以归纳总结为三个部分,如下图所示。

其中黄色背景标注的部分是可能跟 iOS App 开发者相关的。

一、申请人提交申请资料

首先,收集公司资料信息,这些资料主要包括了认证负责人联系信息,企业情况介绍,公司组织架构、企业网站,物料品质控制以及 ISO 体系证书等资料。然后是在苹果 MFi 官网(mfi.apple.com)上进行注册,并提交第一步收集到的公司资料,进行账号申请。

接下来苹果会进行 MFi 体系审核。这个是非常关键的一个步骤。 主要考察公司对 MFi 芯片的管理体系,看公司是否有规范的流程和系统来管理 MFi 芯片,能有效防止转售芯片或者挪用芯片 (把芯片用到未通过 MFi 认知的项目上),苹果会安排专人或者代理公司来抽查。

如果 MFi 体系审核过了,苹果还会对公司其他情况进行考察,来评估该公司是否满足 MFi 会员的资格。审核的标准主要看公司相关资质,是否有较大的生产规模;是否拥有自主品牌;品牌在业内是否有较高的地位 (主要表现为各类荣誉);是否曾为其他国际知名企业供货;研发人员是否达到苹果要求的人数等,申请者一定保证申报资料的真实性,苹果公司都会一一核实。

如果这些条件都满足,恭喜你公司成为了 MFi 会员,能够有资格购买样品芯片,并且拿到苹果提供的 MFi 官方开发文档,该文档的每一页都是带有申请人姓名水印的,禁止对外公开,如果被发现,有可能会被取消 MFi 会员资格。据说大部分的企业都会被卡在会员资格审核这一步。

二、提交产品计划,研发和自测

如果你的公司是属于那幸运的那一小部分通过了 MFi 会员资格审核,拿到了苹果的 MFi 研发官方文档,也购买了 MFi 样品芯片,那么就可以提交产品计划,进行产品研发和自测了。

提交产品计划是非常关键的一步,需要根据要研发的公司产品的形态、所用技术方案和需要支持的 iOS 设备、iOS 的相关信息都进行详细的描述,其中比较重要信息有。

(1) 附件概览 (Accessory Overview)

技术方案 (Technology) 如果你是做支持 CapPlay 的车机,那么就选择 CarPlay,否则都应该选择 iAP;如果你的硬件需要跟 iPhone 连接,并且处理相关业务,而不仅仅是充电线或者数据线,那么在 Components 里应该选择 Authentication coprocessor。

(2) 固件和硬件 (Firmware & Hardware)


现在所有的 MFi 认证的硬件都需要支持 iAP2 协议,所以必须要选
iAP2 或者同时支持 iAP2 和 iAP1。然后外设硬件跟苹果设备是如何通信的,是使用 USB 的 Host 模式,还 USB 的 Devices 模式,还是串口或者蓝牙,这个需要根据产品的需求、特性进行选择。

(3) 选择硬件所支持的 iAP2 的特性

(4) 选择所支持的苹果设备型号

根据产品的设计选择所需要支持的苹果设备型号,包括 iPad,iPhone 和 iPod 的各种型号。

(5) App 相关的信息

这部分也是 iOS 开发者需要重点关注的部分,包括 App 的版本号,BundleID 和协议字符串以及 iOS App 的主要功能特性描述,这部分信息需要跟最后送 MFi 审核时附带的 App 测试包的信息保持一致。提交了产品计划之后,就可以拿到 PPID (Product Plan ID)。这个 PPID 也是跟 iOS App 开发者需要关注的。当 App 开发完成,提交 AppStore 上线时,需要在版本审核备注信息里带上这个 PPID,否则审核是过不了的。

接下来就可以进行产品研发了。主要是硬件生成商需要根据苹果提供的开发文档进行硬件和驱动认证程序的开发。而 iOS App 开发者则主要是需要成 iOS 系统提供的一个系统框架 ExternalAccessory.framework,并且在 info.plist 中配置好协议字符串 (Supported external accessory protocols)。当 iOS 设备通过
USB 线或者蓝牙连接到对应硬件时,iOS 系统会把符合 MFi 认证要求的外设抽象成了一个流对象,App 通过指定的协议字符串来创建一个 EASession 类的实例来访问到该流对象,就能通过
NSInputStream 和 NSOutputStream 跟硬件件进行通信了。这部分功能实现可以参考苹果官方的 EADemo( https://developer.apple.com/library/content/samplecode/EADemo/Introduction/Intro.html )进行入门和学习。

产品研发完成后需要进行 ATS (Accessory Test System) 自测,并提供自测报告。ATS 自测苹果会提供 ATS Box 的测试工具和软件,主要是针对硬件进行电气特性相关的测试,包括各个节点的电压电流值是否满足苹果要求,然后传输带宽是否稳定,是否达到苹果要求等等。

自测完成之后就可以把硬件和所配套的软件 (iOS App 的 ipa 安装包) 送到苹果指定的测试实验室进行认证测试。iOS 开发者在这个步骤需要关注的是如何打包 ipa 包。因为如果直接用开发证书打包,那么苹果测试人员的 iPhone 不在你开发证书的设备列表中,是无法安装的。如果用企业证书打包的话,可能 AppStore 发布证书对应的 BundleID 跟企业证书的 BundleID 不一致,所以也不可行。所以推荐的做法是,等到产品研发完成和自测之后,就带上产品计划中拿到的 PPID,提交 AppStore 进行审核。等审核通过之后,就可以直接从 AppStore 下载对应的 ipa 安装包,配合硬件一起送 MFi 认证测试了。

三、测试审核和批量生产

这个阶段也是硬件生产商主导进行的,跟 iOS App 开发者关系不大。当硬件的 MFi 认证送审通过之后,还需要对产品的包装也提交认证和审核。审核通过之后,就可以获得苹果授权进行 MFi 芯片的批量购买,然后根据销售计划进行硬件的批量生产和销售了。

整个 MFi 认证的周期大概需要3个月到半年的时间,并且每次提交认证测试都需要支付一笔600美金的测试费用,所寄去测试的硬件测试样品苹果也是不会寄回来的。

iOS 外设连接黑科技,不需要 MFi 认证,实现 USB 连接

USBMuxd,利用这种连接方式不需要做 MFi 认证,支持 iPhone 上的 App 跟外设通过进行通信,非常方便。

基本原理 
iPhone 的 iOS 系统中自带了 USBMuxd 服务,该服务能够实现
USB-TCP 协议的转换,能够把 USB 的端口映射到本机 (localhost) 的 TCP 端 (基于 Unix Domain Socket )。只需要在外设端也实现一个 USBMuxd 服务,并指定端口映射关系, 那
iPhone 的 App 和外设上的应用就可以使用 Socket 进行 TCP 进行通信了。

一个开源实例
开源项目 peertalk( https://github.com/rsms/peertalk ) 是一个完整的使用 USBMuxd 方式实现 iPhone App 跟 Mac App 进行 TCP 通信的例子。因为 Mac OS 系统中天生就自带了 USBMuxd 服务,所以 peertalk 的 Mac 端程序是比较简单的。外设一般都不会是 Mac 系统,而是 Android 或者 Linux 系统,那怎么办呢?那就自己在系统中集成 USBMuxd 服务,这里可以利用到 libimobiledevice( https://github.com/libimobiledevice ) 实现在外设上集成 USBMuxd 服务。

MFi iOS App 端开发步骤

与附件设备通讯的 App 需设备支持的协议,这些协议由设备制造商维护,可是自定义或标准协议,标准协议可与其他设备通信,iOS不负责这些协议的维护。

为防止命名空间冲突,推荐使用反 DNS 形式命名协议,如
com.apple.myProtocol、com.dji.video、com.dji.protocol、com.dji.common。

1、编程步骤

1.1、引入框架与头文件

外部附件框架 (ExternalAccessory.framework) 为 App 与附件设备通信提供了桥梁。因此,在 Xcode 项目中,需要为每个与附件设备通信相关的项目添加 ExternalAccessory.framework。

下一步是引入头文件 #import <ExternalAccessory/ExternalAccessory.h>

1.2、声明App支持的协议

不声明协议直接调用EA框架的类会崩溃。
使用 UISupportedExternalAccessoryProtocols 键在 Info.plist 中声明支持的协议,值为数组,数组的元素为支持的协议,元素的顺序任意且不限数量。这些值只用于判断 App 与附件设备的通信能力。当App与设备通信时,具体通信协议由我们编程决定。

当附件设备插入 iOS 设备时,系统才知道 App 可被新插入的设备启动。若当前已安装的 App 都没注册协议,则系统可能到 App Store 去搜索支持新设备声明的协议的 App。

UISupportedExternalAccessoryProtocols 对应的值虽说可参考UISupportedExternalAccessoryProtocols( https://developer.apple.com/library/content/documentation/General/Reference/InfoPlistKeyReference/Articles/iPhoneOSKeys.html#//apple_ref/doc/uid/TP40009252-SW4 ),实际此链接并没给出有帮助的信息,《MFi Accessory Interface Specification for Apple Devices》也没给出所谓标准协议的字符串值。

1.3、开始通信

  1. 创建 EASession。此对象管理与附件设备交互的情况,它与底层系统工作,在设备上来回传输数据。一旦会话建立,数据通过
    NSInputStream 和 NSOutputStream 的实例在 App 中传输。收发的数据包的格式由与附件设备通信的协议决定。

  2. 接收数据。使用自定义委托对象,监视 NSInputStream 实例可从附件设备接收数据。

  3. 发送数据。向 NSOutputStream 写入数据包即可发送至附件设备。

1.4、一个读取外接设备的示例

  1. Info.plist 中加入 Supported external accessory protocols,值为 com.apple.p1。这个只是令系统认为我们的应用有能力与外接设备沟通,这里使用 Lightning USB Camera Adapter 测试。

  2. 读取外接设备信息代码:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

 NSMutableString *info = [[NSMutableString alloc] initWithCapacity:1024];


 EAAccessoryManager *manager = [EAAccessoryManager sharedAccessoryManager];


 NSArray<EAAccessory *> *accessArr = [manager connectedAccessories];


 for (EAAccessory *access in accessArr) {

     for (NSString *proStr in access.protocolStrings) {

         [info appendFormat:@"protocolString = %@\n", proStr];

     }


     [info appendFormat:@"\n"];

     [info appendFormat:@"manufacturer = %@\n", access.manufacturer];

     [info appendFormat:@"name = %@\n", access.name];

     [info appendFormat:@"modelNumber = %@\n", access.modelNumber];

     [info appendFormat:@"serialNumber = %@\n", access.serialNumber];

     [info appendFormat:@"firmwareRevision = %@\n", access.firmwareRevision];

     [info appendFormat:@"hardwareRevision = %@\n", access.hardwareRevision];

     [info appendFormat:@"dockType = %@\n", access.dockType];

 }


 dispatch_async(dispatch_get_main_queue(), ^{

     label.text = info;

 });

});


运行结果为:

manufacturer = Apple

name = Apple USB Camera Adapter

modelNumber = A1440

serialNumber = 

firmwareRevision = 1.0.0

hardwareRevision = 1.0.0

dockType = (null)


1.5、与外接设备交互数据的示例

如下代码展示与 DXO One 相机通信。

  1. 在非 UI 线程中打开设备,否则可能导致程序崩溃。

EAAccessoryManager *manager = [EAAccessoryManager sharedAccessoryManager];


NSArray<EAAccessory *> *accessArr = [manager connectedAccessories];


if (accessArr.first) {

  EASession *session = [[EASession alloc] initWithAccessory:accessArr.firstObject forProtocol:@"com.dxo.one.protocol"];


  if (!session) return;


  NSInputStream *inputStream = [session inputStream];


  if (!inputStream) {

    // LOG inputStream = null

  }


  inputStream.delegate = self;


  [inputStream open];

}


  1. 实现 NSStreamDelegate 协议

- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode {

   // LOG stream & event code

    switch (eventCode) {

        case NSStreamEventNone:

            break;

        case NSStreamEventOpenCompleted:

            // 开始读取

            break;

        case NSStreamEventHasBytesAvailable:

            // 获取可读数据大小,读取流才有效。

            break;

        case NSStreamEventHasSpaceAvailable:

            // 获取可写空间大小,写入流才有效。

            break;

        case NSStreamEventErrorOccurred:

            // 出错处理

            break;

        case NSStreamEventEndEncountered:

            // 读取结束

            break;

    }

}


2、ExternalAccessory框架

1、EAAccessory 
提供一个已连接的设备的信息,如制造商,固件版本等。

2、EAAccessoryManager 
协调MFi设备与iOS设备之间的工作。

3、EASession 
用来创建 App 与附件设备之间的通信通道。

4、EAWiFiUnconfiguredAccessory 
提供未配置的 MFi Wireless Accessory Configuration 设备的信息给 App。

5、EAWiFiUnconfiguredAccessoryBrowser 
让 App 访问 MFi Wireless Accessory Configuration 进程。

3、开发技巧

Lightning 接了设备则不能连接计算机,所以直观的做法是,将日志用 UITextView 显示出来。写成日志就得每次都拔掉设备,插上电脑,如此反复。

另一个办法是,通过蓝牙测试传输协议,手机连接电脑,可单步调试。验证完再用 Lightning 连接设备联调。

因本人目前在做关于 iOS App 连接外设方面的工作,因此搜集查找相关信息,文章中信息出自于以下简友:我是云峰小罗、熊皮皮,在此对两位的分享表示感谢。