4. Zigbee绑定表管理

任务目标

在Zigbee网络中,绑定(Binding)允许源设备无需通过协调器转发即可直接向目标设备发送控制命令。本案例通过协调器发送ZCL命令,完成以下闭环:建立绑定表项(将开关绑定到灯)、验证绑定生效、删除绑定表项、验证删除结果。

环境与依赖

  • 硬件:任意支持Zigbee 3.0的协调器(如CC2530/CC2652 + Z-Stack 3.x),两个终端设备(一个开关/按键设备作为源,一个灯/插座设备作为目标)
  • 软件:Z-Stack 3.0.2或更高版本,使用ZNP(Zigbee Network Processor)接口通过UART与主机通信
  • 主机端:Python 3.6+,安装pyserial库(pip install pyserial)

实现原理

绑定表存储在协调器(或支持集中绑定表的设备)中。协调器通过ZDO(Zigbee Device Object)的Bind_Req和Unbind_Req命令管理绑定表。本案例使用Z-Stack的ZNP串口协议,直接构造AF(Application Framework)数据请求来发送ZDO命令。

关键完整代码

以下Python脚本通过串口与协调器通信,完成绑定建立、验证、删除的完整流程。代码中已包含ZNP帧封装、校验和计算及ZDO命令构造。

import serial
import struct
import time

# ---------- ZNP 帧封装 ----------
def znp_frame(cmd0, cmd1, data):
    """构造ZNP串口帧: SOF(0xFE) + Len + Cmd0 + Cmd1 + Data + FCS"""
    length = len(data) + 2  # Cmd0 + Cmd1
    fcs = 0
    for b in [length, cmd0, cmd1] + list(data):
        fcs ^= b
    frame = bytes([0xFE, length, cmd0, cmd1]) + bytes(data) + bytes([fcs])
    return frame

def send_znp(ser, cmd0, cmd1, data):
    ser.write(znp_frame(cmd0, cmd1, data))
    time.sleep(0.2)
    # 读取并丢弃响应(简化处理,实际应解析状态)
    while ser.in_waiting:
        ser.read(ser.in_waiting)
        time.sleep(0.1)

# ---------- 地址解析 ----------
def ieee_to_bytes(ieee_str):
    """将 '01:02:03:04:05:06:07:08' 转为小端字节序"""
    parts = ieee_str.split(':')
    return bytes(int(x, 16) for x in reversed(parts))

def short_to_bytes(short_addr):
    return struct.pack('<H', short_addr)

# ---------- ZDO 绑定命令 ----------
def zdo_bind_req(ser, src_ieee, src_ep, dst_ieee, dst_ep, cluster):
    """
    发送 ZDO Bind_Req (0x0021)
    参数: 源设备IEEE地址、源端点、目标IEEE地址、目标端点、集群ID
    """
    cluster_bytes = struct.pack('<H', cluster)
    dst_addr_mode = 0x03  # 64位IEEE地址
    data = (ieee_to_bytes(src_ieee) + bytes([src_ep]) +
            cluster_bytes + bytes([dst_addr_mode]) +
            ieee_to_bytes(dst_ieee) + bytes([dst_ep]))
    # ZDO 命令通过 AF 数据请求发送,使用 SREQ: AF_DATA_REQUEST (0x24, 0x01)
    # DstAddr=协调器短地址0x0000, DstEndpoint=0x00 (ZDO), ClusterId=0x0021
    af_data = (short_to_bytes(0x0000) + bytes([0x00, 0x00]) +
               struct.pack('<H', 0x0021) + bytes([0x00]) +  # ClusterId, TxnId
               bytes([len(data)]) + data)
    send_znp(ser, 0x24, 0x01, af_data)

def zdo_unbind_req(ser, src_ieee, src_ep, dst_ieee, dst_ep, cluster):
    """发送 ZDO Unbind_Req (0x0022),参数格式同 Bind_Req"""
    cluster_bytes = struct.pack('<H', cluster)
    dst_addr_mode = 0x03
    data = (ieee_to_bytes(src_ieee) + bytes([src_ep]) +
            cluster_bytes + bytes([dst_addr_mode]) +
            ieee_to_bytes(dst_ieee) + bytes([dst_ep]))
    af_data = (short_to_bytes(0x0000) + bytes([0x00, 0x00]) +
               struct.pack('<H', 0x0022) + bytes([0x00]) +
               bytes([len(data)]) + data)
    send_znp(ser, 0x24, 0x01, af_data)

# ---------- 主流程 ----------
if __name__ == '__main__':
    PORT = 'COM5'   # 根据实际串口修改
    BAUD = 115200
    ser = serial.Serial(PORT, BAUD, timeout=1)

    # 设备参数(需替换为实际设备IEEE地址)
    SWITCH_IEEE = '00:12:4B:00:12:34:56:78'  # 开关(源)
    SWITCH_EP = 1
    LIGHT_IEEE = '00:12:4B:00:12:34:56:79'   # 灯(目标)
    LIGHT_EP = 1
    ONOFF_CLUSTER = 0x0006  # On/Off 集群

    # 1. 建立绑定:开关 → 灯
    print("建立绑定...")
    zdo_bind_req(ser, SWITCH_IEEE, SWITCH_EP, LIGHT_IEEE, LIGHT_EP, ONOFF_CLUSTER)
    time.sleep(1)

    # 2. 验证绑定(通过按键触发或发送On/Off命令,观察灯是否直接响应)
    #    此处通过协调器向开关发送"识别"命令,确认绑定表已写入
    #    实际验证:按下开关物理按键,灯应亮/灭(无需协调器转发)
    print("请按下开关按键,观察灯是否直接动作(绑定生效)")
    input("确认后按回车继续...")

    # 3. 删除绑定
    print("删除绑定...")
    zdo_unbind_req(ser, SWITCH_IEEE, SWITCH_EP, LIGHT_IEEE, LIGHT_EP, ONOFF_CLUSTER)
    time.sleep(1)

    # 4. 验证删除:再次按键,灯不应响应
    print("再次按下开关按键,灯应不再动作(绑定已删除)")
    ser.close()

运行与验证

  1. 将协调器通过USB连接电脑,确认串口号(如COM5)
  2. 确保两个终端设备已入网,记录其IEEE地址(可通过协调器主动扫描或从设备标签获取)
  3. 运行脚本:python zigbee_binding.py
  4. 验证步骤:
    • 绑定建立:脚本执行后,按下开关物理按键,灯应立即亮/灭,且协调器串口无转发数据(可在协调器端抓包确认无APS数据转发)
    • 绑定删除:脚本执行删除后,再次按键,灯不响应

通过条件:绑定建立后按键灯动作、删除后按键灯不动作,且协调器日志中无AF中继转发记录,即证明绑定表管理成功。

常见问题

  • 绑定失败:确认源/目标IEEE地址正确、设备在线;部分设备需先使能PERMIT_JOIN并等待入网稳定
  • 按键无反应:检查开关端点号(通常为1),确认集群ID匹配(On/Off为0x0006)
  • 串口无响应:核对波特率(ZNP默认115200),检查协调器固件是否为ZNP模式