ZCD Rollback to Arduino functions
This commit is contained in:
parent
ef05f6f997
commit
394ba748f5
|
|
@ -10,7 +10,7 @@ void WiFiEvent(WiFiEvent_t event) {
|
||||||
case IP_EVENT_STA_GOT_IP:
|
case IP_EVENT_STA_GOT_IP:
|
||||||
DPRINTF("WiFi connected, IP: %s\n", WiFi.localIP().toString().c_str());
|
DPRINTF("WiFi connected, IP: %s\n", WiFi.localIP().toString().c_str());
|
||||||
g_bWiFiHasBeenConnected = true;
|
g_bWiFiHasBeenConnected = true;
|
||||||
ledcWrite(PIN_LED_WIFI, PWM_FULL * 19 / 20); // LED_OFF
|
ledcWrite(PIN_LED_WIFI, PWM_FULL * 39 / 40); // LED_OFF
|
||||||
if (!g_bWiFiSetupExecuted) setupPostWiFi(false);
|
if (!g_bWiFiSetupExecuted) setupPostWiFi(false);
|
||||||
break;
|
break;
|
||||||
case WIFI_EVENT_STA_DISCONNECTED:
|
case WIFI_EVENT_STA_DISCONNECTED:
|
||||||
|
|
@ -74,7 +74,7 @@ void checkWiFi(unsigned long tickMillis) {
|
||||||
if (status == WL_DISCONNECTED && g_bWiFiHasBeenConnected) {
|
if (status == WL_DISCONNECTED && g_bWiFiHasBeenConnected) {
|
||||||
DPRINTLN("Attempting WiFi reconnection...");
|
DPRINTLN("Attempting WiFi reconnection...");
|
||||||
WiFi.reconnect();
|
WiFi.reconnect();
|
||||||
ledcWrite(PIN_LED_WIFI, PWM_FULL * 19 / 20); // Light Blink
|
ledcWrite(PIN_LED_WIFI, PWM_FULL * 39 / 40); // Light Blink
|
||||||
lastAttempt = tickMillis;
|
lastAttempt = tickMillis;
|
||||||
bConnecting = true;
|
bConnecting = true;
|
||||||
}
|
}
|
||||||
|
|
@ -93,7 +93,7 @@ void checkWiFi(unsigned long tickMillis) {
|
||||||
DPRINTF(" WiFi.Begin(%s,%s) returned %d\n", config.ssid, config.pw, ret);
|
DPRINTF(" WiFi.Begin(%s,%s) returned %d\n", config.ssid, config.pw, ret);
|
||||||
lastAttempt = tickMillis;
|
lastAttempt = tickMillis;
|
||||||
|
|
||||||
ledcWrite(PIN_LED_WIFI, PWM_FULL / 10); // Light Blink
|
ledcWrite(PIN_LED_WIFI, PWM_FULL / 20); // Light Blink
|
||||||
lastAttempt = tickMillis;
|
lastAttempt = tickMillis;
|
||||||
bConnecting = true;
|
bConnecting = true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,291 +0,0 @@
|
||||||
/*
|
|
||||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
|
||||||
*/
|
|
||||||
#if defined(ESP32)
|
|
||||||
#ifndef ESP32UPDATER_H
|
|
||||||
#define ESP32UPDATER_H
|
|
||||||
|
|
||||||
#include <Arduino.h>
|
|
||||||
#include <MD5Builder.h>
|
|
||||||
#include <functional>
|
|
||||||
#include <WiFiClientSecure.h>
|
|
||||||
#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_ERROR_DECRYPT (13)
|
|
||||||
|
|
||||||
#define UPDATE_SIZE_UNKNOWN 0xFFFFFFFF
|
|
||||||
|
|
||||||
#define U_FLASH 0
|
|
||||||
#define U_SPIFFS 100
|
|
||||||
#define U_AUTH 200
|
|
||||||
|
|
||||||
#define ENCRYPTED_BLOCK_SIZE 16
|
|
||||||
#define ENCRYPTED_TWEAK_BLOCK_SIZE 32
|
|
||||||
#define ENCRYPTED_KEY_SIZE 32
|
|
||||||
|
|
||||||
#define U_AES_DECRYPT_NONE 0
|
|
||||||
#define U_AES_DECRYPT_AUTO 1
|
|
||||||
#define U_AES_DECRYPT_ON 2
|
|
||||||
#define U_AES_DECRYPT_MODE_MASK 3
|
|
||||||
#define U_AES_IMAGE_DECRYPTING_BIT 4
|
|
||||||
|
|
||||||
#define SPI_SECTORS_PER_BLOCK 16 // usually large erase block is 32k/64k
|
|
||||||
#define SPI_FLASH_BLOCK_SIZE (SPI_SECTORS_PER_BLOCK * SPI_FLASH_SEC_SIZE)
|
|
||||||
|
|
||||||
enum HTTPUpdateResult {
|
|
||||||
HTTP_UPDATE_FAILED,
|
|
||||||
HTTP_UPDATE_NO_UPDATES,
|
|
||||||
HTTP_UPDATE_OK
|
|
||||||
};
|
|
||||||
|
|
||||||
class UpdateClass {
|
|
||||||
public:
|
|
||||||
typedef std::function<void(size_t, size_t)> THandlerFunction_Progress;
|
|
||||||
|
|
||||||
UpdateClass();
|
|
||||||
UpdateClass(int httpClientTimeout);
|
|
||||||
|
|
||||||
int update(WiFiClientSecure& client, String &url, uint16_t port, String& uri,
|
|
||||||
String ¤tVersion, short nDeviceType, bool bForceUpdate);
|
|
||||||
int handleUpdate(HTTPClient& http, const String& currentVersion, short nDeviceType);
|
|
||||||
int http_downloadUpdate(HTTPClient &httpClient);
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
This callback will be called when Update is receiving data
|
|
||||||
*/
|
|
||||||
UpdateClass &onProgress(THandlerFunction_Progress fn);
|
|
||||||
|
|
||||||
/*
|
|
||||||
Call this to check the space needed for the update
|
|
||||||
Will return false if there is not enough space
|
|
||||||
*/
|
|
||||||
bool begin(size_t size = UPDATE_SIZE_UNKNOWN, int command = U_FLASH, int ledPin = -1, uint8_t ledOn = LOW, const char *label = NULL);
|
|
||||||
|
|
||||||
/*
|
|
||||||
Setup decryption configuration
|
|
||||||
Crypt Key is 32bytes(256bits) block of data, use the same key as used to encrypt image file
|
|
||||||
Crypt Address, use the same value as used to encrypt image file
|
|
||||||
Crypt Config, use the same value as used to encrypt image file
|
|
||||||
Crypt Mode, used to select if image files should be decrypted or not
|
|
||||||
*/
|
|
||||||
bool setupCrypt(const uint8_t *cryptKey = 0, size_t cryptAddress = 0, uint8_t cryptConfig = 0xf, int cryptMode = U_AES_DECRYPT_AUTO);
|
|
||||||
|
|
||||||
/*
|
|
||||||
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 helpful when you update without knowing the final size first
|
|
||||||
*/
|
|
||||||
bool end(bool evenIfRemaining = false);
|
|
||||||
|
|
||||||
/*
|
|
||||||
sets AES256 key(32 bytes) used for decrypting image file
|
|
||||||
*/
|
|
||||||
bool setCryptKey(const uint8_t *cryptKey);
|
|
||||||
|
|
||||||
/*
|
|
||||||
sets crypt mode used on image files
|
|
||||||
*/
|
|
||||||
bool setCryptMode(const int cryptMode);
|
|
||||||
|
|
||||||
/*
|
|
||||||
sets address used for decrypting image file
|
|
||||||
*/
|
|
||||||
void setCryptAddress(const size_t cryptAddress) {
|
|
||||||
_cryptAddress = cryptAddress & 0x00fffff0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
sets crypt config used for decrypting image file
|
|
||||||
*/
|
|
||||||
void setCryptConfig(const uint8_t cryptConfig) {
|
|
||||||
_cryptCfg = cryptConfig & 0x0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
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);
|
|
||||||
void _cryptKeyTweak(size_t cryptAddress, uint8_t *tweaked_key);
|
|
||||||
bool _decryptBuffer();
|
|
||||||
bool _writeBuffer();
|
|
||||||
bool _verifyHeader(uint8_t data);
|
|
||||||
bool _verifyEnd();
|
|
||||||
bool _enablePartition(const esp_partition_t *partition);
|
|
||||||
bool _chkDataInBlock(const uint8_t *data, size_t len) const; // check if block contains any data or is empty
|
|
||||||
|
|
||||||
int _httpClientTimeout;
|
|
||||||
followRedirects_t _followRedirects = HTTPC_DISABLE_FOLLOW_REDIRECTS;
|
|
||||||
uint8_t _error;
|
|
||||||
uint8_t *_cryptKey;
|
|
||||||
uint8_t *_cryptBuffer;
|
|
||||||
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;
|
|
||||||
|
|
||||||
uint8_t _cryptMode;
|
|
||||||
size_t _cryptAddress;
|
|
||||||
uint8_t _cryptCfg;
|
|
||||||
};
|
|
||||||
|
|
||||||
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_UPDATE)
|
|
||||||
extern UpdateClass Update;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif // defined(ESP32UPDATER_H)
|
|
||||||
#endif // defined(ESP32)
|
|
||||||
|
|
@ -1,787 +0,0 @@
|
||||||
#if defined(ESP32)
|
|
||||||
/*
|
|
||||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
|
||||||
*/
|
|
||||||
#include "HermitCrab.h"
|
|
||||||
|
|
||||||
#include "Arduino.h"
|
|
||||||
#include "spi_flash_mmap.h"
|
|
||||||
#include "esp_ota_ops.h"
|
|
||||||
#include "esp_image_format.h"
|
|
||||||
#include "mbedtls/aes.h"
|
|
||||||
#include <WiFi.h>
|
|
||||||
#include <WiFiClient.h>
|
|
||||||
#include <HTTPClient.h>
|
|
||||||
#include "HCUpdate.h"
|
|
||||||
|
|
||||||
#define TAG_FW "FW Upate"
|
|
||||||
|
|
||||||
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");
|
|
||||||
} else if (_error == UPDATE_ERROR_DECRYPT) {
|
|
||||||
return ("Decryption error");
|
|
||||||
}
|
|
||||||
return ("UNKNOWN");
|
|
||||||
}
|
|
||||||
|
|
||||||
int UpdateClass::update(WiFiClientSecure& client, String& host, uint16_t port, String&uri, String& currentVersion, short nDeviceType, bool bForceUpdate)
|
|
||||||
{
|
|
||||||
HTTPClient http;
|
|
||||||
if (!http.begin(client, host, port, uri)) {
|
|
||||||
ESP_LOGI(TAG_FW,"OTA - httpClient begin failed\n");
|
|
||||||
return HTTP_UPDATE_FAILED;
|
|
||||||
}
|
|
||||||
int size = handleUpdate(http, currentVersion, nDeviceType);
|
|
||||||
|
|
||||||
ESP_LOGI(TAG_FW,"OTA - size(%d) Server Version: %s Mode: %s\n", size, http.header("version").c_str(),
|
|
||||||
http.header("update") && http.header("update").toInt() == 1 ? "Download" : "Check Only" );
|
|
||||||
|
|
||||||
//is there an image to download
|
|
||||||
if (size >= 0) {
|
|
||||||
if (!http.header("update") || http.header("update").toInt() == 0) {
|
|
||||||
ESP_LOGI(TAG_FW,"OTA - No Firmware available");
|
|
||||||
} else if (!http.header("version") || strcmp(http.header("version").c_str(), HC__VERSION) <= 0) {
|
|
||||||
ESP_LOGI(TAG_FW,"OTA - Firmware is upto Date");
|
|
||||||
} else {
|
|
||||||
//image avaliabe to download & update
|
|
||||||
if (!bForceUpdate) {
|
|
||||||
ESP_LOGI(TAG_FW,"OTA - Found V%s Firmware\n", http.header("version").c_str());
|
|
||||||
} else {
|
|
||||||
ESP_LOGI(TAG_FW,"OTA - Downloading & Installing V%s Firmware\n", http.header("version").c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bForceUpdate) {
|
|
||||||
if (http_downloadUpdate(http) == 0) {
|
|
||||||
http.end(); //end connection
|
|
||||||
ESP_LOGI(TAG_FW,"OTA - Firmware Update Successful, rebooting");
|
|
||||||
ESP.restart();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
http.end(); //end connection
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int UpdateClass::handleUpdate(HTTPClient& http, const String& currentVersion, short nDeviceType)
|
|
||||||
{
|
|
||||||
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-DEVICE-TYPE", String(nDeviceType));
|
|
||||||
http.addHeader("X-ESP32-VERSION", HC__VERSION);
|
|
||||||
http.addHeader("X-ESP32-STA-MAC", WiFi.macAddress());
|
|
||||||
|
|
||||||
unsigned long nChipId = 0;
|
|
||||||
for (int i = 0; i < 17; i = i + 8) {
|
|
||||||
nChipId |= ((ESP.getEfuseMac() >> (40 - i)) & 0xff) << i;
|
|
||||||
}
|
|
||||||
http.addHeader(F("X-ESP32-Chip-ID"), String(nChipId));
|
|
||||||
http.addHeader(F("x-ESP32-free-space"), String(ESP.getFreeSketchSpace()));
|
|
||||||
http.addHeader(F("x-ESP32-sketch-size"), String(ESP.getSketchSize()));
|
|
||||||
http.addHeader(F("x-ESP32-sketch-md5"), String(ESP.getSketchMD5()));
|
|
||||||
//http.addHeader(F("x-ESP32-chip-size"), String(ESP.getFlashChipRealSize()));
|
|
||||||
http.addHeader(F("x-ESP32-sdk-version"), ESP.getSdkVersion());
|
|
||||||
|
|
||||||
//set headers to look for to get returned values in servers http response to our http request
|
|
||||||
const char *headerkeys[] = {"update", "version"}; //server returns update 0=no update found, 1=update found, version=version of update found
|
|
||||||
size_t headerkeyssize = sizeof(headerkeys) / sizeof(char *);
|
|
||||||
http.collectHeaders(headerkeys, headerkeyssize);
|
|
||||||
|
|
||||||
int code = http.GET();
|
|
||||||
int len = http.getSize();
|
|
||||||
|
|
||||||
if (code == HTTP_CODE_OK) {
|
|
||||||
return (len > 0 ? len : 0); //return 0 or length of image to download
|
|
||||||
} else if (code < 0) {
|
|
||||||
ESP_LOGI(TAG_FW,"Error: %s\n", http.errorToString(code).c_str());
|
|
||||||
ESP_LOGI(TAG_FW, "%s", http.getString());
|
|
||||||
return code; //error code should be minus between -1 to -11
|
|
||||||
} else {
|
|
||||||
ESP_LOGI(TAG_FW,"Error: HTTP Server response code %i\n", code);
|
|
||||||
ESP_LOGI(TAG_FW, "%s", http.getString());
|
|
||||||
return -code; //return code should be minus between -100 to -511
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
int UpdateClass::http_downloadUpdate(HTTPClient &httpClient) {
|
|
||||||
WiFiClient *stream = httpClient.getStreamPtr();
|
|
||||||
int written = 0;
|
|
||||||
|
|
||||||
// Check content length and begin update
|
|
||||||
int fileSize = httpClient.getSize();
|
|
||||||
if (fileSize <= 0) {
|
|
||||||
ESP_LOGI(TAG_FW,"Invalid content length");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
if (!Update.begin(fileSize, U_FLASH)) {
|
|
||||||
ESP_LOGI(TAG_FW,"Update begin failed!");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Download and write the update
|
|
||||||
while (httpClient.connected() && written < fileSize) {
|
|
||||||
size_t availableBytes = stream->available();
|
|
||||||
if (availableBytes) {
|
|
||||||
uint8_t buffer[128]; // Buffer to hold incoming data
|
|
||||||
int bytesRead = stream->readBytes(buffer, min(availableBytes, sizeof(buffer)));
|
|
||||||
int bytesWritten = Update.write(buffer, bytesRead);
|
|
||||||
if (bytesWritten != bytesRead) {
|
|
||||||
ESP_LOGI(TAG_FW,"Update write failed!");
|
|
||||||
Update.end();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
written += bytesWritten;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Finalize update
|
|
||||||
if (Update.end()) {
|
|
||||||
if (Update.isFinished()) {
|
|
||||||
ESP_LOGI(TAG_FW,"Update successful!");
|
|
||||||
return 0;
|
|
||||||
} else {
|
|
||||||
ESP_LOGI(TAG_FW,"Update not finished!");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ESP_LOGI(TAG_FW,"Update end failed!");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
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)
|
|
||||||
, _cryptKey(0)
|
|
||||||
, _cryptBuffer(0)
|
|
||||||
, _buffer(0)
|
|
||||||
, _skipBuffer(0)
|
|
||||||
, _bufferLen(0)
|
|
||||||
, _size(0)
|
|
||||||
, _progress_callback(NULL)
|
|
||||||
, _progress(0)
|
|
||||||
, _paroffset(0)
|
|
||||||
, _command(U_FLASH)
|
|
||||||
, _partition(NULL)
|
|
||||||
, _cryptMode(U_AES_DECRYPT_AUTO)
|
|
||||||
, _cryptAddress(0)
|
|
||||||
, _cryptCfg(0xf)
|
|
||||||
, _httpClientTimeout(8000)
|
|
||||||
{}
|
|
||||||
|
|
||||||
UpdateClass::UpdateClass(int httpClientTimeout)
|
|
||||||
: _error(0)
|
|
||||||
, _cryptKey(0)
|
|
||||||
, _cryptBuffer(0)
|
|
||||||
, _buffer(0)
|
|
||||||
, _skipBuffer(0)
|
|
||||||
, _bufferLen(0)
|
|
||||||
, _size(0)
|
|
||||||
, _progress_callback(NULL)
|
|
||||||
, _progress(0)
|
|
||||||
, _paroffset(0)
|
|
||||||
, _command(U_FLASH)
|
|
||||||
, _partition(NULL)
|
|
||||||
, _cryptMode(U_AES_DECRYPT_AUTO)
|
|
||||||
, _cryptAddress(0)
|
|
||||||
, _cryptCfg(0xf)
|
|
||||||
, _httpClientTimeout(httpClientTimeout)
|
|
||||||
{}
|
|
||||||
|
|
||||||
UpdateClass &UpdateClass::onProgress(THandlerFunction_Progress fn) {
|
|
||||||
_progress_callback = fn;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
void UpdateClass::_reset() {
|
|
||||||
if (_buffer) {
|
|
||||||
delete[] _buffer;
|
|
||||||
}
|
|
||||||
if (_skipBuffer) {
|
|
||||||
delete[] _skipBuffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
_cryptBuffer = nullptr;
|
|
||||||
_buffer = nullptr;
|
|
||||||
_skipBuffer = nullptr;
|
|
||||||
_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, const char *label) {
|
|
||||||
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) {
|
|
||||||
_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, 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 = new (std::nothrow) uint8_t[SPI_FLASH_SEC_SIZE];
|
|
||||||
if (!_buffer) {
|
|
||||||
log_e("_buffer allocation failed");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
_size = size;
|
|
||||||
_command = command;
|
|
||||||
_md5.begin();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool UpdateClass::setupCrypt(const uint8_t *cryptKey, size_t cryptAddress, uint8_t cryptConfig, int cryptMode) {
|
|
||||||
if (setCryptKey(cryptKey)) {
|
|
||||||
if (setCryptMode(cryptMode)) {
|
|
||||||
setCryptAddress(cryptAddress);
|
|
||||||
setCryptConfig(cryptConfig);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool UpdateClass::setCryptKey(const uint8_t *cryptKey) {
|
|
||||||
if (!cryptKey) {
|
|
||||||
if (_cryptKey) {
|
|
||||||
delete[] _cryptKey;
|
|
||||||
_cryptKey = 0;
|
|
||||||
log_d("AES key unset");
|
|
||||||
}
|
|
||||||
return false; //key cleared, no key to decrypt with
|
|
||||||
}
|
|
||||||
//initialize
|
|
||||||
if (!_cryptKey) {
|
|
||||||
_cryptKey = new (std::nothrow) uint8_t[ENCRYPTED_KEY_SIZE];
|
|
||||||
}
|
|
||||||
if (!_cryptKey) {
|
|
||||||
log_e("new failed");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
memcpy(_cryptKey, cryptKey, ENCRYPTED_KEY_SIZE);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool UpdateClass::setCryptMode(const int cryptMode) {
|
|
||||||
if (cryptMode >= U_AES_DECRYPT_NONE && cryptMode <= U_AES_DECRYPT_ON) {
|
|
||||||
_cryptMode = cryptMode;
|
|
||||||
} else {
|
|
||||||
log_e("bad crypt mode argument %i", cryptMode);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void UpdateClass::_abort(uint8_t err) {
|
|
||||||
_reset();
|
|
||||||
_error = err;
|
|
||||||
}
|
|
||||||
|
|
||||||
void UpdateClass::abort() {
|
|
||||||
_abort(UPDATE_ERROR_ABORT);
|
|
||||||
}
|
|
||||||
|
|
||||||
void UpdateClass::_cryptKeyTweak(size_t cryptAddress, uint8_t *tweaked_key) {
|
|
||||||
memcpy(tweaked_key, _cryptKey, ENCRYPTED_KEY_SIZE);
|
|
||||||
if (_cryptCfg == 0) {
|
|
||||||
return; //no tweaking needed, use crypt key as-is
|
|
||||||
}
|
|
||||||
|
|
||||||
const uint8_t pattern[] = {23, 23, 23, 14, 23, 23, 23, 12, 23, 23, 23, 10, 23, 23, 23, 8};
|
|
||||||
int pattern_idx = 0;
|
|
||||||
int key_idx = 0;
|
|
||||||
int bit_len = 0;
|
|
||||||
uint32_t tweak = 0;
|
|
||||||
cryptAddress &= 0x00ffffe0; //bit 23-5
|
|
||||||
cryptAddress <<= 8; //bit23 shifted to bit31(MSB)
|
|
||||||
while (pattern_idx < sizeof(pattern)) {
|
|
||||||
tweak = cryptAddress << (23 - pattern[pattern_idx]); //bit shift for small patterns
|
|
||||||
// alternative to: tweak = rotl32(tweak,8 - bit_len);
|
|
||||||
tweak = (tweak << (8 - bit_len)) | (tweak >> (24 + bit_len)); //rotate to line up with end of previous tweak bits
|
|
||||||
bit_len += pattern[pattern_idx++] - 4; //add number of bits in next pattern(23-4 = 19bits = 23bit to 5bit)
|
|
||||||
while (bit_len > 7) {
|
|
||||||
tweaked_key[key_idx++] ^= tweak; //XOR byte
|
|
||||||
// alternative to: tweak = rotl32(tweak, 8);
|
|
||||||
tweak = (tweak << 8) | (tweak >> 24); //compiler should optimize to use rotate(fast)
|
|
||||||
bit_len -= 8;
|
|
||||||
}
|
|
||||||
tweaked_key[key_idx] ^= tweak; //XOR remaining bits, will XOR zeros if no remaining bits
|
|
||||||
}
|
|
||||||
if (_cryptCfg == 0xf) {
|
|
||||||
return; //return with fully tweaked key
|
|
||||||
}
|
|
||||||
|
|
||||||
//some of tweaked key bits need to be restore back to crypt key bits
|
|
||||||
const uint8_t cfg_bits[] = {67, 65, 63, 61};
|
|
||||||
key_idx = 0;
|
|
||||||
pattern_idx = 0;
|
|
||||||
while (key_idx < ENCRYPTED_KEY_SIZE) {
|
|
||||||
bit_len += cfg_bits[pattern_idx];
|
|
||||||
if ((_cryptCfg & (1 << pattern_idx)) == 0) { //restore crypt key bits
|
|
||||||
while (bit_len > 0) {
|
|
||||||
if (bit_len > 7 || ((_cryptCfg & (2 << pattern_idx)) == 0)) { //restore a crypt key byte
|
|
||||||
tweaked_key[key_idx] = _cryptKey[key_idx];
|
|
||||||
} else { //MSBits restore crypt key bits, LSBits keep as tweaked bits
|
|
||||||
tweaked_key[key_idx] &= (0xff >> bit_len);
|
|
||||||
tweaked_key[key_idx] |= (_cryptKey[key_idx] & (~(0xff >> bit_len)));
|
|
||||||
}
|
|
||||||
key_idx++;
|
|
||||||
bit_len -= 8;
|
|
||||||
}
|
|
||||||
} else { //keep tweaked key bits
|
|
||||||
while (bit_len > 0) {
|
|
||||||
if (bit_len < 8 && ((_cryptCfg & (2 << pattern_idx)) == 0)) { //MSBits keep as tweaked bits, LSBits restore crypt key bits
|
|
||||||
tweaked_key[key_idx] &= (~(0xff >> bit_len));
|
|
||||||
tweaked_key[key_idx] |= (_cryptKey[key_idx] & (0xff >> bit_len));
|
|
||||||
}
|
|
||||||
key_idx++;
|
|
||||||
bit_len -= 8;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pattern_idx++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool UpdateClass::_decryptBuffer() {
|
|
||||||
if (!_cryptKey) {
|
|
||||||
log_w("AES key not set");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (_bufferLen % ENCRYPTED_BLOCK_SIZE != 0) {
|
|
||||||
log_e("buffer size error");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!_cryptBuffer) {
|
|
||||||
_cryptBuffer = new (std::nothrow) uint8_t[ENCRYPTED_BLOCK_SIZE];
|
|
||||||
}
|
|
||||||
if (!_cryptBuffer) {
|
|
||||||
log_e("new failed");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
uint8_t tweaked_key[ENCRYPTED_KEY_SIZE]; //tweaked crypt key
|
|
||||||
int done = 0;
|
|
||||||
|
|
||||||
/*
|
|
||||||
Mbedtls functions will be replaced with esp_aes functions when hardware acceleration is available
|
|
||||||
|
|
||||||
To Do:
|
|
||||||
Replace mbedtls for the cases where there's no hardware acceleration
|
|
||||||
*/
|
|
||||||
|
|
||||||
mbedtls_aes_context ctx; //initialize AES
|
|
||||||
mbedtls_aes_init(&ctx);
|
|
||||||
while ((_bufferLen - done) >= ENCRYPTED_BLOCK_SIZE) {
|
|
||||||
for (int i = 0; i < ENCRYPTED_BLOCK_SIZE; i++) {
|
|
||||||
_cryptBuffer[(ENCRYPTED_BLOCK_SIZE - 1) - i] = _buffer[i + done]; //reverse order 16 bytes to decrypt
|
|
||||||
}
|
|
||||||
if (((_cryptAddress + _progress + done) % ENCRYPTED_TWEAK_BLOCK_SIZE) == 0 || done == 0) {
|
|
||||||
_cryptKeyTweak(_cryptAddress + _progress + done, tweaked_key); //update tweaked crypt key
|
|
||||||
if (mbedtls_aes_setkey_enc(&ctx, tweaked_key, 256)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (mbedtls_aes_setkey_dec(&ctx, tweaked_key, 256)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (mbedtls_aes_crypt_ecb(&ctx, MBEDTLS_AES_ENCRYPT, _cryptBuffer, _cryptBuffer)) { //use MBEDTLS_AES_ENCRYPT to decrypt flash code
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
for (int i = 0; i < ENCRYPTED_BLOCK_SIZE; i++) {
|
|
||||||
_buffer[i + done] = _cryptBuffer[(ENCRYPTED_BLOCK_SIZE - 1) - i]; //reverse order 16 bytes from decrypt
|
|
||||||
}
|
|
||||||
done += ENCRYPTED_BLOCK_SIZE;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool UpdateClass::_writeBuffer() {
|
|
||||||
//first bytes of loading image, check to see if loading image needs decrypting
|
|
||||||
if (!_progress) {
|
|
||||||
_cryptMode &= U_AES_DECRYPT_MODE_MASK;
|
|
||||||
if ((_cryptMode == U_AES_DECRYPT_ON) || ((_command == U_FLASH) && (_cryptMode & U_AES_DECRYPT_AUTO) && (_buffer[0] != ESP_IMAGE_HEADER_MAGIC))) {
|
|
||||||
_cryptMode |= U_AES_IMAGE_DECRYPTING_BIT; //set to decrypt the loading image
|
|
||||||
log_d("Decrypting OTA Image");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//check if data in buffer needs decrypting
|
|
||||||
if (_cryptMode & U_AES_IMAGE_DECRYPTING_BIT) {
|
|
||||||
if (!_decryptBuffer()) {
|
|
||||||
_abort(UPDATE_ERROR_DECRYPT);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//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 = new (std::nothrow) uint8_t[skip];
|
|
||||||
if (!_skipBuffer) {
|
|
||||||
log_e("_skipBuffer allocation failed");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
memcpy(_skipBuffer, _buffer, skip);
|
|
||||||
}
|
|
||||||
if (!_progress && _progress_callback) {
|
|
||||||
_progress_callback(0, _size);
|
|
||||||
}
|
|
||||||
size_t offset = _partition->address + _progress;
|
|
||||||
bool block_erase =
|
|
||||||
(_size - _progress >= SPI_FLASH_BLOCK_SIZE) && (offset % SPI_FLASH_BLOCK_SIZE == 0); // if it's the block boundary, than erase the whole block from here
|
|
||||||
bool part_head_sectors =
|
|
||||||
_partition->address % SPI_FLASH_BLOCK_SIZE
|
|
||||||
&& offset < (_partition->address / SPI_FLASH_BLOCK_SIZE + 1) * SPI_FLASH_BLOCK_SIZE; // sector belong to unaligned partition heading block
|
|
||||||
bool part_tail_sectors =
|
|
||||||
offset >= (_partition->address + _size) / SPI_FLASH_BLOCK_SIZE * SPI_FLASH_BLOCK_SIZE; // sector belong to unaligned partition tailing block
|
|
||||||
if (block_erase || part_head_sectors || part_tail_sectors) {
|
|
||||||
if (!ESP.partitionEraseRange(_partition, _progress, block_erase ? SPI_FLASH_BLOCK_SIZE : SPI_FLASH_SEC_SIZE)) {
|
|
||||||
_abort(UPDATE_ERROR_ERASE);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// try to skip empty blocks on unencrypted partitions
|
|
||||||
if ((_partition->encrypted || _chkDataInBlock(_buffer + skip / sizeof(uint32_t), _bufferLen - skip))
|
|
||||||
&& !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;
|
|
||||||
_target_md5.toLowerCase();
|
|
||||||
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;
|
|
||||||
int timeout_failures = 0;
|
|
||||||
|
|
||||||
if (hasError() || !isRunning()) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_command == U_FLASH && !_cryptMode) {
|
|
||||||
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;
|
|
||||||
|
|
||||||
#if CONFIG_FREERTOS_UNICORE
|
|
||||||
delay(1); // Fix solo WDT
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
return written;
|
|
||||||
}
|
|
||||||
|
|
||||||
void UpdateClass::printError(Print &out) {
|
|
||||||
out.println(_err2str(_error));
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *UpdateClass::errorString() {
|
|
||||||
return _err2str(_error);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool UpdateClass::_chkDataInBlock(const uint8_t *data, size_t len) const {
|
|
||||||
// check 32-bit aligned blocks only
|
|
||||||
if (!len || len % sizeof(uint32_t)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t dwl = len / sizeof(uint32_t);
|
|
||||||
|
|
||||||
do {
|
|
||||||
if (*(uint32_t *)data ^ 0xffffffff) { // for SPI NOR flash empty blocks are all one's, i.e. filled with 0xff byte
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
data += sizeof(uint32_t);
|
|
||||||
} while (--dwl);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_UPDATE)
|
|
||||||
UpdateClass Update;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif // defined(ESP32)
|
|
||||||
2
OTA.cpp
2
OTA.cpp
|
|
@ -17,7 +17,7 @@
|
||||||
// OTA
|
// OTA
|
||||||
//
|
//
|
||||||
// ==============================================================
|
// ==============================================================
|
||||||
const char *HC__VERSION = "20260418016";
|
const char *HC__VERSION = "20260418036";
|
||||||
#define UPDATE_PORT ((uint16_t) 443)
|
#define UPDATE_PORT ((uint16_t) 443)
|
||||||
const char *url = "visionsoft.kr";
|
const char *url = "visionsoft.kr";
|
||||||
const char *uri = "/sc/pages/firmware_download.php";
|
const char *uri = "/sc/pages/firmware_download.php";
|
||||||
|
|
|
||||||
135
OTA.cpp.backup
135
OTA.cpp.backup
|
|
@ -1,135 +0,0 @@
|
||||||
#define NO_GLOBAL_UPDATE
|
|
||||||
#include <string.h>
|
|
||||||
#include <WiFi.h>
|
|
||||||
#include <WiFiClientSecure.h>
|
|
||||||
#include <HTTPClient.h>
|
|
||||||
#include <esp_task_wdt.h>
|
|
||||||
|
|
||||||
#include "HCUpdate.h"
|
|
||||||
#include "HermitCrab.h"
|
|
||||||
#include "Config.h"
|
|
||||||
#include "ConnectWiFi.h"
|
|
||||||
|
|
||||||
#define TAG_OTA "OTA"
|
|
||||||
|
|
||||||
// ==============================================================
|
|
||||||
//
|
|
||||||
// OTA
|
|
||||||
//
|
|
||||||
// ==============================================================
|
|
||||||
const char *HC__VERSION = "20250405001";
|
|
||||||
#define UPDATE_PORT ((uint16_t) 443)
|
|
||||||
String url = "visionsoft.kr";
|
|
||||||
String uri = "/hc/hc_firmware_update.php";
|
|
||||||
const char *HTTPUPDATE_USERAGRENT = "ESP32-http-Update";
|
|
||||||
const char *COMPANY_NAME = "VisionSoft";
|
|
||||||
const char *SERVICE_NAME = "HermitCrab";
|
|
||||||
|
|
||||||
const char* rootCACertificate = \
|
|
||||||
"-----BEGIN CERTIFICATE-----\n" \
|
|
||||||
"MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw\n" \
|
|
||||||
"TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh\n" \
|
|
||||||
"cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4\n" \
|
|
||||||
"WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu\n" \
|
|
||||||
"ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY\n" \
|
|
||||||
"MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc\n" \
|
|
||||||
"h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+\n" \
|
|
||||||
"0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U\n" \
|
|
||||||
"A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW\n" \
|
|
||||||
"T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH\n" \
|
|
||||||
"B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC\n" \
|
|
||||||
"B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv\n" \
|
|
||||||
"KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn\n" \
|
|
||||||
"OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn\n" \
|
|
||||||
"jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw\n" \
|
|
||||||
"qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI\n" \
|
|
||||||
"rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV\n" \
|
|
||||||
"HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq\n" \
|
|
||||||
"hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL\n" \
|
|
||||||
"ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ\n" \
|
|
||||||
"3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK\n" \
|
|
||||||
"NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5\n" \
|
|
||||||
"ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur\n" \
|
|
||||||
"TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC\n" \
|
|
||||||
"jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc\n" \
|
|
||||||
"oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq\n" \
|
|
||||||
"4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA\n" \
|
|
||||||
"mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d\n" \
|
|
||||||
"emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=\n" \
|
|
||||||
"-----END CERTIFICATE-----\n";
|
|
||||||
|
|
||||||
String getSketchSHA256(); // Function to retrieve current sketch hash
|
|
||||||
|
|
||||||
// Callback function for OTA progress
|
|
||||||
void onOTAProgress(int current, int total) {
|
|
||||||
ESP_LOGD(TAG_OTA,"OTA -- Progress: %d%%\n", (current * 100) / total);
|
|
||||||
}
|
|
||||||
|
|
||||||
//==========================================================================
|
|
||||||
String urlEncode(const String &url, const char *safeChars = "-_.~") {
|
|
||||||
String encoded = "";
|
|
||||||
char temp[4];
|
|
||||||
|
|
||||||
for (int i = 0; i < url.length(); i++) {
|
|
||||||
temp[0] = url.charAt(i);
|
|
||||||
if (temp[0] == 32) { //space
|
|
||||||
encoded.concat('+');
|
|
||||||
} else if ((temp[0] >= 48 && temp[0] <= 57) /*0-9*/
|
|
||||||
|| (temp[0] >= 65 && temp[0] <= 90) /*A-Z*/
|
|
||||||
|| (temp[0] >= 97 && temp[0] <= 122) /*a-z*/
|
|
||||||
|| (strchr(safeChars, temp[0]) != NULL) /* "=&-_.~" */
|
|
||||||
) {
|
|
||||||
encoded.concat(temp[0]);
|
|
||||||
} else { //character needs encoding
|
|
||||||
snprintf(temp, 4, "%%%02X", temp[0]);
|
|
||||||
encoded.concat(temp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return encoded;
|
|
||||||
}
|
|
||||||
|
|
||||||
//==========================================================================
|
|
||||||
bool addQuery(String *query, const String name, const String value) {
|
|
||||||
if (name.length() && value.length()) {
|
|
||||||
if (query->length() < 3) {
|
|
||||||
*query = "?";
|
|
||||||
} else {
|
|
||||||
query->concat('&');
|
|
||||||
}
|
|
||||||
query->concat(urlEncode(name));
|
|
||||||
query->concat('=');
|
|
||||||
query->concat(urlEncode(value));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//==========================================================================
|
|
||||||
bool checkOTA(bool bForceUpdate)
|
|
||||||
{
|
|
||||||
|
|
||||||
// Set callbacks
|
|
||||||
//Update.onStart(onOTAStart);
|
|
||||||
//Update.onEnd(onOTAEnd);
|
|
||||||
String query = "";
|
|
||||||
addQuery(&query, "cmd", (bForceUpdate ? "download" : "check")); //action command
|
|
||||||
uri.concat(query);
|
|
||||||
String version = String(HC__VERSION);
|
|
||||||
ESP_LOGI(TAG_OTA,"OTA - URL: %s\n", url.c_str());
|
|
||||||
ESP_LOGI(TAG_OTA,"OTA - URI: %s\n", uri.c_str());
|
|
||||||
ESP_LOGI(TAG_OTA,"OTA - Ver: %s\n", HC__VERSION);
|
|
||||||
|
|
||||||
|
|
||||||
WiFiClientSecure client;
|
|
||||||
client.setCACert(rootCACertificate);
|
|
||||||
|
|
||||||
UpdateClass hcUpdate(5000);
|
|
||||||
hcUpdate.onProgress(onOTAProgress);
|
|
||||||
//int update(WiFiClient& client, String &url, uint16_t port, String& uri,
|
|
||||||
// String ¤tVersion, short nDeviceType, bool bForceUpdate);
|
|
||||||
int result = hcUpdate.update(client, url, UPDATE_PORT, uri, version, (short)config.m_nDeviceType, true);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
11
Setup.cpp
11
Setup.cpp
|
|
@ -117,14 +117,7 @@ void setupWiFi() {
|
||||||
strncpy(BLE_PW, config.pw, sizeof(BLE_PW));
|
strncpy(BLE_PW, config.pw, sizeof(BLE_PW));
|
||||||
// Connect WiFi for OTA
|
// Connect WiFi for OTA
|
||||||
if (config.ssid[0] && config.pw[0]) {
|
if (config.ssid[0] && config.pw[0]) {
|
||||||
#if defined(ESP32)
|
|
||||||
esp_wifi_set_max_tx_power(74);
|
esp_wifi_set_max_tx_power(74);
|
||||||
#elif defined(ESP8266)
|
|
||||||
WiFi.setOutputPower(20.5f);
|
|
||||||
pinMode(16,OUTPUT);
|
|
||||||
digitalWrite(16, LOW);
|
|
||||||
int c = 0;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
DPRINTF("BOOT: Connecting to WiFi: SSID: '%s', PW: '%s'\n", config.ssid, config.pw);
|
DPRINTF("BOOT: Connecting to WiFi: SSID: '%s', PW: '%s'\n", config.ssid, config.pw);
|
||||||
WiFi.mode(WIFI_STA);
|
WiFi.mode(WIFI_STA);
|
||||||
|
|
@ -173,7 +166,7 @@ void setupPostWiFi(bool bBoot = false) {
|
||||||
ui.message(2, (char *) "Update check...");
|
ui.message(2, (char *) "Update check...");
|
||||||
ledcWrite(PIN_LED_WIFI, PWM_FULL / 20);
|
ledcWrite(PIN_LED_WIFI, PWM_FULL / 20);
|
||||||
checkOTA(true);
|
checkOTA(true);
|
||||||
ledcWrite(PIN_LED_WIFI, PWM_FULL * 19 / 20);
|
ledcWrite(PIN_LED_WIFI, PWM_FULL * 39 / 40);
|
||||||
ui.message(2, (char *) "Update OK!");
|
ui.message(2, (char *) "Update OK!");
|
||||||
DPRINTLN(" OTA Process completed!");
|
DPRINTLN(" OTA Process completed!");
|
||||||
DPRINTLN("===============================\n");
|
DPRINTLN("===============================\n");
|
||||||
|
|
@ -221,7 +214,7 @@ void setupPins() {
|
||||||
ledcWrite(PIN_FAN, PWM_OFF);
|
ledcWrite(PIN_FAN, PWM_OFF);
|
||||||
ledcWrite(PIN_LIGHT, PWM_OFF);
|
ledcWrite(PIN_LIGHT, PWM_OFF);
|
||||||
ledcWrite(PIN_MIST, PWM_OFF);
|
ledcWrite(PIN_MIST, PWM_OFF);
|
||||||
ledcWrite(PIN_LED_WIFI, PWM_FULL / 10); // 80% Initial Blink
|
ledcWrite(PIN_LED_WIFI, PWM_FULL / 20); // 80% Initial Blink
|
||||||
|
|
||||||
// Heaters (Inverted logic for MOSFET safety)
|
// Heaters (Inverted logic for MOSFET safety)
|
||||||
ledcWrite(PIN_LED_HEATER1, PWM_FULL - PWM_OFF);
|
ledcWrite(PIN_LED_HEATER1, PWM_FULL - PWM_OFF);
|
||||||
|
|
|
||||||
|
|
@ -188,7 +188,7 @@ void CWiFiHost::Loop(unsigned long clock)
|
||||||
m_bClientConnected = true;
|
m_bClientConnected = true;
|
||||||
m_bHelloSent = false;
|
m_bHelloSent = false;
|
||||||
m_nMode = MODE_PACKET;
|
m_nMode = MODE_PACKET;
|
||||||
ledcWrite(PIN_LED_WIFI, PWM_FULL * 9 / 10);
|
ledcWrite(PIN_LED_WIFI, PWM_FULL * 39 / 40);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
135
zcd.cpp
135
zcd.cpp
|
|
@ -11,29 +11,44 @@
|
||||||
#include "driver/timer.h" // Needed for timer_isr_register
|
#include "driver/timer.h" // Needed for timer_isr_register
|
||||||
#include "rom/ets_sys.h"
|
#include "rom/ets_sys.h"
|
||||||
#include "driver/gpio.h"
|
#include "driver/gpio.h"
|
||||||
|
#include "driver/periph_ctrl.h"
|
||||||
|
|
||||||
|
|
||||||
timg_dev_t *tg0 = &TIMERG0;
|
timg_dev_t *tg0 = &TIMERG0;
|
||||||
timg_dev_t *tg1 = &TIMERG1;
|
timg_dev_t *tg1 = &TIMERG1;
|
||||||
|
|
||||||
|
#define SET_TIMER_1_SAFE(duty) do { \
|
||||||
|
timerWrite(timerHeater1, 0); \
|
||||||
|
timerAlarm(timerHeater1, duty, false, 0); \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
#define SET_TIMER_2_SAFE(duty) do { \
|
||||||
|
timerWrite(timerHeater2, 0); \
|
||||||
|
timerAlarm(timerHeater2, duty, false, 0); \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
#define SET_TIMER_1(duty) do { \
|
#define SET_TIMER_1(duty) do { \
|
||||||
timer_ll_enable_counter(tg1, 0, false); \
|
|
||||||
tg1->hw_timer[0].loadhi.val = 0; \
|
tg1->hw_timer[0].loadhi.val = 0; \
|
||||||
tg1->hw_timer[0].loadlo.val = 0; \
|
tg1->hw_timer[0].loadlo.val = 0; \
|
||||||
tg1->hw_timer[0].load.val = 1; \
|
tg1->hw_timer[0].load.val = 1; \
|
||||||
|
\
|
||||||
timer_ll_set_alarm_value(tg1, 0, duty); \
|
timer_ll_set_alarm_value(tg1, 0, duty); \
|
||||||
timer_ll_enable_alarm(tg1, 0, true); \
|
\
|
||||||
timer_ll_enable_counter(tg1, 0, true); \
|
tg1->int_clr_timers.t0_int_clr = 1; \
|
||||||
|
\
|
||||||
|
tg1->hw_timer[0].update.val = 1; \
|
||||||
} while(0)
|
} while(0)
|
||||||
|
|
||||||
#define SET_TIMER_2(duty) do { \
|
#define SET_TIMER_2(duty) do { \
|
||||||
timer_ll_enable_counter(tg1, 1, false); \
|
|
||||||
tg1->hw_timer[1].loadhi.val = 0; \
|
tg1->hw_timer[1].loadhi.val = 0; \
|
||||||
tg1->hw_timer[1].loadlo.val = 0; \
|
tg1->hw_timer[1].loadlo.val = 0; \
|
||||||
tg1->hw_timer[1].load.val = 1; \
|
tg1->hw_timer[1].load.val = 1; \
|
||||||
|
\
|
||||||
timer_ll_set_alarm_value(tg1, 1, duty); \
|
timer_ll_set_alarm_value(tg1, 1, duty); \
|
||||||
timer_ll_enable_alarm(tg1, 1, true); \
|
\
|
||||||
timer_ll_enable_counter(tg1, 1, true); \
|
tg1->int_clr_timers.t1_int_clr = 1; \
|
||||||
|
\
|
||||||
|
tg1->hw_timer[1].update.val = 1; \
|
||||||
} while(0)
|
} while(0)
|
||||||
|
|
||||||
#define ESP_INTR_FLAG_LEVEL3 (1<<3)
|
#define ESP_INTR_FLAG_LEVEL3 (1<<3)
|
||||||
|
|
@ -67,6 +82,8 @@ volatile uint8_t fireStatusTimer1 = 0;
|
||||||
volatile uint8_t fireStatusTimer2 = 0;
|
volatile uint8_t fireStatusTimer2 = 0;
|
||||||
|
|
||||||
|
|
||||||
|
hw_timer_t *timer10K;
|
||||||
|
hw_timer_t *timerDummy;
|
||||||
hw_timer_t *timerHeater1;
|
hw_timer_t *timerHeater1;
|
||||||
hw_timer_t *timerHeater2;
|
hw_timer_t *timerHeater2;
|
||||||
|
|
||||||
|
|
@ -90,9 +107,9 @@ const char fireTable[17][16] {
|
||||||
{1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1} // 16 100%
|
{1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1} // 16 100%
|
||||||
};
|
};
|
||||||
|
|
||||||
uint8_t seqStep = 0;
|
volatile uint8_t seqStep = 0;
|
||||||
uint8_t dutyAC1TableIndex = 0;
|
volatile uint8_t dutyAC1TableIndex = 0;
|
||||||
uint8_t dutyAC2TableIndex = 0;
|
volatile uint8_t dutyAC2TableIndex = 0;
|
||||||
|
|
||||||
// AC Frequency and health status
|
// AC Frequency and health status
|
||||||
volatile uint8_t zcdACISRCount = 0;
|
volatile uint8_t zcdACISRCount = 0;
|
||||||
|
|
@ -187,34 +204,30 @@ ARDUINO_ISR_ATTR void setHeater2Duty(short duty) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void ARDUINO_ISR_ATTR onTimer1(void *) {
|
void ARDUINO_ISR_ATTR onTimer1() {
|
||||||
tg1->int_clr_timers.t0_int_clr = 1; // Clear Interrupt
|
|
||||||
|
|
||||||
if (fireStatusTimer1 == 0) {
|
if (fireStatusTimer1 == 0) {
|
||||||
// First Trigger: Turn ON
|
// First Trigger: Turn ON
|
||||||
REG_WRITE(GPIO_OUT1_W1TS_REG, (1UL << (PIN_HEATER1 - 32)));
|
REG_WRITE(GPIO_OUT1_W1TS_REG, (1UL << (PIN_HEATER1 - 32)));
|
||||||
fireStatusTimer1 = 1;
|
fireStatusTimer1 = 1;
|
||||||
SET_TIMER_1(8); // Schedule OFF pulse 8us later
|
SET_TIMER_1_SAFE(18); // Schedule OFF pulse 8us later
|
||||||
} else {
|
} else {
|
||||||
// Second Trigger: Turn OFF
|
// Second Trigger: Turn OFF
|
||||||
REG_WRITE(GPIO_OUT1_W1TC_REG, (1UL << (PIN_HEATER1 - 32)));
|
REG_WRITE(GPIO_OUT1_W1TC_REG, (1UL << (PIN_HEATER1 - 32)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ARDUINO_ISR_ATTR onTimer2(void *) {
|
void ARDUINO_ISR_ATTR onTimer2() {
|
||||||
tg1->int_clr_timers.t1_int_clr = 1;
|
|
||||||
|
|
||||||
if (fireStatusTimer2 == 0) {
|
if (fireStatusTimer2 == 0) {
|
||||||
REG_WRITE(GPIO_OUT1_W1TS_REG, (1UL << (PIN_HEATER2 - 32)));
|
REG_WRITE(GPIO_OUT1_W1TS_REG, (1UL << (PIN_HEATER2 - 32)));
|
||||||
fireStatusTimer2 = 1;
|
fireStatusTimer2 = 1;
|
||||||
SET_TIMER_2(8);
|
SET_TIMER_2_SAFE(18);
|
||||||
} else {
|
} else {
|
||||||
REG_WRITE(GPIO_OUT1_W1TC_REG, (1UL << (PIN_HEATER2 - 32)));
|
REG_WRITE(GPIO_OUT1_W1TC_REG, (1UL << (PIN_HEATER2 - 32)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Zero-Cross Detection Interrupt Service Routine
|
// Zero-Cross Detection Interrupt Service Routine
|
||||||
void ARDUINO_ISR_ATTR zcdACISR(void *) {
|
void ARDUINO_ISR_ATTR zcdACISR() {
|
||||||
// 1. Power side AC ZCD Count
|
// 1. Power side AC ZCD Count
|
||||||
zcdACISRCount++;
|
zcdACISRCount++;
|
||||||
|
|
||||||
|
|
@ -223,27 +236,29 @@ void ARDUINO_ISR_ATTR zcdACISR(void *) {
|
||||||
|
|
||||||
// 3. Heater 1
|
// 3. Heater 1
|
||||||
if (ac1ControlMode == ZCD_CONTROL) {
|
if (ac1ControlMode == ZCD_CONTROL) {
|
||||||
if (fireTable[dutyAC1TableIndex][seqStep]) SET_TIMER_1(LEADING_ZCD_COUNT);
|
if (fireTable[dutyAC1TableIndex][seqStep]) SET_TIMER_1_SAFE(LEADING_ZCD_COUNT);
|
||||||
} else if ( dutyHeater1 >= LEADING_PULSE_COUNT){
|
} else if ( dutyHeater1 >= LEADING_PULSE_COUNT){
|
||||||
SET_TIMER_1(dutyHeater1);
|
SET_TIMER_1_SAFE(dutyHeater1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. Heater 2
|
// 4. Heater 2
|
||||||
if (ac2ControlMode == ZCD_CONTROL) {
|
if (ac2ControlMode == ZCD_CONTROL) {
|
||||||
if (fireTable[dutyAC2TableIndex][seqStep]) SET_TIMER_2(LEADING_ZCD_COUNT);
|
if (fireTable[dutyAC2TableIndex][seqStep]) SET_TIMER_2_SAFE(LEADING_ZCD_COUNT);
|
||||||
} else if (dutyHeater2 >= LEADING_PULSE_COUNT) {
|
} else if (dutyHeater2 >= LEADING_PULSE_COUNT) {
|
||||||
SET_TIMER_2(dutyHeater2);
|
SET_TIMER_2_SAFE(dutyHeater2);
|
||||||
}
|
}
|
||||||
|
|
||||||
seqStep = ++seqStep & 0x0F;
|
seqStep = ++seqStep & 0x0F;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ARDUINO_ISR_ATTR zcdLoadISR(void *) {
|
void ARDUINO_ISR_ATTR zcdLoadISR() {
|
||||||
// Load side AC ZCD Count
|
// Load side AC ZCD Count
|
||||||
zcdLoadISRCount++;
|
zcdLoadISRCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setupZCD() {
|
void setupZCD() {
|
||||||
|
periph_module_enable(PERIPH_TIMG0_MODULE);
|
||||||
|
periph_module_enable(PERIPH_TIMG1_MODULE);
|
||||||
pinMode(PIN_ZCD_AC, INPUT);
|
pinMode(PIN_ZCD_AC, INPUT);
|
||||||
pinMode(PIN_ZCD_LOAD, INPUT);
|
pinMode(PIN_ZCD_LOAD, INPUT);
|
||||||
pinMode(PIN_HEATER1, OUTPUT);
|
pinMode(PIN_HEATER1, OUTPUT);
|
||||||
|
|
@ -264,6 +279,8 @@ void setupZCD() {
|
||||||
ac1ControlMode = config.ac1ControlMode;
|
ac1ControlMode = config.ac1ControlMode;
|
||||||
ac2ControlMode = config.ac2ControlMode;
|
ac2ControlMode = config.ac2ControlMode;
|
||||||
|
|
||||||
|
//===============================================================================
|
||||||
|
|
||||||
// --- 0. Hardware Config for TG0-Timer0 (g_millis)
|
// --- 0. Hardware Config for TG0-Timer0 (g_millis)
|
||||||
timer_ll_set_clock_prescale(tg0, 0, 8000); // 10KHz
|
timer_ll_set_clock_prescale(tg0, 0, 8000); // 10KHz
|
||||||
timer_ll_set_count_direction(tg0, 0, GPTIMER_COUNT_UP);
|
timer_ll_set_count_direction(tg0, 0, GPTIMER_COUNT_UP);
|
||||||
|
|
@ -273,63 +290,43 @@ void setupZCD() {
|
||||||
timer_ll_enable_alarm(tg0, 0, false); // No ISR needed for g_millis
|
timer_ll_enable_alarm(tg0, 0, false); // No ISR needed for g_millis
|
||||||
timer_ll_enable_counter(tg0, 0, true);
|
timer_ll_enable_counter(tg0, 0, true);
|
||||||
|
|
||||||
// --- 1. Hardware Config for TG0-Timer1 (Watchdog) ---
|
attachInterrupt(PIN_ZCD_AC, zcdACISR, CHANGE); // Attach zero-cross detection ISR
|
||||||
tg0->hw_timer[1].loadhi.val = 0UL;
|
attachInterrupt(PIN_ZCD_LOAD, zcdLoadISR, CHANGE); // Attach zero-cross detection ISR
|
||||||
tg0->hw_timer[1].loadlo.val = 0UL;
|
|
||||||
tg0->hw_timer[1].load.val = 1UL;
|
|
||||||
timer_ll_set_clock_prescale(tg0, 1, 80); // 1MHz
|
|
||||||
timer_ll_set_count_direction(tg0, 1, GPTIMER_COUNT_UP);
|
|
||||||
timer_ll_enable_alarm(tg0, 1, false); // No ISR needed for zcdACISR Watchdog
|
|
||||||
timer_ll_enable_counter(tg0, 1, true);
|
|
||||||
|
|
||||||
// --- 2. Basic Hardware Config for TG1 (Heaters) ---
|
// Initialize and configure the timer
|
||||||
// Timer 0
|
if ((timer10K = timerBegin(10000)) != NULL) {
|
||||||
timer_ll_set_clock_prescale(tg1, 0, 80);
|
// TG0-Timer0
|
||||||
timer_ll_set_count_direction(tg1, 0, GPTIMER_COUNT_UP);
|
timerStop(timer10K); // Ensure timer is stopped initially
|
||||||
timer_ll_enable_alarm(tg1, 0, true);
|
timerStart(timer10K); // Explicitly start the timer after setup
|
||||||
|
|
||||||
// Timer 1
|
}
|
||||||
timer_ll_set_clock_prescale(tg1, 1, 80);
|
|
||||||
timer_ll_set_count_direction(tg1, 1, GPTIMER_COUNT_UP);
|
|
||||||
timer_ll_enable_alarm(tg1, 1, true);
|
|
||||||
|
|
||||||
// --- 3. Manual ISR Registration ---
|
if ((timerDummy = timerBegin(1000000)) != NULL) {
|
||||||
esp_intr_alloc(ETS_TG1_T0_LEVEL_INTR_SOURCE, ESP_INTR_FLAG_IRAM, onTimer1, NULL, NULL);
|
// TG0-Timer1
|
||||||
esp_intr_alloc(ETS_TG1_T1_LEVEL_INTR_SOURCE, ESP_INTR_FLAG_IRAM, onTimer2, NULL, NULL);
|
timerStop(timerDummy); // Ensure timer is stopped initially
|
||||||
|
timerStart(timerDummy); // Explicitly start the timer after setup
|
||||||
|
}
|
||||||
|
|
||||||
tg0->hw_timer[0].update.val = 1;
|
if ((timerHeater1 = timerBegin(1000000)) != NULL) {
|
||||||
uint32_t startupTime = timer_ll_get_counter_value(tg0, 0);
|
// TG1-Timer0
|
||||||
|
timerAttachInterrupt(timerHeater1, &onTimer1); // Attach TRIAC firing routine
|
||||||
|
timerStop(timerHeater1); // Ensure timer is stopped initially
|
||||||
|
timerStart(timerHeater1); // Explicitly start the timer after setup
|
||||||
|
|
||||||
zcdACISRCount = 0;
|
}
|
||||||
zcdLoadISRCount = 0;
|
|
||||||
|
|
||||||
// --- 4. Hardware Trigger ISR Registration ---
|
if ((timerHeater2 = timerBegin(1000000)) != NULL) {
|
||||||
//attachInterrupt(PIN_ZCD_AC, zcdACISR, CHANGE); // Attach zero-cross detection ISR
|
// TG1-Timer1
|
||||||
//attachInterrupt(PIN_ZCD_LOAD, zcdLoadISR, CHANGE); // Attach zero-cross detection ISR
|
timerAttachInterrupt(timerHeater2, &onTimer2); // Attach TRIAC firing routine
|
||||||
|
timerStop(timerHeater2); // Ensure timer is stopped initially
|
||||||
// Attach Load ISR with higher priority
|
timerStart(timerHeater2); // Explicitly start the timer after setup
|
||||||
// Set the edges
|
}
|
||||||
gpio_config_t io_conf = {};
|
|
||||||
io_conf.intr_type = GPIO_INTR_ANYEDGE;
|
|
||||||
io_conf.pin_bit_mask = (1ULL << PIN_ZCD_AC) | (1ULL << PIN_ZCD_LOAD);
|
|
||||||
io_conf.mode = GPIO_MODE_INPUT;
|
|
||||||
gpio_config(&io_conf);
|
|
||||||
|
|
||||||
// 2. Set the GLOBAL Priority for all GPIOs
|
|
||||||
// We use LEVEL3 so that BOTH ZCD pulses can preempt lower tasks.
|
|
||||||
// This replaces the attachInterrupt() default (Level 1).
|
|
||||||
gpio_install_isr_service(ESP_INTR_FLAG_IRAM | ESP_INTR_FLAG_LEVEL3);
|
|
||||||
|
|
||||||
// 3. Register the independent handlers
|
|
||||||
// The service handles the "Which pin was it?" logic automatically.
|
|
||||||
gpio_isr_handler_add((gpio_num_t)PIN_ZCD_LOAD, zcdLoadISR, NULL);
|
|
||||||
gpio_isr_handler_add((gpio_num_t)PIN_ZCD_AC, zcdACISR, NULL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void setACLoadStatus(uint32_t tNow) {
|
void setACLoadStatus(uint32_t tNow) {
|
||||||
static uint32_t lastTick = 0;
|
static uint32_t lastTick = 0;
|
||||||
// --- 1. Process AC/Load Window (Every 1s) ---
|
// --- 1. Process AC/Load Window (Every 1s) ---
|
||||||
if (tNow - lastTick >= 9999) {
|
if (tNow - lastTick >= 10000) {
|
||||||
zcdACCount = zcdACISRCount;
|
zcdACCount = zcdACISRCount;
|
||||||
zcdLoadCount = zcdLoadISRCount;
|
zcdLoadCount = zcdLoadISRCount;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user