1. Home
  2. 博客

    Matter开发,看这一篇就够了

Matter开发,看这一篇就够了 2024-02-21 Kevin Ai, Nordic Semiconductor

Matter(以前称为 Project Connected Home over IP 或 Project CHIP)是由CSA联盟制定的一个应用层面的标准,旨在打造一个统一的智能家居应用标准,以消除智能家居市场的碎片化。

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/

Matter技术包含了三份规格书及其他要求文档

其中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资源

Matter是一个非常复杂而又现代的标准,但是短短4年时间,Matter就实现了从提出到落地到追捧的成绩,不得不说这是一个“Matter奇迹”。下面我们一起来了解一下这个“奇迹”的更多细节。

1.1 Matter架构

如下展示了Matter协议栈在整个系统中的位置,可以看出Matter就是一个应用协议。

Matter协议栈在整个系统中的位置

Matter协议栈本身又是由application, data model, interaction model, action framing, security, Message Framing and Routing和Transport and IP Framing组成,如下:

Matter协议栈

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拓扑结构

一般来说,一个Matter网络称为一个Fabric,共享同一个根操作证书的所有节点可以归为同一个Fabric,简单来说,一个生态就是一个Fabric(当然也可以包含多个),比如家里同时有苹果Google亚马逊的音箱,那么你可以认为你家里有三个Matter fabric,每个fabric是独立的,但这里要强调的是,Matter支持一个设备接入多个fabric,也就是可以同时用苹果Google亚马逊控制同一个Matter设备,比如门锁。

1.3 Matter数据模型(Data Model)和设备类型

如下为一个典型的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条或者多条信息,如下:

每个action包含1条或者多条信息

下面我们以门锁为例子来具体讲讲interaction模型。

1.4.1 Interaction例子:门锁

下面例子假设initiator为Matter controller,target为door lock。

Read interaction

如下为读取DoorLock cluster 中的LockType attribute 的交互图:

LockType attribute 的交互图

Write interaction

如下为修改DoorLock cluster 中的OperatingMode attribute的交互图(修改为privacy模式意味着门锁只能从建筑内打开):

OperatingMode attribute的交互图

Invoke interaction

如下为调用DoorLock cluster的UnlockDoor command的交互图,其中Timed request用来定时命令的有效时间,为可选项。

UnlockDoor command的交互图

Subscribe interaction

如下为订阅DoorLock cluster 的LockState attribute状态值的交互图,这是一个持续进行的交互,除非一方停止或者返回失败。

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。

两种典型的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中选择的开发板类型为:

  • nrf52840dk_nrf52840

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,如下:

Matter SDK锁定的nRF Connect SDK版本

由于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 前置安装

在我们开始正式的开发环境配置之前,我们先需要下载如下三个工具:

  • Visual Studio Code:https://code.visualstudio.com/,这个就是我们的跨Windows/Linux/MacOS平台的IDE工具。
  • nRF command line tools:https://www.nordicsemi.com/Software-and-Tools/Development-Tools/nRF-Command-Line-Tools/Download#infotabs,这个就是j-link驱动以及nrfjprog等Nordic提供的一些有用的命令行工具。
  • west,git和Python,这三个工具是可选的,不过大家手动安装他们一下,某些场合还是蛮有用处的。请注意,即使大家没有手动安装这3个工具,Nordic工具链也会自动包含这三个工具,只不过大家使用工具链里面自带的工具,会有一点麻烦,所以这里建议大家可以先手动安装他们。

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插件:

nRF插件

4.2.2 nRF Connect SDK安装

由于nRF Connect SDK放在GitHub服务器上,下载和安装nRF Connect SDK的时候请一定要使用VPN,否则很有可能就会下载不完整或者失败。

上面的nRF Connect for VS code插件安装成功后,点击左边的插件图标,进入WELCOME面板,选择Manage SDKs,如下:

选择Manage SDKs

然后在右边列表框中选择Install SDK,如下:

在右边列表框中选择Install SDK

然后选择相应版本的nRF Connect SDK,如下:

选择相应版本的nRF Connect SDK

请注意,如果你是一个新用户,强烈建议你选择最新版本,即列表里面版本最高的版本,上面是v2.5.0(截止本文第一次发表时),但是当你读到这时,最高版本有可能已经到v2.6.0,v2.7.0,甚至更高,请选择此时最高版本。(注意:*.*.99之类的版本是开发专用版本,不能用于量产)

选择好版本后,然后选择SDK安装根目录,一般使用默认推荐的目录即可,如下。

