Added game mgmt, thread restart
All checks were successful
Deploy Docs / build-and-deploy (push) Successful in 24s

This commit is contained in:
2026-01-13 13:23:40 +01:00
parent a041d5a49c
commit 832a60d044
14 changed files with 258 additions and 395 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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 */

View File

@@ -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
============================================================================ */

View File

@@ -0,0 +1,5 @@
if(CONFIG_BLE_MGMT)
zephyr_library()
zephyr_sources(src/game_mgmt.c)
zephyr_include_directories(include)
endif()

View 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

View 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 */

View 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; }

View File

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

View File

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

View File

@@ -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.");
}
}