2020-07-25 17:25:06 +01:00
/*
2020-07-27 14:43:24 +01:00
xdrv_23_zigbee_9a_upload . ino - zigbee : serial xmodem upload to MCU
2020-07-25 17:25:06 +01:00
Copyright ( C ) 2020 Theo Arends and Stephan Hadinger
This program is free software : you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation , either version 3 of the License , or
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program . If not , see < http : //www.gnu.org/licenses/>.
*/
# ifdef USE_ZIGBEE
# ifdef USE_ZIGBEE_EZSP
/*********************************************************************************************\
* MCU EFR32 firmware upload using xmodem
*
* Step 1 - Upload MCU firmware in ESP8266 flash free space ( current size is about 200 k )
* Step 2 - Upload MCU firmware from ESP8266 flash to MCU EFR32 using XMODEM protocol
* Step 3 - Restart
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2020-07-27 17:31:14 +01:00
//#define ZIGBEE_BOOTLOADER_SOFTWARE_RESET_FIRST
2020-07-26 17:55:31 +01:00
# define XM_SOH 0x01
# define XM_EOT 0x04
# define XM_ACK 0x06
# define XM_CR 0x0d
# define XM_NAK 0x15
# define XM_CAN 0x18
2020-07-27 14:43:24 +01:00
# define XM_SUB 0x1a
2020-07-26 17:55:31 +01:00
2020-07-27 17:31:14 +01:00
enum ZbUploadSteps { ZBU_IDLE , ZBU_INIT ,
ZBU_SOFTWARE_RESET , ZBU_SOFTWARE_SEND , ZBU_HARDWARE_RESET , ZBU_PROMPT ,
2020-09-01 12:05:46 +01:00
ZBU_SYNC , ZBU_UPLOAD , ZBU_EOT , ZBU_COMPLETE , ZBU_DONE , ZBU_ERROR , ZBU_FINISH } ;
2020-07-26 17:55:31 +01:00
2020-07-25 17:25:06 +01:00
const uint8_t PIN_ZIGBEE_BOOTLOADER = 5 ;
struct ZBUPLOAD {
uint32_t ota_size = 0 ;
uint32_t sector_cursor = 0 ;
uint32_t sector_counter = 0 ;
2020-07-26 17:55:31 +01:00
uint32_t byte_counter = 0 ;
char * buffer ;
uint8_t ota_step = ZBU_IDLE ;
2020-07-27 17:31:14 +01:00
uint8_t bootloader = 0 ;
2020-09-01 12:05:46 +01:00
uint8_t state = ZBU_IDLE ;
2020-07-25 17:25:06 +01:00
} ZbUpload ;
2020-07-26 17:55:31 +01:00
/*********************************************************************************************\
* Flash
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2020-07-25 17:25:06 +01:00
uint32_t ZigbeeUploadFlashStart ( void ) {
return ( ESP . getSketchSize ( ) / SPI_FLASH_SEC_SIZE ) + 2 ;
}
2020-07-26 17:55:31 +01:00
uint32_t ZigbeeUploadAvailable ( void ) {
int available = ZbUpload . ota_size - ZbUpload . byte_counter ;
if ( available < 0 ) { available = 0 ; }
return available ;
}
char ZigbeeUploadFlashRead ( void ) {
if ( 0 = = ZbUpload . byte_counter ) {
if ( ! ( ZbUpload . buffer = ( char * ) malloc ( SPI_FLASH_SEC_SIZE ) ) ) {
return ( - 1 ) ; // Not enough (memory) space
}
ZbUpload . sector_counter = ZigbeeUploadFlashStart ( ) ;
}
uint32_t index = ZbUpload . byte_counter % SPI_FLASH_SEC_SIZE ;
if ( 0 = = index ) {
ESP . flashRead ( ZbUpload . sector_counter * SPI_FLASH_SEC_SIZE , ( uint32_t * ) ZbUpload . buffer , SPI_FLASH_SEC_SIZE ) ;
ZbUpload . sector_counter + + ;
}
char data = ZbUpload . buffer [ index ] ;
ZbUpload . byte_counter + + ;
if ( ZbUpload . byte_counter > ZbUpload . ota_size ) {
2020-07-27 14:43:24 +01:00
// 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 ;
2020-07-26 17:55:31 +01:00
// if (ZbUpload.buffer) { free(ZbUpload.buffer); } // Don't in case of retries
}
return data ;
}
/*********************************************************************************************\
* XModem protocol
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2020-10-01 11:58:42 +01:00
// Number of milliseconds to wait before prompt is received
const uint32_t XMODEM_FLUSH_DELAY = 1000 ;
2020-07-26 17:55:31 +01:00
// Number of seconds until giving up hope of receiving sync packets from host.
const uint8_t XMODEM_SYNC_TIMEOUT = 30 ;
// Number of times we try to send a packet to the host until we give up sending..
const uint8_t XMODEM_MAX_RETRY = 30 ;
// Packet size
const uint8_t XMODEM_PACKET_SIZE = 128 ;
struct XMODEM {
uint32_t timeout = 0 ;
2020-07-27 10:01:20 +01:00
uint32_t delay = 0 ;
2020-10-01 11:58:42 +01:00
uint32_t flush_delay = 0xFFFFFFFF ;
2020-07-26 17:55:31 +01:00
uint32_t filepos = 0 ;
int crcBuf = 0 ;
uint8_t packetNo = 1 ;
uint8_t checksumBuf = 0 ;
bool oldChecksum ;
} XModem ;
// Send out a byte of payload data, includes checksumming
void XModemOutputByte ( uint8_t out_char ) {
XModem . checksumBuf + = out_char ;
XModem . crcBuf = XModem . crcBuf ^ ( int ) out_char < < 8 ;
for ( uint32_t i = 0 ; i < 8 ; i + + ) {
if ( XModem . crcBuf & 0x8000 ) {
XModem . crcBuf = XModem . crcBuf < < 1 ^ 0x1021 ;
} else {
XModem . crcBuf = XModem . crcBuf < < 1 ;
}
}
ZigbeeSerial - > write ( out_char ) ;
}
// Wait for the remote to acknowledge or cancel.
// Returns the received char if no timeout occured or a CAN was received. In this cases, it returns -1.
char XModemWaitACK ( void )
{
char in_char ;
do {
uint8_t i = 0 ;
while ( ! ZigbeeSerial - > available ( ) ) {
delayMicroseconds ( 100 ) ;
i + + ;
if ( i > 200 ) { return - 1 ; }
}
in_char = ZigbeeSerial - > read ( ) ;
2020-07-27 14:43:24 +01:00
if ( XM_CAN = = in_char ) { return XM_CAN ; }
2020-07-26 17:55:31 +01:00
} while ( ( in_char ! = XM_NAK ) & & ( in_char ! = XM_ACK ) & & ( in_char ! = ' C ' ) ) ;
return in_char ;
}
bool XModemSendPacket ( uint32_t packet_no ) {
XModem . filepos = ZbUpload . byte_counter ;
// Sending a packet will be retried
uint32_t retries = 0 ;
char in_char ;
do {
// Seek to start of current data block,
// will advance through the file as block will be acked..
ZbUpload . byte_counter = XModem . filepos ;
// Reset checksum stuff
XModem . checksumBuf = 0x00 ;
XModem . crcBuf = 0x00 ;
// Try to send packet, so header first
ZigbeeSerial - > write ( XM_SOH ) ;
ZigbeeSerial - > write ( packet_no ) ;
ZigbeeSerial - > write ( ~ packet_no ) ;
for ( uint32_t i = 0 ; i < XMODEM_PACKET_SIZE ; i + + ) {
in_char = ZigbeeUploadFlashRead ( ) ;
XModemOutputByte ( in_char ) ;
}
// Send out checksum, either CRC-16 CCITT or classical inverse of sum of bytes.
// Depending on how the received introduced himself
if ( XModem . oldChecksum ) {
ZigbeeSerial - > write ( ( char ) XModem . checksumBuf ) ;
} else {
ZigbeeSerial - > write ( ( char ) ( XModem . crcBuf > > 8 ) ) ;
ZigbeeSerial - > write ( ( char ) ( XModem . crcBuf & 0xFF ) ) ;
}
in_char = XModemWaitACK ( ) ;
if ( XM_CAN = = in_char ) { return false ; }
retries + + ;
if ( retries > XMODEM_MAX_RETRY ) { return false ; }
} while ( in_char ! = XM_ACK ) ;
return true ;
}
/*********************************************************************************************\
* Step 2 - Upload MCU firmware from ESP8266 flash to MCU EFR32 using XMODEM protocol
*
* https : //www.silabs.com/documents/public/application-notes/an760-using-legacy-standalone-bootloader.pdf
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2020-07-27 17:31:14 +01:00
void ZigbeeUploadSetSoftwareBootloader ( ) {
// https://github.com/arendst/Tasmota/issues/8583#issuecomment-663967883
SBuffer buf ( 4 ) ;
buf . add16 ( EZSP_launchStandaloneBootloader ) ;
buf . add8 ( 0x01 ) ;
ZigbeeEZSPSendCmd ( buf . getBuffer ( ) , buf . len ( ) ) ; // Send software bootloader init
}
2020-07-26 17:55:31 +01:00
void ZigbeeUploadSetBootloader ( uint8_t state ) {
pinMode ( PIN_ZIGBEE_BOOTLOADER , OUTPUT ) ;
digitalWrite ( PIN_ZIGBEE_BOOTLOADER , state ) ; // Toggle Gecko bootloader
digitalWrite ( Pin ( GPIO_ZIGBEE_RST ) , 0 ) ;
delay ( 100 ) ; // Need to experiment to find a value as low as possible
digitalWrite ( Pin ( GPIO_ZIGBEE_RST ) , 1 ) ; // Reboot MCU EFR32
}
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
// unchanged, the menu title and options text is liable to change, and new options might be added.
2020-10-01 11:58:42 +01:00
uint8_t serial_buffer [ 255 ] ;
uint32_t buf_len = 0 ;
2020-07-26 17:55:31 +01:00
while ( ZigbeeSerial - > available ( ) ) {
yield ( ) ;
char bootloader_byte = ZigbeeSerial - > read ( ) ;
2020-10-01 11:58:42 +01:00
if ( ( ( uint8_t ) bootloader_byte > = 0 ) & & ( buf_len < sizeof ( serial_buffer ) - 2 ) ) {
serial_buffer [ buf_len + + ] = bootloader_byte ;
2020-07-26 17:55:31 +01:00
}
2020-10-01 11:58:42 +01:00
if ( ZbUpload . byte_counter ! = 4 ) {
switch ( ZbUpload . byte_counter ) {
case 0 :
if ( ' B ' = = bootloader_byte ) { ZbUpload . byte_counter + + ; } break ;
case 1 :
if ( ' L ' = = bootloader_byte ) { ZbUpload . byte_counter + + ; } break ;
case 2 :
if ( ' ' = = bootloader_byte ) { ZbUpload . byte_counter + + ; } break ;
case 3 :
if ( ' > ' = = bootloader_byte ) {
ZbUpload . byte_counter + + ;
XModem . flush_delay = millis ( ) + XMODEM_FLUSH_DELAY ;
XModem . delay = XModem . flush_delay + XMODEM_FLUSH_DELAY ;
}
}
}
}
if ( buf_len ) {
char hex_char [ 256 ] ;
ToHex_P ( serial_buffer , buf_len , hex_char , 256 ) ;
AddLog_P2 ( LOG_LEVEL_DEBUG_MORE , PSTR ( " XMD: Rcvd %s " ) , hex_char ) ;
2020-07-26 17:55:31 +01:00
}
2020-10-01 11:58:42 +01:00
return ( ( 4 = = ZbUpload . byte_counter ) & & ( millis ( ) > XModem . flush_delay ) ) ;
2020-07-26 17:55:31 +01:00
}
bool ZigbeeUploadXmodem ( void ) {
switch ( ZbUpload . ota_step ) {
2020-07-27 14:43:24 +01:00
case ZBU_IDLE : { // *** Upload disabled
return false ;
}
2020-07-27 17:31:14 +01:00
# ifdef ZIGBEE_BOOTLOADER_SOFTWARE_RESET_FIRST
case ZBU_INIT : { // *** Init ESF32 bootloader
AddLog_P2 ( LOG_LEVEL_DEBUG , PSTR ( " XMD: Init bootloader " ) ) ;
ZbUpload . ota_step = ZBU_SOFTWARE_RESET ;
return false ; // Keep Zigbee serial active
}
case ZBU_SOFTWARE_RESET : {
SBuffer buf ( 4 ) ;
buf . add16 ( EZSP_launchStandaloneBootloader ) ;
buf . add8 ( 0x01 ) ;
ZigbeeEZSPSendCmd ( buf . getBuffer ( ) , buf . len ( ) ) ; // Send software bootloader init
XModem . timeout = millis ( ) + ( 10 * 1000 ) ; // Allow 10 seconds to send Zigbee command
ZbUpload . ota_step = ZBU_SOFTWARE_SEND ;
return false ; // Keep Zigbee serial active
}
case ZBU_SOFTWARE_SEND : {
if ( millis ( ) > XModem . timeout ) {
AddLog_P2 ( LOG_LEVEL_DEBUG , PSTR ( " XMD: Bootloader software reset send timeout " ) ) ;
ZbUpload . ota_step = ZBU_HARDWARE_RESET ;
return true ;
}
if ( EZSP_Serial . to_send = = EZSP_Serial . to_end ) {
ZbUpload . bootloader = ZBU_SOFTWARE_RESET ;
XModem . timeout = millis ( ) + ( 10 * 1000 ) ; // Allow 10 seconds to receive EBL prompt
2020-10-01 11:58:42 +01:00
XModem . delay = millis ( ) + ( 2 * XMODEM_FLUSH_DELAY ) ;
2020-07-27 17:31:14 +01:00
ZbUpload . byte_counter = 0 ;
ZbUpload . ota_step = ZBU_PROMPT ;
}
break ;
}
case ZBU_HARDWARE_RESET : {
ZbUpload . bootloader = ZBU_HARDWARE_RESET ;
ZigbeeUploadSetBootloader ( 0 ) ; // Reboot MCU EFR32 which returns below text
XModem . timeout = millis ( ) + ( 30 * 1000 ) ; // Allow 30 seconds to receive EBL prompt
2020-10-01 11:58:42 +01:00
XModem . delay = millis ( ) + ( 2 * XMODEM_FLUSH_DELAY ) ;
2020-07-27 17:31:14 +01:00
ZbUpload . byte_counter = 0 ;
ZbUpload . ota_step = ZBU_PROMPT ;
break ;
}
case ZBU_PROMPT : { // *** Wait for prompt and select option upload ebl
if ( millis ( ) > XModem . timeout ) {
if ( ZBU_SOFTWARE_RESET = = ZbUpload . bootloader ) {
AddLog_P2 ( LOG_LEVEL_DEBUG , PSTR ( " XMD: Bootloader software reset timeout " ) ) ;
ZbUpload . ota_step = ZBU_HARDWARE_RESET ;
} else {
AddLog_P2 ( LOG_LEVEL_DEBUG , PSTR ( " XMD: Bootloader hardware reset timeout " ) ) ;
2020-09-01 12:05:46 +01:00
ZbUpload . ota_step = ZBU_ERROR ;
2020-07-27 17:31:14 +01:00
}
return true ;
}
# else // No ZIGBEE_BOOTLOADER_SOFTWARE_RESET_FIRST
2020-07-27 14:43:24 +01:00
case ZBU_INIT : { // *** Init ESF32 bootloader
2020-07-27 10:01:20 +01:00
AddLog_P2 ( LOG_LEVEL_DEBUG , PSTR ( " XMD: Init bootloader " ) ) ;
2020-07-27 14:43:24 +01:00
ZigbeeUploadSetBootloader ( 0 ) ; // Reboot MCU EFR32 which returns below text
2020-07-27 10:01:20 +01:00
XModem . timeout = millis ( ) + ( 30 * 1000 ) ; // Allow 30 seconds to receive EBL prompt
2020-10-01 11:58:42 +01:00
XModem . delay = millis ( ) + ( 2 * XMODEM_FLUSH_DELAY ) ;
2020-07-27 14:43:24 +01:00
ZbUpload . byte_counter = 0 ;
2020-07-26 17:55:31 +01:00
ZbUpload . ota_step = ZBU_PROMPT ;
break ;
}
2020-07-27 14:43:24 +01:00
case ZBU_PROMPT : { // *** Wait for prompt and select option upload ebl
2020-07-27 10:01:20 +01:00
if ( millis ( ) > XModem . timeout ) {
AddLog_P2 ( LOG_LEVEL_DEBUG , PSTR ( " XMD: Bootloader timeout " ) ) ;
2020-09-01 12:05:46 +01:00
ZbUpload . ota_step = ZBU_ERROR ;
2020-07-27 10:01:20 +01:00
return true ;
}
2020-07-27 17:31:14 +01:00
# endif // ZIGBEE_BOOTLOADER_SOFTWARE_RESET_FIRST
2020-10-01 11:58:42 +01:00
else if ( ! ZigbeeSerial - > available ( ) & & ( millis ( ) < XModem . flush_delay ) ) {
2020-07-27 14:43:24 +01:00
// The target device’ s bootloader sends output over its serial port after it receives a
// carriage return from the source device
2020-07-27 10:01:20 +01:00
if ( millis ( ) > XModem . delay ) {
ZigbeeSerial - > write ( XM_CR ) ;
2020-10-01 11:58:42 +01:00
XModem . delay = millis ( ) + ( 2 * XMODEM_FLUSH_DELAY ) ;
2020-07-27 10:01:20 +01:00
}
2020-07-26 17:55:31 +01:00
} else {
// After the bootloader receives a carriage return from the target device, it displays a menu
// Gecko Bootloader v1.A.3
// 1. upload gbl
// 2. run
// 3. ebl info
// BL >
if ( ZigbeeUploadBootloaderPrompt ( ) ) {
2020-07-27 10:01:20 +01:00
AddLog_P2 ( LOG_LEVEL_DEBUG , PSTR ( " XMD: Init sync " ) ) ;
2020-07-26 17:55:31 +01:00
ZigbeeSerial - > flush ( ) ;
2020-07-27 14:43:24 +01:00
ZigbeeSerial - > write ( ' 1 ' ) ; // upload ebl
2020-07-27 10:01:20 +01:00
if ( ssleep > 0 ) {
2020-07-27 14:43:24 +01:00
ssleep = 1 ; // Speed up loop used for xmodem upload
2020-07-27 10:01:20 +01:00
}
2020-07-26 17:55:31 +01:00
XModem . timeout = millis ( ) + ( XMODEM_SYNC_TIMEOUT * 1000 ) ;
ZbUpload . ota_step = ZBU_SYNC ;
}
}
break ;
}
2020-07-27 14:43:24 +01:00
case ZBU_SYNC : { // *** Handle file upload using XModem - sync
2020-07-26 17:55:31 +01:00
if ( millis ( ) > XModem . timeout ) {
2020-07-27 14:43:24 +01:00
AddLog_P2 ( LOG_LEVEL_DEBUG , PSTR ( " XMD: SYNC timeout " ) ) ;
2020-09-01 12:05:46 +01:00
ZbUpload . ota_step = ZBU_ERROR ;
2020-07-26 17:55:31 +01:00
return true ;
}
2020-07-27 14:43:24 +01:00
// Wait for either C or NACK as a sync packet. Determines protocol details, checksum algorithm.
2020-07-26 17:55:31 +01:00
if ( ZigbeeSerial - > available ( ) ) {
char xmodem_sync = ZigbeeSerial - > read ( ) ;
2020-07-27 14:43:24 +01:00
if ( ( ' C ' = = xmodem_sync ) | | ( XM_NAK = = xmodem_sync ) ) {
2020-07-26 17:55:31 +01:00
// Determine which checksum algorithm to use
XModem . oldChecksum = ( xmodem_sync = = XM_NAK ) ;
XModem . packetNo = 1 ;
ZbUpload . byte_counter = 0 ;
ZbUpload . ota_step = ZBU_UPLOAD ;
2020-07-27 10:01:20 +01:00
AddLog_P2 ( LOG_LEVEL_DEBUG , PSTR ( " XMD: Init packet send " ) ) ;
2020-07-26 17:55:31 +01:00
}
}
break ;
}
2020-07-27 14:43:24 +01:00
case ZBU_UPLOAD : { // *** Handle file upload using XModem - upload
2020-07-26 17:55:31 +01:00
if ( ZigbeeUploadAvailable ( ) ) {
if ( ! XModemSendPacket ( XModem . packetNo ) ) {
AddLog_P2 ( LOG_LEVEL_DEBUG , PSTR ( " XMD: Packet send failed " ) ) ;
2020-09-01 12:05:46 +01:00
ZbUpload . ota_step = ZBU_ERROR ;
2020-07-26 17:55:31 +01:00
return true ;
}
XModem . packetNo + + ;
} else {
2020-07-27 14:43:24 +01:00
// 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_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 " ) ) ;
2020-09-01 12:05:46 +01:00
ZbUpload . ota_step = ZBU_ERROR ;
2020-07-27 14:43:24 +01:00
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 ;
2020-07-27 17:31:14 +01:00
ZbUpload . ota_step = ZBU_COMPLETE ;
2020-07-26 17:55:31 +01:00
}
2020-07-27 14:43:24 +01:00
}
break ;
}
case ZBU_COMPLETE : { // *** Wait for Serial upload complete EBL prompt
if ( millis ( ) > XModem . timeout ) {
AddLog_P2 ( LOG_LEVEL_DEBUG , PSTR ( " XMD: Bootloader timeout " ) ) ;
2020-09-01 12:05:46 +01:00
ZbUpload . ota_step = ZBU_ERROR ;
2020-07-27 14:43:24 +01:00
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
2020-07-27 17:31:14 +01:00
// Gecko Bootloader v1.A.3
// 1. upload gbl
// 2. run
// 3. ebl info
2020-07-27 14:43:24 +01:00
// BL >
if ( ZigbeeUploadBootloaderPrompt ( ) ) {
2020-09-01 12:05:46 +01:00
ZbUpload . state = ZBU_COMPLETE ;
2020-07-27 14:43:24 +01:00
ZbUpload . ota_step = ZBU_DONE ;
}
2020-07-26 17:55:31 +01:00
}
break ;
}
2020-09-01 12:05:46 +01:00
case ZBU_ERROR :
ZbUpload . state = ZBU_ERROR ;
2020-07-27 14:43:24 +01:00
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
}
2020-09-01 12:05:46 +01:00
// restart_flag = 2; // Restart to disable bootloader and use new firmware
2020-07-27 14:43:24 +01:00
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 ;
2020-07-26 17:55:31 +01:00
}
}
return true ;
}
/*********************************************************************************************\
* Step 1 - Upload MCU firmware in ESP8266 flash free space ( current size is about 200 k )
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
bool ZigbeeUploadOtaReady ( void ) {
return ( ZBU_INIT = = ZbUpload . ota_step ) ;
}
2020-09-01 12:05:46 +01:00
bool ZigbeeUploadFinish ( void ) {
return ( ZBU_FINISH = = ZbUpload . ota_step ) ;
}
2020-07-25 17:25:06 +01:00
uint8_t ZigbeeUploadInit ( void ) {
if ( ! PinUsed ( GPIO_ZIGBEE_RST ) & & ( ZigbeeSerial = = nullptr ) ) { return 1 ; } // Wrong pin configuration - No file selected
ZbUpload . sector_counter = ZigbeeUploadFlashStart ( ) ;
ZbUpload . sector_cursor = 0 ;
ZbUpload . ota_size = 0 ;
2020-07-26 17:55:31 +01:00
ZbUpload . ota_step = ZBU_IDLE ;
2020-09-01 12:05:46 +01:00
ZbUpload . state = ZBU_IDLE ;
2020-07-25 17:25:06 +01:00
return 0 ;
}
bool ZigbeeUploadWriteBuffer ( uint8_t * buf , size_t size ) {
// Read complete file into ESP8266 flash
// Current files are about 200k
if ( 0 = = ZbUpload . sector_cursor ) { // Starting a new sector write so we need to erase it first
ESP . flashEraseSector ( ZbUpload . sector_counter ) ;
}
ZbUpload . sector_cursor + + ;
ESP . flashWrite ( ( ZbUpload . sector_counter * SPI_FLASH_SEC_SIZE ) + ( ( ZbUpload . sector_cursor - 1 ) * 2048 ) , ( uint32_t * ) buf , size ) ;
ZbUpload . ota_size + = 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 + + ;
2020-07-27 14:43:24 +01:00
if ( ZbUpload . sector_counter > ( SPIFFS_END - 2 ) ) {
2020-07-25 17:25:06 +01:00
return false ; // File too large - Not enough free space
}
}
return true ;
}
void ZigbeeUploadDone ( void ) {
2020-07-26 17:55:31 +01:00
ZbUpload . ota_step = ZBU_INIT ;
2020-09-01 12:05:46 +01:00
ZbUpload . state = ZBU_UPLOAD ;
2020-07-25 17:25:06 +01:00
}
2020-09-01 12:05:46 +01:00
# ifdef USE_WEBSERVER
# define WEB_HANDLE_ZIGBEE_XFER "zx"
const char HTTP_SCRIPT_XFER_STATE [ ] PROGMEM =
" function z9(){ "
" if(x!=null){x.abort();} " // Abort if no response within 2 seconds (happens on restart 1)
" x=new XMLHttpRequest(); "
" x.onreadystatechange=function(){ "
" if(x.readyState==4&&x.status==200){ "
" var s=x.responseText; "
" if(s!=7){ " // ZBU_UPLOAD
2020-09-02 10:45:17 +01:00
" location.href='/u3'; " // Load page HandleUploadDone()
2020-09-01 12:05:46 +01:00
" } "
" } "
" }; "
2020-09-02 10:45:17 +01:00
" x.open('GET',' " WEB_HANDLE_ZIGBEE_XFER " ?z=1',true); " // ?z related to Webserver->hasArg("z")
2020-09-01 12:05:46 +01:00
" x.send(); "
2020-09-02 10:45:17 +01:00
" lt=setTimeout(z9,950); " // Poll every 0.95 second
2020-09-01 12:05:46 +01:00
" } "
2020-09-02 10:45:17 +01:00
" wl(z9); " ; // Execute z9() on page load
2020-09-01 12:05:46 +01:00
void HandleZigbeeXfer ( void ) {
if ( ! HttpCheckPriviledgedAccess ( ) ) { return ; }
2020-09-02 10:45:17 +01:00
if ( Webserver - > hasArg ( " z " ) ) { // Status refresh requested
2020-09-01 12:05:46 +01:00
WSContentBegin ( 200 , CT_PLAIN ) ;
WSContentSend_P ( PSTR ( " %d " ) , ZbUpload . state ) ;
WSContentEnd ( ) ;
if ( ZBU_ERROR = = ZbUpload . state ) {
2020-09-02 10:45:17 +01:00
Web . upload_error = 7 ; // Upload aborted (xmodem transfer failed)
2020-09-01 12:05:46 +01:00
}
return ;
}
AddLog_P ( LOG_LEVEL_DEBUG , S_LOG_HTTP , PSTR ( D_UPLOAD_TRANSFER ) ) ;
WSContentStart_P ( S_INFORMATION ) ;
WSContentSend_P ( HTTP_SCRIPT_XFER_STATE ) ;
WSContentSendStyle ( ) ;
2020-09-02 10:45:17 +01:00
WSContentSend_P ( PSTR ( " <div style='text-align:center;'><b> " D_UPLOAD_TRANSFER " ...</b></div> " ) ) ;
2020-09-01 12:05:46 +01:00
WSContentSpaceButton ( BUTTON_MAIN ) ;
WSContentStop ( ) ;
}
# endif // USE_WEBSERVER
# endif // USE_ZIGBEE_EZSP
2020-07-25 17:25:06 +01:00
2020-09-01 12:05:46 +01:00
# endif // USE_ZIGBEE