Zigbee SDK FAQ

更新时间:2022-02-23 06:44:20下载pdf

Zigbee 网关如何识别设备?

设备加入 Zigbee 网络后,网关会根据读取到的设备基础信息来识别一个设备。基础信息配置正确是网关能正确识别设备的前提。

在开发之前,您需要在工程文件中的 package.json 文件中配置好相关的基础信息。

package.json { "fimwareInfo": { "name": "oem_si32_zg_xxxxxx", //固件名称 "description": "Zigbee device", //固件描述,用户自己填充 "version": "1.0.9", //固件版本号, "bv_version": "1.0", //SDK版本号 "ic": "efr32mg13p732gm48", // Zigbee 芯片型号 "ota_image_type":"0x____", //OTA 升级识别使用,目前固定 "manufacture_id":"0x____", //OTA 升级识别使用,目前固定 "model_id":"XXXXXX", //设备组网快速识别设备类型使用 "pid": "XXXXXXXX", //设备默认产品 id,产品经理分配 "manufacture_name": "XXXXXXXXX" //产品id前缀,网关识别用什么协议来交互, } … }

设备接受到网关命令后,由哪个函数处理?

ZCL_CMD_RET_T dev_msg_recv_callback(dev_msg_t *dev_msg) 回调函数处理。设备接收到网关 ZCL 层命令后,可以解析命令后做出相应的处理。

例如,开关设备处理网关发送的开关指令的相关代码如下:

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: //private data processing { break; } case CLUSTER_ON_OFF_CLUSTER_ID: //0x0006 Zigbee ON OFF Cluster //standard data processin { 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: //关命令 { __dev_switch_op(dev_msg->endpoint, DEV_IO_OFF); //控制硬件接口 __dev_report_onoff_msg(dev_msg->endpoint, QOS_0);//上报网关设备开关状态 break; } case CMD_ON_COMMAND_ID: //开命令 { __dev_switch_op(dev_msg->endpoint, DEV_IO_ON); __dev_report_onoff_msg(dev_msg->endpoint, QOS_0); break; } case CMD_TOGGLE_COMMAND_ID: //反转命令 { __dev_switch_op(dev_msg->endpoint, (DEV_IO_ST_T)!g_relay_onoff_status[dev_msg->endpoint - 1]); __dev_report_onoff_msg(dev_msg->endpoint,QOS_0); break; } default: { break; } } break; } } default: // Unrecognized cluster ID, error status will apply. break; } return result; }

一个设备如何发送数据到其他 Zigbee 设备?

可以通过 dev_zigbee_send_data() 来发送数据到其他 Zigbee 设备。该函数数据参数赋值,可以参考 Zigbee SDK Demo 说明 中的使用方法。

例如,设备向网关发送的开关信息的相关代码如下:

static void __dev_report_onoff_msg(uint8_t endpoint, SEND_QOS_T qs) { dev_send_data_t send_data; memset(&send_data, 0, sizeof(dev_send_data_t)); send_data.zcl_id = 0; send_data.qos = qs; //重传类型 send_data.direction = ZCL_DATA_DIRECTION_SERVER_TO_CLIENT;//数据方向 send_data.command_id = CMD_REPORT_ATTRIBUTES_COMMAND_ID; //Report命令 send_data.addr.mode = SEND_MODE_GW; //上报给网关 send_data.addr.type.gw.cluster_id = CLUSTER_ON_OFF_CLUSTER_ID; //Cluster ID send_data.addr.type.gw.src_ep = 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; //属性 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] = g_relay_onoff_status[endpoint - 1]; //数据 dev_zigbee_send_data(&send_data, NULL, 1000); //发送Zigbee数据 __dev_status_save(endpoint); //保存属性 }

如何配置 Router 设备和 End device 设备?

在上电初始化函数 dev_power_on_init() 中配置设备类型,具体配置方式可以参照对应的 Demo 工程。

例如:

  • 开关(Switch)配置的 Router 节点类型:

    zg_dev_config_t g_zg_dev_config; g_zg_dev_config.dev_type = ZG_ROUTER; // 设置 Router 类型,常供电设备 g_zg_dev_config.config.router_cfg.reserved = 0; dev_register_zg_dev_config(&g_zg_dev_config);
  • 传感(Sensor)配置的 End device 节点类型:

    // 配置低功耗设备类型,搜网参数,data poll 参数,重连参数 zg_dev_config_t st_zg_dev_config; memset(&st_zg_dev_config, 0, sizeof(st_zg_dev_config)); // device type choice st_zg_dev_config.dev_type = ZG_SLEEPY_END_DEVICE; // 设置 End device 类型,低功耗设备 st_zg_dev_config.beacon_send_interval_for_join = CHANNEL_SW_PER_MS; // Channel switching period st_zg_dev_config.zb_scan_duration = ZB_SCAN_DURATION_3; // RF rx open interval st_zg_dev_config.beacon_send_interval_for_rejoin = BEACON_PER_MS; // rejoin beacon send interval //配置 data poll 相关参数 // poll parameter registe st_zg_dev_config.config.sleep_dev_cfg.poll_conifg.poll_interval = POLL_INTERVAL_MS; st_zg_dev_config.config.sleep_dev_cfg.poll_conifg.wait_app_ack_time = WAIT_APP_ACK_MS; st_zg_dev_config.config.sleep_dev_cfg.poll_conifg.poll_forever_flag = POLL_FOREVER; st_zg_dev_config.config.sleep_dev_cfg.poll_conifg.poll_failed_times = POLL_MISS_MAX; // 配置 rejion 相关参数 // rejoin parameter registe st_zg_dev_config.config.sleep_dev_cfg.rejoin_config.next_rejoin_time = (NEXT_REJOIN_PER_HOUR*60*60*1000); // rejoin group interval st_zg_dev_config.config.sleep_dev_cfg.rejoin_config.wake_up_time_after_join = JOINED_CONTINUE_POLL_TIME_MS; // continuous poll interval after joined st_zg_dev_config.config.sleep_dev_cfg.rejoin_config.wake_up_time_after_rejoin = REJOINED_CONTINUE_POLL_TIME_MS; // continuous poll interval after rejoined st_zg_dev_config.config.sleep_dev_cfg.rejoin_config.rejoin_try_times = BEACON_TIME; // try rejoin times st_zg_dev_config.config.sleep_dev_cfg.rejoin_config.power_on_auto_rejoin_flag = POWER_ON_REJOIN; st_zg_dev_config.config.sleep_dev_cfg.rejoin_config.auto_rejoin_send_data = AUTO_REJOIN_POLL; dev_register_zg_dev_config(&st_zg_dev_config);

设备如何搜索 Zigbee 网络和加入网络?

通过 dev_zigbee_join_start( ) 函数搜索 Zigbee 网络。如果设备已经加入网络,该函数会退网并立即搜网。

设备如果要加入 Zigbee 网络,需要用户在应用上(例如 涂鸦智能 App)在 Zigbee 网关界面点击 添加子设备 ,并让设备进入搜网状态,设备入网后,会进入网络状态改变函数 nwk_state_changed_callback()

void nwk_state_changed_callback(NET_EVT_T state) { switch(state) { case NET_POWER_ON_LEAVE: // 上电检测到设备没有加入网络 { break; } case NET_JOIN_START: //设备正在搜网 { dev_led_start_blink(NET_LED_1_IO_INDEX, 250, 250, DEV_LED_BLINK_FOREVER, DEV_IO_OFF); //搜网时LED灯250ms间隔闪烁 break; } case NET_JOIN_TIMEOUT: //设备搜网超时 { dev_led_stop_blink(NET_LED_1_IO_INDEX, DEV_IO_ON); break; } case NET_POWER_ON_ONLINE: //上电检测到设备已加入网络 case NET_JOIN_OK: //设备入网 { dev_led_stop_blink(NET_LED_1_IO_INDEX, DEV_IO_OFF); break; } case NET_LOST: //End device 丢失父节点 { break; } case NET_REMOTE_LEAVE: // APP 上删除设备 { break; } case NET_LOCAL_LEAVE: // 设备本地退网 { break; } default: { break; } } }

初始化串口时,我需要注意什么?

涂鸦 Zigbee 模组 SDK 默认包含串口产测功能,使用的串口配置信息在 mf_test_uart_config() 函数中。

产测超时后,产测串口被禁用。如果用户需要使用串口,可以在 dev_system_on_init() 函数中进行初始化。

注意:不能在 dev_power_on_init() 函数中进行初始化。

// 串口初始化配置 user_uart_config_t uart_config = APP_UART_CONFIG_DEFAULT; // 默认配置,根据需要自己修改 uart_config.func = serial_rx_callback; // 串口接收回调函数 user_uart_init(&uart_config);

如何使用通用输入 I/O?

按键初始化函数 gpio_button_init() 一般在 dev_power_on_init() 函数中进行。请注意,按键初始化函数不要调用多次。一旦按键按下后,按键回调函数会周期性进入,可以根据传入的参数来判断短按,长按,松开。

如果不作为按键类型处理,可以使用中断初始化 gpio_int_register()

// 输入 IO 口中断方式初始化 void gpio_int_register(gpio_config_t *config, gpio_int_func_t func); // 按键初始化 gpio_button_init((gpio_config_t *)gpio_input_config, get_array_len(gpio_input_config), 50, __dev_key_handle); //处理按键的回调函数,按键有效会间隔 20 ms 进入该回调函数,直到按键被抬起 static void __dev_key_handle(uint32_t key_id, key_st_t key_st, uint32_t push_time) { uint8_t ep = 1; if(EP_SUMS <= key_id) { return; } if(g_work_st == DEV_WORK_ST_TEST) // mf test mode { if(key_st != KEY_ST_PUSH) { dev_mf_button_test_notify(key_id); //按键产测上报 } return; } switch(key_id) { case KEY_1_IO_INDEX: //按键 ID { if(key_st == KEY_ST_PUSH) //按键按下 { } else //按键松开 { } break; } default: { break; } } }

如何使用通用输出 I/O?

通用 I/O 初始化函数一般在 dev_power_on_init() 中进行初始化。请注意不要调用多次。

//通用输出 IO 口初始化 gpio_output_init((gpio_config_t *)gpio_ouput_config, get_array_len(gpio_ouput_config)); //与之配套常用的数据 IO 口操作函数 //可以操作 IO 高低电平保持时间和次数,例如 LED 灯闪烁 void dev_led_start_blink(uint8_t led_index, uint16_t on_time, uint16_t off_time, uint16_t blink_times, DEV_IO_ST_T st); //操作 IO 的输出开关状态,同时会停止对应 led_index 输出闪烁。 void dev_led_stop_blink(uint8_t led_index, DEV_IO_ST_T st);

如何进行设备产测?

SDK 底层已经对部分产测测试项进行过处理。例如,进入测试模式,读取 Mac,检测固件版本,写入 PID,读取 PID,进行 RF 测试,获取 RSSI 信号值,授权测试等。

您需要处理的内容可以在回调函数 dev_mf_test_callback() 中处理,串口产测和整机产测通用同一个处理方式。

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: //所有 LED 灯开 { 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: //所有 LED 灯关 { 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: //所有 LED 灯闪烁 { 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: //按键产测,APP 会等待按键按下 { g_work_st = DEV_WORK_ST_TEST; return MF_TEST_DOING; //等待测试 } case MF_TRANSFER: //其他测试项可以通过这个项进行详细解析处理 { break; } default : { break; } } return MF_TEST_SUCCESS; }

低功耗设备在进行网关产测时,需要在进入产测时,加快数据请求。您需要在 MF_TRANSFER 对应的 case 下进行以下处理。

typedef struct{ uint8_t test_cmd; uint16_t test_data_len; uint8_t test_data[128]; }dev_test_t; ... case MF_TRANSFER: { dev_test_t dev_test; dev_test.test_cmd = args[0]; dev_test.test_data_len = arg_len; if(arg_len > 1 && arg_len < 128) { memcpy(dev_test.test_data,&args[1],arg_len -1); } switch(dev_test.test_cmd) { case MF_ENTER_TEST: //进入产测 { zg_poll_interval_change(1000); zg_poll_start(); //Start data request break; } case MF_DIGIT_TEST: { return MF_TEST_DOING; break; } default : { break; } } break; } ...

如何添加和调用场景?

移动应用上配置添加场景时,会发送控制命令和添加场景命令给设备。第一条控制命令和最后的添加场景命令,会在 15s 内完成,收到添加场景命令后,设备需要保存 15s 内控制的设备的状态,以便在场景调用时使用。

// 收到添加场景命令后,会进入添加场景回调 void dev_scene_add_callback(uint8_t endpoint, uint8_t *out_data, uint8_t *in_out_len) { *in_out_len = 0; out_data[(*in_out_len)++] = SceneOnOff_Flag; //如果设备 15s 内控制,该位是 TRUE out_data[(*in_out_len)++] = Dev_onoff_status[endpoint-1]; //保存设备的状态 } // 收到调用场景命令后,会进入调用场景回调 void dev_scene_recall_callback(uint8_t endpoint, const scene_save_data_t *in_data) { uint8_t dataOffset = 0; switch(in_data->type) { case SCENE_DATA_TYPE_USER_DEFINE: { if(in_data->data[dataOffset] == SCENE_TRUE) //判断场景是否有效 { Dev_OnOff_op(endpoint,in_data->data[dataOffset + 1],TRUE);//控制设备 } break; } case SCENE_DATA_TYPE_SERVER_DEFINE: //根据服务器的配置格式解析 break; case SCENE_DATA_TYPE_ZIGBEE_DEFINE: //根据 zigbee 标准协议格式解析 break; default: break; } }

设备如何 OTA 升级,我需要注意什么?

Zigbee 模组 SDK 自带 OTA 升级功能。工程开发编译完成后,会生成后缀为 .s37 的烧录固件以及后缀为 .bin 的 OTA 升级固件。

设备可以通过网关升级固件版本。如果 OTA 版本固件比当前版本低或者一致,就不能进行升级。高版本固件上传到涂鸦 IoT 平台,可以给要升级的设备配置白名单进行升级,详情请参考 选择和管理固件版本

  • 对于 Router 节点(常供电设备),您不需要对 OTA 升级进行额外的开发。

  • 对于 End device 节点(低功耗设备),因为低功耗设备长期进行休眠,关闭了接收 RF 数据能力,所以在移动应用上操作设备升级后,需要唤醒设备发送数据请求获取升级命令,并且设备需要在以下回调函数中处理相关逻辑:

    // OTA 升级回调 void zg_ota_evt_callback(ZG_OTA_EVT_T evt) { switch(evt) { case ZG_OTA_EVT_START: { //开始升级 zg_poll_interval_change( 250 ); // 配置数据请求间隔时间为 250ms,以加快升级。 zg_poll_start(); break; } default: { zg_poll_interval_change( 1000 ); zg_poll_end(); // 升级完成后,停止数据请求,避免电量浪费。 break; } } }

设备授权指的是什么,如何授权?

授权是产测中的其中一个测试项。涂鸦云模组烧录工具软件,以及 PCBA 产测软件都可以对模组进行授权。涂鸦云模组烧录工具进行烧录时,会自动对模组进行授权。只有对设备授权后,设备才能长期正常运行。

涂鸦 SDK 授权功能,已经在模组 SDK 内部实现,您无需开发。

说明:通过 J-Link 软件烧录的固件的设备,不能长期的运行,只能正常运行一周。固件开发好后,可以上传到涂鸦后台,进行云模组烧录工具进行授权。