更新时间:2024-11-20 08:51:25下载pdf
本文以 SDK 内的智能单插 Demo 为例,介绍如何使用涂鸦标准模组 Zigbee SDK 开发,开发一款智能单插产品。
在 涂鸦开发者平台,进入 硬件开发 阶段后,选择 涂鸦标准模组 SDK 开发 下载的 SDK 开发资料包内,有丰富的应用 Demo 可以用于参考开发应用代码。智能单插 Demo 就众多 Demo 中的其中之一。
解压下载到的 SDK 开发资料包后,您可以在 apps
文件夹下找到众多 Demo 例程。其中,插座 Demo 的目录结构如下:
app
目录包含的文件和文件夹结构如下所示:
app
├── build-all.py
├── light
├── sensor
├── smart_plug
└── switch
├── common
│ ├── include
│ └── src
└── project
└── sample_switch1
├── documents
├── EFR32MG13P732F512
├── EFR32MG21A020F768
├── include
├── src
├── pre-build.py
└── pre-build-iar.py
下表介绍了 sample_switch1
文件夹中包含的文件说明:
目录 | 说明 |
---|---|
EFR32MG13P732F512 | 芯片是 EFR32MG13P732F512 的编译的入口,包含 IAR 和 GCC 编译的工程文件 |
EFR32MG21A020F768 | 芯片是 EFR32MG21A020F768 的编译的入口,包含 IAR 和 GCC 编译的工程文件 |
include | 存放应用工程的 .h 格式的头文件 |
src | 存放应用工程的 .c 格式的源文件 |
pre-build.py | GCC 编译用到的脚本,应用无需修改 |
pre-build-iar.py | IAR 编译用到的脚本,应用无需修改 |
switch/common | 如果多个功能有公共编译文件可以放到这里面 |
本章节介绍 Demo 代码是如何实现插座的功能,以及您可以怎么参考 Demo,并进行应用代码的开发。
设备与云端交互的逻辑主要概括如下:
数据交互方式。
Zigbee 子设备的数据先是上传到涂鸦网关,涂鸦网关将 Zigbee 协议解析成涂鸦标准协议,与云端使用 JSON 数据格式进行交互,数据的标识和定义是在平台第一步选择功能点处配置的。
设备标识。
package.json
文件中写入 PID 信息。package.json
文件中设置。其中 name 字段一般指的是固件标识名,version 指的是版本号。注意:固件标识名和固件版本号一定要和平台上创建的版本号一样,否则用云模组烧录平台会授权失败。
连接云端前授权。
设备要连接到云端,需要进行授权,Zigbee 的设备如果不授权只能免费使用 7 天。授权码的获取和授权的流程请参考 通用 Wi-Fi SDK 烧录、授权和产测。
Demo 实现的功能概括如下:
功能名称 | 实现方式 |
---|---|
本地控制 | 检测按键 I/O 口状态,控制继电器 I/O 口输出信号对继电器进行控制,将状态上报到网关,网关上报到云端。 |
进入组网状态 | 检测按键 I/O 口状态,如果检测到长按按键超过一定时间后(10S),调用重新组网函数,使设备进入到组网状态。 |
状态指示 | 判断设备当前的状态,并通过 LED 灯处于亮、灭、闪烁对不同状态进行指示。 |
App 下控制网关组网 | 此部分代码涂鸦 SDK 和网关已经封装完成,设备进入到配网状态后,可以在 App 上控制网关进行组网。 |
App 控制 | 解析 App 下发数据对硬件进行控制,并将状态上报到云端。 |
App 移除 | 调用移除接口,控制设备进入到配网状态。 |
语音控制 | 此部分代码与 App 对设备进行控制原理相同。 |
OTA 升级 | 接收云端推送升级固件,并更新运行。 |
产测 | 通过串口或者扫描信标进行产测。 |
在了解 Demo 代码是如何实现设备管控功能前,您需要了解 SDK 的初始化流程:
本章节主要介绍功能相关的代码介绍,部分函数对应多个功能实现,所以在对应功能内展示相关部分代码,其他部分代码使用 *****
替代。
整个流程中,功能的实现,您可以先关以下函数:
dev_power_on_init(); // 上电初始化
dev_system_on_init(); // 协议栈和系统基本组件启动后初始化
dev_mf_test_callback(); //生产测试用户回调
nwk_state_changed_callback(); //网络状态改变回调
配置固件信息
在涂鸦开发者平台创建完产品之后,首先配置固件信息。固件信息配置在对应工程下的 package.json
文件里进行配置,重要片段如下所示;
package.json
{
"fimwareInfo": {
"name": "oem_si32_zg_plug_10A_USB_dlx", //固件名称
"description": "ZigBee switch common", //固件描述
"version": "1.0.9", //软件版本号
"bv_version": "1.0", //目前固定,暂不需要更改
"ic": "efr32mg13p732gm48", // ZigBee 芯片型号
"ota_image_type":"0x1602", //OTA升级识别使用,目前固定
"manufacture_id":"0x1002", //OTA升级识别使用,目前固定
"model_id":"TS0105", //设备组网快速识别设备类型使用
"pid": "ZHahfZRP", //设备默认产品 id,IOT平台创建
"manufacture_name": "_TZ3000_",//产品id前缀,网关识别用什么协议来交互
"module_name":"ZS3L" //使用的模组型号
}
}
Zigee设备创建和组网参数设定
Zigbee 设备创建和对应的网络行为是密不可分的,这部分需要您通过 dev_power_on_init()
实现。以下代码是创建一个单插设备:
const attr_t g_group_attr_list[] = {
GROUP_ATTR_LIST
};
const attr_t g_scene_attr_list[] = {
SCENE_ATTR_LIST
};
#define ON_OFF_PRIVATE_ATTR_LIST \
{ 0x0000, ATTR_BOOLEAN_ATTRIBUTE_TYPE, 1, (ATTR_MASK_TOKEN_FAST), (uint8_t*)0x00 }, \
const attr_t g_light_attr_list[] = {
ON_OFF_PRIVATE_ATTR_LIST
};
const cluster_t g_server_cluster_id[] = {
DEF_CLUSTER_GROUPS_CLUSTER_ID(g_group_attr_list)
DEF_CLUSTER_SCENES_CLUSTER_ID(g_scene_attr_list)
DEF_CLUSTER_ON_OFF_CLUSTER_ID(g_light_attr_list)
};
#define SERVER_CLUSTER_LEN get_array_len(g_server_cluster_id)
/**
* @note 代表一个ON_OFF_LIGHT 设备,1个endpoint,
* 支持 Server端 group、scene、onoff cluster。
* 分别支持GROUP_ATTR_LIST、SCENE_ATTR_LIST、ON_OFF_PRIVATE_ATTR_LIST属性
*/
const dev_description_t g_dev_des[] = {
{ 0x01, ZHA_PROFILE_ID, ZG_DEVICE_ID_ON_OFF_LIGHT, SERVER_CLUSTER_LEN, (cluster_t *)&g_server_cluster_id[0], 0, NULL},
};
void dev_power_on_init(void)
{
zg_dev_config_t g_zg_dev_config;
join_config_t cfg;
/**
* @note 描述是一个什么设备,多少 endpoint,每个 endpoint 有什么 cluster 和 attributes。
*/
dev_register_zg_ep_infor((dev_description_t *)g_dev_des, EP_SUMS);
/**
* @note 把 Zigbee 设备设置配成一个路由设备。
*/
memset(&g_zg_dev_config, 0, sizeof(zg_dev_config_t));
g_zg_dev_config.dev_type = ZG_ROUTER;
dev_register_zg_dev_config(&g_zg_dev_config);
/**
* @note 配置上电或者远程删除后立即进入组网状态,组网超时 1 分钟。
*/
memset(&cfg, 0, sizeof(cfg));
cfg.auto_join_power_on_flag = TRUE;
cfg.auto_join_remote_leave_flag = TRUE;
cfg.join_timeout = 60000;
dev_zg_join_config(&cfg);
return;
Zigbee 设备入网和退网
通过调用 dev_zigbee_join_start()
可以实现退出之前的网络,然后尝试组网。
通过调用 dev_zigbee_leave_for_user()
可以实现只退出之前的网络。
首先可以先写一个按键处理函数,通过按键来控制入网和退网。
/*
* @note __dev_key_handle 假定是注册的按键处理函数
*/
static void __dev_key_handle(uint32_t key_id, key_st_t key_st, uint32_t push_time)
{
switch(key_id) {
case KEY_1_INDEX: {
if(key_st == KEY_ST_PUSH) { ///< 按钮处于按下状态
if(push_time == 3000) { ///< 按钮按下持续了3000ms,后让LED2 开始闪烁
dev_led_start_blink(LED_2_INDEX, 500, 500, DEV_LED_BLINK_FOREVER, DEV_IO_OFF); // /< LED1输出500ms高,300ms低,交替输出,让灯闪烁
}
}
else {
if(push_time < 3000) { ///< 按钮按下持续了不到3000ms,做短按处理
//TODO:
}
else {
dev_zigbee_join_start(60000); // /< 按键长按3s以上后,松开组网,组网超时1分钟
}
}
break;
}
case KEY_2_INDEX: {
if(key_st == KEY_ST_PUSH) { ///< 按钮处于按下状态
if(push_time == 3000) { ///< 按钮按下持续了3000ms,后让LED2 开始闪烁
dev_led_start_blink(LED_2_INDEX, 500, 500, DEV_LED_BLINK_FOREVER, DEV_IO_OFF); // /< LED1输出500ms高,300ms低,交替输出,让灯闪烁
}
}
else {
if(push_time < 3000) { ///< 按钮按下持续了不到3000ms,做短按处理
//TODO:
}
else {
dev_zigbee_leave_for_user(); // /< 按键长按3s以上后,松开退网
dev_led_stop_blink(LED_2_INDEX, DEV_IO_ON); // /< 点亮灯代表处于退网状态
}
}
break;
}
default: {
break;
}
}
}
管理 Zigbee 网关心跳
为了维护和网关之间的链路有效性,刷新路由信息。设备和网关之间会维护一个心跳信息。心跳的启动会依赖系统的软定时服务,所以需要在 dev_system_on_init()
或更晚的地方调用。如果设备和网关没有定期维护心跳,设备会在 App 上显示离线(比如断电)。
SDK 内部默认会根据设备类型产生一个周期心跳消息,您也可以显性调用更改默认行为。下面是一个在 dev_system_on_init()
实现的例子:
void dev_system_on_init(void)
{
dev_heartbeat_set(APP_VERSION, 180000); // /< 180s+30s随机值发送一次心跳
}
Zigbee 网络状态
SDK 提供一个回调函数来报告当前设备的 Zigbee 网关网络状态,应用开发无需维护网络状态,一般是通过指示灯来展示网络状态。应用要实现的回调函数例子如下:
void nwk_state_changed_callback(NET_EVT_T state)
{
switch(state) {
case NET_POWER_ON_LEAVE: {
//TODO: 上电无网络状态
break;
}
case NET_JOIN_START: {
//TODO: 组网开始
break;
}
case NET_JOIN_TIMEOUT: {
//TODO: 组网失败
break;
}
case NET_POWER_ON_ONLINE: {
//TODO: 上电有网络状态
break;
}
case NET_JOIN_OK: {
//TODO: 组网成功
break;
}
case NET_REJOIN_OK: {
//TODO: 重连成功
break;
}
case NET_LOST: {
//TODO: 和父节点丢失
break;
}
case NET_REMOTE_LEAVE: {
//TODO: 远程离网通知
break;
}
case NET_LOCAL_LEAVE: {
//TODO: 本地离网通知
break;
}
case NET_MF_TEST_LEAVE: {
//TODO: 产测离网通知
break;
}
default: {
break;
设备在成功配网之后,可以在 App 操作控制面板,来下发对应的 DP 数据。设备通过 dev_msg_recv_callback()
回调函数来接收 Zigbee 网关的相关数据。以下为一个开关的数据接收示例:
ZCL_CMD_RET_T dev_msg_recv_callback(dev_msg_t *dev_msg)
{
ZCL_CMD_RET_T result = ZCL_CMD_RET_SUCCESS;
switch (dev_msg->cluster) {
case CLUSTER_PRIVATE_TUYA_CLUSTER_ID: { // 私有数据处理
uint8_t len = dev_msg->data.bare_data.len;
uint8_t *data = dev_msg->data.bare_data.data;
//TODO: 私有协议处理
break;
}
// 标准数据处理
case CLUSTER_ON_OFF_CLUSTER_ID: {
attr_value_t *attr_list = dev_msg->data.attr_data.attr_value;
uint8_t attr_sums = dev_msg->data.attr_data.attr_value_sums;
uint8_t i;
for(i=0; i<attr_sums; i++) {
switch(attr_list[i].cmd) {
case CMD_OFF_COMMAND_ID: {
//TODO:关命令
break;
}
case CMD_ON_COMMAND_ID: {
//TODO:开命令
break;
}
case CMD_TOGGLE_COMMAND_ID: {
//TODO:取反命令
break;
}
default: {
break;
}
}
break;
}
}
default:
// Unrecognized cluster ID, error status will apply.
break;
}
return result;
}
数据上报指的是将数据上报给网关,网关将数据同步到云端及 App。
static void __send_result_cb(SEND_ST_T st, dev_send_data_t *msg)
{
switch(st) {
case SEND_ST_OK: {
//TODO: 发送成功
break;
}
default: {
//TODO: 发送失败
break;
}
}
}
static void send_data_demo(void)
{
dev_send_data_t send_data;
memset(&send_data, 0, sizeof(dev_send_data_t));
send_data.zcl_id = 0; ///< 用户自定义 id,发送成功失败回调会传回该参数
send_data.qos = QOS_1; // /< 如果没有收到ACK,会重传
send_data.direction = ZCL_DATA_DIRECTION_SERVER_TO_CLIENT;
send_data.command_id = CMD_REPORT_ATTRIBUTES_COMMAND_ID; ///< 通用上报属性的命令
send_data.addr.mode = SEND_MODE_GW; ///< 发送给网关
send_data.addr.type.gw.cluster_id = CLUSTER_ON_OFF_CLUSTER_ID; // /< 开关属性
send_data.addr.type.gw.src_ep = 1; // /< 设备 endpoint
send_data.delay_time = 0; ///< 延时发送时间
send_data.random_time = 0; ///< 随机发送时间范围
send_data.data.zg.attr_sum = 1; ///< 上报属性的个数
send_data.data.zg.attr[0].attr_id = ATTR_ON_OFF_ATTRIBUTE_ID; // /< 开光状态属性
send_data.data.zg.attr[0].type = ATTR_BOOLEAN_ATTRIBUTE_TYPE; // /< 属性数据类型
send_data.data.zg.attr[0].value_size = 1; ///< 属性数据长度
send_data.data.zg.attr[0].value[0] = 1; // /< 1代表开,0代表关状态
dev_zigbee_send_data(&send_data, __send_result_cb, 1000); // /< 发送,1000代表报文最大重传持续时间是1秒
}
语音控制与 App 控制实现原理一样,实现 App 控制的相关功能即可
您需要在平台增值服务开通语音功能后,才能使用音响对设备进行控制,具体参考 第三方音响接入服务。
产品在批量生产时,为提高生产效率,会用代码实现批量的自动化测试,通常称为产测。
产测一般用到串口,首先确定程序是否初始化对应的串口,SDK 里面会写好,代码如下:
/**
* @description: mf test uart register, aotomatic generated, not edit
* @param {void} none
* @return: user_uart_config_t mf uart config information
*/
user_uart_config_t config;
user_uart_config_t* mf_test_uart_config(void)
{
memset(&config, 0, sizeof(user_uart_config_t));
if (MODULE_NAME == TYZS2R)
{
user_uart_config_t default_config = TYZS2R_USART_CONFIG_DEFAULT;
memcpy(&config, &default_config, sizeof(user_uart_config_t));
}
else if(MODULE_NAME == TYZS5)
{
user_uart_config_t default_config = TYZS5_USART_CONFIG_DEFAULT;
memcpy(&config, &default_config, sizeof(user_uart_config_t));
}
else
{
user_uart_config_t default_config = TYZS3_USART_CONFIG_DEFAULT;
memcpy(&config, &default_config, sizeof(user_uart_config_t));
}
return &config;
}
程序里面的 MODULE_NAME
是根据 package.json 文件里面的 module_name 字段来的,如果用的模组名称再程序中没有,按照自己的模组型号来配置程序。如上段程序中没有 ZS3L 型号,您可以按照以下代码填写:
SDK 底层已经对部分产测测试项进行过处理。例如,进入测试模式,读取 Mac,检测固件版本,写入 PID,读取 PID,进行 RF 测试,获取 RSSI 信号值,授权测试等。SDK 中代码如下:
/**
* @description: device manufactury test callback, when device is in manufactury test model,
* sdk will use this callback to notify application the test item and test command;
* @param {cmd} manufactury test type
* @param {*args} manufactury test data
* @param {arg_len} manufactury test data length
* @return: none
*/
MF_TEST_RET_T dev_mf_test_callback(MF_TEST_CMD_T cmd, uint8_t *args, uint16_t arg_len)
{
switch(cmd)
{
case MF_TEST_LED_ON_ALL:
{
dev_led_stop_blink(NET_LED_1_IO_INDEX, DEV_IO_ON);
dev_led_stop_blink(LED_1_IO_INDEX, DEV_IO_ON);
break;
}
case MF_TEST_LED_OFF_ALL:
{
dev_led_stop_blink(NET_LED_1_IO_INDEX, DEV_IO_OFF);
dev_led_stop_blink(LED_1_IO_INDEX, DEV_IO_OFF);
break;
}
case MF_TEST_LED_BLINK_ALL:
{
dev_led_start_blink(NET_LED_1_IO_INDEX, 500, 500, 4, DEV_IO_OFF);
dev_led_start_blink(LED_1_IO_INDEX, 500, 500, 4, DEV_IO_OFF);
break;
}
case MF_TEST_RELAY_ON_ALL:
{
dev_led_stop_blink(RELAY_1_IO_INDEX, DEV_IO_ON);
break;
}
case MF_TEST_RELAY_OFF_ALL:
{
dev_led_stop_blink(RELAY_1_IO_INDEX, DEV_IO_OFF);
break;
}
case MF_TEST_RELAY_BLINK_ALL:
{
dev_led_start_blink(RELAY_1_IO_INDEX, 500, 500, 4, DEV_IO_OFF);
break;
}
case MF_TEST_BUTTON:
{
g_work_st = DEV_WORK_ST_TEST;
return MF_TEST_DOING;
}
case MF_TRANSFER:
{
}
default :
{
break;
}
}
return MF_TEST_SUCCESS;
}
Zigbee 模组 SDK 自带 OTA 升级功能。工程开发编译完成后,会生成后缀为 .s37
的烧录固件以及后缀为 .bin
的 OTA 升级固件。
/**
* @note (OPTION) This function is called when an OTA event occurs.Includes the beginning and end of the OTA
* @param[in] {evt} The type of OTA event. Refer to enum ZG_OTA_EVT_T definition for details.
* @return none
*/
VIRTUAL_FUNC void zg_ota_evt_callback(ZG_OTA_EVT_T evt);
以 sample_switch1
为例,固件使用 IAR 编译的步骤如下:
进入目录silicon_labs_zigbee\app\switch\project\sample_switch1\EFR32MG13P732F512
。
双击 tuya_sdk.eww
即可打开工程。
打开工程后,进行开发编译。
打开 IAR 后,建议不要通过 Open workspace 方式来打开工程,否则编译可能出错。另外,Rebuild All 之前建议 Clean 一下。
编译成功后会在 \app\smart_plug\project\sample_plug1\EFR32MG21A020F768\build\exe
目录下生成烧录用的.s37
文件和.bin
,如下图片所示:
编译出的文件,名称中尾缀为 .s37
的是生产固件,生产时使用,一般您上传和烧录都是这个 QIO 文件。
将固件上传至涂鸦开发者平台,申请激活码,通过涂鸦 云模组烧录授权平台 写入授权信息,详情参考 ZigbeeSDK烧录授权。
该内容对您有帮助吗?
是意见反馈该内容对您有帮助吗?
是意见反馈