sync
This commit is contained in:
@@ -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
|
||||
|
||||
10
buzzer_tool/core/commands/play.py
Normal file
10
buzzer_tool/core/commands/play.py
Normal 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}")
|
||||
@@ -10,6 +10,7 @@ target_sources(app PRIVATE
|
||||
src/audio.c
|
||||
src/usb.c
|
||||
src/protocol.c
|
||||
src/utils.c
|
||||
)
|
||||
zephyr_include_directories(src)
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
VERSION_MAJOR = 0
|
||||
VERSION_MINOR = 1
|
||||
PATCHLEVEL = 8
|
||||
PATCHLEVEL = 11
|
||||
VERSION_TWEAK = 0
|
||||
EXTRAVERSION = 0
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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.");
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
53
firmware/src/utils.c
Normal 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
24
firmware/src/utils.h
Normal 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
|
||||
Reference in New Issue
Block a user