added rollback and fixed memory leaks

master
Jan Umbach 2023-08-14 22:05:27 +02:00
parent 6c74f93cf8
commit 5078f40e3c
8 changed files with 364 additions and 56 deletions

View File

@ -1,5 +1,6 @@
#pragma once #pragma once
void checkForUpdatesAndInstall(void *parameter); void checkForUpdatesAndInstall(void *parameter);
void checkIfOTAwasSuccessful(void *parameter);
extern bool updateAvailable; extern bool updateAvailable;

View File

@ -1,3 +1,3 @@
#pragma once #pragma once
void startOTAupdate(); void startOTAupdate(bool forceUpdate);

View File

@ -11,22 +11,93 @@
#include "esp_websocket_client.h" #include "esp_websocket_client.h"
#include "esp_event.h" #include "esp_event.h"
#include <stdio.h>
#include <string.h>
#include <esp_log.h>
#include <esp_system.h>
#include "mbedtls/base64.h"
#define CALC_BASE64_ENCODED_LENGTH(input_length) ((4 * ((input_length + 2) / 3)) + 1)
char cacheMessage[CONFIG_HTTP_SERVER_DEBUG_CACHE_LENGTH]; char cacheMessage[CONFIG_HTTP_SERVER_DEBUG_CACHE_LENGTH];
char cacheMessageClone[CONFIG_HTTP_SERVER_DEBUG_CACHE_LENGTH];
int cacheMessageIndex = 0; int cacheMessageIndex = 0;
SemaphoreHandle_t cacheMutex;
const char *TAG = "Console"; const char *TAG = "Console";
const char *LOG_TAG = "[LOG]";
esp_websocket_client_handle_t client = NULL; esp_websocket_client_handle_t client = NULL;
int skippedMessages = 0;
void sendOlderMessages() void sendOlderMessages()
{ {
if (esp_websocket_client_is_connected(client)) if (esp_websocket_client_is_connected(client))
{ {
if (cacheMessageIndex > 0) // printf("cacheMessageIndex:%i\n", cacheMessageIndex);
int newDataLength = 0;
// mutex lock
if (xSemaphoreTake(cacheMutex, (TickType_t)10) == pdTRUE)
{ {
esp_websocket_client_send_text(client, cacheMessage, cacheMessageIndex, 1000 / portTICK_PERIOD_MS); if (cacheMessageIndex > 0)
cacheMessageIndex = 0; {
// clone cacheMessage to cacheMessageClone
memcpy(cacheMessageClone, cacheMessage, cacheMessageIndex);
newDataLength = cacheMessageIndex;
// clear cacheMessage
cacheMessageIndex = 0;
}
// mutex unlock
xSemaphoreGive(cacheMutex);
} }
if (newDataLength > 0)
{
cacheMessageClone[newDataLength - 1] = '\0';
esp_websocket_client_send_text(client, cacheMessageClone, newDataLength, 1000 / portTICK_PERIOD_MS);
newDataLength = 0;
/*// count \n in cacheMessage
int count = 0;
for (int i = 0; i < cacheMessageIndex; i++)
{
if (cacheMessage[i] == '\n')
{
count++;
}
}
printf("count:%i\n", count);
// split cacheMessage into multiple messages
char *pch = strtok(cacheMessage, "\n");
while (pch != NULL)
{
if (strlen(pch) > 0)
{
printf("pf-'%s'\n", pch);
esp_websocket_client_send_text(client, pch, strlen(pch), 1000 / portTICK_PERIOD_MS);
}
pch = strtok(NULL, "\n");
}
cacheMessageIndex = 0;*/
}
else
{
}
}
else
{
} }
} }
@ -35,48 +106,104 @@ int jannex_LOG(const char *format, va_list args)
int status = 0; int status = 0;
// sprintf to jannex_LOG func and dynamically allocate memory // sprintf to jannex_LOG func and dynamically allocate memory
char *message; char *esp_message;
status = vasprintf(&message, format, args); status = vasprintf(&esp_message, format, args);
// check if socket is online size_t encoded_length = 0;
if (client != NULL && esp_websocket_client_is_connected(client)) size_t input_length = strlen(esp_message);
size_t message_length = CALC_BASE64_ENCODED_LENGTH(input_length) + 1;
char *message = (char *)malloc(message_length + 1 + 1); // + \n + \0
if (message != NULL)
{ {
sendOlderMessages(); int ret = mbedtls_base64_encode((unsigned char *)message, message_length - 1, &encoded_length, (unsigned char *)esp_message, input_length);
esp_websocket_client_send_text(client, message, strlen(message), 1000 / portTICK_PERIOD_MS);
} if (ret != 0)
else
{
if (cacheMessageIndex + strlen(message) + 1 > CONFIG_HTTP_SERVER_DEBUG_CACHE_LENGTH)
{ {
// Cache is full printf("Base64 encoding failed!");
// replace at the end of the cache skippedMessages++;
strcpy(cacheMessage + CONFIG_HTTP_SERVER_DEBUG_CACHE_LENGTH - 30, " ! CONSOLE CACHE OVERFLOW ! \n");
cacheMessageIndex = CONFIG_HTTP_SERVER_DEBUG_CACHE_LENGTH;
goto END; goto END;
} }
// cache message // set last char to \n
if (cacheMessageIndex == 0) message[encoded_length] = '\n';
message[encoded_length + 1] = '\0';
}
else
{
printf("Memory allocation failed!");
skippedMessages++;
goto END;
}
message_length = encoded_length + 1;
// check if socket is online
/*if (client != NULL && esp_websocket_client_is_connected(client))
{
// print last char of message in hex
// printf("last char: %02x\n", message[encoded_length - 1]);
sendOlderMessages();
printf("message_length:%i\n", message_length);
esp_websocket_client_send_text(client, message, message_length, 1000 / portTICK_PERIOD_MS);
}
else*/
{
// Take the mutex before writing to the variable
if (xSemaphoreTake(cacheMutex, portMAX_DELAY) == pdTRUE)
{ {
strcpy(cacheMessage + cacheMessageIndex, message);
cacheMessageIndex += strlen(message); if (cacheMessageIndex + message_length + 1 > CONFIG_HTTP_SERVER_DEBUG_CACHE_LENGTH)
{
// Cache is full
// replace at the end of the cache
strcpy(cacheMessage + CONFIG_HTTP_SERVER_DEBUG_CACHE_LENGTH - 7, "!CCO!\n");
cacheMessageIndex = CONFIG_HTTP_SERVER_DEBUG_CACHE_LENGTH;
xSemaphoreGive(cacheMutex);
goto END;
}
// cache message thread safe
// xSemaphoreTake(cacheMessageSemaphore, portMAX_DELAY);
// cache message
if (cacheMessageIndex == 0)
{
strcpy(cacheMessage + cacheMessageIndex, message);
cacheMessageIndex += message_length;
}
else
{
// cacheMessage[cacheMessageIndex] = '\n';
strcpy(cacheMessage + cacheMessageIndex, message);
cacheMessageIndex += message_length;
}
// Release the mutex
xSemaphoreGive(cacheMutex);
} }
else else
{ {
// cacheMessage[cacheMessageIndex] = '\n'; // Unable to take the mutex
strcpy(cacheMessage + cacheMessageIndex, message); // Handle the error
cacheMessageIndex += strlen(message);
} }
} }
END: END:
free(esp_message);
free(message); free(message);
if (status >= 0) if (status >= 0)
{ {
return vprintf(format, args); // print to console return vprintf(format, args); // print to console
} }
return status; return status;
@ -153,7 +280,10 @@ static void console_task(void *pvParameters)
esp_websocket_client_config_t websocket_cfg = { esp_websocket_client_config_t websocket_cfg = {
.uri = CONFIG_HTTP_SERVER_DEBUG_URL, .uri = CONFIG_HTTP_SERVER_DEBUG_URL,
.port = CONFIG_HTTP_SERVER_DEBUG_PORT, .port = CONFIG_HTTP_SERVER_DEBUG_PORT,
.task_stack = 1024 * 6,
.reconnect_timeout_ms = 5000,
.network_timeout_ms = 500,
.ping_interval_sec = 10000,
}; };
ESP_LOGI(TAG, "Connecting to %s... on port %d", websocket_cfg.uri, websocket_cfg.port); ESP_LOGI(TAG, "Connecting to %s... on port %d", websocket_cfg.uri, websocket_cfg.port);
@ -176,12 +306,32 @@ static void console_task(void *pvParameters)
esp_websocket_client_destroy(client);*/ esp_websocket_client_destroy(client);*/
vTaskDelete(NULL); while (true)
{
sendOlderMessages();
vTaskDelay(500 / portTICK_PERIOD_MS);
}
} }
void initConsole() void initConsole()
{ {
cacheMutex = xSemaphoreCreateMutex();
if (cacheMutex == NULL)
{
// Mutex creation failed
// Handle the error
printf("initConsole Mutex creation failed!!!!!!!!!!!");
}
esp_log_set_vprintf(&jannex_LOG); esp_log_set_vprintf(&jannex_LOG);
if (cacheMutex == NULL)
{
// Mutex creation failed
// Handle the error
printf("initConsole Mutex creation failed!!!!!!!!!!!");
ESP_LOGE(TAG, "initConsole Mutex creation failed!!!!!!!!!!!");
}
} }
void connectConsole() void connectConsole()

