This commit is contained in:
2026-02-27 16:22:11 +01:00
parent 125d11fa46
commit 09a2d1d82d
10 changed files with 218 additions and 40 deletions

View File

@@ -3,7 +3,7 @@ import argparse
import sys import sys
from core.config import load_config from core.config import load_config
from core.connection import BuzzerConnection, BuzzerError 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(): def main():
parser = argparse.ArgumentParser(description="Edis Buzzer Host Tool") 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("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") 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 # Befehl: confirm
confirm_parser = subparsers.add_parser("confirm", help="Bestätigt die aktuell laufende Firmware") 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") reboot_parser = subparsers.add_parser("reboot", help="Startet den Buzzer neu")
# Argumente parsen # Argumente parsen
args = parser.parse_args() args = parser.parse_args()
config = load_config(args) config = load_config(args)
@@ -87,6 +93,8 @@ def main():
confirm.execute(conn) confirm.execute(conn)
elif args.command == "reboot": elif args.command == "reboot":
reboot.execute(conn) reboot.execute(conn)
elif args.command == "play":
play.execute(conn, path=args.path)
elif args.command == "info" or args.command is None: elif args.command == "info" or args.command is None:
# Wurde kein Befehl oder explizit 'info' angegeben, sind wir hier schon fertig # Wurde kein Befehl oder explizit 'info' angegeben, sind wir hier schon fertig
pass pass

View File

@@ -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}")

View File

@@ -10,6 +10,7 @@ target_sources(app PRIVATE
src/audio.c src/audio.c
src/usb.c src/usb.c
src/protocol.c src/protocol.c
src/utils.c
) )
zephyr_include_directories(src) zephyr_include_directories(src)

View File

@@ -1,5 +1,5 @@
VERSION_MAJOR = 0 VERSION_MAJOR = 0
VERSION_MINOR = 1 VERSION_MINOR = 1
PATCHLEVEL = 8 PATCHLEVEL = 11
VERSION_TWEAK = 0 VERSION_TWEAK = 0
EXTRAVERSION = 0 EXTRAVERSION = 0

View File

@@ -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 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 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 volatile bool abort_playback = false;
static char next_random_filename[64] = {0}; static char next_random_filename[64] = {0};
@@ -219,6 +219,8 @@ void audio_thread(void *arg1, void *arg2, void *arg3)
bool trigger_started = false; bool trigger_started = false;
int queued_blocks = 0; 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) while (!abort_playback)
{ {
@@ -234,7 +236,7 @@ void audio_thread(void *arg1, void *arg2, void *arg3)
if (abort_playback) if (abort_playback)
{ {
k_mem_slab_free(&audio_slab, &block); k_mem_slab_free(&audio_slab, block);
break; break;
} }
@@ -242,7 +244,7 @@ void audio_thread(void *arg1, void *arg2, void *arg3)
if (bytes_read <= 0) if (bytes_read <= 0)
{ {
k_mem_slab_free(&audio_slab, &block); k_mem_slab_free(&audio_slab, block);
break; break;
} }
@@ -252,7 +254,7 @@ void audio_thread(void *arg1, void *arg2, void *arg3)
for (int i = samples_read - 1; i >= 0; i--) 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] = sample;
samples[i * 2 + 1] = 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 */ /* Block in die DMA-Queue schieben */
if (i2s_write(i2s_dev, block, AUDIO_BLOCK_SIZE) < 0) 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; break;
} }

View File

