Sync while working on OT
All checks were successful
Deploy Docs / build-and-deploy (push) Successful in 12s

This commit is contained in:
2026-02-18 14:37:32 +01:00
parent 07127fb074
commit 1a589e104c
24 changed files with 616 additions and 518 deletions

View File

@@ -0,0 +1,44 @@
// To get started, press Ctrl+Space (or Option+Esc) to bring up the completion menu and view the available nodes.
// You can also use the buttons in the sidebar to perform actions on nodes.
// Actions currently available include:
// * Enabling / disabling the node
// * Adding the bus to a bus
// * Removing the node
// * Connecting ADC channels
// For more help, browse the DeviceTree documentation at https://docs.zephyrproject.org/latest/guides/dts/index.html
// You can also visit the nRF DeviceTree extension documentation at https://docs.nordicsemi.com/bundle/nrf-connect-vscode/page/guides/ncs_configure_app.html#devicetree-support-in-the-extension
/ {
chosen {
nordic,pm-ext-flash = &mx25r64;
};
};
&pinctrl {
i2s0_default: i2s0_default {
group1 {
psels = <NRF_PSEL(I2S_SCK_M, 0, 31)>, /* SCK Pin */
<NRF_PSEL(I2S_LRCK_M, 0, 30)>, /* WS/LRCK Pin */
<NRF_PSEL(I2S_SDOUT, 0, 29)>; /* SD Pin (DIN am MAX) */
};
};
i2s0_sleep: i2s0_sleep {
group1 {
psels = <NRF_PSEL(I2S_SCK_M, 0, 31)>,
<NRF_PSEL(I2S_LRCK_M, 0, 30)>,
<NRF_PSEL(I2S_SDOUT, 0, 29)>;
low-power-enable;
};
};
};
&i2s0 {
status = "okay";
pinctrl-0 = <&i2s0_default>;
pinctrl-1 = <&i2s0_sleep>;
pinctrl-names = "default", "sleep";
};

View File

@@ -6,12 +6,17 @@ CONFIG_UART_INTERRUPT_DRIVEN=y
# Shell configuration
CONFIG_SHELL_BACKEND_SERIAL=y
CONFIG_FILE_SYSTEM_SHELL=y
# Lasertag-specific configuration
CONFIG_BLE_MGMT=y
CONFIG_GAME_MGMT=y
CONFIG_GAME_MGMT_SHELL=y
CONFIG_GAME_MGMT_LOG_LEVEL_DBG=y
CONFIG_LASERTAG_ROLE_LEADER=y
CONFIG_THREAD_MGMT=y
CONFIG_THREAD_MGMT_LOG_LEVEL_DBG=y
CONFIG_THREAD_MGMT_SHELL=y
CONFIG_FS_MGMT=y
CONFIG_FS_MGMT_LOG_LEVEL_DBG=y
CONFIG_AUDIO_LOG_LEVEL_DBG=y

View File

