diff --git a/firmware/libs/CMakeLists.txt b/firmware/libs/CMakeLists.txt index 0ffea08..e1c20f3 100644 --- a/firmware/libs/CMakeLists.txt +++ b/firmware/libs/CMakeLists.txt @@ -1,3 +1,4 @@ +add_subdirectory(fw_mgmt) add_subdirectory(fs_mgmt) add_subdirectory(ble_mgmt) add_subdirectory(buzz_proto) \ No newline at end of file diff --git a/firmware/libs/Kconfig b/firmware/libs/Kconfig index b0f20d6..01efc5f 100644 --- a/firmware/libs/Kconfig +++ b/firmware/libs/Kconfig @@ -1,3 +1,4 @@ +rsource "fw_mgmt/Kconfig" rsource "fs_mgmt/Kconfig" rsource "ble_mgmt/Kconfig" rsource "buzz_proto/Kconfig" \ No newline at end of file diff --git a/firmware/libs/buzz_proto/include/buzz_proto.h b/firmware/libs/buzz_proto/include/buzz_proto.h index c62f95a..fc1ae35 100644 --- a/firmware/libs/buzz_proto/include/buzz_proto.h +++ b/firmware/libs/buzz_proto/include/buzz_proto.h @@ -35,6 +35,7 @@ enum buzz_data_type BUZZ_DATA_PROTO_INFO = 0x01, BUZZ_DATA_DEVICE_INFO = 0x02, BUZZ_DATA_FS_INFO = 0x03, + BUZZ_DATA_FW_INFO = 0x04, BUZZ_DATA_FILE_GET = 0x20, BUZZ_DATA_FILE_PUT = 0x21, @@ -42,7 +43,7 @@ enum buzz_data_type BUZZ_DATA_TAGS_PUT = 0x23, BUZZ_DATA_RM_FILE = 0x24, BUZZ_DATA_RENAME_FILE = 0x25, - + BUZZ_DATA_FW_UPDATE = 0x30, BUZZ_DATA_LS = 0x40, @@ -88,6 +89,17 @@ struct __attribute__((packed)) buzz_resp_proto_version uint16_t max_chunk_size; /* Little Endian */ }; +/* Payload für die Geräteinformationen */ +struct __attribute__((packed)) buzz_resp_device_info +{ + uint8_t data_type; /* BUZZ_DATA_DEVICE_INFO */ + uint8_t device_id[8]; /* EUI64 oder ähnliche eindeutige ID */ + uint8_t board_name_length; /* Länge des Board-Namens */ + uint8_t board_revision_length; /* Länge der Board-Revision */ + uint8_t soc_name_length; /* Länge des SOC-Namens */ + char data[]; /* Variabler String ohne Null-Terminierung: [board_name][board_revision][soc_name] */ +}; + /* Payload für die Dateisystem-Informationen */ struct __attribute__((packed)) buzz_resp_fs_info { @@ -100,6 +112,16 @@ struct __attribute__((packed)) buzz_resp_fs_info uint8_t data[]; /* Pfadnamen */ }; +/* Payload für die Firmware-Infos */ +struct __attribute__((packed)) buzz_resp_fw_info +{ + uint8_t data_type; /* BUZZ_DATA_FW_INFO */ + uint8_t fw_status; /* fw_state_t */ + uint32_t slot1_size; /* Größe des Slot1-Partitionsbereichs (Little Endian) */ + uint8_t fw_version_length; /* Länge der Firmware-Versionszeichenkette */ + uint8_t kernel_version_length; /* Länge der Kernel-Versionszeichenkette */ + char data[]; /* Variabler String ohne Null-Terminierung: [fw_version][kernel_version] */ +}; /* Payload für das Entfernen einer Datei */ struct __attribute__((packed)) buzz_rm_file_payload { @@ -171,7 +193,6 @@ void buzz_proto_buf_free(uint8_t **buf); /* Übergabe eines empfangenen Frames an den Protokoll-Thread */ int buzz_proto_submit_frame(struct buzz_frame_msg *msg); - /* Gibt die Anzahl der freien Slabs zurück (abzüglich Reserve) */ uint16_t buzz_proto_get_free_rx_slabs(void); diff --git a/firmware/libs/buzz_proto/src/buzz_proto.c b/firmware/libs/buzz_proto/src/buzz_proto.c index a2671ab..8da2763 100644 --- a/firmware/libs/buzz_proto/src/buzz_proto.c +++ b/firmware/libs/buzz_proto/src/buzz_proto.c @@ -8,6 +8,7 @@ #include "buzz_proto.h" #include "fs_mgmt.h" +#include "fw_mgmt.h" LOG_MODULE_REGISTER(buzz_proto, CONFIG_BUZZ_PROTO_LOG_LEVEL); K_MEM_SLAB_DEFINE(buzz_proto_slabs, CONFIG_BUZZ_PROTO_SLAB_SIZE, CONFIG_BUZZ_PROTO_SLAB_COUNT, 4); @@ -172,7 +173,7 @@ static void handle_proto_version_request(struct buzz_frame_msg *msg) resp_data->data_type = BUZZ_DATA_PROTO_INFO; resp_data->version = sys_cpu_to_le16(BUZZ_PROTO_VERSION); - /* Dynamische Chunk-Größe basierend auf der aktuellen Transport-MTU berechnen */ + /* Dynamische Chunk-Grösse basierend auf der aktuellen Transport-MTU berechnen */ uint16_t slab_payload = CONFIG_BUZZ_PROTO_SLAB_SIZE - sizeof(struct buzz_proto_header); uint16_t transport_payload = 0; @@ -192,6 +193,34 @@ static void handle_proto_version_request(struct buzz_frame_msg *msg) } } +void handle_device_info_request(struct buzz_frame_msg *msg) { + struct buzz_proto_header *hdr = (struct buzz_proto_header *)msg->data_ptr; + hdr->frame_type = BUZZ_FRAME_RESPONSE; + struct buzz_resp_device_info *resp_data = (struct buzz_resp_device_info *)(msg->data_ptr + sizeof(*hdr)); + resp_data->data_type = BUZZ_DATA_DEVICE_INFO; + if (fw_mgmt_get_id(resp_data->device_id, sizeof(resp_data->device_id)) < 0) { + LOG_ERR("Failed to get device ID"); + send_error_frame(msg, EIO); + return; + } + const char *board_name = fw_mgmt_get_board_name(); + const char *board_rev = fw_mgmt_get_board_revision(); + const char *soc_name = fw_mgmt_get_soc_name(); + resp_data->board_name_length = MIN(strlen(board_name), 32); // Sicherheitsmassnahme gegen zu lange Namen + resp_data->board_revision_length = MIN(strlen(board_rev), 32); // Sicherheitsmassnahme gegen zu lange Namen + resp_data->soc_name_length = MIN(strlen(soc_name), 32); // Sicherheitsmassnahme gegen zu lange Namen + memcpy(resp_data->data, board_name, resp_data->board_name_length); + memcpy(resp_data->data + resp_data->board_name_length, board_rev, resp_data->board_revision_length); + memcpy(resp_data->data + resp_data->board_name_length + resp_data->board_revision_length, soc_name, resp_data->soc_name_length); + uint16_t payload_length = sizeof(struct buzz_resp_device_info) + resp_data->board_name_length + resp_data->board_revision_length + resp_data->soc_name_length; + hdr->payload_length = sys_cpu_to_le16(payload_length); + uint16_t total_len = sizeof(struct buzz_proto_header) + payload_length; + if (msg->reply_cb) + { + msg->reply_cb(msg->data_ptr, total_len); + } +} + void handle_fs_info_request(struct buzz_frame_msg *msg) { struct buzz_proto_header *hdr = (struct buzz_proto_header *)msg->data_ptr; @@ -234,6 +263,36 @@ void handle_fs_info_request(struct buzz_frame_msg *msg) } } +static void handle_fw_info_request(struct buzz_frame_msg *msg) +{ + struct buzz_proto_header *hdr = (struct buzz_proto_header *)msg->data_ptr; + + hdr->frame_type = BUZZ_FRAME_RESPONSE; + + struct buzz_resp_fw_info *resp_data = (struct buzz_resp_fw_info *)(msg->data_ptr + sizeof(*hdr)); + + resp_data->data_type = BUZZ_DATA_FW_INFO; + resp_data->fw_status = fw_mgmt_get_fw_state(); + resp_data->slot1_size = sys_cpu_to_le32(fw_mgmt_get_slot1_size()); + + const char *fw_version = fw_mgmt_get_fw_version_string(); + const char *kernel_version = fw_mgmt_get_kernel_version_string(); + resp_data->fw_version_length = MIN(strlen(fw_version), 32); // Sicherheitsmassnahme gegen zu lange Strings + resp_data->kernel_version_length = MIN(strlen(kernel_version), 32); // Sicherheitsmassnahme gegen zu lange Strings + memcpy(resp_data->data, fw_version, resp_data->fw_version_length); + memcpy(resp_data->data + resp_data->fw_version_length, kernel_version, resp_data->kernel_version_length); + + uint16_t payload_length = sizeof(struct buzz_resp_fw_info) + resp_data->fw_version_length + resp_data->kernel_version_length; + hdr->payload_length = sys_cpu_to_le16(payload_length); + + uint16_t total_len = sizeof(struct buzz_proto_header) + payload_length; + + if (msg->reply_cb) + { + msg->reply_cb(msg->data_ptr, total_len); + } +} + static void handle_ls_request(struct buzz_frame_msg *msg) { struct buzz_proto_header *hdr = (struct buzz_proto_header *)msg->data_ptr; @@ -619,6 +678,11 @@ static void handle_request(struct buzz_frame_msg *msg) handle_proto_version_request(msg); break; + case BUZZ_DATA_DEVICE_INFO: + LOG_DBG("Received Device Info Request"); + handle_device_info_request(msg); + break; + case BUZZ_DATA_FS_INFO: LOG_DBG("Received FS Info Request"); handle_fs_info_request(msg); @@ -629,6 +693,11 @@ static void handle_request(struct buzz_frame_msg *msg) handle_ls_request(msg); break; + case BUZZ_DATA_FW_INFO: + LOG_DBG("Received FW Info Request"); + handle_fw_info_request(msg); + break; + case BUZZ_DATA_FILE_GET: LOG_DBG("Received FILE_GET Request"); handle_file_get_request(msg, false); diff --git a/firmware/libs/fs_mgmt/src/fs_mgmt.c b/firmware/libs/fs_mgmt/src/fs_mgmt.c index 1f98dee..222a91d 100644 --- a/firmware/libs/fs_mgmt/src/fs_mgmt.c +++ b/firmware/libs/fs_mgmt/src/fs_mgmt.c @@ -514,8 +514,8 @@ static void fs_thread_entry(void *p1, void *p2, void *p3) LOG_WRN("Write timeout! Aborting transfer."); if (write_ctx.state == FS_STATE_RECEIVING_FILE) { - // fs_mgmt_pm_close(&write_ctx.file); - // fs_mgmt_pm_unlink(write_ctx.filename); + fs_mgmt_pm_close(&write_ctx.file); + fs_mgmt_pm_unlink(write_ctx.filename); } write_ctx.state = FS_STATE_IDLE; continue; @@ -536,8 +536,8 @@ static void fs_thread_entry(void *p1, void *p2, void *p3) memcpy(write_ctx.filename, msg.slab_ptr + msg.data_offset, msg.data_len); write_ctx.filename[msg.data_len] = '\0'; - // fs_mgmt_pm_unlink(write_ctx.filename); - // rc = fs_mgmt_pm_open(&write_ctx.file, write_ctx.filename, FS_O_CREATE | FS_O_WRITE); + fs_mgmt_pm_unlink(write_ctx.filename); + rc = fs_mgmt_pm_open(&write_ctx.file, write_ctx.filename, FS_O_CREATE | FS_O_WRITE); if (rc == 0) { @@ -568,7 +568,7 @@ static void fs_thread_entry(void *p1, void *p2, void *p3) write_ctx.filename[msg.data_len] = '\0'; /* Datei öffnen: Nur Lese- und Schreibrechte, Datei muss bereits existieren */ - // int rc = fs_mgmt_pm_open(&write_ctx.file, write_ctx.filename, FS_O_READ | FS_O_WRITE); + int rc = fs_mgmt_pm_open(&write_ctx.file, write_ctx.filename, FS_O_READ | FS_O_WRITE); if (rc == 0) { @@ -583,7 +583,7 @@ static void fs_thread_entry(void *p1, void *p2, void *p3) } /* Datei ab dem Ende der Audiodaten abschneiden (alte Tags entfernen) */ - // rc = fs_truncate(&write_ctx.file, audio_len); + rc = fs_truncate(&write_ctx.file, audio_len); if (rc != 0) { LOG_ERR("Failed to truncate file: %d", rc); @@ -593,7 +593,7 @@ static void fs_thread_entry(void *p1, void *p2, void *p3) } /* File-Pointer exakt an das neue Ende (audio_len) setzen */ - // fs_seek(&write_ctx.file, audio_len, FS_SEEK_SET); + fs_seek(&write_ctx.file, audio_len, FS_SEEK_SET); write_ctx.state = FS_STATE_RECEIVING_TAGS; write_ctx.crc32 = 0; @@ -622,8 +622,7 @@ static void fs_thread_entry(void *p1, void *p2, void *p3) case FS_STATE_RECEIVING_FILE: if (msg.op == FS_WRITE_OP_FILE_CHUNK && msg.slab_ptr) { - // ssize_t written = fs_write(&write_ctx.file, msg.slab_ptr + msg.data_offset, msg.data_len); - ssize_t written = msg.data_len; /* Zum Testen, da wir ja kein echtes FS-Backend haben */ + ssize_t written = fs_write(&write_ctx.file, msg.slab_ptr + msg.data_offset, msg.data_len); if (written == msg.data_len) { write_ctx.crc32 = crc32_ieee_update(write_ctx.crc32, msg.slab_ptr + msg.data_offset, msg.data_len); @@ -649,7 +648,7 @@ static void fs_thread_entry(void *p1, void *p2, void *p3) } else if (msg.op == FS_WRITE_OP_FILE_END) { - // fs_mgmt_pm_close(&write_ctx.file); + fs_mgmt_pm_close(&write_ctx.file); write_ctx.state = FS_STATE_IDLE; if (write_ctx.crc32 == msg.metadata) @@ -660,14 +659,14 @@ static void fs_thread_entry(void *p1, void *p2, void *p3) else { LOG_ERR("CRC Mismatch! Expected: 0x%08X, Got: 0x%08X", msg.metadata, write_ctx.crc32); - // fs_mgmt_pm_unlink(write_ctx.filename); + fs_mgmt_pm_unlink(write_ctx.filename); buzz_proto_send_error_reusing_slab(msg.reply_cb, EBADMSG, msg.slab_ptr); } } else if (msg.op == FS_WRITE_OP_ABORT) { - // fs_mgmt_pm_close(&write_ctx.file); - // fs_mgmt_pm_unlink(write_ctx.filename); + fs_mgmt_pm_close(&write_ctx.file); + fs_mgmt_pm_unlink(write_ctx.filename); write_ctx.state = FS_STATE_IDLE; if (msg.slab_ptr) buzz_proto_buf_free(&msg.slab_ptr); @@ -677,8 +676,7 @@ static void fs_thread_entry(void *p1, void *p2, void *p3) case FS_STATE_RECEIVING_TAGS: if (msg.op == FS_WRITE_OP_FILE_CHUNK && msg.slab_ptr) { - // ssize_t written = fs_write(&write_ctx.file, msg.slab_ptr + msg.data_offset, msg.data_len); - ssize_t written = msg.data_len; /* Zum Testen, da wir ja kein echtes FS-Backend haben */ + ssize_t written = fs_write(&write_ctx.file, msg.slab_ptr + msg.data_offset, msg.data_len); if (written == msg.data_len) { write_ctx.crc32 = crc32_ieee_update(write_ctx.crc32, msg.slab_ptr + msg.data_offset, msg.data_len); @@ -699,8 +697,8 @@ static void fs_thread_entry(void *p1, void *p2, void *p3) else { LOG_ERR("Flash write failed during tags transfer!"); - // fs_truncate(&write_ctx.file, write_ctx.audio_len); /* Rollback */ - // fs_mgmt_pm_close(&write_ctx.file); + fs_truncate(&write_ctx.file, write_ctx.audio_len); /* Rollback */ + fs_mgmt_pm_close(&write_ctx.file); write_ctx.state = FS_STATE_IDLE; buzz_proto_send_error_reusing_slab(msg.reply_cb, EIO, msg.slab_ptr); } @@ -716,16 +714,16 @@ static void fs_thread_entry(void *p1, void *p2, void *p3) else { LOG_ERR("Tags CRC Mismatch! Expected: 0x%08X, Got: 0x%08X", msg.metadata, write_ctx.crc32); - // fs_truncate(&write_ctx.file, write_ctx.audio_len); /* Rollback */ - // fs_mgmt_pm_close(&write_ctx.file); + fs_truncate(&write_ctx.file, write_ctx.audio_len); /* Rollback */ + fs_mgmt_pm_close(&write_ctx.file); buzz_proto_send_error_reusing_slab(msg.reply_cb, EBADMSG, msg.slab_ptr); } write_ctx.state = FS_STATE_IDLE; } else if (msg.op == FS_WRITE_OP_ABORT) { - // fs_truncate(&write_ctx.file, write_ctx.audio_len); /* Rollback */ - // fs_mgmt_pm_close(&write_ctx.file); + fs_truncate(&write_ctx.file, write_ctx.audio_len); /* Rollback */ + fs_mgmt_pm_close(&write_ctx.file); write_ctx.state = FS_STATE_IDLE; if (msg.slab_ptr) buzz_proto_buf_free(&msg.slab_ptr); diff --git a/firmware/libs/fw_mgmt/CMakeLists.txt b/firmware/libs/fw_mgmt/CMakeLists.txt index d4c5abb..63a7c2f 100644 --- a/firmware/libs/fw_mgmt/CMakeLists.txt +++ b/firmware/libs/fw_mgmt/CMakeLists.txt @@ -1,17 +1,16 @@ -if(CONFIG_FS_MGMT) +if(CONFIG_FW_MGMT) zephyr_library() - zephyr_library_sources(src/fs_mgmt.c) + zephyr_library_sources(src/fw_mgmt.c) zephyr_include_directories(include) - if(CONFIG_FILE_SYSTEM_LITTLEFS) - if(DEFINED ZEPHYR_LITTLEFS_MODULE_DIR) - zephyr_include_directories(${ZEPHYR_LITTLEFS_MODULE_DIR}) - elseif(DEFINED WEST_TOPDIR) - zephyr_include_directories(${WEST_TOPDIR}/modules/fs/littlefs) + if(CONFIG_MCUBOOT_IMG_MANAGER) + # img_mgmt.h pulls in and + if(DEFINED ZEPHYR_MCUBOOT_MODULE_DIR) + zephyr_include_directories(${ZEPHYR_MCUBOOT_MODULE_DIR}/boot/bootutil/include) + zephyr_include_directories(${ZEPHYR_MCUBOOT_MODULE_DIR}/boot/zephyr/include) endif() - - if(DEFINED ZEPHYR_BASE) - zephyr_include_directories(${ZEPHYR_BASE}/modules/littlefs) + if(DEFINED ZEPHYR_ZCBOR_MODULE_DIR) + zephyr_include_directories(${ZEPHYR_ZCBOR_MODULE_DIR}/include) endif() endif() endif() \ No newline at end of file diff --git a/firmware/libs/fw_mgmt/Kconfig b/firmware/libs/fw_mgmt/Kconfig index 5b656db..6cabe92 100644 --- a/firmware/libs/fw_mgmt/Kconfig +++ b/firmware/libs/fw_mgmt/Kconfig @@ -1,64 +1,22 @@ -menuconfig FS_MGMT - bool "File System Management" +menuconfig FW_MGMT + bool "Firmware Management" select FLASH select FLASH_MAP - select FILE_SYSTEM - select FILE_SYSTEM_LITTLEFS - select FILE_SYSTEM_MKFS + select STREAM_FLASH select FLASH_PAGE_LAYOUT - select NORDIC_QSPI_NOR if SOC_SERIES_NRF52X && (SOC_NRF52840_QIAA || SOC_NRF52833_QIAA) + select BOOTLOADER_MCUBOOT + select IMG_MANAGER + select MCUBOOT_IMG_MGR + select HWINFO + help - Library for initializing and managing the file system. + Library for firmware operations. -if FS_MGMT - config FS_MGMT_MOUNT_POINT - string "Littlefs Mount Point" - default "/lfs" - help - Set the mount point for the Littlefs file system. Default is "/lfs". - - config FS_MGMT_AUDIO_SUBDIR - string "Audio File Path" - default "/a" - help - Set the path for the audio file within the file system. Default is "/a". - - config FS_MGMT_SYSTEM_SUBDIR - string "System File Path" - default "/sys" - help - Set the path for the system file within the file system. Default is "/sys". - - config FS_MGMT_THREAD_STACK_SIZE - int "File System Management Thread Stack Size" - default 2048 - help - Set the stack size for the file system management thread. Default is 2048 bytes. - - config FS_MGMT_THREAD_PRIORITY - int "File System Management Thread Priority" - default 6 - help - Set the priority for the file system management thread. Default is 6. - - if SOC_SERIES_NRF52X - config PM_PARTITION_REGION_LITTLEFS_EXTERNAL - default y - - config PM_PARTITION_SIZE_LITTLEFS - default 0x1000000 - endif # SOC_SERIES_NRF52X - - config FS_LITTLEFS_READ_SIZE - default 256 - config FS_LITTLEFS_PROG_SIZE - default 256 - config FS_LITTLEFS_CACHE_SIZE - default 4096 - config FS_LITTLEFS_LOOKAHEAD_SIZE - default 512 - - module = FS_MGMT - module-str = fs_mgmt +if FW_MGMT + config MCUBOOT_UTIL_LOG_LEVEL_ERR + # config CONFIG_MCUMGR_GRP_IMG + # default y + module = FW_MGMT + module-str = fw_mgmt source "subsys/logging/Kconfig.template.log_config" -endif # FS_MGMT \ No newline at end of file +endif # FW_MGMT \ No newline at end of file diff --git a/firmware/libs/fw_mgmt/include/fw_mgmt.h b/firmware/libs/fw_mgmt/include/fw_mgmt.h new file mode 100644 index 0000000..67005c6 --- /dev/null +++ b/firmware/libs/fw_mgmt/include/fw_mgmt.h @@ -0,0 +1,23 @@ +#ifndef FW_MGMT_H +#define FW_MGMT_H + +#include + +typedef enum +{ + FW_STATE_CONFIRMED = 0x00, + FW_STATE_PENDING = 0x01, + FW_STATE_TESTING = 0x02, + FW_STATE_UNKNOWN = 0xFF +} fw_state_t; + +const char *fw_mgmt_get_fw_version_string(void); +const char *fw_mgmt_get_kernel_version_string(void); +const char *fw_mgmt_get_board_name(); +const char *fw_mgmt_get_board_revision(); +const char *fw_mgmt_get_soc_name(); +int fw_mgmt_get_id(uint8_t *buffer, size_t length); +ssize_t fw_mgmt_get_slot1_size(void); + +fw_state_t fw_mgmt_get_fw_state(void); +#endif /* FW_MGMT_H */ \ No newline at end of file diff --git a/firmware/libs/fw_mgmt/src/fw_mgmt.c b/firmware/libs/fw_mgmt/src/fw_mgmt.c new file mode 100644 index 0000000..3ea9485 --- /dev/null +++ b/firmware/libs/fw_mgmt/src/fw_mgmt.c @@ -0,0 +1,79 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "fw_mgmt.h" + +#define SLOT1_PARTITION_SIZE DT_REG_SIZE(DT_NODELABEL(slot1_partition)) + +static char fw_version_string[] = APP_VERSION_STRING; +static char kernel_version_string[] = KERNEL_VERSION_STRING; +static char board_name[] = CONFIG_BOARD; +static char board_revision[] = CONFIG_BOARD_REVISION; +static char soc_name[] = CONFIG_SOC; +static uint8_t hwid[8]; + +LOG_MODULE_REGISTER(fw_mgmt, CONFIG_FW_MGMT_LOG_LEVEL); + +const char* fw_mgmt_get_fw_version_string(void) +{ + return (const char*)fw_version_string; +} + +const char* fw_mgmt_get_kernel_version_string(void) +{ + return (const char*)kernel_version_string; +} + +const char* fw_mgmt_get_board_name(void) +{ + return (const char*)board_name; +} + +const char* fw_mgmt_get_board_revision(void) +{ + return (const char*)board_revision; +} + +const char* fw_mgmt_get_soc_name(void) +{ + return (const char*)soc_name; +} + +int fw_mgmt_get_id(uint8_t *buffer, size_t length) +{ + int ret = hwinfo_get_device_id(hwid, length); + if (ret < 0) { + LOG_ERR("Failed to get device EUI64: %d", ret); + return ret; + } + + memcpy(buffer, hwid, sizeof(hwid)); + return ret; +} + +fw_state_t fw_mgmt_get_fw_state(void) +{ + if (!boot_is_img_confirmed()) { + return FW_STATE_TESTING; + } + int swap_type = mcuboot_swap_type(); + if (swap_type == BOOT_SWAP_TYPE_NONE) { + return FW_STATE_CONFIRMED; + } else if (swap_type == BOOT_SWAP_TYPE_TEST) { + return FW_STATE_PENDING; + } else { + LOG_ERR("Unexpected swap type: %d", swap_type); + return FW_STATE_UNKNOWN; // Fallback auf bestätigten Zustand bei unerwartetem Swap-Typ + } + return FW_STATE_UNKNOWN; // Fallback, sollte nie erreicht werden +} + +ssize_t fw_mgmt_get_slot1_size(void) +{ + return SLOT1_PARTITION_SIZE; +} \ No newline at end of file diff --git a/firmware/prj.conf b/firmware/prj.conf index e453327..a13502c 100644 --- a/firmware/prj.conf +++ b/firmware/prj.conf @@ -1,33 +1,34 @@ ### Logging CONFIG_LOG=y -### Bootloader -CONFIG_BOOTLOADER_MCUBOOT=y - ### File System CONFIG_FS_MGMT=y -# CONFIG_FS_MGMT_LOG_LEVEL_DBG=y CONFIG_FS_LOG_LEVEL_WRN=y ### Bluetooth CONFIG_BLE_MGMT=y - # Advertising 500ms - 1s CONFIG_BLE_MGMT_ADV_INT_MIN=160 CONFIG_BLE_MGMT_ADV_INT_MAX=320 -## Buzzer protocol +### Firmware Management +CONFIG_FW_MGMT=y +CONFIG_FW_MGMT_LOG_LEVEL_DBG=y +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 CONFIG_PM_DEVICE=y -## Shell -# CONFIG_SHELL=y -# CONFIG_FILE_SYSTEM_SHELL=y +# ## Shell +# # CONFIG_SHELL=y +# # CONFIG_FILE_SYSTEM_SHELL=y -# Airtime-Maximierung +# # Airtime-Maximierung CONFIG_BT_CTLR_SDC_MAX_CONN_EVENT_LEN_DEFAULT=4000000 # MTU-Setup @@ -57,4 +58,9 @@ CONFIG_BT_GAP_AUTO_UPDATE_CONN_PARAMS=y 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 \ No newline at end of file +CONFIG_BT_PERIPHERAL_PREF_TIMEOUT=400 + + +CONFIG_HEAP_MEM_POOL_SIZE=2048 + +CONFIG_BT_CENTRAL=n \ No newline at end of file diff --git a/firmware/src/main.c b/firmware/src/main.c index 90c7f6f..81de266 100644 --- a/firmware/src/main.c +++ b/firmware/src/main.c @@ -5,6 +5,7 @@ #include "fs_mgmt.h" #include "ble_mgmt.h" #include "buzz_proto.h" +#include "fw_mgmt.h" LOG_MODULE_REGISTER(main); @@ -44,7 +45,13 @@ void ble_rx_cb(const uint8_t *data, uint16_t len) int main(void) { - LOG_INF("Starting app on %s (SOC: %s)", CONFIG_BOARD, CONFIG_SOC); + 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; diff --git a/firmware/sysbuild/mcuboot.conf b/firmware/sysbuild/mcuboot.conf index 2e10fa4..9ad5a36 100644 --- a/firmware/sysbuild/mcuboot.conf +++ b/firmware/sysbuild/mcuboot.conf @@ -1,5 +1,4 @@ CONFIG_LOG=y -CONFIG_MCUBOOT_LOG_LEVEL_DBG=y # CONFIG_MCUBOOT_SERIAL=y CONFIG_UART_CONSOLE=y # CONFIG_SINGLE_APPLICATION_SLOT=n diff --git a/firmware/sysbuild/mcuboot.overlay b/firmware/sysbuild/mcuboot.overlay index 972c222..907ce4d 100644 --- a/firmware/sysbuild/mcuboot.overlay +++ b/firmware/sysbuild/mcuboot.overlay @@ -1,7 +1,7 @@ / { aliases { - mcuboot-button0 = &button1; - mcuboot-led0 = &led1; + mcuboot-button0 = &button0; + mcuboot-led0 = &led0; }; }; diff --git a/protocol.md b/protocol.md index 1548acf..51a8373 100644 --- a/protocol.md +++ b/protocol.md @@ -3,7 +3,7 @@ Stand: 2026-03-18 Quelle: aktueller Implementierungsstand aus Firmware (`buzz_proto`, `fs_mgmt`, `ble_mgmt`) und Web-Client (`transport`, `parser`). -## 1. Ziel und Scope +## Ziel und Scope Das Buzzer Protocol ist ein binäres Frame-Protokoll für Host <-> Device Kommunikation. @@ -19,7 +19,7 @@ Nicht produktiv implementiert: - `DEVICE_INFO` (`0x02`) - Firmware-Update (`FW_*`, `FW_UPDATE`) -## 2. Transport und Grundregeln +## Transport und Grundregeln - Alle Integer-Felder sind Little Endian. - Jedes Frame hat einen 3-Byte Header. @@ -32,9 +32,9 @@ BLE Service UUIDs: - RX: `e517d988-bab5-4574-8479-97c6cb115ca1` - TX: `e517d988-bab5-4574-8479-97c6cb115ca2` -## 3. Frame-Format +## Frame-Format -### 3.1 Header +### Header ```c uint8_t frame_type; @@ -42,7 +42,7 @@ uint16_t payload_length; // LE ``` -### 3.2 Paketstruktur +### Paketstruktur ```mermaid --- @@ -54,7 +54,7 @@ packet +40: "Payload (variable length)" ``` -### 3.3 Maximalgröße +### Maximalgröße Firmware-Buffer ist slab-basiert (`CONFIG_BUZZ_PROTO_SLAB_SIZE`). Der effektive Chunk für Transfers wird zusätzlich durch den Transport limitiert. Bei Bluetooth sind das zum Beispiel 3 Bytes: @@ -65,7 +65,7 @@ Der effektive Chunk für Transfers wird zusätzlich durch den Transport limitier Das Protokoll ist so ausgelegt, dass es mit mindestens 100 Bytes auskommen sollte. -## 4. Frame-Typen +## Frame-Typen | Wert | Name | Richtung | Bedeutung | |---|---|---|---| @@ -84,13 +84,14 @@ Das Protokoll ist so ausgelegt, dass es mit mindestens 100 Bytes auskommen sollt | `0x41` | `LS_ENTRY` | Device -> Host | Verzeichniseintrag | | `0x42` | `LS_END` | Device -> Host | Ende Verzeichnis-Stream | -## 5. Data-Typen (`REQUEST`) +## Data-Typen (`REQUEST`) | Wert | Name | Status | Beschreibung | |---|---|---|---| | `0x01` | `PROTO_INFO` | aktiv | Protokollversion + max Chunkgröße | -| `0x02` | `DEVICE_INFO` | reserviert | aktuell nicht bedient | +| `0x02` | `DEVICE_INFO` | aktiv | Device-Infos (Board, Revision, SOC, ID) | | `0x03` | `FS_INFO` | aktiv | Dateisystem- und Pfadinfos | +| `0x04` | `FW_INFO` | aktiv | Info über Firmware-Status und -Version sowie Kernelversion | | `0x20` | `FILE_GET` | aktiv | Datei vom Device streamen | | `0x21` | `FILE_PUT` | aktiv | Datei zum Device hochladen | | `0x22` | `TAGS_GET` | aktiv | nur Tag-Bereich streamen | @@ -100,9 +101,9 @@ Das Protokoll ist so ausgelegt, dass es mit mindestens 100 Bytes auskommen sollt | `0x30` | `FW_UPDATE` | reserviert | aktuell nicht bedient | | `0x40` | `LS` | aktiv | Verzeichnisliste starten | -## 6. Request/Response-Formate +## Request/Response-Formate -## 6.1 Generischer Request +### Generischer Request ```c uint8_t data_type; @@ -126,7 +127,7 @@ packet +32: "Optional payload (variable length)" ``` -## 6.2 `PROTO_INFO` (`0x01`) +### `PROTO_INFO` (`0x01`) Request: keine Zusatzdaten. @@ -150,7 +151,24 @@ packet +16: "Max Chunk Size (LE)" ``` -## 6.3 `FS_INFO` (`0x03`) +### `DEVICE_INFO` (`0x02`) + +Request: keine Zusatzdaten. + +Respone-Payload: + +```c +uint8_t data_type; // 0x02 +uint8_t[8] device_id; +uint8_t board_len; +uint8_t rev_len; +uint8_t soc_len; +uint8_t data[]; // board, rev und soc, ohne Nullterminierung +``` + +***Hinweis:*** In der aktuellen implementierung werden die Strings auf eine maximale Länge von 32 Zeichen beschränkt. Dies sollte für alle Fälle genügen. + +### `FS_INFO` (`0x03`) Request: keine Zusatzdaten. @@ -163,7 +181,7 @@ uint32_t free_size; // LE uint8_t max_path_length; uint8_t sys_path_length; uint8_t audio_path_length; -uint8_t data[]; // sys_path + audio_path (beide nicht nullterminiert) +uint8_t data[]; // sys_path + audio_path ohne Nullterminierung ``` Im `data` folgen sich System- und Audiopfad ohne Abstand, und ohne 0-Terminierung (`\0`). Beispiel für Systempfad `/lfs/sys`und Audiopfad `/lfs/a`: @@ -213,7 +231,23 @@ Das Beispiel schaut in HEX so aus: 0x2F 0x6C 0x66 0x73 0x2F 0x73 0x79 0x73 0x2F 0x6C 0x66 0x73 0x2F 0x61 ``` -## 6.4 `LS` (`0x40`) +### `FW_INFO` (`0x04`) + +Request: keine Zusatzdaten + +Response: + +```c +uint8_t fw_status; /* 0x00: Confirmed, 0x01: Pending, 0x02: Testing, 0xFF: Unbekannt */ +uint32_t slot1_size; /* (LE) Grösse des Firmware Update Slots */ +uint8_t fw_version_len; /* Länge des Firmware-Versionsstring */ +uint8_t kernel_version_len; /* Länge des Kernel-Versionsstrings */ +uint8_t data[]; /* FW-Version und Kernelversion, ohne Nullterminierung */ +``` + +***Hinweis:*** in der Aktuellen implementierung werden die Versionen auf 32 Zeichen limitiert. + +### `LS` (`0x40`) Request-Payload: @@ -222,7 +256,7 @@ uint8_t data_type; // 0x40 char path[]; // ohne Nullterminierung ``` -## 6.5 `FILE_GET` (`0x20`) und `TAGS_GET` (`0x22`) +### `FILE_GET` (`0x20`) und `TAGS_GET` (`0x22`) Request-Payload: @@ -233,7 +267,7 @@ char path[]; // ohne Nullterminierung Antwort ist ein Stream aus `FILE_START` -> `FILE_CHUNK`* -> `FILE_END`. -## 6.6 `FILE_PUT` (`0x21`) und `TAGS_PUT` (`0x23`) +### `FILE_PUT` (`0x21`) und `TAGS_PUT` (`0x23`) Request-Payload: @@ -247,7 +281,7 @@ Danach sendet der Host: - `FILE_CHUNK` Frames - abschließend `FILE_END` mit CRC32 -## 6.7 `RM_FILE` (`0x24`) +### `RM_FILE` (`0x24`) Request-Payload: @@ -257,7 +291,7 @@ uint8_t path_length; char path[]; // ohne Nullterminierung ``` -## 6.8 `RENAME_FILE` (`0x25`) +### `RENAME_FILE` (`0x25`) Request-Payload: @@ -268,9 +302,9 @@ uint8_t new_path_length; char paths[]; // old_path + new_path (jeweils ohne Nullterminierung) ``` -## 7. ACK / ERROR / SUCCESS +## ACK / ERROR / SUCCESS -## 7.1 ACK (`0x11`) +### ACK (`0x11`) Payload: @@ -285,7 +319,7 @@ Wichtig: Es gibt zwei Semantiken je nach Richtung. - Upload (`FILE_PUT`, `TAGS_PUT`): Device -> Host, Credits sind zusätzlich gewährte Tokens (Host addiert sie). -## 7.2 ERROR (`0x12`) +### ERROR (`0x12`) Payload: @@ -310,7 +344,7 @@ Häufige Codes: | `116` | `ETIMEDOUT` | Credit-/Stream-Timeout | | `134` | `ENOTSUP` | nicht unterstützt | -## 7.3 SUCCESS (`0x13`) +### SUCCESS (`0x13`) Payload: @@ -320,9 +354,7 @@ uint8_t data_type; // erfolgreich abgeschlossener Befehl Wird derzeit u.a. für `FILE_PUT`, `TAGS_PUT`, `RM_FILE`, `RENAME_FILE` genutzt. -## 8. Stream-Sequenzen (Mermaid) - -## 8.1 Verzeichnisliste (`LS`) +## Verzeichnisliste (`LS`) ```mermaid sequenceDiagram @@ -346,7 +378,7 @@ sequenceDiagram Hinweis zum aktuellen Web-Client (März 2026): Für `LS` wird initial `ACK(64)` gesendet, ein dynamisches Nachfüllen ist noch nicht implementiert. Große Verzeichnisse können deshalb in `ETIMEDOUT` laufen. -## 8.2 Datei-/Tag-Download (`FILE_GET`, `TAGS_GET`) +## Datei-/Tag-Download (`FILE_GET`, `TAGS_GET`) ```mermaid sequenceDiagram @@ -369,7 +401,7 @@ sequenceDiagram Note over Host: CRC prüfen ``` -## 8.3 Datei-/Tag-Upload (`FILE_PUT`, `TAGS_PUT`) +## Datei-/Tag-Upload (`FILE_PUT`, `TAGS_PUT`) ```mermaid sequenceDiagram @@ -395,9 +427,9 @@ sequenceDiagram end ``` -## 9. Payload-Frames im Detail +## Payload-Frames im Detail -## 9.1 `LS_ENTRY` (`0x41`) +### `LS_ENTRY` (`0x41`) ```c uint8_t type; // 0x00 file, 0x01 dir @@ -406,13 +438,13 @@ uint8_t name_length; char name[]; // ohne Nullterminierung ``` -## 9.2 `LS_END` (`0x42`) +### `LS_END` (`0x42`) ```c uint32_t total_entries; // LE ``` -## 9.3 `FILE_START` (`0x20`) +### `FILE_START` (`0x20`) ```c uint32_t total_size; // LE @@ -421,19 +453,19 @@ uint32_t total_size; // LE `FILE_GET`: komplette Dateigröße. `TAGS_GET`: nur Tag-Teil der Datei. -## 9.4 `FILE_CHUNK` (`0x21`) +### `FILE_CHUNK` (`0x21`) Payload sind rohe Nutzdatenbytes. -## 9.5 `FILE_END` (`0x22`) +### `FILE_END` (`0x22`) ```c uint32_t crc32; // LE, IEEE CRC32 ``` -## 10. Beispiel-Frames (Hex) +## Beispiel-Frames (Hex) -## 10.1 `PROTO_INFO` Request/Response +### `PROTO_INFO` Request/Response Request: @@ -454,7 +486,7 @@ Interpretation: - `01 00`: Version 1 - `FD 00`: max_chunk_size = 253 -## 10.2 `LS` Request für `/a` +### `LS` Request für `/a` ```text 00 03 00 40 2F 61 @@ -466,7 +498,7 @@ Interpretation: - `40`: data_type LS - `2F 61`: `/a` -## 11. Implementierungsnotizen +### Implementierungsnotizen - Unknown `REQUEST.data_type` wird aktuell mit `ERROR(EINVAL)` beantwortet. - Unbekannte/unerwartete `frame_type` im aktiven Protokollthread führen zu `ERROR(EPROTO)`. diff --git a/webpage/.prettierrc b/webpage/.prettierrc index 6bfa08a..27459a9 100644 --- a/webpage/.prettierrc +++ b/webpage/.prettierrc @@ -4,7 +4,7 @@ "singleAttributePerLine": false, "overrides": [ { - "files": ["*.svelte", "*.astro"], + "files": ["*.svelte", "*.astro", "*.ts", "*.js", "*.tsx", "*.jsx"], "options": { "printWidth": 100 } diff --git a/webpage/src/components/AudioDropzone.svelte b/webpage/src/components/AudioDropzone.svelte new file mode 100644 index 0000000..8342be9 --- /dev/null +++ b/webpage/src/components/AudioDropzone.svelte @@ -0,0 +1,456 @@ + + + { + if (e.key === "Escape") showSettings = false; + }} +/> + +
+
+

Dateiverarbeitung

+ +
+ +
(isFileDragOver = true)} + on:dragleave={() => (isFileDragOver = false)} + on:dragover|preventDefault + on:drop|preventDefault={handleDrop} + > + {#if !showSettings} +
+
+ LOW CUT + | + + COMPRESSOR + + | + + NORMALIZER + +
+
+ +
+
+
+ + + + + + +
+
+
+ {:else} +
+
+

Audio-Optionen

+
+ +
+
+
+
+ +
+ +
+ {#if $audioOptions.lowCut} +
+ + {$audioOptions.lowCutFreq} Hz + + +
+ {/if} +
+ +
+
+
+ +
+ +
+ + {#if $audioOptions.compress} +
+
(showPresetDropdown = false)}> + + + {#if showPresetDropdown} +
+ {#each Object.entries(presetInfos) as [key, info]} + + {/each} +
+ {/if} +
+ +
+
+ + Threshold + + + {$audioOptions.compressorThreshold} dB + + +
+
+ + Ratio + + + {$audioOptions.compressorRatio}:1 + + +
+
+ + Attack + + + {($audioOptions.compressorAttack * 1000).toFixed(0)} ms + + +
+
+ + Release + + + {($audioOptions.compressorRelease * 1000).toFixed(0)} ms + + +
+
+
+ {/if} +
+ +
+
+
+ +
+ +
+ {#if $audioOptions.normalize} +
+ + {$audioOptions.normalizeTargetDb} dB + + +
+ {/if} +
+ +
+ Hinweis: Die Konvertierung zu 16kHz Mono PCM erfolgt immer automatisch beim Speichern. +
+
+
+ {/if} +
+
+ + diff --git a/webpage/src/components/DeviceInfo.svelte b/webpage/src/components/DeviceInfo.svelte index 1c4cab7..e6db300 100644 --- a/webpage/src/components/DeviceInfo.svelte +++ b/webpage/src/components/DeviceInfo.svelte @@ -1,54 +1,145 @@ + import FlashUsage from "./FlashUsage.svelte"; + import { deviceInfo, fwInfo } from "../lib/store"; + import { FW_STATUS } from "../lib/protocol/constants"; + import { tooltip } from "../lib/actions/tooltip"; + import { + CheckCircleIcon, + WarningIcon, + BatteryEmptyIcon, + BatteryLowIcon, + BatteryMediumIcon, + BatteryHighIcon, + BatteryFullIcon, + BatteryChargingIcon, + } from "phosphor-svelte"; + +
- - - - - - - - - - - - - - - - - - - - - - - -
- Modell - - nrf52840dk-prototyp -
- Version - - 2.3.22-debug -
- HW-ID - - DEAD-BEAF-0102-3456 -
- Batterie - - 85% 1200mAh -
- Speicher - -
- -
-
+ + + + + + + + + + + + + + + + + + + + + + + +
Modell + {#if $deviceInfo} + {$deviceInfo.boardName} + {#if $deviceInfo.boardRevision} + >Rev. {$deviceInfo.boardRevision} + {/if} + {:else} + unbekannt + {/if} +
Version + {#if $fwInfo} + ${$fwInfo.slot1Size / 1024} kB. Wieso musst Du das wissen? Edi sorgt schon dafür, dass die Updates nicht zu gross sind.
😈
`, + pos: "bottom", + }} + > + {$fwInfo.fwVersion} + + {#if $fwInfo.fwStatus === FW_STATUS.CONFIRMED} + + + + {:else if $fwInfo.fwStatus === FW_STATUS.PENDING} + reboot testen. Wenn sie funktioniert, bestätige sie, damit sie dauerhaft nutzbar ist.
Achtung: der Reboot dauert einen Moment, da die Firmware erst umkopiert werden muss.", + pos: "bottom", + }} + class="relative flex size-5" + > + + +
+ {:else if $fwInfo.fwStatus === FW_STATUS.TESTING} + bestätige sie, damit sie dauerhaft nutzbar ist. Wenn sie nicht richtig funktioniert, dann kannst Du den Buzzer rebooten, er kehrt dann zur vorhergehenden Version zurück.
Achtung: der Reboot dauert einen Moment, da die Firmware erst umkopiert werden muss.", + pos: "bottom", + }} + class="relative flex size-5" + > + + +
+ {:else if $fwInfo.fwStatus === FW_STATUS.UNKNOWN} + 0x${$fwInfo.fwStatus.toString(16).padStart(2, "0").toUpperCase()}. Weiss der Teufel, was da wieder passiert ist... Vielleicht hilft ein Reboot? Oder Firmware neu flashen? Oder... hast Du ne Idee???`, + pos: "bottom", + }} + class="relative flex size-5" + > + + + + {/if} + (Zephyr {$fwInfo.kernelVersion}) + {:else} + unbekannt + {/if} +
HW-ID + {#if $deviceInfo} + {$deviceInfo.deviceId} + {:else} + unbekannt + {/if} +
Batterie + 85% 1200mAh +
Speicher +
+ +
+