diff --git a/tasmota/xdrv_01_webserver.ino b/tasmota/xdrv_01_webserver.ino
index b94a2a902..384b38c6f 100644
--- a/tasmota/xdrv_01_webserver.ino
+++ b/tasmota/xdrv_01_webserver.ino
@@ -44,7 +44,7 @@ const uint16_t HTTP_OTA_RESTART_RECONNECT_TIME = 28000; // milliseconds - Allow
uint8_t *efm8bb1_update = nullptr;
#endif // USE_RF_FLASH
-enum UploadTypes { UPL_TASMOTA, UPL_SETTINGS, UPL_EFM8BB1, UPL_TASMOTACLIENT };
+enum UploadTypes { UPL_TASMOTA, UPL_SETTINGS, UPL_EFM8BB1, UPL_TASMOTACLIENT, UPL_EFR32 };
static const char * HEADER_KEYS[] = { "User-Agent", };
@@ -2627,20 +2627,26 @@ void HandleUploadDone(void)
WSContentSend_P(PSTR("%06x'>" D_SUCCESSFUL "
"), WebColor(COL_TEXT_SUCCESS));
WSContentSend_P(HTTP_MSG_RSTRT);
ShowWebSource(SRC_WEBGUI);
+ restart_flag = 2; // Always restart to re-enable disabled features during update
+#if defined(USE_ZIGBEE) && defined(USE_ZIGBEE_EZSP)
+ if (ZigbeeUploadOtaReady()) {
+ restart_flag = 0; // Hold restart as firmware still needs to be written to MCU EFR32
+ }
+#endif // USE_ZIGBEE and USE_ZIGBEE_EZSP
#ifdef USE_TASMOTA_CLIENT
if (TasmotaClient_GetFlagFlashing()) {
- restart_flag = 0;
- } else { // It was a normal firmware file, or we are ready to restart device
- restart_flag = 2;
+ restart_flag = 0; // Hold restart as code still needs to be trasnferred to Atmega
}
-#else
- restart_flag = 2; // Always restart to re-enable disabled features during update
-#endif
+#endif // USE_TASMOTA_CLIENT
}
SettingsBufferFree();
WSContentSend_P(PSTR("
"));
WSContentSpaceButton(BUTTON_MAIN);
WSContentStop();
+
+#if defined(USE_ZIGBEE) && defined(USE_ZIGBEE_EZSP)
+ ZigbeeUploadXmodem();
+#endif // USE_ZIGBEE and USE_ZIGBEE_EZSP
#ifdef USE_TASMOTA_CLIENT
if (TasmotaClient_GetFlagFlashing()) {
TasmotaClient_Flash();
@@ -2707,6 +2713,15 @@ void HandleUploadLoop(void)
Web.config_block_count = 0;
}
else {
+#if defined(USE_ZIGBEE) && defined(USE_ZIGBEE_EZSP)
+ if ((SONOFF_ZB_BRIDGE == my_module_type) && (upload.buf[0] == 0xEB)) { // Check if this is a Zigbee bridge FW file
+ Update.end(); // End esp8266 update session
+ Web.upload_file_type = UPL_EFR32;
+
+ Web.upload_error = ZigbeeUploadInit(); // 15
+ if (Web.upload_error != 0) { return; }
+ } else
+#endif // USE_ZIGBEE and USE_ZIGBEE_EZSP
#ifdef USE_RF_FLASH
if ((SONOFF_BRIDGE == my_module_type) && (upload.buf[0] == ':')) { // Check if this is a RF bridge FW file
Update.end(); // End esp8266 update session
@@ -2750,6 +2765,15 @@ void HandleUploadLoop(void)
Web.config_block_count++;
}
}
+#if defined(USE_ZIGBEE) && defined(USE_ZIGBEE_EZSP)
+ else if (UPL_EFR32 == Web.upload_file_type) {
+ // Write buffers to MCU EFR32
+ if (!ZigbeeUploadWriteBuffer(upload.buf, upload.currentSize)) {
+ Web.upload_error = 9; // File too large
+ return;
+ }
+ }
+#endif // USE_ZIGBEE and USE_ZIGBEE_EZSP
#ifdef USE_RF_FLASH
else if (UPL_EFM8BB1 == Web.upload_file_type) {
if (efm8bb1_update != nullptr) { // We have carry over data since last write, i. e. a start but not an end
@@ -2844,6 +2868,13 @@ void HandleUploadLoop(void)
return;
}
}
+#if defined(USE_ZIGBEE) && defined(USE_ZIGBEE_EZSP)
+ else if (UPL_EFR32 == Web.upload_file_type) {
+ // Zigbee FW upload to ESP8266 flash is done
+ ZigbeeUploadDone(); // Signal upload done and ready for delayed upload to MCU EFR32
+ Web.upload_file_type = UPL_TASMOTA;
+ }
+#endif
#ifdef USE_RF_FLASH
else if (UPL_EFM8BB1 == Web.upload_file_type) {
// RF FW flash done
diff --git a/tasmota/xdrv_23_zigbee_9a_upload.ino b/tasmota/xdrv_23_zigbee_9a_upload.ino
new file mode 100644
index 000000000..1e1ff935f
--- /dev/null
+++ b/tasmota/xdrv_23_zigbee_9a_upload.ino
@@ -0,0 +1,121 @@
+/*
+ xdrv_23_zigbee_9_serial.ino - zigbee: serial communication with MCU
+
+ 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 .
+*/
+
+#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 200k)
+ * Step 2 - Upload MCU firmware from ESP8266 flash to MCU EFR32 using XMODEM protocol
+ * Step 3 - Restart
+\*********************************************************************************************/
+
+const uint8_t PIN_ZIGBEE_BOOTLOADER = 5;
+
+struct ZBUPLOAD {
+ uint32_t ota_size = 0;
+ uint32_t sector_cursor = 0;
+ uint32_t sector_counter = 0;
+ bool ota_ready = false;
+} ZbUpload;
+
+bool ZigbeeUploadOtaReady(void) {
+ return ZbUpload.ota_ready;
+}
+
+uint32_t ZigbeeUploadFlashStart(void) {
+ return (ESP.getSketchSize() / SPI_FLASH_SEC_SIZE) + 2;
+}
+
+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;
+ ZbUpload.ota_ready = false;
+ 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++;
+ if (ZbUpload.sector_counter > (SPIFFS_END + 12)) {
+ return false; // File too large - Not enough free space
+ }
+ }
+ return true;
+}
+
+void ZigbeeUploadDone(void) {
+ ZbUpload.ota_ready = true;
+}
+
+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
+}
+
+void ZigbeeUploadXmodem(void) {
+ if (!ZbUpload.ota_ready) { return; }
+
+ // Copy uploaded OTA file from ESP8266 flash to MCU EFR32 using xmodem
+ uint32_t sector_counter = ZigbeeUploadFlashStart() * SPI_FLASH_SEC_SIZE;
+
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZIG: Upload start 0x%08X, size 0x%08X"), sector_counter, ZbUpload.ota_size);
+
+ // TODO - implement XMODEM upload
+
+// ZigbeeUploadSetBootloader(1); // Reboot MCU EFR32 which returns below text
+ // Gecko Bootloader v1.A.3
+ // 1. upload gbl
+ // 2. run
+ // 3. ebl info
+ // BL >
+ // Use ZigbeeInputLoop to flush received data
+
+ // Send option 1 to prepare for xmodem upload
+// ZigbeeSerial->write('1');
+// ZigbeeSerial->write(0x0d);
+// ZigbeeSerial->write(0x0a);
+
+ // Start xmodem upload
+
+ ZbUpload.ota_ready = false;
+// ZigbeeUploadSetBootloader(0); // Disable bootloader and reset MCU
+ restart_flag = 2; // Restart to disable bootloader and use new firmware
+}
+
+#endif // USE_ZIGBEE_EZSP
+
+#endif // USE_ZIGBEE