mirror of
https://github.com/arendst/Tasmota.git
synced 2025-07-24 11:16:34 +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
|
||||
|
||||
@ -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 device’s bootloader sends output over its serial port after it receives a carriage return
|
||||
// from the source device
|
||||
// The target device’s 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
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user