|
|
|
|
@ -21,18 +21,32 @@
|
|
|
|
|
|
|
|
|
|
#define XDRV_23 23
|
|
|
|
|
|
|
|
|
|
#ifdef USE_ZIGBEE_ZNP
|
|
|
|
|
const uint32_t ZIGBEE_BUFFER_SIZE = 256; // Max ZNP frame is SOF+LEN+CMD1+CMD2+250+FCS = 255
|
|
|
|
|
const uint8_t ZIGBEE_SOF = 0xFE;
|
|
|
|
|
const uint8_t ZIGBEE_SOF_ALT = 0xFF;
|
|
|
|
|
#endif // USE_ZIGBEE_ZNP
|
|
|
|
|
|
|
|
|
|
#ifdef USE_ZIGBEE_EZSP
|
|
|
|
|
const uint32_t ZIGBEE_BUFFER_SIZE = 256;
|
|
|
|
|
const uint8_t ZIGBEE_EZSP_CANCEL = 0x1A; // cancel byte
|
|
|
|
|
const uint8_t ZIGBEE_EZSP_EOF = 0x7E; // end of frame
|
|
|
|
|
const uint8_t ZIGBEE_EZSP_ESCAPE = 0x7D; // escape byte
|
|
|
|
|
#endif // USE_ZIGBEE_EZSP
|
|
|
|
|
|
|
|
|
|
#include <TasmotaSerial.h>
|
|
|
|
|
TasmotaSerial *ZigbeeSerial = nullptr;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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_ZIGBEEZNPRECEIVE "|"
|
|
|
|
|
#ifdef USE_ZIGBEE_ZNP
|
|
|
|
|
D_CMND_ZIGBEEZNPSEND "|" D_CMND_ZIGBEEZNPRECEIVE "|"
|
|
|
|
|
#endif // USE_ZIGBEE_ZNP
|
|
|
|
|
#ifdef USE_ZIGBEE_EZSP
|
|
|
|
|
D_CMND_ZIGBEE_EZSP_SEND "|" D_CMND_ZIGBEE_EZSP_RECEIVE "|"
|
|
|
|
|
#endif // USE_ZIGBEE_EZSP
|
|
|
|
|
D_CMND_ZIGBEE_PERMITJOIN "|"
|
|
|
|
|
D_CMND_ZIGBEE_STATUS "|" D_CMND_ZIGBEE_RESET "|" D_CMND_ZIGBEE_SEND "|" D_CMND_ZIGBEE_PROBE "|"
|
|
|
|
|
D_CMND_ZIGBEE_FORGET "|" D_CMND_ZIGBEE_SAVE "|" D_CMND_ZIGBEE_NAME "|"
|
|
|
|
|
D_CMND_ZIGBEE_BIND "|" D_CMND_ZIGBEE_UNBIND "|" D_CMND_ZIGBEE_PING "|" D_CMND_ZIGBEE_MODELID "|"
|
|
|
|
|
D_CMND_ZIGBEE_LIGHT "|" D_CMND_ZIGBEE_RESTORE "|" D_CMND_ZIGBEE_BIND_STATE "|"
|
|
|
|
|
@ -40,9 +54,14 @@ const char kZbCommands[] PROGMEM = D_PRFX_ZB "|" // prefix
|
|
|
|
|
;
|
|
|
|
|
|
|
|
|
|
void (* const ZigbeeCommand[])(void) PROGMEM = {
|
|
|
|
|
&CmndZbZNPSend, &CmndZbPermitJoin,
|
|
|
|
|
&CmndZbStatus, &CmndZbReset, &CmndZbSend,
|
|
|
|
|
&CmndZbProbe, &CmndZbZNPReceive,
|
|
|
|
|
#ifdef USE_ZIGBEE_ZNP
|
|
|
|
|
&CmndZbZNPSend, &CmndZbZNPReceive,
|
|
|
|
|
#endif // USE_ZIGBEE_ZNP
|
|
|
|
|
#ifdef USE_ZIGBEE_EZSP
|
|
|
|
|
&CmndZbEZSPSend, &CmndZbEZSPReceive,
|
|
|
|
|
#endif // USE_ZIGBEE_EZSP
|
|
|
|
|
&CmndZbPermitJoin,
|
|
|
|
|
&CmndZbStatus, &CmndZbReset, &CmndZbSend, &CmndZbProbe,
|
|
|
|
|
&CmndZbForget, &CmndZbSave, &CmndZbName,
|
|
|
|
|
&CmndZbBind, &CmndZbUnbind, &CmndZbPing, &CmndZbModelId,
|
|
|
|
|
&CmndZbLight, &CmndZbRestore, &CmndZbBindState,
|
|
|
|
|
@ -52,11 +71,12 @@ void (* const ZigbeeCommand[])(void) PROGMEM = {
|
|
|
|
|
//
|
|
|
|
|
// Called at event loop, checks for incoming data from the CC2530
|
|
|
|
|
//
|
|
|
|
|
void ZigbeeInputLoop(void)
|
|
|
|
|
{
|
|
|
|
|
static uint32_t zigbee_polling_window = 0;
|
|
|
|
|
void ZigbeeInputLoop(void) {
|
|
|
|
|
|
|
|
|
|
#ifdef USE_ZIGBEE_ZNP
|
|
|
|
|
static uint32_t zigbee_polling_window = 0; // number of milliseconds since first byte
|
|
|
|
|
static uint8_t fcs = ZIGBEE_SOF;
|
|
|
|
|
static uint32_t zigbee_frame_len = 5; // minimal zigbee frame lenght, will be updated when buf[1] is read
|
|
|
|
|
static uint32_t zigbee_frame_len = 5; // minimal zigbee frame length, will be updated when buf[1] is read
|
|
|
|
|
// Receive only valid ZNP frames:
|
|
|
|
|
// 00 - SOF = 0xFE
|
|
|
|
|
// 01 - Length of Data Field - 0..250
|
|
|
|
|
@ -140,6 +160,132 @@ void ZigbeeInputLoop(void)
|
|
|
|
|
}
|
|
|
|
|
zigbee_buffer->setLen(0); // empty buffer
|
|
|
|
|
}
|
|
|
|
|
#endif // USE_ZIGBEE_ZNP
|
|
|
|
|
|
|
|
|
|
#ifdef USE_ZIGBEE_EZSP
|
|
|
|
|
static uint32_t zigbee_polling_window = 0; // number of milliseconds since first byte
|
|
|
|
|
bool escape = false; // was the previous byte an escape?
|
|
|
|
|
bool frame_complete = false; // frame is ready and complete
|
|
|
|
|
// Receive only valid EZSP frames:
|
|
|
|
|
// 1A - Cancel - cancel all previous bytes
|
|
|
|
|
// 7D - Escape byte - following byte is escaped
|
|
|
|
|
// 7E - end of frame
|
|
|
|
|
|
|
|
|
|
while (ZigbeeSerial->available()) {
|
|
|
|
|
yield();
|
|
|
|
|
uint8_t zigbee_in_byte = ZigbeeSerial->read();
|
|
|
|
|
AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("ZIG: ZbInput byte=0x%02X len=%d"), zigbee_in_byte, zigbee_buffer->len());
|
|
|
|
|
|
|
|
|
|
// if (0 == zigbee_buffer->len()) { // make sure all variables are correctly initialized
|
|
|
|
|
// escape = false;
|
|
|
|
|
// frame_complete = false;
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
if ((0x11 == zigbee_in_byte) || (0x13 == zigbee_in_byte)) {
|
|
|
|
|
continue; // ignore reserved bytes XON/XOFF
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ZIGBEE_EZSP_ESCAPE == zigbee_in_byte) {
|
|
|
|
|
AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("ZIG: Escape byte received"));
|
|
|
|
|
escape = true;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ZIGBEE_EZSP_CANCEL == zigbee_in_byte) {
|
|
|
|
|
AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("ZIG: ZbInput byte=0x1A, cancel byte received, discarding %d bytes"), zigbee_buffer->len());
|
|
|
|
|
zigbee_buffer->setLen(0); // empty buffer
|
|
|
|
|
escape = false;
|
|
|
|
|
frame_complete = false;
|
|
|
|
|
continue; // re-loop
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ZIGBEE_EZSP_EOF == zigbee_in_byte) {
|
|
|
|
|
// end of frame
|
|
|
|
|
frame_complete = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (zigbee_buffer->len() < ZIGBEE_BUFFER_SIZE) {
|
|
|
|
|
// check if escape
|
|
|
|
|
if (ZIGBEE_EZSP_ESCAPE == zigbee_in_byte) {
|
|
|
|
|
escape = true;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (escape) {
|
|
|
|
|
// invert bit 5
|
|
|
|
|
zigbee_in_byte ^= 0x10;
|
|
|
|
|
escape = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
zigbee_buffer->add8(zigbee_in_byte);
|
|
|
|
|
zigbee_polling_window = millis(); // Wait for more data
|
|
|
|
|
} // adding bytes
|
|
|
|
|
} // while (ZigbeeSerial->available())
|
|
|
|
|
|
|
|
|
|
uint32_t frame_len = zigbee_buffer->len();
|
|
|
|
|
if (frame_complete || (frame_len && (millis() > (zigbee_polling_window + ZIGBEE_POLLING)))) {
|
|
|
|
|
char hex_char[frame_len * 2 + 2];
|
|
|
|
|
ToHex_P((unsigned char*)zigbee_buffer->getBuffer(), zigbee_buffer->len(), hex_char, sizeof(hex_char));
|
|
|
|
|
|
|
|
|
|
AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_ZIGBEE "Bytes follow_read_metric = %0d"), ZigbeeSerial->getLoopReadMetric());
|
|
|
|
|
if ((frame_complete) && (frame_len >= 3)) {
|
|
|
|
|
// frame received and has at least 3 bytes (without EOF), checking CRC
|
|
|
|
|
// AddLog_P2(LOG_LEVEL_INFO, PSTR(D_JSON_ZIGBEE_EZSP_RECEIVED ": received raw frame %s"), hex_char);
|
|
|
|
|
uint16_t crc = 0xFFFF; // frame CRC
|
|
|
|
|
// compute CRC
|
|
|
|
|
for (uint32_t i=0; i<frame_len-2; i++) {
|
|
|
|
|
crc = crc ^ ((uint16_t)zigbee_buffer->get8(i) << 8);
|
|
|
|
|
for (uint32_t i=0; i<8; i++) {
|
|
|
|
|
if (crc & 0x8000) {
|
|
|
|
|
crc = (crc << 1) ^ 0x1021; // polynom is x^16 + x^12 + x^5 + 1, CCITT standard
|
|
|
|
|
} else {
|
|
|
|
|
crc <<= 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint16_t crc_received = zigbee_buffer->get8(frame_len - 2) << 8 | zigbee_buffer->get8(frame_len - 1);
|
|
|
|
|
// remove 2 last bytes
|
|
|
|
|
|
|
|
|
|
if (crc_received != crc) {
|
|
|
|
|
AddLog_P2(LOG_LEVEL_INFO, PSTR(D_JSON_ZIGBEE_EZSP_RECEIVED ": bad crc (received 0x%04X, computed 0x%04X) %s"), crc_received, crc, hex_char);
|
|
|
|
|
} else {
|
|
|
|
|
// copy buffer
|
|
|
|
|
SBuffer ezsp_buffer = zigbee_buffer->subBuffer(0, frame_len - 2); // CRC
|
|
|
|
|
|
|
|
|
|
// CRC is correct, apply de-stuffing if DATA frame
|
|
|
|
|
if (0 == (ezsp_buffer.get8(0) & 0x80)) {
|
|
|
|
|
// DATA frame
|
|
|
|
|
uint8_t rand = 0x42;
|
|
|
|
|
for (uint32_t i=1; i<ezsp_buffer.len(); i++) {
|
|
|
|
|
ezsp_buffer.set8(i, ezsp_buffer.get8(i) ^ rand);
|
|
|
|
|
if (rand & 1) { rand = (rand >> 1) ^ 0xB8; }
|
|
|
|
|
else { rand = (rand >> 1); }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ToHex_P((unsigned char*)ezsp_buffer.getBuffer(), ezsp_buffer.len(), hex_char, sizeof(hex_char));
|
|
|
|
|
Response_P(PSTR("{\"" D_JSON_ZIGBEE_EZSP_RECEIVED "\":\"%s\"}"), hex_char);
|
|
|
|
|
if (Settings.flag3.tuya_serial_mqtt_publish) {
|
|
|
|
|
MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR));
|
|
|
|
|
XdrvRulesProcess();
|
|
|
|
|
} else {
|
|
|
|
|
AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "%s"), mqtt_data); // TODO move to LOG_LEVEL_DEBUG when stable
|
|
|
|
|
}
|
|
|
|
|
// now process the message
|
|
|
|
|
ZigbeeProcessInput(ezsp_buffer);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// the buffer timed-out, print error and discard
|
|
|
|
|
AddLog_P2(LOG_LEVEL_INFO, PSTR(D_JSON_ZIGBEE_EZSP_RECEIVED ": time-out, discarding %s, %d"), hex_char);
|
|
|
|
|
}
|
|
|
|
|
zigbee_buffer->setLen(0); // empty buffer
|
|
|
|
|
escape = false;
|
|
|
|
|
frame_complete = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif // USE_ZIGBEE_EZSP
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/********************************************************************************************/
|
|
|
|
|
@ -201,15 +347,20 @@ uint32_t strToUInt(const JsonVariant &val) {
|
|
|
|
|
return 0; // couldn't parse anything
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifdef USE_ZIGBEE_ZNP
|
|
|
|
|
// Do a factory reset of the CC2530
|
|
|
|
|
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
|
|
|
|
|
#endif // USE_ZIGBEE_ZNP
|
|
|
|
|
|
|
|
|
|
void CmndZbReset(void) {
|
|
|
|
|
if (ZigbeeSerial) {
|
|
|
|
|
switch (XdrvMailbox.payload) {
|
|
|
|
|
case 1:
|
|
|
|
|
#ifdef USE_ZIGBEE_ZNP
|
|
|
|
|
ZigbeeZNPSend(ZIGBEE_FACTORY_RESET, sizeof(ZIGBEE_FACTORY_RESET));
|
|
|
|
|
#endif // USE_ZIGBEE_ZNP
|
|
|
|
|
eraseZigbeeDevices();
|
|
|
|
|
restart_flag = 2;
|
|
|
|
|
ResponseCmndChar_P(PSTR(D_JSON_ZIGBEE_CC2530 " " D_JSON_RESET_AND_RESTARTING));
|
|
|
|
|
@ -220,6 +371,38 @@ void CmndZbReset(void) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifdef USE_ZIGBEE_ZNP
|
|
|
|
|
|
|
|
|
|
void ZigbeeZNPSend(const uint8_t *msg, size_t len) {
|
|
|
|
|
if ((len < 2) || (len > 252)) {
|
|
|
|
|
// abort, message cannot be less than 2 bytes for CMD1 and CMD2
|
|
|
|
|
AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_JSON_ZIGBEEZNPSENT ": bad message len %d"), len);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
uint8_t data_len = len - 2; // removing CMD1 and CMD2
|
|
|
|
|
|
|
|
|
|
if (ZigbeeSerial) {
|
|
|
|
|
uint8_t fcs = data_len;
|
|
|
|
|
|
|
|
|
|
ZigbeeSerial->write(ZIGBEE_SOF); // 0xFE
|
|
|
|
|
//AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("ZNPSend SOF %02X"), ZIGBEE_SOF);
|
|
|
|
|
ZigbeeSerial->write(data_len);
|
|
|
|
|
//AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("ZNPSend LEN %02X"), data_len);
|
|
|
|
|
for (uint32_t i = 0; i < len; i++) {
|
|
|
|
|
uint8_t b = pgm_read_byte(msg + i);
|
|
|
|
|
ZigbeeSerial->write(b);
|
|
|
|
|
fcs ^= b;
|
|
|
|
|
//AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("ZNPSend byt %02X"), b);
|
|
|
|
|
}
|
|
|
|
|
ZigbeeSerial->write(fcs); // finally send fcs checksum byte
|
|
|
|
|
//AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("ZNPSend FCS %02X"), fcs);
|
|
|
|
|
}
|
|
|
|
|
// Now send a MQTT message to report the sent message
|
|
|
|
|
char hex_char[(len * 2) + 2];
|
|
|
|
|
AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE D_JSON_ZIGBEEZNPSENT " %s"),
|
|
|
|
|
ToHex_P(msg, len, hex_char, sizeof(hex_char)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Same code for `ZbZNPSend` and `ZbZNPReceive`
|
|
|
|
|
// building the complete message (intro, length)
|
|
|
|
|
@ -264,36 +447,138 @@ void CmndZbZNPSend(void)
|
|
|
|
|
CmndZbZNPSendOrReceive(true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ZigbeeZNPSend(const uint8_t *msg, size_t len) {
|
|
|
|
|
if ((len < 2) || (len > 252)) {
|
|
|
|
|
#endif // USE_ZIGBEE_ZNP
|
|
|
|
|
|
|
|
|
|
#ifdef USE_ZIGBEE_EZSP
|
|
|
|
|
|
|
|
|
|
// internal function to output a byte, and escape it (stuffing) if needed
|
|
|
|
|
void ZigbeeEZSPSend_Out(uint8_t out_byte) {
|
|
|
|
|
switch (out_byte) {
|
|
|
|
|
case 0x7E: // Flag byte
|
|
|
|
|
case 0x11: // XON
|
|
|
|
|
case 0x13: // XOFF
|
|
|
|
|
case 0x18: // Substitute byte
|
|
|
|
|
case 0x1A: // Cancel byte
|
|
|
|
|
case 0x7D: // Escape byte
|
|
|
|
|
ZigbeeSerial->write(ZIGBEE_EZSP_ESCAPE); // send Escape byte 0x7D
|
|
|
|
|
ZigbeeSerial->write(out_byte ^ 0x10); // send with bit 5 inverted
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
ZigbeeSerial->write(out_byte); // send unchanged
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Send low-level EZSP frames
|
|
|
|
|
//
|
|
|
|
|
// The frame should contain the Control Byte and Data Field
|
|
|
|
|
// The frame shouldn't be escaped, nor randomized
|
|
|
|
|
//
|
|
|
|
|
// Before sending:
|
|
|
|
|
// - send Cancel byte (0x1A) if requested
|
|
|
|
|
// - randomize Data Field if DATA Frame
|
|
|
|
|
// - compute CRC16
|
|
|
|
|
// - escape (stuff) reserved bytes
|
|
|
|
|
// - add EOF (0x7E)
|
|
|
|
|
// - send frame
|
|
|
|
|
// send_cancel: should we first send a EZSP_CANCEL (0x1A) before the message to clear any leftover
|
|
|
|
|
void ZigbeeEZSPSend(const uint8_t *msg, size_t len, bool send_cancel = false) {
|
|
|
|
|
if ((len < 1) || (len > 252)) {
|
|
|
|
|
// abort, message cannot be less than 2 bytes for CMD1 and CMD2
|
|
|
|
|
AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_JSON_ZIGBEEZNPSENT ": bad message len %d"), len);
|
|
|
|
|
AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_JSON_ZIGBEE_EZSP_SENT ": bad message len %d"), len);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
uint8_t data_len = len - 2; // removing CMD1 and CMD2
|
|
|
|
|
|
|
|
|
|
if (ZigbeeSerial) {
|
|
|
|
|
uint8_t fcs = data_len;
|
|
|
|
|
if (send_cancel) {
|
|
|
|
|
ZigbeeSerial->write(ZIGBEE_EZSP_CANCEL); // 0x1A
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ZigbeeSerial->write(ZIGBEE_SOF); // 0xFE
|
|
|
|
|
//AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("ZNPSend SOF %02X"), ZIGBEE_SOF);
|
|
|
|
|
ZigbeeSerial->write(data_len);
|
|
|
|
|
//AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("ZNPSend LEN %02X"), data_len);
|
|
|
|
|
for (uint32_t i = 0; i < len; i++) {
|
|
|
|
|
uint8_t b = pgm_read_byte(msg + i);
|
|
|
|
|
ZigbeeSerial->write(b);
|
|
|
|
|
fcs ^= b;
|
|
|
|
|
//AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("ZNPSend byt %02X"), b);
|
|
|
|
|
}
|
|
|
|
|
ZigbeeSerial->write(fcs); // finally send fcs checksum byte
|
|
|
|
|
//AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("ZNPSend FCS %02X"), fcs);
|
|
|
|
|
bool data_frame = (0 == (msg[0] & 0x80));
|
|
|
|
|
uint8_t rand = 0x42; // pseudo-randomizer initial value
|
|
|
|
|
uint16_t crc = 0xFFFF; // CRC16 CCITT initialization
|
|
|
|
|
|
|
|
|
|
for (uint32_t i=0; i<len; i++) {
|
|
|
|
|
uint8_t out_byte = msg[i];
|
|
|
|
|
|
|
|
|
|
// apply randomization if DATA field
|
|
|
|
|
if (data_frame && (i > 0)) {
|
|
|
|
|
out_byte ^= rand;
|
|
|
|
|
if (rand & 1) { rand = (rand >> 1) ^ 0xB8; }
|
|
|
|
|
else { rand = (rand >> 1); }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// compute CRC
|
|
|
|
|
crc = crc ^ ((uint16_t)out_byte << 8);
|
|
|
|
|
for (uint32_t i=0; i<8; i++) {
|
|
|
|
|
if (crc & 0x8000) {
|
|
|
|
|
crc = (crc << 1) ^ 0x1021; // polynom is x^16 + x^12 + x^5 + 1, CCITT standard
|
|
|
|
|
} else {
|
|
|
|
|
crc <<= 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// output byte
|
|
|
|
|
ZigbeeEZSPSend_Out(out_byte);
|
|
|
|
|
}
|
|
|
|
|
// send CRC16 in big-endian
|
|
|
|
|
ZigbeeEZSPSend_Out(crc >> 8);
|
|
|
|
|
ZigbeeEZSPSend_Out(crc & 0xFF);
|
|
|
|
|
|
|
|
|
|
// finally send End of Frame
|
|
|
|
|
ZigbeeSerial->write(ZIGBEE_EZSP_EOF); // 0x1A
|
|
|
|
|
}
|
|
|
|
|
// Now send a MQTT message to report the sent message
|
|
|
|
|
char hex_char[(len * 2) + 2];
|
|
|
|
|
AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE D_JSON_ZIGBEEZNPSENT " %s"),
|
|
|
|
|
AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE D_JSON_ZIGBEE_EZSP_SENT " %s"),
|
|
|
|
|
ToHex_P(msg, len, hex_char, sizeof(hex_char)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Same code for `ZbZNPSend` and `ZbZNPReceive`
|
|
|
|
|
// building the complete message (intro, length)
|
|
|
|
|
//
|
|
|
|
|
void CmndZbEZSPSendOrReceive(bool send)
|
|
|
|
|
{
|
|
|
|
|
if (ZigbeeSerial && (XdrvMailbox.data_len > 0)) {
|
|
|
|
|
uint8_t code;
|
|
|
|
|
|
|
|
|
|
char *codes = RemoveSpace(XdrvMailbox.data);
|
|
|
|
|
int32_t size = strlen(XdrvMailbox.data);
|
|
|
|
|
|
|
|
|
|
SBuffer buf((size+1)/2);
|
|
|
|
|
|
|
|
|
|
while (size > 1) {
|
|
|
|
|
char stemp[3];
|
|
|
|
|
strlcpy(stemp, codes, sizeof(stemp));
|
|
|
|
|
code = strtol(stemp, nullptr, 16);
|
|
|
|
|
buf.add8(code);
|
|
|
|
|
size -= 2;
|
|
|
|
|
codes += 2;
|
|
|
|
|
}
|
|
|
|
|
if (send) {
|
|
|
|
|
// Command was `ZbEZSPSend`
|
|
|
|
|
ZigbeeEZSPSend(buf.getBuffer(), buf.len());
|
|
|
|
|
} else {
|
|
|
|
|
// Command was `ZbEZSPReceive`
|
|
|
|
|
ZigbeeProcessInput(buf);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
ResponseCmndDone();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// For debug purposes only, simulates a message received
|
|
|
|
|
void CmndZbEZSPReceive(void)
|
|
|
|
|
{
|
|
|
|
|
CmndZbEZSPSendOrReceive(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CmndZbEZSPSend(void)
|
|
|
|
|
{
|
|
|
|
|
CmndZbEZSPSendOrReceive(true);
|
|
|
|
|
}
|
|
|
|
|
#endif // USE_ZIGBEE_EZSP
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Internal function, send the low-level frame
|
|
|
|
|
// Input:
|
|
|
|
|
@ -311,6 +596,7 @@ void ZigbeeZNPSend(const uint8_t *msg, size_t len) {
|
|
|
|
|
//
|
|
|
|
|
void ZigbeeZCLSend_Raw(uint16_t shortaddr, uint16_t groupaddr, uint16_t clusterId, uint8_t endpoint, uint8_t cmdId, bool clusterSpecific, uint16_t manuf, const uint8_t *msg, size_t len, bool needResponse, uint8_t transacId) {
|
|
|
|
|
|
|
|
|
|
#ifdef USE_ZIGBEE_ZNP
|
|
|
|
|
SBuffer buf(32+len);
|
|
|
|
|
buf.add8(Z_SREQ | Z_AF); // 24
|
|
|
|
|
buf.add8(AF_DATA_REQUEST_EXT); // 02
|
|
|
|
|
@ -342,6 +628,7 @@ void ZigbeeZCLSend_Raw(uint16_t shortaddr, uint16_t groupaddr, uint16_t clusterI
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ZigbeeZNPSend(buf.getBuffer(), buf.len());
|
|
|
|
|
#endif // USE_ZIGBEE_ZNP
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/********************************************************************************************/
|
|
|
|
|
@ -914,6 +1201,7 @@ void ZbBindUnbind(bool unbind) { // false = bind, true = unbind
|
|
|
|
|
if (&to_group && dstLongAddr) { ResponseCmndChar_P(PSTR("Cannot have both \"ToDevice\" and \"ToGroup\"")); return; }
|
|
|
|
|
if (!&to_group && !dstLongAddr) { ResponseCmndChar_P(PSTR("Missing \"ToDevice\" or \"ToGroup\"")); return; }
|
|
|
|
|
|
|
|
|
|
#ifdef USE_ZIGBEE_ZNP
|
|
|
|
|
SBuffer buf(34);
|
|
|
|
|
buf.add8(Z_SREQ | Z_ZDO);
|
|
|
|
|
if (unbind) {
|
|
|
|
|
@ -935,6 +1223,7 @@ void ZbBindUnbind(bool unbind) { // false = bind, true = unbind
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ZigbeeZNPSend(buf.getBuffer(), buf.len());
|
|
|
|
|
#endif // USE_ZIGBEE_ZNP
|
|
|
|
|
|
|
|
|
|
ResponseCmndDone();
|
|
|
|
|
}
|
|
|
|
|
@ -961,6 +1250,7 @@ void CmndZbBindState(void) {
|
|
|
|
|
uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data);
|
|
|
|
|
if (BAD_SHORTADDR == shortaddr) { ResponseCmndChar_P(PSTR("Unknown device")); return; }
|
|
|
|
|
|
|
|
|
|
#ifdef USE_ZIGBEE_ZNP
|
|
|
|
|
SBuffer buf(10);
|
|
|
|
|
buf.add8(Z_SREQ | Z_ZDO); // 25
|
|
|
|
|
buf.add8(ZDO_MGMT_BIND_REQ); // 33
|
|
|
|
|
@ -968,6 +1258,7 @@ void CmndZbBindState(void) {
|
|
|
|
|
buf.add8(0); // StartIndex = 0
|
|
|
|
|
|
|
|
|
|
ZigbeeZNPSend(buf.getBuffer(), buf.len());
|
|
|
|
|
#endif // USE_ZIGBEE_ZNP
|
|
|
|
|
|
|
|
|
|
ResponseCmndDone();
|
|
|
|
|
}
|
|
|
|
|
@ -1191,6 +1482,7 @@ void CmndZbPermitJoin(void) {
|
|
|
|
|
duration = 0xFF; // unlimited time
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifdef USE_ZIGBEE_ZNP
|
|
|
|
|
SBuffer buf(34);
|
|
|
|
|
buf.add8(Z_SREQ | Z_ZDO); // 25
|
|
|
|
|
buf.add8(ZDO_MGMT_PERMIT_JOIN_REQ); // 36
|
|
|
|
|
@ -1200,6 +1492,7 @@ void CmndZbPermitJoin(void) {
|
|
|
|
|
buf.add8(0x00); // TCSignificance
|
|
|
|
|
|
|
|
|
|
ZigbeeZNPSend(buf.getBuffer(), buf.len());
|
|
|
|
|
#endif // USE_ZIGBEE_ZNP
|
|
|
|
|
|
|
|
|
|
ResponseCmndDone();
|
|
|
|
|
}
|
|
|
|
|
|