mirror of
https://github.com/arendst/Tasmota.git
synced 2025-11-16 06:20:38 +00:00
Rename HttpClientLight
This commit is contained in:
17
lib/libesp32/HttpClientLight/library.json
Normal file
17
lib/libesp32/HttpClientLight/library.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "HttpClient light",
|
||||
"version": "1.0",
|
||||
"description": "Forked version of Arduino HttpClient to support BearSSL instead of mbedTLS",
|
||||
"license": "MIT",
|
||||
"homepage": "https://github.com/arendst/Tasmota",
|
||||
"frameworks": "arduino",
|
||||
"platforms": "espressif32",
|
||||
"authors":
|
||||
{
|
||||
"name": "Stephan Hadinger",
|
||||
"maintainer": true
|
||||
},
|
||||
"build": {
|
||||
"flags": [ "-I$PROJECT_DIR/include" ]
|
||||
}
|
||||
}
|
||||
474
lib/libesp32/HttpClientLight/src/HTTPUpdateLight.cpp
Normal file
474
lib/libesp32/HttpClientLight/src/HTTPUpdateLight.cpp
Normal file
@@ -0,0 +1,474 @@
|
||||
/**
|
||||
*
|
||||
* @file HTTPUpdate.cpp based om ESP8266HTTPUpdate.cpp
|
||||
* @date 16.10.2018
|
||||
* @author Markus Sattler
|
||||
*
|
||||
* Copyright (c) 2015 Markus Sattler. All rights reserved.
|
||||
* This file is part of the ESP32 Http Updater.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include "HTTPUpdateLight.h"
|
||||
#include <StreamString.h>
|
||||
|
||||
#include <esp_partition.h>
|
||||
#include <esp_ota_ops.h> // get running partition
|
||||
|
||||
// Tasmota Logging
|
||||
extern void AddLog(uint32_t loglevel, PGM_P formatP, ...);
|
||||
enum LoggingLevels {LOG_LEVEL_NONE, LOG_LEVEL_ERROR, LOG_LEVEL_INFO, LOG_LEVEL_DEBUG, LOG_LEVEL_DEBUG_MORE};
|
||||
|
||||
// To do extern "C" uint32_t _SPIFFS_start;
|
||||
// To do extern "C" uint32_t _SPIFFS_end;
|
||||
|
||||
HTTPUpdateLight::HTTPUpdateLight(void)
|
||||
: _httpClientTimeout(8000), _ledPin(-1)
|
||||
{
|
||||
_followRedirects = HTTPC_STRICT_FOLLOW_REDIRECTS;
|
||||
}
|
||||
|
||||
HTTPUpdateLight::HTTPUpdateLight(int httpClientTimeout)
|
||||
: _httpClientTimeout(httpClientTimeout), _ledPin(-1)
|
||||
{
|
||||
_followRedirects = HTTPC_STRICT_FOLLOW_REDIRECTS;
|
||||
}
|
||||
|
||||
HTTPUpdateLight::~HTTPUpdateLight(void)
|
||||
{
|
||||
}
|
||||
|
||||
// HTTPUpdateResult HTTPUpdateLight::update(WiFiClient& client, const String& url, const String& currentVersion)
|
||||
// {
|
||||
// HTTPClient http;
|
||||
// if(!http.begin(client, url))
|
||||
// {
|
||||
// return HTTP_UPDATE_FAILED;
|
||||
// }
|
||||
// return handleUpdate(http, currentVersion, false);
|
||||
// }
|
||||
|
||||
// HTTPUpdateResult HTTPUpdateLight::updateSpiffs(HTTPClient& httpClient, const String& currentVersion)
|
||||
// {
|
||||
// return handleUpdate(httpClient, currentVersion, true);
|
||||
// }
|
||||
|
||||
// HTTPUpdateResult HTTPUpdateLight::updateSpiffs(WiFiClient& client, const String& url, const String& currentVersion)
|
||||
// {
|
||||
// HTTPClient http;
|
||||
// if(!http.begin(client, url))
|
||||
// {
|
||||
// return HTTP_UPDATE_FAILED;
|
||||
// }
|
||||
// return handleUpdate(http, currentVersion, true);
|
||||
// }
|
||||
|
||||
HTTPUpdateResult HTTPUpdateLight::update(HTTPClientLight& httpClient,
|
||||
const String& currentVersion)
|
||||
{
|
||||
return handleUpdate(httpClient, currentVersion, false);
|
||||
}
|
||||
|
||||
// HTTPUpdateResult HTTPUpdateLight::update(WiFiClient& client, const String& host, uint16_t port, const String& uri,
|
||||
// const String& currentVersion)
|
||||
// {
|
||||
// HTTPClient http;
|
||||
// if(!http.begin(client, host, port, uri))
|
||||
// {
|
||||
// return HTTP_UPDATE_FAILED;
|
||||
// }
|
||||
// return handleUpdate(http, currentVersion, false);
|
||||
// }
|
||||
|
||||
/**
|
||||
* return error code as int
|
||||
* @return int error code
|
||||
*/
|
||||
int HTTPUpdateLight::getLastError(void)
|
||||
{
|
||||
return _lastError;
|
||||
}
|
||||
|
||||
/**
|
||||
* return error code as String
|
||||
* @return String error
|
||||
*/
|
||||
String HTTPUpdateLight::getLastErrorString(void)
|
||||
{
|
||||
|
||||
if(_lastError == 0) {
|
||||
return String(); // no error
|
||||
}
|
||||
|
||||
// error from Update class
|
||||
if(_lastError > 0) {
|
||||
StreamString error;
|
||||
TasUpdate.printError(error);
|
||||
error.trim(); // remove line ending
|
||||
return String("Update error: ") + error;
|
||||
}
|
||||
|
||||
// error from http client
|
||||
if(_lastError > -100) {
|
||||
return String("HTTP error: ") + HTTPClientLight::errorToString(_lastError);
|
||||
}
|
||||
|
||||
switch(_lastError) {
|
||||
case HTTP_UE_TOO_LESS_SPACE:
|
||||
return "Not Enough space";
|
||||
case HTTP_UE_SERVER_NOT_REPORT_SIZE:
|
||||
return "Server Did Not Report Size";
|
||||
case HTTP_UE_SERVER_FILE_NOT_FOUND:
|
||||
return "File Not Found (404)";
|
||||
case HTTP_UE_SERVER_FORBIDDEN:
|
||||
return "Forbidden (403)";
|
||||
case HTTP_UE_SERVER_WRONG_HTTP_CODE:
|
||||
return "Wrong HTTP Code";
|
||||
case HTTP_UE_SERVER_FAULTY_MD5:
|
||||
return "Wrong MD5";
|
||||
case HTTP_UE_BIN_VERIFY_HEADER_FAILED:
|
||||
return "Verify Bin Header Failed";
|
||||
case HTTP_UE_BIN_FOR_WRONG_FLASH:
|
||||
return "New Binary Does Not Fit Flash Size";
|
||||
case HTTP_UE_NO_PARTITION:
|
||||
return "Partition Could Not be Found";
|
||||
}
|
||||
|
||||
return String();
|
||||
}
|
||||
|
||||
extern String getSketchSHA256();
|
||||
// String getSketchSHA256() {
|
||||
// const size_t HASH_LEN = 32; // SHA-256 digest length
|
||||
|
||||
// uint8_t sha_256[HASH_LEN] = { 0 };
|
||||
|
||||
// // get sha256 digest for running partition
|
||||
// if(esp_partition_get_sha256(esp_ota_get_running_partition(), sha_256) == 0) {
|
||||
// char buffer[2 * HASH_LEN + 1];
|
||||
|
||||
// for(size_t index = 0; index < HASH_LEN; index++) {
|
||||
// uint8_t nibble = (sha_256[index] & 0xf0) >> 4;
|
||||
// buffer[2 * index] = nibble < 10 ? char(nibble + '0') : char(nibble - 10 + 'A');
|
||||
|
||||
// nibble = sha_256[index] & 0x0f;
|
||||
// buffer[2 * index + 1] = nibble < 10 ? char(nibble + '0') : char(nibble - 10 + 'A');
|
||||
// }
|
||||
|
||||
// buffer[2 * HASH_LEN] = '\0';
|
||||
|
||||
// return String(buffer);
|
||||
// } else {
|
||||
|
||||
// return String();
|
||||
// }
|
||||
// }
|
||||
|
||||
/**
|
||||
*
|
||||
* @param http HTTPClientLight *
|
||||
* @param currentVersion const char *
|
||||
* @return HTTPUpdateResult
|
||||
*/
|
||||
HTTPUpdateResult HTTPUpdateLight::handleUpdate(HTTPClientLight& http, const String& currentVersion, bool spiffs)
|
||||
{
|
||||
|
||||
HTTPUpdateResult ret = HTTP_UPDATE_FAILED;
|
||||
|
||||
// use HTTP/1.0 for update since the update handler not support any transfer Encoding
|
||||
http.useHTTP10(true);
|
||||
http.setTimeout(_httpClientTimeout);
|
||||
http.setFollowRedirects(_followRedirects);
|
||||
http.setUserAgent("ESP32-http-Update");
|
||||
http.addHeader("Cache-Control", "no-cache");
|
||||
http.addHeader("x-ESP32-STA-MAC", WiFi.macAddress());
|
||||
http.addHeader("x-ESP32-AP-MAC", WiFi.softAPmacAddress());
|
||||
http.addHeader("x-ESP32-free-space", String(ESP.getFreeSketchSpace()));
|
||||
http.addHeader("x-ESP32-sketch-size", String(ESP.getSketchSize()));
|
||||
String sketchMD5 = ESP.getSketchMD5();
|
||||
if(sketchMD5.length() != 0) {
|
||||
http.addHeader("x-ESP32-sketch-md5", sketchMD5);
|
||||
}
|
||||
// Add also a SHA256
|
||||
String sketchSHA256 = getSketchSHA256();
|
||||
if(sketchSHA256.length() != 0) {
|
||||
http.addHeader("x-ESP32-sketch-sha256", sketchSHA256);
|
||||
}
|
||||
http.addHeader("x-ESP32-chip-size", String(ESP.getFlashChipSize()));
|
||||
http.addHeader("x-ESP32-sdk-version", ESP.getSdkVersion());
|
||||
|
||||
if(spiffs) {
|
||||
http.addHeader("x-ESP32-mode", "spiffs");
|
||||
} else {
|
||||
http.addHeader("x-ESP32-mode", "sketch");
|
||||
}
|
||||
|
||||
if(currentVersion && currentVersion[0] != 0x00) {
|
||||
http.addHeader("x-ESP32-version", currentVersion);
|
||||
}
|
||||
|
||||
const char * headerkeys[] = { "x-MD5" };
|
||||
size_t headerkeyssize = sizeof(headerkeys) / sizeof(char*);
|
||||
|
||||
// track these headers
|
||||
http.collectHeaders(headerkeys, headerkeyssize);
|
||||
|
||||
uint32_t http_connect_time = millis();
|
||||
|
||||
int code = http.GET(); // 0 if ok or < 0 if error
|
||||
int len = http.getSize(); // -1 if no info or > 0 when Content-Length is set by server
|
||||
|
||||
// Add specific logging for Tasmota
|
||||
if (len < 0) { // -1 if no info or > 0 when Content-Length is set by server
|
||||
if (code <= -1000) { // BearSSL error 46 transformed to -1046
|
||||
AddLog(LOG_LEVEL_INFO, "OTA: TLS connection error %d after %d ms", -code - 1000, millis() - http_connect_time);
|
||||
} else if (code == -1) { // HTTPC_ERROR_CONNECTION_REFUSED
|
||||
AddLog(LOG_LEVEL_INFO, "OTA: Connection timeout after %d ms", millis() - http_connect_time);
|
||||
} else {
|
||||
AddLog(LOG_LEVEL_INFO, "OTA: Connection error %d after %d ms", code, millis() - http_connect_time);
|
||||
}
|
||||
} else {
|
||||
AddLog(LOG_LEVEL_DEBUG, PSTR("OTA: Connected in %d ms, stack low mark %d"),
|
||||
millis() - http_connect_time, uxTaskGetStackHighWaterMark(nullptr));
|
||||
}
|
||||
|
||||
if(code <= 0) {
|
||||
// log_e("HTTP error: %s\n", http.errorToString(code).c_str());
|
||||
_lastError = code;
|
||||
http.end();
|
||||
return HTTP_UPDATE_FAILED;
|
||||
}
|
||||
|
||||
|
||||
log_d("Header read fin.\n");
|
||||
log_d("Server header:\n");
|
||||
log_d(" - code: %d\n", code);
|
||||
log_d(" - len: %d\n", len);
|
||||
|
||||
if(http.hasHeader("x-MD5")) {
|
||||
log_d(" - MD5: %s\n", http.header("x-MD5").c_str());
|
||||
}
|
||||
|
||||
log_d("ESP32 info:\n");
|
||||
log_d(" - free Space: %d\n", ESP.getFreeSketchSpace());
|
||||
log_d(" - current Sketch Size: %d\n", ESP.getSketchSize());
|
||||
|
||||
if(currentVersion && currentVersion[0] != 0x00) {
|
||||
log_d(" - current version: %s\n", currentVersion.c_str() );
|
||||
}
|
||||
|
||||
switch(code) {
|
||||
case HTTP_CODE_OK: ///< OK (Start Update)
|
||||
if(len > 0) {
|
||||
bool startUpdate = true;
|
||||
if(spiffs) {
|
||||
const esp_partition_t* _partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_SPIFFS, NULL);
|
||||
if(!_partition){
|
||||
_lastError = HTTP_UE_NO_PARTITION;
|
||||
return HTTP_UPDATE_FAILED;
|
||||
}
|
||||
|
||||
if(len > _partition->size) {
|
||||
log_e("spiffsSize to low (%d) needed: %d\n", _partition->size, len);
|
||||
startUpdate = false;
|
||||
}
|
||||
} else {
|
||||
int sketchFreeSpace = ESP.getFreeSketchSpace();
|
||||
if(!sketchFreeSpace){
|
||||
_lastError = HTTP_UE_NO_PARTITION;
|
||||
return HTTP_UPDATE_FAILED;
|
||||
}
|
||||
|
||||
if(len > sketchFreeSpace) {
|
||||
log_e("FreeSketchSpace to low (%d) needed: %d\n", sketchFreeSpace, len);
|
||||
startUpdate = false;
|
||||
}
|
||||
}
|
||||
|
||||
if(!startUpdate) {
|
||||
_lastError = HTTP_UE_TOO_LESS_SPACE;
|
||||
ret = HTTP_UPDATE_FAILED;
|
||||
} else {
|
||||
// Warn main app we're starting up...
|
||||
if (_cbStart) {
|
||||
_cbStart();
|
||||
}
|
||||
|
||||
WiFiClient * tcp = http.getStreamPtr();
|
||||
|
||||
// To do? WiFiUDP::stopAll();
|
||||
// To do? WiFiClient::stopAllExcept(tcp);
|
||||
|
||||
delay(100);
|
||||
|
||||
int command;
|
||||
|
||||
if(spiffs) {
|
||||
command = U_SPIFFS;
|
||||
log_d("runUpdate spiffs...\n");
|
||||
} else {
|
||||
command = U_FLASH;
|
||||
log_d("runUpdate flash...\n");
|
||||
}
|
||||
|
||||
if(!spiffs) {
|
||||
/* To do
|
||||
uint8_t buf[4];
|
||||
if(tcp->peekBytes(&buf[0], 4) != 4) {
|
||||
log_e("peekBytes magic header failed\n");
|
||||
_lastError = HTTP_UE_BIN_VERIFY_HEADER_FAILED;
|
||||
http.end();
|
||||
return HTTP_UPDATE_FAILED;
|
||||
}
|
||||
*/
|
||||
|
||||
// check for valid first magic byte
|
||||
// if(buf[0] != 0xE9) {
|
||||
if(tcp->peek() != 0xE9) {
|
||||
log_e("Magic header does not start with 0xE9\n");
|
||||
_lastError = HTTP_UE_BIN_VERIFY_HEADER_FAILED;
|
||||
http.end();
|
||||
return HTTP_UPDATE_FAILED;
|
||||
|
||||
}
|
||||
/* To do
|
||||
uint32_t bin_flash_size = ESP.magicFlashChipSize((buf[3] & 0xf0) >> 4);
|
||||
|
||||
// check if new bin fits to SPI flash
|
||||
if(bin_flash_size > ESP.getFlashChipRealSize()) {
|
||||
log_e("New binary does not fit SPI Flash size\n");
|
||||
_lastError = HTTP_UE_BIN_FOR_WRONG_FLASH;
|
||||
http.end();
|
||||
return HTTP_UPDATE_FAILED;
|
||||
}
|
||||
*/
|
||||
}
|
||||
if(runUpdate(*tcp, len, http.header("x-MD5"), command)) {
|
||||
ret = HTTP_UPDATE_OK;
|
||||
log_d("Update ok\n");
|
||||
http.end();
|
||||
// Warn main app we're all done
|
||||
if (_cbEnd) {
|
||||
_cbEnd();
|
||||
}
|
||||
|
||||
if(_rebootOnUpdate && !spiffs) {
|
||||
ESP.restart();
|
||||
}
|
||||
|
||||
} else {
|
||||
ret = HTTP_UPDATE_FAILED;
|
||||
log_e("Update failed\n");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
_lastError = HTTP_UE_SERVER_NOT_REPORT_SIZE;
|
||||
ret = HTTP_UPDATE_FAILED;
|
||||
log_e("Content-Length was 0 or wasn't set by Server?!\n");
|
||||
}
|
||||
break;
|
||||
case HTTP_CODE_NOT_MODIFIED:
|
||||
///< Not Modified (No updates)
|
||||
ret = HTTP_UPDATE_NO_UPDATES;
|
||||
break;
|
||||
case HTTP_CODE_NOT_FOUND:
|
||||
_lastError = HTTP_UE_SERVER_FILE_NOT_FOUND;
|
||||
ret = HTTP_UPDATE_FAILED;
|
||||
break;
|
||||
case HTTP_CODE_FORBIDDEN:
|
||||
_lastError = HTTP_UE_SERVER_FORBIDDEN;
|
||||
ret = HTTP_UPDATE_FAILED;
|
||||
break;
|
||||
default:
|
||||
_lastError = HTTP_UE_SERVER_WRONG_HTTP_CODE;
|
||||
ret = HTTP_UPDATE_FAILED;
|
||||
AddLog(LOG_LEVEL_INFO, "OTA: unsupported HTTP return code %i", code);
|
||||
// log_e("HTTP Code is (%d)\n", code);
|
||||
break;
|
||||
}
|
||||
|
||||
http.end();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* write Update to flash
|
||||
* @param in Stream&
|
||||
* @param size uint32_t
|
||||
* @param md5 String
|
||||
* @return true if Update ok
|
||||
*/
|
||||
bool HTTPUpdateLight::runUpdate(Stream& in, uint32_t size, String md5, int command)
|
||||
{
|
||||
|
||||
StreamString error;
|
||||
|
||||
if (_cbProgress) {
|
||||
TasUpdate.onProgress(_cbProgress);
|
||||
}
|
||||
|
||||
// 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("TasUpdate.begin failed! (%s)\n", error.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_cbProgress) {
|
||||
_cbProgress(0, size);
|
||||
}
|
||||
|
||||
if(md5.length()) {
|
||||
if(!TasUpdate.setMD5(md5.c_str())) {
|
||||
_lastError = HTTP_UE_SERVER_FAULTY_MD5;
|
||||
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(TasUpdate.writeStream(in) != size) {
|
||||
_lastError = TasUpdate.getError();
|
||||
TasUpdate.printError(error);
|
||||
error.trim(); // remove line ending
|
||||
log_e("TasUpdate.writeStream failed! (%s)\n", error.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_cbProgress) {
|
||||
_cbProgress(size, size);
|
||||
}
|
||||
|
||||
if(!TasUpdate.end()) {
|
||||
_lastError = TasUpdate.getError();
|
||||
TasUpdate.printError(error);
|
||||
error.trim(); // remove line ending
|
||||
log_e("TasUpdate.end failed! (%s)\n", error.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_HTTPUPDATE)
|
||||
HTTPUpdateLight httpUpdateLight;
|
||||
#endif
|
||||
150
lib/libesp32/HttpClientLight/src/HTTPUpdateLight.h
Normal file
150
lib/libesp32/HttpClientLight/src/HTTPUpdateLight.h
Normal file
@@ -0,0 +1,150 @@
|
||||
/**
|
||||
*
|
||||
* @file HTTPUpdate.h based on ESP8266HTTPUpdate.h
|
||||
* @date 16.10.2018
|
||||
* @author Markus Sattler
|
||||
*
|
||||
* Copyright (c) 2015 Markus Sattler. All rights reserved.
|
||||
* This file is part of the ESP32 Http Updater.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ___HTTP_UPDATE_LIGHT_H___
|
||||
#define ___HTTP_UPDATE_LIGHT_H___
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <WiFi.h>
|
||||
#include <WiFiClient.h>
|
||||
#include <WiFiUdp.h>
|
||||
#include <HttpClientLight.h>
|
||||
#include <TasUpdate.h>
|
||||
#include <HTTPUpdate.h>
|
||||
|
||||
/// note we use HTTP client errors too so we start at 100
|
||||
// #define HTTP_UE_TOO_LESS_SPACE (-100)
|
||||
// #define HTTP_UE_SERVER_NOT_REPORT_SIZE (-101)
|
||||
// #define HTTP_UE_SERVER_FILE_NOT_FOUND (-102)
|
||||
// #define HTTP_UE_SERVER_FORBIDDEN (-103)
|
||||
// #define HTTP_UE_SERVER_WRONG_HTTP_CODE (-104)
|
||||
// #define HTTP_UE_SERVER_FAULTY_MD5 (-105)
|
||||
// #define HTTP_UE_BIN_VERIFY_HEADER_FAILED (-106)
|
||||
// #define HTTP_UE_BIN_FOR_WRONG_FLASH (-107)
|
||||
// #define HTTP_UE_NO_PARTITION (-108)
|
||||
|
||||
// enum HTTPUpdateResult {
|
||||
// HTTP_UPDATE_FAILED,
|
||||
// HTTP_UPDATE_NO_UPDATES,
|
||||
// HTTP_UPDATE_OK
|
||||
// };
|
||||
|
||||
// typedef HTTPUpdateResult t_httpUpdate_return; // backward compatibility
|
||||
|
||||
// using HTTPUpdateStartCB = std::function<void()>;
|
||||
// using HTTPUpdateEndCB = std::function<void()>;
|
||||
// using HTTPUpdateErrorCB = std::function<void(int)>;
|
||||
// using HTTPUpdateProgressCB = std::function<void(int, int)>;
|
||||
|
||||
class HTTPUpdateLight
|
||||
{
|
||||
public:
|
||||
HTTPUpdateLight(void);
|
||||
HTTPUpdateLight(int httpClientTimeout);
|
||||
~HTTPUpdateLight(void);
|
||||
|
||||
void rebootOnUpdate(bool reboot)
|
||||
{
|
||||
_rebootOnUpdate = reboot;
|
||||
}
|
||||
|
||||
/**
|
||||
* set redirect follow mode. See `followRedirects_t` enum for avaliable modes.
|
||||
* @param follow
|
||||
*/
|
||||
void setFollowRedirects(followRedirects_t follow)
|
||||
{
|
||||
_followRedirects = follow;
|
||||
}
|
||||
|
||||
void setLedPin(int ledPin = -1, uint8_t ledOn = HIGH)
|
||||
{
|
||||
_ledPin = ledPin;
|
||||
_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 = "/",
|
||||
// const String& currentVersion = "");
|
||||
|
||||
// t_httpUpdate_return updateSpiffs(WiFiClient& client, const String& url, const String& currentVersion = "");
|
||||
|
||||
t_httpUpdate_return update(HTTPClientLight& httpClient,
|
||||
const String& currentVersion = "");
|
||||
|
||||
// t_httpUpdate_return updateSpiffs(HTTPClient &httpClient, const String ¤tVersion = "");
|
||||
|
||||
// Notification callbacks
|
||||
void onStart(HTTPUpdateStartCB cbOnStart) { _cbStart = cbOnStart; }
|
||||
void onEnd(HTTPUpdateEndCB cbOnEnd) { _cbEnd = cbOnEnd; }
|
||||
void onError(HTTPUpdateErrorCB cbOnError) { _cbError = cbOnError; }
|
||||
void onProgress(HTTPUpdateProgressCB cbOnProgress) { _cbProgress = cbOnProgress; }
|
||||
|
||||
int getLastError(void);
|
||||
String getLastErrorString(void);
|
||||
|
||||
protected:
|
||||
t_httpUpdate_return handleUpdate(HTTPClientLight& http, const String& currentVersion, bool spiffs = false);
|
||||
bool runUpdate(Stream& in, uint32_t size, String md5, int command = U_FLASH);
|
||||
|
||||
// Set the error and potentially use a CB to notify the application
|
||||
void _setLastError(int err) {
|
||||
_lastError = err;
|
||||
if (_cbError) {
|
||||
_cbError(err);
|
||||
}
|
||||
}
|
||||
int _lastError;
|
||||
bool _rebootOnUpdate = true;
|
||||
private:
|
||||
int _httpClientTimeout;
|
||||
followRedirects_t _followRedirects;
|
||||
|
||||
// Callbacks
|
||||
HTTPUpdateStartCB _cbStart;
|
||||
HTTPUpdateEndCB _cbEnd;
|
||||
HTTPUpdateErrorCB _cbError;
|
||||
HTTPUpdateProgressCB _cbProgress;
|
||||
|
||||
int _ledPin;
|
||||
uint8_t _ledOn;
|
||||
// Start Tasmota Factory patch
|
||||
bool _factory;
|
||||
// End Tasmota Factory patch
|
||||
};
|
||||
|
||||
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_HTTPUPDATE)
|
||||
extern HTTPUpdateLight httpUpdateLight;
|
||||
#endif
|
||||
|
||||
#endif /* ___HTTP_UPDATE_LIGHT_H___ */
|
||||
1533
lib/libesp32/HttpClientLight/src/HttpClientLight.cpp
Normal file
1533
lib/libesp32/HttpClientLight/src/HttpClientLight.cpp
Normal file
File diff suppressed because it is too large
Load Diff
277
lib/libesp32/HttpClientLight/src/HttpClientLight.h
Normal file
277
lib/libesp32/HttpClientLight/src/HttpClientLight.h
Normal file
@@ -0,0 +1,277 @@
|
||||
/**
|
||||
* HTTPClientLight.h
|
||||
*
|
||||
* Created on: 02.11.2015
|
||||
*
|
||||
* Copyright (c) 2015 Markus Sattler. All rights reserved.
|
||||
* This file is part of the HTTPClient for Arduino.
|
||||
* Port to ESP32 by Evandro Luis Copercini (2017),
|
||||
* changed fingerprints to CA verification.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef HTTPClient_Light_H_
|
||||
#define HTTPClient_Light_H_
|
||||
|
||||
#define HTTPCLIENT_1_1_COMPATIBLE
|
||||
|
||||
#include <memory>
|
||||
#include <Arduino.h>
|
||||
#include <WiFiClient.h>
|
||||
#include <WiFiClientSecure.h>
|
||||
|
||||
#include <HTTPClient.h> // import definitions from the original code
|
||||
|
||||
#include "tasmota_options.h"
|
||||
|
||||
#define HTTPCLIENT_DEFAULT_TCP_TIMEOUT (5000)
|
||||
|
||||
/// HTTP client errors
|
||||
#define HTTPC_ERROR_CONNECTION_REFUSED (-1)
|
||||
#define HTTPC_ERROR_SEND_HEADER_FAILED (-2)
|
||||
#define HTTPC_ERROR_SEND_PAYLOAD_FAILED (-3)
|
||||
#define HTTPC_ERROR_NOT_CONNECTED (-4)
|
||||
#define HTTPC_ERROR_CONNECTION_LOST (-5)
|
||||
#define HTTPC_ERROR_NO_STREAM (-6)
|
||||
#define HTTPC_ERROR_NO_HTTP_SERVER (-7)
|
||||
#define HTTPC_ERROR_TOO_LESS_RAM (-8)
|
||||
#define HTTPC_ERROR_ENCODING (-9)
|
||||
#define HTTPC_ERROR_STREAM_WRITE (-10)
|
||||
#define HTTPC_ERROR_READ_TIMEOUT (-11)
|
||||
|
||||
/// size for the stream handling
|
||||
#define HTTP_TCP_BUFFER_SIZE (1460)
|
||||
|
||||
/// HTTP codes see RFC7231
|
||||
// typedef enum {
|
||||
// HTTP_CODE_CONTINUE = 100,
|
||||
// HTTP_CODE_SWITCHING_PROTOCOLS = 101,
|
||||
// HTTP_CODE_PROCESSING = 102,
|
||||
// HTTP_CODE_OK = 200,
|
||||
// HTTP_CODE_CREATED = 201,
|
||||
// HTTP_CODE_ACCEPTED = 202,
|
||||
// HTTP_CODE_NON_AUTHORITATIVE_INFORMATION = 203,
|
||||
// HTTP_CODE_NO_CONTENT = 204,
|
||||
// HTTP_CODE_RESET_CONTENT = 205,
|
||||
// HTTP_CODE_PARTIAL_CONTENT = 206,
|
||||
// HTTP_CODE_MULTI_STATUS = 207,
|
||||
// HTTP_CODE_ALREADY_REPORTED = 208,
|
||||
// HTTP_CODE_IM_USED = 226,
|
||||
// HTTP_CODE_MULTIPLE_CHOICES = 300,
|
||||
// HTTP_CODE_MOVED_PERMANENTLY = 301,
|
||||
// HTTP_CODE_FOUND = 302,
|
||||
// HTTP_CODE_SEE_OTHER = 303,
|
||||
// HTTP_CODE_NOT_MODIFIED = 304,
|
||||
// HTTP_CODE_USE_PROXY = 305,
|
||||
// HTTP_CODE_TEMPORARY_REDIRECT = 307,
|
||||
// HTTP_CODE_PERMANENT_REDIRECT = 308,
|
||||
// HTTP_CODE_BAD_REQUEST = 400,
|
||||
// HTTP_CODE_UNAUTHORIZED = 401,
|
||||
// HTTP_CODE_PAYMENT_REQUIRED = 402,
|
||||
// HTTP_CODE_FORBIDDEN = 403,
|
||||
// HTTP_CODE_NOT_FOUND = 404,
|
||||
// HTTP_CODE_METHOD_NOT_ALLOWED = 405,
|
||||
// HTTP_CODE_NOT_ACCEPTABLE = 406,
|
||||
// HTTP_CODE_PROXY_AUTHENTICATION_REQUIRED = 407,
|
||||
// HTTP_CODE_REQUEST_TIMEOUT = 408,
|
||||
// HTTP_CODE_CONFLICT = 409,
|
||||
// HTTP_CODE_GONE = 410,
|
||||
// HTTP_CODE_LENGTH_REQUIRED = 411,
|
||||
// HTTP_CODE_PRECONDITION_FAILED = 412,
|
||||
// HTTP_CODE_PAYLOAD_TOO_LARGE = 413,
|
||||
// HTTP_CODE_URI_TOO_LONG = 414,
|
||||
// HTTP_CODE_UNSUPPORTED_MEDIA_TYPE = 415,
|
||||
// HTTP_CODE_RANGE_NOT_SATISFIABLE = 416,
|
||||
// HTTP_CODE_EXPECTATION_FAILED = 417,
|
||||
// HTTP_CODE_MISDIRECTED_REQUEST = 421,
|
||||
// HTTP_CODE_UNPROCESSABLE_ENTITY = 422,
|
||||
// HTTP_CODE_LOCKED = 423,
|
||||
// HTTP_CODE_FAILED_DEPENDENCY = 424,
|
||||
// HTTP_CODE_UPGRADE_REQUIRED = 426,
|
||||
// HTTP_CODE_PRECONDITION_REQUIRED = 428,
|
||||
// HTTP_CODE_TOO_MANY_REQUESTS = 429,
|
||||
// HTTP_CODE_REQUEST_HEADER_FIELDS_TOO_LARGE = 431,
|
||||
// HTTP_CODE_INTERNAL_SERVER_ERROR = 500,
|
||||
// HTTP_CODE_NOT_IMPLEMENTED = 501,
|
||||
// HTTP_CODE_BAD_GATEWAY = 502,
|
||||
// HTTP_CODE_SERVICE_UNAVAILABLE = 503,
|
||||
// HTTP_CODE_GATEWAY_TIMEOUT = 504,
|
||||
// HTTP_CODE_HTTP_VERSION_NOT_SUPPORTED = 505,
|
||||
// HTTP_CODE_VARIANT_ALSO_NEGOTIATES = 506,
|
||||
// HTTP_CODE_INSUFFICIENT_STORAGE = 507,
|
||||
// HTTP_CODE_LOOP_DETECTED = 508,
|
||||
// HTTP_CODE_NOT_EXTENDED = 510,
|
||||
// HTTP_CODE_NETWORK_AUTHENTICATION_REQUIRED = 511
|
||||
// } t_http_codes;
|
||||
|
||||
// typedef enum {
|
||||
// HTTPC_TE_IDENTITY,
|
||||
// HTTPC_TE_CHUNKED
|
||||
// } transferEncoding_t;
|
||||
|
||||
/**
|
||||
* redirection follow mode.
|
||||
* + `HTTPC_DISABLE_FOLLOW_REDIRECTS` - no redirection will be followed.
|
||||
* + `HTTPC_STRICT_FOLLOW_REDIRECTS` - strict RFC2616, only requests using
|
||||
* GET or HEAD methods will be redirected (using the same method),
|
||||
* since the RFC requires end-user confirmation in other cases.
|
||||
* + `HTTPC_FORCE_FOLLOW_REDIRECTS` - all redirections will be followed,
|
||||
* regardless of a used method. New request will use the same method,
|
||||
* and they will include the same body data and the same headers.
|
||||
* In the sense of the RFC, it's just like every redirection is confirmed.
|
||||
*/
|
||||
// typedef enum {
|
||||
// HTTPC_DISABLE_FOLLOW_REDIRECTS,
|
||||
// HTTPC_STRICT_FOLLOW_REDIRECTS,
|
||||
// HTTPC_FORCE_FOLLOW_REDIRECTS
|
||||
// } followRedirects_t;
|
||||
|
||||
|
||||
#ifdef HTTPCLIENT_1_1_COMPATIBLE
|
||||
class TransportTraitsLight;
|
||||
typedef std::unique_ptr<TransportTraitsLight> TransportTraitsLightPtr;
|
||||
#endif
|
||||
|
||||
|
||||
class HTTPClientLight
|
||||
{
|
||||
public:
|
||||
HTTPClientLight();
|
||||
~HTTPClientLight();
|
||||
|
||||
/*
|
||||
* Since both begin() functions take a reference to client as a parameter, you need to
|
||||
* ensure the client object lives the entire time of the HTTPClientLight
|
||||
*/
|
||||
// bool begin(WiFiClient &client, String url);
|
||||
// bool begin(WiFiClient &client, String host, uint16_t port, String uri = "/", bool https = false);
|
||||
|
||||
#ifdef HTTPCLIENT_1_1_COMPATIBLE
|
||||
bool begin(String url);
|
||||
bool begin(String url, const char* CAcert);
|
||||
// bool begin(String host, uint16_t port, String uri = "/");
|
||||
// bool begin(String host, uint16_t port, String uri, const char* CAcert);
|
||||
// bool begin(String host, uint16_t port, String uri, const char* CAcert, const char* cli_cert, const char* cli_key);
|
||||
#endif
|
||||
|
||||
void end(void);
|
||||
|
||||
bool connected(void);
|
||||
|
||||
void setReuse(bool reuse); /// keep-alive
|
||||
void setUserAgent(const String& userAgent);
|
||||
void setAuthorization(const char * user, const char * password);
|
||||
void setAuthorization(const char * auth);
|
||||
void setConnectTimeout(int32_t connectTimeout);
|
||||
void setTimeout(uint16_t timeout);
|
||||
|
||||
// Redirections
|
||||
void setFollowRedirects(followRedirects_t follow);
|
||||
void setRedirectLimit(uint16_t limit); // max redirects to follow for a single request
|
||||
|
||||
bool setURL(const String &url);
|
||||
void useHTTP10(bool usehttp10 = true);
|
||||
|
||||
/// request handling
|
||||
int GET();
|
||||
int PATCH(uint8_t * payload, size_t size);
|
||||
int PATCH(String payload);
|
||||
int POST(uint8_t * payload, size_t size);
|
||||
int POST(String payload);
|
||||
int PUT(uint8_t * payload, size_t size);
|
||||
int PUT(String payload);
|
||||
int DELETE(uint8_t * payload, size_t size);
|
||||
int DELETE(String payload);
|
||||
int sendRequest(const char * type, String payload);
|
||||
int sendRequest(const char * type, uint8_t * payload = NULL, size_t size = 0);
|
||||
int sendRequest(const char * type, Stream * stream, size_t size = 0);
|
||||
|
||||
void addHeader(const String& name, const String& value, bool first = false, bool replace = true);
|
||||
|
||||
/// Response handling
|
||||
void collectHeaders(const char* headerKeys[], const size_t headerKeysCount);
|
||||
String header(const char* name); // get request header value by name
|
||||
String header(size_t i); // get request header value by number
|
||||
String headerName(size_t i); // get request header name by number
|
||||
int headers(); // get header count
|
||||
bool hasHeader(const char* name); // check if header exists
|
||||
|
||||
|
||||
int getSize(void);
|
||||
const String &getLocation(void);
|
||||
|
||||
WiFiClient& getStream(void);
|
||||
WiFiClient* getStreamPtr(void);
|
||||
int writeToStream(Stream* stream);
|
||||
String getString(void);
|
||||
|
||||
static String errorToString(int error);
|
||||
|
||||
protected:
|
||||
struct RequestArgument {
|
||||
String key;
|
||||
String value;
|
||||
};
|
||||
|
||||
bool beginInternal(String url, const char* expectedProtocol);
|
||||
void disconnect(bool preserveClient = false);
|
||||
void clear();
|
||||
int returnError(int error);
|
||||
bool connect(void);
|
||||
bool sendHeader(const char * type);
|
||||
int handleHeaderResponse();
|
||||
int writeToStreamDataBlock(Stream * stream, int len);
|
||||
|
||||
|
||||
#ifdef HTTPCLIENT_1_1_COMPATIBLE
|
||||
TransportTraitsLightPtr _transportTraits;
|
||||
std::unique_ptr<WiFiClient> _tcpDeprecated;
|
||||
#endif
|
||||
|
||||
WiFiClient* _client = nullptr;
|
||||
|
||||
/// request handling
|
||||
String _host;
|
||||
uint16_t _port = 0;
|
||||
int32_t _connectTimeout = HTTPCLIENT_DEFAULT_TCP_TIMEOUT; // Do not set to -1 as it fails WiFiClient connect()
|
||||
bool _reuse = true;
|
||||
uint16_t _tcpTimeout = HTTPCLIENT_DEFAULT_TCP_TIMEOUT;
|
||||
bool _useHTTP10 = false;
|
||||
bool _secure = false;
|
||||
|
||||
String _uri;
|
||||
String _protocol;
|
||||
String _headers;
|
||||
String _userAgent = "ESP32HTTPClient";
|
||||
String _base64Authorization;
|
||||
|
||||
/// Response handling
|
||||
RequestArgument* _currentHeaders = nullptr;
|
||||
size_t _headerKeysCount = 0;
|
||||
|
||||
int _returnCode = 0;
|
||||
int _size = -1;
|
||||
bool _canReuse = false;
|
||||
followRedirects_t _followRedirects = HTTPC_DISABLE_FOLLOW_REDIRECTS;
|
||||
uint16_t _redirectLimit = 10;
|
||||
String _location;
|
||||
transferEncoding_t _transferEncoding = HTTPC_TE_IDENTITY;
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif /* HTTPClient_Light_H_ */
|
||||
194
lib/libesp32/HttpClientLight/src/TasUpdate.h
Normal file
194
lib/libesp32/HttpClientLight/src/TasUpdate.h
Normal file
@@ -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
|
||||
404
lib/libesp32/HttpClientLight/src/TasUpdater.cpp
Normal file
404
lib/libesp32/HttpClientLight/src/TasUpdater.cpp
Normal file
@@ -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;
|
||||
Reference in New Issue
Block a user