fix(flash): increase INT_WDT timeout, enable littlefs wdt reset and thread safety
All checks were successful
ESP32 Build & Release / build (push) Successful in 1m11s
All checks were successful
ESP32 Build & Release / build (push) Successful in 1m11s
This commit is contained in:
@@ -224,7 +224,9 @@ static esp_err_t http_get_json(const char *url, char **out_buf)
|
||||
|
||||
static esp_err_t http_download_file(const char *url, const char *path)
|
||||
{
|
||||
FILE *f = fopen(path, "wb");
|
||||
char tmp[128];
|
||||
snprintf(tmp, sizeof(tmp), "%s.tmp", path);
|
||||
FILE *f = fopen(tmp, "wb");
|
||||
if (f == NULL) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
@@ -232,9 +234,14 @@ static esp_err_t http_download_file(const char *url, const char *path)
|
||||
esp_err_t ret = http_request(url, &sink);
|
||||
fclose(f);
|
||||
if (ret != ESP_OK) {
|
||||
remove(path);
|
||||
remove(tmp);
|
||||
return ret;
|
||||
}
|
||||
remove(path);
|
||||
if (rename(tmp, path) != 0) {
|
||||
remove(tmp);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
@@ -397,9 +404,11 @@ static esp_err_t scryfall_do_fetch(const char *card_name, const char *uid)
|
||||
if (image_url && strlen(image_url) > 0) {
|
||||
char jpg_path[64];
|
||||
snprintf(jpg_path, sizeof(jpg_path), STORAGE_CARDS_DIR "/%s.jpg", uid);
|
||||
char enc_img[512];
|
||||
url_encode(image_url, enc_img, sizeof(enc_img));
|
||||
char proxy_url[600];
|
||||
/* wsrv.nl converts Scryfall's progressive JPEG to a lightweight 200x110 baseline JPEG */
|
||||
snprintf(proxy_url, sizeof(proxy_url), "https://wsrv.nl/?url=%s&w=200&h=110&fit=cover&output=jpg", image_url);
|
||||
snprintf(proxy_url, sizeof(proxy_url), "https://wsrv.nl/?url=%s&w=200&h=110&fit=cover&output=jpg", enc_img);
|
||||
esp_err_t dl = http_download_file(proxy_url, jpg_path);
|
||||
if (dl != ESP_OK) {
|
||||
ESP_LOGW(TAG, "proxied image download failed, trying direct: %s", image_url);
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
#include <sys/stat.h>
|
||||
#include <sys/unistd.h>
|
||||
#include <dirent.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_littlefs.h"
|
||||
#include "cJSON.h"
|
||||
@@ -17,11 +19,32 @@ static const char *TAG = "storage";
|
||||
|
||||
#define STORAGE_PARTITION_LABEL "storage"
|
||||
|
||||
static SemaphoreHandle_t s_storage_mux = NULL;
|
||||
|
||||
static void storage_lock(void)
|
||||
{
|
||||
if (s_storage_mux == NULL) {
|
||||
s_storage_mux = xSemaphoreCreateRecursiveMutex();
|
||||
}
|
||||
if (s_storage_mux != NULL) {
|
||||
xSemaphoreTakeRecursive(s_storage_mux, portMAX_DELAY);
|
||||
}
|
||||
}
|
||||
|
||||
static void storage_unlock(void)
|
||||
{
|
||||
if (s_storage_mux != NULL) {
|
||||
xSemaphoreGiveRecursive(s_storage_mux);
|
||||
}
|
||||
}
|
||||
|
||||
bool storage_file_exists(const char *path)
|
||||
{
|
||||
storage_lock();
|
||||
struct stat st;
|
||||
return (stat(path, &st) == 0) && S_ISREG(st.st_mode);
|
||||
bool exists = (stat(path, &st) == 0) && S_ISREG(st.st_mode);
|
||||
storage_unlock();
|
||||
return exists;
|
||||
}
|
||||
|
||||
esp_err_t storage_read_text(const char *path, char **out_buf, size_t *out_len)
|
||||
@@ -29,21 +52,25 @@ esp_err_t storage_read_text(const char *path, char **out_buf, size_t *out_len)
|
||||
if (out_buf == NULL || out_len == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
storage_lock();
|
||||
*out_buf = NULL;
|
||||
*out_len = 0;
|
||||
|
||||
FILE *f = fopen(path, "rb");
|
||||
if (f == NULL) {
|
||||
storage_unlock();
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
if (fseek(f, 0, SEEK_END) != 0) {
|
||||
fclose(f);
|
||||
storage_unlock();
|
||||
return ESP_FAIL;
|
||||
}
|
||||
long size = ftell(f);
|
||||
if (size < 0) {
|
||||
fclose(f);
|
||||
storage_unlock();
|
||||
return ESP_FAIL;
|
||||
}
|
||||
rewind(f);
|
||||
@@ -51,6 +78,7 @@ esp_err_t storage_read_text(const char *path, char **out_buf, size_t *out_len)
|
||||
char *buf = malloc((size_t)size + 1);
|
||||
if (buf == NULL) {
|
||||
fclose(f);
|
||||
storage_unlock();
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
@@ -58,11 +86,13 @@ esp_err_t storage_read_text(const char *path, char **out_buf, size_t *out_len)
|
||||
fclose(f);
|
||||
if (rd != (size_t)size) {
|
||||
free(buf);
|
||||
storage_unlock();
|
||||
return ESP_FAIL;
|
||||
}
|
||||
buf[rd] = '\0';
|
||||
*out_buf = buf;
|
||||
*out_len = rd;
|
||||
storage_unlock();
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
@@ -76,27 +106,34 @@ esp_err_t storage_write_text(const char *path, const char *text)
|
||||
}
|
||||
snprintf(tmp, sizeof(tmp), "%s.tmp", path);
|
||||
|
||||
storage_lock();
|
||||
FILE *f = fopen(tmp, "wb");
|
||||
if (f == NULL) {
|
||||
ESP_LOGE(TAG, "open %s failed", tmp);
|
||||
storage_unlock();
|
||||
return ESP_FAIL;
|
||||
}
|
||||
size_t wr = fwrite(text, 1, strlen(text), f);
|
||||
if (wr != strlen(text) || fflush(f) != 0 || fclose(f) != 0) {
|
||||
ESP_LOGE(TAG, "write %s failed", tmp);
|
||||
remove(tmp);
|
||||
storage_unlock();
|
||||
return ESP_FAIL;
|
||||
}
|
||||
remove(path);
|
||||
if (rename(tmp, path) != 0) {
|
||||
ESP_LOGE(TAG, "rename %s -> %s failed", tmp, path);
|
||||
remove(tmp);
|
||||
storage_unlock();
|
||||
return ESP_FAIL;
|
||||
}
|
||||
storage_unlock();
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t storage_manager_bind_uid(const char *uid, const char *card_name)
|
||||
{
|
||||
storage_lock();
|
||||
cJSON *root = NULL;
|
||||
char *existing = NULL;
|
||||
size_t existing_len = 0;
|
||||
@@ -111,6 +148,7 @@ esp_err_t storage_manager_bind_uid(const char *uid, const char *card_name)
|
||||
root = cJSON_CreateObject();
|
||||
}
|
||||
if (root == NULL) {
|
||||
storage_unlock();
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
@@ -133,6 +171,7 @@ esp_err_t storage_manager_bind_uid(const char *uid, const char *card_name)
|
||||
esp_err_t ret = out ? storage_write_text(STORAGE_MAPPINGS, out) : ESP_ERR_NO_MEM;
|
||||
free(out);
|
||||
cJSON_Delete(root);
|
||||
storage_unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -141,6 +180,7 @@ esp_err_t storage_manager_unbind_uid(const char *uid)
|
||||
if (uid == NULL || uid[0] == '\0') {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
storage_lock();
|
||||
char *existing = NULL;
|
||||
size_t existing_len = 0;
|
||||
cJSON *root = NULL;
|
||||
@@ -152,11 +192,13 @@ esp_err_t storage_manager_unbind_uid(const char *uid)
|
||||
|
||||
if (root == NULL || !cJSON_IsObject(root)) {
|
||||
if (root) cJSON_Delete(root);
|
||||
storage_unlock();
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
if (!cJSON_HasObjectItem(root, uid)) {
|
||||
cJSON_Delete(root);
|
||||
storage_unlock();
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
@@ -174,16 +216,19 @@ esp_err_t storage_manager_unbind_uid(const char *uid)
|
||||
esp_err_t ret = out ? storage_write_text(STORAGE_MAPPINGS, out) : ESP_ERR_NO_MEM;
|
||||
free(out);
|
||||
cJSON_Delete(root);
|
||||
storage_unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t storage_manager_lookup_name(const char *uid, char **out)
|
||||
{
|
||||
*out = NULL;
|
||||
storage_lock();
|
||||
char *buf = NULL;
|
||||
size_t len = 0;
|
||||
if (storage_read_text(STORAGE_MAPPINGS, &buf, &len) != ESP_OK || buf == NULL) {
|
||||
free(buf);
|
||||
storage_unlock();
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
@@ -191,6 +236,7 @@ esp_err_t storage_manager_lookup_name(const char *uid, char **out)
|
||||
free(buf);
|
||||
if (root == NULL || !cJSON_IsObject(root)) {
|
||||
if (root) cJSON_Delete(root);
|
||||
storage_unlock();
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
@@ -203,6 +249,7 @@ esp_err_t storage_manager_lookup_name(const char *uid, char **out)
|
||||
}
|
||||
}
|
||||
cJSON_Delete(root);
|
||||
storage_unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -269,8 +316,10 @@ esp_err_t storage_manager_count_cached(int *json_count, int *jpg_count)
|
||||
*json_count = 0;
|
||||
*jpg_count = 0;
|
||||
|
||||
storage_lock();
|
||||
DIR *d = opendir(STORAGE_CARDS_DIR);
|
||||
if (d == NULL) {
|
||||
storage_unlock();
|
||||
return ESP_OK;
|
||||
}
|
||||
struct dirent *entry;
|
||||
@@ -285,11 +334,13 @@ esp_err_t storage_manager_count_cached(int *json_count, int *jpg_count)
|
||||
}
|
||||
}
|
||||
closedir(d);
|
||||
storage_unlock();
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t storage_manager_clear_all_cards(bool reset_mappings)
|
||||
{
|
||||
storage_lock();
|
||||
DIR *d = opendir(STORAGE_CARDS_DIR);
|
||||
if (d != NULL) {
|
||||
struct dirent *entry;
|
||||
@@ -309,5 +360,6 @@ esp_err_t storage_manager_clear_all_cards(bool reset_mappings)
|
||||
storage_write_text(STORAGE_MAPPINGS, "{\n}\n");
|
||||
ESP_LOGI(TAG, "Reset %s to empty", STORAGE_MAPPINGS);
|
||||
}
|
||||
storage_unlock();
|
||||
return ESP_OK;
|
||||
}
|
||||
14
sdkconfig
14
sdkconfig
@@ -1148,12 +1148,12 @@ CONFIG_ESP_CONSOLE_UART_NUM=0
|
||||
CONFIG_ESP_CONSOLE_ROM_SERIAL_PORT_NUM=0
|
||||
CONFIG_ESP_CONSOLE_UART_BAUDRATE=115200
|
||||
CONFIG_ESP_INT_WDT=y
|
||||
CONFIG_ESP_INT_WDT_TIMEOUT_MS=300
|
||||
CONFIG_ESP_INT_WDT_TIMEOUT_MS=2000
|
||||
CONFIG_ESP_INT_WDT_CHECK_CPU1=y
|
||||
CONFIG_ESP_TASK_WDT_EN=y
|
||||
CONFIG_ESP_TASK_WDT_INIT=y
|
||||
# CONFIG_ESP_TASK_WDT_PANIC is not set
|
||||
CONFIG_ESP_TASK_WDT_TIMEOUT_S=5
|
||||
CONFIG_ESP_TASK_WDT_TIMEOUT_S=15
|
||||
CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU0=y
|
||||
CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU1=y
|
||||
# CONFIG_ESP_PANIC_HANDLER_IRAM is not set
|
||||
@@ -1921,10 +1921,10 @@ CONFIG_SPI_FLASH_DANGEROUS_WRITE_ABORTS=y
|
||||
# CONFIG_SPI_FLASH_DANGEROUS_WRITE_FAILS is not set
|
||||
# CONFIG_SPI_FLASH_DANGEROUS_WRITE_ALLOWED is not set
|
||||
# CONFIG_SPI_FLASH_SHARE_SPI1_BUS is not set
|
||||
# CONFIG_SPI_FLASH_BYPASS_BLOCK_ERASE is not set
|
||||
CONFIG_SPI_FLASH_BYPASS_BLOCK_ERASE=y
|
||||
CONFIG_SPI_FLASH_YIELD_DURING_ERASE=y
|
||||
CONFIG_SPI_FLASH_ERASE_YIELD_DURATION_MS=20
|
||||
CONFIG_SPI_FLASH_ERASE_YIELD_TICKS=1
|
||||
CONFIG_SPI_FLASH_ERASE_YIELD_TICKS=2
|
||||
CONFIG_SPI_FLASH_WRITE_CHUNK_SIZE=8192
|
||||
# CONFIG_SPI_FLASH_SIZE_OVERRIDE is not set
|
||||
# CONFIG_SPI_FLASH_CHECK_ERASE_TIMEOUT_DISABLED is not set
|
||||
@@ -2114,7 +2114,7 @@ CONFIG_LITTLEFS_MALLOC_STRATEGY_DEFAULT=y
|
||||
# CONFIG_LITTLEFS_MALLOC_STRATEGY_SPIRAM_PREFER is not set
|
||||
CONFIG_LITTLEFS_ASSERTS=y
|
||||
# CONFIG_LITTLEFS_MMAP_PARTITION is not set
|
||||
# CONFIG_LITTLEFS_WDT_RESET is not set
|
||||
CONFIG_LITTLEFS_WDT_RESET=y
|
||||
# end of LittleFS
|
||||
|
||||
#
|
||||
@@ -2541,12 +2541,12 @@ CONFIG_CONSOLE_UART=y
|
||||
CONFIG_CONSOLE_UART_NUM=0
|
||||
CONFIG_CONSOLE_UART_BAUDRATE=115200
|
||||
CONFIG_INT_WDT=y
|
||||
CONFIG_INT_WDT_TIMEOUT_MS=300
|
||||
CONFIG_INT_WDT_TIMEOUT_MS=2000
|
||||
CONFIG_INT_WDT_CHECK_CPU1=y
|
||||
CONFIG_TASK_WDT=y
|
||||
CONFIG_ESP_TASK_WDT=y
|
||||
# CONFIG_TASK_WDT_PANIC is not set
|
||||
CONFIG_TASK_WDT_TIMEOUT_S=5
|
||||
CONFIG_TASK_WDT_TIMEOUT_S=15
|
||||
CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU0=y
|
||||
CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU1=y
|
||||
# CONFIG_ESP32_DEBUG_STUBS_ENABLE is not set
|
||||
|
||||
@@ -35,8 +35,9 @@ CONFIG_SPI_FLASH_ERASE_YIELD_DURATION_MS=20
|
||||
CONFIG_SPI_FLASH_ERASE_YIELD_TICKS=2
|
||||
|
||||
# Watchdog Timeouts for Flash Erase & OTA Operations
|
||||
CONFIG_ESP_INT_WDT_TIMEOUT_MS=1000
|
||||
CONFIG_ESP_INT_WDT_TIMEOUT_MS=2000
|
||||
CONFIG_ESP_TASK_WDT_TIMEOUT_S=15
|
||||
CONFIG_LITTLEFS_WDT_RESET=y
|
||||
|
||||
# mbedTLS Dynamic Buffers & Memory Optimization (Vital for ESP32 without PSRAM)
|
||||
CONFIG_MBEDTLS_DYNAMIC_BUFFER=y
|
||||
|
||||
Reference in New Issue
Block a user