@@ -14,6 +14,20 @@
#include <audio.h> #include <audio.h>
#include <io.h> #include <io.h>
#include <usb.h> #include <usb.h>
#include <utils.h>
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); LOG_MODULE_REGISTER(main, LOG_LEVEL_INF);
@@ -24,25 +38,29 @@ int main(void)
int rc; int rc;
rc = fs_init(); rc = fs_init();
if (rc < 0) { if (rc < 0)
{
LOG_ERR("Filesystem initialization failed: %d", rc); LOG_ERR("Filesystem initialization failed: %d", rc);
return rc; return rc;
} }
rc = audio_init(); rc = audio_init();
if (rc < 0) { if (rc < 0)
{
LOG_ERR("Audio initialization failed: %d", rc); LOG_ERR("Audio initialization failed: %d", rc);
return rc; return rc;
} }
rc = usb_cdc_acm_init(); rc = usb_cdc_acm_init();
if (rc < 0) { if (rc < 0)
{
LOG_ERR("USB initialization failed: %d", rc); LOG_ERR("USB initialization failed: %d", rc);
return rc; return rc;
} }
rc = io_init(); rc = io_init();
if (rc < 0) { if (rc < 0)
{
LOG_ERR("I/O initialization failed: %d", rc); LOG_ERR("I/O initialization failed: %d", rc);
return rc; return rc;
} }
@@ -54,6 +72,11 @@ int main(void)
LOG_INF("Confirmation of firmware image pending. Inform user..."); LOG_INF("Confirmation of firmware image pending. Inform user...");
audio_play("/lfs/sys/update"); 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 else
{ {
LOG_INF("Firmware image already confirmed. No need to confirm again."); LOG_INF("Firmware image already confirmed. No need to confirm again.");

View File

@@ -6,8 +6,8 @@
#include <app_version.h> #include <app_version.h>
#include <zephyr/sys/crc.h> #include <zephyr/sys/crc.h>
#include <zephyr/dfu/mcuboot.h> #include <zephyr/dfu/mcuboot.h>
#include <zephyr/sys/reboot.h>
#include <utils.h>
#include <usb.h> #include <usb.h>
#include <protocol.h> #include <protocol.h>
#include <audio.h> #include <audio.h>
@@ -41,7 +41,7 @@ void send_error(int32_t error_code)
usb_write_buffer((const uint8_t *)response, strlen(response)); 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_dir_t dirp;
struct fs_dirent entry; struct fs_dirent entry;
@@ -65,7 +65,7 @@ int send_ls(const char *path)
return 0; return 0;
} }
int send_info() int cmd_info()
{ {
char info[112]; char info[112];
struct fs_statvfs stat; struct fs_statvfs stat;
@@ -80,7 +80,7 @@ int send_info()
return 0; 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; int rc;
ssize_t bytes_written = 0; 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(); send_ok();
LOG_INF("Firmware upgrade requested, rebooting into bootloader..."); LOG_INF("Firmware upgrade requested, rebooting into bootloader...");
k_sleep(K_MSEC(100)); // Kurze Pause, damit die OK-Antwort noch rausgeht reboot_with_status(REBOOT_STATUS_FIRMWARE_UPDATE);
while (log_process());
sys_reboot(SYS_REBOOT_COLD);
} }
else else
{ {
@@ -247,7 +245,7 @@ int put_binary_file(const char *filename, ssize_t filesize, uint32_t expected_cr
return 0; return 0;
} }
int mkdir(const char *path) { int cmd_mkdir(const char *path) {
int rc = fs_pm_mkdir(path); int rc = fs_pm_mkdir(path);
if (rc < 0) if (rc < 0)
{ {
@@ -257,7 +255,7 @@ int mkdir(const char *path) {
return rc; return rc;
} }
int rm(const char *path) { int cmd_rm(const char *path) {
int rc = fs_pm_unlink(path); int rc = fs_pm_unlink(path);
if (rc < 0) if (rc < 0)
{ {
@@ -267,7 +265,7 @@ int rm(const char *path) {
return rc; return rc;
} }
int confirm_firmware() { int cmd_confirm_firmware() {
int rc = boot_write_img_confirmed(); int rc = boot_write_img_confirmed();
if (rc < 0) if (rc < 0)
{ {
@@ -275,19 +273,50 @@ int confirm_firmware() {
return rc; return rc;
} }
LOG_INF("Firmware confirmed successfully"); LOG_INF("Firmware confirmed successfully");
audio_play("/lfs/sys/confirm"); reboot_with_status(REBOOT_STATUS_FIRMWARE_CONFIRMED);
return 0; return 0;
} }
int reboot_device() { int cmd_reboot_device() {
LOG_INF("Rebooting device as requested by host..."); LOG_INF("Rebooting device as requested by host...");
send_ok(); send_ok();
k_sleep(K_MSEC(100)); // Kurze Pause, damit die OK-Antwort noch rausgeht reboot_with_status(REBOOT_STATUS_NORMAL);
while (log_process());
sys_reboot(SYS_REBOOT_COLD);
return 0; // Dieser Code wird nie erreicht, aber wir geben ihn der Vollständigkeit halber zurück 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) void execute_current_command(void)
{ {
int rc; int rc;
@@ -295,7 +324,7 @@ void execute_current_command(void)
{ {
case CMD_LS: case CMD_LS:
LOG_DBG("Executing LS command with parameters: '%s'", buffer); LOG_DBG("Executing LS command with parameters: '%s'", buffer);
rc = send_ls((char *)buffer); rc = cmd_ls((char *)buffer);
if (rc == 0) if (rc == 0)
{ {
send_ok(); send_ok();
@@ -311,7 +340,7 @@ void execute_current_command(void)
LOG_WRN("INFO command received with unexpected parameters: '%s'", buffer); LOG_WRN("INFO command received with unexpected parameters: '%s'", buffer);
} }
LOG_DBG("Executing INFO command"); LOG_DBG("Executing INFO command");
rc = send_info(); rc = cmd_info();
if (rc == 0) if (rc == 0)
{ {
send_ok(); send_ok();
@@ -334,7 +363,7 @@ void execute_current_command(void)
break; break;
} }
LOG_DBG("Executing PUT_BINARY_FILE command filename: '%s', filesize: %zd, crc32: 0x%08x", filename, filesize, crc32); 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) if (rc == 0)
{ {
send_ok(); send_ok();
@@ -348,7 +377,7 @@ void execute_current_command(void)
break; break;
case CMD_MKDIR: case CMD_MKDIR:
LOG_DBG("Executing MKDIR command with parameters: '%s'", buffer); LOG_DBG("Executing MKDIR command with parameters: '%s'", buffer);
rc = mkdir((char *)buffer); rc = cmd_mkdir((char *)buffer);
if (rc == 0) { if (rc == 0) {
send_ok(); send_ok();
} }
@@ -358,7 +387,7 @@ void execute_current_command(void)
break; break;
case CMD_RM: case CMD_RM:
LOG_DBG("Executing RM command with parameters: '%s'", buffer); LOG_DBG("Executing RM command with parameters: '%s'", buffer);
rc = rm((char *)buffer); rc = cmd_rm((char *)buffer);
if (rc == 0) { if (rc == 0) {
send_ok(); send_ok();
audio_refresh_file_count(); // Nach erfolgreichem Löschen die Anzahl der verfügbaren Audiodateien aktualisieren 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; break;
case CMD_CONFIRM: case CMD_CONFIRM:
LOG_DBG("Executing CONFIRM command"); LOG_DBG("Executing CONFIRM command");
rc = confirm_firmware(); rc = cmd_confirm_firmware();
if (rc == 0) if (rc != 0) {
{ send_ok();
}
else
{
send_error(rc); send_error(rc);
break;
} }
send_ok();
break; break;
case CMD_REBOOT: case CMD_REBOOT:
LOG_DBG("Executing REBOOT command"); LOG_DBG("Executing REBOOT command");
rc = reboot_device(); rc = cmd_reboot_device();
if (rc != 0) { if (rc != 0) {
send_error(rc); send_error(rc);
} }
break; 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: default:
LOG_ERR("No execution logic for command %d", current_command); LOG_ERR("No execution logic for command %d", current_command);
send_error(ENOSYS); send_error(ENOSYS);
@@ -435,15 +478,27 @@ protocol_state_t reading_command(uint8_t byte)
{ {
LOG_DBG("Received RM command"); LOG_DBG("Received RM command");
current_command = CMD_RM; current_command = CMD_RM;
} else if (strcmp((char *)buffer, "confirm") == 0) }
else if (strcmp((char *)buffer, "confirm") == 0)
{ {
LOG_DBG("Received CONFIRM command"); LOG_DBG("Received CONFIRM command");
current_command = CMD_CONFIRM; current_command = CMD_CONFIRM;
} else if (strcmp((char *)buffer, "reboot") == 0) }
else if (strcmp((char *)buffer, "reboot") == 0)
{ {
LOG_DBG("Received REBOOT command"); LOG_DBG("Received REBOOT command");
current_command = CMD_REBOOT; 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 else
{ {
LOG_DBG("Unknown command: %s", buffer); LOG_DBG("Unknown command: %s", buffer);

View File

@@ -17,6 +17,8 @@ typedef enum {
CMD_RM, CMD_RM,
CMD_CONFIRM, CMD_CONFIRM,
CMD_REBOOT, CMD_REBOOT,
CMD_PLAY,
CMD_CHECK,
/* Weitere Kommandos folgen hier */ /* Weitere Kommandos folgen hier */
} protocol_cmd_t; } protocol_cmd_t;

53
firmware/src/utils.c Normal file
View File

@@ -0,0 +1,53 @@
#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
#include <zephyr/sys/printk.h>
#include <zephyr/logging/log_ctrl.h>
#include <zephyr/sys/reboot.h>
#if IS_ENABLED(CONFIG_SOC_SERIES_NRF52X)
#include <hal/nrf_power.h>
#elif IS_ENABLED(CONFIG_SOC_SERIES_STM32G0X)
#include <stm32_ll_rtc.h>
#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;
}

24
firmware/src/utils.h Normal file
View File

@@ -0,0 +1,24 @@
#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