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
@ -40,9 +40,9 @@
#define XM_XOFF 0x13
#define XM_NAK 0x15
#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;
@ -87,7 +87,9 @@ char ZigbeeUploadFlashRead(void) {
ZbUpload.byte_counter++;
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
}
return data;
@ -143,7 +145,7 @@ char XModemWaitACK(void)
if (i > 200) { return -1; }
}
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'));
return in_char;
}
@ -187,20 +189,6 @@ bool XModemSendPacket(uint32_t packet_no) {
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
*
@ -215,15 +203,6 @@ void ZigbeeUploadSetBootloader(uint8_t state) {
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) {
// 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
@ -246,26 +225,28 @@ bool ZigbeeUploadBootloaderPrompt(void) {
}
bool ZigbeeUploadXmodem(void) {
if (!ZbUpload.ota_step) { return false; }
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"));
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.delay = millis() + 500;
ZbUpload.byte_counter = 0;
ZbUpload.ota_step = ZBU_PROMPT;
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) {
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("XMD: Bootloader timeout"));
ZigbeeUploadBootloaderDone();
ZbUpload.ota_step = ZBU_DONE;
return true;
}
else if (!ZigbeeSerial->available()) {
// The target devices bootloader sends output over its serial port after it receives a carriage return
// from the source device
// The target devices bootloader sends output over its serial port after it receives a
// carriage return from the source device
if (millis() > XModem.delay) {
ZigbeeSerial->write(XM_CR);
XModem.delay = millis() + 500;
@ -280,9 +261,9 @@ bool ZigbeeUploadXmodem(void) {
if (ZigbeeUploadBootloaderPrompt()) {
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("XMD: Init sync"));
ZigbeeSerial->flush();
ZigbeeSerial->write('1'); // upload ebl
ZigbeeSerial->write('1'); // upload ebl
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);
ZbUpload.ota_step = ZBU_SYNC;
@ -290,17 +271,16 @@ bool ZigbeeUploadXmodem(void) {
}
break;
}
case ZBU_SYNC: { // Handle file upload using XModem - sync
case ZBU_SYNC: { // *** Handle file upload using XModem - sync
if (millis() > XModem.timeout) {
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("XMD: Sync timeout"));
ZigbeeUploadBootloaderDone();
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("XMD: SYNC timeout"));
ZbUpload.ota_step = ZBU_DONE;
return true;
}
// Wait for either C or NACK as a sync packet.
// Determines protocol details, checksum algorithm.
// Wait for either C or NACK as a sync packet. Determines protocol details, checksum algorithm.
if (ZigbeeSerial->available()) {
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
XModem.oldChecksum = (xmodem_sync == XM_NAK);
XModem.packetNo = 1;
@ -311,27 +291,78 @@ bool ZigbeeUploadXmodem(void) {
}
break;
}
case ZBU_UPLOAD: { // Handle file upload using XModem - upload
case ZBU_UPLOAD: { // *** Handle file upload using XModem - upload
if (ZigbeeUploadAvailable()) {
if (!XModemSendPacket(XModem.packetNo)) {
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("XMD: Packet send failed"));
ZigbeeUploadBootloaderDone();
ZbUpload.ota_step = ZBU_DONE;
return true;
}
XModem.packetNo++;
} else {
if (!XModemSendEOT()) {
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("XMD: EOT failed"));
ZigbeeUploadBootloaderDone();
return true;
}
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("XMD: Complete"));
ZbUpload.ota_step = ZBU_DONE;
// Once the last block is ACKed by the target, the transfer should be finalized by an
// EOT (ASCII 0x04) packet from the source. Once this packet is confirmed via XModem ACK
// from the target, the device will reboot, causing the new firmware to be launched.
ZigbeeSerial->write(XM_EOT);
XModem.timeout = millis() + (30 * 1000); // Allow 30 seconds to receive EOT ACK
ZbUpload.ota_step = ZBU_EOT;
}
break;
}
case ZBU_DONE: { // Clean up and restart to disable bootloader and use new firmware
ZigbeeUploadBootloaderDone();
case ZBU_EOT: { // *** Send EOT and wait for ACK
// 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;
@ -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
ZbUpload.sector_cursor = 0;
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
}
}