#include "Update.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 UpdateClass::_enablePartition(const esp_partition_t* partition){ if(!partition){ return false; } return ESP.partitionWrite(partition, 0, (uint32_t*) _skipBuffer, ENCRYPTED_BLOCK_SIZE); } UpdateClass::UpdateClass() : _error(0) , _buffer(0) , _bufferLen(0) , _size(0) , _progress_callback(NULL) , _progress(0) , _command(U_FLASH) , _partition(NULL) { } UpdateClass& UpdateClass::onProgress(THandlerFunction_Progress fn) { _progress_callback = fn; return *this; } void UpdateClass::_reset() { if (_buffer) delete[] _buffer; _buffer = 0; _bufferLen = 0; _progress = 0; _size = 0; _command = U_FLASH; if(_ledPin != -1) { digitalWrite(_ledPin, !_ledOn); // off } } bool UpdateClass::canRollBack(){ if(_buffer){ //Update is running return false; } const esp_partition_t* partition = esp_ota_get_next_update_partition(NULL); return _partitionIsBootable(partition); } bool UpdateClass::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); } bool UpdateClass::begin(size_t size, int command, int ledPin, uint8_t ledOn) { if(_size > 0){ log_w("already running"); return false; } _ledPin = ledPin; _ledOn = !!ledOn; // 0(LOW) or 1(HIGH) _reset(); _error = 0; if(size == 0) { _error = UPDATE_ERROR_SIZE; return false; } if (command == U_FLASH) { _partition = esp_ota_get_next_update_partition(NULL); 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, NULL); 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 UpdateClass::_abort(uint8_t err){ _reset(); _error = err; } void UpdateClass::abort(){ _abort(UPDATE_ERROR_ABORT); } bool UpdateClass::_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 UpdateClass::_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 UpdateClass::_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 UpdateClass::setMD5(const char * expected_md5){ if(strlen(expected_md5) != 32) { return false; } _target_md5 = expected_md5; return true; } bool UpdateClass::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 UpdateClass::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 UpdateClass::writeStream(Stream &data) { size_t written = 0; size_t toRead = 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(); } toRead = data.readBytes(_buffer + _bufferLen, bytesToRead); if(toRead == 0) { //Timeout delay(100); toRead = data.readBytes(_buffer + _bufferLen, bytesToRead); if(toRead == 0) { //Timeout _abort(UPDATE_ERROR_STREAM); return written; } } if(_ledPin != -1) { digitalWrite(_ledPin, !_ledOn); // Switch LED off } _bufferLen += toRead; if((_bufferLen == remaining() || _bufferLen == SPI_FLASH_SEC_SIZE) && !_writeBuffer()) return written; written += toRead; } return written; } void UpdateClass::printError(Print &out){ out.println(_err2str(_error)); } const char * UpdateClass::errorString(){ return _err2str(_error); } UpdateClass Update;