This commit is contained in:
2026-03-07 08:51:50 +01:00
parent 4f3fbff258
commit f85143d7e5
60 changed files with 3245 additions and 1205 deletions

38
firmware/include/audio.h Normal file
View File

@@ -0,0 +1,38 @@
#ifndef AUDIO_H
#define AUDIO_H
/**
* @brief Initializes the audio subsystem
*
* @return 0 on success, negative error code on failure
*/
int audio_init(void);
/**
* @brief Plays an audio file from the filesystem
*
* @param filename The path to the audio file to play
*/
void audio_play(const char *filename);
/**
* @brief Stops the currently playing audio
*/
void audio_stop(void);
/**
* @brief Signals the audio thread that the system is fully booted
*/
void audio_system_ready(void);
/**
* @brief Refreshes the count of available audio files
*/
void audio_refresh_file_count(void);
/**
* @brief Puts the QSPI flash into deep sleep mode to save power
*/
void flash_deep_sleep(void);
#endif // AUDIO_H

236
firmware/include/fs.h Normal file
View File

@@ -0,0 +1,236 @@
#ifndef FS_H
#define FS_H
#include <zephyr/fs/fs.h>
#define MAX_PATH_LEN 32U
typedef struct slot_info_t {
size_t start_addr;
size_t size;
} slot_info_t;
typedef enum {
FS_MSG_START,
FS_MSG_CHUNK,
FS_MSG_EOF,
FS_MSG_ABORT
} fs_msg_type_t;
typedef struct {
fs_msg_type_t type;
/* Die Union spart RAM, da Start- und Chunk-Parameter
nie gleichzeitig im selben Message-Paket benötigt werden. */
union {
/* Payload für FS_MSG_START */
struct {
/* Der String wird sicher in die Queue kopiert */
char filename[MAX_PATH_LEN];
uint32_t expected_size;
uint32_t start_position;
} start;
/* Payload für FS_MSG_CHUNK */
struct {
void *slab_ptr;
uint32_t chunk_size;
} chunk;
};
} fs_msg_t;
extern struct k_msgq fs_msgq;
/**
* @brief Initializes the filesystem by mounting it
*/
int fs_init(void);
/**
* @brief Puts the QSPI flash into deep sleep mode to save power
*/
int fs_pm_flash_suspend(void);
/**
* @brief Resumes the QSPI flash from deep sleep mode
*/
int fs_pm_flash_resume(void);
/**
* @brief Wrapper around fs_open that handles power management for the flash
* Resumes the flash before opening and suspends it if opening fails
* @param file Pointer to fs_file_t structure to be initialized
* @param path Path to the file to open
* @param mode Open flags (e.g. FS_O_READ, FS_O_WRITE)
* @return 0 on success, negative error code on failure
*/
int fs_pm_open(struct fs_file_t *file, const char *path, fs_mode_t mode);
/**
* @brief Wrapper around fs_close that handles power management for the flash
* Resumes the flash after closing and suspends it if closing fails
* @param file Pointer to fs_file_t structure to be closed
* @return 0 on success, negative error code on failure
*/
int fs_pm_close(struct fs_file_t *file);
/**
* @brief Wrapper around fs_opendir that handles power management for the flash
* Resumes the flash before opening and suspends it if opening fails
* @param dirp Pointer to fs_dir_t structure to be initialized
* @param path Path to the directory to open
* @return 0 on success, negative error code on failure
*/
int fs_pm_opendir(struct fs_dir_t *dirp, const char *path);
/**
* @brief Wrapper around fs_closedir that handles power management for the flash
* Resumes the flash after closing and suspends it if closing fails
* @param dirp Pointer to fs_dir_t structure to be closed
* @return 0 on success, negative error code on failure
*/
int fs_pm_closedir(struct fs_dir_t *dirp);
/**
* @brief Unlinks (deletes) a file, ensuring the flash is active during the operation
* @param path Path to the file to unlink
* @return 0 on success, negative error code on failure
*/
int fs_pm_unlink(const char *path);
/**
* @brief Wrapper around fs_statvfs that handles power management for the flash
* Resumes the flash before getting stats and suspends it afterwards
* @param path Path to the filesystem to get stats for
* @param stat Pointer to fs_statvfs structure to be filled with stats
* @return 0 on success, negative error code on failure
*/
int fs_pm_statvfs(const char *path, struct fs_statvfs *stat);
/**
* @brief Wrapper around fs_stat that handles power management for the flash
* Resumes the flash before stat and suspends it afterwards
* @param path Path to file or directory
* @param entry Pointer to fs_dirent structure to receive metadata
* @return 0 on success, negative error code on failure
*/
int fs_pm_stat(const char *path, struct fs_dirent *entry);
/**
* @brief Wrapper around fs_mkdir that handles power management for the flash
* Resumes the flash before creating the directory and suspends it afterwards
* @param path Path to the directory to create
* @return 0 on success, negative error code on failure
*/
int fs_pm_mkdir(const char *path);
/**
* @brief Wrapper around fs_rename that handles power management for the flash
* Resumes the flash before renaming and suspends it afterwards
* @param old_path Current path of the file or directory
* @param new_path New path for the file or directory
* @return 0 on success, negative error code on failure
*/
int fs_pm_rename(const char *old_path, const char *new_path);
/**
* @brief Recursively creates directories for the given path, ensuring the flash is active during the operation
* @param path Path to the directory to create (can include multiple levels, e.g. "/dir1/dir2/dir3")
* @return 0 on success, negative error code on failure
*/
int fs_pm_mkdir_recursive(char *path);
/**
* @brief Recursively removes a directory and all its contents, ensuring the flash is active during the operation
* @param path Path to the directory to remove
* @param max_len Maximum length of the path buffer
* @return 0 on success, negative error code on failure
*/
int fs_pm_rm_recursive(char *path, size_t max_len);
/**
* @brief Gets the length of the audio data in a file, accounting for any metadata tags
* @param fp Pointer to an open fs_file_t structure representing the audio file
* @return Length of the audio data in bytes, or negative error code on failure
*/
int fs_get_audio_data_len(struct fs_file_t *fp);
/**
* @brief Reads audio data from a file, ensuring that it does not read past the audio data limit
* @param fp Pointer to an open fs_file_t structure representing the audio file
* @param buffer Pointer to the buffer to read data into
* @param len Maximum number of bytes to read
* @param audio_limit Maximum byte offset for audio data (e.g. file size minus metadata)
* @return Number of bytes read, or negative error code on failure
*/
int fs_read_audio(struct fs_file_t *fp, void *buffer, size_t len, size_t audio_limit);
/**
* @brief Positions file pointer at start of tag payload if tags exist.
* @param fp Pointer to an open fs_file_t structure representing the audio file
* @param version Pointer to receive tag format version
* @param payload_len Pointer to receive tag payload length in bytes
* @return 0 on success, -ENOENT if no tags exist, negative error code on failure
*/
int fs_tag_open_read(struct fs_file_t *fp, uint8_t *version, size_t *payload_len);
/**
* @brief Reads a chunk from current tag payload position.
* @param fp Pointer to an open fs_file_t positioned in tag payload
* @param buffer Destination buffer
* @param len Maximum bytes to read
* @return Number of bytes read, 0 at payload end, negative error code on failure
*/
ssize_t fs_tag_read_chunk(struct fs_file_t *fp, void *buffer, size_t len);
/**
* @brief Setzt die Synchronisation für einen neuen Dateitransfer zurück.
*/
void fs_reset_transfer_sync(void);
/**
* @brief Blockiert den aufrufenden Thread, bis der FS-Thread den Transfer
* (EOF oder ABORT) vollständig auf dem Flash abgeschlossen hat.
*/
void fs_wait_for_transfer_complete(void);
/**
* @brief Retrieves information about the firmware slot, such as start address and size
* @param info Pointer to slot_info_t structure to be filled with slot information
* @return 0 on success, negative error code on failure
*/
int flash_get_slot_info(slot_info_t *info);
/**
* @brief Initializes the flash for firmware upload, preparing it for receiving new firmware data
* @return 0 on success, negative error code on failure
*/
int flash_init_firmware_upload(void);
/**
* @brief Writes a block of firmware data to the flash
* @param buffer Pointer to the data buffer
* @param length Length of the data buffer
* @param is_last_block Indicates if this is the last block of the firmware
* @return 0 on success, negative error code on failure
*/
int flash_write_firmware_block(const uint8_t *buffer, size_t length, bool is_last_block);
/**
* @brief Gets the page size of the internal flash, which is needed for proper write operations
* @return Page size in bytes
*/
size_t fs_get_internal_flash_page_size(void);
/**
* @brief Gets the size of the firmware slot, which is needed for proper write operations
* @return Size in bytes
*/
size_t fs_get_fw_slot_size(void);
/**
* @brief Gets the page size of the external flash, which is needed for proper write operations
* @return Page size in bytes
*/
size_t fs_get_external_flash_page_size(void);
#endif // FS_H

