Added game mgmt, thread restart
All checks were successful
Deploy Docs / build-and-deploy (push) Successful in 24s
All checks were successful
Deploy Docs / build-and-deploy (push) Successful in 24s
This commit is contained in:
@@ -24,6 +24,7 @@ CONFIG_NET_L2_OPENTHREAD=y
|
||||
CONFIG_OPENTHREAD=y
|
||||
CONFIG_OPENTHREAD_FTD=y
|
||||
CONFIG_OPENTHREAD_SHELL=y
|
||||
CONFIG_OPENTHREAD_DEFAULT_TX_POWER=8
|
||||
|
||||
# --- CoAP & UDP Features ---
|
||||
CONFIG_OPENTHREAD_COAP=y
|
||||
@@ -42,4 +43,5 @@ CONFIG_BT_ATT_PREPARE_COUNT=5
|
||||
# Enable Lasertag Shared Modules
|
||||
CONFIG_LASERTAG_UTILS=y
|
||||
CONFIG_THREAD_MGMT=y
|
||||
CONFIG_BLE_MGMT=y
|
||||
CONFIG_BLE_MGMT=y
|
||||
CONFIG_GAME_MGMT=y
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <lasertag_utils.h>
|
||||
#include <thread_mgmt.h>
|
||||
#include <ble_mgmt.h>
|
||||
#include <game_mgmt.h>
|
||||
|
||||
LOG_MODULE_REGISTER(leader_app, CONFIG_LOG_DEFAULT_LEVEL);
|
||||
|
||||
@@ -12,34 +13,39 @@ int main(void)
|
||||
lasertag_utils_init();
|
||||
|
||||
/* Initialize and start BLE management for provisioning */
|
||||
int rc = ble_mgmt_init(LT_TYPE_LEADER);
|
||||
if (rc) {
|
||||
LOG_ERR("BLE initialization failed (err %d)", rc);
|
||||
return rc;
|
||||
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.");
|
||||
}
|
||||
|
||||
/* 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.");
|
||||
}
|
||||
|
||||
/* Start BLE advertising */
|
||||
rc = ble_mgmt_adv_start();
|
||||
if (rc) {
|
||||
LOG_ERR("BLE advertising start failed (err %d)", rc);
|
||||
err = ble_mgmt_adv_start();
|
||||
if (err) {
|
||||
LOG_ERR("BLE advertising start failed (err %d)", err);
|
||||
return err;
|
||||
} else {
|
||||
LOG_INF("BLE advertising started.");
|
||||
}
|
||||
|
||||
/* Initialize and start OpenThread stack */
|
||||
rc = thread_mgmt_init();
|
||||
if (rc) {
|
||||
LOG_ERR("Thread initialization failed (err %d)", rc);
|
||||
/* Initialize game management module */
|
||||
err = game_mgmt_init();
|
||||
if (err) {
|
||||
LOG_ERR("Game Management initialization failed (err %d)", err);
|
||||
return err;
|
||||
} else {
|
||||
LOG_INF("Leader Application successfully started with Thread Mesh.");
|
||||
return rc;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
/* Main loop - handle high-level game logic here */
|
||||
k_sleep(K_MSEC(1000));
|
||||
LOG_INF("Game Management initialized successfully.");
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -3,4 +3,5 @@
|
||||
add_subdirectory(ble_mgmt)
|
||||
add_subdirectory(thread_mgmt)
|
||||
add_subdirectory(lasertag_utils)
|
||||
add_subdirectory(ir)
|
||||
add_subdirectory(ir)
|
||||
add_subdirectory(game_mgmt)
|
||||
@@ -2,4 +2,5 @@
|
||||
rsource "lasertag_utils/Kconfig"
|
||||
rsource "thread_mgmt/Kconfig"
|
||||
rsource "ble_mgmt/Kconfig"
|
||||
rsource "ir/Kconfig"
|
||||
rsource "ir/Kconfig"
|
||||
rsource "game_mgmt/Kconfig"
|
||||
@@ -8,4 +8,10 @@ if BLE_MGMT
|
||||
config BLE_MGMT_LOG_LEVEL
|
||||
int "BLE Management Log Level"
|
||||
default 3
|
||||
|
||||
config BLE_MGMT_CAN_BE_GAME_LEADER
|
||||
bool "Can be game leader"
|
||||
default n
|
||||
help
|
||||
Allow this device to take the game leader role in the lasertag game.
|
||||
endif
|
||||
@@ -35,24 +35,4 @@ int ble_mgmt_adv_start(void);
|
||||
*/
|
||||
int ble_mgmt_adv_stop(void);
|
||||
|
||||
/**
|
||||
* @brief Get the current system state
|
||||
*/
|
||||
uint8_t lasertag_get_system_state(void);
|
||||
|
||||
/**
|
||||
* @brief Set the current system state.
|
||||
*/
|
||||
void lasertag_set_system_state(uint8_t state);
|
||||
|
||||
/**
|
||||
* @brief Get the current 64-bit Game ID
|
||||
*/
|
||||
uint64_t lasertag_get_game_id(void);
|
||||
|
||||
/**
|
||||
* @brief Set the current 64-bit Game ID.
|
||||
*/
|
||||
void lasertag_set_game_id(uint64_t id);
|
||||
|
||||
#endif /* BLE_MGMT_H */
|
||||
@@ -20,6 +20,7 @@
|
||||
#include <lasertag_utils.h>
|
||||
#include <thread_mgmt.h>
|
||||
#include <ble_mgmt.h>
|
||||
#include <game_mgmt.h>
|
||||
#include <string.h>
|
||||
|
||||
LOG_MODULE_REGISTER(ble_mgmt, CONFIG_BLE_MGMT_LOG_LEVEL);
|
||||
@@ -40,24 +41,8 @@ LOG_MODULE_REGISTER(ble_mgmt, CONFIG_BLE_MGMT_LOG_LEVEL);
|
||||
|
||||
/* Provisioning characteristics */
|
||||
#define BT_UUID_LT_PROV_NAME_CHAR BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x03afe2cf, 0x6c64, 0x4a22, 0x9289, 0xc3ae820c1001))
|
||||
/* #define BT_UUID_LT_PROV_PANID_CHAR BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x03afe2cf, 0x6c64, 0x4a22, 0x9289, 0xc3ae820c1002)) */
|
||||
/* #define BT_UUID_LT_PROV_CHAN_CHAR BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x03afe2cf, 0x6c64, 0x4a22, 0x9289, 0xc3ae820c1003)) */
|
||||
/* #define BT_UUID_LT_PROV_EXTPAN_CHAR BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x03afe2cf, 0x6c64, 0x4a22, 0x9289, 0xc3ae820c1004)) */
|
||||
/* #define BT_UUID_LT_PROV_NETKEY_CHAR BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x03afe2cf, 0x6c64, 0x4a22, 0x9289, 0xc3ae820c1005)) */
|
||||
/* #define BT_UUID_LT_PROV_NETNAME_CHAR BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x03afe2cf, 0x6c64, 0x4a22, 0x9289, 0xc3ae820c1006)) */
|
||||
#define BT_UUID_LT_PROV_NODES_CHAR BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x03afe2cf, 0x6c64, 0x4a22, 0x9289, 0xc3ae820c1007))
|
||||
#define BT_UUID_LT_PROV_TYPE_CHAR BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x03afe2cf, 0x6c64, 0x4a22, 0x9289, 0xc3ae820c1008))
|
||||
|
||||
/* Full configuration pack characteristic */
|
||||
#define BT_UUID_LT_PROV_CONFIG_CHAR BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x03afe2cf, 0x6c64, 0x4a22, 0x9289, 0xc3ae820c100c))
|
||||
|
||||
/* GAME SERVICE (0x20xx): Game-related commands and logging */
|
||||
#define BT_UUID_LT_GAME_SERVICE BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x03afe2cf, 0x6c64, 0x4a22, 0x9289, 0xc3ae820c2000))
|
||||
|
||||
/* Game service characteristics */
|
||||
#define BT_UUID_LT_GAME_CONFIG_CHAR BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x03afe2cf, 0x6c64, 0x4a22, 0x9289, 0xc3ae820c2001))
|
||||
#define BT_UUID_LT_GAME_COMMAND_CHAR BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x03afe2cf, 0x6c64, 0x4a22, 0x9289, 0xc3ae820c2002))
|
||||
#define BT_UUID_LT_GAME_LOG_DATA_CHAR BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x03afe2cf, 0x6c64, 0x4a22, 0x9289, 0xc3ae820c2003))
|
||||
#define BT_UUID_LT_PROV_CONFIG_CHAR BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x03afe2cf, 0x6c64, 0x4a22, 0x9289, 0xc3ae820c100c))
|
||||
|
||||
|
||||
/* ============================================================================
|
||||
@@ -65,11 +50,11 @@ LOG_MODULE_REGISTER(ble_mgmt, CONFIG_BLE_MGMT_LOG_LEVEL);
|
||||
============================================================================ */
|
||||
|
||||
/**
|
||||
* Leader configuration payload.
|
||||
* Packed structure for transmitting full leader configuration via BLE.
|
||||
* Device configuration payload.
|
||||
* Packed structure for transmitting full device configuration via BLE.
|
||||
* Total size: 86 bytes
|
||||
*/
|
||||
struct leader_config_payload {
|
||||
struct device_config_payload {
|
||||
uint8_t system_state; /* Offset 0 */
|
||||
uint64_t game_id; /* Offset 1 */
|
||||
uint16_t pan_id; /* Offset 9 */
|
||||
@@ -87,8 +72,6 @@ struct leader_config_payload {
|
||||
static uint8_t device_role = 0; /* Store device type for provisioning */
|
||||
static uint8_t adv_enabled = 0; /* Track advertising state */
|
||||
static struct k_work_delayable adv_restart_work; /* Delayed advertising restart */
|
||||
static uint8_t system_state = 0; /* Game system state */
|
||||
static uint64_t game_id = 0; /* Current game identifier */
|
||||
|
||||
/* ============================================================================
|
||||
Advertising Data
|
||||
@@ -134,38 +117,6 @@ static ssize_t read_lasertag_val(struct bt_conn *conn, const struct bt_gatt_attr
|
||||
val_ptr = lasertag_get_device_name();
|
||||
val_len = strlen(val_ptr);
|
||||
}
|
||||
/* Disabled characteristics 1002-1006 */
|
||||
/*
|
||||
else if (bt_uuid_cmp(attr->uuid, BT_UUID_LT_PROV_PANID_CHAR) == 0)
|
||||
{
|
||||
static uint16_t pan_id;
|
||||
pan_id = lasertag_get_thread_pan_id();
|
||||
val_ptr = (char *)&pan_id;
|
||||
val_len = sizeof(pan_id);
|
||||
}
|
||||
else if (bt_uuid_cmp(attr->uuid, BT_UUID_LT_PROV_CHAN_CHAR) == 0)
|
||||
{
|
||||
static uint8_t chan;
|
||||
chan = lasertag_get_thread_channel();
|
||||
val_ptr = (char *)&chan;
|
||||
val_len = sizeof(chan);
|
||||
}
|
||||
else if (bt_uuid_cmp(attr->uuid, BT_UUID_LT_PROV_EXTPAN_CHAR) == 0)
|
||||
{
|
||||
val_ptr = (char *)lasertag_get_thread_ext_pan_id();
|
||||
val_len = 8;
|
||||
}
|
||||
else if (bt_uuid_cmp(attr->uuid, BT_UUID_LT_PROV_NETKEY_CHAR) == 0)
|
||||
{
|
||||
val_ptr = (char *)lasertag_get_thread_network_key();
|
||||
val_len = 16;
|
||||
}
|
||||
else if (bt_uuid_cmp(attr->uuid, BT_UUID_LT_PROV_NETNAME_CHAR) == 0)
|
||||
{
|
||||
val_ptr = lasertag_get_thread_network_name();
|
||||
val_len = strlen(val_ptr);
|
||||
}
|
||||
*/
|
||||
|
||||
return bt_gatt_attr_read(conn, attr, buf, len, offset, val_ptr, val_len);
|
||||
}
|
||||
@@ -186,65 +137,12 @@ static ssize_t write_lasertag_val(struct bt_conn *conn, const struct bt_gatt_att
|
||||
bt_set_name(lasertag_get_device_name());
|
||||
}
|
||||
}
|
||||
/* Disabled characteristics 1002-1006 */
|
||||
/*
|
||||
else if (bt_uuid_cmp(attr->uuid, BT_UUID_LT_PROV_PANID_CHAR) == 0)
|
||||
{
|
||||
if (len != 2)
|
||||
return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
|
||||
rc = lasertag_set_thread_pan_id(*(uint16_t *)buf);
|
||||
}
|
||||
else if (bt_uuid_cmp(attr->uuid, BT_UUID_LT_PROV_CHAN_CHAR) == 0)
|
||||
{
|
||||
if (len != 1)
|
||||
return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
|
||||
rc = lasertag_set_thread_channel(*(uint8_t *)buf);
|
||||
}
|
||||
else if (bt_uuid_cmp(attr->uuid, BT_UUID_LT_PROV_EXTPAN_CHAR) == 0)
|
||||
{
|
||||
if (len != 8)
|
||||
return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
|
||||
rc = lasertag_set_thread_ext_pan_id(buf);
|
||||
}
|
||||
else if (bt_uuid_cmp(attr->uuid, BT_UUID_LT_PROV_NETKEY_CHAR) == 0)
|
||||
{
|
||||
if (len != 16)
|
||||
return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
|
||||
rc = lasertag_set_thread_network_key(buf);
|
||||
}
|
||||
else if (bt_uuid_cmp(attr->uuid, BT_UUID_LT_PROV_NETNAME_CHAR) == 0)
|
||||
{
|
||||
rc = lasertag_set_thread_network_name(buf, len);
|
||||
}
|
||||
*/
|
||||
|
||||
if (rc)
|
||||
return BT_GATT_ERR(BT_ATT_ERR_UNLIKELY);
|
||||
return len;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read handler for discovered nodes characteristic.
|
||||
* Returns the list of discovered Thread mesh nodes as a string.
|
||||
*/
|
||||
static ssize_t read_discovered_nodes(struct bt_conn *conn, const struct bt_gatt_attr *attr,
|
||||
void *buf, uint16_t len, uint16_t offset)
|
||||
{
|
||||
const char *list = thread_mgmt_get_discovered_list();
|
||||
return bt_gatt_attr_read(conn, attr, buf, len, offset, list, strlen(list));
|
||||
}
|
||||
|
||||
/**
|
||||
* Write handler for node discovery trigger characteristic.
|
||||
* Any write to this characteristic starts the Thread mesh node discovery process.
|
||||
*/
|
||||
static ssize_t write_discover_cmd(struct bt_conn *conn, const struct bt_gatt_attr *attr,
|
||||
const void *buf, uint16_t len, uint16_t offset, uint8_t flags)
|
||||
{
|
||||
/* If anything is written, trigger discovery in Thread Mesh */
|
||||
thread_mgmt_discover_nodes();
|
||||
return len;
|
||||
}
|
||||
/**
|
||||
* Read handler for the full leader configuration.
|
||||
* Returns a packed structure containing all device configuration in a single read.
|
||||
@@ -252,11 +150,11 @@ static ssize_t write_discover_cmd(struct bt_conn *conn, const struct bt_gatt_att
|
||||
static ssize_t read_leader_config(struct bt_conn *conn, const struct bt_gatt_attr *attr,
|
||||
void *buf, uint16_t len, uint16_t offset)
|
||||
{
|
||||
struct leader_config_payload payload;
|
||||
struct device_config_payload payload;
|
||||
memset(&payload, 0, sizeof(payload));
|
||||
|
||||
payload.system_state = lasertag_get_system_state();
|
||||
payload.game_id = lasertag_get_game_id();
|
||||
payload.system_state = game_mgmt_get_state();
|
||||
payload.game_id = game_mgmt_get_game_id();
|
||||
payload.pan_id = lasertag_get_thread_pan_id();
|
||||
payload.channel = lasertag_get_thread_channel();
|
||||
|
||||
@@ -275,31 +173,49 @@ static ssize_t read_leader_config(struct bt_conn *conn, const struct bt_gatt_att
|
||||
* Accepts a packed structure and applies all configuration at once.
|
||||
*/
|
||||
static ssize_t write_leader_config(struct bt_conn *conn, const struct bt_gatt_attr *attr,
|
||||
const void *buf, uint16_t len, uint16_t offset, uint8_t flags)
|
||||
const void *buf, uint16_t len, uint16_t offset, uint8_t flags)
|
||||
{
|
||||
if (len != sizeof(struct leader_config_payload)) {
|
||||
return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
|
||||
}
|
||||
if (len != sizeof(struct device_config_payload))
|
||||
{
|
||||
return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
|
||||
}
|
||||
|
||||
const struct leader_config_payload *payload = (const struct leader_config_payload *)buf;
|
||||
const struct device_config_payload *payload = (const struct device_config_payload *)buf;
|
||||
|
||||
/* Apply RAM values */
|
||||
lasertag_set_system_state(payload->system_state);
|
||||
lasertag_set_game_id(payload->game_id);
|
||||
/* Apply RAM values */
|
||||
if (payload->system_state != SYS_STATE_NO_CHANGE)
|
||||
{
|
||||
game_mgmt_set_state(payload->system_state);
|
||||
}
|
||||
if (payload->game_id != 0)
|
||||
{
|
||||
game_mgmt_set_game_id(payload->game_id);
|
||||
}
|
||||
|
||||
/* Apply NVS values via utils */
|
||||
lasertag_set_thread_pan_id(payload->pan_id);
|
||||
lasertag_set_thread_channel(payload->channel);
|
||||
lasertag_set_thread_ext_pan_id(payload->ext_pan_id);
|
||||
lasertag_set_thread_network_key(payload->network_key);
|
||||
lasertag_set_thread_network_name(payload->network_name, strlen(payload->network_name));
|
||||
|
||||
lasertag_set_device_name((const void *)payload->node_name, strlen(payload->node_name));
|
||||
bt_set_name(lasertag_get_device_name());
|
||||
/* Apply NVS values via utils */
|
||||
if (payload->node_name[0] != '\0') // Only update if a name is provided
|
||||
{
|
||||
lasertag_set_device_name((const void *)payload->node_name, strlen(payload->node_name));
|
||||
bt_set_name(lasertag_get_device_name());
|
||||
}
|
||||
|
||||
LOG_INF("Leader config updated via BLE (Packed Struct)");
|
||||
return len;
|
||||
if (payload->channel != 0)
|
||||
{
|
||||
lasertag_set_thread_pan_id(payload->pan_id);
|
||||
lasertag_set_thread_channel(payload->channel);
|
||||
lasertag_set_thread_ext_pan_id(payload->ext_pan_id);
|
||||
lasertag_set_thread_network_key(payload->network_key);
|
||||
if (payload->network_name[0] != '\0') // Only update if a name is provided
|
||||
{
|
||||
lasertag_set_thread_network_name(payload->network_name, strlen(payload->network_name));
|
||||
}
|
||||
thread_mgmt_restart_thread_stack(false);
|
||||
}
|
||||
|
||||
LOG_INF("Device config processed. Only changed values were applied.");
|
||||
return len;
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
GATT Service Definition
|
||||
|
||||
@@ -360,12 +276,6 @@ BT_GATT_SERVICE_DEFINE(provisioning_svc,
|
||||
read_lasertag_val, write_lasertag_val, NULL),
|
||||
*/
|
||||
|
||||
/* Node discovery list and trigger (read/write) */
|
||||
BT_GATT_CHARACTERISTIC(BT_UUID_LT_PROV_NODES_CHAR,
|
||||
BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE,
|
||||
BT_GATT_PERM_READ | BT_GATT_PERM_WRITE,
|
||||
read_discovered_nodes, write_discover_cmd, NULL),
|
||||
|
||||
/* Full leader configuration (packed struct) */
|
||||
BT_GATT_CHARACTERISTIC(BT_UUID_LT_PROV_CONFIG_CHAR,
|
||||
BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE,
|
||||
@@ -467,34 +377,6 @@ int ble_mgmt_adv_stop(void)
|
||||
return err;
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
Game State Management Functions
|
||||
============================================================================ */
|
||||
|
||||
/**
|
||||
* Get the current system state.
|
||||
* @return Current system state value
|
||||
*/
|
||||
uint8_t lasertag_get_system_state(void) { return system_state; }
|
||||
|
||||
/**
|
||||
* Set the system state.
|
||||
* @param state The new system state
|
||||
*/
|
||||
void lasertag_set_system_state(uint8_t state) { system_state = state; }
|
||||
|
||||
/**
|
||||
* Get the current game ID.
|
||||
* @return Current game identifier
|
||||
*/
|
||||
uint64_t lasertag_get_game_id(void) { return game_id; }
|
||||
|
||||
/**
|
||||
* Set the game ID.
|
||||
* @param id The new game identifier
|
||||
*/
|
||||
void lasertag_set_game_id(uint64_t id) { game_id = id; }
|
||||
|
||||
/* ============================================================================
|
||||
BLE Connection Event Handlers
|
||||
============================================================================ */
|
||||
|
||||
5
firmware/libs/game_mgmt/CMakeLists.txt
Normal file
5
firmware/libs/game_mgmt/CMakeLists.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
if(CONFIG_BLE_MGMT)
|
||||
zephyr_library()
|
||||
zephyr_sources(src/game_mgmt.c)
|
||||
zephyr_include_directories(include)
|
||||
endif()
|
||||
11
firmware/libs/game_mgmt/Kconfig
Normal file
11
firmware/libs/game_mgmt/Kconfig
Normal file
@@ -0,0 +1,11 @@
|
||||
menuconfig GAME_MGMT
|
||||
bool "Game Management"
|
||||
depends on BT
|
||||
help
|
||||
Library for managing game states and logic in the lasertag device.
|
||||
|
||||
if GAME_MGMT
|
||||
config GAME_MGMT_LOG_LEVEL
|
||||
int "Game Management Log Level"
|
||||
default 3
|
||||
endif
|
||||
64
firmware/libs/game_mgmt/include/game_mgmt.h
Normal file
64
firmware/libs/game_mgmt/include/game_mgmt.h
Normal file
@@ -0,0 +1,64 @@
|
||||
#ifndef GAME_MGMT_H
|
||||
#define GAME_MGMT_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
/**
|
||||
* @brief System states for the Lasertag devices.
|
||||
* These states define the behavior of the device in the network.
|
||||
*/
|
||||
typedef enum {
|
||||
SYS_STATE_NO_CHANGE = 0x00, /* Placeholder for no state change */
|
||||
SYS_STATE_IDLE = 0x01, /* Device is on but inactive */
|
||||
SYS_STATE_LOBBY = 0x02, /* Discovery phase: Leader sends beacon, Nodes reply */
|
||||
SYS_STATE_STARTING = 0x03, /* Countdown phase before match start */
|
||||
SYS_STATE_RUNNING = 0x04, /* Match is active: IR and local logic enabled */
|
||||
SYS_STATE_POST_GAME = 0x05 /* Match ended: Data collection and review */
|
||||
} sys_state_t;
|
||||
|
||||
/**
|
||||
* @brief Callback for state changes.
|
||||
* Allows apps/modules to react when the system transitions (e.g., UI updates).
|
||||
*/
|
||||
typedef void (*game_mgmt_state_cb_t)(sys_state_t new_state);
|
||||
|
||||
/**
|
||||
* @brief Initialize the game management module.
|
||||
* Sets the initial state and prepares timers/workqueues.
|
||||
* @return 0 on success.
|
||||
*/
|
||||
int game_mgmt_init(void);
|
||||
|
||||
/**
|
||||
* @brief Set the system state and trigger corresponding actions.
|
||||
* Leader: Starts/stops beacons. Nodes: Starts/stops heartbeats.
|
||||
* @param state The target state to transition to.
|
||||
*/
|
||||
void game_mgmt_set_state(sys_state_t state);
|
||||
|
||||
/**
|
||||
* @brief Returns the current system state.
|
||||
* @return Current sys_state_t value.
|
||||
*/
|
||||
sys_state_t game_mgmt_get_state(void);
|
||||
|
||||
/**
|
||||
* @brief Set the current game ID.
|
||||
* @param id The game ID to set.
|
||||
*/
|
||||
void game_mgmt_set_game_id(uint64_t id);
|
||||
|
||||
/**
|
||||
* @brief Get the current game ID.
|
||||
* @return The current game ID.
|
||||
*/
|
||||
uint64_t game_mgmt_get_game_id(void);
|
||||
|
||||
/**
|
||||
* @brief Registers a callback for state changes.
|
||||
* @param cb Function to be called on transition.
|
||||
*/
|
||||
void game_mgmt_register_state_cb(game_mgmt_state_cb_t cb);
|
||||
|
||||
#endif /* GAME_MGMT_H */
|
||||
31
firmware/libs/game_mgmt/src/game_mgmt.c
Normal file
31
firmware/libs/game_mgmt/src/game_mgmt.c
Normal file
@@ -0,0 +1,31 @@
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
#include <game_mgmt.h>
|
||||
|
||||
LOG_MODULE_REGISTER(game_mgmt, CONFIG_GAME_MGMT_LOG_LEVEL);
|
||||
|
||||
static sys_state_t current_state = SYS_STATE_IDLE;
|
||||
static uint64_t current_game_id = 0;
|
||||
|
||||
int game_mgmt_init(void)
|
||||
{
|
||||
LOG_INF("Game Management initialized (State: IDLE)");
|
||||
return 0;
|
||||
}
|
||||
|
||||
void game_mgmt_set_state(sys_state_t state)
|
||||
{
|
||||
if (current_state == state) return;
|
||||
LOG_INF("State Change: %d -> %d", current_state, state);
|
||||
current_state = state;
|
||||
/* Future: Trigger events based on state (e.g. start/stop beacon) */
|
||||
}
|
||||
|
||||
sys_state_t game_mgmt_get_state(void) { return current_state; }
|
||||
|
||||
void game_mgmt_set_game_id(uint64_t id) {
|
||||
current_game_id = id;
|
||||
LOG_INF("Game ID updated: %llu", id);
|
||||
}
|
||||
|
||||
uint64_t game_mgmt_get_game_id(void) { return current_game_id; }
|
||||
@@ -159,18 +159,6 @@ static int cmd_name_set(const struct shell *sh, size_t argc, char **argv)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmd_thread_ping(const struct shell *sh, size_t argc, char **argv)
|
||||
{
|
||||
char msg[64];
|
||||
snprintf(msg, sizeof(msg), "Ping von %s", device_name);
|
||||
shell_print(sh, "Sende Multicast-Ping an ff03::1...");
|
||||
int rc = thread_mgmt_send_udp("ff03::1", (uint8_t *)msg, strlen(msg));
|
||||
if (rc)
|
||||
shell_error(sh, "Ping fehlgeschlagen (%d)", rc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Subcommands definitions omitted for brevity, but they should include 'ping' */
|
||||
static int cmd_thread_set_panid(const struct shell *sh, size_t argc, char **argv)
|
||||
{
|
||||
uint16_t pan = (uint16_t)strtoul(argv[1], NULL, 0);
|
||||
@@ -188,7 +176,6 @@ static int cmd_thread_set_chan(const struct shell *sh, size_t argc, char **argv)
|
||||
SHELL_STATIC_SUBCMD_SET_CREATE(sub_thread,
|
||||
SHELL_CMD_ARG(panid, NULL, "Set PAN ID", cmd_thread_set_panid, 2, 0),
|
||||
SHELL_CMD_ARG(chan, NULL, "Set channel", cmd_thread_set_chan, 2, 0),
|
||||
SHELL_CMD(ping, NULL, "Send multicast ping", cmd_thread_ping),
|
||||
SHELL_SUBCMD_SET_END);
|
||||
|
||||
static int cmd_ble_start(const struct shell *sh, size_t argc, char **argv)
|
||||
|
||||
@@ -4,23 +4,13 @@
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* @brief Initializes the OpenThread stack, UDP and CoAP.
|
||||
* @brief Initializes the OpenThread stack.
|
||||
*/
|
||||
int thread_mgmt_init(void);
|
||||
|
||||
/**
|
||||
* @brief Sends a UDP message.
|
||||
* @brief Restarts the Thread stack.
|
||||
* @param force If true, forces a full restart even if dataset is unchanged.
|
||||
*/
|
||||
int thread_mgmt_send_udp(const char *addr_str, uint8_t *payload, uint16_t len);
|
||||
|
||||
/**
|
||||
* @brief Starts device discovery via CoAP Multicast.
|
||||
*/
|
||||
int thread_mgmt_discover_nodes(void);
|
||||
|
||||
/**
|
||||
* @brief Returns the list of discovered node names (comma-separated).
|
||||
*/
|
||||
const char* thread_mgmt_get_discovered_list(void);
|
||||
|
||||
void thread_mgmt_restart_thread_stack(bool force);
|
||||
#endif
|
||||
@@ -17,157 +17,6 @@ LOG_MODULE_REGISTER(thread_mgmt, CONFIG_THREAD_MGMT_LOG_LEVEL);
|
||||
#define UDP_PORT 1234
|
||||
#define MAX_DISCOVERED_NODES 10
|
||||
|
||||
static otUdpSocket s_udp_socket;
|
||||
static char discovered_nodes_list[256] = "";
|
||||
static uint8_t node_count = 0;
|
||||
|
||||
/* --- CoAP Server Logik --- */
|
||||
|
||||
static void coap_id_handler(void *context, otMessage *message, const otMessageInfo *message_info)
|
||||
{
|
||||
otError error = OT_ERROR_NONE;
|
||||
otMessage *response;
|
||||
otInstance *instance = (otInstance *)context;
|
||||
|
||||
if (otCoapMessageGetCode(message) != OT_COAP_CODE_GET) {
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_INF("CoAP GET /id received");
|
||||
|
||||
response = otCoapNewMessage(instance, NULL);
|
||||
if (response == NULL) return;
|
||||
|
||||
otCoapMessageInitResponse(response, message, OT_COAP_TYPE_ACKNOWLEDGMENT, OT_COAP_CODE_CONTENT);
|
||||
otCoapMessageSetPayloadMarker(response);
|
||||
|
||||
const char *name = lasertag_get_device_name();
|
||||
error = otMessageAppend(response, name, strlen(name));
|
||||
if (error != OT_ERROR_NONE) {
|
||||
otMessageFree(response);
|
||||
return;
|
||||
}
|
||||
|
||||
error = otCoapSendResponse(instance, response, message_info);
|
||||
if (error != OT_ERROR_NONE) {
|
||||
otMessageFree(response);
|
||||
}
|
||||
}
|
||||
|
||||
static otCoapResource s_id_resource = {
|
||||
.mUriPath = "id",
|
||||
.mHandler = coap_id_handler,
|
||||
.mContext = NULL,
|
||||
.mNext = NULL
|
||||
};
|
||||
|
||||
/* --- CoAP Discovery Client Logik --- */
|
||||
|
||||
static void coap_discover_res_handler(void *context, otMessage *message, const otMessageInfo *message_info, otError result)
|
||||
{
|
||||
if (result != OT_ERROR_NONE || otCoapMessageGetCode(message) != OT_COAP_CODE_CONTENT) {
|
||||
return;
|
||||
}
|
||||
|
||||
char name_buf[32];
|
||||
uint16_t length = otMessageGetLength(message) - otMessageGetOffset(message);
|
||||
if (length > sizeof(name_buf) - 1) length = sizeof(name_buf) - 1;
|
||||
|
||||
otMessageRead(message, otMessageGetOffset(message), name_buf, length);
|
||||
name_buf[length] = '\0';
|
||||
|
||||
char addr_str[OT_IP6_ADDRESS_STRING_SIZE];
|
||||
otIp6AddressToString(&message_info->mPeerAddr, addr_str, sizeof(addr_str));
|
||||
|
||||
LOG_INF("Node discovered: %s (%s)", name_buf, addr_str);
|
||||
|
||||
/* Add to list (simple CSV format for BLE) */
|
||||
if (node_count < MAX_DISCOVERED_NODES && !strstr(discovered_nodes_list, name_buf)) {
|
||||
if (node_count > 0) strcat(discovered_nodes_list, ",");
|
||||
strcat(discovered_nodes_list, name_buf);
|
||||
node_count++;
|
||||
}
|
||||
}
|
||||
|
||||
int thread_mgmt_discover_nodes(void)
|
||||
{
|
||||
otInstance *instance = openthread_get_default_instance();
|
||||
otMessage *message;
|
||||
otMessageInfo message_info;
|
||||
otError error = OT_ERROR_NONE;
|
||||
|
||||
/* Reset list */
|
||||
discovered_nodes_list[0] = '\0';
|
||||
node_count = 0;
|
||||
|
||||
message = otCoapNewMessage(instance, NULL);
|
||||
if (message == NULL) return -ENOMEM;
|
||||
|
||||
otCoapMessageInit(message, OT_COAP_TYPE_NON_CONFIRMABLE, OT_COAP_CODE_GET);
|
||||
otCoapMessageAppendUriPathOptions(message, "id");
|
||||
|
||||
memset(&message_info, 0, sizeof(message_info));
|
||||
otIp6AddressFromString("ff03::1", &message_info.mPeerAddr);
|
||||
message_info.mPeerPort = OT_DEFAULT_COAP_PORT;
|
||||
|
||||
error = otCoapSendRequest(instance, message, &message_info, coap_discover_res_handler, NULL);
|
||||
if (error != OT_ERROR_NONE) {
|
||||
otMessageFree(message);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
LOG_INF("Discovery started...");
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char* thread_mgmt_get_discovered_list(void)
|
||||
{
|
||||
return discovered_nodes_list;
|
||||
}
|
||||
|
||||
/* --- UDP & Init --- */
|
||||
|
||||
static void udp_receive_cb(void *context, otMessage *message, const otMessageInfo *message_info)
|
||||
{
|
||||
uint8_t buf[64];
|
||||
uint16_t length = otMessageGetLength(message) - otMessageGetOffset(message);
|
||||
if (length > sizeof(buf) - 1) length = sizeof(buf) - 1;
|
||||
|
||||
int read = otMessageRead(message, otMessageGetOffset(message), buf, length);
|
||||
buf[read] = '\0';
|
||||
|
||||
char addr_str[OT_IP6_ADDRESS_STRING_SIZE];
|
||||
otIp6AddressToString(&message_info->mPeerAddr, addr_str, sizeof(addr_str));
|
||||
|
||||
LOG_INF("------------------------------------------");
|
||||
LOG_INF("UDP DATA RECEIVED!");
|
||||
LOG_INF("From: [%s]", addr_str);
|
||||
LOG_INF("Payload: %s", buf);
|
||||
LOG_INF("------------------------------------------");
|
||||
}
|
||||
|
||||
int thread_mgmt_send_udp(const char *addr_str, uint8_t *payload, uint16_t len)
|
||||
{
|
||||
struct otInstance *instance = openthread_get_default_instance();
|
||||
otMessage *message;
|
||||
otMessageInfo message_info;
|
||||
|
||||
if (!instance) return -ENODEV;
|
||||
|
||||
memset(&message_info, 0, sizeof(message_info));
|
||||
otIp6AddressFromString(addr_str, &message_info.mPeerAddr);
|
||||
message_info.mPeerPort = UDP_PORT;
|
||||
|
||||
message = otUdpNewMessage(instance, NULL);
|
||||
if (message == NULL) return -ENOMEM;
|
||||
|
||||
otMessageAppend(message, payload, len);
|
||||
otUdpSend(instance, &s_udp_socket, message, &message_info);
|
||||
|
||||
LOG_INF("UDP sent to %s", addr_str);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int thread_mgmt_init(void)
|
||||
{
|
||||
struct otInstance *instance = openthread_get_default_instance();
|
||||
@@ -199,18 +48,66 @@ int thread_mgmt_init(void)
|
||||
otIp6SetEnabled(instance, true);
|
||||
otThreadSetEnabled(instance, true);
|
||||
|
||||
/* UDP Initialisierung */
|
||||
otSockAddr listen_addr;
|
||||
memset(&listen_addr, 0, sizeof(listen_addr));
|
||||
listen_addr.mPort = UDP_PORT;
|
||||
otUdpOpen(instance, &s_udp_socket, udp_receive_cb, NULL);
|
||||
otUdpBind(instance, &s_udp_socket, &listen_addr, OT_NETIF_UNSPECIFIED);
|
||||
|
||||
/* CoAP Initialisierung */
|
||||
otCoapStart(instance, OT_DEFAULT_COAP_PORT);
|
||||
s_id_resource.mContext = instance;
|
||||
otCoapAddResource(instance, &s_id_resource);
|
||||
|
||||
LOG_INF("Thread MGMT: Initialized, UDP %d & CoAP %d open.", UDP_PORT, OT_DEFAULT_COAP_PORT);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void thread_mgmt_restart_thread_stack(bool force)
|
||||
{
|
||||
struct otInstance *instance = openthread_get_default_instance();
|
||||
otOperationalDataset active_dataset;
|
||||
bool changed = false;
|
||||
|
||||
// Get current active dataset. Force restart if unable to get it.
|
||||
if (otDatasetGetActive(instance, &active_dataset) != OT_ERROR_NONE) {
|
||||
LOG_WRN("Failed to get active dataset, forcing Thread stack restart.");
|
||||
force = true;
|
||||
}
|
||||
|
||||
// Compare each relevant field and set 'changed' if any differ
|
||||
if (force || active_dataset.mChannel != lasertag_get_thread_channel()) {
|
||||
active_dataset.mChannel = lasertag_get_thread_channel();
|
||||
active_dataset.mComponents.mIsChannelPresent = true;
|
||||
changed = true;
|
||||
}
|
||||
if (force || active_dataset.mPanId != lasertag_get_thread_pan_id()) {
|
||||
active_dataset.mPanId = lasertag_get_thread_pan_id();
|
||||
active_dataset.mComponents.mIsPanIdPresent = true;
|
||||
changed = true;
|
||||
}
|
||||
if (force || memcmp(active_dataset.mExtendedPanId.m8, lasertag_get_thread_ext_pan_id(), 8) != 0) {
|
||||
memcpy(active_dataset.mExtendedPanId.m8, lasertag_get_thread_ext_pan_id(), 8);
|
||||
active_dataset.mComponents.mIsExtendedPanIdPresent = true;
|
||||
changed = true;
|
||||
}
|
||||
if (force || memcmp(active_dataset.mNetworkKey.m8, lasertag_get_thread_network_key(), 16) != 0) {
|
||||
memcpy(active_dataset.mNetworkKey.m8, lasertag_get_thread_network_key(), 16);
|
||||
active_dataset.mComponents.mIsNetworkKeyPresent = true;
|
||||
changed = true;
|
||||
}
|
||||
const char *net_name = lasertag_get_thread_network_name();
|
||||
if (force || strncmp(active_dataset.mNetworkName.m8, net_name, strlen(net_name)) != 0) {
|
||||
memcpy(active_dataset.mNetworkName.m8, net_name, strlen(net_name));
|
||||
active_dataset.mComponents.mIsNetworkNamePresent = true;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
active_dataset.mActiveTimestamp.mSeconds += 1; // Increment timestamp to signal change
|
||||
|
||||
otThreadSetEnabled(instance, false);
|
||||
otIp6SetEnabled(instance, false);
|
||||
otError error = otDatasetSetActive(instance, &active_dataset);
|
||||
if (error != OT_ERROR_NONE) {
|
||||
LOG_ERR("Failed to set active dataset: %d", error);
|
||||
return;
|
||||
}
|
||||
otIp6SetEnabled(instance, true);
|
||||
otThreadSetEnabled(instance, true);
|
||||
|
||||
LOG_INF("Thread stack restarted with updated dataset.");
|
||||
} else {
|
||||
LOG_INF("Thread stack restart not required; dataset unchanged.");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user