From 845fdd306e537c9cdd66a0e961c5f8c9bf521101 Mon Sep 17 00:00:00 2001 From: Peter Siegmund Date: Fri, 26 Sep 2025 23:01:26 +0200 Subject: [PATCH] day/night cycle on LED 1 from CSV file Signed-off-by: Peter Siegmund --- .../bootloader_components/main/CMakeLists.txt | 12 -- .../bootloader_components/main/bootloader.c | 65 ---------- .../led-manager/include/led_status.h | 2 +- .../led-manager/src/led_manager.cpp | 3 +- firmware/components/simulator/CMakeLists.txt | 8 ++ .../components/simulator/include/simulator.h | 17 +++ .../components/simulator/include/storage.h | 5 + firmware/components/simulator/simulator.c | 120 ++++++++++++++++++ firmware/components/simulator/storage.c | 77 +++++++++++ firmware/main/CMakeLists.txt | 3 + firmware/main/main.cpp | 2 + firmware/partitions.csv | 3 +- firmware/sdkconfig.defaults | 7 + firmware/spiffs_image/schema_01.csv | 48 +++++++ firmware/spiffs_image/schema_02.csv | 48 +++++++ 15 files changed, 340 insertions(+), 80 deletions(-) delete mode 100644 firmware/bootloader_components/main/CMakeLists.txt delete mode 100644 firmware/bootloader_components/main/bootloader.c create mode 100644 firmware/components/simulator/CMakeLists.txt create mode 100644 firmware/components/simulator/include/simulator.h create mode 100644 firmware/components/simulator/include/storage.h create mode 100644 firmware/components/simulator/simulator.c create mode 100644 firmware/components/simulator/storage.c create mode 100644 firmware/spiffs_image/schema_01.csv create mode 100644 firmware/spiffs_image/schema_02.csv diff --git a/firmware/bootloader_components/main/CMakeLists.txt b/firmware/bootloader_components/main/CMakeLists.txt deleted file mode 100644 index 7d29357..0000000 --- a/firmware/bootloader_components/main/CMakeLists.txt +++ /dev/null @@ -1,12 +0,0 @@ -idf_component_register(SRCS "bootloader.c" - REQUIRES bootloader bootloader_support) - -idf_build_get_property(target IDF_TARGET) - -set(target_folder "${target}") - -# Use the linker script files from the actual bootloader -set(scripts "${IDF_PATH}/components/bootloader/subproject/main/ld/${target_folder}/bootloader.ld" - "${IDF_PATH}/components/bootloader/subproject/main/ld/${target_folder}/bootloader.rom.ld") - -target_linker_script(${COMPONENT_LIB} INTERFACE "${scripts}") \ No newline at end of file diff --git a/firmware/bootloader_components/main/bootloader.c b/firmware/bootloader_components/main/bootloader.c deleted file mode 100644 index 6b0eafb..0000000 --- a/firmware/bootloader_components/main/bootloader.c +++ /dev/null @@ -1,65 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - */ -#include -#include "sdkconfig.h" -#include "esp_log.h" -#include "bootloader_init.h" -#include "bootloader_utility.h" -#include "bootloader_common.h" - -static const char* TAG = "boot"; - -static int select_partition_number(bootloader_state_t* bs); - -/* - * We arrive here after the ROM bootloader finished loading this second stage bootloader from flash. - * The hardware is mostly uninitialized, flash cache is down and the app CPU is in reset. - * We do have a stack, so we can do the initialization in C. - */ -void __attribute__((noreturn)) call_start_cpu0(void) { - // 1. Hardware initialization - if(bootloader_init() != ESP_OK) { - bootloader_reset(); - } - -#ifdef CONFIG_BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP - // If this boot is a wake up from the deep sleep then go to the short way, - // try to load the application which worked before deep sleep. - // It skips a lot of checks due to it was done before (while first boot). - bootloader_utility_load_boot_image_from_deep_sleep(); - // If it is not successful try to load an application as usual. -#endif - - // 2. Select the number of boot partition - bootloader_state_t bs = {0}; - int boot_index = select_partition_number(&bs); - if(boot_index == INVALID_INDEX) { - bootloader_reset(); - } - - // 2.1 Print a custom message! - ESP_LOGI(TAG, "Custom bootloader completed"); - - // 3. Load the app image for booting - bootloader_utility_load_boot_image(&bs, boot_index); -} - -// Select the number of boot partition -static int select_partition_number(bootloader_state_t* bs) { - // 1. Load partition table - if(!bootloader_utility_load_partition_table(bs)) { - ESP_LOGE(TAG, "load partition table error!"); - return INVALID_INDEX; - } - - // 2. Select the number of boot partition - return bootloader_utility_get_selected_boot_partition(bs); -} - -// Return global reent struct if any newlib functions are linked to bootloader -struct _reent* __getreent(void) { - return _GLOBAL_REENT; -} diff --git a/firmware/components/led-manager/include/led_status.h b/firmware/components/led-manager/include/led_status.h index 21d0efc..f3dfeda 100644 --- a/firmware/components/led-manager/include/led_status.h +++ b/firmware/components/led-manager/include/led_status.h @@ -1,6 +1,6 @@ #pragma once -#include "driver/rmt_tx.h" +#include "esp_check.h" #include // Number of LEDs to be controlled diff --git a/firmware/components/led-manager/src/led_manager.cpp b/firmware/components/led-manager/src/led_manager.cpp index 8364cdb..56e022e 100644 --- a/firmware/components/led-manager/src/led_manager.cpp +++ b/firmware/components/led-manager/src/led_manager.cpp @@ -72,7 +72,8 @@ void event_handler(void *arg, esp_event_base_t base, int32_t id, void *event_dat } led_strip_refresh(led_strip); - led_behavior_t led2_behavior = {.mode = LED_MODE_SOLID, .color = {.r = red, .g = green, .b = blue}}; + led_behavior_t led2_behavior = { + .mode = LED_MODE_SOLID, .color = {.r = red, .g = green, .b = blue}, .on_time_ms = 0, .off_time_ms = 0}; led_status_set_behavior(2, led2_behavior); } diff --git a/firmware/components/simulator/CMakeLists.txt b/firmware/components/simulator/CMakeLists.txt new file mode 100644 index 0000000..796658f --- /dev/null +++ b/firmware/components/simulator/CMakeLists.txt @@ -0,0 +1,8 @@ +idf_component_register(SRCS + "simulator.c" + "storage.c" + INCLUDE_DIRS "include" + PRIV_REQUIRES + led-manager + spiffs +) diff --git a/firmware/components/simulator/include/simulator.h b/firmware/components/simulator/include/simulator.h new file mode 100644 index 0000000..b6ea299 --- /dev/null +++ b/firmware/components/simulator/include/simulator.h @@ -0,0 +1,17 @@ +#pragma once + +#include "esp_check.h" +#include + +esp_err_t add_light_item(const char time[5], uint8_t red, uint8_t green, uint8_t blue); + +void cleanup_light_items(void); + +#ifdef __cplusplus +extern "C" +{ +#endif + void simulate(void *args); +#ifdef __cplusplus +} +#endif diff --git a/firmware/components/simulator/include/storage.h b/firmware/components/simulator/include/storage.h new file mode 100644 index 0000000..4a1f814 --- /dev/null +++ b/firmware/components/simulator/include/storage.h @@ -0,0 +1,5 @@ +#pragma once + +void initialize_storage(); + +void load_file(const char *filename); diff --git a/firmware/components/simulator/simulator.c b/firmware/components/simulator/simulator.c new file mode 100644 index 0000000..96d32ec --- /dev/null +++ b/firmware/components/simulator/simulator.c @@ -0,0 +1,120 @@ +#include "simulator.h" + +#include "esp_heap_caps.h" +#include "esp_log.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "led_status.h" +#include "storage.h" +#include +#include +#include + +static const char *TAG = "simulator"; + +// The struct is extended with a 'next' pointer to form a linked list. +typedef struct light_item_node_t +{ + char time[5]; + uint8_t red; + uint8_t green; + uint8_t blue; + struct light_item_node_t *next; +} light_item_node_t; + +// Global pointers for the head and tail of the list. +static light_item_node_t *head = NULL; +static light_item_node_t *tail = NULL; + +esp_err_t add_light_item(const char time[5], uint8_t red, uint8_t green, uint8_t blue) +{ + // Allocate memory for a new node in PSRAM. + light_item_node_t *new_node = (light_item_node_t *)heap_caps_malloc(sizeof(light_item_node_t), MALLOC_CAP_SPIRAM); + if (new_node == NULL) + { + ESP_LOGE(TAG, "Failed to allocate memory in PSRAM for new light_item_node_t."); + return ESP_FAIL; + } + + // Initialize the data of the new node. + memcpy(new_node->time, time, sizeof(new_node->time)); + new_node->red = red; + new_node->green = green; + new_node->blue = blue; + new_node->next = NULL; + + // Append the new node to the end of the list. + if (head == NULL) + { + // If the list is empty, the new node becomes both head and tail. + head = new_node; + tail = new_node; + } + else + { + // Otherwise, append the new node to the end and update tail. + tail->next = new_node; + tail = new_node; + } + + return ESP_OK; +} + +void cleanup_light_items(void) +{ + light_item_node_t *current = head; + light_item_node_t *next_node; + + while (current != NULL) + { + next_node = current->next; + heap_caps_free(current); + current = next_node; + } + + head = NULL; + tail = NULL; + ESP_LOGI(TAG, "Cleaned up all light items."); +} + +void simulate(void *args) +{ + ESP_LOGI(TAG, "Simulation task started with args: %p", args); + + initialize_storage(); + load_file("/spiffs/schema_02.csv"); + + if (head == NULL) + { + ESP_LOGW(TAG, "Light schedule is empty. Simulation will not run."); + vTaskDelete(NULL); + return; + } + + ESP_LOGI(TAG, "Starting simulation loop."); + light_item_node_t *current_item = head; + + while (1) + { + if (current_item == NULL) + { + current_item = head; + ESP_LOGI(TAG, "Reached end of schedule, restarting from head."); + } + + ESP_LOGI(TAG, "Simulating time: %s -> R:%d, G:%d, B:%d", current_item->time, current_item->red, + current_item->green, current_item->blue); + led_behavior_t led1_behavior = { + .mode = LED_MODE_SOLID, + .color = {.r = current_item->red, .g = current_item->green, .b = current_item->blue}, + .on_time_ms = 0, + .off_time_ms = 0}; + led_status_set_behavior(1, led1_behavior); + + current_item = current_item->next; + + vTaskDelay(pdMS_TO_TICKS(1000)); + } + + cleanup_light_items(); +} diff --git a/firmware/components/simulator/storage.c b/firmware/components/simulator/storage.c new file mode 100644 index 0000000..8cf3939 --- /dev/null +++ b/firmware/components/simulator/storage.c @@ -0,0 +1,77 @@ +#include "storage.h" +#include "esp_check.h" +#include "esp_log.h" +#include "esp_spiffs.h" +#include "simulator.h" +#include +#include + +static const char *TAG = "storage"; + +void initialize_storage() +{ + esp_vfs_spiffs_conf_t conf = { + .base_path = "/spiffs", // Der Basispfad, unter dem das Dateisystem gemountet wird + .partition_label = NULL, // NULL, um die erste gefundene SPIFFS-Partition zu verwenden + .max_files = 5, // Maximale Anzahl gleichzeitig geöffneter Dateien + .format_if_mount_failed = false // Partition formatieren, wenn das Mounten fehlschlägt + }; + + esp_err_t ret = esp_vfs_spiffs_register(&conf); + + if (ret != ESP_OK) + { + if (ret == ESP_FAIL) + { + ESP_LOGE(TAG, "Failed to mount or format filesystem"); + } + else if (ret == ESP_ERR_NOT_FOUND) + { + ESP_LOGE(TAG, "Failed to find SPIFFS partition"); + } + else + { + ESP_LOGE(TAG, "Failed to initialize SPIFFS (%s)", esp_err_to_name(ret)); + } + return; // Oder entsprechende Fehlerbehandlung + } +} + +void load_file(const char *filename) +{ + ESP_LOGI(TAG, "Loading file: %s", filename); + FILE *f = fopen(filename, "r"); + if (f == NULL) + { + ESP_LOGE(TAG, "Failed to open file for reading"); + return; + } + + char line[128]; // Puffer für eine Zeile, vergrößert für mehr Sicherheit + while (fgets(line, sizeof(line), f)) + { + // Entferne möglichen Zeilenumbruch am Ende + char *pos = strchr(line, '\n'); + if (pos) + { + *pos = '\0'; + } + + char time[5] = {0}; // 4 Zeichen + Nullterminator + int red, green, blue; + + // Parse die Zeile im Format "HHMM,R,G,B" + int items_scanned = sscanf(line, "%4[^,],%d,%d,%d", time, &red, &green, &blue); + if (items_scanned == 4) + { + add_light_item(time, (uint8_t)red, (uint8_t)green, (uint8_t)blue); + } + else + { + ESP_LOGW(TAG, "Could not parse line: %s", line); + } + } + + fclose(f); + ESP_LOGI(TAG, "Finished loading file."); +} \ No newline at end of file diff --git a/firmware/main/CMakeLists.txt b/firmware/main/CMakeLists.txt index f910728..bd98993 100755 --- a/firmware/main/CMakeLists.txt +++ b/firmware/main/CMakeLists.txt @@ -11,6 +11,7 @@ idf_component_register(SRCS connectivity-manager led-manager persistence-manager + simulator u8g2 nvs_flash esp_timer @@ -19,3 +20,5 @@ idf_component_register(SRCS app_update rmaker_common ) + +spiffs_create_partition_image(storage ../spiffs_image FLASH_IN_PROJECT) diff --git a/firmware/main/main.cpp b/firmware/main/main.cpp index f0aca25..119eda9 100644 --- a/firmware/main/main.cpp +++ b/firmware/main/main.cpp @@ -10,6 +10,7 @@ #include "led_status.h" #include "nvs_flash.h" #include "sdkconfig.h" +#include "simulator.h" #include "wifi_manager.h" #ifdef __cplusplus @@ -33,6 +34,7 @@ extern "C" register_handler(); xTaskCreatePinnedToCore(app_task, "app_task", 4096, NULL, tskIDLE_PRIORITY + 1, NULL, portNUM_PROCESSORS - 1); + xTaskCreatePinnedToCore(simulate, "simulate", 4096, NULL, tskIDLE_PRIORITY + 1, NULL, portNUM_PROCESSORS - 1); // xTaskCreatePinnedToCore(ble_manager_task, "ble_manager", 4096, NULL, tskIDLE_PRIORITY + 1, NULL, // portNUM_PROCESSORS - 1); diff --git a/firmware/partitions.csv b/firmware/partitions.csv index 7729c75..e6dca74 100644 --- a/firmware/partitions.csv +++ b/firmware/partitions.csv @@ -1,6 +1,7 @@ # Name , Type , SubType , Offset , Size , Flags nvs , data , nvs , 0x9000 , 24k , phy_init , data , phy , , 4k , -factory , app , factory , 0x10000 , 3072K , +factory , app , factory , 0x10000 , 2048K , +storage , data , spiffs , , 1024K , coredump , data , coredump , , 576k , fctry , data , nvs , , 24k , diff --git a/firmware/sdkconfig.defaults b/firmware/sdkconfig.defaults index f08c42a..a4dd8c4 100755 --- a/firmware/sdkconfig.defaults +++ b/firmware/sdkconfig.defaults @@ -35,3 +35,10 @@ CONFIG_ESP32_CORE_DUMP_MAX_TASKS_NUM=64 CONFIG_ESP32_CORE_DUMP_STACK_SIZE=1024 CONFIG_ESP_RMAKER_DEF_TIMEZONE="Europe/Berlin" + +# ESP PSRAM +CONFIG_SPIRAM=y + +# SPI RAM config +CONFIG_SPIRAM_SPEED=80 +CONFIG_SPIRAM_USE_CAPS_ALLOC=y diff --git a/firmware/spiffs_image/schema_01.csv b/firmware/spiffs_image/schema_01.csv new file mode 100644 index 0000000..f6bf6ef --- /dev/null +++ b/firmware/spiffs_image/schema_01.csv @@ -0,0 +1,48 @@ +0000,25,25,112 +0030,25,25,112 +0100,25,25,112 +0130,25,25,112 +0200,25,25,112 +0230,25,25,112 +0300,25,25,112 +0330,25,25,112 +0400,25,25,112 +0430,140,25,112 +0500,255,130,112 +0530,255,155,112 +0600,255,177,115 +0630,255,200,135 +0700,255,219,170 +0730,255,234,205 +0800,255,249,240 +0830,255,249,250 +0900,239,245,255 +0930,224,240,255 +1000,215,235,255 +1030,212,234,255 +1100,210,233,255 +1130,208,232,255 +1200,207,231,255 +1230,205,230,255 +1300,204,229,255 +1330,204,229,255 +1400,206,230,255 +1430,208,231,255 +1500,213,232,255 +1530,219,234,255 +1600,229,239,255 +1630,236,246,255 +1700,255,252,251 +1730,255,243,236 +1800,255,225,202 +1830,255,203,174 +1900,255,178,129 +1930,255,146,85 +2000,255,93,38 +2030,140,55,70 +2100,25,25,112 +2130,25,25,112 +2200,25,25,112 +2230,25,25,112 +2300,25,25,112 +2330,25,25,112 diff --git a/firmware/spiffs_image/schema_02.csv b/firmware/spiffs_image/schema_02.csv new file mode 100644 index 0000000..a4d7fc0 --- /dev/null +++ b/firmware/spiffs_image/schema_02.csv @@ -0,0 +1,48 @@ +0000,25,25,112 +0030,25,25,112 +0100,25,25,112 +0130,25,25,112 +0200,25,25,112 +0230,25,25,112 +0300,25,25,112 +0330,62,25,95 +0400,102,60,78 +0430,140,78,61 +0500,178,95,44 +0530,214,113,27 +0600,255,130,10 +0630,255,139,22 +0700,255,147,34 +0730,255,155,46 +0800,255,163,58 +0830,255,172,70 +0900,255,180,82 +0930,255,189,93 +1000,255,197,105 +1030,255,205,117 +1100,255,213,129 +1130,255,222,141 +1200,255,230,153 +1230,255,222,143 +1300,255,214,133 +1330,255,206,124 +1400,255,198,114 +1430,255,191,104 +1500,255,183,94 +1530,255,175,84 +1600,255,167,74 +1630,255,159,64 +1700,255,151,55 +1730,255,143,45 +1800,255,135,35 +1830,214,116,45 +1900,178,98,61 +1930,140,80,74 +2000,102,62,86 +2030,63,44,99 +2100,25,25,112 +2130,25,25,112 +2200,25,25,112 +2230,25,25,112 +2300,25,25,112 +2330,25,25,112