View File

@ -22,6 +22,11 @@ esp_err_t https_get_request_SYNC(const char *url, char *out_buffer, int *out_buf
}; };
esp_http_client_handle_t client = esp_http_client_init(&config); esp_http_client_handle_t client = esp_http_client_init(&config);
if (client == NULL)
{
ESP_LOGE(TAG, "Failed to initialize HTTP client");
return ESP_FAIL;
}
// GET Request // GET Request
esp_http_client_set_method(client, HTTP_METHOD_GET); esp_http_client_set_method(client, HTTP_METHOD_GET);

View File

@ -62,6 +62,28 @@ void initialize_sntp()
}*/ }*/
} }
void startJannex(void *parameter)
{
defaultValuesToStorage();
#ifdef WIFI_ENABLED
esp_log_level_set("esp-x509-crt-bundle", ESP_LOG_WARN);
esp_log_level_set("wifi", ESP_LOG_WARN);
init_wifi();
#endif
#ifdef WEB_SERVER_ENABLED
initServer();
#endif
#ifdef CONFIG_HTTP_SERVER_ENABLE_DEBUG_MODE
connectConsole();
#endif // CONFIG_HTTP_SERVER_ENABLE_DEBUG_MODE
initialize_sntp();
vTaskDelete(NULL);
}
void initJannex() void initJannex()
{ {
initStorage(); initStorage();
@ -80,26 +102,11 @@ void initJannex()
ESP_LOGI(jannexTAG, "Current Build date: %s", app_desc->date); ESP_LOGI(jannexTAG, "Current Build date: %s", app_desc->date);
ESP_LOGI(jannexTAG, "Current Build time: %s", app_desc->time); ESP_LOGI(jannexTAG, "Current Build time: %s", app_desc->time);
defaultValuesToStorage(); xTaskCreate(startJannex, "startJannex", 4096, NULL, 5, NULL);
xTaskCreate(checkIfOTAwasSuccessful, "checkIfOTAwasSuccessful", 4096, NULL, 5, NULL);
#ifdef WIFI_ENABLED
esp_log_level_set("esp-x509-crt-bundle", ESP_LOG_WARN);
esp_log_level_set("wifi", ESP_LOG_WARN);
init_wifi();
#endif
#ifdef WEB_SERVER_ENABLED
initServer();
#endif
#ifdef CONFIG_HTTP_SERVER_ENABLE_DEBUG_MODE
connectConsole();
#endif // CONFIG_HTTP_SERVER_ENABLE_DEBUG_MODE
initialize_sntp();
} }
void updateJannex() void updateJannex()
{ {
xTaskCreate(&checkForUpdatesAndInstall, "checkForUpdatesAndInstall", 8192, NULL, 5, NULL); xTaskCreate(checkForUpdatesAndInstall, "checkForUpdatesAndInstall", 8192, NULL, 5, NULL);
} }