大家千万不要使用很长的目录作为安装根目录,否则在Windows上编译例子的时候,经常会碰到目录名太长的编译报错。

一般使用默认推荐的目录即可

然后VS code开始下载nRF Connect SDK,如下:

VS code开始下载nRF Connect SDK

output

下载完成之后,你就可以打开SDK所在的根目录,如下:

下载完成之后,你就可以打开SDK所在的根目录

SDK在下载过程中,经常碰到下载不完整的情况,而且这种情况又不会报错,为此我们可以通过下面的方式去检验nRF Connect SDK是否下载完整和正确,选择Manage west workspace,然后选择West Update,如下:

West Update

如果nRF Connect SDK还缺少一些组件没有下载完整,此时在OUTPUT窗口,你将会看到类似下面这样的报错信息:

报错信息

如果nRF Connect SDK已经完整下载并正确,此时在OUTPUT窗口,你将会看到下面的信息输出:

信息输出

除了上述的VS code安装成功确认方式,我们也可以通过目录和命令行的方式来确认nRF Connect SDK是否完整安装正确。

nRF Connect SDK和工具链安装成功后,都放在Windows如下目录里面:

Windows如下目录

打开CMD,进入相应SDK根目录,然后输入命令:git show,以确认安装版本是否正确:

打开CMD

然后,我们可以手动输入命令:west update,以同步nRF Connect SDK所有仓库,从而确认SDK是否下载完整和正确,如下:

手动输入命令:west update

确认SDK是否下载完整和正确

如果最后没有出现报错信息,说明所有关联仓库都已经下载并同步成功,nRF Connect SDK已经下载完整和正确。

4.2.3 Toolchain安装

nRF Connect SDK开发使用的Toolchain也可以通过VS Code直接安装,上面的nRF Connect for VS code插件安装成功后,点击左边的插件图标,进入WELCOME面板,然后选择Manage toolchains,在右边的列表框中选择Install Toolchain,如下:

在右边的列表框中选择Install Toolchain

根据你选择的nRF Connect SDK版本选择对应版本的Toolchain,比如nRF Connect SDK v2.5.0对应的Toolchain安装如下所示:

nRF Connect SDK v2.5.0对应的Toolchain安装如下所示

然后开始下载Toolchain,如下:

开始下载Toolchain

下载成功后,自动解压和安装,如下:

下载成功后,自动解压和安装

如下为安装成功的提示:

安装成功的提示

这里需要注意的是,Toolchain安装可以不用VPN,但使用VPN可以让安装更稳定更可靠。

如果你PC里面有多个版本的Toolchain,你可以选择任一版本作为当前版本,如下:

set active toolchain

manage 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终端

这样就保证了该命令行的环境是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,如下:

Open Existing Application

选择目录:nrf/samples/matter/lock,如下:

选择目录:nrf/samples/matter/lock

打开上述项目后,将自动跳出Add Build Configuration页面,在Board列表框中,选择开发板:nrf52840dk_nrf52840,如下:

Add Build Configuration页面

然后选择Configuration选项,如果留空,默认选择prj.conf,大家也可以选择其他配置项。

选择Configuration选项

其他一些配置项的说明如下所示:

其他一些配置项的说明如下所示

选择Build Configuration,开始编译,视你的PC配置情况,编译有可能耗时达10分钟之久,编译成功后,你将看到如下输出:

编译成功后,你将看到如下输出

整个VS code一览图如下所示:

整个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://github.com/project-chip/connectedhomeip.git

git

上面命令将把Matter SDK公共的仓库都下载下来,而且自动切换到Matter SDK的master分支,如果你要切换到一个特定的tag,比如v1.2.0.1,请执行:

git checkout v1.2.0.1

git checkout v1.2.0.1

由于Matter SDK支持众多平台和厂商,我们还需要下载这些第三方的仓库,为此,我们再输入如下命令:

cd C:/ncs/connectedhomeip/scripts
python checkout_submodules.py --platform nrfconnect

platform参数

上面的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终端

这样就保证了该命令行的环境是nRF Connect SDK Toolchain设置的环境,在该终端中执行的任何命令都是针对nRF Connect SDK Toolchain环境来说的。

我们输入如下命令:

pip3 install -r C:/ncs/connectedhomeip/scripts/setup/requirements.build.txt

这样系统就会自动补全nRF Connect SDK Toolchain缺少的Python包,如下所示:

这样系统就会自动补全nRF Connect SDK Toolchain缺少的Python包

5.1.3 安装合适的zap工具

