本文首先介紹Zigbee的若干基本概念,然后針對TI Zigbee解決方案中的Zigbee網(wǎng)絡(luò)處理器(ZNP)應(yīng)用形式,介紹如何創(chuàng)建一個(gè)Zigbee網(wǎng)絡(luò),并與已有的節(jié)點(diǎn)進(jìn)行通信。
基本概念
Zigbee
Zigbee是一種短距離、低速率、低功耗的無線傳感器網(wǎng)絡(luò)技術(shù),包含了從底層網(wǎng)絡(luò)到上層應(yīng)用的完整定義,標(biāo)準(zhǔn)由Zigbee聯(lián)盟制定,最新規(guī)格為Zigbee 3.0。
多個(gè)IC廠商提供包含Zigbee技術(shù)的產(chǎn)品,本文只討論TI的解決方案。
信道
Zigbee可以在Sub-1GHz、2.4GHz等多個(gè)頻率范圍工作,一般選擇在2.4GHz頻率范圍,這個(gè)范圍中又劃分出16個(gè)具體的頻點(diǎn)(信道),對應(yīng)編號(hào)從11(0x0B)到26(0x1A)。當(dāng)同一個(gè)或相鄰近的物理空間中有多個(gè)Zigbee網(wǎng)絡(luò)時(shí),需要錯(cuò)開信道。
設(shè)備類型
Zigbee網(wǎng)絡(luò)中的設(shè)備分為三種類型:協(xié)調(diào)器(Coordinator)、路由(Router)和終端節(jié)點(diǎn)(End Device)。其中,協(xié)調(diào)器負(fù)責(zé)建立網(wǎng)絡(luò),并為新加入的節(jié)點(diǎn)分配地址(當(dāng)網(wǎng)絡(luò)建立、節(jié)點(diǎn)入網(wǎng)后,即使去掉協(xié)調(diào)器,網(wǎng)絡(luò)也能正常工作);路由負(fù)責(zé)將收到數(shù)據(jù)轉(zhuǎn)發(fā)給其他節(jié)點(diǎn),以拓展網(wǎng)絡(luò)的覆蓋范圍;終端節(jié)點(diǎn)只能接收和發(fā)送自身的數(shù)據(jù),不能轉(zhuǎn)發(fā)。
需要說明的是:(1)路由本身也具有終端節(jié)點(diǎn)的功能,可以收發(fā)自身的數(shù)據(jù);(2)終端節(jié)點(diǎn)可以不依賴路由,直接通過協(xié)調(diào)器入網(wǎng)。所以,一個(gè)完整的Zigbee網(wǎng)絡(luò),可以沒有終端節(jié)點(diǎn),只有協(xié)調(diào)器和路由;也可以沒有路由,只有協(xié)調(diào)器和終端節(jié)點(diǎn)。
網(wǎng)絡(luò)地址
Zigbee設(shè)備在出廠時(shí)都有一個(gè)唯一的8字節(jié)MAC地址,但在加入網(wǎng)絡(luò)時(shí),會(huì)被協(xié)調(diào)器分配一個(gè)兩字節(jié)的網(wǎng)絡(luò)地址,后續(xù)以這個(gè)網(wǎng)絡(luò)地址通信。協(xié)調(diào)器的網(wǎng)絡(luò)地址是0x0000。0xFFFF是廣播地址,如果將通信的目的地址設(shè)為0xFFFF,那么所有節(jié)點(diǎn)都會(huì)收到。
PANID
PANID是Zigbee網(wǎng)絡(luò)的標(biāo)識(shí),即使同一信道里有多個(gè)Zigbee網(wǎng)絡(luò),只要它們的PANID不同,也可以共存并獨(dú)立工作。
Z-Stack
Z-Stack是TI的Zigbee協(xié)議棧,包含文檔、源代碼和預(yù)編譯的庫,可以從TI網(wǎng)站上下載。Z-Stack使用IAR編譯和調(diào)試,生成的hex文件通過燒寫器燒寫進(jìn)Zigbee芯片當(dāng)中。
ZNP
ZNP,即Zigbee網(wǎng)絡(luò)處理器,是TI的Zigbee解決方案的一種應(yīng)用形式。在這種形式下,Zigbee芯片只負(fù)責(zé)處理底層網(wǎng)絡(luò)的功能,而將上層應(yīng)用交給主機(jī)處理,ZNP與主機(jī)之間通過USB或UART連接。Zigbee芯片實(shí)現(xiàn)ZNP功能所需的固件及其源碼,以及接口API的說明都包含在Z-Stack中。所謂接口API,指主機(jī)通過USB或UART與ZNP通信的幀格式,以及命令和參數(shù)的定義。在主機(jī)側(cè),TI提供了對接口API的包裝,即znp-host-framework,C語言實(shí)現(xiàn),開源,代碼托管在TI的Git網(wǎng)站上,另外在GitHub上還有適用于Node.js的版本。
安全
Zigbee網(wǎng)絡(luò)支持安全加密機(jī)制,通信數(shù)據(jù)采集AES加密。密鑰的分發(fā)有兩種方法:一種是預(yù)先配置在所有需要加入網(wǎng)絡(luò)的節(jié)點(diǎn)上;另一種是只配置在協(xié)調(diào)器上,節(jié)點(diǎn)入網(wǎng)時(shí)分發(fā)到節(jié)點(diǎn)。
配置ZNP
TI有多款芯片可以用作ZNP,我們選擇CC2538,通過其自帶的USB接口連接主機(jī),硬件dongle可以從淘寶采購。對應(yīng)的工程文件在Z-Stack的Projects\zstack\ZNP\CC2538目錄當(dāng)中。
Z-Stack中默認(rèn)開啟了安全加密,可以通過修改代碼禁用:
Projects/zstack/Tools/CC2538DB/f8wConfig.cfg
21(修改宏定義): DSECURE=0
Components/stack/bdb/bdb_touchlink.h
50(注釋掉此行): //#error SECURE must be globally defined to TRUE.
如果按照TI提供的參考設(shè)計(jì),配置了CC2592作為PA,還需要使能它:
Components/hal/target/CC2538ZNP/hal_board_cfg.h
84(增加一行): #define HAL_PA_LNA_CC2592
使用ZNP創(chuàng)建網(wǎng)絡(luò)
將CC2538 USB dongle連接到主機(jī),就可以從主機(jī)上發(fā)命令控制它了。CC2538的USB接口是一個(gè)CDC設(shè)備,在Windows上無需安裝驅(qū)動(dòng),會(huì)被識(shí)別成一個(gè)串口;在Linux系統(tǒng)上,需要確保內(nèi)核包含USB CDC設(shè)備驅(qū)動(dòng),會(huì)被識(shí)別成/dev/ttyACMx(x是序號(hào),從0開始)。
Z-Tool
Z-Tool是Z-Stack附帶的一個(gè)測試工具,可以用它給ZNP發(fā)送指令。在安裝Z-Stack以后,在安裝目錄下找到Tools\Z-Tool\Z-Tool.exe,雙擊打開,它會(huì)自動(dòng)搜索并連接ZNP。