152
src/ota.c
View File

@ -13,11 +13,38 @@
#include "esp_app_desc.h" #include "esp_app_desc.h"
#include "esp_ota_ops.h" #include "esp_ota_ops.h"
#include "string.h"
// #include "esp_heap_trace.h"
// #define NUM_RECORDS 100
// static heap_trace_record_t trace_record[NUM_RECORDS]; // This buffer must be in internal RAM
bool updateAvailable = false; bool updateAvailable = false;
bool isCheckingForUpdates = false; bool isCheckingForUpdates = false;
void getNewestVersion() void printFreeHeap(int start)
{ {
if (start == 0)
{
ESP_LOGI(jannexTAG, " \\/ \\/ \\/ \\/ \\/ Free heap: ");
ESP_LOGI(jannexTAG, " %i", heap_caps_get_free_size(MALLOC_CAP_8BIT));
}
else if (start == -1)
{
ESP_LOGI(jannexTAG, " %i", heap_caps_get_free_size(MALLOC_CAP_8BIT));
ESP_LOGI(jannexTAG, " /\\ /\\ /\\ /\\ /\\ Free heap: ");
}
else
{
ESP_LOGI(jannexTAG, " %i", heap_caps_get_free_size(MALLOC_CAP_8BIT));
}
}
void getNewestVersion(bool forceUpdate)
{
isCheckingForUpdates = true; isCheckingForUpdates = true;
// buffer for the response // buffer for the response
@ -53,8 +80,10 @@ void getNewestVersion()
ESP_LOGE(jannexTAG, "HTTP GET Response: %i %i %s", status_code, responseLength, response); ESP_LOGE(jannexTAG, "HTTP GET Response: %i %i %s", status_code, responseLength, response);
ESP_LOGE(jannexTAG, "Parser failed"); ESP_LOGE(jannexTAG, "Parser failed");
isCheckingForUpdates = false; isCheckingForUpdates = false;
return;
goto FREE_MEM;
} }
char newestVersion[32]; char newestVersion[32];
if (json_obj_get_string(&jctx, "error", newestVersion, sizeof(newestVersion)) == OS_SUCCESS) if (json_obj_get_string(&jctx, "error", newestVersion, sizeof(newestVersion)) == OS_SUCCESS)
@ -62,7 +91,8 @@ void getNewestVersion()
ESP_LOGW(jannexTAG, "Error: %s", newestVersion); ESP_LOGW(jannexTAG, "Error: %s", newestVersion);
// ERROR // ERROR
isCheckingForUpdates = false; isCheckingForUpdates = false;
return;
goto FREE_MEM;
} }
if (json_obj_get_string(&jctx, "version", newestVersion, sizeof(newestVersion)) == OS_SUCCESS) if (json_obj_get_string(&jctx, "version", newestVersion, sizeof(newestVersion)) == OS_SUCCESS)
@ -73,7 +103,8 @@ void getNewestVersion()
{ {
ESP_LOGI(jannexTAG, "No version"); ESP_LOGI(jannexTAG, "No version");
isCheckingForUpdates = false; isCheckingForUpdates = false;
return;
goto FREE_MEM;
} }
// print current version // print current version
@ -140,22 +171,95 @@ void getNewestVersion()
updateAvailable = true; updateAvailable = true;
} }
esp_partition_t *last_invalid_app = esp_ota_get_last_invalid_partition();
if (last_invalid_app != NULL)
{
// get partition description
esp_app_desc_t last_app_desc;
esp_err_t ret = esp_ota_get_partition_description(last_invalid_app, &last_app_desc);
if (ret == ESP_OK)
{
ESP_LOGI(jannexTAG, "Last invalid app partition: %s", last_app_desc.version);
// check if the last invalid partition is the same as the newest version
if (strcmp(last_app_desc.version, newestVersion) == 0)
{
// the last invalid partition is the same as the newest version -> DONT update
ESP_LOGE(jannexTAG, "BAD UPDATE DETECTED -> KEEP CURRENT VERSION AND DO NOT UPDATE");
vTaskDelay(10000 / portTICK_PERIOD_MS);
updateAvailable = false;
}
}
else
ESP_LOGE(jannexTAG, "Could not get last invalid app partition description: %s", esp_err_to_name(ret));
} /*
else
{
ESP_LOGI(jannexTAG, "No invalid partition");
}*/
isCheckingForUpdates = false; isCheckingForUpdates = false;
// esp ota do update // esp ota do update
if (updateAvailable) if (updateAvailable)
{ {
startOTAupdate(); startOTAupdate(forceUpdate);
}
FREE_MEM:
json_parse_end(&jctx);
// free(response); // crashes ESP
}
void printFreeHeapTask(void *parameter)
{
while (1)
{
size_t heap = esp_get_free_heap_size();
ESP_LOGI(jannexTAG, "Biggest free heap Block: %i", heap);
ESP_LOGI(jannexTAG, "Total Free heap: %i", heap_caps_get_free_size(MALLOC_CAP_8BIT));
vTaskDelay(500 / portTICK_PERIOD_MS);
} }
} }
void checkForUpdatesAndInstall(void *parameter) void checkForUpdatesAndInstall(void *parameter)
{ {
// ESP_ERROR_CHECK(heap_trace_init_standalone(trace_record, NUM_RECORDS));
xTaskCreate(printFreeHeapTask, "printFreeHeapTask", 4096, NULL, 5, NULL);
vTaskDelay(pdMS_TO_TICKS(5000)); // Delay for 5000 milliseconds
while (1) while (1)
{ {
// get current ota state
esp_partition_t *running = esp_ota_get_running_partition();
esp_ota_img_states_t state;
esp_err_t ret = esp_ota_get_state_partition(running, &state);
if (ret == ESP_OK)
{
if (state == ESP_OTA_IMG_PENDING_VERIFY)
{
ESP_LOGI(jannexTAG, "OTA update pending verify...");
// The firmware is half-way through an OTA update, but it's waiting to verify.
// We'll just reboot.
vTaskDelay(pdMS_TO_TICKS(5000)); // Delay for 5000 milliseconds
continue;
}
}
if (isCheckingForUpdates == false) if (isCheckingForUpdates == false)
getNewestVersion(); {
// ESP_ERROR_CHECK(heap_trace_start(HEAP_TRACE_LEAKS));
getNewestVersion(false);
// ESP_ERROR_CHECK(heap_trace_stop());
// heap_trace_dump();
// vTaskDelay(pdMS_TO_TICKS(50000)); // Delay for 50000 milliseconds
}
#ifdef CONFIG_FIRMWARE_UPDATE_SCAN_SECONDS #ifdef CONFIG_FIRMWARE_UPDATE_SCAN_SECONDS
if (CONFIG_FIRMWARE_UPDATE_SCAN_SECONDS >= 1) if (CONFIG_FIRMWARE_UPDATE_SCAN_SECONDS >= 1)
@ -169,3 +273,39 @@ void checkForUpdatesAndInstall(void *parameter)
} }
} }
} }
void checkIfOTAwasSuccessful(void *parameter)
{
esp_partition_t *running = esp_ota_get_running_partition();
esp_ota_img_states_t state;
esp_err_t ret = esp_ota_get_state_partition(running, &state);
if (ret != ESP_OK)
{
ESP_LOGE(jannexTAG, "esp_ota_get_state_partition failed! %i", ret);
vTaskDelete(NULL);
return;
}
if (state == ESP_OTA_IMG_PENDING_VERIFY)
{
ESP_LOGI(jannexTAG, "ESP_OTA_IMG_PENDING_VERIFY");
vTaskDelay(1000 * 30 / portTICK_PERIOD_MS); // wait 30sec minute
// run diagnostic function ...
bool diagnostic_is_ok = true; // diagnostic();
if (diagnostic_is_ok)
{
ESP_LOGI(jannexTAG, "Diagnostics completed successfully! Continuing execution ...");
esp_ota_mark_app_valid_cancel_rollback();
}
else
{
ESP_LOGE(jannexTAG, "Diagnostics failed! Start rollback to the previous version ...");
esp_ota_mark_app_invalid_rollback_and_reboot();
}
}
ESP_LOGI(jannexTAG, "State: %i", state);
vTaskDelete(NULL);
}

