1. Matter介绍
Matter(以前称为 Project Connected Home over IP 或 Project CHIP)是由CSA联盟制定的一个应用层面的标准,旨在打造一个统一的智能家居应用标准,以消除智能家居市场的碎片化。在Matter出来之前,智能家居市场是比较混乱的,每一个生态都有自己的协议,比如苹果有自己的 Homekit,Google有自己的Weave,亚马逊有自己的Echo,ZigBee联盟有自己的Zigbee,这种割裂让设备商很头疼,他们需要为不同的生态制造不同的设备,最终用户也很头疼,他们需要学习各种生态的智能家居设备的使用方式。为此CSA提出了Matter技术,以打造一个统一的标准,让一个设备可以在各个生态中工作,并给最终用户呈现统一的使用界面。
需要说明的是,Matter只是一个应用标准,它的传输是建立在支持IPv6的TCP和UDP协议上的,Matter不对传输层进行约定,Matter也不对网络进行约定,但是Matter约定了只能使用Thread/Wi-Fi/Ethernet三种连接协议。Thread协议由Thread Group制定,所以Matter over Thread产品必须通过Thread Group的认证。Wi-Fi则由Wi-Fi联盟进行规范,所以Matter over Wi-Fi产品必须通过Wi-Fi Alliance认证。
为了让Matter产品加入Matter网络,需要一个配网的过程,Matter支持三种类型的配网方式,而低功耗蓝牙则是首选配网方式,所以Matter产品一般都会要求支持低功耗蓝牙功能。
Matter技术包含了三份规格书及其他要求文档,大家可以从以下链接获取Matter技术规范:https://csa-iot.org/developer-resource/specifications-download-request/
其中Core specification是它的核心规范,Application cluster specification对Matter组件cluster进行了详细规定,Device library specification则是对设备层面的一些约束和规定。
不像蓝牙和Wi-Fi,即使你不过认证,也照样可以使用蓝牙和Wi-Fi技术。由于Matter采用了PKI技术,而且它又是一个应用规范,你要使用Matter技术,必须通过Matter认证。为此CSA提供了多种资源帮助大家学习了解Matter,大家可以参考如下资源网站:https://groups.csa-iot.org/wg/all-users/home/matter-resource-kit
Matter是一个非常复杂而又现代的标准,但是短短4年时间,Matter就实现了从提出到落地到追捧的成绩,不得不说这是一个“Matter奇迹”。下面我们一起来了解一下这个“奇迹”的更多细节。
1.1 Matter架构
如下展示了Matter协议栈在整个系统中的位置,可以看出Matter就是一个应用协议。
Matter协议栈本身又是由application, data model, interaction model, action framing, security, Message Framing and Routing和Transport and IP Framing组成,如下:
Application
Application层定义最终产品的业务逻辑。例如,对于门锁应用程序,业务逻辑可以根据来自特定语音命令来打开和关闭门锁。它还可以定义用PIN码开锁或者LED反应等。
Data Model
Data Model层用来描述Matter节点支持的远程操作,这里面主要运用了attributes, commands和events三个Matter概念,我们把相应的attributes,commands和events组合,就成了Matter设备里面的一个基石:cluster。Cluster本身是一个抽象的概念,用来定义一个一个的Matter设备,具体定义可参见之前的:Matter Application Cluster Specification。正因为有了cluster,才能让用户快速开发出自己的Matter产品,同时保证互联互通。
Interaction Model
Data Model层对数据进行了抽象,而Interaction Model层则用来定义节点与节点之间如何交换数据。通俗地讲,Interaction model就是用来规定交互命令集的,我们把发起交互的节点叫initiator (一般都是client设备),而接收者称为target (一般为server设备)。
Action Framing
Action Framing层用来把Interaction Model层的命令转成serialized格式。
Security
Security层把上面的数据进行加密并添加MAC。
Message Framing and Routing
这层主要把包头添加到数据中,组成一个真正的包。
Transport and IP Framing
这层把数据传输到对端,主要是利用TCP或者UDP协议,同时Matter定义了Message Reliability Protocol (MRP)协议,用来信息确认,重传和拒绝重复信息。如前所述,在配网(commissioning)过程中,通信是建立在Bluetooth Transport Protocol (BTP)协议之上。
1.2 Matter拓扑结构
Matter可以同时支持Wi-Fi/Thread/Ethernet,也就是说Matter可以让不同网络中的设备进行互联互通通信,这个主要是指Thread board router 可以实现Wi-Fi和Thread通信互转。不仅如此,Matter还允许接入其他网络设备,比如ZigBee设备,这主要通过一个Matter bridge设备来实现。在Matter拓扑结构中,还有一个节点非常重要:Matter controller,Matter controller用来完成配网和远程控制设备,比如苹果的HomePod mini和Home app就是一个典型的Matter controller节点。如下为一个典型的Matter拓扑结构:
一般来说,一个Matter网络称为一个Fabric,共享同一个根操作证书的所有节点可以归为同一个Fabric,简单来说,一个生态就是一个Fabric(当然也可以包含多个),比如家里同时有苹果Google亚马逊的音箱,那么你可以认为你家里有三个Matter fabric,每个fabric是独立的,但这里要强调的是,Matter支持一个设备接入多个fabric,也就是可以同时用苹果Google亚马逊控制同一个Matter设备,比如门锁。
1.3 Matter数据模型(Data Model)和设备类型
如下为一个典型的data model表示:
Node
节点(Node)是一个逻辑上独立的设备,有自己唯一的网络地址。每个Matter设备由一个或多个Node组成。
Endpoint
一个Node包含多个Endpoint,每个endpoint是一个逻辑上独立的功能模块。比如门锁,它除了可以包含门锁这个endpoint外,它还可以包含温度传感器这个endpoint。
注意:endpoint 0预留为Matter的utility cluster,而且每个Matter设备都必须强制包含它。
Cluster
Endpoint由一个或多个cluster组成,cluster可以认为是一个基本功能集,它包含attributes, commands和events三个组件。比如前面说的门锁endpoint,它除了可以包含开锁/关锁这个cluster外,它还可以包含报警cluster以实现报警功能。
Matter定义了两种类型的Cluster:
- Server –提供Attributes, Commands和Events
- Client – 对Server发起交互(interaction)操作
Cluster的详细规格定义请参见Matter Application Cluster Specification。如何通过cluster组成endpoint,进而组成设备类型,这个则是Matter Device Library Specification规定的内容。
Attribute
Attribute就是一条条表示物理量或者状态的数据记录,他们保存在设备的存储器中。
Command
Command就是下文所说的action,用来触发server的特定行为,比如关锁命令用来触发关锁操作。
Event
Event其实是一种特殊的attribute,它用来更新设备的状态,因此你可以把event当成是一种历史数据记录。
1.4 Matter交互命令和模型(Interaction Model)
通俗地讲,Interaction model就是用来规定交互命令集的,我们把发起交互的节点叫initiator (一般都是client设备),而接收者称为target (一般为server设备)。
Matter定义了如下interaction类型:
Read
用来读取attributes或events的值
Write
用来修改attribute的值
Invoke
用来发送commands
Subscribe
用来订阅target的数据报告,从而不用定时去查询相关数据,我们可以订阅attribute,也可以订阅event。
Interaction本身由transaction组成,而transaction又由action组成,每个action包含1条或者多条信息,如下:
下面我们以门锁为例子来具体讲讲interaction模型。
1.4.1 Interaction例子:门锁
下面例子假设initiator为Matter controller,target为door lock。
Read interaction
如下为读取DoorLock cluster 中的LockType attribute 的交互图:
Write interaction
如下为修改DoorLock cluster 中的OperatingMode attribute的交互图(修改为privacy模式意味着门锁只能从建筑内打开):
Invoke interaction
如下为调用DoorLock cluster的UnlockDoor command的交互图,其中Timed request用来定时命令的有效时间,为可选项。
Subscribe interaction
如下为订阅DoorLock cluster 的LockState attribute状态值的交互图,这是一个持续进行的交互,除非一方停止或者返回失败。
1.5 Matter网络安全
Matter使用128-bit AES-CCM 算法来加密数据,为了得到AES密钥,根据两种不同的应用场景,Matter定义了两种会话(session)建立方式:Passcode-Authenticated Session Establishment (PASE)和Certificate-Authenticated Session Establishment (CASE)
1.5.1 PASE
PASE仅用于配网(commission)过程中,SPAKE2+算法通过8位passcode来建立一个安全通道,为了减轻设备端计算负担,可以直接把离线计算好的SPAKE2+ Verifier用来验证passcode。
1.5.2 CASE
CASE用于正常业务通信过程中,CASE是建立在前面配网成功基础上的,配网成功后,节点会得到一个节点操作证书(Node Operational Certificate,NOC)。当两个节点使用CASE进行通信时,他们的NOC必须共用同一个根证书,也就是他们必须属于同一个Fabric。有了NOC,就可以使用SIGMA算法来得到前述AES密钥了。
需要注意的是,Matter message包含如下元素:
- Message Header – 会话和传输有关的信息
- Protocol Header – Matter消息的语义规定
- Payload – 真正的内容
- 虽然AES-CCM算法可以保证三个元素的完整性,但是只有Protocol Header和Payload会加密。
1.6 Matter网络配网
Matter配网(commissioning)就是把一个设备加入Matter Fabric(也叫Matter操作网络)的过程,此过程包含两个角色:
- Commissioner device,一般放在Matter controller中,用于发起配网过程。
- Commissionee device,就是还未添加到Matter网络中的设备。
为了完成配网,commissionee必须提供如下onboarding信息:
- 16-bit Vendor ID and 16-bit Product ID
- 12-bit device discriminator
- 27-bit setup passcode
- 8-bit Discovery Capabilities Bitmask
上面这些信息可以以下面三种方式提供:
- 手动配对码(Manual Pairing Code)
- 二维码(QR Code)
- QR Code Payload
双方必须支持Manual Pairing Code,但推荐使用二维码,当然各个生态系统也可以定义自己的discriminator和setup passcode。
配网流程如下图所示:
如果上述配网流程成功,那么设备将得到如下信息:
- 由fabric ID和node ID组成的实例名
- Node Operational Certificate(NOC)
- NOC对应的私钥
- Access Control List
- 操作网络的其他信息
1.7 Matter重要概念介绍
1.7.1 Matter Node(节点)
Matter node就是一个Matter设备的实例,一个Matter设备有可能包含一个或多个node,每个node通过64bit的node id来标识。我们也可以把不同的node组成一个group,并用16bit的group id来标识。
1.7.2 Matter controller(控制器)
Matter controller也是Matter网络的一个节点,它可以远程配置和控制附件设备,如下为两种典型的controller。
PC机中我们常见的controller就是CHIP Tool,当然Android或者iOS也支持CHIP Tool,开发者在调试Matter设备的时候,用得最多的就是CHIP Tool,如何在各大平台中配置和使用CHIP Tool,大家可以参考:
对于商用Matter生态系统,controller是一个组合体,比如苹果Matter生态,它的controller应该是iOS里面的Home app和HomePod音箱的组合体。
1.7.3 Matter Fabric(网络)
一个Matter Fabric就是一个Matter网络,一个Matter Fabric中的所有节点共享同一个根证书,所以他们可以相互通信,每个Matter Fabric会分配一个64bit的ID进行标识。一般来说,一个Matter生态就是一个Matter fabric,比如苹果的Home就是一个fabric,谷歌的home又是另一个fabric。
1.7.4 Multi-fabric/multi-admin接入多生态
一个Matter node(节点)可以接入一个Matter fabric,也可以同时接入多个Matter fabric,比如同时被苹果/谷歌/亚马逊的音箱控制,这个特性就称为multi-fabric或者multi-admin。Multi-fabric将保证不同的生态可以相互兼容,提高Matter应用的互联互通性能。
1.7.5 Matter bridge
智能家居是一个非常碎片化的市场,除了Matter技术外,还有很多其他技术运用在智能家居市场中,比如蓝牙和ZigBee。为了将这些现有的智能家居产品一揽子接入Matter生态,Matter规范里面提出了Matter Bridge这个设备类型,通过Matter bridge,我们可以把非Matter设备快速接入Matter生态。如下网络拓扑结构演示了如何将一个蓝牙灯泡接入Matter网络,然后Matter controller像控制正常Matter设备一样去控制它。
1.7.6 Matter标准OTA
Matter规范要求设备必须支持设备升级功能,虽然Matter定义了一整套设备升级标准,但Matter没有强制规定设备必须采用这套标准的OTA升级方式,也就是说设备也可以采用其他方式来升级,比如使用基于蓝牙或者Wi-Fi的SMP方式进行升级。
Matter标准OTA定义了两个角色:OTA Provider和OTA Requestor,OTA Provider和OTA Requestor都是cluster,因此他们都有client和server角色之分,要升级固件的设备叫OTA requestor,而提供固件的设备(比如controller)叫OTA provider。
1.7.7 Distributed Compliance Ledger(DCL)
通俗地说,DCL是CSA运营的一个分布式数据库服务器,既然DCL是一个记账簿,它不会不断地更新和发展的。每当成员公司有新品发布,他们就会把这个新品的认证信息写到DCL中。如果颁发了新的PAA证书,这个信息也会写入DCL中。前面提及的Matter标准OTA,新image的URL信息也可以写入到DCL中。CSA也可以撤销或者作废DCL中的一些证书或者认证信息。总之,DCL就是一个分布式数据库服务器,保证了PKI系统的正常运行,同时也保证了Matter各个生态的互联互通。
1.7.8 Certificate Declaration(CD)
每当一个产品通过了Matter认证,CSA就会颁发一个CD证书给它,CD证书包括Vendor ID,Certificate ID,certification type等,由于一个产品类别下所有设备共用同一个CD,因此CD都是hard code在应用中的。
2. 支持Matter的nRF设备介绍
2.1 Matter over Thread nRF设备
Nordic支持Matter over thread应用的SoC主要包括nRF5340和nRF52840两款。
2.1.1 nRF5340主要特性
- 带1 MB 闪存和 512 KB 内存的128/64 MHz Arm Cortex-M33 应用处理器
- 带256 KB 闪存和 64 KB 内存的64 MHz Arm Cortex-M33 网络处理器
- 低功耗蓝牙
- 蓝牙测向
- Matter
- Bluetooth mesh, Thread, Zigbee
- ANT
- NFC
- 高级安全性
- USB, QSPI, HS-SPI
- 105 °C 扩展工作温度
- 1.7-5.5 V 电源电压范围
开发nRF5340应用时,在nRF Connect SDK中选择的开发板类型为:
- nrf5340dk_nrf5340_cpuapp。
2.1.2 nRF52840主要特性
- 64 MHz Arm Cortex-M4 带FPU
- 1 MB闪存 + 256 KB RAM
- 低功耗蓝牙,蓝牙mesh
- 2Mbps
- 2.4 GHz 收发器
- ANT, 802.15.4, Thread, Zigbee
- 长距离
- +8 dBm 发射功率
- -95 dBm 灵敏度
- 支持IEEE 802.15.4无线电
- Matter
- Thread
- Zigbee
- 1.7V至5.5V供电电压范围
- 全速12 Mbps USB
- NFC-A tag
- PWM
- UART, SPI, TWI, PDM, I2S, QSP
- 高速 32 MHz SPI
- Quad SPI 接口32 MHz
- 12位/200 ksps ADC
- 128位 AES CCM, ARM CryptoCell
- USB 2.0
开发nRF52840应用时,在nRF Connect SDK中选择的开发板类型为:
2.2 Matter over Wi-Fi nRF设备
要实现Matter over Wi-Fi应用,需要同时使用nRF5340和nRF7002/nRF7001两款芯片,nRF7002/nRF7001是Wi-Fi 6协同IC,他们只运行Wi-Fi有关的MAC层,Matter的其他部分还是跑在nRF5340上。
2.2.1 nRF7002主要特性
- Wi-Fi 6站点(STA)
- 2.4 GHz和5 GHz双频段
- 符合802.11a/b/g/n/ac/ax标准
- 用于物联网的低功耗安全Wi-Fi
- 与低功耗蓝牙的理想共存
- 目标唤醒时间(TWT)
- SPI / QSPI
- 1个空间流(SS)
- 20MHz通道带宽
- 64 QAM (MCS7),86 Mbps PHY
- OFDMA(下行链路和上行链路)
- BSS着色
- 共存接口
- nRF Connect SDK提供支持
开发nRF5340+nRF7002应用时,在nRF Connect SDK中选择的开发板类型为:
- nrf7002dk_nrf5340_cpuapp。
2.2.2 nRF7001主要特性
- nRF7001跟nRF7002功能差不多
- 但它只支持2.4G频段
- 不支持5G频段。
3. nRF Connect SDK和Matter SDK
开发Matter应用,你可以选择Nordic自己的SDK:nRF Connect SDK来开发,也可以选择Matter官方SDK:Matter SDK来开发。其实两套SDK基本上差不多,并且是相互包含的关系,也就是说,nRF Connect SDK里面已经包含了Matter SDK,站在nRF Connect SDK角度来看,Matter SDK就是它的一个模块。
同样,Matter SDK里面也包含了nRF Connect SDK,站在Matter SDK角度来看,nRF Connect SDK就是它的一个模块。由于两者的互包含关系,导致两者的版本无法同步,也就是说,当nRF Connect SDK开始下一个版本开发的时候,它会锁定Matter SDK一个版本,比如nRF Connect SDK v2.5.0开发的时候,由于Matter SDK v1.2.0.0还没有发布,因此它锁定的版本就是Matter SDK v1.1.0.1;同样当Matter SDK v1.2.0.0开始开发的时候,由于nRF Connect SDK v2.5.0还没有发布,因此它锁定的版本就是nRF Connect SDK v2.4.0。
这就导致一个现象,Matter SDK锁定的nRF Connect SDK版本总是落后于Nordic最新版本,nRF Connect SDK锁定的Matter SDK版本也总是落后于Matter官方最新版本。
如果你是做一个商业产品开发,强烈建议你使用nRF Connect SDK来进行开发,因为每个nRF Connect SDK版本的发布都会对Matter所有例子进行考核和测试,以保证他们的质量和稳定性,这也是为什么nRF Connect SDK会对Matter SDK打很多补丁以保证其质量。
而且nRF Connect SDK同时支持Windows/MacOS/Linux平台,让你不再局限于Linux开发平台(经过一定修改后,我们也可以让Matter SDK跑在Windows平台,下文会对此进行介绍)。
如果你想测试Matter最新的特性,而这个特性nRF Connect SDK暂时又没有,那么可以使用Matter SDK来进行开发和调试,待下版本nRF Connect SDK包含了该特性,你就可以直接移植过去,快速发布你的产品。
欲查看Matter SDK锁定的nRF Connect SDK版本,大家可以打开这个文件:config/nrfconnect/.nrfconnect-recommended-revision,如下:
由于nRF Connect SDK会对它锁定的Matter SDK版本进行修改,我们没办法一眼看出它锁定的版本,但是大家可以从如下网站找到每个版本的nRF Connect SDK锁定的Matter SDK版本以及它遵守的Matter规范版本。
https://docs.nordicsemi.com/bundle/ncs-latest/page/nrf/protocols/matter/index.html
nRF Connect SDK version |
Matter specification version |
Matter SDK version |
v2.5.99 (latest) |
1.2.0 |
1.2.0.1 |
v2.5.2 |
1.1.0 |
1.1.0.1 |
v2.5.1 |
1.1.0 |
1.1.0.1 |
v2.5.0 |
1.1.0 |
1.1.0.1 |
v2.4.3 |
1.1.0 |
1.1.0.1 |
v2.4.2 |
1.1.0 |
1.1.0.1 |
v2.4.1 |
1.1.0 |
1.1.0.1 |
v2.4.0 |
1.1.0 |
1.1.0.1 |
v2.3.0 |
1.0.0 |
1.0.0.2 |
v2.2.0 |
1.0.0 |
1.0.0.0 |
v2.1.4 |
1.0.0 |
1.0.0.0 |
v2.1.3 |
1.0.0 |
1.0.0.0 |
v2.1.2 |
1.0.0 |
1.0.0.0 |
虽然nRF Connect SDK和Matter SDK相互包含,但他们使用同一套工具链,而且这套工具链是伴随nRF Connect SDK一起下载和安装的,另外,不管你使用Matter SDK来开发Matter应用还是使用nRF Connect SDK来开发Matter应用,nRF Connect SDK都必须下载和安装,下面我们会以nRF Connect SDK为主来讲解Matter应用开发流程,然后在此基础上再讲解Matter SDK开发Matter应用的流程。
实际上,你只要熟悉了nRF Connect SDK开发流程,也就熟悉了Matter SDK开发流程,因为两者几乎一模一样。
4. nRF Connect SDK开发环境搭建
关于nRF Connect SDK搭建和介绍,这里有一篇博文:
推荐大家去看一下。下面我们会对博文内容进行浓缩,简要介绍nRF Connect SDK。
4.1 前置安装
在我们开始正式的开发环境配置之前,我们先需要下载如下三个工具:
4.2 VS Code开发环境搭建
Microsoft Visual Studio Code(VS Code)是Nordic推荐的开发nRF Connect SDK应用的跨平台IDE工具,所以nRF Connect SDK开发环境搭建都是在VS Code中进行的。
4.2.1 插件安装
首先下载相应的插件。打开VS Code,进入Marketplace,搜索“nrf”,然后选择“nRF Connect for VS Code Extension Pack”进行安装,一旦nRF Connect for VS Code Extension Pack安装成功,所有nRF插件都自动安装成功。目前Nordic开发了如下nRF插件:
4.2.2 nRF Connect SDK安装
由于nRF Connect SDK放在GitHub服务器上,下载和安装nRF Connect SDK的时候请一定要使用VPN,否则很有可能就会下载不完整或者失败。
上面的nRF Connect for VS code插件安装成功后,点击左边的插件图标,进入WELCOME面板,选择Manage SDKs,如下:
然后在右边列表框中选择Install SDK,如下:
然后选择相应版本的nRF Connect SDK,如下:
请注意,如果你是一个新用户,强烈建议你选择最新版本,即列表里面版本最高的版本,上面是v2.5.0(截止本文第一次发表时),但是当你读到这时,最高版本有可能已经到v2.6.0,v2.7.0,甚至更高,请选择此时最高版本。(注意:*.*.99之类的版本是开发专用版本,不能用于量产)
选择好版本后,然后选择SDK安装根目录,一般使用默认推荐的目录即可,如下。
大家千万不要使用很长的目录作为安装根目录,否则在Windows上编译例子的时候,经常会碰到目录名太长的编译报错。
然后VS code开始下载nRF Connect SDK,如下:
下载完成之后,你就可以打开SDK所在的根目录,如下:
SDK在下载过程中,经常碰到下载不完整的情况,而且这种情况又不会报错,为此我们可以通过下面的方式去检验nRF Connect SDK是否下载完整和正确,选择Manage west workspace,然后选择West Update,如下:
如果nRF Connect SDK还缺少一些组件没有下载完整,此时在OUTPUT窗口,你将会看到类似下面这样的报错信息:
如果nRF Connect SDK已经完整下载并正确,此时在OUTPUT窗口,你将会看到下面的信息输出:
除了上述的VS code安装成功确认方式,我们也可以通过目录和命令行的方式来确认nRF Connect SDK是否完整安装正确。
nRF Connect SDK和工具链安装成功后,都放在Windows如下目录里面:
打开CMD,进入相应SDK根目录,然后输入命令:git show,以确认安装版本是否正确:
然后,我们可以手动输入命令:west update,以同步nRF Connect SDK所有仓库,从而确认SDK是否下载完整和正确,如下:
如果最后没有出现报错信息,说明所有关联仓库都已经下载并同步成功,nRF Connect SDK已经下载完整和正确。
4.2.3 Toolchain安装
nRF Connect SDK开发使用的Toolchain也可以通过VS Code直接安装,上面的nRF Connect for VS code插件安装成功后,点击左边的插件图标,进入WELCOME面板,然后选择Manage toolchains,在右边的列表框中选择Install Toolchain,如下:
根据你选择的nRF Connect SDK版本选择对应版本的Toolchain,比如nRF Connect SDK v2.5.0对应的Toolchain安装如下所示:
然后开始下载Toolchain,如下:
下载成功后,自动解压和安装,如下:
如下为安装成功的提示:
这里需要注意的是,Toolchain安装可以不用VPN,但使用VPN可以让安装更稳定更可靠。
如果你PC里面有多个版本的Toolchain,你可以选择任一版本作为当前版本,如下:
4.2.4 安装必须的Python脚本
nRF Connect SDK虽然已经预装了大部分的Python脚本,但是Matter模块里面有些Python脚本还没有安装。很多人电脑里装了多个Python环境,怎么保证我们现在使用的Python环境是nRF Connect SDK v2.5.0 Toolchain自带的?为了确保执行的是nRF Connect SDK Toolchain里面的Python,而不是操作系统环境变量中的Python,推荐大家使用VS code的终端来执行命令,即打开如下nRF Connect终端:
这样就保证了该命令行的环境是nRF Connect SDK Toolchain设置的环境,在该终端中执行的任何命令都是针对nRF Connect SDK Toolchain环境来说的。
我们输入如下命令:
pip3 install -r C:/ncs/v2.5.0/modules/lib/matter/scripts/setup/requirements.nrfconnect.txt
这样系统就会自动补全nRF Connect SDK Toolchain缺少的Python包,如下所示:
4.2.5 zap工具安装
nRF Connect SDK安装成功后,我们就需要开始安装zap工具,zap工具是用来添加/编辑/删除前文所述的node/endpoint/cluster,因此它是一个必须安装的工具。对nRF Connect SDK来说,zap工具的安装是比较简单的,Nordic特意提供了如下Python命令帮助大家安装:
首先进入matter仓库主目录:
cd C:\ncs\v2.5.0\modules\lib\matter
然后执行如下命令,其中“C:\Nordic\Tools\zap-win-x64”是zap工具安装的根目录,大家也可以选择其他目录。
python scripts/setup/nrfconnect/get_zap.py -l C:\Nordic\Tools\zap-win-x64 -o
zap工具安装成功后,记得把C:\Nordic\Tools\zap-win-x64这个目录添加到操作系统的环境变量中。
至此,nRF Connect SDK开发环境已经搭好,下面我们来编译一个例子来验证一下开发环境是否真得搭成功了。
4.3 使用nRF Connect SDK编译Matter例程
nRF Connect SDK使用CMakeLists.txt文件来表示项目,并通过CMake命令来取代传统的通过GUI来添加源文件和目录,使用Kconfig来配置项目以取代传统的头文件配置方式,使用DeviceTree来配置底层驱动文件以取代传统的头文件配置方式,如果大家对CMake/Kconfig/DeviceTree不熟的话,建议阅读:开发你的第一个nRF Connect SDK/Zephyr应用程序l,以加深对nRF Connect SDK的理解。
Matter项目很大,编译起来特别费时间,有时为了快速验证你的开发环境是否搭好,大家可以跑一个简单的例子,比如zephyr/samples/basic/blinky,以测试你的开发环境是否搭好。这里我们就不跑了,大家自己可以先测一下。下面我们为大家直接演示Matter例子是如何编译和运行的。
我们以nrf/samples/matter/lock为例,详细阐述如何使用nRF Connect SDK来编译和运行Matter例子。nrf/samples/matter/lock是一个门锁例子,同时支持Matter over Thread和Matter over Wi-Fi,如果你选择nrf5340dk_nrf5340_cpuapp或者nrf52840dk_nrf52840,那么这个例子自动编译成Matter over Thread应用;如果你选择nrf7002dk_nrf5340_cpuapp,那么这个例子自动编译成Matter over Wi-Fi应用。
下面我们以nrf52840dk_nrf52840为例(nrf5340dk_nrf5340_cpuapp和nrf7002dk_nrf5340_cpuapp跟它一模一样,只需更换板子即可),来编译和运行这个门锁例子。
选择APPLICATIONS标签页,选择Open Existing Application,如下:
选择目录:nrf/samples/matter/lock,如下:
打开上述项目后,将自动跳出Add Build Configuration页面,在Board列表框中,选择开发板:nrf52840dk_nrf52840,如下:
然后选择Configuration选项,如果留空,默认选择prj.conf,大家也可以选择其他配置项。
其他一些配置项的说明如下所示:
选择Build Configuration,开始编译,视你的PC配置情况,编译有可能耗时达10分钟之久,编译成功后,你将看到如下输出:
整个VS code一览图如下所示:
至此,项目编译成功,大家可以点击“Flash”将代码下载到板子里面,然后Matter应用就自动跑起来了,打开PC串口助手,你将看到如下输出日志,说明Matter应用已经在正常运行了。
5. Matter SDK开发环境搭建
如前所述,搭建Matter SDK开发环境之前,必须先搭建好nRF Connect SDK开发环境,请大家先按照第4章的要求,搭建好nRF Connect SDK开发环境,然后再来读这一章。
5.1 Matter SDK下载和安装
5.1.1 Matter SDK下载
Matter SDK是放在GitHub上的,因此必须使用VPN来下载Matter SDK。
首先,打开CMD,进入c:/ncs目录,然后输入如下命令:
git clone https:
上面命令将把Matter SDK公共的仓库都下载下来,而且自动切换到Matter SDK的master分支,如果你要切换到一个特定的tag,比如v1.2.0.1,请执行:
git checkout v1.2.0.1
由于Matter SDK支持众多平台和厂商,我们还需要下载这些第三方的仓库,为此,我们再输入如下命令:
cd C:/ncs/connectedhomeip/scripts
python checkout_submodules.py --platform nrfconnect
上面的platform参数来指明下载哪个平台或厂商的仓库,如果没有指明,说明下载所有平台或厂商的仓库,这个工作量是巨大的(目前是19GB左右),下载的时间也是很长的。上面的例子我们指明了nrfconnect平台,这就意味着它只会下载Nordic Matter开发相关的仓库,这个就小多了,大概1.5GB不到。
如果你之前已经安装过Matter SDK,现在只是想更新到最新版本,那么使用下面命令即可:
git pull
git submodule update --init
5.1.2 安装缺少的Python模块
然后我们还需要安装缺少的Python模块,这个是针对于nRF Connect SDK Toolchain来说的。nRF Connect SDK Toolchain是直接为nRF Connect SDK准备的,因此它不用做任何修改,就可以直接编译nRF Connect SDK例子,但对Matter SDK例子,它有一些额外的Python模块需求,因此我们需要将其补充完整。为了确保执行的是nRF Connect SDK Toolchain里面的Python,而不是操作系统环境变量中的Python,推荐大家使用VS code的终端来执行命令,即打开如下nRF Connect终端:
这样就保证了该命令行的环境是nRF Connect SDK Toolchain设置的环境,在该终端中执行的任何命令都是针对nRF Connect SDK Toolchain环境来说的。
我们输入如下命令:
pip3 install -r C:/ncs/connectedhomeip/scripts/setup/requirements.build.txt
这样系统就会自动补全nRF Connect SDK Toolchain缺少的Python包,如下所示:
5.1.3 安装合适的zap工具
虽然前面nRF Connect SDK开发环境搭建过程中,已经安装好了zap工具,但是这个zap工具的版本有可能跟Matter SDK要求的zap工具版本不一样,为此我们还需要再安装一次期望版本的zap工具。
首先我们需要先确认zap工具的版本对不对,大家可以通过scripts/setup/zap.version这个文件确认:
这就意味着对Matter SDK v1.2.0.1来说,它需要的zap工具版本是v2023.10.24-nightly,然后大家可以到如下链接下载相应版本:https://github.com/project-chip/zap/releases,找到版本“v2023.10.24-nightly”,然后下载自己平台的安装包。
比如Windows,就下载zap-win-x64.zip这个压缩包,下载成功后,解压缩,将其放在某个目录下,然后把该目录添加到Path变量中,如下为我电脑上的解压缩路径和Path变量值:
如果你的PC安装有老的zap工具,那么请记得把上面的环境变量一定要指向你刚刚解压缩的目录,而不是原来的值。另外原来的zap工具会在如下目录生成一个缓冲文件:
为了更好的兼容性,大家可以把这个文件直接删掉。
至此,Matter SDK已经下载和安装完毕,下面我们就可以开始用它来编译和运行Matter应用了。
5.2 编译Matter SDK例子
5.2.1 Linux/MacOS平台
原生的Matter SDK就支持Linux和MacOS平台,因此你不要做任何修改,你就可以在Linux和MacOS上编译Matter SDK例子,大家可以参考4.3节介绍的步骤,去编译Matter SDK里面的例子,比如examples\lock-app\nrfconnect,这个是Matter SDK里面自带的门锁例子。
5.2.2 Windows平台
由于前面已经把nRF Connect SDK开发环境搭建好,我们可以直接使用nRF Connect SDK开发环境来编译和运行Matter SDK例子,整个步骤跟前述的编译运行nRF Connect SDK例子一模一样。
5.2.2.1 修改Matter SDK以适配Windows平台
由于原生的Matter SDK只能在Linux和MacOS上进行编译,我们需要对原生的Matter SDK进行几处修改才能直接在Windows上进行编译,修改的地方如下所示:
- 在connectedhomeip/build_overrides目录下,新建一个空文件,文件名为:pigweed_environment.gni。这个文件创建一次就可以,然后可以适用所有Matter工程。
- 修改一些编译脚本。请大家将
对应的文件下载下来,命名为30982.diff,放在c:/ncs目录下,然后执行
git apply c:/ncs/30982.diff
就可以自动修改相应脚本以支持Windows,如下:
注意:这个补丁是针对Matter SDK版本v1.2.0.1的,Matter SDK未来版本有可能会把这个PR合并进去,这样这个补丁就没有必要再打了。
- 修改每个项目的CMakelists.txt文件,只改一个地方:
- 把本项目目录下的third_party\connectedhomeip这个文件包含的内容直接取代CMakelists.txt中的变量connectedhomeip,即把../../../..取代connectedhomeip,使
get_filename_component(CHIP_ROOT $\{CMAKE_CURRENT_SOURCE_DIR\}/third_party/connectedhomeip REALPATH)
这句话变成
get_filename_component(CHIP_ROOT $\{CMAKE_CURRENT_SOURCE_DIR\}/third_party/../../../.. REALPATH)
5.2.2.2使用Matter SDK编译Matter例程
如果你前面没有选择Matter SDK的版本,那么你首先要checkout一个你期望的版本,一般选择一个tag来开发和测试,而且一般都是选择最新的tag来开发和测试,如下图所见,截止本文发表时,Matter SDK最新的tag是v1.2.0.1。
为此我们选择v1.2.0.1这个tag来演示我们下面例子的编译和测试。我们在CMD中输入如下命令:
git checkout v1.2.0.1
git submodule update
我们还是以Matter SDK原生的lock例子为例:connectedhomeip\examples\lock-app\nrfconnect,来阐述整个编译过程。
首先修改CMakelists.txt文件(前面说得pigweed_environment.gni这个空文件记得要先创建),得到:
然后像编译nRF Connect SDK例子一样去编译这个lock例子。由于Matter SDK使用了nRF Connect SDK的工具链,因此我们必须要知道当前Matter SDK锁定的nRF Connect SDK的版本是多少。查看Matter SDK要求的nRF Connect SDK版本,大家可以打开这个文件:C:\ncs\connectedhomeip\config\nrfconnect.nrfconnect-recommended-revision,如下:
说明它要求的nRF Connect SDK版本是v2.4.0,接下来我们就要在WELCOME标签页中选择nRF Connect SDK v2.4.0以及对应的Toolchain,如下:
然后选择Open Existing Application,找到目录:connectedhomeip\examples\lock-app\nrfconnect,如下:
然后选择开发板:nrf52840dk_nrf52840
然后选择“Build Configuration”开始编译,编译成功后,将有如下输出日志:
点击Flash,将程序下载到开发板:
程序运行起来后,将有如下日志输出:
按下Button4,设备就会开始蓝牙广播,表示已经做好配网准备,可以接入Matter生态了:
下面我们就会讲解如何使用HomePod mini来测试Matter设备。
6. 使用苹果HomePod mini测试你的Matter设备
不管是用nRF Connect SDK编译的lock例程(4.3节),还是用Matter SDK编译的lock例程(5.2.2.2节),编译成功并运行后,他们的测试流程都是一样的。
首先确保设备蓝牙广播已经开启,日志有“advertising started”字样,如果没有开启,请按开发板最后一个按键以激活蓝牙广播。
然后我们从日志中可以看到一个网址,如下:
https://project-chip.github.io/connectedhomeip/qrcode.html?data=MT%3A8IXS142C00KA0648G00
将上面网址拷到浏览器中,即可以得到配网用的二维码,如下:
目前Nordic所有例子默认都使用上面二维码来配网,大家只要扫描上面二维码就可以完成配网。
在开始下面的测试步骤之前,请确保你的HomePod mini和iPhone或者iPad连接同一个路由器,而且HomePod mini已经跟该iPhone或者iPad的Home app绑定好。
打开iOS设备上的Home app,点击右上角的“+”,选择Add or Scan Accessory,如下:
跳出下面的扫描窗口,将扫描窗口对准上面的二维码,扫描成功后,配网真正开始。
大家也可以从日志看出,配网流程正在进行中,下面就是截取配网流程的一个小片段的日志:
过一会,iOS设备就会弹出如下对话框,这个是因为我们使用的是测试DAC证书和测试用PID等,请大家参考第8章如何获取生产用的证书和ID等信息,这里我们选择“Add Anyway”。
设备配网成功后,iOS设备会连续出现如下界面:
选择这个设备所在的位置:
给这个设备取一个好记的名字:
如果是锁设备类型,还需要设置它的访问密码:
设置一些自动化的场景:
最后显示设备添加成功的界面,如下:
同时大家也可以从Home app的主界面看到这个添加成功的设备,如下:
除了从iOS设备上观察设备是否添加成功,你也可以从日志输出来佐证设备是否添加成功,设备添加成功后,你一般会看到如下红色标注的日志:
设备添加成功后,我们就可以对设备进行各种操作,比如我们去开锁,开锁可以通过Home app直接操作,也可以通过语音Siri来操作,不过二者有点不一样。通过Home app操作的话,你直接点击如下图标即可:
通过Siri可以发出开锁命令,但是它无法开锁,必须通过Home app确认才能开锁(这个是出于安全考虑),即Siri发出开锁命令后,Home app会收到如下确认信息,确认后才能开锁:
关锁操作相对安全一些,因此Home app和Siri都可以直接执行关锁操作,Home app直接就关锁成功,而Siri关锁成功的同时会给Home app发一条消息,告知锁已关闭,如下:
大家除了可以从iOS设备看到开锁或者关锁成功的界面,也可以从输出的串口日志看到开锁或者关锁成功的标志,如下:
7. 开始你的Matter定制开发之旅
Matter开发主要包括两方面的工作:一是Matter应用本身的开发,二是非Matter应用开发。Matter应用开发本质上就是cluster/endpoint/node的添加、编辑、删除以及相关回调事件处理等,如前所述,这个需要通过zcl编辑工具zap来生成ember层的代码,以及手动添加或者修改其他c++文件来实现,下面一一对此进行介绍。
下面例子我们是基于nRF Connect SDK v2.5.0例子:
- nrf\samples\matter\template
来阐述的(v2.5.0是截止本文发表时的最高版本,对于读者你来说,请选择当下最新版本)。
如果你第一次接触nRF Connect SDK,建议你先读完这篇文章:
首先在VS Code的nRF Connect插件中重新选择nRF Connect SDK v2.5.0和nRF Connect SDK Toolchain v2.5.0,如下:
然后选择Create New Application,并选择Copy a sample,如下:
然后选择Matter template例程,如下:
选择例程放置在哪里,如下:
选择Add to Workspace:
然后就可以按照4.3节的做法来编译这个例子,选择开发板:nrf52840dk_nrf52840,如下:
然后选择Build Configuration,开始编译,视你的PC配置情况,编译有可能耗时达10分钟之久,编译成功后,你将看到如下输出:
下面我们将在此例程基础上,对其进行定制开发,开发的内容主要包括两部分:一是添加Matter on/off light设备,二是添加蓝牙LBS(LED和Button服务)服务,具体请见下文。
7.1 添加Matter On/Off Light设备
7.1.1 添加cluster
如前所述,我们可以使用ZAP来添加endpoint和cluster。首先我们确认zap版本是否正确(前面我们来回切换nRF Connect SDK和Matter SDK,有可能会导致zap版本混乱,这里有必要再次确认一下),输入如下两条命令进行确认:
cd C:\\ncs\\v2.5.0\\modules\\lib\\matter
python scripts/setup/nrfconnect/get_zap.py -l C:\\Nordic\\Tools\\zap-win-x64 -o
ZAP工具安装成功后,我们就可以用它来编辑zap文件,在我们这个项目里面,zap文件就是
- C:/ncs/bulb/src/template.zap
template.zap是一个JSON文件,包含了应用所有的cluster,command和attribute。我们可以使用如下命令打开这个zap文件:
zap C:/ncs/bulb/src/template.zap --zcl C:/ncs/v2.5.0/modules/lib/matter/src/app/zap-templates/zcl/zcl.json --gen C:/ncs/v2.5.0/modules/lib/matter/src/app/zap-templates/app-templates.json
cmd窗口如下所示:
同时弹出ZCL窗口:
可以看出,例子目前只有Endpoint 0,选择Endpoint 0,并选择“Enabled Clusters”,我们可以看到这个Endpoint使能了哪些cluster,比如Descriptor cluster。
下面我们添加一个Endpoint以支持Matter On/Off Light设备,选择“ADD ENDPOINT”,在弹出的对话框中,选择“Matter On/Off Light”,如下:
请确认on/off cluster已使能,然后点击配置图标。
在弹出的对话框中,确认On/Off Attribute已经使能。
同时确认Off和On命令已使能:
至此,我们的endpoint和cluster就添加成功了。
但上面的添加操作只是修改了zap文件,对我们SDK没有做任何修改。接下来,我们就要使用一个Python脚本将这个zap文件转成相应的c++文件和头文件,以让这个修改操作可以编译到我们的代码中去。(大家也可以直接去创建相应的c++文件和头文件,但这个过程比较枯燥和繁琐,所以我们才引入了zap工具和相应的代码转换Python脚本)。
打开CMD,输入如下命令:
cd C:\ncs\v2.5.0\modules\lib\matter
python ./scripts/tools/zap/generate.py C:/ncs/bulb/src/template.zap -t src/app/zap-templates/app-templates.json -o C:/ncs/bulb/src/zap-generated
至此,cluster已经添加到我们的应用中了。
7.1.2 修改项目代码
添加完cluster后,我们还需要修改项目代码,以让新添加的设备真正可以工作起来。这个工作主要包括两部分:
- 添加Matter stack回调函数,以处理controller发送过来的命令,比如开灯。这个主要通过覆盖MatterPostAttributeChangeCallback()来实现
- 更新attribute数值,以让Matter stack可以将最新的状态或者event同步给controller。这个主要通过Clusters::OnOff::Attributes::OnOff::Set()之类的函数实现。
首先我们先实现MatterPostAttributeChangeCallback(),新建一个文件:src/zcl_callbacks.cpp,然后将如下代码拷入:
#include "app_task.h"
#include <app-common/zap-generated/ids/Attributes.h>
#include <app-common/zap-generated/ids/Clusters.h>
#include <app/ConcreteAttributePath.h>
using namespace ::chip;
using namespace ::chip::app::Clusters;
void MatterPostAttributeChangeCallback(const chip::app::ConcreteAttributePath & attributePath, uint8_t type,
uint16_t size, uint8_t * value)
{
if (attributePath.mClusterId != OnOff::Id || attributePath.mAttributeId != OnOff::Attributes::OnOff::Id)
return;
AppEvent event;
if (*value) {
event.Type = AppEventType::TurnOnLED;
event.Handler = AppTask::TurnOnLEDHandler;
}
else {
event.Type = AppEventType::TurnOffLED;
event.Handler = AppTask::TurnOffLEDHandler;
}
AppTask::Instance().PostEvent(event);
}
本来代码逻辑可以更简单,当收到On命令时,调用sBulbLED.Set(true)开灯;收到Off命令时,调用sBulbLED.Set(false)关灯。但这种做法会影响Matter协议栈的行为,因此我们把sBulbLED.Set()操作扔到一个event queue中去执行,而AppTask正好有一个现成的sAppEventQueue,我们正好可以加以利用,这样当收到On命令时,我们把TurnOnLED事件扔到Event queue,然后在AppTask主循环中取出该事件,并调用该事件的handler:TurnOnLEDHandler。Off命令的处理情况也类似。我们在app_task.cpp加入如下代码:
void AppTask::TurnOnLEDHandler(const AppEvent &)
{
LOG_INF("TurnOnLEDHandler");
sBulbLED.Set(true);
}
void AppTask::TurnOffLEDHandler(const AppEvent &)
{
LOG_INF("TurnOffLEDHandler");
sBulbLED.Set(false);
}
其中sBulbLED定义如下:
LEDWidget sBulbLED;
我们同时在AppTask::Init()中对其做如下初始化:
sBulbLED.Init(DK_LED2);
sBulbLED.Set(false);
然后不要忘了在app_task.h中声明TurnOnLEDHandler和TurnOffLEDHandler,如下:
static void TurnOnLEDHandler(const AppEvent &);
static void TurnOffLEDHandler(const AppEvent &);
为了让AppTask可以处理上面的TurnOnLED和TurnOffLED事件,我们需要在app_event.h修改如下定义:
enum class AppEventType : uint8_t \{ None = 0, Button, ButtonPushed, ButtonReleased, Timer, UpdateLedState, TurnOnLED, TurnOffLED\};
为了让app_task.cpp访问cluster attribute,比如Clusters::OnOff::Attributes::OnOff::Set(),需要加入如下头文件:(注:本文我们并没有调用Clusters::OnOff::Attributes::OnOff::Set(),但实际应用肯定会调用它的)
#include <app-common/zap-generated/attributes/Accessors.h>
最后我们把src/zcl_callbacks.cpp文件添加到CMakeLists.txt,如下:
然后我们就可以编译了,成功后会有如下显示信息:
7.1.3 测试
我们可以按照第6章的方式,测试这个新的Matter设备,这里就不再赘述了。设备配网成功后,iOS Home app将会看到如下设备:
操作灯泡的界面如下所示:
7.2 添加蓝牙LBS服务(LED和Button服务)
在nRF Connect SDK中,Matter相关代码都是用C++撰写的,除此之外,其他代码都是使用C代码编写的。两者浑然一体,各自运行良好。
因此,我们只要参考nrf\samples\bluetooth\peripheral_lbs这个例子,就可以把LBS服务加入到例子中。
首先我们把nrf\samples\bluetooth\peripheral_lbs\src\main.c,改名为lbs.c,并拷贝到项目的src目录下,即C:\ncs\bulb\src。然后在CMakeLists.txt文件中添加此文件:
然后我们需要对lbs.c文件作几处修改。首先main()函数已经被C++占用了,如下:
我们需要把lbs.c里面的main()函数换成一个普通线程,为此,我们先定义一个静态线程:
K_THREAD_DEFINE(ble_lbs_thread_id, 1024, ble_lbs_thread, NULL, NULL,
NULL, 7, 0, 0);
然后把int main(void)改成:
int ble_lbs_thread(void)
由于在Matter应用部分我们已经调用了bt_enable(),dk_leds_init(),init_button(),bt_le_adv_start(),settings_load()等,因此我们把lbs.c中相关调用删掉。实际上我们只需要bt_lbs_init()这一个初始化函数。
由于Matter应用和lbs.c中都使用了LED模块,为了有一个更好的直观效果,我们把lbs.c中的
#define RUN_STATUS_LED DK_LED1
#define CON_STATUS_LED DK_LED2
改成
#define RUN_STATUS_LED DK_LED4
#define CON_STATUS_LED DK_LED4
最后我们合并两个项目的prj.conf文件,即把
CONFIG_BT_LBS=y
CONFIG_BT_LBS_POLL_BUTTON=y
添加到项目的prj.conf文件中。
至此项目代码修改完毕,我们可以编译了,编译成功后,下载代码到板子中,打开手机app:nRF Connect,将看到如下广播:
点击“CONNECT”,连接成功后,我们将看到LBS服务,如下:
然后我们可以对LED这个特征进行操作:ON或者OFF,板子上的LED3将跟随命令而变化。
7.3 C代码与C++代码混合编程
7.3.1概述
从上面例子大家可以看出,添加Matter设备的时候,我们使用的是C++代码;添加蓝牙LBS服务的时候,我们使用的是C代码。一个工程中同时存在C和C++代码,这是Matter应用一个非常突出的特性,这就要求我们既需要理解C语言代码,也需要了解一定的C++代码,所以Matter开发是一个比较复杂而富有挑战的工作。
在Matter开发中,大家碰到一个很常见的问题是:如何让C代码调用外部C++代码编写的API,或者如何让C++代码调用外部C代码编写的API。
关于C++代码调用外部C代码API,前面其实已经出现过多次,这个跟C代码调用C代码API一模一样,比如在app_task.cpp中,调用了如下外部C语言API:dk_buttons_init,k_msgq_get,k_timer_start等,可以看出跟普通调用几乎没有区别。
关于C代码调用外部C++代码API,这个处理起来就稍微复杂一些。如果C代码只是调用一个全局的C++函数,那么我们需要把该函数申明为extern "C",然后就可以被C代码调用了;但是C编译器又不识别extern "C",因此我们一般在头文件中使用如下申明:
其中start_lbs_adv()就是一个可以被C调用的C++函数。
还有一种情况,就是C要调用C++类里面的函数,这个时候我们就需要先获得类对应的实例,然后通过实例去引用本实例自身的函数。如何获得实例的引用是整个调用最关键的部分,好在Matter应用中,我们使用了Singleton(单例)模式,也就是说每个类只会创建一个静态的全局实例,这也就意味着类函数调用跟全局函数调用差不多,通过这个静态全局实例,我们就可以调用类里面任何公共(public)函数,比如类AppTask,它的静态全局实例可以通过AppTask::Instance()获得,得到这个实例后,我们就可以调用AppTask里面任何公共函数了,比如StartApp(),这样完整调用例子就变成:AppTask::Instance().StartApp()。
由于我们不会去更改SDK任何原始代码,我们需要建一个CPP文件作为桥梁去调用其他CPP文件里面的API,这样就可以保证SDK原始的CPP文件不会动,我们只需要修改该新建的CPP文件就可以完成我们的目标。
下面我们结合实际应用场景来看看C和C++是如何相互交互的。还是以上面的工程为例,我们要添加两项工作:一是增加LBS原始的广播,二是当Button状态变化时,通知到手机app。
7.3.2 C调用外部C++函数示例
本来启动广播直接调用bt_le_adv_start()即可,为了演示C调用外部C++ API,这次我们通过调用BLEAdvertisingArbiter::InsertRequest来实现。
首先我们新建两个文件:lbs_adv.cpp和lbs_adv.h,在lbs_adv.h我们定义如下可以被C调用的C++ API:
#pragma once
#ifdef __cplusplus
extern "C"
{
#endif
void start_lbs_adv();
#ifdef __cplusplus
}
#endif
然后我们在lbs_adv.cpp中实现start_lbs_adv(),实现代码如下所示:
#include "lbs_adv.h"
#include <platform/Zephyr/BLEAdvertisingArbiter.h>
#include <lib/support/Span.h>
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/addr.h>
#include <zephyr/bluetooth/gatt.h>
#include <zephyr/bluetooth/hci.h>
#include <zephyr/bluetooth/conn.h>
#include <zephyr/bluetooth/uuid.h>
#include <bluetooth/services/lbs.h>
#include <array>
#include <zephyr/logging/log.h>
using namespace ::chip;
constexpr uint8_t kAdvertisingFlags = BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR;
constexpr uint8_t serviceData[] = {BT_UUID_LBS_VAL};
constexpr uint8_t name[] = "LBS_ADV";
void lbs_adv_started(int rc)
{
printk("LBS advertising started:%d\n", rc);
}
void lbs_adv_stopped()
{
printk("LBS advertising stopped\n");
}
void start_lbs_adv()
{
static std::array<bt_data, 2> advertisingData;
static std::array<bt_data, 1> scanResponseData;
static chip::DeviceLayer::BLEAdvertisingArbiter::Request lbsAdvRequest = {};
advertisingData[0] = BT_DATA(BT_DATA_FLAGS, &kAdvertisingFlags, sizeof(kAdvertisingFlags));
advertisingData[1] = BT_DATA(BT_DATA_NAME_COMPLETE, name, sizeof(name));
scanResponseData[0] = BT_DATA(BT_DATA_UUID128_ALL, serviceData, sizeof(serviceData));
lbsAdvRequest.priority = 2;
lbsAdvRequest.options = BT_LE_ADV_OPT_CONNECTABLE;
lbsAdvRequest.minInterval = 360;
lbsAdvRequest.maxInterval = 500;
lbsAdvRequest.advertisingData = Span<bt_data>(advertisingData);
lbsAdvRequest.scanResponseData = Span<bt_data>(scanResponseData);
lbsAdvRequest.onStarted = lbs_adv_started;
lbsAdvRequest.onStopped = lbs_adv_stopped;
chip::DeviceLayer::BLEAdvertisingArbiter::InsertRequest(lbsAdvRequest);
}
可以看出start_lbs_adv()最终调用了Matter C++库函数: BLEAdvertisingArbiter::InsertRequest()。
最后我们在lbs.c文件中调用start_lbs_adv(),这样当Matter配网广播超时之后,我们的LBS广播就会自动启动。为了更快地观察到效果,大家可以把modules\lib\matter\src\include\platform\CHIPDeviceConfig.h中的CHIP_DEVICE_CONFIG_DISCOVERY_TIMEOUT_SECS定义做如下修改:
#define CHIP_DEVICE_CONFIG_DISCOVERY_TIMEOUT_SECS (3 * 60)
将编译成功的代码烧录到板子,3分钟超时过后,大家可以看到如下广播:
同时串口日志会有如下显示:
这个时候我们点击“CONNECT”,连接这个设备,会发现设备只包含LBS服务,之前的CHIPoBLE配网服务由于超时已经被解除注册了,如下:
7.3.3 C++调用外部C函数示例
现在我们完成第二个工作:当Button状态变化时,通知到手机app。
这个工作在原始的peripheral_lbs例程中,是通过在button_changed回调函数中调用bt_lbs_send_button_state()实现,由于在app_task.cpp中我们已经实现了按键的回调函数:AppTask::ButtonEventHandler,我们将在AppTask::ButtonEventHandler中调用bt_lbs_send_button_state()来实现我们的目的。
首先在app_task.cpp中包含如下文件:
#include <bluetooth/services/lbs.h>
然后直接在AppTask::ButtonEventHandler中调用bt_lbs_send_button_state(),如下:
至此,代码已全部更改完毕。可以看出C++调用外部C函数的确比较简单,没有任何多余的操作。
再次编译代码,并将新程序烧录到板子中。跟前面操作一样,通过手机app:nRF Connect连上板子后,并使能Button特征对应的CCCD,然后我们按下或者松开板子上的按键1,手机app的Button特征值会跟随一起变化,如下:
项目最终代码大家可以从这里下载:
8. Matter产品量产注意事项
nRF Connect SDK或者Matter SDK例子默认配置都是开发者模式,这种模式是不能量产的。因此,在产品量产之前,我们还需要做一些额外的配置。
8.1 Matter认证(Certification)
纯蓝牙或者Wi-Fi产品,即使不通过认证,也是可以量产的,因为蓝牙和Wi-Fi更强调他们的连接标准性。跟纯蓝牙或Wi-Fi标准不一样,Matter是一个应用级别的标准,而且Matter使用了PKI技术进行鉴定(authenticate),这就意味着Matter产品必须经过认证(certification),才能确保产品的互联互通性。
对于蓝牙和Wi-Fi新产品认证,如果它完全继承芯片原厂或者模块商的认证,那么它是可以免除ATL实验室的测试。对于Matter新产品,它必须经过ATL实验室的测试,然后才能获得认证证书。当然Matter也有快速认证,前提是你必须有一款产品已经获得过认证,新产品是在已认证产品的基础上做微小改动情况下,才能免除ATL实验室的测试。
前面也提及过,过Matter认证之前,你必须先确保你的产品已经通过蓝牙和Thread或Wi-Fi认证。而且要获得产品认证资格,你还必须加入相关联盟,这也会产生一定费用。
关于Matter认证的详细说明,请参考:
Matter认证过程中,有一份PICS文件非常重要,这个需要你提交给ATL和CSA。关于如何生成自己产品对应的PICS文件,还是请参考上面链接。
8.2 生态系统的认证
获得了Matter认证,就意味着你的产品跟所有Matter生态都是互联互通的。由于种种原因,每个生态还有自己的认证,比如Google有Works with Google Home认证,Amazon有Frustration-Free Setup(FFS)认证。这些认证其实不是必须的,看大家的意愿去选择要不要过这些认证。具体请见:
8.3 Certification Declaration(CD)固化
当你通过了CSA的Matter认证后,CSA会颁发一个CD证书给你。按照Matter规定,你每升级一次固件,都需要重做一次认证(这个认证是免费的哦),每重做一次认证,CSA都会颁发一个新的CD证书给你,然后你通过OTA把这个新CD取代老CD。
CD一个最重要的作用,就是在设备配网过程中,充当设备自证(Device Attestation)申明。因此我们需要把CD和固件一起烧入到设备中。最简单的方法,就是定义一个宏,固化到固件中,但这种方法无法更新CD。
还有一种方法,就是把CD放在一个特殊Flash区域,比如Settings或者NVS区域,这样我们就可以通过Settings或者NVS API更新它。更多细节请见:
8.4 如何获取PAI和DAC证书/私钥
DAC,全称Device Attestation Certificate,即设备自证证书,DAC证书和DAC私钥对每个设备来说都是唯一的,也就是大家常说的“一机一密”。PAI,全称Product Attestation Intermediate,一般一个产品类别对应一个唯一PAI,每个产品下面有许许多多的设备,所以PAI是用来签署前面的DAC证书的。
对于量产产品来说,你只能使用CSA认证过的CA机构签发的PAI以及DAC公私钥对,大家可以联系CSA授权的CA机构去购买相应证书和密钥。
在产品开发过程中,如果你不想使用SDK默认的测试证书,那么也可以通过chip-cert来生成自己的测试证书,具体请参见8.3节和8.5节的两个链接里面的介绍。
8.5 Factory data烧写
Matter产品量产的时候,有很多信息需要烧入到设备中,并且整个产品生命周期不再会改变,这些信息就称为出厂数据(factory data)。对于Matter设备来说,前面所述的DAC证书,PAI证书,供应商ID,产品ID,序列号,配网信息等,都属于出厂信息。
为了保证大家可以生成自己需要的factory data,以及把这些factory data正确烧入到设备中,nRF Connect SDK准备了两个Python脚本:generate_nrfconnect_chip_factory_data.py用来生成JSON格式的出厂数据/配对码/配对二维码等,以及nrfconnect_generate_partition.py用来把前面的JSON文件转成hex文件,这样就可以通过nrfjprog等J-link工具把hex文件烧入到设备中。
具体来说,我们可以按照下面步骤来完成出厂数据烧写工作:
- 在prj.conf中加入:
CONFIG_CHIP_FACTORY_DATA=y
然后重新编译项目,待编译成功后,将代码烧录板子中。
- 打开VS Code的nRF Connect终端,然后输入如下命令:
cd C:\ncs\v2.5.0\modules\lib\matter
python scripts/tools/nrfconnect/generate_nrfconnect_chip_factory_data.py --sn "99887766554433221100" --vendor_id 65521 --product_id 32774 --vendor_name "Nordic" --product_name "MyLock" --date "2024-02-05" --hw_ver 1 --hw_ver_str "release" --dac_cert "credentials/development/attestation/Matter-Development-DAC-FFF1-8006-Cert.der" --dac_key "credentials/development/attestation/Matter-Development-DAC-FFF1-8006-Key.der" --pai_cert "credentials/development/attestation/Matter-Development-PAI-FFF1-noPID-Cert.der" --spake2_it 1000 --spake2_salt "U1BBS0UyUCBLZXkgU2FsdA==" --discriminator 0x666 --generate_rd_uid --rd_uid e2eb609c5a793e5e9de536c211246a2e --include_passcode --passcode 666888 --product_finish "matte" --product_color "black" --out "c:/ncs/bulb/factory_data.json" --schema "scripts/tools/nrfconnect/nrfconnect_factory_data.schema" --overwrite
注意:上面的DAC和PAI还是测试用证书,记得换成自己购买的证书。sn/vendor_id/product_id/vendor_name/discriminator/passcode/spake2_it/spake2_salt等等其他参数也需要按照你们的需求做相应调整。
日志如下所示:
- 确认configuration\nrf52840dk_nrf52840(或者其他板子对应目录)对应的pm_static.yml包含factory_data分区,并记下分区的起始地址,下面需要用到这个地址值。
- 然后在VS Code nRF Connect终端中执行如下命令:
python scripts/tools/nrfconnect/nrfconnect_generate_partition.py -i c:/ncs/bulb/factory_data.json -o c:/ncs/bulb/factory_data --offset 0xf7000 --size 0x1000
offset和size就是前面pm_static.yml文件中的address和size
执行结果如下所示:
- 把上述生成的出厂数据烧录到设备中,这可以通过如下命令实现:
nrfjprog -f nrf52 --program c:/ncs/bulb/factory_data.hex --sectorerase --verify --reset
执行结果如下所示:
这时,观察串口日志,我们发现设备的配网信息已发生改变,如下:
关于如何生成和烧写出厂信息,详细说明请参考: