Sync while working on OT
All checks were successful
Deploy Docs / build-and-deploy (push) Successful in 12s
All checks were successful
Deploy Docs / build-and-deploy (push) Successful in 12s
This commit is contained in:
44
firmware/apps/_samples/thread/nrf52840dk_nrf52840.overlay
Normal file
44
firmware/apps/_samples/thread/nrf52840dk_nrf52840.overlay
Normal 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";
|
||||
};
|
||||
@@ -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
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
/ {
|
||||
chosen {
|
||||
nordic,pm-ext-flash = &mx25r64;
|
||||
};
|
||||
};
|
||||
@@ -1,4 +0,0 @@
|
||||
littlefs_storage:
|
||||
address: 0x0
|
||||
size: 0x800000
|
||||
region: external_flash
|
||||
1
firmware/apps/vest/pm_static.yml
Symbolic link
1
firmware/apps/vest/pm_static.yml
Symbolic link
@@ -0,0 +1 @@
|
||||
../leader/pm_static.yml
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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"
|
||||
|
||||
@@ -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()
|
||||
@@ -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.
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -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()
|
||||
@@ -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
|
||||
|
||||
@@ -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 */
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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"]
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user