POC3 Zigbee EFR32 xmodem upload

POC3 Zigbee EFR32 xmodem upload (#8583)
- Extend ACK timeout after EOT
This commit is contained in:
Theo Arends 2020-07-27 15:43:24 +02:00
parent e8beeba651
commit 2012eaccf4

View File

@ -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 devices bootloader sends output over its serial port after it receives a carriage return // The target devices 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
} }
} }