Merge pull request #8862 from s-hadinger/zigbee_july_5

Zigbee EZSP support for bindings and groups
This commit is contained in:
Theo Arends 2020-07-05 21:21:44 +02:00 committed by GitHub
commit d612d2b62e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 160 additions and 49 deletions

View File

@ -461,6 +461,7 @@ enum EZSP_ZDO {
// Network Management Server Services Requests
ZDO_Mgmt_Lqi_req = 0x0031,
ZDO_Mgmt_Rtg_req = 0x0032,
ZDO_Mgmt_Bind_req = 0x0033,
ZDO_Mgmt_Leave_req = 0x0034,
ZDO_Mgmt_Permit_Joining_req = 0x0036,
ZDO_Mgmt_NWK_Update_req = 0x0038,
@ -496,6 +497,7 @@ enum EZSP_ZDO {
// Network Management Server Services Responses
ZDO_Mgmt_Lqi_rsp = 0x8031,
ZDO_Mgmt_Rtg_rsp = 0x8032,
ZDO_Mgmt_Bind_rsp = 0x8033,
ZDO_Mgmt_Leave_rsp = 0x8034,
ZDO_Mgmt_Permit_Joining_rsp = 0x8036,
ZDO_Mgmt_NWK_Update_rsp = 0x8038,
@ -1106,6 +1108,34 @@ typedef struct Z_StatusLine {
const char * status_msg;
} Z_StatusLine;
// ZDP Enumeration, see Zigbee spec 2.4.5
String getZDPStatusMessage(uint8_t status) {
static const char StatusMsg[] PROGMEM = "SUCCESS|INV_REQUESTTYPE|DEVICE_NOT_FOUND|INVALID_EP|NOT_ACTIVE|NOT_SUPPORTED"
"|TIMEOUT|NO_MATCH|NO_ENTRY|NO_DESCRIPTOR|INSUFFICIENT_SPACE|NOT_PERMITTED"
"|TABLE_FULL|NOT_AUTHORIZED|DEVICE_BINDING_TABLE_FULL"
;
static const uint8_t StatusIdx[] PROGMEM = { 0x00, 0x80, 0x81, 0x82, 0x83, 0x84,
0x85, 0x86, 0x88, 0x89, 0x8A, 0x8B,
0x8C, 0x8D, 0x8E };
char msg[32];
int32_t idx = -1;
for (uint32_t i = 0; i < sizeof(StatusIdx); i++) {
if (status == pgm_read_byte(&StatusIdx[i])) {
idx = i;
break;
}
}
if (idx >= 0) {
GetTextIndexed(msg, sizeof(msg), idx, StatusMsg);
} else {
*msg = 0x00; // empty string
}
return String(msg);
}
// Undocumented Zigbee ZCL code here: https://github.com/dresden-elektronik/deconz-rest-plugin/wiki/Zigbee-Error-Codes-in-the-Log
String getZigbeeStatusMessage(uint8_t status) {
static const char StatusMsg[] PROGMEM = "SUCCESS|FAILURE|NOT_AUTHORIZED|RESERVED_FIELD_NOT_ZERO|MALFORMED_COMMAND|UNSUP_CLUSTER_COMMAND|UNSUP_GENERAL_COMMAND"

View File

@ -670,9 +670,17 @@ int32_t ZNP_ReceiveTCDevInd(int32_t res, const class SBuffer &buf) {
//
// Handle Bind Rsp incoming message
//
int32_t ZNP_BindRsp(int32_t res, const class SBuffer &buf) {
int32_t Z_BindRsp(int32_t res, const class SBuffer &buf) {
#ifdef USE_ZIGBEE_ZNP
Z_ShortAddress nwkAddr = buf.get16(2);
uint8_t status = buf.get8(4);
String msg = getZigbeeStatusMessage(status);
#endif // USE_ZIGBEE_ZNP
#ifdef USE_ZIGBEE_EZSP
uint8_t status = buf.get8(0);
Z_ShortAddress nwkAddr = buf.get16(buf.len()-2); // last 2 bytes
String msg = getZDPStatusMessage(status);
#endif // USE_ZIGBEE_EZSP
const char * friendlyName = zigbee_devices.getFriendlyName(nwkAddr);
@ -682,7 +690,7 @@ int32_t ZNP_BindRsp(int32_t res, const class SBuffer &buf) {
}
ResponseAppend_P(PSTR(",\"" D_JSON_ZIGBEE_STATUS "\":%d"
",\"" D_JSON_ZIGBEE_STATUS_MSG "\":\"%s\""
"}}"), status, getZigbeeStatusMessage(status).c_str());
"}}"), status, msg.c_str());
MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED));
XdrvRulesProcess();
@ -693,9 +701,17 @@ int32_t ZNP_BindRsp(int32_t res, const class SBuffer &buf) {
//
// Handle Unbind Rsp incoming message
//
int32_t ZNP_UnbindRsp(int32_t res, const class SBuffer &buf) {
int32_t Z_UnbindRsp(int32_t res, const class SBuffer &buf) {
#ifdef USE_ZIGBEE_ZNP
Z_ShortAddress nwkAddr = buf.get16(2);
uint8_t status = buf.get8(4);
String msg = getZigbeeStatusMessage(status);
#endif // USE_ZIGBEE_ZNP
#ifdef USE_ZIGBEE_EZSP
uint8_t status = buf.get8(0);
Z_ShortAddress nwkAddr = buf.get16(buf.len()-2); // last 2 bytes
String msg = getZDPStatusMessage(status);
#endif // USE_ZIGBEE_EZSP
const char * friendlyName = zigbee_devices.getFriendlyName(nwkAddr);
@ -703,8 +719,9 @@ int32_t ZNP_UnbindRsp(int32_t res, const class SBuffer &buf) {
if (friendlyName) {
ResponseAppend_P(PSTR(",\"" D_JSON_ZIGBEE_NAME "\":\"%s\""), friendlyName);
}
ResponseAppend_P(PSTR(",\"" D_JSON_ZIGBEE_STATUS_MSG "\":\"%s\""
"}}"), status, getZigbeeStatusMessage(status).c_str());
ResponseAppend_P(PSTR(",\"" D_JSON_ZIGBEE_STATUS "\":%d"
",\"" D_JSON_ZIGBEE_STATUS_MSG "\":\"%s\""
"}}"), status, msg.c_str());
MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED));
XdrvRulesProcess();
@ -714,12 +731,23 @@ int32_t ZNP_UnbindRsp(int32_t res, const class SBuffer &buf) {
//
// Handle MgMt Bind Rsp incoming message
//
int32_t ZNP_MgmtBindRsp(int32_t res, const class SBuffer &buf) {
int32_t Z_MgmtBindRsp(int32_t res, const class SBuffer &buf) {
#ifdef USE_ZIGBEE_ZNP
uint16_t shortaddr = buf.get16(2);
uint8_t status = buf.get8(4);
uint8_t bind_total = buf.get8(5);
uint8_t bind_start = buf.get8(6);
uint8_t bind_len = buf.get8(7);
const size_t prefix_len = 8;
#endif // USE_ZIGBEE_ZNP
#ifdef USE_ZIGBEE_EZSP
uint16_t shortaddr = buf.get16(buf.len()-2);
uint8_t status = buf.get8(0);
uint8_t bind_total = buf.get8(1);
uint8_t bind_start = buf.get8(2);
uint8_t bind_len = buf.get8(3);
const size_t prefix_len = 4;
#endif // USE_ZIGBEE_EZSP
const char * friendlyName = zigbee_devices.getFriendlyName(shortaddr);
@ -733,7 +761,7 @@ int32_t ZNP_MgmtBindRsp(int32_t res, const class SBuffer &buf) {
",\"Bindings\":["
), status, getZigbeeStatusMessage(status).c_str(), bind_total);
uint32_t idx = 8;
uint32_t idx = prefix_len;
for (uint32_t i = 0; i < bind_len; i++) {
if (idx + 14 > buf.len()) { break; } // overflow, frame size is between 14 and 21
@ -791,7 +819,8 @@ void Z_SendIEEEAddrReq(uint16_t shortaddr) {
ZigbeeZNPSend(IEEEAddrReq, sizeof(IEEEAddrReq));
#endif
#ifdef USE_ZIGBEE_EZSP
// TODO
uint8_t IEEEAddrReq[] = { Z_B0(shortaddr), Z_B1(shortaddr), 0x00, 0x00 };
EZ_SendZDO(shortaddr, ZDO_IEEE_addr_req, IEEEAddrReq, sizeof(IEEEAddrReq));
#endif
}
@ -991,25 +1020,31 @@ int32_t EZ_IncomingMessage(int32_t res, const class SBuffer &buf) {
uint8_t linkquality = buf.get8(14);
// uint8_t linkrsssi = buf.get8(15); // probably not used as there is no equivalent in Z-Stack
uint16_t srcaddr = buf.get16(16);
uint8_t bindingindex = buf.get8(18); // TODO not sure we need this one as a coordinator
uint8_t addressindex = buf.get8(19); // TODO not sure how to handle this one
// uint8_t bindingindex = buf.get8(18); // not sure we need this one as a coordinator
// uint8_t addressindex = buf.get8(19); // not sure how to handle this one
// offset 20 is len, and buffer starts at offset 21
if ((0x0000 == profileid) && (0x00 == srcendpoint)) {
// ZDO request
// Since ZDO messages start with a sequence number, we skip it
SBuffer zdo_buf = buf.subBuffer(22, buf.get8(20) - 1);
// but we add the source address in the last 2 bytes
SBuffer zdo_buf(buf.get8(20) - 1 + 2);
zdo_buf.addBuffer(buf.buf(22), buf.get8(20) - 1);
zdo_buf.add16(srcaddr);
switch (clusterid) {
case ZDO_Device_annce:
return Z_ReceiveEndDeviceAnnonce(res, zdo_buf);
break;
case ZDO_Active_EP_rsp:
return Z_ReceiveActiveEp(res, zdo_buf);
break;
case ZDO_IEEE_addr_rsp:
return Z_ReceiveIEEEAddr(res, zdo_buf);
break;
case ZDO_Bind_rsp:
return Z_BindRsp(res, zdo_buf);
case ZDO_Unbind_rsp:
return Z_UnbindRsp(res, zdo_buf);
case ZDO_Mgmt_Bind_rsp:
return Z_MgmtBindRsp(res, zdo_buf);
}
} else {
bool defer_attributes = false; // do we defer attributes reporting to coalesce
@ -1130,15 +1165,6 @@ int32_t ZNP_ReceiveAfIncomingMessage(int32_t res, const class SBuffer &buf) {
return -1;
}
//
// Callback for loading Zigbee configuration from Flash, called by the state machine
//
int32_t Z_Reset_Device(uint8_t value) {
// TODO - GPIO is hardwired to GPIO4
digitalWrite(4, value ? HIGH : LOW);
return 0; // continue
}
#endif // USE_ZIGBEE_ZNP
@ -1189,9 +1215,9 @@ const Z_Dispatcher Z_DispatchTable[] PROGMEM = {
{ { Z_AREQ | Z_ZDO, ZDO_NODE_DESC_RSP }, &ZNP_ReceiveNodeDesc }, // 4582
{ { Z_AREQ | Z_ZDO, ZDO_ACTIVE_EP_RSP }, &Z_ReceiveActiveEp }, // 4585
{ { Z_AREQ | Z_ZDO, ZDO_IEEE_ADDR_RSP }, &Z_ReceiveIEEEAddr }, // 4581
{ { Z_AREQ | Z_ZDO, ZDO_BIND_RSP }, &ZNP_BindRsp }, // 45A1
{ { Z_AREQ | Z_ZDO, ZDO_UNBIND_RSP }, &ZNP_UnbindRsp }, // 45A2
{ { Z_AREQ | Z_ZDO, ZDO_MGMT_BIND_RSP }, &ZNP_MgmtBindRsp }, // 45B3
{ { Z_AREQ | Z_ZDO, ZDO_BIND_RSP }, &Z_BindRsp }, // 45A1
{ { Z_AREQ | Z_ZDO, ZDO_UNBIND_RSP }, &Z_UnbindRsp }, // 45A2
{ { Z_AREQ | Z_ZDO, ZDO_MGMT_BIND_RSP }, &Z_MgmtBindRsp }, // 45B3
};
/*********************************************************************************************\

View File

@ -718,33 +718,61 @@ void ZigbeeZCLSend_Raw(uint16_t shortaddr, uint16_t groupaddr, uint16_t clusterI
#ifdef USE_ZIGBEE_EZSP
SBuffer buf(32+len);
buf.add16(EZSP_sendUnicast); // 3400
buf.add8(EMBER_OUTGOING_DIRECT); // 00
buf.add16(shortaddr); // dest addr
// ApsFrame
buf.add16(Z_PROF_HA); // Home Automation profile
buf.add16(clusterId); // cluster
buf.add8(0x01); // srcEp
buf.add8(endpoint); // dstEp
buf.add16(EMBER_APS_OPTION_ENABLE_ROUTE_DISCOVERY | EMBER_APS_OPTION_RETRY); // APS frame
buf.add16(groupaddr); // groupId
buf.add8(transacId);
// end of ApsFrame
buf.add8(0x01); // tag TODO
if (BAD_SHORTADDR != shortaddr) {
// send unicast message to an address
buf.add16(EZSP_sendUnicast); // 3400
buf.add8(EMBER_OUTGOING_DIRECT); // 00
buf.add16(shortaddr); // dest addr
// ApsFrame
buf.add16(Z_PROF_HA); // Home Automation profile
buf.add16(clusterId); // cluster
buf.add8(0x01); // srcEp
buf.add8(endpoint); // dstEp
buf.add16(EMBER_APS_OPTION_ENABLE_ROUTE_DISCOVERY | EMBER_APS_OPTION_RETRY); // APS frame
buf.add16(groupaddr); // groupId
buf.add8(transacId);
// end of ApsFrame
buf.add8(0x01); // tag TODO
buf.add8(3 + len + (manuf ? 2 : 0));
buf.add8((needResponse ? 0x00 : 0x10) | (clusterSpecific ? 0x01 : 0x00) | (manuf ? 0x04 : 0x00)); // Frame Control Field
if (manuf) {
buf.add16(manuf); // add Manuf Id if not null
}
buf.add8(transacId); // Transaction Sequance Number
buf.add8(cmdId);
if (len > 0) {
buf.addBuffer(msg, len); // add the payload
buf.add8(3 + len + (manuf ? 2 : 0));
buf.add8((needResponse ? 0x00 : 0x10) | (clusterSpecific ? 0x01 : 0x00) | (manuf ? 0x04 : 0x00)); // Frame Control Field
if (manuf) {
buf.add16(manuf); // add Manuf Id if not null
}
buf.add8(transacId); // Transaction Sequance Number
buf.add8(cmdId);
if (len > 0) {
buf.addBuffer(msg, len); // add the payload
}
} else {
// send broadcast group address, aka groupcast
buf.add16(EZSP_sendMulticast); // 3800
// ApsFrame
buf.add16(Z_PROF_HA); // Home Automation profile
buf.add16(clusterId); // cluster
buf.add8(0x01); // srcEp
buf.add8(endpoint); // broadcast endpoint for groupcast
buf.add16(EMBER_APS_OPTION_ENABLE_ROUTE_DISCOVERY | EMBER_APS_OPTION_RETRY); // APS frame
buf.add16(groupaddr); // groupId
buf.add8(transacId);
// end of ApsFrame
buf.add8(0); // hops, 0x00 = EMBER_MAX_HOPS
buf.add8(7); // nonMemberRadius, 7 = infinite
buf.add8(0x01); // tag TODO
buf.add8(3 + len + (manuf ? 2 : 0));
buf.add8((needResponse ? 0x00 : 0x10) | (clusterSpecific ? 0x01 : 0x00) | (manuf ? 0x04 : 0x00)); // Frame Control Field
if (manuf) {
buf.add16(manuf); // add Manuf Id if not null
}
buf.add8(transacId); // Transaction Sequance Number
buf.add8(cmdId);
if (len > 0) {
buf.addBuffer(msg, len); // add the payload
}
}
ZigbeeEZSPSendCmd(buf.buf(), buf.len(), true);
#endif // USE_ZIGBEE_EZSP
}

View File

@ -713,6 +713,25 @@ void ZbBindUnbind(bool unbind) { // false = bind, true = unbind
ZigbeeZNPSend(buf.getBuffer(), buf.len());
#endif // USE_ZIGBEE_ZNP
#ifdef USE_ZIGBEE_EZSP
SBuffer buf(24);
// ZDO message payload (see Zigbee spec 2.4.3.2.2)
buf.add64(srcLongAddr);
buf.add8(endpoint);
buf.add16(cluster);
if (dstLongAddr) {
buf.add8(Z_Addr_IEEEAddress); // DstAddrMode - 0x03 = ADDRESS_64_BIT
buf.add64(dstLongAddr);
buf.add8(toendpoint);
} else {
buf.add8(Z_Addr_Group); // DstAddrMode - 0x01 = GROUP_ADDRESS
buf.add16(toGroup);
}
EZ_SendZDO(srcDevice, unbind ? ZDO_UNBIND_REQ : ZDO_BIND_REQ, buf.buf(), buf.len());
#endif // USE_ZIGBEE_EZSP
ResponseCmndDone();
}
@ -748,6 +767,14 @@ void CmndZbBindState(void) {
ZigbeeZNPSend(buf.getBuffer(), buf.len());
#endif // USE_ZIGBEE_ZNP
#ifdef USE_ZIGBEE_EZSP
// ZDO message payload (see Zigbee spec 2.4.3.3.4)
uint8_t buf[] = { 0x00 }; // index = 0
EZ_SendZDO(shortaddr, ZDO_Mgmt_Bind_req, buf, sizeof(buf));
#endif // USE_ZIGBEE_EZSP
ResponseCmndDone();
}