diff --git a/firmware/apps/leader/prj.conf b/firmware/apps/leader/prj.conf index 153d4a0..dae3e17 100644 --- a/firmware/apps/leader/prj.conf +++ b/firmware/apps/leader/prj.conf @@ -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 \ No newline at end of file +CONFIG_BLE_MGMT=y +CONFIG_GAME_MGMT=y \ No newline at end of file diff --git a/firmware/apps/leader/src/main.c b/firmware/apps/leader/src/main.c index 6ed8136..47658d5 100644 --- a/firmware/apps/leader/src/main.c +++ b/firmware/apps/leader/src/main.c @@ -3,6 +3,7 @@ #include #include #include +#include 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; diff --git a/firmware/libs/CMakeLists.txt b/firmware/libs/CMakeLists.txt index c88206b..bb08544 100644 --- a/firmware/libs/CMakeLists.txt +++ b/firmware/libs/CMakeLists.txt @@ -3,4 +3,5 @@ add_subdirectory(ble_mgmt) add_subdirectory(thread_mgmt) add_subdirectory(lasertag_utils) -add_subdirectory(ir) \ No newline at end of file +add_subdirectory(ir) +add_subdirectory(game_mgmt) \ No newline at end of file diff --git a/firmware/libs/Kconfig b/firmware/libs/Kconfig index 2051f98..f6b3baf 100644 --- a/firmware/libs/Kconfig +++ b/firmware/libs/Kconfig @@ -2,4 +2,5 @@ rsource "lasertag_utils/Kconfig" rsource "thread_mgmt/Kconfig" rsource "ble_mgmt/Kconfig" -rsource "ir/Kconfig" \ No newline at end of file +rsource "ir/Kconfig" +rsource "game_mgmt/Kconfig" \ No newline at end of file diff --git a/firmware/libs/ble_mgmt/Kconfig b/firmware/libs/ble_mgmt/Kconfig index b0dda94..96205b0 100644 --- a/firmware/libs/ble_mgmt/Kconfig +++ b/firmware/libs/ble_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 \ No newline at end of file diff --git a/firmware/libs/ble_mgmt/include/ble_mgmt.h b/firmware/libs/ble_mgmt/include/ble_mgmt.h index c2e5516..5ec9197 100644 --- a/firmware/libs/ble_mgmt/include/ble_mgmt.h +++ b/firmware/libs/ble_mgmt/include/ble_mgmt.h @@ -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 */ \ No newline at end of file diff --git a/firmware/libs/ble_mgmt/src/ble_mgmt.c b/firmware/libs/ble_mgmt/src/ble_mgmt.c index f11456a..5c074f1 100644 --- a/firmware/libs/ble_mgmt/src/ble_mgmt.c +++ b/firmware/libs/ble_mgmt/src/ble_mgmt.c @@ -20,6 +20,7 @@ #include #include #include +#include #include 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 ============================================================================ */ diff --git a/firmware/libs/game_mgmt/CMakeLists.txt b/firmware/libs/game_mgmt/CMakeLists.txt new file mode 100644 index 0000000..9db0d0b --- /dev/null +++ b/firmware/libs/game_mgmt/CMakeLists.txt @@ -0,0 +1,5 @@ +if(CONFIG_BLE_MGMT) + zephyr_library() + zephyr_sources(src/game_mgmt.c) + zephyr_include_directories(include) +endif() \ No newline at end of file diff --git a/firmware/libs/game_mgmt/Kconfig b/firmware/libs/game_mgmt/Kconfig new file mode 100644 index 0000000..21aaa4a --- /dev/null +++ b/firmware/libs/game_mgmt/Kconfig @@ -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 \ No newline at end of file diff --git a/firmware/libs/game_mgmt/include/game_mgmt.h b/firmware/libs/game_mgmt/include/game_mgmt.h new file mode 100644 index 0000000..203f1a6 --- /dev/null +++ b/firmware/libs/game_mgmt/include/game_mgmt.h @@ -0,0 +1,64 @@ +#ifndef GAME_MGMT_H +#define GAME_MGMT_H + +#include +#include + +/** + * @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 */ \ No newline at end of file diff --git a/firmware/libs/game_mgmt/src/game_mgmt.c b/firmware/libs/game_mgmt/src/game_mgmt.c new file mode 100644 index 0000000..364ba63 --- /dev/null +++ b/firmware/libs/game_mgmt/src/game_mgmt.c @@ -0,0 +1,31 @@ +#include +#include +#include + +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; } \ No newline at end of file diff --git a/firmware/libs/lasertag_utils/src/lasertag_utils.c b/firmware/libs/lasertag_utils/src/lasertag_utils.c index 5720345..1dd2a2e 100644 --- a/firmware/libs/lasertag_utils/src/lasertag_utils.c +++ b/firmware/libs/lasertag_utils/src/lasertag_utils.c @@ -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) diff --git a/firmware/libs/thread_mgmt/include/thread_mgmt.h b/firmware/libs/thread_mgmt/include/thread_mgmt.h index ac1b4ab..36d768f 100644 --- a/firmware/libs/thread_mgmt/include/thread_mgmt.h +++ b/firmware/libs/thread_mgmt/include/thread_mgmt.h @@ -4,23 +4,13 @@ #include /** - * @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 \ No newline at end of file diff --git a/firmware/libs/thread_mgmt/src/thread_mgmt.c b/firmware/libs/thread_mgmt/src/thread_mgmt.c index 66bbcaa..3e52c7b 100644 --- a/firmware/libs/thread_mgmt/src/thread_mgmt.c +++ b/firmware/libs/thread_mgmt/src/thread_mgmt.c @@ -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; -} \ No newline at end of file +} + +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."); + } + +}