mirror of
https://github.com/arendst/Tasmota.git
synced 2025-07-27 04:36:31 +00:00
POC3 Zigbee EFR32 xmodem upload
POC3 Zigbee EFR32 xmodem upload (#8583) - Extend ACK timeout after EOT
This commit is contained in:
parent
e8beeba651
commit
2012eaccf4
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
xdrv_23_zigbee_9_serial.ino - zigbee: serial communication with MCU
|
xdrv_23_zigbee_9a_upload.ino - zigbee: serial xmodem upload to MCU
|
||||||
|
|
||||||
Copyright (C) 2020 Theo Arends and Stephan Hadinger
|
Copyright (C) 2020 Theo Arends and Stephan Hadinger
|
||||||
|
|
||||||
@ -40,9 +40,9 @@
|
|||||||
#define XM_XOFF 0x13
|
#define XM_XOFF 0x13
|
||||||
#define XM_NAK 0x15
|
#define XM_NAK 0x15
|
||||||
#define XM_CAN 0x18
|
#define XM_CAN 0x18
|
||||||
#define XM_EOF 0x1a
|
#define XM_SUB 0x1a
|
||||||
|
|
||||||
enum ZbUploadSteps { ZBU_IDLE, ZBU_INIT, ZBU_PROMPT, ZBU_SYNC, ZBU_UPLOAD, ZBU_DONE, ZBU_COMPLETE };
|
enum ZbUploadSteps { ZBU_IDLE, ZBU_INIT, ZBU_PROMPT, ZBU_SYNC, ZBU_UPLOAD, ZBU_EOT, ZBU_COMPLETE, ZBU_DONE, ZBU_FINISH };
|
||||||
|
|
||||||
const uint8_t PIN_ZIGBEE_BOOTLOADER = 5;
|
const uint8_t PIN_ZIGBEE_BOOTLOADER = 5;
|
||||||
|
|
||||||
@ -87,7 +87,9 @@ char ZigbeeUploadFlashRead(void) {
|
|||||||
ZbUpload.byte_counter++;
|
ZbUpload.byte_counter++;
|
||||||
|
|
||||||
if (ZbUpload.byte_counter > ZbUpload.ota_size) {
|
if (ZbUpload.byte_counter > ZbUpload.ota_size) {
|
||||||
data = XM_EOF;
|
// When the source device reaches the last XModem data block, it should be padded to 128 bytes
|
||||||
|
// of data using SUB (ASCII 0x1A) characters.
|
||||||
|
data = XM_SUB;
|
||||||
// if (ZbUpload.buffer) { free(ZbUpload.buffer); } // Don't in case of retries
|
// if (ZbUpload.buffer) { free(ZbUpload.buffer); } // Don't in case of retries
|
||||||
}
|
}
|
||||||
return data;
|
return data;
|
||||||
@ -143,7 +145,7 @@ char XModemWaitACK(void)
|
|||||||
if (i > 200) { return -1; }
|
if (i > 200) { return -1; }
|
||||||
}
|
}
|
||||||
in_char = ZigbeeSerial->read();
|
in_char = ZigbeeSerial->read();
|
||||||
if (in_char == XM_CAN) { return XM_CAN; }
|
if (XM_CAN == in_char) { return XM_CAN; }
|
||||||
} while ((in_char != XM_NAK) && (in_char != XM_ACK) && (in_char != 'C'));
|
} while ((in_char != XM_NAK) && (in_char != XM_ACK) && (in_char != 'C'));
|
||||||
return in_char;
|
return in_char;
|
||||||
}
|
}
|
||||||
@ -187,20 +189,6 @@ bool XModemSendPacket(uint32_t packet_no) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool XModemSendEOT(void) {
|
|
||||||
// Send EOT and wait for ACK
|
|
||||||
uint32_t retries = 0;
|
|
||||||
char in_char;
|
|
||||||
do {
|
|
||||||
ZigbeeSerial->write(XM_EOT);
|
|
||||||
in_char = XModemWaitACK();
|
|
||||||
retries++;
|
|
||||||
// When timed out, leave immediately
|
|
||||||
if (retries == XMODEM_SYNC_TIMEOUT) { return false; }
|
|
||||||
} while (in_char != XM_ACK);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*********************************************************************************************\
|
/*********************************************************************************************\
|
||||||
* Step 2 - Upload MCU firmware from ESP8266 flash to MCU EFR32 using XMODEM protocol
|
* Step 2 - Upload MCU firmware from ESP8266 flash to MCU EFR32 using XMODEM protocol
|
||||||
*
|
*
|
||||||
@ -215,15 +203,6 @@ void ZigbeeUploadSetBootloader(uint8_t state) {
|
|||||||
digitalWrite(Pin(GPIO_ZIGBEE_RST), 1); // Reboot MCU EFR32
|
digitalWrite(Pin(GPIO_ZIGBEE_RST), 1); // Reboot MCU EFR32
|
||||||
}
|
}
|
||||||
|
|
||||||
void ZigbeeUploadBootloaderDone(void) {
|
|
||||||
ZbUpload.ota_step = ZBU_COMPLETE; // Never return to zero without a restart to get a sane Zigbee environment
|
|
||||||
ZigbeeUploadSetBootloader(1); // Disable bootloader and reset MCU - should happen or restart
|
|
||||||
if (1 == ssleep) {
|
|
||||||
ssleep = Settings.sleep; // Restore loop sleep
|
|
||||||
}
|
|
||||||
restart_flag = 2; // Restart to disable bootloader and use new firmware
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ZigbeeUploadBootloaderPrompt(void) {
|
bool ZigbeeUploadBootloaderPrompt(void) {
|
||||||
// Scripts that interact with the bootloader should use only the “BL >” prompt to determine
|
// Scripts that interact with the bootloader should use only the “BL >” prompt to determine
|
||||||
// when the bootloader is ready for input. While current menu options should remain functionally
|
// when the bootloader is ready for input. While current menu options should remain functionally
|
||||||
@ -246,26 +225,28 @@ bool ZigbeeUploadBootloaderPrompt(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool ZigbeeUploadXmodem(void) {
|
bool ZigbeeUploadXmodem(void) {
|
||||||
if (!ZbUpload.ota_step) { return false; }
|
|
||||||
|
|
||||||
switch (ZbUpload.ota_step) {
|
switch (ZbUpload.ota_step) {
|
||||||
case ZBU_INIT: { // Init ESF32 bootloader
|
case ZBU_IDLE: { // *** Upload disabled
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
case ZBU_INIT: { // *** Init ESF32 bootloader
|
||||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("XMD: Init bootloader"));
|
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("XMD: Init bootloader"));
|
||||||
ZigbeeUploadSetBootloader(0); // Reboot MCU EFR32 which returns below text
|
ZigbeeUploadSetBootloader(0); // Reboot MCU EFR32 which returns below text
|
||||||
XModem.timeout = millis() + (30 * 1000); // Allow 30 seconds to receive EBL prompt
|
XModem.timeout = millis() + (30 * 1000); // Allow 30 seconds to receive EBL prompt
|
||||||
XModem.delay = millis() + 500;
|
XModem.delay = millis() + 500;
|
||||||
|
ZbUpload.byte_counter = 0;
|
||||||
ZbUpload.ota_step = ZBU_PROMPT;
|
ZbUpload.ota_step = ZBU_PROMPT;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ZBU_PROMPT: { // Wait for prompt and select option upload ebl
|
case ZBU_PROMPT: { // *** Wait for prompt and select option upload ebl
|
||||||
if (millis() > XModem.timeout) {
|
if (millis() > XModem.timeout) {
|
||||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("XMD: Bootloader timeout"));
|
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("XMD: Bootloader timeout"));
|
||||||
ZigbeeUploadBootloaderDone();
|
ZbUpload.ota_step = ZBU_DONE;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else if (!ZigbeeSerial->available()) {
|
else if (!ZigbeeSerial->available()) {
|
||||||
// The target device’s bootloader sends output over its serial port after it receives a carriage return
|
// The target device’s bootloader sends output over its serial port after it receives a
|
||||||
// from the source device
|
// carriage return from the source device
|
||||||
if (millis() > XModem.delay) {
|
if (millis() > XModem.delay) {
|
||||||
ZigbeeSerial->write(XM_CR);
|
ZigbeeSerial->write(XM_CR);
|
||||||
XModem.delay = millis() + 500;
|
XModem.delay = millis() + 500;
|
||||||
@ -280,9 +261,9 @@ bool ZigbeeUploadXmodem(void) {
|
|||||||
if (ZigbeeUploadBootloaderPrompt()) {
|
if (ZigbeeUploadBootloaderPrompt()) {
|
||||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("XMD: Init sync"));
|
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("XMD: Init sync"));
|
||||||
ZigbeeSerial->flush();
|
ZigbeeSerial->flush();
|
||||||
ZigbeeSerial->write('1'); // upload ebl
|
ZigbeeSerial->write('1'); // upload ebl
|
||||||
if (ssleep > 0) {
|
if (ssleep > 0) {
|
||||||
ssleep = 1; // Speed up loop used for xmodem upload
|
ssleep = 1; // Speed up loop used for xmodem upload
|
||||||
}
|
}
|
||||||
XModem.timeout = millis() + (XMODEM_SYNC_TIMEOUT * 1000);
|
XModem.timeout = millis() + (XMODEM_SYNC_TIMEOUT * 1000);
|
||||||
ZbUpload.ota_step = ZBU_SYNC;
|
ZbUpload.ota_step = ZBU_SYNC;
|
||||||
@ -290,17 +271,16 @@ bool ZigbeeUploadXmodem(void) {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ZBU_SYNC: { // Handle file upload using XModem - sync
|
case ZBU_SYNC: { // *** Handle file upload using XModem - sync
|
||||||
if (millis() > XModem.timeout) {
|
if (millis() > XModem.timeout) {
|
||||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("XMD: Sync timeout"));
|
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("XMD: SYNC timeout"));
|
||||||
ZigbeeUploadBootloaderDone();
|
ZbUpload.ota_step = ZBU_DONE;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// Wait for either C or NACK as a sync packet.
|
// Wait for either C or NACK as a sync packet. Determines protocol details, checksum algorithm.
|
||||||
// Determines protocol details, checksum algorithm.
|
|
||||||
if (ZigbeeSerial->available()) {
|
if (ZigbeeSerial->available()) {
|
||||||
char xmodem_sync = ZigbeeSerial->read();
|
char xmodem_sync = ZigbeeSerial->read();
|
||||||
if ((xmodem_sync == 'C') || (xmodem_sync == XM_NAK)) {
|
if (('C' == xmodem_sync) || (XM_NAK == xmodem_sync)) {
|
||||||
// Determine which checksum algorithm to use
|
// Determine which checksum algorithm to use
|
||||||
XModem.oldChecksum = (xmodem_sync == XM_NAK);
|
XModem.oldChecksum = (xmodem_sync == XM_NAK);
|
||||||
XModem.packetNo = 1;
|
XModem.packetNo = 1;
|
||||||
@ -311,27 +291,78 @@ bool ZigbeeUploadXmodem(void) {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ZBU_UPLOAD: { // Handle file upload using XModem - upload
|
case ZBU_UPLOAD: { // *** Handle file upload using XModem - upload
|
||||||
if (ZigbeeUploadAvailable()) {
|
if (ZigbeeUploadAvailable()) {
|
||||||
if (!XModemSendPacket(XModem.packetNo)) {
|
if (!XModemSendPacket(XModem.packetNo)) {
|
||||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("XMD: Packet send failed"));
|
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("XMD: Packet send failed"));
|
||||||
ZigbeeUploadBootloaderDone();
|
ZbUpload.ota_step = ZBU_DONE;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
XModem.packetNo++;
|
XModem.packetNo++;
|
||||||
} else {
|
} else {
|
||||||
if (!XModemSendEOT()) {
|
// Once the last block is ACKed by the target, the transfer should be finalized by an
|
||||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("XMD: EOT failed"));
|
// EOT (ASCII 0x04) packet from the source. Once this packet is confirmed via XModem ACK
|
||||||
ZigbeeUploadBootloaderDone();
|
// from the target, the device will reboot, causing the new firmware to be launched.
|
||||||
return true;
|
ZigbeeSerial->write(XM_EOT);
|
||||||
}
|
XModem.timeout = millis() + (30 * 1000); // Allow 30 seconds to receive EOT ACK
|
||||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("XMD: Complete"));
|
ZbUpload.ota_step = ZBU_EOT;
|
||||||
ZbUpload.ota_step = ZBU_DONE;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ZBU_DONE: { // Clean up and restart to disable bootloader and use new firmware
|
case ZBU_EOT: { // *** Send EOT and wait for ACK
|
||||||
ZigbeeUploadBootloaderDone();
|
// The ACK for the last XModem data packet may take much longer (1-3 seconds) than prior
|
||||||
|
// data packets to be received. This is due to the CRC32 checksum being performed across
|
||||||
|
// the received EBL file data prior to sending the ACK. The source device must ensure that
|
||||||
|
// its XModem state machine waits a sufficient amount of time to allow this checksum process
|
||||||
|
// to occur without timing out on the response just before the EOT is sent.
|
||||||
|
if (millis() > XModem.timeout) {
|
||||||
|
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("XMD: EOT ACK timeout"));
|
||||||
|
ZbUpload.ota_step = ZBU_DONE;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (ZigbeeSerial->available()) {
|
||||||
|
char xmodem_ack = XModemWaitACK();
|
||||||
|
if (XM_ACK == xmodem_ack) {
|
||||||
|
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("XMD: " D_SUCCESSFUL));
|
||||||
|
XModem.timeout = millis() + (30 * 1000); // Allow 30 seconds to receive EBL prompt
|
||||||
|
ZbUpload.byte_counter = 0;
|
||||||
|
// ZbUpload.ota_step = ZBU_COMPLETE;
|
||||||
|
ZbUpload.ota_step = ZBU_DONE; // Skip prompt for now
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
case ZBU_COMPLETE: { // *** Wait for Serial upload complete EBL prompt
|
||||||
|
if (millis() > XModem.timeout) {
|
||||||
|
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("XMD: Bootloader timeout"));
|
||||||
|
ZbUpload.ota_step = ZBU_DONE;
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
// After an image successfully uploads, the XModem transaction completes and the bootloader displays
|
||||||
|
// ‘Serial upload complete’ before redisplaying the menu
|
||||||
|
// Serial upload complete
|
||||||
|
// BL >
|
||||||
|
if (ZigbeeUploadBootloaderPrompt()) {
|
||||||
|
ZbUpload.ota_step = ZBU_DONE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
case ZBU_DONE: { // *** Clean up and restart to disable bootloader and use new firmware
|
||||||
|
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("XMD: " D_RESTARTING));
|
||||||
|
ZigbeeUploadSetBootloader(1); // Disable bootloader and reset MCU - should happen at restart
|
||||||
|
if (1 == ssleep) {
|
||||||
|
ssleep = Settings.sleep; // Restore loop sleep
|
||||||
|
}
|
||||||
|
restart_flag = 2; // Restart to disable bootloader and use new firmware
|
||||||
|
ZbUpload.ota_step = ZBU_FINISH; // Never return to zero without a restart to get a sane Zigbee environment
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ZBU_FINISH: { // *** Wait for restart making sure not to start Zigbee serial again
|
||||||
|
// Wait for restart
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@ -367,7 +398,7 @@ bool ZigbeeUploadWriteBuffer(uint8_t *buf, size_t size) {
|
|||||||
if (2 == ZbUpload.sector_cursor) { // The web upload sends 2048 bytes at a time so keep track of the cursor position to reset it for the next flash sector erase
|
if (2 == ZbUpload.sector_cursor) { // The web upload sends 2048 bytes at a time so keep track of the cursor position to reset it for the next flash sector erase
|
||||||
ZbUpload.sector_cursor = 0;
|
ZbUpload.sector_cursor = 0;
|
||||||
ZbUpload.sector_counter++;
|
ZbUpload.sector_counter++;
|
||||||
if (ZbUpload.sector_counter > (SPIFFS_END + 12)) {
|
if (ZbUpload.sector_counter > (SPIFFS_END -2)) {
|
||||||
return false; // File too large - Not enough free space
|
return false; // File too large - Not enough free space
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user