虽然前面nRF Connect SDK开发环境搭建过程中,已经安装好了zap工具,但是这个zap工具的版本有可能跟Matter SDK要求的zap工具版本不一样,为此我们还需要再安装一次期望版本的zap工具。

首先我们需要先确认zap工具的版本对不对,大家可以通过scripts/setup/zap.version这个文件确认:

zap工具

这就意味着对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变量值:

我电脑上的解压缩路径和Path变量值

zap

如果你的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上进行编译,修改的地方如下所示:

  1. 在connectedhomeip/build_overrides目录下,新建一个空文件,文件名为:pigweed_environment.gni。这个文件创建一次就可以,然后可以适用所有Matter工程。

build overrides

  1. 修改一些编译脚本。请大家将

对应的文件下载下来,命名为30982.diff,放在c:/ncs目录下,然后执行

git apply c:/ncs/30982.diff

就可以自动修改相应脚本以支持Windows,如下:

自动修改相应脚本以支持Windows

注意:这个补丁是针对Matter SDK版本v1.2.0.1的,Matter SDK未来版本有可能会把这个PR合并进去,这样这个补丁就没有必要再打了。

  1. 修改每个项目的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。

Matter SDK最新的tag是v1.2.0.1

为此我们选择v1.2.0.1这个tag来演示我们下面例子的编译和测试。我们在CMD中输入如下命令:

git checkout v1.2.0.1
git submodule update

CMD中输入如下命令

我们还是以Matter SDK原生的lock例子为例:connectedhomeip\examples\lock-app\nrfconnect,来阐述整个编译过程。

首先修改CMakelists.txt文件(前面说得pigweed_environment.gni这个空文件记得要先创建),得到:

CMakelists

然后像编译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,如下:

nrfconnect-recommended-revision

说明它要求的nRF Connect SDK版本是v2.4.0,接下来我们就要在WELCOME标签页中选择nRF Connect SDK v2.4.0以及对应的Toolchain,如下:

nRF Connect SDK版本是v2.4.0

然后选择Open Existing Application,找到目录:connectedhomeip\examples\lock-app\nrfconnect,如下:

选择Open Existing Application,找到目录

然后选择开发板:nrf52840dk_nrf52840

然后选择开发板:nrf52840dk_nrf52840

然后选择“Build Configuration”开始编译,编译成功后,将有如下输出日志:

然后选择“Build Configuration”开始编译,编译成功后,将有如下输出日志

点击Flash,将程序下载到开发板:

点击Flash,将程序下载到开发板:

程序运行起来后,将有如下日志输出:

程序运行起来后,将有如下日志输出:

按下Button4,设备就会开始蓝牙广播,表示已经做好配网准备,可以接入Matter生态了:

可以接入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,如下:

home app

跳出下面的扫描窗口,将扫描窗口对准上面的二维码,扫描成功后,配网真正开始。

add accessory

大家也可以从日志看出,配网流程正在进行中,下面就是截取配网流程的一个小片段的日志:

一个小片段的日志

过一会,iOS设备就会弹出如下对话框,这个是因为我们使用的是测试DAC证书和测试用PID等,请大家参考第8章如何获取生产用的证书和ID等信息,这里我们选择“Add Anyway”。

add anyway

设备配网成功后,iOS设备会连续出现如下界面:

选择这个设备所在的位置:

lock location

给这个设备取一个好记的名字:

lock name

如果是锁设备类型,还需要设置它的访问密码:

access code

设置一些自动化的场景:

lock auto

最后显示设备添加成功的界面,如下:

lock added

同时大家也可以从Home app的主界面看到这个添加成功的设备,如下:

添加成功的设备

除了从iOS设备上观察设备是否添加成功,你也可以从日志输出来佐证设备是否添加成功,设备添加成功后,你一般会看到如下红色标注的日志:

你一般会看到如下红色标注的日志:

设备添加成功后,我们就可以对设备进行各种操作,比如我们去开锁,开锁可以通过Home app直接操作,也可以通过语音Siri来操作,不过二者有点不一样。通过Home app操作的话,你直接点击如下图标即可:

通过Home app操作的话,你直接点击如下图标即可:

通过Siri可以发出开锁命令,但是它无法开锁,必须通过Home app确认才能开锁(这个是出于安全考虑),即Siri发出开锁命令后,Home app会收到如下确认信息,确认后才能开锁:

Home app会收到如下确认信息,确认后才能开锁:

关锁

关锁操作相对安全一些,因此Home app和Siri都可以直接执行关锁操作,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,如下:

VS Code的nRF Connect

然后选择Create New Application,并选择Copy a sample,如下:

然后选择Create New Application,并选择Copy a sample,如下

然后选择Matter template例程,如下:

然后选择Matter template例程,如下:

选择例程放置在哪里,如下:

选择例程放置在哪里,如下:

选择Add to Workspace:

选择Add to Workspace:

然后就可以按照4.3节的做法来编译这个例子,选择开发板:nrf52840dk_nrf52840,如下:

然后就可以按照4.3节的做法来编译这个例子

然后选择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窗口如下所示:

cmd窗口如下所示:

同时弹出ZCL窗口:

同时弹出ZCL窗口:

可以看出,例子目前只有Endpoint 0,选择Endpoint 0,并选择“Enabled Clusters”,我们可以看到这个Endpoint使能了哪些cluster,比如Descriptor cluster。

下面我们添加一个Endpoint以支持Matter On/Off Light设备,选择“ADD ENDPOINT”,在弹出的对话框中,选择“Matter On/Off Light”,如下:

Matter On/Off Light

请确认on/off cluster已使能,然后点击配置图标。

请确认on/off cluster已使能,然后点击配置图标。

在弹出的对话框中,确认On/Off Attribute已经使能。

在弹出的对话框中,确认On/Off Attribute已经使能。

同时确认Off和On命令已使能:

同时确认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

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,如下:

添加到CMakeLists.txt

然后我们就可以编译了,成功后会有如下显示信息:

然后我们就可以编译了,成功后会有如下显示信息

7.1.3 测试

我们可以按照第6章的方式,测试这个新的Matter设备,这里就不再赘述了。设备配网成功后,iOS Home app将会看到如下设备:

设备配网成功后,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文件中添加此文件:

然后在CMakeLists.txt文件中添加此文件:

然后我们需要对lbs.c文件作几处修改。首先main()函数已经被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,将看到如下广播:

打开手机app:nRF Connect,将看到如下广播:

点击“CONNECT”,连接成功后,我们将看到LBS服务,如下:

点击“CONNECT”,连接成功后,我们将看到LBS服务,如下:

然后我们可以对LED这个特征进行操作:ON或者OFF,板子上的LED3将跟随命令而变化。

板子上的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(),实现代码如下所示:

/*
 * Copyright (c) 2021 Nordic Semiconductor ASA
 *
 * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
 */

#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分钟超时过后,大家可以看到如下广播:

3分钟超时过后,大家可以看到如下广播:

同时串口日志会有如下显示:

同时串口日志会有如下显示:

这个时候我们点击“CONNECT”,连接这个设备,会发现设备只包含LBS服务,之前的CHIPoBLE配网服务由于超时已经被解除注册了,如下:

之前的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(),如下:

调用bt_lbs_send_button_state(),如下:

至此,代码已全部更改完毕。可以看出C++调用外部C函数的确比较简单,没有任何多余的操作。

再次编译代码,并将新程序烧录到板子中。跟前面操作一样,通过手机app:nRF Connect连上板子后,并使能Button特征对应的CCCD,然后我们按下或者松开板子上的按键1,手机app的Button特征值会跟随一起变化,如下:

手机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文件烧入到设备中。

具体来说,我们可以按照下面步骤来完成出厂数据烧写工作:

  1. 在prj.conf中加入:
CONFIG_CHIP_FACTORY_DATA=y

然后重新编译项目,待编译成功后,将代码烧录板子中。

  1. 打开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等等其他参数也需要按照你们的需求做相应调整。

日志如下所示:

日志如下所示:

  1. 确认configuration\nrf52840dk_nrf52840(或者其他板子对应目录)对应的pm_static.yml包含factory_data分区,并记下分区的起始地址,下面需要用到这个地址值。

下面需要用到这个地址值。

  1. 然后在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

执行结果如下所示:

执行结果如下所示:

  1. 把上述生成的出厂数据烧录到设备中,这可以通过如下命令实现:
nrfjprog -f nrf52 --program c:/ncs/bulb/factory_data.hex --sectorerase --verify --reset

执行结果如下所示:

5.	把上述生成的出厂数据烧录到设备中,

这时,观察串口日志,我们发现设备的配网信息已发生改变,如下:

我们发现设备的配网信息已发生改变,如下:

关于如何生成和烧写出厂信息,详细说明请参考:

订阅Nordic新闻简报

了解最新信息!订阅后即可获取最新Nordic及物联网资讯

立即订阅