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
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

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/usb.c
src/protocol.c
src/utils.c
)
zephyr_include_directories(src)

View File

@@ -1,5 +1,5 @@
VERSION_MAJOR = 0
VERSION_MINOR = 1
PATCHLEVEL = 8
PATCHLEVEL = 11
VERSION_TWEAK = 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 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;
}

View File

@@ -14,6 +14,20 @@
#include <audio.h>
#include <io.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);
@@ -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.");

View File

@@ -6,8 +6,8 @@
#include <app_version.h>
#include <zephyr/sys/crc.h>
#include <zephyr/dfu/mcuboot.h>
#include <zephyr/sys/reboot.h>
#include <utils.h>
#include <usb.h>
#include <protocol.h>
#include <audio.h>
@@ -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);

View File

@@ -17,6 +17,8 @@ typedef enum {
CMD_RM,
CMD_CONFIRM,
CMD_REBOOT,
CMD_PLAY,
CMD_CHECK,
/* Weitere Kommandos folgen hier */
} 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