View File

@ -47,7 +47,7 @@ esp_err_t _http_event_handler(esp_http_client_event_t *evt)
return ESP_OK; return ESP_OK;
} }
void startOTAupdate() void startOTAupdate(bool forceUpdate)
{ {
// get mac address // get mac address
uint8_t mac[6]; uint8_t mac[6];
@ -65,7 +65,7 @@ void startOTAupdate()
esp_http_client_config_t config = { esp_http_client_config_t config = {
.url = url, .url = url,
.crt_bundle_attach = esp_crt_bundle_attach, .crt_bundle_attach = esp_crt_bundle_attach,
.buffer_size = 4096, .buffer_size = 4096 * 4,
.event_handler = _http_event_handler, .event_handler = _http_event_handler,
.keep_alive_enable = true, .keep_alive_enable = true,
@ -90,4 +90,4 @@ void startOTAupdate()
{ {
ESP_LOGE(TAG, "Firmware upgrade failed!"); ESP_LOGE(TAG, "Firmware upgrade failed!");
} }
} }

View File

@ -27,7 +27,7 @@ esp_err_t readStorage(const char *key, char *out_value, size_t *length, char *de
case ESP_OK: case ESP_OK:
// ESP_LOGI(TAG, "Current %s: %s", key, out_value); // ESP_LOGI(TAG, "Current %s: %s", key, out_value);
return ret; break;
case ESP_ERR_NVS_NOT_FOUND: case ESP_ERR_NVS_NOT_FOUND:
ESP_LOGI(TAG, "The value is not initialized yet!\n"); ESP_LOGI(TAG, "The value is not initialized yet!\n");
@ -35,7 +35,12 @@ esp_err_t readStorage(const char *key, char *out_value, size_t *length, char *de
default: default:
ESP_LOGE(TAG, "Error (%s) reading!\n", esp_err_to_name(ret)); ESP_LOGE(TAG, "Error (%s) reading!\n", esp_err_to_name(ret));
} }
nvs_close(my_handle); nvs_close(my_handle);
if (ret == ESP_OK)
{
return ret;
}
} }
// if not found, set to default value // if not found, set to default value