diff --git a/buzzer_tool/buzzer.py b/buzzer_tool/buzzer.py index b753c83..afe0a95 100644 --- a/buzzer_tool/buzzer.py +++ b/buzzer_tool/buzzer.py @@ -3,7 +3,7 @@ import argparse import sys from core.config import load_config from core.connection import BuzzerConnection, BuzzerError -from core.commands import info, ls, put, mkdir, rm, confirm, reboot +from core.commands import info, ls, put, mkdir, rm, confirm, reboot, play def main(): parser = argparse.ArgumentParser(description="Edis Buzzer Host Tool") @@ -38,10 +38,16 @@ def main(): rm_parser.add_argument("path", type=str, help="Pfad der zu löschenden Datei/Ordner") rm_parser.add_argument("-r", "--recursive", action="store_true", help="Ordnerinhalte rekursiv löschen") + # Befehl: play + play_parser = subparsers.add_parser("play", help="Spielt eine Datei auf dem Controller ab") + play_parser.add_argument("path", type=str, help="Pfad der abzuspielenden Datei (z.B. /lfs/a/neu)") + # Befehl: confirm confirm_parser = subparsers.add_parser("confirm", help="Bestätigt die aktuell laufende Firmware") + # Befehl: reboot reboot_parser = subparsers.add_parser("reboot", help="Startet den Buzzer neu") + # Argumente parsen args = parser.parse_args() config = load_config(args) @@ -87,6 +93,8 @@ def main(): confirm.execute(conn) elif args.command == "reboot": reboot.execute(conn) + elif args.command == "play": + play.execute(conn, path=args.path) elif args.command == "info" or args.command is None: # Wurde kein Befehl oder explizit 'info' angegeben, sind wir hier schon fertig pass diff --git a/buzzer_tool/core/commands/play.py b/buzzer_tool/core/commands/play.py new file mode 100644 index 0000000..b1c6f0b --- /dev/null +++ b/buzzer_tool/core/commands/play.py @@ -0,0 +1,10 @@ +# core/commands/mkdir.py +from core.connection import BuzzerError + +def execute(conn, path: str): + """Spielt eine Datei auf dem Controller ab.""" + try: + conn.send_command(f"play {path}") + print(f"▶️ Datei '{path}' wird abgespielt.") + except BuzzerError as e: + print(f"❌ Fehler beim Abspielen von '{path}': {e}") \ No newline at end of file diff --git a/firmware/CMakeLists.txt b/firmware/CMakeLists.txt index 82f9832..15fe1ba 100644 --- a/firmware/CMakeLists.txt +++ b/firmware/CMakeLists.txt @@ -10,6 +10,7 @@ target_sources(app PRIVATE src/audio.c src/usb.c src/protocol.c + src/utils.c ) zephyr_include_directories(src) diff --git a/firmware/VERSION b/firmware/VERSION index 4e53074..5c7a711 100644 --- a/firmware/VERSION +++ b/firmware/VERSION @@ -1,5 +1,5 @@ VERSION_MAJOR = 0 VERSION_MINOR = 1 -PATCHLEVEL = 8 +PATCHLEVEL = 11 VERSION_TWEAK = 0 EXTRAVERSION = 0 \ No newline at end of file diff --git a/firmware/src/audio.c b/firmware/src/audio.c index b08b97d..b10ca42 100644 --- a/firmware/src/audio.c +++ b/firmware/src/audio.c @@ -38,7 +38,7 @@ K_SEM_DEFINE(audio_ready_sem, 0, 1); static const struct device *const i2s_dev = DEVICE_DT_GET(I2S_NODE); static const struct gpio_dt_spec amp_en_dev = GPIO_DT_SPEC_GET(AUDIO_AMP_ENABLE_NODE, gpios); - +static volatile int current_volume = 8; static volatile bool abort_playback = false; static char next_random_filename[64] = {0}; @@ -219,6 +219,8 @@ void audio_thread(void *arg1, void *arg2, void *arg3) bool trigger_started = false; int queued_blocks = 0; + uint8_t factor = MIN(255, current_volume * 0xFF / 100); + LOG_INF("Volume factor: %u (for volume %d%%)", factor, current_volume); while (!abort_playback) { @@ -234,7 +236,7 @@ void audio_thread(void *arg1, void *arg2, void *arg3) if (abort_playback) { - k_mem_slab_free(&audio_slab, &block); + k_mem_slab_free(&audio_slab, block); break; } @@ -242,7 +244,7 @@ void audio_thread(void *arg1, void *arg2, void *arg3) if (bytes_read <= 0) { - k_mem_slab_free(&audio_slab, &block); + k_mem_slab_free(&audio_slab, block); break; } @@ -252,7 +254,7 @@ void audio_thread(void *arg1, void *arg2, void *arg3) for (int i = samples_read - 1; i >= 0; i--) { - int16_t sample = samples[i]; + int16_t sample = (samples[i] * factor) >> 8; // Lautstärkeanpassung samples[i * 2] = sample; samples[i * 2 + 1] = sample; } @@ -267,7 +269,7 @@ void audio_thread(void *arg1, void *arg2, void *arg3) /* Block in die DMA-Queue schieben */ if (i2s_write(i2s_dev, block, AUDIO_BLOCK_SIZE) < 0) { - k_mem_slab_free(&audio_slab, &block); + k_mem_slab_free(&audio_slab, block); break; } diff --git a/firmware/src/main.c b/firmware/src/main.c index 25adcbc..0c16dfc 100644 --- a/firmware/src/main.c +++ b/firmware/src/main.c @@ -14,6 +14,20 @@ #include #include #include +#include + +volatile static uint8_t reboot_code = 0; + +void init_reboot_status() +{ + reboot_code = get_reboot_status(); + if (reboot_code != 0) + { + LOG_INF("Device rebooted with status code: 0x%02x", reboot_code); + } +} + +SYS_INIT(init_reboot_status, POST_KERNEL, 0); LOG_MODULE_REGISTER(main, LOG_LEVEL_INF); @@ -24,25 +38,29 @@ int main(void) int rc; rc = fs_init(); - if (rc < 0) { + if (rc < 0) + { LOG_ERR("Filesystem initialization failed: %d", rc); return rc; } rc = audio_init(); - if (rc < 0) { + if (rc < 0) + { LOG_ERR("Audio initialization failed: %d", rc); return rc; } rc = usb_cdc_acm_init(); - if (rc < 0) { + if (rc < 0) + { LOG_ERR("USB initialization failed: %d", rc); return rc; } rc = io_init(); - if (rc < 0) { + if (rc < 0) + { LOG_ERR("I/O initialization failed: %d", rc); return rc; } @@ -54,6 +72,11 @@ int main(void) LOG_INF("Confirmation of firmware image pending. Inform user..."); audio_play("/lfs/sys/update"); } + else if (reboot_code == REBOOT_STATUS_FIRMWARE_CONFIRMED) + { + LOG_INF("Firmware was just confirmed in the last reboot. Playing confirmation sound."); + audio_play("/lfs/sys/confirm"); + } else { LOG_INF("Firmware image already confirmed. No need to confirm again."); diff --git a/firmware/src/protocol.c b/firmware/src/protocol.c index 598b136..d269ac6 100644 --- a/firmware/src/protocol.c +++ b/firmware/src/protocol.c @@ -6,8 +6,8 @@ #include #include #include -#include +#include #include #include #include @@ -41,7 +41,7 @@ void send_error(int32_t error_code) usb_write_buffer((const uint8_t *)response, strlen(response)); } -int send_ls(const char *path) +int cmd_ls(const char *path) { struct fs_dir_t dirp; struct fs_dirent entry; @@ -65,7 +65,7 @@ int send_ls(const char *path) return 0; } -int send_info() +int cmd_info() { char info[112]; struct fs_statvfs stat; @@ -80,7 +80,7 @@ int send_info() return 0; } -int put_binary_file(const char *filename, ssize_t filesize, uint32_t expected_crc32) +int cmd_put_binary_file(const char *filename, ssize_t filesize, uint32_t expected_crc32) { int rc; ssize_t bytes_written = 0; @@ -228,9 +228,7 @@ int put_binary_file(const char *filename, ssize_t filesize, uint32_t expected_cr } send_ok(); LOG_INF("Firmware upgrade requested, rebooting into bootloader..."); - k_sleep(K_MSEC(100)); // Kurze Pause, damit die OK-Antwort noch rausgeht - while (log_process()); - sys_reboot(SYS_REBOOT_COLD); + reboot_with_status(REBOOT_STATUS_FIRMWARE_UPDATE); } else { @@ -247,7 +245,7 @@ int put_binary_file(const char *filename, ssize_t filesize, uint32_t expected_cr return 0; } -int mkdir(const char *path) { +int cmd_mkdir(const char *path) { int rc = fs_pm_mkdir(path); if (rc < 0) { @@ -257,7 +255,7 @@ int mkdir(const char *path) { return rc; } -int rm(const char *path) { +int cmd_rm(const char *path) { int rc = fs_pm_unlink(path); if (rc < 0) { @@ -267,7 +265,7 @@ int rm(const char *path) { return rc; } -int confirm_firmware() { +int cmd_confirm_firmware() { int rc = boot_write_img_confirmed(); if (rc < 0) { @@ -275,19 +273,50 @@ int confirm_firmware() { return rc; } LOG_INF("Firmware confirmed successfully"); - audio_play("/lfs/sys/confirm"); + reboot_with_status(REBOOT_STATUS_FIRMWARE_CONFIRMED); return 0; } -int reboot_device() { +int cmd_reboot_device() { LOG_INF("Rebooting device as requested by host..."); send_ok(); - k_sleep(K_MSEC(100)); // Kurze Pause, damit die OK-Antwort noch rausgeht - while (log_process()); - sys_reboot(SYS_REBOOT_COLD); + reboot_with_status(REBOOT_STATUS_NORMAL); return 0; // Dieser Code wird nie erreicht, aber wir geben ihn der Vollständigkeit halber zurück } +void cmd_play(const char *filename) { + LOG_DBG("Play command received with filename: '%s'", filename); + audio_play(filename); +} + +int cmd_check(const char *param) { + LOG_DBG("Check command received with parameter: '%s'", param); + struct fs_file_t file; + fs_file_t_init(&file); + int rc = fs_pm_open(&file, param, FS_O_READ); + if (rc < 0) { + LOG_ERR("Check failed: file '%s' not found", param); + return -ENOENT; + } + uint32_t crc32 = 0; + uint8_t buffer[256]; + ssize_t read; + while ((read = fs_read(&file, buffer, sizeof(buffer))) > 0) + { + crc32 = crc32_ieee_update(crc32, buffer, read); + } + fs_pm_close(&file); + if (read < 0) { + LOG_ERR("Check failed: error reading file '%s': %d", param, (int)read); + return (int)read; + } + LOG_DBG("Check successful: file '%s' has CRC32 0x%08x", param, crc32); + char response[64]; + snprintf(response, sizeof(response), "CRC32 %s 0x%08x\n", param, crc32); + usb_write_buffer((const uint8_t *)response, strlen(response)); + return 0; +} + void execute_current_command(void) { int rc; @@ -295,7 +324,7 @@ void execute_current_command(void) { case CMD_LS: LOG_DBG("Executing LS command with parameters: '%s'", buffer); - rc = send_ls((char *)buffer); + rc = cmd_ls((char *)buffer); if (rc == 0) { send_ok(); @@ -311,7 +340,7 @@ void execute_current_command(void) LOG_WRN("INFO command received with unexpected parameters: '%s'", buffer); } LOG_DBG("Executing INFO command"); - rc = send_info(); + rc = cmd_info(); if (rc == 0) { send_ok(); @@ -334,7 +363,7 @@ void execute_current_command(void) break; } LOG_DBG("Executing PUT_BINARY_FILE command filename: '%s', filesize: %zd, crc32: 0x%08x", filename, filesize, crc32); - rc = put_binary_file(filename, filesize, crc32); + rc = cmd_put_binary_file(filename, filesize, crc32); if (rc == 0) { send_ok(); @@ -348,7 +377,7 @@ void execute_current_command(void) break; case CMD_MKDIR: LOG_DBG("Executing MKDIR command with parameters: '%s'", buffer); - rc = mkdir((char *)buffer); + rc = cmd_mkdir((char *)buffer); if (rc == 0) { send_ok(); } @@ -358,7 +387,7 @@ void execute_current_command(void) break; case CMD_RM: LOG_DBG("Executing RM command with parameters: '%s'", buffer); - rc = rm((char *)buffer); + rc = cmd_rm((char *)buffer); if (rc == 0) { send_ok(); audio_refresh_file_count(); // Nach erfolgreichem Löschen die Anzahl der verfügbaren Audiodateien aktualisieren @@ -369,22 +398,36 @@ void execute_current_command(void) break; case CMD_CONFIRM: LOG_DBG("Executing CONFIRM command"); - rc = confirm_firmware(); - if (rc == 0) - { send_ok(); - } - else - { + rc = cmd_confirm_firmware(); + if (rc != 0) { send_error(rc); + break; } + send_ok(); break; case CMD_REBOOT: LOG_DBG("Executing REBOOT command"); - rc = reboot_device(); + rc = cmd_reboot_device(); if (rc != 0) { send_error(rc); } break; + case CMD_PLAY: + LOG_DBG("Executing PLAY command"); + cmd_play((char *)buffer); + send_ok(); + break; + case CMD_CHECK: + LOG_DBG("Executing CHECK command"); + rc = cmd_check((char *)buffer); + if (rc == 0) { + send_ok(); + } + else + { + send_error(rc); + } + break; default: LOG_ERR("No execution logic for command %d", current_command); send_error(ENOSYS); @@ -435,15 +478,27 @@ protocol_state_t reading_command(uint8_t byte) { LOG_DBG("Received RM command"); current_command = CMD_RM; - } else if (strcmp((char *)buffer, "confirm") == 0) + } + else if (strcmp((char *)buffer, "confirm") == 0) { LOG_DBG("Received CONFIRM command"); current_command = CMD_CONFIRM; - } else if (strcmp((char *)buffer, "reboot") == 0) + } + else if (strcmp((char *)buffer, "reboot") == 0) { LOG_DBG("Received REBOOT command"); current_command = CMD_REBOOT; } + else if (strcmp((char *)buffer, "play") == 0) + { + LOG_DBG("Received PLAY command"); + current_command = CMD_PLAY; + } + else if (strcmp((char *)buffer, "check") == 0) + { + LOG_DBG("Received CHECK command"); + current_command = CMD_CHECK; + } else { LOG_DBG("Unknown command: %s", buffer); diff --git a/firmware/src/protocol.h b/firmware/src/protocol.h index 0b148e2..387f69c 100644 --- a/firmware/src/protocol.h +++ b/firmware/src/protocol.h @@ -17,6 +17,8 @@ typedef enum { CMD_RM, CMD_CONFIRM, CMD_REBOOT, + CMD_PLAY, + CMD_CHECK, /* Weitere Kommandos folgen hier */ } protocol_cmd_t; diff --git a/firmware/src/utils.c b/firmware/src/utils.c new file mode 100644 index 0000000..3d0ce71 --- /dev/null +++ b/firmware/src/utils.c @@ -0,0 +1,53 @@ +#include +#include +#include +#include +#include + +#if IS_ENABLED(CONFIG_SOC_SERIES_NRF52X) +#include +#elif IS_ENABLED(CONFIG_SOC_SERIES_STM32G0X) +#include +#endif + +LOG_MODULE_REGISTER(utils, LOG_LEVEL_DBG); + +/* Wir nutzen Register 0 für unseren Reboot-Status */ +#define REBOOT_STATUS_REG_IDX 0 + +void reboot_with_status(uint8_t status) +{ +#if IS_ENABLED(CONFIG_SOC_SERIES_NRF52X) + /* Korrigierter Aufruf mit Register-Index 0 */ + nrf_power_gpregret_set(NRF_POWER, REBOOT_STATUS_REG_IDX, (uint32_t)status); +#elif IS_ENABLED(CONFIG_SOC_SERIES_STM32G0X) + LL_RTC_BAK_SetRegister(RTC, LL_RTC_BKP_DR0, status); +#endif + + LOG_INF("Setting Reboot-Status 0x%02x and resetting...", status); + + k_sleep(K_MSEC(100)); + LOG_PANIC(); + + sys_reboot(SYS_REBOOT_COLD); +} + +uint8_t get_reboot_status(void) +{ + uint8_t status = 0; +#if IS_ENABLED(CONFIG_SOC_SERIES_NRF52X) + /* Korrigierter Aufruf mit Register-Index 0 */ + status = (uint8_t)nrf_power_gpregret_get(NRF_POWER, REBOOT_STATUS_REG_IDX); + /* Register nach dem Lesen löschen */ + nrf_power_gpregret_set(NRF_POWER, REBOOT_STATUS_REG_IDX, 0); +#elif IS_ENABLED(CONFIG_SOC_SERIES_STM32G0X) + status = (uint8_t)LL_RTC_BAK_GetRegister(RTC, LL_RTC_BKP_DR0); + /* Register nach dem Lesen löschen */ + LL_RTC_BAK_SetRegister(RTC, LL_RTC_BKP_DR0, 0); +#endif + + if (status != 0) { + printk("Reboot status detected: 0x%02x\n", status); + } + return status; +} \ No newline at end of file diff --git a/firmware/src/utils.h b/firmware/src/utils.h new file mode 100644 index 0000000..2bc2354 --- /dev/null +++ b/firmware/src/utils.h @@ -0,0 +1,24 @@ +#ifndef UTILS_H +#define UTILS_H + +#include + +enum RebootStatus { + REBOOT_STATUS_NORMAL = 0x00, + REBOOT_STATUS_FIRMWARE_UPDATE = 0xA1, + REBOOT_STATUS_FIRMWARE_CONFIRMED = 0xB2 +}; +/** + * @brief Reboots the controller with a specific status code. + * On reboot, you can read this status code in the bootloader to determine the reason for the reboot (e.g., normal restart, firmware update, error state). + * @param status_code A user-defined code indicating the reason for the reboot. + */ +void reboot_with_status(uint8_t status_code); + +/** + * @brief Retrieves the reboot status code set before the last reboot. + * This can be used in the bootloader to determine why the device was rebooted and take appropriate actions (e.g., enter firmware update mode if the status indicates a failed update). + * @return The reboot status code set before the last reboot, or 0 if no status was set. + */ +uint8_t get_reboot_status(); +#endif // UTILS_H \ No newline at end of file