Merge pull request #7651 from s-hadinger/zigbee_29

Add ``ZbBind`` (experimental) and bug fixes
This commit is contained in:
Theo Arends 2020-02-04 09:21:48 +01:00 committed by GitHub
commit 7122a53b2d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 134 additions and 37 deletions

View File

@ -4,6 +4,7 @@
- Change wifi connectivity stability (#7602)
- Add ``SetOption84 1`` sends AWS IoT device shadow updates (alternative to retained)
- Add ``ZbBind`` (experimental) and bug fixes
- Fix PWM flickering at low levels (#7415)
### 8.1.0.4 20200116

View File

@ -493,6 +493,7 @@
#define D_JSON_ZIGBEE_ZCL_SENT "ZbZCLSent"
#define D_JSON_ZIGBEE_RECEIVED "ZbReceived"
#define D_JSON_ZIGBEE_RECEIVED_LEGACY "ZigbeeReceived"
#define D_CMND_ZIGBEE_BIND "Bind"
// Commands xdrv_25_A4988_Stepper.ino
#define D_CMND_MOTOR "MOTOR"

View File

@ -71,6 +71,8 @@ public:
uint16_t isKnownIndex(uint32_t index) const;
uint16_t isKnownFriendlyName(const char * name) const;
uint64_t getDeviceLongAddr(uint16_t shortaddr) const;
// Add new device, provide ShortAddr and optional longAddr
// If it is already registered, update information, otherwise create the entry
void updateDevice(uint16_t shortaddr, uint64_t longaddr = 0);
@ -142,6 +144,7 @@ private:
static int32_t findClusterEndpoint(const std::vector<uint32_t> & vecOfElements, uint16_t element);
Z_Device & getShortAddr(uint16_t shortaddr); // find Device from shortAddr, creates it if does not exist
const Z_Device & getShortAddrConst(uint16_t shortaddr) const ; // find Device from shortAddr, creates it if does not exist
Z_Device & getLongAddr(uint64_t longaddr); // find Device from shortAddr, creates it if does not exist
int32_t findShortAddr(uint16_t shortaddr) const;
@ -160,6 +163,9 @@ private:
Z_Devices zigbee_devices = Z_Devices();
// Local coordinator information
uint64_t localIEEEAddr = 0;
// https://thispointer.com/c-how-to-find-an-element-in-vector-and-get-its-index/
template < typename T>
bool Z_Devices::findInVector(const std::vector<T> & vecOfElements, const T & element) {
@ -326,6 +332,11 @@ uint16_t Z_Devices::isKnownFriendlyName(const char * name) const {
}
}
uint64_t Z_Devices::getDeviceLongAddr(uint16_t shortaddr) const {
const Z_Device & device = getShortAddrConst(shortaddr);
return device.longaddr;
}
//
// We have a seen a shortaddr on the network, get the corresponding
//
@ -335,9 +346,18 @@ Z_Device & Z_Devices::getShortAddr(uint16_t shortaddr) {
if (found >= 0) {
return _devices[found];
}
//Serial.printf("Device entry created for shortaddr = 0x%02X, found = %d\n", shortaddr, found);
//Serial.printf("Device entry created for shortaddr = 0x%02X, found = %d\n", shortaddr, found);
return createDeviceEntry(shortaddr, 0);
}
// Same version but Const
const Z_Device & Z_Devices::getShortAddrConst(uint16_t shortaddr) const {
if (!shortaddr) { return *(Z_Device*) nullptr; } // this is not legal
int32_t found = findShortAddr(shortaddr);
if (found >= 0) {
return _devices[found];
}
return *((Z_Device*)nullptr);
}
// find the Device object by its longaddr (unique key if not null)
Z_Device & Z_Devices::getLongAddr(uint64_t longaddr) {

View File

@ -39,10 +39,10 @@ class ZCLFrame {
public:
ZCLFrame(uint8_t frame_control, uint16_t manuf_code, uint8_t transact_seq, uint8_t cmd_id,
const char *buf, size_t buf_len, uint16_t clusterid = 0, uint16_t groupid = 0,
uint16_t srcaddr = 0, uint8_t srcendpoint = 0, uint8_t dstendpoint = 0, uint8_t wasbroadcast = 0,
uint8_t linkquality = 0, uint8_t securityuse = 0, uint8_t seqnumber = 0,
uint32_t timestamp = 0):
const char *buf, size_t buf_len, uint16_t clusterid, uint16_t groupid,
uint16_t srcaddr, uint8_t srcendpoint, uint8_t dstendpoint, uint8_t wasbroadcast,
uint8_t linkquality, uint8_t securityuse, uint8_t seqnumber,
uint32_t timestamp):
_cmd_id(cmd_id), _manuf_code(manuf_code), _transact_seq(transact_seq),
_payload(buf_len ? buf_len : 250), // allocate the data frame from source or preallocate big enough
_cluster_id(clusterid), _group_id(groupid),
@ -74,9 +74,9 @@ public:
}
static ZCLFrame parseRawFrame(const SBuffer &buf, uint8_t offset, uint8_t len, uint16_t clusterid, uint16_t groupid,
uint16_t srcaddr = 0, uint8_t srcendpoint = 0, uint8_t dstendpoint = 0, uint8_t wasbroadcast = 0,
uint8_t linkquality = 0, uint8_t securityuse = 0, uint8_t seqnumber = 0,
uint32_t timestamp = 0) { // parse a raw frame and build the ZCL frame object
uint16_t srcaddr, uint8_t srcendpoint, uint8_t dstendpoint, uint8_t wasbroadcast,
uint8_t linkquality, uint8_t securityuse, uint8_t seqnumber,
uint32_t timestamp) { // parse a raw frame and build the ZCL frame object
uint32_t i = offset;
ZCLHeaderFrameControl_t frame_control;
uint16_t manuf_code = 0;
@ -92,7 +92,10 @@ public:
cmd_id = buf.get8(i++);
ZCLFrame zcl_frame(frame_control.d8, manuf_code, transact_seq, cmd_id,
(const char *)(buf.buf() + i), len + offset - i,
clusterid, groupid);
clusterid, groupid,
srcaddr, srcendpoint, dstendpoint, wasbroadcast,
linkquality, securityuse, seqnumber,
timestamp);
return zcl_frame;
}

View File

@ -33,6 +33,9 @@ int32_t Z_ReceiveDeviceInfo(int32_t res, class SBuffer &buf) {
uint8_t device_state = buf.get8(14);
uint8_t device_associated = buf.get8(15);
// keep track of the local IEEE address
localIEEEAddr = long_adr;
char hex[20];
Uint64toHex(long_adr, hex, 64);
Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{"

View File

@ -33,19 +33,19 @@ const char kZbCommands[] PROGMEM = D_PRFX_ZB "|" // prefix
D_CMND_ZIGBEEZNPSEND "|" D_CMND_ZIGBEE_PERMITJOIN "|"
D_CMND_ZIGBEE_STATUS "|" D_CMND_ZIGBEE_RESET "|" D_CMND_ZIGBEE_SEND "|"
D_CMND_ZIGBEE_PROBE "|" D_CMND_ZIGBEE_READ "|" D_CMND_ZIGBEEZNPRECEIVE "|"
D_CMND_ZIGBEE_FORGET "|" D_CMND_ZIGBEE_SAVE "|" D_CMND_ZIGBEE_NAME ;
D_CMND_ZIGBEE_FORGET "|" D_CMND_ZIGBEE_SAVE "|" D_CMND_ZIGBEE_NAME "|" D_CMND_ZIGBEE_BIND ;
const char kZigbeeCommands[] PROGMEM = D_PRFX_ZIGBEE "|" // legacy prefix -- deprecated
D_CMND_ZIGBEEZNPSEND "|" D_CMND_ZIGBEE_PERMITJOIN "|"
D_CMND_ZIGBEE_STATUS "|" D_CMND_ZIGBEE_RESET "|" D_CMND_ZIGBEE_SEND "|"
D_CMND_ZIGBEE_PROBE "|" D_CMND_ZIGBEE_READ "|" D_CMND_ZIGBEEZNPRECEIVE "|"
D_CMND_ZIGBEE_FORGET "|" D_CMND_ZIGBEE_SAVE "|" D_CMND_ZIGBEE_NAME ;
D_CMND_ZIGBEE_FORGET "|" D_CMND_ZIGBEE_SAVE "|" D_CMND_ZIGBEE_NAME "|" D_CMND_ZIGBEE_BIND ;
void (* const ZigbeeCommand[])(void) PROGMEM = {
&CmndZigbeeZNPSend, &CmndZigbeePermitJoin,
&CmndZigbeeStatus, &CmndZigbeeReset, &CmndZigbeeSend,
&CmndZigbeeProbe, &CmndZigbeeRead, &CmndZigbeeZNPReceive,
&CmndZigbeeForget, &CmndZigbeeSave, &CmndZigbeeName
&CmndZbZNPSend, &CmndZbPermitJoin,
&CmndZbStatus, &CmndZbReset, &CmndZbSend,
&CmndZbProbe, &CmndZbRead, &CmndZbZNPReceive,
&CmndZbForget, &CmndZbSave, &CmndZbName, &CmndZbBind
};
int32_t ZigbeeProcessInput(class SBuffer &buf) {
@ -257,7 +257,7 @@ const unsigned char ZIGBEE_FACTORY_RESET[] PROGMEM =
{ Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_STARTUP_OPTION, 0x01 /* len */, 0x01 /* STARTOPT_CLEAR_CONFIG */};
//"2605030101"; // Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_STARTUP_OPTION, 0x01 len, 0x01 STARTOPT_CLEAR_CONFIG
// Do a factory reset of the CC2530
void CmndZigbeeReset(void) {
void CmndZbReset(void) {
if (ZigbeeSerial) {
switch (XdrvMailbox.payload) {
case 1:
@ -272,7 +272,7 @@ void CmndZigbeeReset(void) {
}
}
void CmndZigbeeZNPSendOrReceive(bool send)
void CmndZbZNPSendOrReceive(bool send)
{
if (ZigbeeSerial && (XdrvMailbox.data_len > 0)) {
uint8_t code;
@ -300,14 +300,14 @@ void CmndZigbeeZNPSendOrReceive(bool send)
}
// For debug purposes only, simulates a message received
void CmndZigbeeZNPReceive(void)
void CmndZbZNPReceive(void)
{
CmndZigbeeZNPSendOrReceive(false);
CmndZbZNPSendOrReceive(false);
}
void CmndZigbeeZNPSend(void)
void CmndZbZNPSend(void)
{
CmndZigbeeZNPSendOrReceive(true);
CmndZbZNPSendOrReceive(true);
}
void ZigbeeZNPSend(const uint8_t *msg, size_t len) {
@ -442,7 +442,7 @@ void zigbeeZCLSendStr(uint16_t dstAddr, uint8_t endpoint, const char *data) {
ResponseCmndDone();
}
void CmndZigbeeSend(void) {
void CmndZbSend(void) {
// ZigbeeSend { "device":"0x1234", "endpoint":"0x03", "send":{"Power":1} }
// ZigbeeSend { "device":"0x1234", "endpoint":"0x03", "send":{"Power":"3"} }
// ZigbeeSend { "device":"0x1234", "endpoint":"0x03", "send":{"Power":"0xFF"} }
@ -465,9 +465,14 @@ void CmndZigbeeSend(void) {
uint8_t endpoint = 0x00; // 0x00 is invalid for the dst endpoint
String cmd_str = ""; // the actual low-level command, either specified or computed
const JsonVariant &val_device = getCaseInsensitive(json, PSTR("device"));
if (nullptr != &val_device) { device = strToUInt(val_device); }
const JsonVariant &val_endpoint = getCaseInsensitive(json, PSTR("endpoint"));
const JsonVariant &val_device = getCaseInsensitive(json, PSTR("Device"));
if (nullptr != &val_device) {
device = zigbee_devices.parseDeviceParam(val_device.as<char*>());
if (0xFFFF == device) { ResponseCmndChar("Invalid parameter"); return; }
}
if ((nullptr == &val_device) || (0x000 == device)) { ResponseCmndChar("Unknown device"); return; }
const JsonVariant &val_endpoint = getCaseInsensitive(json, PSTR("Endpoint"));
if (nullptr != &val_endpoint) { endpoint = strToUInt(val_endpoint); }
const JsonVariant &val_cmd = getCaseInsensitive(json, PSTR("Send"));
if (nullptr != &val_cmd) {
@ -547,8 +552,64 @@ void CmndZigbeeSend(void) {
}
ZBM(ZBS_BIND_REQ, Z_SREQ | Z_ZDO, ZDO_BIND_REQ,
0,0, // dstAddr - 16 bits, device to send the bind to
0,0,0,0,0,0,0,0, // srcAddr - 64 bits, IEEE binding source
0x00, // source endpoint
0x00, 0x00, // cluster
0x03, // DstAddrMode - 0x03 = ADDRESS_64_BIT
0,0,0,0,0,0,0,0, // dstAddr - 64 bits, IEEE binding destination, i.e. coordinator
0x01 // dstEndpoint - 0x01 for coordinator
)
void CmndZbBind(void) {
// ZbBind { "device":"0x1234", "endpoint":1, "cluster":6 }
// local endpoint is always 1, IEEE addresses are calculated
if (zigbee.init_phase) { ResponseCmndChar(D_ZIGBEE_NOT_STARTED); return; }
DynamicJsonBuffer jsonBuf;
JsonObject &json = jsonBuf.parseObject(XdrvMailbox.data);
if (!json.success()) { ResponseCmndChar(D_JSON_INVALID_JSON); return; }
// params
// static char delim[] = ", "; // delimiters for parameters
uint16_t device = 0xFFFF; // 0xFFFF is broadcast, so considered valid
uint8_t endpoint = 0x00; // 0x00 is invalid for the dst endpoint
uint16_t cluster = 0; // 0xFFFF is invalid
uint32_t group = 0xFFFFFFFF; // 16 bits values, otherwise 0xFFFFFFFF is unspecified
const JsonVariant &val_device = getCaseInsensitive(json, PSTR("Device"));
if (nullptr != &val_device) {
device = zigbee_devices.parseDeviceParam(val_device.as<char*>());
if (0xFFFF == device) { ResponseCmndChar("Invalid parameter"); return; }
}
if ((nullptr == &val_device) || (0x000 == device)) { ResponseCmndChar("Unknown device"); return; }
const JsonVariant &val_endpoint = getCaseInsensitive(json, PSTR("Endpoint"));
if (nullptr != &val_endpoint) { endpoint = strToUInt(val_endpoint); }
const JsonVariant &val_cluster = getCaseInsensitive(json, PSTR("Cluster"));
if (nullptr != &val_cluster) { cluster = strToUInt(val_cluster); }
// TODO compute endpoint from cluster
SBuffer buf(sizeof(ZBS_BIND_REQ));
buf.add8(Z_SREQ | Z_ZDO);
buf.add8(ZDO_BIND_REQ);
buf.add16(device);
buf.add64(zigbee_devices.getDeviceLongAddr(device));
buf.add8(endpoint);
buf.add16(cluster);
buf.add8(0x03); // DstAddrMode - 0x03 = ADDRESS_64_BIT
buf.add64(localIEEEAddr); // coordinatore IEEE address
buf.add8(0x01); // local endpoint = 1
ZigbeeZNPSend(buf.getBuffer(), buf.len());
ResponseCmndDone();
}
// Probe a specific device to get its endpoints and supported clusters
void CmndZigbeeProbe(void) {
void CmndZbProbe(void) {
if (zigbee.init_phase) { ResponseCmndChar(D_ZIGBEE_NOT_STARTED); return; }
uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data);
if (0x0000 == shortaddr) { ResponseCmndChar("Unknown device"); return; }
@ -560,7 +621,7 @@ void CmndZigbeeProbe(void) {
}
// Specify, read or erase a Friendly Name
void CmndZigbeeName(void) {
void CmndZbName(void) {
// Syntax is:
// ZigbeeName <device_id>,<friendlyname> - assign a friendly name
// ZigbeeName <device_id> - display the current friendly name
@ -589,7 +650,7 @@ void CmndZigbeeName(void) {
}
// Remove an old Zigbee device from the list of known devices, use ZigbeeStatus to know all registered devices
void CmndZigbeeForget(void) {
void CmndZbForget(void) {
if (zigbee.init_phase) { ResponseCmndChar(D_ZIGBEE_NOT_STARTED); return; }
uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data);
if (0x0000 == shortaddr) { ResponseCmndChar("Unknown device"); return; }
@ -604,7 +665,7 @@ void CmndZigbeeForget(void) {
}
// Save Zigbee information to flash
void CmndZigbeeSave(void) {
void CmndZbSave(void) {
if (zigbee.init_phase) { ResponseCmndChar(D_ZIGBEE_NOT_STARTED); return; }
saveZigbeeDevices();
@ -613,7 +674,7 @@ void CmndZigbeeSave(void) {
}
// Send an attribute read command to a device, specifying cluster and list of attributes
void CmndZigbeeRead(void) {
void CmndZbRead(void) {
// ZigbeeRead {"Device":"0xF289","Cluster":0,"Endpoint":3,"Attr":5}
// ZigbeeRead {"Device":"0xF289","Cluster":"0x0000","Endpoint":"0x0003","Attr":"0x0005"}
// ZigbeeRead {"Device":"0xF289","Cluster":0,"Endpoint":3,"Attr":[5,6,7,4]}
@ -629,10 +690,14 @@ void CmndZigbeeRead(void) {
size_t attrs_len = 0;
uint8_t* attrs = nullptr; // empty string is valid
const JsonVariant &val_device = getCaseInsensitive(json, PSTR("Device"));
if (nullptr != &val_device) { device = strToUInt(val_device); }
const JsonVariant val_cluster = getCaseInsensitive(json, PSTR("Cluster"));
if (nullptr != &val_device) {
device = zigbee_devices.parseDeviceParam(val_device.as<char*>());
if (0xFFFF == device) { ResponseCmndChar("Invalid parameter"); return; }
}
if ((nullptr == &val_device) || (0x000 == device)) { ResponseCmndChar("Unknown device"); return; }
const JsonVariant &val_cluster = getCaseInsensitive(json, PSTR("Cluster"));
if (nullptr != &val_cluster) { cluster = strToUInt(val_cluster); }
const JsonVariant &val_endpoint = getCaseInsensitive(json, PSTR("Endpoint"));
if (nullptr != &val_endpoint) { endpoint = strToUInt(val_endpoint); }
@ -659,14 +724,18 @@ void CmndZigbeeRead(void) {
}
}
ZigbeeZCLSend(device, cluster, endpoint, ZCL_READ_ATTRIBUTES, false, attrs, attrs_len, false /* we do want a response */);
if ((0 != endpoint) && (attrs_len > 0)) {
ZigbeeZCLSend(device, cluster, endpoint, ZCL_READ_ATTRIBUTES, false, attrs, attrs_len, false /* we do want a response */);
ResponseCmndDone();
} else {
ResponseCmndChar("Missing parameters");
}
if (attrs) { delete[] attrs; }
ResponseCmndDone();
}
// Allow or Deny pairing of new Zigbee devices
void CmndZigbeePermitJoin(void)
void CmndZbPermitJoin(void)
{
if (zigbee.init_phase) { ResponseCmndChar(D_ZIGBEE_NOT_STARTED); return; }
uint32_t payload = XdrvMailbox.payload;
@ -683,7 +752,7 @@ void CmndZigbeePermitJoin(void)
ResponseCmndDone();
}
void CmndZigbeeStatus(void) {
void CmndZbStatus(void) {
if (ZigbeeSerial) {
if (zigbee.init_phase) { ResponseCmndChar(D_ZIGBEE_NOT_STARTED); return; }
uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data);