mirror of https://github.com/arendst/Tasmota.git
Add command upload 2
Add command upload 2 to upload saveboot binary from production partition
This commit is contained in:
parent
d136c20551
commit
865ba51b7a
|
@ -117,7 +117,7 @@ String HTTPUpdateLight::getLastErrorString(void)
|
|||
// error from Update class
|
||||
if(_lastError > 0) {
|
||||
StreamString error;
|
||||
Update.printError(error);
|
||||
TasUpdate.printError(error);
|
||||
error.trim(); // remove line ending
|
||||
return String("Update error: ") + error;
|
||||
}
|
||||
|
@ -418,14 +418,17 @@ bool HTTPUpdateLight::runUpdate(Stream& in, uint32_t size, String md5, int comma
|
|||
StreamString error;
|
||||
|
||||
if (_cbProgress) {
|
||||
Update.onProgress(_cbProgress);
|
||||
TasUpdate.onProgress(_cbProgress);
|
||||
}
|
||||
|
||||
if(!Update.begin(size, command, _ledPin, _ledOn)) {
|
||||
_lastError = Update.getError();
|
||||
Update.printError(error);
|
||||
// Start Tasmota Factory patch
|
||||
// if(!Update.begin(size, command, _ledPin, _ledOn)) {
|
||||
if(!TasUpdate.begin(size, command, _ledPin, _ledOn, NULL, _factory)) {
|
||||
// End Tasmota Factory patch
|
||||
_lastError = TasUpdate.getError();
|
||||
TasUpdate.printError(error);
|
||||
error.trim(); // remove line ending
|
||||
log_e("Update.begin failed! (%s)\n", error.c_str());
|
||||
log_e("TasUpdate.begin failed! (%s)\n", error.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -434,20 +437,20 @@ bool HTTPUpdateLight::runUpdate(Stream& in, uint32_t size, String md5, int comma
|
|||
}
|
||||
|
||||
if(md5.length()) {
|
||||
if(!Update.setMD5(md5.c_str())) {
|
||||
if(!TasUpdate.setMD5(md5.c_str())) {
|
||||
_lastError = HTTP_UE_SERVER_FAULTY_MD5;
|
||||
log_e("Update.setMD5 failed! (%s)\n", md5.c_str());
|
||||
log_e("TasUpdate.setMD5 failed! (%s)\n", md5.c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// To do: the SHA256 could be checked if the server sends it
|
||||
|
||||
if(Update.writeStream(in) != size) {
|
||||
_lastError = Update.getError();
|
||||
Update.printError(error);
|
||||
if(TasUpdate.writeStream(in) != size) {
|
||||
_lastError = TasUpdate.getError();
|
||||
TasUpdate.printError(error);
|
||||
error.trim(); // remove line ending
|
||||
log_e("Update.writeStream failed! (%s)\n", error.c_str());
|
||||
log_e("TasUpdate.writeStream failed! (%s)\n", error.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -455,11 +458,11 @@ bool HTTPUpdateLight::runUpdate(Stream& in, uint32_t size, String md5, int comma
|
|||
_cbProgress(size, size);
|
||||
}
|
||||
|
||||
if(!Update.end()) {
|
||||
_lastError = Update.getError();
|
||||
Update.printError(error);
|
||||
if(!TasUpdate.end()) {
|
||||
_lastError = TasUpdate.getError();
|
||||
TasUpdate.printError(error);
|
||||
error.trim(); // remove line ending
|
||||
log_e("Update.end failed! (%s)\n", error.c_str());
|
||||
log_e("TasUpdate.end failed! (%s)\n", error.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
#include <WiFiClient.h>
|
||||
#include <WiFiUdp.h>
|
||||
#include <HttpClientLight.h>
|
||||
#include <Update.h>
|
||||
#include <TasUpdate.h>
|
||||
#include <HTTPUpdate.h>
|
||||
|
||||
/// note we use HTTP client errors too so we start at 100
|
||||
|
@ -69,7 +69,7 @@ public:
|
|||
{
|
||||
_rebootOnUpdate = reboot;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* set redirect follow mode. See `followRedirects_t` enum for avaliable modes.
|
||||
* @param follow
|
||||
|
@ -85,6 +85,13 @@ public:
|
|||
_ledOn = ledOn;
|
||||
}
|
||||
|
||||
// Start Tasmota Factory patch
|
||||
void setFactory(bool factory = false)
|
||||
{
|
||||
_factory = factory;
|
||||
}
|
||||
// End Tasmota Factory patch
|
||||
|
||||
// t_httpUpdate_return update(WiFiClient& client, const String& url, const String& currentVersion = "");
|
||||
|
||||
// t_httpUpdate_return update(WiFiClient& client, const String& host, uint16_t port, const String& uri = "/",
|
||||
|
@ -131,6 +138,9 @@ private:
|
|||
|
||||
int _ledPin;
|
||||
uint8_t _ledOn;
|
||||
// Start Tasmota Factory patch
|
||||
bool _factory;
|
||||
// End Tasmota Factory patch
|
||||
};
|
||||
|
||||
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_HTTPUPDATE)
|
||||
|
|
|
@ -0,0 +1,194 @@
|
|||
#ifndef TASUPDATER_H
|
||||
#define TASUPDATER_H
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <MD5Builder.h>
|
||||
#include <functional>
|
||||
#include "esp_partition.h"
|
||||
|
||||
#define UPDATE_ERROR_OK (0)
|
||||
#define UPDATE_ERROR_WRITE (1)
|
||||
#define UPDATE_ERROR_ERASE (2)
|
||||
#define UPDATE_ERROR_READ (3)
|
||||
#define UPDATE_ERROR_SPACE (4)
|
||||
#define UPDATE_ERROR_SIZE (5)
|
||||
#define UPDATE_ERROR_STREAM (6)
|
||||
#define UPDATE_ERROR_MD5 (7)
|
||||
#define UPDATE_ERROR_MAGIC_BYTE (8)
|
||||
#define UPDATE_ERROR_ACTIVATE (9)
|
||||
#define UPDATE_ERROR_NO_PARTITION (10)
|
||||
#define UPDATE_ERROR_BAD_ARGUMENT (11)
|
||||
#define UPDATE_ERROR_ABORT (12)
|
||||
|
||||
#define UPDATE_SIZE_UNKNOWN 0xFFFFFFFF
|
||||
|
||||
#define U_FLASH 0
|
||||
#define U_SPIFFS 100
|
||||
#define U_AUTH 200
|
||||
|
||||
#define ENCRYPTED_BLOCK_SIZE 16
|
||||
|
||||
class TasUpdateClass {
|
||||
public:
|
||||
typedef std::function<void(size_t, size_t)> THandlerFunction_Progress;
|
||||
|
||||
TasUpdateClass();
|
||||
|
||||
/*
|
||||
This callback will be called when Update is receiving data
|
||||
*/
|
||||
TasUpdateClass& onProgress(THandlerFunction_Progress fn);
|
||||
|
||||
/*
|
||||
Call this to check the space needed for the update
|
||||
Will return false if there is not enough space
|
||||
*/
|
||||
// Start Tasmota Factory patch
|
||||
// bool begin(size_t size=UPDATE_SIZE_UNKNOWN, int command = U_FLASH, int ledPin = -1, uint8_t ledOn = LOW, const char *label = NULL);
|
||||
bool begin(size_t size=UPDATE_SIZE_UNKNOWN, int command = U_FLASH, int ledPin = -1, uint8_t ledOn = LOW, const char *label = NULL, bool factory = false);
|
||||
// End Tasmota Factory patch
|
||||
|
||||
/*
|
||||
Writes a buffer to the flash and increments the address
|
||||
Returns the amount written
|
||||
*/
|
||||
size_t write(uint8_t *data, size_t len);
|
||||
|
||||
/*
|
||||
Writes the remaining bytes from the Stream to the flash
|
||||
Uses readBytes() and sets UPDATE_ERROR_STREAM on timeout
|
||||
Returns the bytes written
|
||||
Should be equal to the remaining bytes when called
|
||||
Usable for slow streams like Serial
|
||||
*/
|
||||
size_t writeStream(Stream &data);
|
||||
|
||||
/*
|
||||
If all bytes are written
|
||||
this call will write the config to eboot
|
||||
and return true
|
||||
If there is already an update running but is not finished and !evenIfRemaining
|
||||
or there is an error
|
||||
this will clear everything and return false
|
||||
the last error is available through getError()
|
||||
evenIfRemaining is helpfull when you update without knowing the final size first
|
||||
*/
|
||||
bool end(bool evenIfRemaining = false);
|
||||
|
||||
/*
|
||||
Aborts the running update
|
||||
*/
|
||||
void abort();
|
||||
|
||||
/*
|
||||
Prints the last error to an output stream
|
||||
*/
|
||||
void printError(Print &out);
|
||||
|
||||
const char * errorString();
|
||||
|
||||
/*
|
||||
sets the expected MD5 for the firmware (hexString)
|
||||
*/
|
||||
bool setMD5(const char * expected_md5);
|
||||
|
||||
/*
|
||||
returns the MD5 String of the successfully ended firmware
|
||||
*/
|
||||
String md5String(void){ return _md5.toString(); }
|
||||
|
||||
/*
|
||||
populated the result with the md5 bytes of the successfully ended firmware
|
||||
*/
|
||||
void md5(uint8_t * result){ return _md5.getBytes(result); }
|
||||
|
||||
//Helpers
|
||||
uint8_t getError(){ return _error; }
|
||||
void clearError(){ _error = UPDATE_ERROR_OK; }
|
||||
bool hasError(){ return _error != UPDATE_ERROR_OK; }
|
||||
bool isRunning(){ return _size > 0; }
|
||||
bool isFinished(){ return _progress == _size; }
|
||||
size_t size(){ return _size; }
|
||||
size_t progress(){ return _progress; }
|
||||
size_t remaining(){ return _size - _progress; }
|
||||
|
||||
/*
|
||||
Template to write from objects that expose
|
||||
available() and read(uint8_t*, size_t) methods
|
||||
faster than the writeStream method
|
||||
writes only what is available
|
||||
*/
|
||||
template<typename T>
|
||||
size_t write(T &data){
|
||||
size_t written = 0;
|
||||
if (hasError() || !isRunning())
|
||||
return 0;
|
||||
|
||||
size_t available = data.available();
|
||||
while(available) {
|
||||
if(_bufferLen + available > remaining()){
|
||||
available = remaining() - _bufferLen;
|
||||
}
|
||||
if(_bufferLen + available > 4096) {
|
||||
size_t toBuff = 4096 - _bufferLen;
|
||||
data.read(_buffer + _bufferLen, toBuff);
|
||||
_bufferLen += toBuff;
|
||||
if(!_writeBuffer())
|
||||
return written;
|
||||
written += toBuff;
|
||||
} else {
|
||||
data.read(_buffer + _bufferLen, available);
|
||||
_bufferLen += available;
|
||||
written += available;
|
||||
if(_bufferLen == remaining()) {
|
||||
if(!_writeBuffer()) {
|
||||
return written;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(remaining() == 0)
|
||||
return written;
|
||||
available = data.available();
|
||||
}
|
||||
return written;
|
||||
}
|
||||
|
||||
/*
|
||||
check if there is a firmware on the other OTA partition that you can bootinto
|
||||
*/
|
||||
bool canRollBack();
|
||||
/*
|
||||
set the other OTA partition as bootable (reboot to enable)
|
||||
*/
|
||||
bool rollBack();
|
||||
|
||||
private:
|
||||
void _reset();
|
||||
void _abort(uint8_t err);
|
||||
bool _writeBuffer();
|
||||
bool _verifyHeader(uint8_t data);
|
||||
bool _verifyEnd();
|
||||
bool _enablePartition(const esp_partition_t* partition);
|
||||
|
||||
|
||||
uint8_t _error;
|
||||
uint8_t *_buffer;
|
||||
uint8_t *_skipBuffer;
|
||||
size_t _bufferLen;
|
||||
size_t _size;
|
||||
THandlerFunction_Progress _progress_callback;
|
||||
uint32_t _progress;
|
||||
uint32_t _paroffset;
|
||||
uint32_t _command;
|
||||
const esp_partition_t* _partition;
|
||||
|
||||
String _target_md5;
|
||||
MD5Builder _md5;
|
||||
|
||||
int _ledPin;
|
||||
uint8_t _ledOn;
|
||||
};
|
||||
|
||||
extern TasUpdateClass TasUpdate;
|
||||
|
||||
#endif // TASUPDATER_H
|
|
@ -0,0 +1,404 @@
|
|||
#include "TasUpdate.h"
|
||||
#include "Arduino.h"
|
||||
#include "esp_spi_flash.h"
|
||||
#include "esp_ota_ops.h"
|
||||
#include "esp_image_format.h"
|
||||
|
||||
static const char * _err2str(uint8_t _error){
|
||||
if(_error == UPDATE_ERROR_OK){
|
||||
return ("No Error");
|
||||
} else if(_error == UPDATE_ERROR_WRITE){
|
||||
return ("Flash Write Failed");
|
||||
} else if(_error == UPDATE_ERROR_ERASE){
|
||||
return ("Flash Erase Failed");
|
||||
} else if(_error == UPDATE_ERROR_READ){
|
||||
return ("Flash Read Failed");
|
||||
} else if(_error == UPDATE_ERROR_SPACE){
|
||||
return ("Not Enough Space");
|
||||
} else if(_error == UPDATE_ERROR_SIZE){
|
||||
return ("Bad Size Given");
|
||||
} else if(_error == UPDATE_ERROR_STREAM){
|
||||
return ("Stream Read Timeout");
|
||||
} else if(_error == UPDATE_ERROR_MD5){
|
||||
return ("MD5 Check Failed");
|
||||
} else if(_error == UPDATE_ERROR_MAGIC_BYTE){
|
||||
return ("Wrong Magic Byte");
|
||||
} else if(_error == UPDATE_ERROR_ACTIVATE){
|
||||
return ("Could Not Activate The Firmware");
|
||||
} else if(_error == UPDATE_ERROR_NO_PARTITION){
|
||||
return ("Partition Could Not be Found");
|
||||
} else if(_error == UPDATE_ERROR_BAD_ARGUMENT){
|
||||
return ("Bad Argument");
|
||||
} else if(_error == UPDATE_ERROR_ABORT){
|
||||
return ("Aborted");
|
||||
}
|
||||
return ("UNKNOWN");
|
||||
}
|
||||
|
||||
static bool _partitionIsBootable(const esp_partition_t* partition){
|
||||
uint8_t buf[ENCRYPTED_BLOCK_SIZE];
|
||||
if(!partition){
|
||||
return false;
|
||||
}
|
||||
if(!ESP.partitionRead(partition, 0, (uint32_t*)buf, ENCRYPTED_BLOCK_SIZE)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(buf[0] != ESP_IMAGE_HEADER_MAGIC) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TasUpdateClass::_enablePartition(const esp_partition_t* partition){
|
||||
if(!partition){
|
||||
return false;
|
||||
}
|
||||
return ESP.partitionWrite(partition, 0, (uint32_t*) _skipBuffer, ENCRYPTED_BLOCK_SIZE);
|
||||
}
|
||||
|
||||
TasUpdateClass::TasUpdateClass()
|
||||
: _error(0)
|
||||
, _buffer(0)
|
||||
, _bufferLen(0)
|
||||
, _size(0)
|
||||
, _progress_callback(NULL)
|
||||
, _progress(0)
|
||||
, _paroffset(0)
|
||||
, _command(U_FLASH)
|
||||
, _partition(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
TasUpdateClass& TasUpdateClass::onProgress(THandlerFunction_Progress fn) {
|
||||
_progress_callback = fn;
|
||||
return *this;
|
||||
}
|
||||
|
||||
void TasUpdateClass::_reset() {
|
||||
if (_buffer)
|
||||
delete[] _buffer;
|
||||
_buffer = 0;
|
||||
_bufferLen = 0;
|
||||
_progress = 0;
|
||||
_size = 0;
|
||||
_command = U_FLASH;
|
||||
|
||||
if(_ledPin != -1) {
|
||||
digitalWrite(_ledPin, !_ledOn); // off
|
||||
}
|
||||
}
|
||||
|
||||
bool TasUpdateClass::canRollBack(){
|
||||
if(_buffer){ //Update is running
|
||||
return false;
|
||||
}
|
||||
const esp_partition_t* partition = esp_ota_get_next_update_partition(NULL);
|
||||
return _partitionIsBootable(partition);
|
||||
}
|
||||
|
||||
bool TasUpdateClass::rollBack(){
|
||||
if(_buffer){ //Update is running
|
||||
return false;
|
||||
}
|
||||
const esp_partition_t* partition = esp_ota_get_next_update_partition(NULL);
|
||||
return _partitionIsBootable(partition) && !esp_ota_set_boot_partition(partition);
|
||||
}
|
||||
|
||||
// Start Tasmota Factory patch
|
||||
//bool UpdateClass::begin(size_t size, int command, int ledPin, uint8_t ledOn, const char *label) {
|
||||
bool TasUpdateClass::begin(size_t size, int command, int ledPin, uint8_t ledOn, const char *label, bool factory) {
|
||||
// End Tasmota Factory patch
|
||||
if(_size > 0){
|
||||
log_w("already running");
|
||||
return false;
|
||||
}
|
||||
|
||||
_ledPin = ledPin;
|
||||
_ledOn = !!ledOn; // 0(LOW) or 1(HIGH)
|
||||
|
||||
_reset();
|
||||
_error = 0;
|
||||
_target_md5 = emptyString;
|
||||
_md5 = MD5Builder();
|
||||
|
||||
if(size == 0) {
|
||||
_error = UPDATE_ERROR_SIZE;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (command == U_FLASH) {
|
||||
// Start Tasmota Factory patch
|
||||
// _partition = esp_ota_get_next_update_partition(NULL);
|
||||
if (factory) {
|
||||
_partition = esp_partition_find_first(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_APP_FACTORY, NULL);
|
||||
} else {
|
||||
_partition = esp_ota_get_next_update_partition(NULL);
|
||||
}
|
||||
// End Tasmota Factory patch
|
||||
if(!_partition){
|
||||
_error = UPDATE_ERROR_NO_PARTITION;
|
||||
return false;
|
||||
}
|
||||
log_d("OTA Partition: %s", _partition->label);
|
||||
}
|
||||
else if (command == U_SPIFFS) {
|
||||
_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_SPIFFS, label);
|
||||
_paroffset = 0;
|
||||
if(!_partition){
|
||||
_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_FAT, NULL);
|
||||
_paroffset = 0x1000; //Offset for ffat, assuming size is already corrected
|
||||
if(!_partition){
|
||||
_error = UPDATE_ERROR_NO_PARTITION;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
_error = UPDATE_ERROR_BAD_ARGUMENT;
|
||||
log_e("bad command %u", command);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(size == UPDATE_SIZE_UNKNOWN){
|
||||
size = _partition->size;
|
||||
} else if(size > _partition->size){
|
||||
_error = UPDATE_ERROR_SIZE;
|
||||
log_e("too large %u > %u", size, _partition->size);
|
||||
return false;
|
||||
}
|
||||
|
||||
//initialize
|
||||
_buffer = (uint8_t*)malloc(SPI_FLASH_SEC_SIZE);
|
||||
if(!_buffer){
|
||||
log_e("malloc failed");
|
||||
return false;
|
||||
}
|
||||
_size = size;
|
||||
_command = command;
|
||||
_md5.begin();
|
||||
return true;
|
||||
}
|
||||
|
||||
void TasUpdateClass::_abort(uint8_t err){
|
||||
_reset();
|
||||
_error = err;
|
||||
}
|
||||
|
||||
void TasUpdateClass::abort(){
|
||||
_abort(UPDATE_ERROR_ABORT);
|
||||
}
|
||||
|
||||
bool TasUpdateClass::_writeBuffer(){
|
||||
//first bytes of new firmware
|
||||
uint8_t skip = 0;
|
||||
if(!_progress && _command == U_FLASH){
|
||||
//check magic
|
||||
if(_buffer[0] != ESP_IMAGE_HEADER_MAGIC){
|
||||
_abort(UPDATE_ERROR_MAGIC_BYTE);
|
||||
return false;
|
||||
}
|
||||
|
||||
//Stash the first 16 bytes of data and set the offset so they are
|
||||
//not written at this point so that partially written firmware
|
||||
//will not be bootable
|
||||
skip = ENCRYPTED_BLOCK_SIZE;
|
||||
_skipBuffer = (uint8_t*)malloc(skip);
|
||||
if(!_skipBuffer){
|
||||
log_e("malloc failed");
|
||||
return false;
|
||||
}
|
||||
memcpy(_skipBuffer, _buffer, skip);
|
||||
}
|
||||
if (!_progress && _progress_callback) {
|
||||
_progress_callback(0, _size);
|
||||
}
|
||||
if(!ESP.partitionEraseRange(_partition, _progress, SPI_FLASH_SEC_SIZE)){
|
||||
_abort(UPDATE_ERROR_ERASE);
|
||||
return false;
|
||||
}
|
||||
if (!ESP.partitionWrite(_partition, _progress + skip, (uint32_t*)_buffer + skip/sizeof(uint32_t), _bufferLen - skip)) {
|
||||
_abort(UPDATE_ERROR_WRITE);
|
||||
return false;
|
||||
}
|
||||
//restore magic or md5 will fail
|
||||
if(!_progress && _command == U_FLASH){
|
||||
_buffer[0] = ESP_IMAGE_HEADER_MAGIC;
|
||||
}
|
||||
_md5.add(_buffer, _bufferLen);
|
||||
_progress += _bufferLen;
|
||||
_bufferLen = 0;
|
||||
if (_progress_callback) {
|
||||
_progress_callback(_progress, _size);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TasUpdateClass::_verifyHeader(uint8_t data) {
|
||||
if(_command == U_FLASH) {
|
||||
if(data != ESP_IMAGE_HEADER_MAGIC) {
|
||||
_abort(UPDATE_ERROR_MAGIC_BYTE);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} else if(_command == U_SPIFFS) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TasUpdateClass::_verifyEnd() {
|
||||
if(_command == U_FLASH) {
|
||||
if(!_enablePartition(_partition) || !_partitionIsBootable(_partition)) {
|
||||
_abort(UPDATE_ERROR_READ);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(esp_ota_set_boot_partition(_partition)){
|
||||
_abort(UPDATE_ERROR_ACTIVATE);
|
||||
return false;
|
||||
}
|
||||
_reset();
|
||||
return true;
|
||||
} else if(_command == U_SPIFFS) {
|
||||
_reset();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TasUpdateClass::setMD5(const char * expected_md5){
|
||||
if(strlen(expected_md5) != 32)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
_target_md5 = expected_md5;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TasUpdateClass::end(bool evenIfRemaining){
|
||||
if(hasError() || _size == 0){
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!isFinished() && !evenIfRemaining){
|
||||
log_e("premature end: res:%u, pos:%u/%u\n", getError(), progress(), _size);
|
||||
_abort(UPDATE_ERROR_ABORT);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(evenIfRemaining) {
|
||||
if(_bufferLen > 0) {
|
||||
_writeBuffer();
|
||||
}
|
||||
_size = progress();
|
||||
}
|
||||
|
||||
_md5.calculate();
|
||||
if(_target_md5.length()) {
|
||||
if(_target_md5 != _md5.toString()){
|
||||
_abort(UPDATE_ERROR_MD5);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return _verifyEnd();
|
||||
}
|
||||
|
||||
size_t TasUpdateClass::write(uint8_t *data, size_t len) {
|
||||
if(hasError() || !isRunning()){
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(len > remaining()){
|
||||
_abort(UPDATE_ERROR_SPACE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t left = len;
|
||||
|
||||
while((_bufferLen + left) > SPI_FLASH_SEC_SIZE) {
|
||||
size_t toBuff = SPI_FLASH_SEC_SIZE - _bufferLen;
|
||||
memcpy(_buffer + _bufferLen, data + (len - left), toBuff);
|
||||
_bufferLen += toBuff;
|
||||
if(!_writeBuffer()){
|
||||
return len - left;
|
||||
}
|
||||
left -= toBuff;
|
||||
}
|
||||
memcpy(_buffer + _bufferLen, data + (len - left), left);
|
||||
_bufferLen += left;
|
||||
if(_bufferLen == remaining()){
|
||||
if(!_writeBuffer()){
|
||||
return len - left;
|
||||
}
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
size_t TasUpdateClass::writeStream(Stream &data) {
|
||||
size_t written = 0;
|
||||
size_t toRead = 0;
|
||||
int timeout_failures = 0;
|
||||
|
||||
if(hasError() || !isRunning())
|
||||
return 0;
|
||||
|
||||
if(!_verifyHeader(data.peek())) {
|
||||
_reset();
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(_ledPin != -1) {
|
||||
pinMode(_ledPin, OUTPUT);
|
||||
}
|
||||
|
||||
while(remaining()) {
|
||||
if(_ledPin != -1) {
|
||||
digitalWrite(_ledPin, _ledOn); // Switch LED on
|
||||
}
|
||||
size_t bytesToRead = SPI_FLASH_SEC_SIZE - _bufferLen;
|
||||
if(bytesToRead > remaining()) {
|
||||
bytesToRead = remaining();
|
||||
}
|
||||
|
||||
/*
|
||||
Init read&timeout counters and try to read, if read failed, increase counter,
|
||||
wait 100ms and try to read again. If counter > 300 (30 sec), give up/abort
|
||||
*/
|
||||
toRead = 0;
|
||||
timeout_failures = 0;
|
||||
while(!toRead) {
|
||||
toRead = data.readBytes(_buffer + _bufferLen, bytesToRead);
|
||||
if(toRead == 0) {
|
||||
timeout_failures++;
|
||||
if (timeout_failures >= 300) {
|
||||
_abort(UPDATE_ERROR_STREAM);
|
||||
return written;
|
||||
}
|
||||
delay(100);
|
||||
}
|
||||
}
|
||||
|
||||
if(_ledPin != -1) {
|
||||
digitalWrite(_ledPin, !_ledOn); // Switch LED off
|
||||
}
|
||||
_bufferLen += toRead;
|
||||
if((_bufferLen == remaining() || _bufferLen == SPI_FLASH_SEC_SIZE) && !_writeBuffer())
|
||||
return written;
|
||||
written += toRead;
|
||||
|
||||
delay(1); // Fix solo WDT
|
||||
}
|
||||
return written;
|
||||
}
|
||||
|
||||
void TasUpdateClass::printError(Print &out){
|
||||
out.println(_err2str(_error));
|
||||
}
|
||||
|
||||
const char * TasUpdateClass::errorString(){
|
||||
return _err2str(_error);
|
||||
}
|
||||
|
||||
TasUpdateClass TasUpdate;
|
|
@ -837,7 +837,15 @@ void CmndUpgrade(void)
|
|||
TasmotaGlobal.ota_state_flag = 3;
|
||||
char stemp1[TOPSZ];
|
||||
Response_P(PSTR("{\"%s\":\"" D_JSON_VERSION " %s " D_JSON_FROM " %s\"}"), XdrvMailbox.command, TasmotaGlobal.version, GetOtaUrl(stemp1, sizeof(stemp1)));
|
||||
} else {
|
||||
}
|
||||
#if defined(ESP32) && defined(USE_WEBCLIENT_HTTPS)
|
||||
else if (EspSingleOtaPartition() && !EspRunningFactoryPartition() && (1 == XdrvMailbox.data_len) && (2 == XdrvMailbox.payload)) {
|
||||
TasmotaGlobal.ota_factory = true;
|
||||
TasmotaGlobal.ota_state_flag = 3;
|
||||
ResponseCmndChar(PSTR("Saveboot"));
|
||||
}
|
||||
#endif // ESP32 and WEBCLIENT_HTTPS
|
||||
else {
|
||||
Response_P(PSTR("{\"%s\":\"" D_JSON_ONE_OR_GT "\"}"), XdrvMailbox.command, TasmotaGlobal.version);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1248,6 +1248,21 @@ void Every250mSeconds(void)
|
|||
|
||||
#ifdef ESP32
|
||||
#ifndef FIRMWARE_MINIMAL
|
||||
#ifdef USE_WEBCLIENT_HTTPS
|
||||
if (TasmotaGlobal.ota_factory) {
|
||||
char *bch = strrchr(full_ota_url, '/'); // Only consider filename after last backslash prevent change of urls having "-" in it
|
||||
if (bch == nullptr) { bch = full_ota_url; } // No path found so use filename only
|
||||
char *ech = strchr(bch, '.'); // Find file type in filename (none, .ino.bin, .ino.bin.gz, .bin, .bin.gz or .gz)
|
||||
if (ech == nullptr) { ech = full_ota_url + strlen(full_ota_url); } // Point to '/0' at end of full_ota_url becoming an empty string
|
||||
char ota_url_type[strlen(ech) +1];
|
||||
strncpy(ota_url_type, ech, sizeof(ota_url_type)); // Either empty, .ino.bin, .ino.bin.gz, .bin, .bin.gz or .gz
|
||||
|
||||
char *pch = strrchr(bch, '-'); // Find last dash (-) and ignore remainder - handles tasmota-DE
|
||||
if (pch == nullptr) { pch = ech; } // No dash so ignore filetype
|
||||
*pch = '\0'; // full_ota_url = http://domus1:80/api/arduino/tasmota
|
||||
snprintf_P(full_ota_url, sizeof(full_ota_url), PSTR("%s-safeboot%s"), full_ota_url, ota_url_type); // Saveboot filename must be filename-safeboot
|
||||
} else
|
||||
#endif // USE_WEBCLIENT_HTTPS
|
||||
if (EspSingleOtaPartition()) {
|
||||
#ifdef CONFIG_IDF_TARGET_ESP32C3
|
||||
OtaFactoryWrite(true);
|
||||
|
@ -1271,6 +1286,7 @@ void Every250mSeconds(void)
|
|||
ota_result = -999;
|
||||
} else {
|
||||
httpUpdateLight.rebootOnUpdate(false);
|
||||
httpUpdateLight.setFactory(TasmotaGlobal.ota_factory);
|
||||
ota_result = (HTTP_UPDATE_FAILED != httpUpdateLight.update(OTAclient, version));
|
||||
}
|
||||
#else // standard OTA over HTTP
|
||||
|
|
|
@ -186,6 +186,7 @@ struct TasmotaGlobal_t {
|
|||
bool i2c_enabled; // I2C configured
|
||||
#ifdef ESP32
|
||||
bool i2c_enabled_2; // I2C configured, second controller on ESP32, Wire1
|
||||
bool ota_factory; // Select safeboot binary
|
||||
#endif
|
||||
bool ntp_force_sync; // Force NTP sync
|
||||
bool skip_light_fade; // Temporarily skip light fading
|
||||
|
|
Loading…
Reference in New Issue