23
firmware/include/io.h Normal file
View File

@@ -0,0 +1,23 @@
#ifndef IO_H
#define IO_H
#include <zephyr/device.h>
#include <zephyr/drivers/gpio.h>
/**
* @brief Initializes the I/O subsystem, including GPIOs and any related hardware
* @return 0 on success, negative error code on failure
*/
int io_init(void);
/**
* @brief Sets the status of the I/O subsystem
* @param status The status to set
*/
void io_status(bool status);
/**
* @brief Sets the USB connection status indicator
* @param connected True if USB is connected, false otherwise
*/
void io_usb_status(bool connected);
#endif

View File

@@ -0,0 +1,90 @@
#ifndef PROTOCOL_H
#define PROTOCOL_H
#include <zephyr/kernel.h>
#include <stdint.h>
#define PROTOCOL_MAX_PATH_LEN 32U
typedef enum {
PS_WAIT_SYNC = 0,
PS_READ_FRAME_TYPE,
PS_READ_REQ,
PS_READ_REQ_DATA,
} protocol_state_t;
typedef enum {
CMD_GET_PROTOCOL_VERSION = 0x00,
CMD_GET_FIRMWARE_STATUS = 0x01,
CMD_GET_FLASH_STATUS = 0x02,
CMD_CONFIRM_FIRMWARE = 0x03,
CMD_REBOOT = 0x04,
CMD_LIST_DIR = 0x10,
CMD_CHECK_FILE_CRC = 0x11,
CMD_MKDIR = 0x12,
CMD_RM = 0x13,
CMD_STAT = 0x18,
CMD_RENAME = 0x19,
CMD_PUT_FILE = 0x20,
CMD_PUT_FW = 0x21,
CMD_GET_FILE = 0x22,
CMD_PUT_TAGS = 0x24,
CMD_GET_TAGS = 0x25,
CMD_PLAY = 0x30,
CMD_STOP = 0x31,
CMD_SET_SETTING = 0x40,
CMD_GET_SETTING = 0x41,
} protocol_cmd_t;
typedef enum {
FRAME_REQ = 0x01,
FRAME_REQ_DATA = 0x02,
FRAME_RESP_ACK = 0x10,
FRAME_RESP_DATA = 0x11,
FRAME_RESP_STREAM_START = 0x12,
// FRAME_RESP_STREAM_CHUNK = 0x13,
FRAME_RESP_STREAM_END = 0x14,
FRAME_RESP_LIST_START = 0x15,
FRAME_RESP_LIST_CHUNK = 0x16,
FRAME_RESP_LIST_END = 0x17,
FRAME_RESP_ERROR = 0xFF,
} protocol_frame_type_t;
typedef enum {
P_ERR_NONE = 0x00,
P_ERR_INVALID_COMMAND = 0x01,
P_ERR_INVALID_PARAMETERS = 0x02,
P_ERR_COMMAND_TOO_LONG = 0x03,
P_ERR_FILE_NOT_FOUND = 0x10,
P_ERR_ALREADY_EXISTS = 0x11,
P_ERR_NOT_A_DIRECTORY = 0x12,
P_ERR_IS_A_DIRECTORY = 0x13,
P_ERR_ACCESS_DENIED = 0x14,
P_ERR_NO_SPACE = 0x15,
P_ERR_FILE_TOO_LARGE = 0x16,
P_ERR_IO = 0x20,
P_ERR_TIMEOUT = 0x21,
P_ERR_CRC_MISMATCH = 0x22,
P_ERR_TRANSFER_ABORTED = 0x23,
P_ERR_NOT_SUPPORTED = 0x30,
P_ERR_BUSY = 0x31,
P_ERR_INTERNAL = 0x32,
} protocol_error_t;
typedef enum
{
FW_STATUS_CONFIRMED = 0x00,
FW_STATUS_PENDING = 0x01,
FW_STATUS_TESTING = 0x02,
} firmware_status_t;
void protocol_thread_entry(void *p1, void *p2, void *p3);
#endif // PROTOCOL_H