如上圖所示,界面左上角是Z-Tool支持的各種命令,分為System、AF等多個(gè)類別。下面介紹使用這些命令控制ZNP創(chuàng)建網(wǎng)絡(luò)的步驟。
- 配置啟動(dòng)選項(xiàng),在下次復(fù)位前清除網(wǎng)絡(luò)狀態(tài)和所有配置
命令:SimpleAPI/ZB_WRITE_CONFIGURATION
參數(shù):ConfigId: 3(ZCD_NV_STARTUP_OPTION),Len: 1,Value: [3] - 復(fù)位
命令:SimpleAPI/ZB_SYSTEM_RESET
- 配置設(shè)備類型
命令:SimpleAPI/ZB_WRITE_CONFIGURATION
參數(shù):ConfigId: 135(ZCD_NV_LOGICAL_TYPE),Len: 1,Value: [0](協(xié)調(diào)器) - 設(shè)置PANID
命令:Util/UTIL_SET_PANID
參數(shù):PanId: 6 - 設(shè)置信道(復(fù)位后生效)
命令:Util/UTIL_SET_CHANNELS
參數(shù):Channels: 0x00020000(即17信道,計(jì)算方法:1<<17 = 0x00020000) - 設(shè)置信道(立即生效)
命令:AppConfig/APP_CNF_BDB_SET_CHANNEL
參數(shù):isPrimary: TRUE, Channels: 0x00020000(計(jì)算方法同上一條命令) - 注冊應(yīng)用程序(對于創(chuàng)建網(wǎng)絡(luò)來說,這一步不是必需的,但后面收發(fā)數(shù)據(jù)需要)
命令:AF/AF_REGISTER
參數(shù):
EndPoint: 11
AppProfID: 3845
AppDeviceId: 256
AppDevVer: 0
LatencyReq: 0
AppNumInClusters: 1
AppInClusterList: [1]
AppNumOutClusters: 1
AppOutClusterList: [1] - 啟動(dòng)網(wǎng)絡(luò)
命令:ZDO/ZDO_STARTUP_FROM_APP
- 配置啟動(dòng)選項(xiàng),在下次復(fù)位后保持網(wǎng)絡(luò)狀態(tài)和配置
命令:ZB_WRITE_CONFIGURATION
參數(shù):ConfigId: 3(ZCD_NV_STARTUP_OPTION),Len: 1, Value: 0
Node.js
以上步驟可以通過Node.js腳本實(shí)現(xiàn):
const znp = require('cc-znp')
znp.request('SAPI', 'writeConfiguration', {
configid: 3,
len: 1,
value: [0x03]
})
znp.request('SAPI', 'systemReset', {})
znp.request('SAPI', 'writeConfiguration', {
configid: 0x87,
len: 1,
value: [0] //coordinator
})
znp.request('UTIL', 'setPanid', {
panid: 6
})
znp.request('UTIL', 'setChannels', {
channels: 0x00020000
})
znp.sendCmd(1, 'RPC_SYS_DEBUG', 0x08, new Buffer([1, 0, 0, 2, 0]))
znp.request('AF', 'register', {
endpoint: 0x0B,
appprofid: 0xF05,
appdeviceid: 0x100,
appdevver: 0,
latencyreq: 0,
appnuminclusters: 1,
appinclusterlist: [1],
appnumoutclusters: 1,
appoutclusterlist: [1]
})
znp.request('ZDO', 'startupFromApp', {
startdelay: 0
}
znp.request('SAPI', 'writeConfiguration', {
configid: 3,
len: 1,
value: [0]
})
發(fā)送數(shù)據(jù)(待發(fā)送的數(shù)據(jù)放在buffer變量中):
znp.request('AF', 'dataRequest', {
dstaddr: 0xFFFF,
destendpoint: 0x0B,
srcendpoint: 0x0B,
clusterid: 0x0001,
transid: 1,
options: 0,
radius: 0x7,
len: buffer.length,
data: buffer
})
接收數(shù)據(jù):
znp.on('AREQ', function (msg) {
if(msg.ind === 'incomingMsg') {
var srcaddr = msg.data.srcaddr;
var data = msg.data.data;
}
});
C語言
在Linux上,可以使用TI提供的znp-host-framework來操作ZNP。其中提供了一個(gè)cmdLine例程,可以在命令行中交互式地設(shè)置創(chuàng)建網(wǎng)絡(luò)的參數(shù),主要步驟與上文大體一致,缺少了AppConfig/APP_CNF_BDB_SET_CHANNEL這條命令(設(shè)置信道并立即生效),可以自行添加:
/*
* mtAppCnf.c
*
* This module contains the API for the MT App Config Interface
*
*/
#include <stdlib.h>
#include <string.h>
#include "mtAppCnf.h"
#include "mtParser.h"
#include "rpc.h"
#include "dbgPrint.h"
extern uint8_t srspRpcBuff[RPC_MAX_LEN];
uint8_t appCnfBdbSetChannel(mtBdbSetChannelFormat_t *req)
{
uint8_t status;
uint8_t cmInd = 0;
uint32_t cmdLen = 5;
uint8_t *cmd = malloc(cmdLen);
if (cmd)
{
cmd[cmInd++] = req->isPrimary;
memcpy((cmd + cmInd), req->channelMask, 4);
cmInd += 4;
status = rpcSendFrame((MT_RPC_CMD_SREQ | MT_RPC_SYS_APP_CNF),
MT_APP_CNF_BDB_SET_CHANNEL, cmd, cmdLen);
if (status == MT_RPC_SUCCESS)
{
rpcWaitMqClientMsg(50);
status = srspRpcBuff[2];
}
free(cmd);
return status;
}
else
{
dbg_print(PRINT_LEVEL_WARNING, "Memory for cmd was not allocated\n");
return 1;
}
}
TODO
- 新節(jié)點(diǎn)入網(wǎng):需要在Zigbee協(xié)調(diào)器上控制是否允許新節(jié)點(diǎn)入網(wǎng)。