Initial support for SHD web gui firmware upload

Initial support for Shelly Dimmer web gui firmware upload when #define SHELLY_FW_UPGRADE2 is enabled. #define SHELLY_FW_UPGRADE must be disabled at that time
This commit is contained in:
Theo Arends 2020-11-16 17:39:16 +01:00
parent ccbf634966
commit acf0e79acb
2 changed files with 219 additions and 103 deletions

View File

@ -44,7 +44,7 @@ const uint16_t HTTP_OTA_RESTART_RECONNECT_TIME = 20000; // milliseconds - Allow
uint8_t *efm8bb1_update = nullptr; uint8_t *efm8bb1_update = nullptr;
#endif // USE_RF_FLASH #endif // USE_RF_FLASH
enum UploadTypes { UPL_TASMOTA, UPL_SETTINGS, UPL_EFM8BB1, UPL_TASMOTACLIENT, UPL_EFR32 }; enum UploadTypes { UPL_TASMOTA, UPL_SETTINGS, UPL_EFM8BB1, UPL_TASMOTACLIENT, UPL_EFR32, UPL_SHD };
#ifdef USE_UNISHOX_COMPRESSION #ifdef USE_UNISHOX_COMPRESSION
#ifdef USE_JAVASCRIPT_ES6 #ifdef USE_JAVASCRIPT_ES6
@ -2569,6 +2569,42 @@ void HandleInformation(void)
/*-------------------------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------------------------*/
#if defined(SHELLY_FW_UPGRADE2)
struct {
size_t spi_hex_size;
size_t spi_sector_counter;
size_t spi_sector_cursor;
bool ready;
} BUpload;
uint32_t BUploadStartSector(void) {
return (ESP.getSketchSize() / SPI_FLASH_SEC_SIZE) + 2; // Stay on the safe side
}
uint8_t BUploadInit(void) {
BUpload.spi_hex_size = 0;
BUpload.spi_sector_counter = BUploadStartSector();
BUpload.spi_sector_cursor = 0;
BUpload.ready = false;
return 0;
}
void BUploadWriteBuffer(uint8_t *buf, size_t size) {
if (0 == BUpload.spi_sector_cursor) { // Starting a new sector write so we need to erase it first
ESP.flashEraseSector(BUpload.spi_sector_counter);
}
BUpload.spi_sector_cursor++;
ESP.flashWrite((BUpload.spi_sector_counter * SPI_FLASH_SEC_SIZE) + ((BUpload.spi_sector_cursor -1) * 2048), (uint32_t*)buf, size);
BUpload.spi_hex_size += size;
if (2 == BUpload.spi_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
BUpload.spi_sector_cursor = 0;
BUpload.spi_sector_counter++;
}
}
#endif // USE_TASMOTA_CLIENT or SHELLY_FW_UPGRADE2
void HandleUpgradeFirmware(void) void HandleUpgradeFirmware(void)
{ {
if (!HttpCheckPriviledgedAccess()) { return; } if (!HttpCheckPriviledgedAccess()) { return; }
@ -2668,12 +2704,21 @@ void HandleUploadDone(void)
} else { } else {
WSContentSend_P(PSTR("%06x'>" D_SUCCESSFUL "</font></b><br>"), WebColor(COL_TEXT_SUCCESS)); WSContentSend_P(PSTR("%06x'>" D_SUCCESSFUL "</font></b><br>"), WebColor(COL_TEXT_SUCCESS));
TasmotaGlobal.restart_flag = 2; // Always restart to re-enable disabled features during update TasmotaGlobal.restart_flag = 2; // Always restart to re-enable disabled features during update
#ifdef SHELLY_FW_UPGRADE2
if (BUpload.ready) {
WSContentSend_P(PSTR("<br><div style='text-align:center;'><b>" D_TRANSFER_STARTED " ...</b></div>"));
TasmotaGlobal.restart_flag = 0; // Hold restart as code still needs to be transferred to STM
}
#endif // SHELLY_FW_UPGRADE2
#ifdef USE_TASMOTA_CLIENT #ifdef USE_TASMOTA_CLIENT
if (TasmotaClient_GetFlagFlashing()) { if (TasmotaClient_GetFlagFlashing()) {
WSContentSend_P(PSTR("<br><div style='text-align:center;'><b>" D_TRANSFER_STARTED " ...</b></div>")); WSContentSend_P(PSTR("<br><div style='text-align:center;'><b>" D_TRANSFER_STARTED " ...</b></div>"));
TasmotaGlobal.restart_flag = 0; // Hold restart as code still needs to be transferred to Atmega TasmotaGlobal.restart_flag = 0; // Hold restart as code still needs to be transferred to Atmega
} }
#endif // USE_TASMOTA_CLIENT #endif // USE_TASMOTA_CLIENT
if (TasmotaGlobal.restart_flag) { if (TasmotaGlobal.restart_flag) {
WSContentSend_P(HTTP_MSG_RSTRT); WSContentSend_P(HTTP_MSG_RSTRT);
ShowWebSource(SRC_WEBGUI); ShowWebSource(SRC_WEBGUI);
@ -2684,11 +2729,17 @@ void HandleUploadDone(void)
WSContentSpaceButton(BUTTON_MAIN); WSContentSpaceButton(BUTTON_MAIN);
WSContentStop(); WSContentStop();
#ifdef SHELLY_FW_UPGRADE2
if (BUpload.ready) {
ShdFlash(BUploadStartSector() * SPI_FLASH_SEC_SIZE, BUpload.spi_hex_size);
}
#endif // SHELLY_FW_UPGRADE2
#ifdef USE_TASMOTA_CLIENT #ifdef USE_TASMOTA_CLIENT
if (TasmotaClient_GetFlagFlashing()) { if (TasmotaClient_GetFlagFlashing()) {
TasmotaClient_Flash(); TasmotaClient_Flash();
} }
#endif #endif // USE_TASMOTA_CLIENT
} }
void HandleUploadLoop(void) void HandleUploadLoop(void)
@ -2776,6 +2827,15 @@ void HandleUploadLoop(void)
if (Web.upload_error != 0) { return; } if (Web.upload_error != 0) { return; }
} else } else
#endif // USE_RF_FLASH #endif // USE_RF_FLASH
#ifdef SHELLY_FW_UPGRADE2
if (ShdPresent() && (0x00 == upload.buf[0]) && (0x10 == upload.buf[1])) {
Update.end(); // End esp8266 update session
Web.upload_file_type = UPL_SHD;
Web.upload_error = BUploadInit();
if (Web.upload_error != 0) { return; }
} else
#endif // SHELLY_FW_UPGRADE2
#ifdef USE_TASMOTA_CLIENT #ifdef USE_TASMOTA_CLIENT
if (TasmotaClient_Available() && (upload.buf[0] == ':')) { // Check if this is a ARDUINO CLIENT hex file if (TasmotaClient_Available() && (upload.buf[0] == ':')) { // Check if this is a ARDUINO CLIENT hex file
Update.end(); // End esp8266 update session Update.end(); // End esp8266 update session
@ -2855,6 +2915,12 @@ void HandleUploadLoop(void)
} }
} }
#endif // USE_RF_FLASH #endif // USE_RF_FLASH
#ifdef SHELLY_FW_UPGRADE2
else if (UPL_SHD == Web.upload_file_type) {
// Write a block
BUploadWriteBuffer(upload.buf, upload.currentSize);
}
#endif // SHELLY_FW_UPGRADE2
#ifdef USE_TASMOTA_CLIENT #ifdef USE_TASMOTA_CLIENT
else if (UPL_TASMOTACLIENT == Web.upload_file_type) { else if (UPL_TASMOTACLIENT == Web.upload_file_type) {
TasmotaClient_WriteBuffer(upload.buf, upload.currentSize); TasmotaClient_WriteBuffer(upload.buf, upload.currentSize);
@ -2931,6 +2997,13 @@ void HandleUploadLoop(void)
Web.upload_file_type = UPL_TASMOTA; Web.upload_file_type = UPL_TASMOTA;
} }
#endif // USE_RF_FLASH #endif // USE_RF_FLASH
#ifdef SHELLY_FW_UPGRADE2
else if (UPL_SHD == Web.upload_file_type) {
// Done writing the hex to SPI flash
BUpload.ready = true; // So we know on upload success page if it needs to flash hex or do a normal restart
Web.upload_file_type = UPL_TASMOTA;
}
#endif // SHELLY_FW_UPGRADE2
#ifdef USE_TASMOTA_CLIENT #ifdef USE_TASMOTA_CLIENT
else if (UPL_TASMOTACLIENT == Web.upload_file_type) { else if (UPL_TASMOTACLIENT == Web.upload_file_type) {
// Done writing the hex to SPI flash // Done writing the hex to SPI flash

View File

@ -28,6 +28,7 @@
* https://shelly.cloud/wifi-smart-home-automation-shelly-dimmer/ * https://shelly.cloud/wifi-smart-home-automation-shelly-dimmer/
* https://shelly.cloud/products/shelly-dimmer-2-smart-home-light-controller/ * https://shelly.cloud/products/shelly-dimmer-2-smart-home-light-controller/
\*********************************************************************************************/ \*********************************************************************************************/
// #define SHELLY_DIMMER_DEBUG // #define SHELLY_DIMMER_DEBUG
// #define SHELLY_HW_DIMMING // #define SHELLY_HW_DIMMING
@ -37,7 +38,7 @@
#define SHD_DRIVER_MAJOR_VERSION 1 #define SHD_DRIVER_MAJOR_VERSION 1
#define SHD_DRIVER_MINOR_VERSION 4 #define SHD_DRIVER_MINOR_VERSION 4
#define SHD_LOGNAME "SHD" #define SHD_LOGNAME "SHD: "
#ifdef SHELLY_CMDS #ifdef SHELLY_CMDS
#define D_PRFX_SHD "Shd" #define D_PRFX_SHD "Shd"
@ -72,6 +73,10 @@
#include <fw/shelly/dimmer/stm_v51.4.h> #include <fw/shelly/dimmer/stm_v51.4.h>
#endif // SHELLY_FW_UPGRADE #endif // SHELLY_FW_UPGRADE
#ifdef SHELLY_FW_UPGRADE2
#include <stm32flash.h>
#endif // SHELLY_FW_UPGRADE2
#include <TasmotaSerial.h> #include <TasmotaSerial.h>
TasmotaSerial *ShdSerial = nullptr; TasmotaSerial *ShdSerial = nullptr;
@ -105,6 +110,117 @@ struct SHD
bool present = false; bool present = false;
} Shd; } Shd;
/*********************************************************************************************\
* SHD firmware update Functions - James
\*********************************************************************************************/
#if defined(SHELLY_FW_UPGRADE) || defined(SHELLY_FW_UPGRADE2)
void ShdResetToDFUMode()
{
AddLog_P(LOG_LEVEL_DEBUG, PSTR(SHD_LOGNAME "Request co-processor reset in dfu mode"));
pinMode(Pin(GPIO_SHELLY_DIMMER_RST_INV), OUTPUT);
digitalWrite(Pin(GPIO_SHELLY_DIMMER_RST_INV), LOW);
pinMode(Pin(GPIO_SHELLY_DIMMER_BOOT0), OUTPUT);
digitalWrite(Pin(GPIO_SHELLY_DIMMER_BOOT0), HIGH);
delay(50);
// clear in the receive buffer
while (Serial.available())
Serial.read();
digitalWrite(Pin(GPIO_SHELLY_DIMMER_RST_INV), HIGH); // pull out of reset
delay(50); // wait 50ms fot the co-processor to come online
}
bool ShdUpdateFirmware(const uint8_t data[], unsigned int size)
{
AddLog_P(LOG_LEVEL_DEBUG, PSTR(SHD_LOGNAME "Update firmware"));
bool ret = true;
stm32_t *stm = stm32_init(&Serial, STREAM_SERIAL, 1);
if (stm)
{
off_t offset = 0;
uint8_t buffer[256];
unsigned int len;
const uint8_t *p_st = data;
uint32_t addr, start, end;
stm32_err_t s_err;
AddLog_P(LOG_LEVEL_DEBUG, PSTR(SHD_LOGNAME "STM32 erase memory"));
stm32_erase_memory(stm, 0, STM32_MASS_ERASE);
addr = stm->dev->fl_start;
end = addr + size;
while(addr < end && offset < size)
{
uint32_t left = end - addr;
len = sizeof(buffer) > left ? left : sizeof(buffer);
len = len > size - offset ? size - offset : len;
if (len == 0)
{
break;
}
// memcpy(buffer, p_st, len);
// p_st += len;
memcpy(buffer, p_st, sizeof(buffer)); // We need 4-byte bounadry flash access
p_st += sizeof(buffer);
s_err = stm32_write_memory(stm, addr, buffer, len);
if (s_err != STM32_ERR_OK)
{
ret = false;
break;
}
addr += len;
offset += len;
}
stm32_close(stm);
}
return ret;
}
#endif // SHELLY_FW_UPGRADE
/*********************************************************************************************\
* SHD firmware update Functions - Theo
\*********************************************************************************************/
#ifdef SHELLY_FW_UPGRADE2
bool ShdPresent(void) {
return Shd.present;
}
void ShdFlash(uint32_t data, size_t size) {
AddLog_P(LOG_LEVEL_INFO, PSTR(SHD_LOGNAME "Updating firmware v%u.%u with %u bytes"),
Shd.dimmer.version_major, Shd.dimmer.version_minor, size);
Serial.end();
Serial.begin(115200, SERIAL_8E1);
ShdResetToDFUMode();
// uint32_t* values = (uint32_t*)(0x40200000 + data);
// AddLog_P(LOG_LEVEL_DEBUG, PSTR(SHD_LOGNAME "Flash 0x%08X"), values[0]);
ShdUpdateFirmware((uint8_t*)(0x40200000 + data), size); // Allow flash access without ESP.flashRead
Serial.end();
ShdResetToAppMode();
Serial.begin(115200, SERIAL_8N1);
ShdSendVersion();
TasmotaGlobal.restart_flag = 2; // Restart to re-init stopped services
}
#endif // SHELLY_FW_UPGRADE2
/*********************************************************************************************\ /*********************************************************************************************\
* Helper Functions * Helper Functions
\*********************************************************************************************/ \*********************************************************************************************/
@ -142,7 +258,7 @@ int check_byte()
if (chksm != chksm_calc) if (chksm != chksm_calc)
{ {
#ifdef SHELLY_DIMMER_DEBUG #ifdef SHELLY_DIMMER_DEBUG
AddLog_P(LOG_LEVEL_DEBUG, PSTR(SHD_LOGNAME" checksum: %x calculated: %x"), chksm, chksm_calc); AddLog_P(LOG_LEVEL_DEBUG, PSTR(SHD_LOGNAME "Checksum: %x calculated: %x"), chksm, chksm_calc);
#endif // SHELLY_DIMMER_DEBUG #endif // SHELLY_DIMMER_DEBUG
return 0; return 0;
} }
@ -187,7 +303,7 @@ bool ShdSerialSend(const uint8_t data[] = nullptr, uint16_t len = 0)
} }
// timeout // timeout
AddLog_P(LOG_LEVEL_ERROR, PSTR(SHD_LOGNAME" serial send timeout")); AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR(SHD_LOGNAME "Serial send timeout"));
} }
return false; return false;
} }
@ -501,79 +617,6 @@ void ShdResetToAppMode()
delay(50); // wait 50ms fot the co-processor to come online delay(50); // wait 50ms fot the co-processor to come online
} }
#ifdef SHELLY_FW_UPGRADE
void ShdResetToDFUMode()
{
AddLog_P(LOG_LEVEL_DEBUG, PSTR(SHD_LOGNAME" Request co-processor reset in dfu mode"));
pinMode(Pin(GPIO_SHELLY_DIMMER_RST_INV), OUTPUT);
digitalWrite(Pin(GPIO_SHELLY_DIMMER_RST_INV), LOW);
pinMode(Pin(GPIO_SHELLY_DIMMER_BOOT0), OUTPUT);
digitalWrite(Pin(GPIO_SHELLY_DIMMER_BOOT0), HIGH);
delay(50);
// clear in the receive buffer
while (Serial.available())
Serial.read();
digitalWrite(Pin(GPIO_SHELLY_DIMMER_RST_INV), HIGH); // pull out of reset
delay(50); // wait 50ms fot the co-processor to come online
}
bool ShdUpdateFirmware(const uint8_t data[], unsigned int size)
{
AddLog_P(LOG_LEVEL_DEBUG, PSTR(SHD_LOGNAME" Update firmware"));
bool ret = true;
stm32_t *stm = stm32_init(&Serial, STREAM_SERIAL, 1);
if (stm)
{
off_t offset = 0;
uint8_t buffer[256];
unsigned int len;
const uint8_t *p_st = data;
uint32_t addr, start, end;
stm32_err_t s_err;
AddLog_P(LOG_LEVEL_DEBUG, PSTR(SHD_LOGNAME" STM32 erase memory"));
stm32_erase_memory(stm, 0, STM32_MASS_ERASE);
addr = stm->dev->fl_start;
end = addr + size;
while(addr < end && offset < size)
{
uint32_t left = end - addr;
len = sizeof(buffer) > left ? left : sizeof(buffer);
len = len > size - offset ? size - offset : len;
if (len == 0)
{
break;
}
memcpy(buffer, p_st, len);
p_st += len;
s_err = stm32_write_memory(stm, addr, buffer, len);
if (s_err != STM32_ERR_OK)
{
ret = false;
break;
}
addr += len;
offset += len;
}
stm32_close(stm);
}
return ret;
}
#endif // SHELLY_FW_UPGRADE
void ShdPoll(void) void ShdPoll(void)
{ {
#ifdef SHELLY_DIMMER_DEBUG #ifdef SHELLY_DIMMER_DEBUG
@ -756,7 +799,7 @@ bool ShdSetChannels(void)
bool ShdSetPower(void) bool ShdSetPower(void)
{ {
AddLog_P(LOG_LEVEL_INFO, PSTR("EXS: Set Power, Power 0x%02x"), XdrvMailbox.index); AddLog_P(LOG_LEVEL_INFO, PSTR(SHD_LOGNAME "Set Power, Power 0x%02x"), XdrvMailbox.index);
Shd.req_on = (bool)XdrvMailbox.index; Shd.req_on = (bool)XdrvMailbox.index;
return ShdSyncState(); return ShdSyncState();