From 947346777f051444c5f337af3bb734ec9cb46aac Mon Sep 17 00:00:00 2001 From: Eduard Iten Date: Wed, 1 Apr 2026 16:06:40 +0200 Subject: [PATCH] Audio added to firmware, Website File handling --- firmware/boards/nrf52840dk_nrf52840.overlay | 27 + firmware/libs/CMakeLists.txt | 4 +- firmware/libs/Kconfig | 4 +- firmware/libs/audio/CMakeLists.txt | 5 + firmware/libs/audio/Kconfig | 60 ++ firmware/libs/audio/include/audio.h | 33 + firmware/libs/audio/src/audio.c | 622 ++++++++++++++++++ firmware/libs/ble_mgmt/Kconfig | 82 ++- firmware/libs/buzz_proto/Kconfig | 1 + firmware/libs/buzz_proto/src/buzz_proto.c | 6 +- firmware/libs/event_mgmt/CMakeLists.txt | 5 + firmware/libs/event_mgmt/Kconfig | 10 + firmware/libs/event_mgmt/include/event_mgmt.h | 29 + firmware/libs/event_mgmt/src/event_mgmt.c | 3 + firmware/libs/fs_mgmt/Kconfig | 6 + firmware/libs/fs_mgmt/include/fs_mgmt.h | 31 +- firmware/libs/fs_mgmt/src/fs_mgmt.c | 20 +- firmware/libs/fw_mgmt/Kconfig | 1 + firmware/prj.conf | 63 +- firmware/src/main.c | 35 +- webpage/src/components/FileListItem.svelte | 13 +- webpage/src/lib/db.ts | 14 + 22 files changed, 951 insertions(+), 123 deletions(-) create mode 100644 firmware/libs/audio/CMakeLists.txt create mode 100644 firmware/libs/audio/Kconfig create mode 100644 firmware/libs/audio/include/audio.h create mode 100644 firmware/libs/audio/src/audio.c create mode 100644 firmware/libs/event_mgmt/CMakeLists.txt create mode 100644 firmware/libs/event_mgmt/Kconfig create mode 100644 firmware/libs/event_mgmt/include/event_mgmt.h create mode 100644 firmware/libs/event_mgmt/src/event_mgmt.c diff --git a/firmware/boards/nrf52840dk_nrf52840.overlay b/firmware/boards/nrf52840dk_nrf52840.overlay index 79d2c24..7d5f608 100644 --- a/firmware/boards/nrf52840dk_nrf52840.overlay +++ b/firmware/boards/nrf52840dk_nrf52840.overlay @@ -4,5 +4,32 @@ }; aliases { qspi-flash = &mx25r64; + i2s-audio = &i2s0; }; }; + +&pinctrl { + i2s0_default: i2s0_default { + group1 { + psels = , /* SCK/Bit Clock */ + , /* WS/Word Select */ + ; /* SD/Serial Data */ + }; + }; + + i2s0_sleep: i2s0_sleep { + group1 { + psels = , + , + ; + low-power-enable; + }; + }; +}; + +&i2s0 { + status = "okay"; + pinctrl-0 = <&i2s0_default>; + pinctrl-1 = <&i2s0_sleep>; + pinctrl-names = "default", "sleep"; +}; \ No newline at end of file diff --git a/firmware/libs/CMakeLists.txt b/firmware/libs/CMakeLists.txt index e1c20f3..52a299a 100644 --- a/firmware/libs/CMakeLists.txt +++ b/firmware/libs/CMakeLists.txt @@ -1,4 +1,6 @@ add_subdirectory(fw_mgmt) add_subdirectory(fs_mgmt) add_subdirectory(ble_mgmt) -add_subdirectory(buzz_proto) \ No newline at end of file +add_subdirectory(buzz_proto) +add_subdirectory(audio) +add_subdirectory(event_mgmt) \ No newline at end of file diff --git a/firmware/libs/Kconfig b/firmware/libs/Kconfig index 01efc5f..89388ed 100644 --- a/firmware/libs/Kconfig +++ b/firmware/libs/Kconfig @@ -1,4 +1,6 @@ rsource "fw_mgmt/Kconfig" rsource "fs_mgmt/Kconfig" rsource "ble_mgmt/Kconfig" -rsource "buzz_proto/Kconfig" \ No newline at end of file +rsource "buzz_proto/Kconfig" +rsource "audio/Kconfig" +rsource "event_mgmt/Kconfig" \ No newline at end of file diff --git a/firmware/libs/audio/CMakeLists.txt b/firmware/libs/audio/CMakeLists.txt new file mode 100644 index 0000000..dce6f25 --- /dev/null +++ b/firmware/libs/audio/CMakeLists.txt @@ -0,0 +1,5 @@ +if(CONFIG_AUDIO) + zephyr_library() + zephyr_library_sources(src/audio.c) + zephyr_include_directories(include) +endif() \ No newline at end of file diff --git a/firmware/libs/audio/Kconfig b/firmware/libs/audio/Kconfig new file mode 100644 index 0000000..ed0fb48 --- /dev/null +++ b/firmware/libs/audio/Kconfig @@ -0,0 +1,60 @@ +menuconfig AUDIO + bool "Audio handling" + default y + select I2S + select POLL + +if AUDIO + config AUDIO_NO_SAMPLES_SAMPLE + string "Audio no samples sample" + default "404" + help + Sound do play when no audio files are available. Must be in the sys directory of the filesystem. + config AUDIO_CACHE_SLAB_SIZE + int "Audio slab size" + default 4096 + help + Audio cache slab size + config AUDIO_CACHE_SLAB_COUNT + int "Audio slab count" + default 4 + help + Number of audio slabs in cache + + config AUDIO_THREAD_STACK_SIZE + int "Audio thread stack size" + default 4096 + help + Stack size for audio processing thread + config AUDIO_THREAD_PRIORITY + int "Audio thread priority" + default 5 + help + Priority for audio processing thread (lower number = higher priority) + + config AUDIO_PUMP_THREAD_STACK_SIZE + int "Audio pump thread stack size" + default 8192 + help + Stack size for audio pump thread + config AUDIO_PUMP_THREAD_PRIORITY + int "Audio pump thread priority" + default 4 + help + Priority for audio pump thread (lower number = higher priority) + + config AUDIO_WORKQUEUE_STACK_SIZE + int "Audio workqueue stack size" + default 2048 + help + Stack size for audio workqueue + config AUDIO_WORKQUEUE_PRIORITY + int "Audio workqueue priority" + default 10 + help + Priority for audio workqueue (lower number = higher priority) + + module = AUDIO + module-str = audio + source "subsys/logging/Kconfig.template.log_config" +endif # AUDIO \ No newline at end of file diff --git a/firmware/libs/audio/include/audio.h b/firmware/libs/audio/include/audio.h new file mode 100644 index 0000000..58c52d6 --- /dev/null +++ b/firmware/libs/audio/include/audio.h @@ -0,0 +1,33 @@ +#ifndef AUDIO_H +#define AUDIO_H +#include + +#include "fs_mgmt.h" + +/** Audio command message structure */ +struct audio_cmd_msg +{ + char filename[CONFIG_FS_MGMT_MAX_PATH_LENGTH]; // Wenn leer, nutze Fallback-Sound + bool is_interrupt; // True = sofort abbrechen, False = Enqueue +}; + +/** + * @brief Queues an audio playback command to the audio thread + * @param filename Name of the audio file to play. If empty, a random sound will be played + * @param is_interrupt If true, the command will interrupt any currently playing audio. If + * false, it will be enqueued and played after the current audio finishes + * @return 0 on success, negative error code on failure + */ +int audio_queue_play(const char *filename, bool is_interrupt); + +/** + * @brief Starts playback of a random audio file from the audio directory. This is a non-blocking call that signals the audio thread to select and play a random sound. + */ +void audio_start_random_playback(void); + +/** + * @brief Refreshes the list of available audio files + */ +void audio_refresh_files(void); + +#endif /* AUDIO_H */ \ No newline at end of file diff --git a/firmware/libs/audio/src/audio.c b/firmware/libs/audio/src/audio.c new file mode 100644 index 0000000..7887571 --- /dev/null +++ b/firmware/libs/audio/src/audio.c @@ -0,0 +1,622 @@ +#include +#include +#include +#include + +#include "audio.h" +#include "fs_mgmt.h" +#include "event_mgmt.h" + +LOG_MODULE_REGISTER(audio, CONFIG_AUDIO_LOG_LEVEL); + +const struct device *i2s_dev = DEVICE_DT_GET(DT_ALIAS(i2s_audio)); + +K_MSGQ_DEFINE(audio_cmd_q, sizeof(struct audio_cmd_msg), 10, 4); +K_SEM_DEFINE(audio_files_count_sem, 0, 1); +K_SEM_DEFINE(audio_file_select_sem, 0, 1); + +K_MEM_SLAB_DEFINE(audio_cache_slab, CONFIG_AUDIO_CACHE_SLAB_SIZE, CONFIG_AUDIO_CACHE_SLAB_COUNT, 4); + +struct k_work_q audio_work_q; +K_THREAD_STACK_DEFINE(audio_work_q_stack, CONFIG_AUDIO_WORKQUEUE_STACK_SIZE); +struct k_work select_next_file_work; + +enum audio_thread_state_t +{ + AUDIO_ARMED, + AUDIO_PRECACHING, + AUDIO_WAIT_FOR_CACHE, + AUDIO_PLAYING, + AUDIO_DRAINING, +}; + +#define EV_PLAY_RANDOM BIT(0) +#define EV_MSGQ_NOT_EMPTY BIT(1) +#define EV_CACHE_READY BIT(2) +#define EV_CACHE_DONE BIT(3) +#define EV_STATE_STEP BIT(4) +#define EV_AUTOSTART BIT(5) +#define EV_ALL (EV_PLAY_RANDOM | EV_MSGQ_NOT_EMPTY | EV_CACHE_READY | EV_CACHE_DONE | EV_STATE_STEP | EV_AUTOSTART) + +K_EVENT_DEFINE(audio_events); + +#define AUDIO_CACHE_EVT_START BIT(0) +#define AUDIO_CACHE_EVT_STOP BIT(1) + +K_EVENT_DEFINE(audio_cache_event); + +struct audio_ctx_t +{ + char next_file_name[CONFIG_FS_MGMT_MAX_PATH_LENGTH]; + struct fs_file_t file; + bool is_file_open; + ssize_t audio_size; + ssize_t cached_bytes; +} audio_ctx; + +static struct i2s_config i2s_cfg = { + .word_size = 16, + .channels = 2, + .format = I2S_FMT_DATA_FORMAT_I2S, + .options = I2S_OPT_BIT_CLK_MASTER | I2S_OPT_FRAME_CLK_MASTER, + .frame_clk_freq = 16000, + .mem_slab = &audio_cache_slab, + .block_size = CONFIG_AUDIO_CACHE_SLAB_SIZE, + .timeout = SYS_FOREVER_MS, +}; + +K_MUTEX_DEFINE(audio_ctx_mutex); + +atomic_t thread_state = ATOMIC_INIT(0); +atomic_t num_files = ATOMIC_INIT(0); + +static uint8_t audio_mono_stage[CONFIG_AUDIO_CACHE_SLAB_SIZE / 2]; + +int audio_queue_play(const char *filename, bool is_interrupt) +{ + if (is_interrupt) + { + /* Keep hardware state changes inside audio_thread to avoid cross-thread races. */ + k_msgq_purge(&audio_cmd_q); + } + + if (strlen(filename) == 0) + { + return 0; + } + if (k_msgq_num_free_get(&audio_cmd_q) == 0) + { + LOG_ERR("Audio command queue is full, cannot enqueue new command"); + return -ENOMEM; + } + + struct audio_cmd_msg cmd; + strncpy(cmd.filename, filename, sizeof(cmd.filename) - 1); + cmd.filename[sizeof(cmd.filename) - 1] = '\0'; + cmd.is_interrupt = is_interrupt; + + if (k_msgq_put(&audio_cmd_q, &cmd, K_FOREVER) != 0) + { + LOG_ERR("Failed to enqueue audio command"); + return -EFAULT; + } + + /* + * Wake immediately for interrupts or when not currently playing. + * Non-interrupt commands during playback are picked up after drain. + */ + if (is_interrupt || (atomic_get(&thread_state) != AUDIO_PLAYING)) + { + k_event_set(&audio_events, EV_MSGQ_NOT_EMPTY); + } + + LOG_DBG("Enqueued audio command: filename='%s', is_interrupt=%d", cmd.filename, cmd.is_interrupt); + return 0; +} + +static int audio_select_random_sound(void) +{ + k_sem_reset(&audio_file_select_sem); + if (k_sem_take(&audio_files_count_sem, K_FOREVER) != 0) + { + LOG_ERR("Failed to take audio files count semaphore"); + k_sem_give(&audio_files_count_sem); + k_mutex_unlock(&audio_ctx_mutex); + return -EFAULT; + } + + int count = atomic_get(&num_files); + + k_mutex_lock(&audio_ctx_mutex, K_FOREVER); + if (count == 0) + { + LOG_WRN("No audio files available to select, returning no files sound"); + FS_MGMT_ASSEMBLE_PATH(audio_ctx.next_file_name, FS_SYSTEM_PATH, CONFIG_AUDIO_NO_SAMPLES_SAMPLE); + + k_sem_give(&audio_file_select_sem); + k_sem_give(&audio_files_count_sem); + k_mutex_unlock(&audio_ctx_mutex); + return -ENOENT; + } + + int random_index = k_cycle_get_32() % count; + struct fs_dir_t dir; + struct fs_dirent entry; + int rc; + + fs_dir_t_init(&dir); + rc = fs_mgmt_pm_opendir(&dir, FS_AUDIO_PATH); + if (rc < 0) + { + LOG_ERR("Failed to open audio directory '%s': %d", FS_AUDIO_PATH, rc); + k_sem_give(&audio_file_select_sem); + k_sem_give(&audio_files_count_sem); + k_mutex_unlock(&audio_ctx_mutex); + return rc; + } + + int current_index = 0; + bool found = false; + while (1) + { + rc = fs_readdir(&dir, &entry); + if (rc < 0) + { + LOG_ERR("Directory read error: %d", rc); + break; + } + if (entry.name[0] == '\0') + { + break; + } + if (entry.type == FS_DIR_ENTRY_FILE) + { + if (current_index == random_index) + { + FS_MGMT_ASSEMBLE_PATH(audio_ctx.next_file_name, FS_AUDIO_PATH, entry.name); + LOG_DBG("Selected random audio file: %s", audio_ctx.next_file_name); + found = true; + break; + } + current_index++; + } + } + + fs_mgmt_pm_closedir(&dir); + k_sem_give(&audio_file_select_sem); + k_sem_give(&audio_files_count_sem); + k_mutex_unlock(&audio_ctx_mutex); + return found ? 0 : -ENOENT; +} + +void audio_refresh_files(void) +{ + // Lokale Strukturen verwenden, um Reentrancy-Probleme zu vermeiden + struct fs_dir_t dir; + struct fs_dirent entry; + int count = 0; + int rc; + + k_sem_reset(&audio_files_count_sem); + + fs_dir_t_init(&dir); + // Nutze deinen PM-Wrapper für den Flash-Zugriff + rc = fs_mgmt_pm_opendir(&dir, FS_AUDIO_PATH); + if (rc < 0) + { + LOG_ERR("Failed to open audio directory '%s': %d", FS_AUDIO_PATH, rc); + atomic_set(&num_files, 0); + return; + } + + while (1) + { + rc = fs_readdir(&dir, &entry); + if (rc < 0) + { + LOG_ERR("Directory read error: %d", rc); + break; + } + if (entry.name[0] == '\0') + { + break; + } + if (entry.type == FS_DIR_ENTRY_FILE) + { + count++; + } + } + + fs_mgmt_pm_closedir(&dir); + k_sem_give(&audio_files_count_sem); + atomic_set(&num_files, count); + audio_select_random_sound(); +} + +static void select_next_file_work_handler(struct k_work *work) +{ + ARG_UNUSED(work); + LOG_DBG("Select next file work handler"); + audio_select_random_sound(); +} + +static int audio_init(void) +{ + struct k_work_queue_config audio_work_q_config = { + .name = "audio_work_q", + .no_yield = false, + .essential = true, + .work_timeout_ms = 0}; + + k_work_queue_start(&audio_work_q, + audio_work_q_stack, + K_THREAD_STACK_SIZEOF(audio_work_q_stack), + CONFIG_AUDIO_WORKQUEUE_PRIORITY, &audio_work_q_config); + + k_work_init(&select_next_file_work, select_next_file_work_handler); + + if (!device_is_ready(i2s_dev)) + { + LOG_ERR("I2S device not ready"); + return -ENODEV; + } + + int rc = i2s_configure(i2s_dev, I2S_DIR_TX, &i2s_cfg); + if (rc < 0) + { + LOG_ERR("Failed to configure I2S: %d", rc); + return rc; + } + + LOG_DBG("Audio module initialized"); + return 0; +} + +SYS_INIT(audio_init, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY); // Stelle sicher, dass dies nach der FS-Initialisierung erfolgt + +static void audio_trigger_next_file_selection(void) +{ + k_sem_reset(&audio_file_select_sem); + LOG_DBG("Triggering workq file selection"); + k_work_submit_to_queue(&audio_work_q, &select_next_file_work); +} + +void audio_start_random_playback(void) +{ + LOG_DBG("audio_start_random_playback called"); + k_event_set(&audio_events, EV_PLAY_RANDOM); +} + +void audio_thread(void *arg1, void *arg2, void *arg3) +{ + ARG_UNUSED(arg1); + ARG_UNUSED(arg2); + ARG_UNUSED(arg3); + + k_event_wait(&event_mgmt_events, EVENT_MGMT_FS_READY, false, K_FOREVER); + + audio_refresh_files(); + atomic_set(&thread_state, AUDIO_PRECACHING); + k_event_set(&audio_events, EV_STATE_STEP); + + while (1) + { + enum audio_thread_state_t state = atomic_get(&thread_state); + k_timeout_t timeout = (state == AUDIO_DRAINING) ? K_MSEC(10) : K_FOREVER; + + uint32_t active_events = k_event_wait(&audio_events, EV_ALL, false, timeout); + + if (active_events & EV_STATE_STEP) + { + k_event_clear(&audio_events, EV_STATE_STEP); + } + + if (active_events & EV_PLAY_RANDOM) + { + LOG_DBG("Play random event received"); + k_event_clear(&audio_events, EV_PLAY_RANDOM); + + if (state == AUDIO_ARMED) + { + i2s_trigger(i2s_dev, I2S_DIR_TX, I2S_TRIGGER_START); + atomic_set(&thread_state, AUDIO_PLAYING); + } + else + { + k_event_set(&audio_cache_event, AUDIO_CACHE_EVT_STOP); + i2s_trigger(i2s_dev, I2S_DIR_TX, I2S_TRIGGER_DROP); + + audio_select_random_sound(); + atomic_set(&thread_state, AUDIO_PRECACHING); + k_event_set(&audio_events, EV_STATE_STEP); + } + continue; + } + + switch (state) + { + case AUDIO_PRECACHING: + LOG_DBG("Audio thread starting precache task"); + k_event_set(&audio_cache_event, AUDIO_CACHE_EVT_START); + atomic_set(&thread_state, AUDIO_WAIT_FOR_CACHE); + break; + + case AUDIO_WAIT_FOR_CACHE: + if (active_events & EV_CACHE_READY) + { + k_event_clear(&audio_events, EV_CACHE_READY); + atomic_set(&thread_state, AUDIO_ARMED); + + if (k_event_wait(&audio_events, EV_AUTOSTART, false, K_NO_WAIT) & EV_AUTOSTART) + { + k_event_clear(&audio_events, EV_AUTOSTART); + i2s_trigger(i2s_dev, I2S_DIR_TX, I2S_TRIGGER_START); + atomic_set(&thread_state, AUDIO_PLAYING); + LOG_DBG("Autostarting queued audio playback"); + } + else + { + LOG_DBG("System Armed. Waiting for Buzzer..."); + audio_trigger_next_file_selection(); + } + } + break; + + case AUDIO_ARMED: + if (active_events & EV_MSGQ_NOT_EMPTY) + { + k_event_clear(&audio_events, EV_MSGQ_NOT_EMPTY); + i2s_trigger(i2s_dev, I2S_DIR_TX, I2S_TRIGGER_DROP); + k_event_set(&audio_cache_event, AUDIO_CACHE_EVT_STOP); + + struct audio_cmd_msg cmd; + k_msgq_get(&audio_cmd_q, &cmd, K_NO_WAIT); + + k_mutex_lock(&audio_ctx_mutex, K_FOREVER); + strncpy(audio_ctx.next_file_name, cmd.filename, sizeof(audio_ctx.next_file_name) - 1); + audio_ctx.next_file_name[sizeof(audio_ctx.next_file_name) - 1] = '\0'; + k_mutex_unlock(&audio_ctx_mutex); + + k_event_set(&audio_events, EV_AUTOSTART); + atomic_set(&thread_state, AUDIO_PRECACHING); + k_event_set(&audio_events, EV_STATE_STEP); + } + break; + + case AUDIO_PLAYING: + if (active_events & EV_MSGQ_NOT_EMPTY) + { + k_event_clear(&audio_events, EV_MSGQ_NOT_EMPTY); + + struct audio_cmd_msg cmd; + if (k_msgq_peek(&audio_cmd_q, &cmd) == 0) + { + if (cmd.is_interrupt) + { + i2s_trigger(i2s_dev, I2S_DIR_TX, I2S_TRIGGER_DROP); + k_event_set(&audio_cache_event, AUDIO_CACHE_EVT_STOP); + + if (k_msgq_get(&audio_cmd_q, &cmd, K_NO_WAIT) == 0) + { + k_mutex_lock(&audio_ctx_mutex, K_FOREVER); + strncpy(audio_ctx.next_file_name, cmd.filename, sizeof(audio_ctx.next_file_name) - 1); + audio_ctx.next_file_name[sizeof(audio_ctx.next_file_name) - 1] = '\0'; + k_mutex_unlock(&audio_ctx_mutex); + + k_event_set(&audio_events, EV_AUTOSTART); + atomic_set(&thread_state, AUDIO_PRECACHING); + k_event_set(&audio_events, EV_STATE_STEP); + } + } + else + { + LOG_DBG("Non-interrupt command queued during playback; will process after drain"); + } + } + + break; + } + + if (active_events & EV_CACHE_DONE) + { + k_event_clear(&audio_events, EV_CACHE_DONE); + i2s_trigger(i2s_dev, I2S_DIR_TX, I2S_TRIGGER_DRAIN); + atomic_set(&thread_state, AUDIO_DRAINING); + } + break; + + case AUDIO_DRAINING: + if (k_mem_slab_num_free_get(&audio_cache_slab) == CONFIG_AUDIO_CACHE_SLAB_COUNT) + { + LOG_DBG("Audio for file drained, ready for next file"); + if (k_msgq_num_used_get(&audio_cmd_q) > 0) + { + k_event_set(&audio_events, EV_MSGQ_NOT_EMPTY); + } + else + { + atomic_set(&thread_state, AUDIO_PRECACHING); + k_event_set(&audio_events, EV_STATE_STEP); + } + } + break; + } + } +} + +K_THREAD_DEFINE(audio_thread_id, CONFIG_AUDIO_THREAD_STACK_SIZE, audio_thread, NULL, NULL, NULL, + CONFIG_AUDIO_THREAD_PRIORITY, 0, 0); + +void audio_pump_thread(void *arg1, void *arg2, void *arg3) +{ + ARG_UNUSED(arg1); + ARG_UNUSED(arg2); + ARG_UNUSED(arg3); + + uint8_t num_channels = 1; + void *mem_slab; + while (1) + { + uint32_t events = k_event_wait(&audio_cache_event, AUDIO_CACHE_EVT_START | AUDIO_CACHE_EVT_STOP, false, K_FOREVER); + + if (events & AUDIO_CACHE_EVT_STOP) + { + k_mutex_lock(&audio_ctx_mutex, K_FOREVER); + if (audio_ctx.is_file_open) + { + fs_close(&audio_ctx.file); + audio_ctx.is_file_open = false; + } + k_mutex_unlock(&audio_ctx_mutex); + + k_event_clear(&audio_cache_event, AUDIO_CACHE_EVT_START | AUDIO_CACHE_EVT_STOP); + continue; + } + + int rc = k_mem_slab_alloc(&audio_cache_slab, &mem_slab, K_NO_WAIT); + if (rc == -ENOMEM) + { + k_event_set(&audio_events, EV_CACHE_READY); + rc = k_mem_slab_alloc(&audio_cache_slab, &mem_slab, K_FOREVER); + if (k_event_wait(&audio_cache_event, AUDIO_CACHE_EVT_STOP, false, K_NO_WAIT)) + { + k_mem_slab_free(&audio_cache_slab, &mem_slab); + continue; + } + } + + if (rc == 0) + { + k_mutex_lock(&audio_ctx_mutex, K_FOREVER); + + if (!audio_ctx.is_file_open) + { + rc = fs_mgmt_pm_open(&audio_ctx.file, audio_ctx.next_file_name, FS_O_READ); + if (rc < 0) + { + LOG_ERR("Failed to open audio file '%s': %d", audio_ctx.next_file_name, rc); + k_mem_slab_free(&audio_cache_slab, &mem_slab); + k_event_clear(&audio_cache_event, AUDIO_CACHE_EVT_START); + k_mutex_unlock(&audio_ctx_mutex); + continue; + } + + audio_ctx.is_file_open = true; + + // NEU: Größen- und Zähler-Initialisierung exklusiv hier! + audio_ctx.audio_size = fs_mgmt_get_audio_data_len(&audio_ctx.file); + audio_ctx.cached_bytes = 0; + + LOG_DBG("Audio file '%s' opened for caching, size: %d", audio_ctx.next_file_name, (int)audio_ctx.audio_size); + } + + if (audio_ctx.audio_size <= 0) + { + LOG_ERR("Invalid audio size for '%s': %d", audio_ctx.next_file_name, (int)audio_ctx.audio_size); + k_mem_slab_free(&audio_cache_slab, &mem_slab); + fs_close(&audio_ctx.file); + audio_ctx.is_file_open = false; + k_event_clear(&audio_cache_event, AUDIO_CACHE_EVT_START); + k_mutex_unlock(&audio_ctx_mutex); + continue; + } + + ssize_t remaining_bytes = audio_ctx.audio_size - audio_ctx.cached_bytes; + if (remaining_bytes <= 0) + { + k_mem_slab_free(&audio_cache_slab, &mem_slab); + fs_close(&audio_ctx.file); + audio_ctx.is_file_open = false; + k_event_clear(&audio_cache_event, AUDIO_CACHE_EVT_START); + k_mutex_unlock(&audio_ctx_mutex); + k_event_set(&audio_events, EV_CACHE_DONE); + continue; + } + + ssize_t bytes_to_read = MIN(CONFIG_AUDIO_CACHE_SLAB_SIZE / 2, remaining_bytes); + ssize_t bytes_read = fs_read(&audio_ctx.file, audio_mono_stage, bytes_to_read); + + if (bytes_read <= 0) // <= 0, um EOF (0) und Fehler (< 0) abzufangen + { + if (bytes_read < 0) + { + LOG_ERR("Failed to read audio data: %d", (int)bytes_read); + } + k_mem_slab_free(&audio_cache_slab, &mem_slab); + + // EOF erreicht -> Datei schließen und START-Bit löschen + fs_close(&audio_ctx.file); + audio_ctx.is_file_open = false; + k_event_clear(&audio_cache_event, AUDIO_CACHE_EVT_START); + k_mutex_unlock(&audio_ctx_mutex); + k_event_set(&audio_events, EV_CACHE_DONE); + continue; + } + + audio_ctx.cached_bytes += bytes_read; + // LOG_DBG("Cached %u%% ", (int)((audio_ctx.cached_bytes * 100) / audio_ctx.audio_size)); + + if (bytes_read > CONFIG_AUDIO_CACHE_SLAB_SIZE / 2) + { + LOG_ERR("Read size %d exceeds half slab size %d", (int)bytes_read, CONFIG_AUDIO_CACHE_SLAB_SIZE / 2); + k_mem_slab_free(&audio_cache_slab, &mem_slab); + fs_close(&audio_ctx.file); + audio_ctx.is_file_open = false; + k_event_clear(&audio_cache_event, AUDIO_CACHE_EVT_START); + k_mutex_unlock(&audio_ctx_mutex); + continue; + } + + if ((bytes_read & 0x1) != 0) + { + if (bytes_read >= (CONFIG_AUDIO_CACHE_SLAB_SIZE / 2)) + { + LOG_ERR("Odd mono byte count at half-slab boundary: %d", (int)bytes_read); + k_mem_slab_free(&audio_cache_slab, &mem_slab); + fs_close(&audio_ctx.file); + audio_ctx.is_file_open = false; + k_event_clear(&audio_cache_event, AUDIO_CACHE_EVT_START); + k_mutex_unlock(&audio_ctx_mutex); + continue; + } + + audio_mono_stage[bytes_read] = 0; + bytes_read++; + } + + if (bytes_read < CONFIG_AUDIO_CACHE_SLAB_SIZE / 2) + { + memset(audio_mono_stage + bytes_read, 0, (CONFIG_AUDIO_CACHE_SLAB_SIZE / 2) - bytes_read); + bytes_read = CONFIG_AUDIO_CACHE_SLAB_SIZE / 2; + } + + if (num_channels == 1) + { + uint8_t *dst = (uint8_t *)mem_slab; + + for (size_t i = 0; i < (size_t)bytes_read; i += 2) + { + uint8_t lo = audio_mono_stage[i]; + uint8_t hi = audio_mono_stage[i + 1]; + size_t out = i * 2; + + dst[out] = lo; + dst[out + 1] = hi; + dst[out + 2] = lo; + dst[out + 3] = hi; + } + } + + k_mutex_unlock(&audio_ctx_mutex); + if (i2s_write(i2s_dev, mem_slab, CONFIG_AUDIO_CACHE_SLAB_SIZE) < 0) + { + LOG_ERR("Failed to write audio data to I2S"); + k_mem_slab_free(&audio_cache_slab, &mem_slab); + continue; + } + } + } +} + +K_THREAD_DEFINE(audio_cache_thread_id, CONFIG_AUDIO_PUMP_THREAD_STACK_SIZE, audio_pump_thread, NULL, NULL, NULL, + CONFIG_AUDIO_PUMP_THREAD_PRIORITY, 0, 0); \ No newline at end of file diff --git a/firmware/libs/ble_mgmt/Kconfig b/firmware/libs/ble_mgmt/Kconfig index 28bbd30..d5c9cb8 100644 --- a/firmware/libs/ble_mgmt/Kconfig +++ b/firmware/libs/ble_mgmt/Kconfig @@ -1,5 +1,6 @@ menuconfig BLE_MGMT bool "Bluetooth Management" + default n select BT select BT_PERIPHERAL select BT_LOG_LEVEL_WARN @@ -27,35 +28,60 @@ if BLE_MGMT default 160 help Maximal advertising interval. 160 equals to 100ms. + + # Airtime + config BT_CTLR_SDC_MAX_CONN_EVENT_LEN_DEFAULT + default 4000000 + + # MTU Setup + config BT_BUF_ACL_RX_SIZE + default 502 + config BT_BUF_ACL_TX_SIZE + default 502 + config BT_L2CAP_TX_MTU + default 498 + config BT_CTLR_DATA_LENGTH_MAX + default 251 - # config BT_L2CAP_TX_MTU - # default 247 - # config BT_BUF_ACL_RX_SIZE - # default 251 - # config BT_BUF_ACL_TX_SIZE - # default 251 - # config BT_CTLR_DATA_LENGTH_MAX - # default 251 - # config BT_USER_DATA_LEN_UPDATE - # default y - # config BT_USER_PHY_UPDATE - # default y - # config BT_HCI_ACL_FLOW_CONTROL - # default y - # config BT_BUF_CMD_TX_COUNT - # default 24 - # config BT_BUF_EVT_RX_COUNT - # default 22 - # config BT_BUF_ACL_TX_COUNT - # default 20 - # config BT_L2CAP_TX_BUF_COUNT - # default 20 - # config BT_CONN_TX_MAX - # default 20 - # config BT_CTLR_SDC_TX_PACKET_COUNT - # default 20 - # config BT_CTLR_SDC_RX_PACKET_COUNT - # default 20 + # Buffers + config BT_BUF_ACL_TX_COUNT + default 15 + config BT_L2CAP_TX_BUF_COUNT + default 15 + config BT_CONN_TX_MAX + default 15 + config BT_CTLR_SDC_TX_PACKET_COUNT + default 15 + config BT_CTLR_SDC_RX_PACKET_COUNT + default 15 + config BT_BUF_EVT_RX_COUNT + default 16 + + # Callbacks + config BT_USER_PHY_UPDATE + default y + config BT_USER_DATA_LEN_UPDATE + default y + + # Automatic updates + config BT_AUTO_PHY_UPDATE + default y + config BT_AUTO_DATA_LEN_UPDATE + default y + config BT_GAP_AUTO_UPDATE_CONN_PARAMS + default y + + # Preferred defaults + config BT_PERIPHERAL_PREF_MIN_INT + default 6 + config BT_PERIPHERAL_PREF_MAX_INT + default 40 + config BT_PERIPHERAL_PREF_LATENCY + default 0 + config BT_PERIPHERAL_PREF_TIMEOUT + default 400 + + # Connections config BT_MAX_CONN default 2 diff --git a/firmware/libs/buzz_proto/Kconfig b/firmware/libs/buzz_proto/Kconfig index 166c264..b6d2062 100644 --- a/firmware/libs/buzz_proto/Kconfig +++ b/firmware/libs/buzz_proto/Kconfig @@ -1,5 +1,6 @@ menuconfig BUZZ_PROTO bool "Buzzer Protocol" + default y select CRC help Library for initializing and managing the buzzer protocol. diff --git a/firmware/libs/buzz_proto/src/buzz_proto.c b/firmware/libs/buzz_proto/src/buzz_proto.c index 8da2763..0ef801c 100644 --- a/firmware/libs/buzz_proto/src/buzz_proto.c +++ b/firmware/libs/buzz_proto/src/buzz_proto.c @@ -64,7 +64,7 @@ enum stream_state_t static enum stream_state_t current_stream = STREAM_IDLE; -static char src_path[FS_MGMT_MAX_PATH_LENGTH], dst_path[FS_MGMT_MAX_PATH_LENGTH]; +static char src_path[CONFIG_FS_MGMT_MAX_PATH_LENGTH], dst_path[CONFIG_FS_MGMT_MAX_PATH_LENGTH]; int buzz_proto_buf_alloc(uint8_t **buf) { @@ -246,7 +246,7 @@ void handle_fs_info_request(struct buzz_frame_msg *msg) resp_data->data_type = BUZZ_DATA_FS_INFO; resp_data->total_size = sys_cpu_to_le32(total_size); resp_data->free_size = sys_cpu_to_le32(free_size); - resp_data->max_path_length = FS_MGMT_MAX_PATH_LENGTH; + resp_data->max_path_length = CONFIG_FS_MGMT_MAX_PATH_LENGTH; resp_data->sys_path_length = strlen(FS_SYSTEM_PATH); resp_data->audio_path_length = strlen(FS_AUDIO_PATH); memcpy(resp_data->data, FS_SYSTEM_PATH, resp_data->sys_path_length); @@ -387,7 +387,7 @@ static void handle_file_get_request(struct buzz_frame_msg *msg, bool only_tags) if (only_tags) { - ssize_t audio_len = fs_get_audio_data_len(&get_file_state.file); + ssize_t audio_len = fs_mgmt_get_audio_data_len(&get_file_state.file); if (audio_len < 0) { LOG_ERR("Failed to get audio data len: %d", (int)audio_len); diff --git a/firmware/libs/event_mgmt/CMakeLists.txt b/firmware/libs/event_mgmt/CMakeLists.txt new file mode 100644 index 0000000..dca1ed8 --- /dev/null +++ b/firmware/libs/event_mgmt/CMakeLists.txt @@ -0,0 +1,5 @@ +if(CONFIG_EVENT_MGMT) + zephyr_library() + zephyr_library_sources(src/event_mgmt.c) + zephyr_include_directories(include) +endif() \ No newline at end of file diff --git a/firmware/libs/event_mgmt/Kconfig b/firmware/libs/event_mgmt/Kconfig new file mode 100644 index 0000000..af060d5 --- /dev/null +++ b/firmware/libs/event_mgmt/Kconfig @@ -0,0 +1,10 @@ +menuconfig EVENT_MGMT + bool "Event management" + default y + select EVENTS + +if EVENT_MGMT + module = EVENT_MGMT + module-str = event_mgmt + source "subsys/logging/Kconfig.template.log_config" +endif # EVENT_MGMT \ No newline at end of file diff --git a/firmware/libs/event_mgmt/include/event_mgmt.h b/firmware/libs/event_mgmt/include/event_mgmt.h new file mode 100644 index 0000000..bc18024 --- /dev/null +++ b/firmware/libs/event_mgmt/include/event_mgmt.h @@ -0,0 +1,29 @@ +#ifndef EVENT_MGMT_H +#define EVENT_MGMT_H + +#include + +#define EVENT_MGMT_FS_READY BIT(0) +#define EVENT_MGMT_AUDIO_READY BIT(1) +#define EVENT_MGMT_BLE_CONNECTED BIT(2) +#define EVENT_MGMT_BLE_DISCONNECTED BIT(3) + +extern struct k_event event_mgmt_events; + +static inline int event_mgmt_wait_for(uint32_t events, k_timeout_t timeout) +{ + uint32_t got = k_event_wait(&event_mgmt_events, events, false, timeout); + return (got & events) == events ? 0 : -ETIMEDOUT; +} + +static inline void event_mgmt_set_event(uint32_t event) +{ + k_event_post(&event_mgmt_events, event); +} + +static inline void event_mgmt_clear_event(uint32_t event) +{ + k_event_clear(&event_mgmt_events, event); +} + +#endif /* EVENT_MGMT_H */ \ No newline at end of file diff --git a/firmware/libs/event_mgmt/src/event_mgmt.c b/firmware/libs/event_mgmt/src/event_mgmt.c new file mode 100644 index 0000000..e55ec39 --- /dev/null +++ b/firmware/libs/event_mgmt/src/event_mgmt.c @@ -0,0 +1,3 @@ +#include "event_mgmt.h" + +K_EVENT_DEFINE(event_mgmt_events); \ No newline at end of file diff --git a/firmware/libs/fs_mgmt/Kconfig b/firmware/libs/fs_mgmt/Kconfig index 5b656db..6fb2efe 100644 --- a/firmware/libs/fs_mgmt/Kconfig +++ b/firmware/libs/fs_mgmt/Kconfig @@ -1,5 +1,6 @@ menuconfig FS_MGMT bool "File System Management" + default y select FLASH select FLASH_MAP select FILE_SYSTEM @@ -11,6 +12,11 @@ menuconfig FS_MGMT Library for initializing and managing the file system. if FS_MGMT + config FS_MGMT_MAX_PATH_LENGTH + int "Maximum File Path Length" + default 32 + help + Set the maximum length for file paths in the file system. Default is 32 characters. config FS_MGMT_MOUNT_POINT string "Littlefs Mount Point" default "/lfs" diff --git a/firmware/libs/fs_mgmt/include/fs_mgmt.h b/firmware/libs/fs_mgmt/include/fs_mgmt.h index 9a6d0de..2d10f79 100644 --- a/firmware/libs/fs_mgmt/include/fs_mgmt.h +++ b/firmware/libs/fs_mgmt/include/fs_mgmt.h @@ -4,32 +4,43 @@ #include #include "buzz_proto.h" -#define FS_MGMT_MAX_PATH_LENGTH 32 #define FS_AUDIO_PATH CONFIG_FS_MGMT_MOUNT_POINT CONFIG_FS_MGMT_AUDIO_SUBDIR #define FS_SYSTEM_PATH CONFIG_FS_MGMT_MOUNT_POINT CONFIG_FS_MGMT_SYSTEM_SUBDIR -/** - * @brief Initializes the filesystem management module. - */ -int fs_mgmt_init(void); +#define MAX_FILE_NAME_LEN(target, path) \ + ((int)(sizeof(target) - (sizeof(path)) - 1)) +/** @brief Assemble a full path from a base path and a filename + * Ensures that the resulting path fits into the target buffer and is null-terminated. If the filename is too long, it will be truncated to fit. + * @param buffer Target buffer to hold the assembled path + * @param path Base path (e.g. "/sys" or "/audio") + * @param filename Name of the file to append to the base path + */ +#define FS_MGMT_ASSEMBLE_PATH(buffer, path, filename) \ + snprintf(buffer, sizeof(buffer), \ + "%s/%.*s", \ + path, \ + MAX_FILE_NAME_LEN(buffer, path), \ + filename) /** * @brief OP-Codes for the FS write thread */ -enum fs_write_op { +enum fs_write_op +{ FS_WRITE_OP_FILE_START, FS_WRITE_OP_FILE_CHUNK, FS_WRITE_OP_FILE_END, - FS_WRITE_OP_TAGS_START, // Schon mal vorgesehen - FS_WRITE_OP_FW_START, // Schon mal vorgesehen + FS_WRITE_OP_TAGS_START, // Schon mal vorgesehen + FS_WRITE_OP_FW_START, // Schon mal vorgesehen FS_WRITE_OP_ABORT }; /** * @brief Structure representing a write message for the FS write thread */ -struct fs_write_msg { +struct fs_write_msg +{ enum fs_write_op op; uint8_t *slab_ptr; /* Basis-Pointer des Memory-Slabs (für k_mem_slab_free) */ uint16_t data_offset; /* Offset ab dem slab_ptr, wo die Nutzdaten beginnen */ @@ -135,7 +146,7 @@ int fs_mgmt_pm_rm_recursive(char *path, size_t max_len); * @param fp Pointer to an open fs_file_t structure representing the file * @return Length of the audio data on success, negative error code on failure */ -ssize_t fs_get_audio_data_len(struct fs_file_t *fp); +ssize_t fs_mgmt_get_audio_data_len(struct fs_file_t *fp); /** * @brief Submits a write message to the FS write thread, which will handle writing data to the filestem asynchronously, ensuring the flash is active during the operation diff --git a/firmware/libs/fs_mgmt/src/fs_mgmt.c b/firmware/libs/fs_mgmt/src/fs_mgmt.c index 222a91d..0be1317 100644 --- a/firmware/libs/fs_mgmt/src/fs_mgmt.c +++ b/firmware/libs/fs_mgmt/src/fs_mgmt.c @@ -7,6 +7,7 @@ #include "fs_mgmt.h" #include "buzz_proto.h" +#include "event_mgmt.h" LOG_MODULE_REGISTER(fs_mgmt, CONFIG_FS_MGMT_LOG_LEVEL); @@ -54,7 +55,7 @@ static struct { fs_thread_state_t state; struct fs_file_t file; - char filename[FS_MGMT_MAX_PATH_LENGTH]; + char filename[CONFIG_FS_MGMT_MAX_PATH_LENGTH]; uint32_t crc32; uint16_t unacked_chunks; off_t audio_len; // Offeset für Tags @@ -386,7 +387,7 @@ int fs_mgmt_pm_mkdir_recursive(char *path) return rc; } -int fs_mgmt_init(void) +static int fs_mgmt_init(void) { k_mutex_init(&flash_pm_lock); @@ -405,11 +406,19 @@ int fs_mgmt_init(void) LOG_ERR("Error mounting filesystem: %d", rc); return rc; } + fs_mgmt_pm_flash_suspend(); LOG_DBG("Filesystem mounted successfully"); + event_mgmt_set_event(EVENT_MGMT_FS_READY); return 0; } +/* * APPLICATION Level sorgt dafür, dass die Treiber (Flash/QSPI) + * bereits bereit sind. + * CONFIG_APPLICATION_INIT_PRIORITY ist ein guter Standardwert (meist 90). + */ +SYS_INIT(fs_mgmt_init, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY); + static int fs_get_tag_bounds(struct fs_file_t *fp, off_t file_size, size_t *audio_limit, size_t *payload_len, bool *has_tag) { @@ -465,7 +474,7 @@ static int fs_get_tag_bounds(struct fs_file_t *fp, off_t file_size, return 0; } -ssize_t fs_get_audio_data_len(struct fs_file_t *fp) +ssize_t fs_mgmt_get_audio_data_len(struct fs_file_t *fp) { off_t file_size; size_t audio_limit = 0U; @@ -572,8 +581,7 @@ static void fs_thread_entry(void *p1, void *p2, void *p3) if (rc == 0) { - // ssize_t audio_len = fs_get_audio_data_len(&write_ctx.file); - ssize_t audio_len = 0; /* Zum Testen, da wir ja kein echtes FS-Backend haben */ + ssize_t audio_len = fs_mgmt_get_audio_data_len(&write_ctx.file); if (audio_len < 0) { LOG_ERR("Failed to get audio length: %d", (int)audio_len); @@ -587,7 +595,7 @@ static void fs_thread_entry(void *p1, void *p2, void *p3) if (rc != 0) { LOG_ERR("Failed to truncate file: %d", rc); - // fs_mgmt_pm_close(&write_ctx.file); + fs_mgmt_pm_close(&write_ctx.file); buzz_proto_send_error_reusing_slab(msg.reply_cb, abs(rc), msg.slab_ptr); break; } diff --git a/firmware/libs/fw_mgmt/Kconfig b/firmware/libs/fw_mgmt/Kconfig index 6cabe92..2d63488 100644 --- a/firmware/libs/fw_mgmt/Kconfig +++ b/firmware/libs/fw_mgmt/Kconfig @@ -1,5 +1,6 @@ menuconfig FW_MGMT bool "Firmware Management" + default y select FLASH select FLASH_MAP select STREAM_FLASH diff --git a/firmware/prj.conf b/firmware/prj.conf index a13502c..0046e86 100644 --- a/firmware/prj.conf +++ b/firmware/prj.conf @@ -1,66 +1,21 @@ ### Logging CONFIG_LOG=y - -### File System -CONFIG_FS_MGMT=y -CONFIG_FS_LOG_LEVEL_WRN=y +CONFIG_AUDIO_LOG_LEVEL_DBG=y ### Bluetooth CONFIG_BLE_MGMT=y -# Advertising 500ms - 1s -CONFIG_BLE_MGMT_ADV_INT_MIN=160 -CONFIG_BLE_MGMT_ADV_INT_MAX=320 -### Firmware Management -CONFIG_FW_MGMT=y -CONFIG_FW_MGMT_LOG_LEVEL_DBG=y +### Error handling CONFIG_HW_STACK_PROTECTION=y CONFIG_RESET_ON_FATAL_ERROR=y -### Buzzer protocol -CONFIG_BUZZ_PROTO=y -CONFIG_BUZZ_PROTO_LOG_LEVEL_DBG=y - -## Power management +### Power management CONFIG_PM_DEVICE=y -# ## Shell -# # CONFIG_SHELL=y -# # CONFIG_FILE_SYSTEM_SHELL=y +### Stack +CONFIG_MAIN_STACK_SIZE=2048 +CONFIG_INIT_STACKS=y +CONFIG_THREAD_STACK_INFO=y +CONFIG_STACK_SENTINEL=y -# # Airtime-Maximierung -CONFIG_BT_CTLR_SDC_MAX_CONN_EVENT_LEN_DEFAULT=4000000 - -# MTU-Setup -CONFIG_BT_BUF_ACL_RX_SIZE=502 -CONFIG_BT_BUF_ACL_TX_SIZE=502 -CONFIG_BT_L2CAP_TX_MTU=498 -CONFIG_BT_CTLR_DATA_LENGTH_MAX=251 - -# Puffer-Konfiguration (TX = 15, EVT = 16) -CONFIG_BT_BUF_ACL_TX_COUNT=15 -CONFIG_BT_L2CAP_TX_BUF_COUNT=15 -CONFIG_BT_CONN_TX_MAX=15 -CONFIG_BT_CTLR_SDC_TX_PACKET_COUNT=15 -CONFIG_BT_CTLR_SDC_RX_PACKET_COUNT=15 -CONFIG_BT_BUF_EVT_RX_COUNT=16 - -# WICHTIG: Diese Flags aktivieren die Callbacks in der bt_conn_cb Struktur -CONFIG_BT_USER_PHY_UPDATE=y -CONFIG_BT_USER_DATA_LEN_UPDATE=y - -# Automatische Updates im Hintergrund aktivieren -CONFIG_BT_AUTO_PHY_UPDATE=y -CONFIG_BT_AUTO_DATA_LEN_UPDATE=y -CONFIG_BT_GAP_AUTO_UPDATE_CONN_PARAMS=y - -# Bevorzugte Parameter für das Auto-Update definieren (entspricht BT_LE_CONN_PARAM(12, 36, 0, 400)) -CONFIG_BT_PERIPHERAL_PREF_MIN_INT=12 -CONFIG_BT_PERIPHERAL_PREF_MAX_INT=40 -CONFIG_BT_PERIPHERAL_PREF_LATENCY=0 -CONFIG_BT_PERIPHERAL_PREF_TIMEOUT=400 - - -CONFIG_HEAP_MEM_POOL_SIZE=2048 - -CONFIG_BT_CENTRAL=n \ No newline at end of file +# CONFIG_LOG_MODE_IMMEDIATE=y \ No newline at end of file diff --git a/firmware/src/main.c b/firmware/src/main.c index 81de266..9a3a917 100644 --- a/firmware/src/main.c +++ b/firmware/src/main.c @@ -3,12 +3,14 @@ #include #include "fs_mgmt.h" -#include "ble_mgmt.h" #include "buzz_proto.h" #include "fw_mgmt.h" +#include "audio.h" LOG_MODULE_REGISTER(main); +#if IS_ENABLED(CONFIG_BLE_MGMT) +#include "ble_mgmt.h" void ble_rx_cb(const uint8_t *data, uint16_t len) { uint8_t *buf; @@ -42,32 +44,27 @@ void ble_rx_cb(const uint8_t *data, uint16_t len) buzz_proto_buf_free(&buf); /* Speicher bei Fehler sofort wieder freigeben */ } } +#endif int main(void) { - uint8_t hw_id[8]; - LOG_INF("Starting app version %s (state: 0x%02x zephyr %s) on %s (Rev: %s, SOC: %s)", fw_mgmt_get_fw_version_string(), fw_mgmt_get_fw_state(), fw_mgmt_get_kernel_version_string(), fw_mgmt_get_board_name(), strlen(fw_mgmt_get_board_revision()) ? fw_mgmt_get_board_revision() : "N/A", fw_mgmt_get_soc_name()); - if (fw_mgmt_get_id(hw_id, sizeof(hw_id)) >= 0) { - LOG_INF("Device EUI64: %02X%02X-%02X%02X-%02X%02X-%02X%02X", hw_id[0], hw_id[1], hw_id[2], hw_id[3], hw_id[4], hw_id[5], hw_id[6], hw_id[7]); - } else { - LOG_ERR("Failed to get device ID"); - } - - int rc; - - rc = fs_mgmt_init(); - if (rc < 0) { - LOG_ERR("Failed to initialize file system management: %d", rc); - return rc; - } - +#if IS_ENABLED(CONFIG_BLE_MGMT) /* BLE-Subsystem initialisieren und RX-Callback registrieren */ - rc = ble_mgmt_init(ble_rx_cb, CONFIG_BLE_MGMT_DEFAULT_DEVICE_NAME); + int rc = ble_mgmt_init(ble_rx_cb, CONFIG_BLE_MGMT_DEFAULT_DEVICE_NAME); if (rc < 0) { LOG_ERR("Failed to initialize BLE management: %d", rc); return rc; } +#endif - LOG_INF("Init complete"); + LOG_INF("Init complete. Starting audio playback test..."); + + k_sleep(K_SECONDS(1)); + audio_queue_play("/lfs/sys/update", false); + k_sleep(K_SECONDS(1)); + audio_start_random_playback(); // Starte die Wiedergabe eines zufälligen Sounds + k_sleep(K_SECONDS(1)); + audio_queue_play("/lfs/sys/404", true); + k_sleep(K_FOREVER); } \ No newline at end of file diff --git a/webpage/src/components/FileListItem.svelte b/webpage/src/components/FileListItem.svelte index b186b13..13d0372 100644 --- a/webpage/src/components/FileListItem.svelte +++ b/webpage/src/components/FileListItem.svelte @@ -13,6 +13,7 @@ CheckCircleIcon, WarningIcon, WarningCircleIcon, + CloudArrowDownIcon, } from "phosphor-svelte"; import { isTransferingRemote, @@ -28,7 +29,7 @@ import { tagEditorState } from "../lib/store"; import { tooltip } from "../lib/actions/tooltip"; import { deleteRemoteFile } from "../lib/transport"; - import { deleteLocalFile, playLocalFile } from "../lib/db"; + import { deleteLocalFile, playLocalFile, downloadLocalFile } from "../lib/db"; import { refreshRemote, refreshLocal } from "../lib/sync"; import { addToast } from "../lib/toast"; @@ -265,6 +266,16 @@ +