View File

@@ -0,0 +1,28 @@
#ifndef BUZZER_SETTINGS_H
#define BUZZER_SETTINGS_H
#include <stdint.h>
#include <stdbool.h>
/* Struktur für den direkten Lesezugriff aus dem RAM (Zero-Latency) */
typedef struct {
uint8_t audio_vol; /* 0..100 */
bool play_norepeat; /* true = 1, false = 0 */
uint32_t storage_interval_s; /* 0..7200 Sekunden */
} app_settings_t;
/* Globale Instanz für den direkten Lesezugriff */
extern app_settings_t app_settings;
/* Initialisiert das Settings-Subsystem, NVS und lädt die gespeicherten Werte */
int app_settings_init(void);
/* Setter: Aktualisieren den RAM-Wert und starten/verlängern den Speichern-Timer */
void app_settings_set_audio_vol(uint8_t vol);
void app_settings_set_play_norepeat(bool norepeat);
void app_settings_set_storage_interval(uint32_t interval_s);
/* Forciert sofortiges Speichern aller anstehenden Werte (Aufruf z.B. vor CMD_REBOOT) */
void app_settings_save_pending_now(void);
#endif /* BUZZER_SETTINGS_H */

8
firmware/include/uart.h Normal file
View File

@@ -0,0 +1,8 @@
#ifndef _UART_H_
#define _UART_H_
int uart_init(void);
int uart_write(const uint8_t *data, size_t len, k_timeout_t timeout);
int uart_write_string(const char *str, k_timeout_t timeout);
int uart_read(uint8_t *buffer, size_t max_len, k_timeout_t timeout);
#endif /* _UART_H_ */

8
firmware/include/usb.h Normal file
View File

@@ -0,0 +1,8 @@
#ifndef USB_H_
#define USB_H_
int usb_init(void);
void usb_wait_for_dtr(void);
bool usb_dtr_active(void);
#endif /* USB_H_ */

25
firmware/include/utils.h Normal file
View File

@@ -0,0 +1,25 @@
#ifndef UTILS_H
#define UTILS_H
#include <zephyr/kernel.h>
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