@@ -3,6 +3,8 @@
#include <thread_mgmt.h>
#include <game_mgmt.h>
#include <lasertag_utils.h>
#include <fs_mgmt.h>
#include <audio.h>
LOG_MODULE_REGISTER(OT_SAMPLE, LOG_LEVEL_INF);
@@ -17,6 +19,20 @@ int main(void)
}
LOG_INF("Thread management initialized successfully.");
rc = fs_mgmt_init();
if (rc < 0) {
LOG_ERR("File system management initialization failed: %d", rc);
return rc;
}
LOG_INF("File system management initialized successfully.");
rc = audio_init();
if (rc < 0) {
LOG_ERR("Audio initialization failed: %d", rc);
return rc;
}
LOG_INF("Audio initialized successfully.");
rc = game_mgmt_init();
if (rc < 0) {
LOG_ERR("Game management initialization failed: %d", rc);

View File

@@ -1,43 +1,29 @@
# Console and Logging
CONFIG_LOG=y
# UART basics
CONFIG_SERIAL=y
CONFIG_UART_INTERRUPT_DRIVEN=y
# Shell and Built-in Commands
CONFIG_SHELL=y
CONFIG_DEVICE_SHELL=n
CONFIG_DEVMEM_SHELL=n
# Shell configuration
CONFIG_SHELL_BACKEND_SERIAL=y
CONFIG_FILE_SYSTEM_SHELL=y
# Stack protection
CONFIG_HW_STACK_PROTECTION=y
CONFIG_STACK_SENTINEL=y
# --- STACK SIZE UPDATES (Fixes the MPU/Stack Fault) ---
CONFIG_MAIN_STACK_SIZE=4096
CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=4096
CONFIG_BT_RX_STACK_SIZE=4096
# Storage and Settings (NVS)
CONFIG_FLASH=y
CONFIG_FLASH_MAP=y
CONFIG_NVS=y
CONFIG_SETTINGS=y
# --- CoAP & UDP Features ---
CONFIG_OPENTHREAD_COAP=y
CONFIG_OPENTHREAD_MANUAL_START=y
# Bluetooth
CONFIG_BT=y
CONFIG_BT_PERIPHERAL=y
CONFIG_BT_DEVICE_NAME="Lasertag-Device"
CONFIG_BT_DEVICE_NAME_DYNAMIC=y
CONFIG_BT_L2CAP_TX_MTU=252
CONFIG_BT_BUF_ACL_TX_SIZE=251
CONFIG_BT_BUF_ACL_RX_SIZE=251
CONFIG_BT_ATT_PREPARE_COUNT=5
CONFIG_BT_LOG_LEVEL_WRN=y
# Enable Lasertag Shared Modules
CONFIG_LASERTAG_UTILS=y
# Lasertag-specific configuration
CONFIG_BLE_MGMT=y
CONFIG_GAME_MGMT=y
CONFIG_GAME_MGMT_SHELL=y
CONFIG_GAME_MGMT_LOG_LEVEL_DBG=y
CONFIG_THREAD_MGMT=y
CONFIG_THREAD_MGMT_LOG_LEVEL_DBG=y
CONFIG_BLE_MGMT=y
CONFIG_BLE_MGMT_LOG_LEVEL_DBG=y
CONFIG_GAME_MGMT=y
CONFIG_THREAD_MGMT_SHELL=y
CONFIG_FS_MGMT=y
CONFIG_FS_MGMT_LOG_LEVEL_DBG=y
CONFIG_AUDIO_LOG_LEVEL_DBG=y
CONFIG_LASERTAG_ROLE_LEADER=y
CONFIG_ENTROPY_GENERATOR=y

View File

@@ -1,52 +1,56 @@
#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
#include <lasertag_utils.h>
#include <thread_mgmt.h>
#include <ble_mgmt.h>
#include <game_mgmt.h>
#include <lasertag_utils.h>
#include <fs_mgmt.h>
#include <audio.h>
#include <zephyr/random/random.h>
LOG_MODULE_REGISTER(leader_app, CONFIG_LOG_DEFAULT_LEVEL);
LOG_MODULE_REGISTER(OT_SAMPLE, LOG_LEVEL_INF);
uint64_t generate_64bit_random(void) {
uint64_t rnd_val;
/* Füllt den Speicherbereich der Variable mit Zufallsbytes */
sys_csrand_get(&rnd_val, sizeof(rnd_val));
return rnd_val;
}
int main(void)
{
/* Initialize shared project logic and NVS */
LOG_INF("Starting Thread Management test application...");
lasertag_utils_init();
/* Initialize and start BLE management for provisioning */
int err = ble_mgmt_init(LT_TYPE_LEADER);
if (err) {
LOG_ERR("BLE initialization failed (err %d)", err);
return err;
} else {
LOG_INF("BLE Management initialized successfully.");
int rc = thread_mgmt_init();
if (rc < 0) {
LOG_ERR("Thread management initialization failed: %d", rc);
return rc;
}
LOG_INF("Thread management initialized successfully.");
/* Initialize and start OpenThread stack */
err = thread_mgmt_init();
if (err) {
LOG_ERR("Thread initialization failed (err %d)", err);
return err;
} else {
LOG_INF("Leader Application successfully started with Thread Mesh.");
rc = fs_mgmt_init();
if (rc < 0) {
LOG_ERR("File system management initialization failed: %d", rc);
return rc;
}
LOG_INF("File system management initialized successfully.");
/* Start BLE advertising */
err = ble_mgmt_adv_start();
if (err) {
LOG_ERR("BLE advertising start failed (err %d)", err);
return err;
} else {
LOG_INF("BLE advertising started.");
rc = audio_init();
if (rc < 0) {
LOG_ERR("Audio initialization failed: %d", rc);
return rc;
}
LOG_INF("Audio initialized successfully.");
/* Initialize game management module */
err = game_mgmt_init();
if (err) {
LOG_ERR("Game Management initialization failed (err %d)", err);
return err;
} else {
LOG_INF("Game Management initialized successfully.");
rc = game_mgmt_init();
if (rc < 0) {
LOG_ERR("Game management initialization failed: %d", rc);
return rc;
}
LOG_INF(FORMAT_BRIGHT("Game management initialized successfully. Switching to LOBBY state..."));
game_mgmt_set_game_id(generate_64bit_random()); /* Set a dummy game ID for testing */
game_mgmt_set_state(SYS_STATE_LOBBY);
return 0;
}
}

View File

@@ -1,5 +0,0 @@
/ {
chosen {
nordic,pm-ext-flash = &mx25r64;
};
};

View File

@@ -1,4 +0,0 @@
littlefs_storage:
address: 0x0
size: 0x800000
region: external_flash

View File

@@ -0,0 +1 @@
../leader/pm_static.yml

View File

@@ -1,40 +1,28 @@
# Console and Logging
CONFIG_LOG=y
# UART basics
CONFIG_SERIAL=y
CONFIG_UART_INTERRUPT_DRIVEN=y
# Shell and Built-in Commands
CONFIG_SHELL=y
CONFIG_DEVICE_SHELL=n
CONFIG_DEVMEM_SHELL=n
# Shell configuration
CONFIG_SHELL_BACKEND_SERIAL=y
CONFIG_FILE_SYSTEM_SHELL=y
# Lasertag-specific configuration
CONFIG_AUDIO=y
CONFIG_AUDIO_LOG_LEVEL_DBG=y
# --- STACK SIZE UPDATES (Fixes the MPU/Stack Fault) ---
CONFIG_MAIN_STACK_SIZE=4096
CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=4096
CONFIG_BT_RX_STACK_SIZE=4096
CONFIG_BLE_MGMT=y
# Storage and Settings (NVS)
CONFIG_FLASH=y
CONFIG_FLASH_MAP=y
CONFIG_NVS=y
CONFIG_SETTINGS=y
# Network and OpenThread
# CONFIG_NETWORKING=y
# CONFIG_NET_L2_OPENTHREAD=y
# CONFIG_OPENTHREAD=y
# CONFIG_OPENTHREAD_SHELL=y
# CONFIG_OPENTHREAD_DEFAULT_TX_POWER=8
# CONFIG_OPENTHREAD_COAP=y
# CONFIG_OPENTHREAD_MANUAL_START=y
# Enable Lasertag Shared Modules
CONFIG_LASERTAG_UTILS=n
CONFIG_THREAD_MGMT=n
CONFIG_OPENTHREAD_FTD=y
CONFIG_BLE_MGMT=n
CONFIG_GAME_MGMT=y
CONFIG_GAME_MGMT_SHELL=y
CONFIG_GAME_MGMT_LOG_LEVEL_DBG=y
CONFIG_THREAD_MGMT=y
CONFIG_THREAD_MGMT_LOG_LEVEL_DBG=y
CONFIG_THREAD_MGMT_SHELL=y
CONFIG_FS_MGMT=y
CONFIG_FS_MGMT_LOG_LEVEL_DBG=y
CONFIG_LASERTAG_ROLE_VEST=y

View File

@@ -1,72 +1,44 @@
#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
#ifdef CONFIG_LASERTAG_UTILS
#include <lasertag_utils.h>
#endif
#ifdef CONFIG_THREAD_MGMT
#include <thread_mgmt.h>
#endif
#ifdef CONFIG_BLE_MGMT
#include <ble_mgmt.h>
#endif
#ifdef CONFIG_FS_MGMT
#include <game_mgmt.h>
#include <lasertag_utils.h>
#include <fs_mgmt.h>
#endif
#include <audio.h>
LOG_MODULE_REGISTER(vest_app, CONFIG_LOG_DEFAULT_LEVEL);
LOG_MODULE_REGISTER(OT_SAMPLE, LOG_LEVEL_INF);
int main(void)
{
int rc;
#ifdef CONFIG_LASERTAG_UTILS
/* Initialize shared project logic and NVS */
LOG_INF("Starting Thread Management test application...");
lasertag_utils_init();
#endif
int rc = thread_mgmt_init();
if (rc < 0) {
LOG_ERR("Thread management initialization failed: %d", rc);
return rc;
}
LOG_INF("Thread management initialized successfully.");
#ifdef CONFIG_FS_MGMT
/* Initialize filesystem management */
rc = fs_mgmt_init();
if (rc) {
LOG_ERR("Filesystem management initialization failed (err %d)", rc);
if (rc < 0) {
LOG_ERR("File system management initialization failed: %d", rc);
return rc;
}
#endif
LOG_INF("File system management initialized successfully.");
#ifdef CONFIG_BLE_MGMT
/* Initialize and start BLE management for provisioning */
rc = ble_mgmt_init(LT_TYPE_VEST);
if (rc) {
LOG_ERR("BLE initialization failed (err %d)", rc);
return rc;
} else {
LOG_INF("BLE Management initialized successfully.");
}
/* Start BLE advertising */
rc = ble_mgmt_adv_start();
if (rc) {
LOG_ERR("BLE advertising start failed (err %d)", rc);
} else {
LOG_INF("BLE advertising started.");
}
#endif
#ifdef CONFIG_THREAD_MGMT
/* Initialize and start OpenThread stack */
rc = thread_mgmt_init();
if (rc) {
LOG_ERR("Thread initialization failed (err %d)", rc);
} else {
LOG_INF("Vest Application successfully started with Thread Mesh.");
rc = audio_init();
if (rc < 0) {
LOG_ERR("Audio initialization failed: %d", rc);
return rc;
}
#endif
LOG_INF("Audio initialized successfully.");
while (1) {
/* Main loop - handle high-level game logic here */
k_sleep(K_MSEC(1000));
rc = game_mgmt_init();
if (rc < 0) {
LOG_ERR("Game management initialization failed: %d", rc);
return rc;
}
LOG_INF(FORMAT_BRIGHT("Game management initialized successfully. Switching to LOBBY state..."));
game_mgmt_set_state(SYS_STATE_LOBBY);
return 0;
}
}

View File

@@ -1,20 +1,23 @@
# Logging
CONFIG_LOG=y
CONFIG_LASERTAG_WEAPON_LOG_LEVEL_INF=y
# Network / OpenThread
CONFIG_NETWORKING=y
CONFIG_NET_L2_OPENTHREAD=y
CONFIG_OPENTHREAD_COAP=y
# UART basics
CONFIG_SERIAL=y
CONFIG_UART_INTERRUPT_DRIVEN=y
# Hardware (Buttons & LEDs)
CONFIG_DK_LIBRARY=y
# Shell configuration
CONFIG_SHELL_BACKEND_SERIAL=y
CONFIG_FILE_SYSTEM_SHELL=y
# Lasertag Game Logic
CONFIG_LASERTAG_GAME_LOGIC=y
CONFIG_LASERTAG_ROLE_PLAYER=y
CONFIG_LASERTAG_PLAYER_ID_DEFAULT=2
# Lasertag-specific configuration
CONFIG_BLE_MGMT=y
CONFIG_GAME_MGMT=y
CONFIG_GAME_MGMT_SHELL=y
CONFIG_GAME_MGMT_LOG_LEVEL_DBG=y
CONFIG_THREAD_MGMT=y
CONFIG_THREAD_MGMT_LOG_LEVEL_DBG=y
CONFIG_THREAD_MGMT_SHELL=y
CONFIG_FS_MGMT=y
CONFIG_FS_MGMT_LOG_LEVEL_DBG=y
CONFIG_AUDIO_LOG_LEVEL_DBG=y
# Optional: Shell for debugging
CONFIG_SHELL=y
CONFIG_OPENTHREAD_SHELL=y
CONFIG_LASERTAG_ROLE_VEST=y

View File

@@ -1,4 +1,22 @@
# Main entry point for custom project Kconfigs
choice LASERTAG_DEVICE_ROLE
prompt "Lasertag Device Role"
default LASERTAG_ROLE_VEST
help
Select the role of the lasertag device. This can be used to conditionally compile code specific to vests, guns, or other device types.
config LASERTAG_ROLE_VEST
bool "Vest"
help
A standard role for the vest device, which may have responsibilities such as receiving hit notifications, managing player health, etc.
config LASERTAG_ROLE_WEAPON
bool "Weapon"
help
A special role for the weapon device, which may have additional responsibilities such as sending hit notifications, managing ammo count, etc.
config LASERTAG_ROLE_LEADER
bool "Game Leader"
help
A special role for the game leader device, which may have additional responsibilities such as starting/stopping games, managing player lists, etc.
endchoice
rsource "lasertag_utils/Kconfig"
rsource "thread_mgmt/Kconfig"
rsource "ble_mgmt/Kconfig"

View File

@@ -1,5 +1,5 @@
if(CONFIG_AUDIO)
zephyr_library()
zephyr_sources(src/audio.c)
zephyr_library_sources(src/audio.c)
zephyr_include_directories(include)
endif()

View File

@@ -1,8 +1,8 @@
menuconfig AUDIO
bool "Audio Support"
depends on FS_MGMT
select FS_MGMT
select I2S
select I2S_NRFX
select I2S_NRFX if DT_HAS_NORDIC_NRF_I2S_ENABLED
help
Library for initializing and managing the audio subsystem.
@@ -64,7 +64,7 @@ if AUDIO
config AUDIO_MAX_PATH_LEN
int "Maximum Audio File Path Length"
default 16
default 32
range 8 128
help
Set the maximum length for audio file paths. Default is 16 characters.

View File

@@ -42,7 +42,7 @@ static void config_apply_work_handler(struct k_work *work)
{
ARG_UNUSED(work);
LOG_DBG("conf rcv, name: " BOLD("%s") ", state: " BOLD("%d") ", game-id: " BOLD("0x%llx") ", net name: " BOLD("%s") ", channel: " BOLD("%u") ", pan: " BOLD("0x%04X"),
LOG_DBG("conf rcv, name: " FORMAT_BOLD("%s") ", state: " FORMAT_BOLD("%d") ", game-id: " FORMAT_BOLD("0x%llx") ", net name: " FORMAT_BOLD("%s") ", channel: " FORMAT_BOLD("%u") ", pan: " FORMAT_BOLD("0x%04X"),
pending_config.node_name,
pending_config.system_state,
pending_config.game_id,
@@ -99,7 +99,8 @@ static ssize_t read_leader_config(struct bt_conn *conn, const struct bt_gatt_att
thread_mgmt_get_network_name(payload.network_name, sizeof(payload.network_name));
strncpy(payload.node_name, lasertag_get_device_name(), 32);
LOG_DBG("conf snd, name: " BOLD("%s") ", state: " BOLD("%d") ", game-id: " BOLD("0x%llx") ", net name: " BOLD("%s") ", channel: " BOLD("%u") ", pan: " BOLD("0x%04X"),
LOG_DBG("conf snd, name: " FORMAT_BOLD("%s") ", state: " FORMAT_BOLD("%d") ", game-id: "
FORMAT_BOLD("0x%llx") ", net name: " FORMAT_BOLD("%s") ", channel: " FORMAT_BOLD("%u") ", pan: " FORMAT_BOLD("0x%04X"),
payload.node_name,
payload.system_state,
payload.game_id,

View File

@@ -27,12 +27,112 @@ static struct fs_mount_t fs_storage_mnt = {
#define CMD_LS 0
#define CMD_RM 1
static int custom_ls_handler(struct smp_streamer *ctxt)
#define FS_MGMT_MAX_PATH_LEN 128
static int fs_mgmt_count_entries(const char *abs_path, bool recursive, int *count)
{
struct fs_dir_t dirp;
struct fs_dirent entry;
fs_dir_t_init(&dirp);
if (fs_opendir(&dirp, abs_path) != 0)
{
return -ENOENT;
}
while (fs_readdir(&dirp, &entry) == 0 && entry.name[0] != '\0')
{
(*count)++;
if (recursive && entry.type == FS_DIR_ENTRY_DIR)
{
char child_path[FS_MGMT_MAX_PATH_LEN];
int len = snprintk(child_path, sizeof(child_path), "%s/%s", abs_path, entry.name);
if (len <= 0 || len >= sizeof(child_path))
{
fs_closedir(&dirp);
return -ENAMETOOLONG;
}
int rc = fs_mgmt_count_entries(child_path, true, count);
if (rc != 0)
{
fs_closedir(&dirp);
return rc;
}
}
}
fs_closedir(&dirp);
return 0;
}
static bool fs_mgmt_encode_entries(zcbor_state_t *zse, const char *abs_path, const char *rel_prefix, bool recursive)
{
struct fs_dir_t dirp;
struct fs_dirent entry;
fs_dir_t_init(&dirp);
if (fs_opendir(&dirp, abs_path) != 0)
{
return false;
}
bool ok = true;
while (ok && fs_readdir(&dirp, &entry) == 0 && entry.name[0] != '\0')
{
const char *type_char = (entry.type == FS_DIR_ENTRY_DIR) ? "d" : "f";
char rel_name[FS_MGMT_MAX_PATH_LEN];
if (rel_prefix[0] == '\0')
{
int len = snprintk(rel_name, sizeof(rel_name), "%s", entry.name);
if (len <= 0 || len >= sizeof(rel_name))
{
ok = false;
break;
}
}
else
{
int len = snprintk(rel_name, sizeof(rel_name), "%s/%s", rel_prefix, entry.name);
if (len <= 0 || len >= sizeof(rel_name))
{
ok = false;
break;
}
}
ok = ok && zcbor_map_start_encode(zse, 2) &&
zcbor_tstr_put_lit(zse, "n") &&
zcbor_tstr_encode(zse, &(struct zcbor_string){.value = (const uint8_t *)rel_name, .len = strlen(rel_name)}) &&
zcbor_tstr_put_lit(zse, "t") &&
zcbor_tstr_encode(zse, &(struct zcbor_string){.value = (const uint8_t *)type_char, .len = 1}) &&
zcbor_map_end_encode(zse, 2);
if (ok && recursive && entry.type == FS_DIR_ENTRY_DIR)
{
char child_abs_path[FS_MGMT_MAX_PATH_LEN];
int len = snprintk(child_abs_path, sizeof(child_abs_path), "%s/%s", abs_path, entry.name);
if (len <= 0 || len >= sizeof(child_abs_path))
{
ok = false;
break;
}
ok = fs_mgmt_encode_entries(zse, child_abs_path, rel_name, true);
}
}
fs_closedir(&dirp);
return ok;
}
static int custom_ls_handler(struct smp_streamer *ctxt)
{
int file_count = 0;
char path[64] = ""; // Startet leer
char path[FS_MGMT_MAX_PATH_LEN] = "";
zcbor_state_t *zsd = ctxt->reader->zs;
zcbor_state_t *zse = ctxt->writer->zs;
@@ -64,41 +164,23 @@ static int custom_ls_handler(struct smp_streamer *ctxt)
zcbor_map_end_decode(zsd);
}
// Falls kein Pfad gesendet wurde, fange im Root an
// If no path is provided, default to root
if (!path_found || strlen(path) == 0)
{
strcpy(path, "/");
}
/* --- PROCESSING & ENCODING --- */
fs_dir_t_init(&dirp);
if (fs_opendir(&dirp, path) != 0)
return MGMT_ERR_ENOENT;
// Zählen...
while (fs_readdir(&dirp, &entry) == 0 && entry.name[0] != '\0')
int rc = fs_mgmt_count_entries(path, false, &file_count);
if (rc != 0)
{
file_count++;
return (rc == -ENOENT) ? MGMT_ERR_ENOENT : MGMT_ERR_EUNKNOWN;
}
fs_closedir(&dirp);
bool ok = zcbor_tstr_put_lit(zse, "files") && zcbor_list_start_encode(zse, file_count);
if (fs_opendir(&dirp, path) == 0)
{
while (fs_readdir(&dirp, &entry) == 0 && entry.name[0] != '\0')
{
const char *type_char = (entry.type == FS_DIR_ENTRY_DIR) ? "d" : "f";
ok = ok && zcbor_map_start_encode(zse, 2) &&
zcbor_tstr_put_lit(zse, "n") &&
zcbor_tstr_encode(zse, &(struct zcbor_string){.value = (const uint8_t *)entry.name, .len = strlen(entry.name)}) &&
zcbor_tstr_put_lit(zse, "t") &&
zcbor_tstr_encode(zse, &(struct zcbor_string){.value = (const uint8_t *)type_char, .len = 1}) &&
zcbor_map_end_encode(zse, 2);
}
fs_closedir(&dirp);
}
ok = ok && fs_mgmt_encode_entries(zse, path, "", false);
ok = ok && zcbor_list_end_encode(zse, file_count);
return ok ? 0 : MGMT_ERR_ENOMEM;
}
@@ -145,13 +227,13 @@ static int custom_rm_handler(struct smp_streamer *ctxt)
int rc = fs_unlink(path);
/* --- ENCODING RESPONSE --- */
// Wir senden den Return-Code zurück (0 = Erfolg)
// Return the filesystem result code (0 = success)
bool ok = zcbor_tstr_put_lit(zse, "rc") && zcbor_int32_put(zse, rc);
if (rc != 0)
{
LOG_WRN("Failed to remove %s: %d", path, rc);
// Optional: Spezifische mgmt_err Mapping
// Optional: map to more specific mgmt errors
return (rc == -ENOENT) ? MGMT_ERR_ENOENT : MGMT_ERR_EUNKNOWN;
}
@@ -164,7 +246,7 @@ static const struct mgmt_handler custom_handlers[] = {
.mh_write = NULL},
[CMD_RM] = {
.mh_read = NULL,
.mh_write = custom_rm_handler // Nutzung von WRITE für Löschvorgänge
.mh_write = custom_rm_handler // Use WRITE for delete operations
},
};

View File

@@ -1,5 +1,16 @@
if(CONFIG_GAME_MGMT)
zephyr_library()
zephyr_library_sources(src/game_mgmt.c)
zephyr_library_sources(
src/game_mgmt.c
src/game_mgmt_coap.c
src/game_mgmt_timing.c
)
if(CONFIG_LASERTAG_ROLE_LEADER)
zephyr_library_sources(src/game_mgmt_thread.c)
endif()
if(CONFIG_LASERTAG_ROLE_LEADER)
zephyr_library_sources(src/game_mgmt_device_list.c)
endif()
zephyr_include_directories(include)
zephyr_library_include_directories(include_lib_only)
endif()

View File

@@ -4,6 +4,8 @@ menuconfig GAME_MGMT
select OPENTHREAD
select OPENTHREAD_COAP
select LASERTAG_UTILS
select AUDIO
select ENTROPY_GENERATOR
help
Library for managing game states and logic in the lasertag device.
@@ -13,6 +15,25 @@ if GAME_MGMT
select SHELL
default n
config GAME_MGMT_BEACON_INTERVAL_S
int "Game Management beacon interval (s)"
default 5
range 1 60
help
Interval in milliseconds for sending leader beacons.
config GAME_MGMT_BEACON_THREAD_PRIORITY
int "Game Management beacon thread priority"
default 10
range 0 10
help
Thread priority for the Game Management beaconing thread (leader device).
config GAME_MGMT_BEACON_THREAD_STACK_SIZE
int "Game Management beacon thread stack size"
default 1024
range 256 4096
help
Stack size for the Game Management beaconing thread (leader device).
# Logging configuration for the Game Management module
module = GAME_MGMT
module-str = game_mgmt

View File

@@ -3,6 +3,9 @@
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include <openthread/thread.h>
#include <lasertag_utils.h>
/**
* @brief System states for the Lasertag devices.
@@ -25,9 +28,11 @@ typedef enum {
*/
typedef enum
{
GAME_CTRL_CMD_START_GAME = 0x01,
GAME_CTRL_CMD_END_GAME = 0x02,
GAME_CTRL_CMD_SET_ID = 0x03,
GAME_CTRL_CMD_START_GAME = 0x00, /* Command to start a game, includes start time and duration */
GAME_CTRL_CMD_REQUEST_ABORT_START_NO_TIME_SYNC = 0x01, /* Request to abort a pending game start when sender has no time sync */
GAME_CTRL_CMD_ABORT_START = 0x0F, /* Command to abort a pending game start */
GAME_CTRL_CMD_END_GAME = 0x20, /* Command to end the current game */
GAME_CTRL_CMD_SET_ID = 0x30, /* Command to set the game ID */
// Future commands can be added here
} game_ctrl_command_t;
@@ -53,6 +58,32 @@ typedef struct __packed
} data;
} game_control_payload_t;
/**
* @brief Leader beacon payload sent periodically during lobby/discovery.
*/
typedef struct __packed
{
uint64_t game_id;
} game_leader_beacon_payload_t;
/**
* @brief Player presence payload sent as unicast response to a leader beacon.
*/
typedef struct __packed
{
lasertag_device_type_t device_type;
uint8_t player_id;
uint8_t team_id;
} game_player_presence_payload_t;
typedef struct {
uint8_t eui64[8];
lasertag_device_type_t device_type;
uint8_t player_id;
uint8_t team_id;
uint8_t missed;
} game_device_info_t;
/**
* @brief Callback for state changes.
* Allows apps/modules to react when the system transitions (e.g., UI updates).
@@ -80,7 +111,46 @@ int game_mgmt_send_control_multicast(const game_control_payload_t *payload);
* @param payload Payload to send.
* @return 0 on success, negative errno-style value on failure.
*/
int game_mgmt_send_control_unicast(const char *peer_addr_str, const game_control_payload_t *payload);
int game_mgmt_send_control_unicast(const otIp6Address *peer_addr, const game_control_payload_t *payload);
/**
* @brief Generic multicast sender for Thread CoAP messages.
* Uses realm-local all-nodes multicast address `ff03::1`.
* @param uri_path CoAP URI path (without leading slash), e.g. `g`.
* @param payload Raw payload buffer.
* @param payload_len Payload size in bytes.
* @return 0 on success, negative errno-style value on failure.
*/
int game_mgmt_send_multicast(const char *uri_path, const void *payload, size_t payload_len);
/**
* @brief Generic unicast sender for Thread CoAP messages.
* @param peer_addr_str IPv6 destination address string.
* @param uri_path CoAP URI path (without leading slash), e.g. `g`.
* @param payload Raw payload buffer.
* @param payload_len Payload size in bytes.
* @return 0 on success, negative errno-style value on failure.
*/
int game_mgmt_send_unicast(const char *peer_addr_str,
const char *uri_path,
const void *payload,
size_t payload_len);
/**
* @brief Send leader beacon via multicast.
* @param payload Beacon payload.
* @return 0 on success, negative errno-style value on failure.
*/
int game_mgmt_send_leader_beacon_multicast(const game_leader_beacon_payload_t *payload);
/**
* @brief Send player presence as unicast response to leader.
* @param leader_addr_str IPv6 address string of the leader.
* @param payload Player presence payload.
* @return 0 on success, negative errno-style value on failure.
*/
int game_mgmt_send_player_presence_unicast(const char *leader_addr_str,
const game_player_presence_payload_t *payload);
/**
* @brief Set the system state and trigger corresponding actions.
@@ -113,4 +183,18 @@ uint64_t game_mgmt_get_game_id(void);
*/
void game_mgmt_register_state_cb(game_mgmt_state_cb_t cb);
/**
* @brief Set the unicast address of the current leader.
* This is used for sending unicast messages (e.g., presence responses).
* @param addr Pointer to the leader's IPv6 address, or NULL to clear.
*/
void game_mgmt_set_leader_unicast_addr(const otIp6Address *addr);
/**
* @brief Get the unicast address of the current leader.
* @param addr Pointer to an otIp6Address struct to be filled with the leader's address.
* @return true if a valid leader address is set, false if not.
*/
bool game_mgmt_get_leader_unicast_addr(otIp6Address *addr);
#endif /* GAME_MGMT_H */

View File

@@ -1,326 +1,95 @@
#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
#include <zephyr/net/openthread.h>
#include <openthread/coap.h>
#include <openthread/ip6.h>
#include <openthread/instance.h>
#include <openthread/thread.h>
#include <errno.h>
#include <stdlib.h>
#include <thread_mgmt.h>
#include <game_mgmt.h>
#include <lasertag_utils.h>
#include <game_mgmt_coap.h>
#include <game_mgmt_timing.h>
#include <game_mgmt_device_list.h>
LOG_MODULE_REGISTER(game_mgmt, CONFIG_GAME_MGMT_LOG_LEVEL);
// Global variables
static sys_state_t current_state = SYS_STATE_IDLE;
static uint64_t current_game_id = 0;
static struct k_work_delayable game_state_work;
// Forward declarations
int game_mgmt_init_coap(void);
void game_start_handler(struct k_work *work);
static otInstance *game_mgmt_get_instance(void)
{
struct openthread_context *context = openthread_get_default_context();
if (context)
{
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
otInstance *instance = context->instance;
#pragma GCC diagnostic pop
if (instance)
{
return instance;
}
}
return openthread_get_default_instance();
}
static int ot_error_to_errno(otError err)
{
switch (err)
{
case OT_ERROR_NONE:
return 0;
case OT_ERROR_INVALID_ARGS:
return -EINVAL;
case OT_ERROR_NO_BUFS:
return -ENOMEM;
case OT_ERROR_INVALID_STATE:
return -EAGAIN;
case OT_ERROR_BUSY:
return -EBUSY;
case OT_ERROR_NO_ROUTE:
return -ENETUNREACH;
default:
return -EIO;
}
}
static uint64_t game_mgmt_expand_t32_us(uint32_t t32_us, uint64_t now_us)
{
uint64_t expanded_us = (now_us & 0xFFFFFFFF00000000ULL) | t32_us;
if (expanded_us < now_us)
{
expanded_us += (1ULL << 32);
}
return expanded_us;
}
static int game_mgmt_send_control_to(const otIp6Address *peer_addr, const game_control_payload_t *payload)
{
otInstance *instance = game_mgmt_get_instance();
if (!instance || !peer_addr || !payload)
{
return -ENODEV;
}
if (!otIp6IsEnabled(instance) || (otThreadGetDeviceRole(instance) == OT_DEVICE_ROLE_DISABLED))
{
return -ENETDOWN;
}
otMessage *message = otCoapNewMessage(instance, NULL);
if (!message)
{
return -ENOMEM;
}
otCoapMessageInit(message, OT_COAP_TYPE_NON_CONFIRMABLE, OT_COAP_CODE_POST);
otError ot_err = otCoapMessageAppendUriPathOptions(message, "g");
if (ot_err != OT_ERROR_NONE)
{
LOG_ERR("otCoapMessageAppendUriPathOptions failed: %d", ot_err);
otMessageFree(message);
return ot_error_to_errno(ot_err);
}
ot_err = otCoapMessageSetPayloadMarker(message);
if (ot_err != OT_ERROR_NONE)
{
LOG_ERR("otCoapMessageSetPayloadMarker failed: %d", ot_err);
otMessageFree(message);
return ot_error_to_errno(ot_err);
}
ot_err = otMessageAppend(message, payload, sizeof(*payload));
if (ot_err != OT_ERROR_NONE)
{
LOG_ERR("otMessageAppend failed: %d", ot_err);
otMessageFree(message);
return ot_error_to_errno(ot_err);
}
otMessageInfo messageInfo = {0};
const otIp6Address *mesh_local_eid = otThreadGetMeshLocalEid(instance);
if (!mesh_local_eid)
{
LOG_ERR("otThreadGetMeshLocalEid returned NULL");
otMessageFree(message);
return -ENETDOWN;
}
messageInfo.mSockAddr = *mesh_local_eid;
messageInfo.mPeerAddr = *peer_addr;
messageInfo.mPeerPort = OT_DEFAULT_COAP_PORT;
ot_err = otCoapSendRequest(instance, message, &messageInfo, NULL, NULL);
if (ot_err != OT_ERROR_NONE)
{
LOG_ERR("otCoapSendRequest failed: %d", ot_err);
otMessageFree(message);
return ot_error_to_errno(ot_err);
}
return 0;
}
void schedule_game_start(uint64_t startTime_us)
{
uint64_t now_us;
now_us = thread_mgmt_get_network_time();
if (now_us == 0) {
LOG_ERR("Cannot schedule game start: Network time not synchronized");
return;
}
int64_t delay_us = (int64_t)(startTime_us - now_us);
if (delay_us > 0) {
k_work_init_delayable(&game_state_work, game_start_handler);
k_work_reschedule(&game_state_work, K_USEC(delay_us));
printk("Game scheduled in %lld ms\n", delay_us / 1000);
} else {
// Start time is in the past -> execute immediately
k_work_init_delayable(&game_state_work, game_start_handler);
k_work_submit(&game_state_work.work);
}
}
int game_mgmt_send_control_multicast(const game_control_payload_t *payload)
{
otIp6Address multicast_addr;
otError ot_err = otIp6AddressFromString("ff03::1", &multicast_addr);
if (ot_err != OT_ERROR_NONE)
{
LOG_ERR("Error parsing multicast address: %d", ot_err);
return ot_error_to_errno(ot_err);
}
int err = game_mgmt_send_control_to(&multicast_addr, payload);
if (err)
{
LOG_ERR("Error sending multicast control message: %d", err);
}
return err;
}
int game_mgmt_send_control_unicast(const char *peer_addr_str, const game_control_payload_t *payload)
{
if (!peer_addr_str)
{
LOG_ERR("Peer address string is NULL");
return -EINVAL;
}
otIp6Address peer_addr;
otError ot_err = otIp6AddressFromString(peer_addr_str, &peer_addr);
if (ot_err != OT_ERROR_NONE)
{
LOG_ERR("Error parsing peer address '%s': %d", peer_addr_str, ot_err);
return ot_error_to_errno(ot_err);
}
int err = game_mgmt_send_control_to(&peer_addr, payload);
if (err)
{
LOG_ERR("Error sending unicast control message to '%s': %d", peer_addr_str, err);
}
return err;
}
static sys_state_t g_current_state = SYS_STATE_IDLE;
static uint64_t g_current_game_id = 0;
static otIp6Address g_leader_unicast_addr;
int game_mgmt_init(void)
{
int err = game_mgmt_init_coap();
int err = game_mgmt_coap_init();
if (err)
{
LOG_ERR("Failed to initialize CoAP service: %d", err);
return err;
}
LOG_DBG("Game Management initialized (State: IDLE)");
#if IS_ENABLED(CONFIG_LASERTAG_ROLE_LEADER)
static const char* device_type = "Leader";
game_mgmt_device_list_init();
#elif IS_ENABLED(CONFIG_LASERTAG_ROLE_WEAPON)
static const char* device_type = "Weapon";
#elif IS_ENABLED(CONFIG_LASERTAG_ROLE_VEST)
static const char* device_type = "Vest";
#else
static const char* device_type = "Unknown";
#endif
LOG_INF("Game management initialized. Device type: " FORMAT_BRIGHT_GREEN_BOLD("%s"), device_type);
return 0;
}
void game_mgmt_set_leader_unicast_addr(const otIp6Address *addr)
{
if (addr)
{
g_leader_unicast_addr = *addr;
}
else
{
memset(&g_leader_unicast_addr, 0, sizeof(g_leader_unicast_addr));
}
}
bool game_mgmt_get_leader_unicast_addr(otIp6Address *addr)
{
if (addr && !otIp6IsAddressUnspecified(&g_leader_unicast_addr))
{
*addr = g_leader_unicast_addr;
return true;
}
return false;
}
void game_mgmt_set_state(sys_state_t state)
{
if (current_state == state)
if (g_current_state == state)
{
return;
}
LOG_DBG("State Change: %d -> %d", current_state, state);
current_state = state;
LOG_DBG("State change: %d -> %d", g_current_state, state);
g_current_state = state;
}
sys_state_t game_mgmt_get_state(void)
{
return current_state;
return g_current_state;
}
void game_mgmt_set_game_id(uint64_t id)
{
if (current_game_id == id)
if (g_current_game_id == id)
{
return;
}
current_game_id = id;
g_current_game_id = id;
LOG_DBG("Game ID updated: 0x%llx", id);
}
uint64_t game_mgmt_get_game_id(void)
{
return current_game_id;
}
static void handle_game_control(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo)
{
ARG_UNUSED(aContext);
ARG_UNUSED(aMessageInfo);
game_control_payload_t payload;
if (otCoapMessageGetCode(aMessage) != OT_COAP_CODE_POST)
{
return;
}
uint16_t offset = otMessageGetOffset(aMessage);
if (otMessageRead(aMessage, offset, &payload, sizeof(payload)) >= 1)
{
if (payload.command == GAME_CTRL_CMD_START_GAME)
{
LOG_DBG("Broadcast received: start time 0x%08X, duration %u s",
payload.data.start_game.start_time,
payload.data.start_game.duration);
uint64_t now_us = thread_mgmt_get_network_time();
uint64_t start_time_us = game_mgmt_expand_t32_us(payload.data.start_game.start_time, now_us);
schedule_game_start(start_time_us);
}
}
}
static otCoapResource game_ctrl_res = {
.mUriPath = "g",
.mHandler = handle_game_control,
.mContext = NULL,
.mNext = NULL,
};
int game_mgmt_init_coap(void)
{
otInstance *instance = game_mgmt_get_instance();
if (!instance)
{
LOG_ERR("OpenThread instance unavailable");
return -ENODEV;
}
otError err = otCoapStart(instance, OT_DEFAULT_COAP_PORT);
if ((err != OT_ERROR_NONE) && (err != OT_ERROR_ALREADY))
{
LOG_ERR("Failed to start CoAP service: %d", err);
return ot_error_to_errno(err);
}
otCoapAddResource(instance, &game_ctrl_res);
LOG_DBG("Registered CoAP resource '/g'");
return 0;
}
void game_start_handler(struct k_work *work)
{
ARG_UNUSED(work);
LOG_INF(FORMAT_BLUE_BOLD("Game start handler triggered"));
return g_current_game_id;
}
#if IS_ENABLED(CONFIG_GAME_MGMT_SHELL)
@@ -328,7 +97,26 @@ void game_start_handler(struct k_work *work)
static int cmd_game_start(const struct shell *sh, size_t argc, char **argv)
{
uint32_t delay_s = (argc > 1) ? strtoul(argv[1], NULL, 10) : 10;
char *endptr = NULL;
uint32_t delay_s = (uint32_t)strtoul(argv[1], &endptr, 10);
if ((endptr == argv[1]) || (*endptr != '\0'))
{
shell_error(sh, "Invalid delay_s: %s", argv[1]);
return -EINVAL;
}
uint32_t duration_s = 600;
if (argc > 2)
{
endptr = NULL;
duration_s = (uint32_t)strtoul(argv[2], &endptr, 10);
if ((endptr == argv[2]) || (*endptr != '\0'))
{
shell_error(sh, "Invalid duration_s: %s", argv[2]);
return -EINVAL;
}
}
game_control_payload_t payload = {.command = GAME_CTRL_CMD_START_GAME};
uint64_t now = thread_mgmt_get_network_time();
@@ -339,7 +127,7 @@ static int cmd_game_start(const struct shell *sh, size_t argc, char **argv)
}
payload.data.start_game.start_time = (uint32_t)(now + (delay_s * 1000000ULL));
payload.data.start_game.duration = 600;
payload.data.start_game.duration = duration_s;
int err = game_mgmt_send_control_multicast(&payload);
if (err)
@@ -348,14 +136,36 @@ static int cmd_game_start(const struct shell *sh, size_t argc, char **argv)
return err;
}
shell_print(sh, "Start broadcast sent for T+%u s.", delay_s);
schedule_game_start(game_mgmt_expand_t32_us(payload.data.start_game.start_time, now));
shell_print(sh, "Start broadcast sent for T+%u s (duration: %u s).", delay_s, duration_s);
game_mgmt_schedule_start(game_mgmt_expand_t32_us(payload.data.start_game.start_time, now), duration_s);
return 0;
}
static int cmd_game_abort(const struct shell *sh, size_t argc, char **argv)
{
ARG_UNUSED(argc);
ARG_UNUSED(argv);
game_control_payload_t payload = {
.command = GAME_CTRL_CMD_ABORT_START,
};
int err = game_mgmt_send_control_multicast(&payload);
if (err)
{
shell_error(sh, "Failed to send abort broadcast: %d", err);
return err;
}
game_mgmt_cancel_scheduled_start("manual shell abort");
shell_print(sh, "Abort broadcast sent.");
return 0;
}
SHELL_STATIC_SUBCMD_SET_CREATE(game_sub,
SHELL_CMD_ARG(start, NULL, "<delay_s>", cmd_game_start, 2, 0),
SHELL_SUBCMD_SET_END);
SHELL_CMD_ARG(start, NULL, "<delay_s> [duration_s]", cmd_game_start, 2, 1),
SHELL_CMD_ARG(abort, NULL, "", cmd_game_abort, 1, 0),
SHELL_SUBCMD_SET_END);
SHELL_CMD_REGISTER(game, &game_sub, "Game Management", NULL);
#endif

View File

@@ -3,12 +3,16 @@
#include <stdint.h>
// ANSI Escape Codes für die Formatierung
#define ANSI_BOLD "\x1b[1m"
#define ANSI_RESET "\x1b[0m"
// Das BOLD-Makro
#define BOLD(s) ANSI_BOLD s ANSI_RESET
/**
* @brief Enumeration for Lasertag device types.
* This is used to identify the role of each device in the game (leader, weapon, vest, or none). It can be extended in the future if needed.
*/
typedef enum {
DEVICE_TYPE_NONE = 0x00,
DEVICE_TYPE_LEADER = 0x10,
DEVICE_TYPE_WEAPON = 0x20,
DEVICE_TYPE_VEST = 0x30,
} lasertag_device_type_t;
/**
* @file lasertag_utils.h
@@ -34,6 +38,25 @@ const char* lasertag_get_device_name(void);
*/
int lasertag_set_device_name(const char *name, size_t len);
/**
* @brief Get the device type (leader, weapon, vest).
* @return Integer representing the device type.
*/
int lasertag_get_device_type(void);
/**
* @brief Get the player ID.
* @return Integer representing the player ID.
*/
int lasertag_get_player_id(void);
/**
* @brief Get the team ID.
* @return Integer representing the team ID.
*/
int lasertag_get_team_id(void);
/**
* @brief Initialize the watchdog timer for lasertag utilities.
* @return 0 on success, negative error code otherwise.
@@ -55,7 +78,7 @@ void lasertag_feed_watchdog(void);
uint8_t lastertag_crc8 (const uint8_t *data, size_t len);
/**
* ANSI Defines for bold text formatting in logs.
* ANSI Defines for text formatting in logs.
*/
#define ANSI_RESET "\x1b[0m"
@@ -63,6 +86,7 @@ uint8_t lastertag_crc8 (const uint8_t *data, size_t len);
#define ANSI_BRIGHT "\x1b[97m"
#define ANSI_GREEN "\x1b[32m"
#define ANSI_BRIGHT_GREEN "\x1b[92m"
#define ANSI_YELLOW "\x1b[33m"
#define ANSI_RED "\x1b[31m"
#define ANSI_BLUE "\x1b[34m"
@@ -74,6 +98,8 @@ uint8_t lastertag_crc8 (const uint8_t *data, size_t len);
#define FORMAT_GREEN(s) ANSI_GREEN s ANSI_RESET
#define FORMAT_GREEN_BOLD(s) ANSI_GREEN ANSI_BOLD s ANSI_RESET
#define FORMAT_BRIGHT_GREEN(s) ANSI_BRIGHT_GREEN s ANSI_RESET
#define FORMAT_BRIGHT_GREEN_BOLD(s) ANSI_BRIGHT_GREEN ANSI_BOLD s ANSI_RESET
#define FORMAT_YELLOW(s) ANSI_YELLOW s ANSI_RESET
#define FORMAT_YELLOW_BOLD(s) ANSI_YELLOW ANSI_BOLD s ANSI_RESET
#define FORMAT_RED(s) ANSI_RED s ANSI_RESET

View File

@@ -54,6 +54,20 @@ int lasertag_set_device_name(const char *name, size_t len)
return settings_save_one("lasertag/name", device_name, len);
}
int lasertag_get_device_type(void)
{
#if IS_ENABLED(CONFIG_LASERTAG_ROLE_VEST)
return DEVICE_TYPE_VEST;
#elif IS_ENABLED(CONFIG_LASERTAG_ROLE_WEAPON)
return DEVICE_TYPE_WEAPON;
#elif IS_ENABLED(CONFIG_LASERTAG_ROLE_LEADER)
return DEVICE_TYPE_LEADER;
#else
return DEVICE_TYPE_NONE;
#endif
return 0;
}
/* --- Watchdog --- */
#ifdef CONFIG_WATCHDOG
#include <zephyr/drivers/watchdog.h>

View File

@@ -145,13 +145,16 @@ class AudioBuilder:
parts = []
for i, txt in enumerate(numbers):
# Jede Zahl als temporäres Asset durch die Standard-Pipeline jagen
# Wir nutzen eine interne ID, um Kollisionen im Preview-Ordner zu vermeiden
part_id = f"cnt_tmp_{i}"
part_file = self.process_asset(part_id, txt, voice, filters)
temp_asset = {
'id': part_id,
'text': txt
}
part_file = self.process_asset(temp_asset, voice, filters)
parts.append(part_file)
# Binäres Zusammenfügen (Da s16le keinen Header hat)
with open(final_cache_file, 'wb') as outfile:
for p_file in parts:
with open(p_file, 'rb') as infile:
@@ -166,6 +169,19 @@ class AudioBuilder:
out_dir = self.config['paths']['output']
out_dir.mkdir(parents=True, exist_ok=True)
countdown_file = Path("countdown.yaml")
if countdown_file.exists():
with open(countdown_file, "r") as f:
cnt_config = yaml.safe_load(f)
# Prüfen, ob der Countdown für das aktuelle Target gebaut werden soll
if target_name in cnt_config.get('targets', []):
# Aufruf der generierenden Methode
countdown_cache = self.generate_countdown(cnt_config)
# Kopieren ins lfs_source Verzeichnis unter dem Namen der ID ("countdown")
dest_file = out_dir / cnt_config['id']
shutil.copy(countdown_cache, dest_file)
for cfg_file in Path(".").glob("*.yaml"):
if cfg_file.name in ["global.yaml", "countdown.yaml"]:
continue
@@ -195,4 +211,4 @@ class AudioBuilder:
if __name__ == "__main__":
import sys
target = sys.argv[1] if len(sys.argv) > 1 else "vest"
AudioBuilder().build_target(target)
AudioBuilder().build_target(target)

View File

@@ -1,4 +1,4 @@
- id: "game_start"
- id: "siren"
type: "sample" # Neu: Unterscheidung zwischen TTS und Datei
source: "horn.ogg" # Die Datei in deinem samples/ Ordner
targets: ["vest", "base"]

View File

@@ -1,2 +1,7 @@
#!/usr/bin/env bash
nrfjprog -s 1050225991 --program lfs_external_flash.hex --qspisectorerase --verify --fast --reset --clockspeed 12000
JLINKS=$(nrfjprog -i)
for s in $JLINKS
do
echo "Programming $s"
nrfjprog -s $s --program lfs_external_flash.hex --qspisectorerase --verify --fast --reset --clockspeed 12000
done