From 394ba748f52a958114177e7904de07e6f174501f Mon Sep 17 00:00:00 2001 From: RnD1 Date: Sat, 18 Apr 2026 21:31:48 +0900 Subject: [PATCH] ZCD Rollback to Arduino functions --- ConnectWiFi.cpp | 6 +- HCUpdate.h.backup | 291 ---------------- HCUpdater.cpp.backup | 787 ------------------------------------------- OTA.cpp | 2 +- OTA.cpp.backup | 135 -------- Setup.cpp | 11 +- WiFiHost.cpp | 2 +- zcd.cpp | 135 ++++---- 8 files changed, 73 insertions(+), 1296 deletions(-) delete mode 100644 HCUpdate.h.backup delete mode 100644 HCUpdater.cpp.backup delete mode 100644 OTA.cpp.backup diff --git a/ConnectWiFi.cpp b/ConnectWiFi.cpp index a90da02..eb76983 100644 --- a/ConnectWiFi.cpp +++ b/ConnectWiFi.cpp @@ -10,7 +10,7 @@ void WiFiEvent(WiFiEvent_t event) { case IP_EVENT_STA_GOT_IP: DPRINTF("WiFi connected, IP: %s\n", WiFi.localIP().toString().c_str()); 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); break; case WIFI_EVENT_STA_DISCONNECTED: @@ -74,7 +74,7 @@ void checkWiFi(unsigned long tickMillis) { if (status == WL_DISCONNECTED && g_bWiFiHasBeenConnected) { DPRINTLN("Attempting WiFi reconnection..."); WiFi.reconnect(); - ledcWrite(PIN_LED_WIFI, PWM_FULL * 19 / 20); // Light Blink + ledcWrite(PIN_LED_WIFI, PWM_FULL * 39 / 40); // Light Blink lastAttempt = tickMillis; bConnecting = true; } @@ -93,7 +93,7 @@ void checkWiFi(unsigned long tickMillis) { DPRINTF(" WiFi.Begin(%s,%s) returned %d\n", config.ssid, config.pw, ret); lastAttempt = tickMillis; - ledcWrite(PIN_LED_WIFI, PWM_FULL / 10); // Light Blink + ledcWrite(PIN_LED_WIFI, PWM_FULL / 20); // Light Blink lastAttempt = tickMillis; bConnecting = true; } diff --git a/HCUpdate.h.backup b/HCUpdate.h.backup deleted file mode 100644 index 16c8c8c..0000000 --- a/HCUpdate.h.backup +++ /dev/null @@ -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 -#include -#include -#include -#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 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 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) diff --git a/HCUpdater.cpp.backup b/HCUpdater.cpp.backup deleted file mode 100644 index 3ebbb21..0000000 --- a/HCUpdater.cpp.backup +++ /dev/null @@ -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 -#include -#include -#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) \ No newline at end of file diff --git a/OTA.cpp b/OTA.cpp index 780d396..6278873 100644 --- a/OTA.cpp +++ b/OTA.cpp @@ -17,7 +17,7 @@ // OTA // // ============================================================== -const char *HC__VERSION = "20260418016"; +const char *HC__VERSION = "20260418036"; #define UPDATE_PORT ((uint16_t) 443) const char *url = "visionsoft.kr"; const char *uri = "/sc/pages/firmware_download.php"; diff --git a/OTA.cpp.backup b/OTA.cpp.backup deleted file mode 100644 index 4a18da2..0000000 --- a/OTA.cpp.backup +++ /dev/null @@ -1,135 +0,0 @@ -#define NO_GLOBAL_UPDATE -#include -#include -#include -#include -#include - -#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; -} \ No newline at end of file diff --git a/Setup.cpp b/Setup.cpp index 4f91711..9b2127d 100644 --- a/Setup.cpp +++ b/Setup.cpp @@ -117,14 +117,7 @@ void setupWiFi() { strncpy(BLE_PW, config.pw, sizeof(BLE_PW)); // Connect WiFi for OTA if (config.ssid[0] && config.pw[0]) { -#if defined(ESP32) 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); WiFi.mode(WIFI_STA); @@ -173,7 +166,7 @@ void setupPostWiFi(bool bBoot = false) { ui.message(2, (char *) "Update check..."); ledcWrite(PIN_LED_WIFI, PWM_FULL / 20); checkOTA(true); - ledcWrite(PIN_LED_WIFI, PWM_FULL * 19 / 20); + ledcWrite(PIN_LED_WIFI, PWM_FULL * 39 / 40); ui.message(2, (char *) "Update OK!"); DPRINTLN(" OTA Process completed!"); DPRINTLN("===============================\n"); @@ -221,7 +214,7 @@ void setupPins() { ledcWrite(PIN_FAN, PWM_OFF); ledcWrite(PIN_LIGHT, 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) ledcWrite(PIN_LED_HEATER1, PWM_FULL - PWM_OFF); diff --git a/WiFiHost.cpp b/WiFiHost.cpp index 8168deb..25e6492 100644 --- a/WiFiHost.cpp +++ b/WiFiHost.cpp @@ -188,7 +188,7 @@ void CWiFiHost::Loop(unsigned long clock) m_bClientConnected = true; m_bHelloSent = false; m_nMode = MODE_PACKET; - ledcWrite(PIN_LED_WIFI, PWM_FULL * 9 / 10); + ledcWrite(PIN_LED_WIFI, PWM_FULL * 39 / 40); } return; } diff --git a/zcd.cpp b/zcd.cpp index e8e52e2..d8630f9 100644 --- a/zcd.cpp +++ b/zcd.cpp @@ -11,29 +11,44 @@ #include "driver/timer.h" // Needed for timer_isr_register #include "rom/ets_sys.h" #include "driver/gpio.h" +#include "driver/periph_ctrl.h" timg_dev_t *tg0 = &TIMERG0; 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 { \ - timer_ll_enable_counter(tg1, 0, false); \ tg1->hw_timer[0].loadhi.val = 0; \ tg1->hw_timer[0].loadlo.val = 0; \ tg1->hw_timer[0].load.val = 1; \ + \ 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) #define SET_TIMER_2(duty) do { \ - timer_ll_enable_counter(tg1, 1, false); \ tg1->hw_timer[1].loadhi.val = 0; \ tg1->hw_timer[1].loadlo.val = 0; \ tg1->hw_timer[1].load.val = 1; \ + \ 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) #define ESP_INTR_FLAG_LEVEL3 (1<<3) @@ -67,6 +82,8 @@ volatile uint8_t fireStatusTimer1 = 0; volatile uint8_t fireStatusTimer2 = 0; +hw_timer_t *timer10K; +hw_timer_t *timerDummy; hw_timer_t *timerHeater1; 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% }; -uint8_t seqStep = 0; -uint8_t dutyAC1TableIndex = 0; -uint8_t dutyAC2TableIndex = 0; +volatile uint8_t seqStep = 0; +volatile uint8_t dutyAC1TableIndex = 0; +volatile uint8_t dutyAC2TableIndex = 0; // AC Frequency and health status volatile uint8_t zcdACISRCount = 0; @@ -187,34 +204,30 @@ ARDUINO_ISR_ATTR void setHeater2Duty(short duty) { } -void ARDUINO_ISR_ATTR onTimer1(void *) { - tg1->int_clr_timers.t0_int_clr = 1; // Clear Interrupt - +void ARDUINO_ISR_ATTR onTimer1() { if (fireStatusTimer1 == 0) { // First Trigger: Turn ON REG_WRITE(GPIO_OUT1_W1TS_REG, (1UL << (PIN_HEATER1 - 32))); fireStatusTimer1 = 1; - SET_TIMER_1(8); // Schedule OFF pulse 8us later + SET_TIMER_1_SAFE(18); // Schedule OFF pulse 8us later } else { // Second Trigger: Turn OFF REG_WRITE(GPIO_OUT1_W1TC_REG, (1UL << (PIN_HEATER1 - 32))); } } -void ARDUINO_ISR_ATTR onTimer2(void *) { - tg1->int_clr_timers.t1_int_clr = 1; - +void ARDUINO_ISR_ATTR onTimer2() { if (fireStatusTimer2 == 0) { REG_WRITE(GPIO_OUT1_W1TS_REG, (1UL << (PIN_HEATER2 - 32))); fireStatusTimer2 = 1; - SET_TIMER_2(8); + SET_TIMER_2_SAFE(18); } else { REG_WRITE(GPIO_OUT1_W1TC_REG, (1UL << (PIN_HEATER2 - 32))); } } // Zero-Cross Detection Interrupt Service Routine -void ARDUINO_ISR_ATTR zcdACISR(void *) { +void ARDUINO_ISR_ATTR zcdACISR() { // 1. Power side AC ZCD Count zcdACISRCount++; @@ -223,27 +236,29 @@ void ARDUINO_ISR_ATTR zcdACISR(void *) { // 3. Heater 1 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){ - SET_TIMER_1(dutyHeater1); + SET_TIMER_1_SAFE(dutyHeater1); } // 4. Heater 2 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) { - SET_TIMER_2(dutyHeater2); + SET_TIMER_2_SAFE(dutyHeater2); } seqStep = ++seqStep & 0x0F; } -void ARDUINO_ISR_ATTR zcdLoadISR(void *) { +void ARDUINO_ISR_ATTR zcdLoadISR() { // Load side AC ZCD Count zcdLoadISRCount++; } void setupZCD() { + periph_module_enable(PERIPH_TIMG0_MODULE); + periph_module_enable(PERIPH_TIMG1_MODULE); pinMode(PIN_ZCD_AC, INPUT); pinMode(PIN_ZCD_LOAD, INPUT); pinMode(PIN_HEATER1, OUTPUT); @@ -264,6 +279,8 @@ void setupZCD() { ac1ControlMode = config.ac1ControlMode; ac2ControlMode = config.ac2ControlMode; + //=============================================================================== + // --- 0. Hardware Config for TG0-Timer0 (g_millis) timer_ll_set_clock_prescale(tg0, 0, 8000); // 10KHz 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_counter(tg0, 0, true); - // --- 1. Hardware Config for TG0-Timer1 (Watchdog) --- - tg0->hw_timer[1].loadhi.val = 0UL; - 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); + attachInterrupt(PIN_ZCD_AC, zcdACISR, CHANGE); // Attach zero-cross detection ISR + attachInterrupt(PIN_ZCD_LOAD, zcdLoadISR, CHANGE); // Attach zero-cross detection ISR - // --- 2. Basic Hardware Config for TG1 (Heaters) --- - // Timer 0 - timer_ll_set_clock_prescale(tg1, 0, 80); - timer_ll_set_count_direction(tg1, 0, GPTIMER_COUNT_UP); - timer_ll_enable_alarm(tg1, 0, true); - - // 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); + // Initialize and configure the timer + if ((timer10K = timerBegin(10000)) != NULL) { + // TG0-Timer0 + timerStop(timer10K); // Ensure timer is stopped initially + timerStart(timer10K); // Explicitly start the timer after setup - // --- 3. Manual ISR Registration --- - esp_intr_alloc(ETS_TG1_T0_LEVEL_INTR_SOURCE, ESP_INTR_FLAG_IRAM, onTimer1, NULL, NULL); - esp_intr_alloc(ETS_TG1_T1_LEVEL_INTR_SOURCE, ESP_INTR_FLAG_IRAM, onTimer2, NULL, NULL); + } - tg0->hw_timer[0].update.val = 1; - uint32_t startupTime = timer_ll_get_counter_value(tg0, 0); - - zcdACISRCount = 0; - zcdLoadISRCount = 0; + if ((timerDummy = timerBegin(1000000)) != NULL) { + // TG0-Timer1 + timerStop(timerDummy); // Ensure timer is stopped initially + timerStart(timerDummy); // Explicitly start the timer after setup + } - // --- 4. Hardware Trigger ISR Registration --- - //attachInterrupt(PIN_ZCD_AC, zcdACISR, CHANGE); // Attach zero-cross detection ISR - //attachInterrupt(PIN_ZCD_LOAD, zcdLoadISR, CHANGE); // Attach zero-cross detection ISR + if ((timerHeater1 = timerBegin(1000000)) != NULL) { + // TG1-Timer0 + timerAttachInterrupt(timerHeater1, &onTimer1); // Attach TRIAC firing routine + timerStop(timerHeater1); // Ensure timer is stopped initially + timerStart(timerHeater1); // Explicitly start the timer after setup - // Attach Load ISR with higher priority - // 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); + if ((timerHeater2 = timerBegin(1000000)) != NULL) { + // TG1-Timer1 + timerAttachInterrupt(timerHeater2, &onTimer2); // Attach TRIAC firing routine + timerStop(timerHeater2); // Ensure timer is stopped initially + timerStart(timerHeater2); // Explicitly start the timer after setup + } } void setACLoadStatus(uint32_t tNow) { static uint32_t lastTick = 0; // --- 1. Process AC/Load Window (Every 1s) --- - if (tNow - lastTick >= 9999) { + if (tNow - lastTick >= 10000) { zcdACCount = zcdACISRCount; zcdLoadCount = zcdLoadISRCount;