From eec6869a0a276d5057d78734a4894d345a37ad0d Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Wed, 22 Jul 2020 19:29:16 +0200 Subject: [PATCH] EZSP flow control --- tasmota/xdrv_23_zigbee_7_statemachine.ino | 2 +- tasmota/xdrv_23_zigbee_8_parsers.ino | 2 +- tasmota/xdrv_23_zigbee_9_serial.ino | 117 +++++++++++++++++----- tasmota/xdrv_23_zigbee_A_impl.ino | 11 +- 4 files changed, 100 insertions(+), 32 deletions(-) diff --git a/tasmota/xdrv_23_zigbee_7_statemachine.ino b/tasmota/xdrv_23_zigbee_7_statemachine.ino index 87bae5911..f04c0ca34 100644 --- a/tasmota/xdrv_23_zigbee_7_statemachine.ino +++ b/tasmota/xdrv_23_zigbee_7_statemachine.ino @@ -1097,7 +1097,7 @@ void ZigbeeStateMachine_Run(void) { ZigbeeZNPSend((uint8_t*) cur_ptr1, cur_d8 /* len */); #endif // USE_ZIGBEE_ZNP #ifdef USE_ZIGBEE_EZSP - ZigbeeEZSPSendCmd((uint8_t*) cur_ptr1, cur_d8 /* len */, true); // send cancel byte + ZigbeeEZSPSendCmd((uint8_t*) cur_ptr1, cur_d8 /* len */); // send cancel byte #endif // USE_ZIGBEE_EZSP break; case ZGB_INSTR_WAIT_UNTIL: diff --git a/tasmota/xdrv_23_zigbee_8_parsers.ino b/tasmota/xdrv_23_zigbee_8_parsers.ino index 738a67c8b..58d4e330b 100644 --- a/tasmota/xdrv_23_zigbee_8_parsers.ino +++ b/tasmota/xdrv_23_zigbee_8_parsers.ino @@ -1005,7 +1005,7 @@ void EZ_SendZDO(uint16_t shortaddr, uint16_t cmd, const unsigned char *payload, buf.addBuffer(payload, payload_len); } - ZigbeeEZSPSendCmd(buf.buf(), buf.len(), true); + ZigbeeEZSPSendCmd(buf.buf(), buf.len()); } /*********************************************************************************************\ diff --git a/tasmota/xdrv_23_zigbee_9_serial.ino b/tasmota/xdrv_23_zigbee_9_serial.ino index 34555441f..bfec8c3bb 100644 --- a/tasmota/xdrv_23_zigbee_9_serial.ino +++ b/tasmota/xdrv_23_zigbee_9_serial.ino @@ -36,11 +36,15 @@ const uint32_t ZIGBEE_LED_SEND = 0; // LED<2> blinks when receiving class EZSP_Serial_t { public: - uint8_t to_ack = 0; // 0..7, frame number of next id to send + uint8_t to_send = 0; // 0..7, frame number of next packet to send, nothing to send if equal to to_end + uint8_t to_end = 0; // 0..7, frame number of next packet to send + uint8_t to_ack = 0; // 0..7, frame number of last packet acknowledged + 1 uint8_t from_ack = 0; // 0..7, frame to ack uint8_t ezsp_seq = 0; // 0..255, EZSP sequence number + SBuffer *to_packets[8] = { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr }; }; + EZSP_Serial_t EZSP_Serial; #endif // USE_ZIGBEE_EZSP @@ -493,7 +497,7 @@ void ZigbeeEZSPSendRaw(const uint8_t *msg, size_t len, bool send_cancel) { // Send an EZSP command and data // Ex: Version with min v8 = 000008 -void ZigbeeEZSPSendCmd(const uint8_t *msg, size_t len, bool send_cancel) { +void ZigbeeEZSPSendCmd(const uint8_t *msg, size_t len) { char hex_char[len*2 + 2]; ToHex_P(msg, len, hex_char, sizeof(hex_char)); AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "ZbEZSPSend %s"), hex_char); @@ -506,20 +510,45 @@ void ZigbeeEZSPSendCmd(const uint8_t *msg, size_t len, bool send_cancel) { cmd.addBuffer(msg, len); // send - ZigbeeEZSPSendDATA(cmd.getBuffer(), cmd.len(), send_cancel); + ZigbeeEZSPSendDATA(cmd.getBuffer(), cmd.len()); } // Send an EZSP DATA frame, automatically calculating the correct frame numbers -void ZigbeeEZSPSendDATA(const uint8_t *msg, size_t len, bool send_cancel) { - uint8_t control_byte = ((EZSP_Serial.to_ack & 0x07) << 4) + (EZSP_Serial.from_ack & 0x07); - // increment to_ack - EZSP_Serial.to_ack = (EZSP_Serial.to_ack + 1) & 0x07; - // build complete frame - SBuffer buf(len+1); - buf.add8(control_byte); - buf.addBuffer(msg, len); +void ZigbeeEZSPSendDATA_frm(bool send_cancel, uint8_t to_frm, uint8_t from_ack) { + SBuffer *buf = EZSP_Serial.to_packets[to_frm]; + if (!buf) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZIG: Buffer for packet %d is not allocated"), EZSP_Serial.to_send); + return; + } + + uint8_t control_byte = ((to_frm & 0x07) << 4) + (from_ack & 0x07); + buf->set8(0, control_byte); // change control_byte // send - ZigbeeEZSPSendRaw(buf.getBuffer(), buf.len(), send_cancel); + ZigbeeEZSPSendRaw(buf->getBuffer(), buf->len(), send_cancel); +} + +// Send an EZSP DATA frame, automatically calculating the correct frame numbers +void ZigbeeEZSPSendDATA(const uint8_t *msg, size_t len) { + // prepare buffer by adding 1 byte prefix + SBuffer *buf = new SBuffer(len+1); // prepare for control_byte prefix + buf->add8(0x00); // placeholder for control_byte + buf->addBuffer(msg, len); + // + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZIG: adding packet to_send, to_ack:%d, to_send:%d, to_end:%d"), + EZSP_Serial.to_ack, EZSP_Serial.to_send, EZSP_Serial.to_end); + uint8_t to_frm = EZSP_Serial.to_end; + if (EZSP_Serial.to_packets[to_frm]) { + delete EZSP_Serial.to_packets[to_frm]; + EZSP_Serial.to_packets[to_frm] = nullptr; + } + EZSP_Serial.to_packets[to_frm] = buf; + EZSP_Serial.to_end = (to_frm + 1) & 0x07; // move cursor + + // ZigbeeEZSPSendDATA_frm(send_cancel, to_frm, EZSP_Serial.from_ack); + + // increment to_frame + //EZSP_Serial.to_ack = (EZSP_Serial.to_ack + 1) & 0x07; + //EZSP_Serial.to_frm = (EZSP_Serial.to_frm + 1) & 0x07; } // Receive a high-level EZSP command/response, starting with 16-bits frame ID @@ -557,23 +586,41 @@ int32_t ZigbeeProcessInputEZSP(class SBuffer &buf) { ZigbeeProcessInput(buf); } +// Check if we advanced in the ACKed frames, and free from memory packets acknowledged +void EZSP_HandleAck(uint8_t new_ack) { + if (EZSP_Serial.to_ack != new_ack) { // new ack receveid + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZIG: new ack/data received, was %d now %d"), EZSP_Serial.to_ack, new_ack); + uint32_t i = EZSP_Serial.to_ack; + do { + if (EZSP_Serial.to_packets[i]) { + delete EZSP_Serial.to_packets[i]; + EZSP_Serial.to_packets[i] = nullptr; + } + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZIG: freeing packet %d from memory"), i); + i = (i + 1) & 0x07; + } while (i != new_ack); + EZSP_Serial.to_ack = new_ack; + } +} // Receive raw ASH frame (CRC was removed, data unstuffed) but still contains frame numbers int32_t ZigbeeProcessInputRaw(class SBuffer &buf) { uint8_t control_byte = buf.get8(0); uint8_t ack_num = control_byte & 0x07; // keep 3 LSB - if (control_byte & 0x80) { + if (control_byte & 0x80) { // non DATA frame - // non DATA frame uint8_t frame_type = control_byte & 0xE0; // keep 3 MSB if (frame_type == 0x80) { // ACK - EZSP_Serial.from_ack = ack_num; // update ack num + EZSP_HandleAck(ack_num); } else if (frame_type == 0xA0) { // NAK - AddLog_P2(LOG_LEVEL_INFO, PSTR("ZIG: Received NAK %d, resending not implemented"), ack_num); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZIG: Received NAK %d, to_ack:%d, to_send:%d, to_end:%d"), + ack_num, EZSP_Serial.to_ack, EZSP_Serial.to_send, EZSP_Serial.to_end); + EZSP_Serial.to_send = ack_num; + AddLog_P2(LOG_LEVEL_INFO, PSTR("ZIG: NAK, resending packet %d"), ack_num); } else if (control_byte == 0xC1) { // RSTACK @@ -581,6 +628,8 @@ int32_t ZigbeeProcessInputRaw(class SBuffer &buf) { EZ_RSTACK(buf.get8(2)); EZSP_Serial.from_ack = 0; EZSP_Serial.to_ack = 0; + EZSP_Serial.to_end = 0; + EZSP_Serial.to_send = 0; // pass it to state machine with a special 0xFFFE frame code (EZSP_RSTACK_ID) buf.set8(0, Z_B0(EZSP_rstAck)); @@ -598,14 +647,12 @@ int32_t ZigbeeProcessInputRaw(class SBuffer &buf) { // Unknown AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZIG: Received unknown control byte 0x%02X"), control_byte); } - } else { + } else { // DATA Frame + + // adjust to latest acked packet + uint8_t new_ack = control_byte & 0x07; + EZSP_HandleAck(new_ack); - // DATA Frame - // check the frame number, and send ACK or NAK - if ((control_byte & 0x07) != EZSP_Serial.to_ack) { - AddLog_P2(LOG_LEVEL_INFO, PSTR("ZIG: wrong ack, received %d, expected %d"), control_byte & 0x07, EZSP_Serial.to_ack); - //EZSP_Serial.to_ack = control_byte & 0x07; - } // MCU acknowledged the correct frame // we acknowledge the frame too EZSP_Serial.from_ack = ((control_byte >> 4) + 1) & 0x07; @@ -651,9 +698,9 @@ void CmndZbEZSPSendOrReceive(bool send) } if (send) { // Command was `ZbEZSPSend` - if (2 == XdrvMailbox.index) { ZigbeeEZSPSendDATA(buf.getBuffer(), buf.len(), true); } + if (2 == XdrvMailbox.index) { ZigbeeEZSPSendDATA(buf.getBuffer(), buf.len()); } else if (3 == XdrvMailbox.index) { ZigbeeEZSPSendRaw(buf.getBuffer(), buf.len(), true); } - else { ZigbeeEZSPSendCmd(buf.getBuffer(), buf.len(), true); } + else { ZigbeeEZSPSendCmd(buf.getBuffer(), buf.len()); } } else { // Command was `ZbEZSPReceive` @@ -785,7 +832,25 @@ void ZigbeeZCLSend_Raw(uint16_t shortaddr, uint16_t groupaddr, uint16_t clusterI } } - ZigbeeEZSPSendCmd(buf.buf(), buf.len(), true); + ZigbeeEZSPSendCmd(buf.buf(), buf.len()); +#endif // USE_ZIGBEE_EZSP +} + +// +// Send any buffered data to the NCP +// +// Used only with EZSP, as there is no replay of procotol control with ZNP +void ZigbeeOutputLoop(void) { +#ifdef USE_ZIGBEE_EZSP + // while (EZSP_Serial.to_send != EZSP_Serial.to_end) { + if (EZSP_Serial.to_send != EZSP_Serial.to_end) { // we send only one packet per tick to lower the chance of NAK + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZIG: Something to_send, to_ack:%d, to_send:%d, to_end:%d"), + EZSP_Serial.to_ack, EZSP_Serial.to_send, EZSP_Serial.to_end); + // we have a frame waiting to be sent + ZigbeeEZSPSendDATA_frm(true, EZSP_Serial.to_send, EZSP_Serial.from_ack); + // increment sent counter + EZSP_Serial.to_send = (EZSP_Serial.to_send + 1) & 0x07; + } #endif // USE_ZIGBEE_EZSP } diff --git a/tasmota/xdrv_23_zigbee_A_impl.ino b/tasmota/xdrv_23_zigbee_A_impl.ino index 5922b3ee3..6b23590cc 100644 --- a/tasmota/xdrv_23_zigbee_A_impl.ino +++ b/tasmota/xdrv_23_zigbee_A_impl.ino @@ -1023,13 +1023,13 @@ void CmndZbPermitJoin(void) { SBuffer buf(3); buf.add16(EZSP_permitJoining); buf.add8(duration); - ZigbeeEZSPSendCmd(buf.getBuffer(), buf.len(), true); + ZigbeeEZSPSendCmd(buf.getBuffer(), buf.len()); // send ZDO_Mgmt_Permit_Joining_req to all routers buf.setLen(0); buf.add8(duration); buf.add8(0x01); // TC_Significance - This field shall always have a value of 1, indicating a request to change the Trust Center policy. If a frame is received with a value of 0, it shall be treated as having a value of 1. - // EZ_SendZDO(0xFFFC, ZDO_Mgmt_Permit_Joining_req, buf.buf(), buf.len()); TODO fix NAK/ACK first + EZ_SendZDO(0xFFFC, ZDO_Mgmt_Permit_Joining_req, buf.buf(), buf.len()); #endif // USE_ZIGBEE_EZSP ResponseCmndDone(); @@ -1059,7 +1059,7 @@ void CmndZbEZSPListen(void) { buf.add16(group); // group buf.add8(0x01); // endpoint buf.add8(0x00); // network index - ZigbeeEZSPSendCmd(buf.getBuffer(), buf.len(), true); + ZigbeeEZSPSendCmd(buf.getBuffer(), buf.len()); ResponseCmndDone(); } @@ -1240,7 +1240,10 @@ bool Xdrv23(uint8_t function) } break; case FUNC_LOOP: - if (ZigbeeSerial) { ZigbeeInputLoop(); } + if (ZigbeeSerial) { + ZigbeeInputLoop(); + ZigbeeOutputLoop(); // send any outstanding data + } if (zigbee.state_machine) { ZigbeeStateMachine_Run(); }