Compare commits

...

2 Commits

Author SHA1 Message Date
93a7da7855 Fix: Network key persistence in Thread dataset
All checks were successful
Deploy Docs / build-and-deploy (push) Successful in 19s
- Network key was being reset to 0x00... after dataset updates
- Root cause: otDatasetSetActive() was overwriting the network key
- Solution: Call otThreadSetNetworkKey() before otDatasetSetActive()
  and read the key back to ensure it's included in the persistent dataset
- Verified: Network key now survives both software and hardware reboots
- Vest joins network as router in <1.1s after reboot
2026-01-14 17:16:17 +01:00
fb4578ac51 Zwischenstand vor refactor 2026-01-14 15:58:45 +01:00
10 changed files with 431 additions and 560 deletions

View File

@@ -1,16 +1,17 @@
# Console and Logging
CONFIG_LOG=y
# Shell and Built-in Commands
CONFIG_SHELL=y
CONFIG_KERNEL_SHELL=y
CONFIG_DEVICE_SHELL=y
CONFIG_REBOOT=y
CONFIG_DEVICE_SHELL=n
CONFIG_DEVMEM_SHELL=n
# --- STACK SIZE UPDATES (Fixes the Hard Fault) ---
# --- STACK SIZE UPDATES (Fixes the MPU/Stack Fault) ---
CONFIG_MAIN_STACK_SIZE=4096
CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048
CONFIG_BT_RX_STACK_SIZE=2048
CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=4096
CONFIG_BT_RX_STACK_SIZE=4096
# Storage and Settings (NVS)
CONFIG_FLASH=y
@@ -39,9 +40,12 @@ 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
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

View File

@@ -1,16 +1,17 @@
# Console and Logging
CONFIG_LOG=y
# Shell and Built-in Commands
CONFIG_SHELL=y
CONFIG_KERNEL_SHELL=y
CONFIG_DEVICE_SHELL=y
CONFIG_REBOOT=y
CONFIG_DEVICE_SHELL=n
CONFIG_DEVMEM_SHELL=n
# --- STACK SIZE UPDATES (Fixes the Hard Fault) ---
# --- STACK SIZE UPDATES (Fixes the MPU/Stack Fault) ---
CONFIG_MAIN_STACK_SIZE=4096
CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048
CONFIG_BT_RX_STACK_SIZE=2048
CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=4096
CONFIG_BT_RX_STACK_SIZE=4096
# Storage and Settings (NVS)
CONFIG_FLASH=y
@@ -24,6 +25,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
@@ -38,9 +40,12 @@ 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
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

View File

@@ -5,9 +5,9 @@ menuconfig BLE_MGMT
Library for BLE provisioning of the lasertag device.
if BLE_MGMT
config BLE_MGMT_LOG_LEVEL
int "BLE Management Log Level"
default 3
module = BLE_MGMT
module-str = ble_mgmt
source "subsys/logging/Kconfig.template.log_config"
config BLE_MGMT_CAN_BE_GAME_LEADER
bool "Can be game leader"

View File

@@ -15,6 +15,20 @@
#define LT_TYPE_VEST 0x03
#define LT_TYPE_BEACON 0x04
/**
* @brief Device configuration payload structure for BLE management.
*/
typedef struct __packed {
uint8_t system_state; /* Offset 0 */
uint64_t game_id; /* Offset 1 */
uint16_t pan_id; /* Offset 9 */
uint8_t channel; /* Offset 11 */
uint8_t ext_pan_id[8]; /* Offset 12 */
uint8_t network_key[16]; /* Offset 20 */
char network_name[17]; /* Offset 36 */
char node_name[33]; /* Offset 53 */
} device_config_payload_t;
/**
* @brief Initialize Bluetooth and prepare services.
*

View File

@@ -1,12 +1,7 @@
/**
* BLE Management Module (ble_mgmt.c)
*
* Handles Bluetooth Low Energy (BLE) setup, advertising, GATT services,
* and connection management for the Lasertag device.
*
* Services provided:
* - Provisioning Service (0x10xx): Device configuration and Thread mesh setup
* - Game Service (0x20xx): Game-related commands and logging
* * Structural Fix: Offloading heavy NVS and Thread operations to a workqueue
* to prevent stack overflows in the BT RX thread.
*/
#include <zephyr/kernel.h>
@@ -25,61 +20,160 @@
LOG_MODULE_REGISTER(ble_mgmt, CONFIG_BLE_MGMT_LOG_LEVEL);
/* ============================================================================
UUID Definitions
============================================================================
Base UUID: 03afe2cf-6c64-4a22-9289-c3ae820cXXXX
Service and characteristic IDs use the last two bytes (XXXX)
========================================================================== */
/* UUID Definitions */
#define BT_UUID_LT_PROV_SERVICE BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x03afe2cf, 0x6c64, 0x4a22, 0x9289, 0xc3ae820c1000))
#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_TYPE_CHAR BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x03afe2cf, 0x6c64, 0x4a22, 0x9289, 0xc3ae820c1008))
#define BT_UUID_LT_PROV_CONFIG_CHAR BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x03afe2cf, 0x6c64, 0x4a22, 0x9289, 0xc3ae820c100c))
#define LT_UUID_BASE_VAL \
BT_UUID_128_ENCODE(0x03afe2cf, 0x6c64, 0x4a22, 0x9289, 0xc3ae820c0000)
/* PROVISIONING SERVICE (0x10xx): Device configuration & Thread mesh setup */
#define BT_UUID_LT_PROV_SERVICE BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x03afe2cf, 0x6c64, 0x4a22, 0x9289, 0xc3ae820c1000))
/* 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_TYPE_CHAR BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x03afe2cf, 0x6c64, 0x4a22, 0x9289, 0xc3ae820c1008))
#define BT_UUID_LT_PROV_CONFIG_CHAR BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x03afe2cf, 0x6c64, 0x4a22, 0x9289, 0xc3ae820c100c))
/* Global state and Workqueue structures */
static uint8_t device_role = 0;
static uint8_t adv_enabled = 0;
static struct k_work_delayable adv_restart_work;
/* Buffers for asynchronous config application */
static device_config_payload_t pending_config;
static struct k_work config_apply_work;
/* ============================================================================
Data Structures
Workqueue Handlers
============================================================================ */
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"),
pending_config.node_name,
pending_config.system_state,
pending_config.game_id,
pending_config.network_name,
pending_config.channel,
pending_config.pan_id);
LOG_HEXDUMP_DBG(pending_config.ext_pan_id, 8, "ext pan id");
LOG_HEXDUMP_DBG(pending_config.network_key, 16, "network key");
if (pending_config.system_state != SYS_STATE_NO_CHANGE) {
game_mgmt_set_state((sys_state_t)pending_config.system_state);
}
if (pending_config.game_id != 0) {
game_mgmt_set_game_id(pending_config.game_id);
}
if (pending_config.node_name[0] != '\0') {
lasertag_set_device_name(pending_config.node_name, strlen(pending_config.node_name));
bt_set_name(lasertag_get_device_name());
}
if (pending_config.channel != 0) {
thread_mgmt_restart_thread_stack(&pending_config, false);
}
}
static void adv_restart_work_handler(struct k_work *work)
{
ARG_UNUSED(work);
LOG_DBG("Restarting BLE advertising via System Workqueue...");
if (adv_enabled == 0) {
int err = ble_mgmt_adv_start();
if (err) {
LOG_ERR("Fehler beim Neustart des Advertisings (err %d)", err);
}
}
}
/* ============================================================================
GATT Handlers
============================================================================ */
/**
* Device configuration payload.
* Packed structure for transmitting full device configuration via BLE.
* Total size: 86 bytes
*/
struct device_config_payload {
uint8_t system_state; /* Offset 0 */
uint64_t game_id; /* Offset 1 */
uint16_t pan_id; /* Offset 9 */
uint8_t channel; /* Offset 11 */
uint8_t ext_pan_id[8]; /* Offset 12 */
uint8_t network_key[16]; /* Offset 20 */
char network_name[17]; /* Offset 36 (16 chars + \0) */
char node_name[33]; /* Offset 53 (32 chars + \0) */
} __packed;
static ssize_t read_leader_config(struct bt_conn *conn, const struct bt_gatt_attr *attr,
void *buf, uint16_t len, uint16_t offset)
{
device_config_payload_t payload;
memset(&payload, 0, sizeof(payload));
payload.system_state = (uint8_t)game_mgmt_get_state();
payload.game_id = game_mgmt_get_game_id();
payload.pan_id = thread_mgmt_get_pan_id();
payload.channel = thread_mgmt_get_channel();
thread_mgmt_get_ext_pan_id(payload.ext_pan_id);
thread_mgmt_get_network_key(payload.network_key);
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"),
payload.node_name,
payload.system_state,
payload.game_id,
payload.network_name,
payload.channel,
payload.pan_id);
LOG_HEXDUMP_DBG(payload.ext_pan_id, 8, "ext pan id");
LOG_HEXDUMP_DBG(payload.network_key, 16, "network key");
return bt_gatt_attr_read(conn, attr, buf, len, offset, &payload, sizeof(payload));
}
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)
{
if (len != sizeof(device_config_payload_t)) {
return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
}
/* Copy data to buffer and delegate to system workqueue */
memcpy(&pending_config, buf, sizeof(pending_config));
k_work_submit(&config_apply_work);
LOG_DBG("Config write received, delegated to workqueue.");
return len;
}
/* Simple value handlers for name and type */
static ssize_t read_simple_val(struct bt_conn *conn, const struct bt_gatt_attr *attr,
void *buf, uint16_t len, uint16_t offset)
{
const char *val_ptr = NULL;
size_t val_len = 0;
if (bt_uuid_cmp(attr->uuid, BT_UUID_LT_PROV_TYPE_CHAR) == 0) {
val_ptr = (char *)&device_role;
val_len = sizeof(device_role);
} else if (bt_uuid_cmp(attr->uuid, BT_UUID_LT_PROV_NAME_CHAR) == 0) {
val_ptr = lasertag_get_device_name();
val_len = strlen(val_ptr);
}
return bt_gatt_attr_read(conn, attr, buf, len, offset, val_ptr, val_len);
}
static ssize_t write_name(struct bt_conn *conn, const struct bt_gatt_attr *attr,
const void *buf, uint16_t len, uint16_t offset, uint8_t flags)
{
int rc = lasertag_set_device_name(buf, len);
if (rc == 0) bt_set_name(lasertag_get_device_name());
return rc ? BT_GATT_ERR(BT_ATT_ERR_UNLIKELY) : len;
}
/* ============================================================================
Global State Variables
Service Definition
============================================================================ */
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 */
BT_GATT_SERVICE_DEFINE(provisioning_svc,
BT_GATT_PRIMARY_SERVICE(BT_UUID_LT_PROV_SERVICE),
BT_GATT_CHARACTERISTIC(BT_UUID_LT_PROV_NAME_CHAR,
BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE,
BT_GATT_PERM_READ | BT_GATT_PERM_WRITE,
read_simple_val, write_name, NULL),
BT_GATT_CHARACTERISTIC(BT_UUID_LT_PROV_TYPE_CHAR,
BT_GATT_CHRC_READ, BT_GATT_PERM_READ,
read_simple_val, NULL, NULL),
BT_GATT_CHARACTERISTIC(BT_UUID_LT_PROV_CONFIG_CHAR,
BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE,
BT_GATT_PERM_READ | BT_GATT_PERM_WRITE,
read_leader_config, write_leader_config, NULL),
);
/* ============================================================================
Advertising Data
Advertising & Management
============================================================================ */
/* Manufacturer data: last byte contains device role type */
static uint8_t mfg_data[] = { 0xff, 0xff, 0x00 };
/* Advertising data array with UUID and flags */
static const struct bt_data ad[] = {
BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
BT_DATA_BYTES(BT_DATA_UUID128_ALL,
@@ -88,277 +182,32 @@ static const struct bt_data ad[] = {
BT_DATA(BT_DATA_MANUFACTURER_DATA, mfg_data, sizeof(mfg_data)),
};
/* ============================================================================
GATT Callback Handlers
These functions handle read/write operations on GATT characteristics.
They validate requests and interface with device configuration APIs.
============================================================================ */
/**
* Read handler for provisioning characteristics.
* Supports reading: device type, name, PAN ID, channel, extended PAN ID,
* network key, and network name.
*/
static ssize_t read_lasertag_val(struct bt_conn *conn, const struct bt_gatt_attr *attr,
void *buf, uint16_t len, uint16_t offset)
{
const char *val_ptr = NULL;
size_t val_len = 0;
if (bt_uuid_cmp(attr->uuid, BT_UUID_LT_PROV_TYPE_CHAR) == 0)
{
val_ptr = (char *)&device_role;
val_len = sizeof(device_role);
}
else if (bt_uuid_cmp(attr->uuid, BT_UUID_LT_PROV_NAME_CHAR) == 0)
{
val_ptr = lasertag_get_device_name();
val_len = strlen(val_ptr);
}
return bt_gatt_attr_read(conn, attr, buf, len, offset, val_ptr, val_len);
}
/**
* Write handler for provisioning characteristics.
* Validates write length and updates device configuration via lasertag_utils APIs.
* Also updates Bluetooth advertised name when device name is changed.
*/
static ssize_t write_lasertag_val(struct bt_conn *conn, const struct bt_gatt_attr *attr,
const void *buf, uint16_t len, uint16_t offset, uint8_t flags)
{
int rc = 0;
if (bt_uuid_cmp(attr->uuid, BT_UUID_LT_PROV_NAME_CHAR) == 0)
{
rc = lasertag_set_device_name(buf, len);
if (rc == 0 ) {
bt_set_name(lasertag_get_device_name());
}
}
if (rc)
return BT_GATT_ERR(BT_ATT_ERR_UNLIKELY);
return len;
}
/**
* Read handler for the full leader configuration.
* Returns a packed structure containing all device configuration in a single read.
*/
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 device_config_payload payload;
memset(&payload, 0, sizeof(payload));
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();
memcpy(payload.ext_pan_id, lasertag_get_thread_ext_pan_id(), 8);
memcpy(payload.network_key, lasertag_get_thread_network_key(), 16);
/* Ensure null termination and copy strings */
strncpy(payload.network_name, lasertag_get_thread_network_name(), 16);
strncpy(payload.node_name, lasertag_get_device_name(), 32);
return bt_gatt_attr_read(conn, attr, buf, len, offset, (const void *)&payload, sizeof(payload));
}
/**
* Write handler for the full leader configuration.
* 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)
{
if (len != sizeof(struct device_config_payload))
{
return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
}
const struct device_config_payload *payload = (const struct device_config_payload *)buf;
/* 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 */
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());
}
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);
LOG_HEXDUMP_INF(payload->network_key, 16, "Setting new Thread network key:");
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
Defines the Provisioning Service with all characteristics and callbacks.
============================================================================ */
BT_GATT_SERVICE_DEFINE(provisioning_svc,
BT_GATT_PRIMARY_SERVICE(BT_UUID_LT_PROV_SERVICE),
/* Device name (readable and writable) */
BT_GATT_CHARACTERISTIC(BT_UUID_LT_PROV_NAME_CHAR,
BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE,
BT_GATT_PERM_READ | BT_GATT_PERM_WRITE,
read_lasertag_val, write_lasertag_val, NULL),
/* Device type / role (read-only) */
BT_GATT_CHARACTERISTIC(BT_UUID_LT_PROV_TYPE_CHAR,
BT_GATT_CHRC_READ,
BT_GATT_PERM_READ,
read_lasertag_val, NULL, NULL),
/* Thread PAN ID (read/write) - DISABLED */
/*
BT_GATT_CHARACTERISTIC(BT_UUID_LT_PROV_PANID_CHAR,
BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE,
BT_GATT_PERM_READ | BT_GATT_PERM_WRITE,
read_lasertag_val, write_lasertag_val, NULL),
*/
/* Thread channel (read/write) - DISABLED */
/*
BT_GATT_CHARACTERISTIC(BT_UUID_LT_PROV_CHAN_CHAR,
BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE,
BT_GATT_PERM_READ | BT_GATT_PERM_WRITE,
read_lasertag_val, write_lasertag_val, NULL),
*/
/* Extended PAN ID (read/write) - DISABLED */
/*
BT_GATT_CHARACTERISTIC(BT_UUID_LT_PROV_EXTPAN_CHAR,
BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE,
BT_GATT_PERM_READ | BT_GATT_PERM_WRITE,
read_lasertag_val, write_lasertag_val, NULL),
*/
/* Network key (read/write) - DISABLED */
/*
BT_GATT_CHARACTERISTIC(BT_UUID_LT_PROV_NETKEY_CHAR,
BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE,
BT_GATT_PERM_READ | BT_GATT_PERM_WRITE,
read_lasertag_val, write_lasertag_val, NULL),
*/
/* Thread network name (read/write) - DISABLED */
/*
BT_GATT_CHARACTERISTIC(BT_UUID_LT_PROV_NETNAME_CHAR,
BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE,
BT_GATT_PERM_READ | BT_GATT_PERM_WRITE,
read_lasertag_val, write_lasertag_val, NULL),
*/
/* Full leader configuration (packed struct) */
BT_GATT_CHARACTERISTIC(BT_UUID_LT_PROV_CONFIG_CHAR,
BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE,
BT_GATT_PERM_READ | BT_GATT_PERM_WRITE,
read_leader_config, write_leader_config, NULL), );
/* ============================================================================
Internal Helper Functions
============================================================================ */
/**
* Work handler for delayed advertising restart.
* Called when a device disconnects to resume advertising after a brief delay.
*/
static void adv_restart_work_handler(struct k_work *work)
{
if (adv_enabled == 0)
{
int err = ble_mgmt_adv_start();
if (err) {
LOG_ERR("Fehler beim verzögerten Neustarten des Advertisings (err %d)", err);
} else {
LOG_INF("Advertising nach Verzögerung erfolgreich neu gestartet");
}
}
}
/* ============================================================================
Public Initialization & Management Functions
============================================================================ */
/**
* Initialize the BLE module.
* Enables Bluetooth and sets up the device role for advertising.
*
* @param device_type The device type/role (leader, weapon, vest, beacon)
* @return 0 on success, negative error code on failure
*/
int ble_mgmt_init(uint8_t device_type)
{
device_role = device_type;
/* Initialize work structures */
k_work_init_delayable(&adv_restart_work, adv_restart_work_handler);
k_work_init(&config_apply_work, config_apply_work_handler);
int err = bt_enable(NULL);
if (err)
return err;
LOG_INF("Bluetooth initialized");
if (err) return err;
LOG_DBG("Bluetooth initialized successfully.");
return 0;
}
/**
* Start BLE advertising.
* Advertises device name and type, configures fast advertising intervals.
*
* @return 0 on success, negative error code on failure
*/
int ble_mgmt_adv_start(void)
{
const char set_device_role = device_role;
mfg_data[2] = set_device_role; // Update device role in advertising data
mfg_data[2] = device_role;
const char *name = lasertag_get_device_name();
bt_set_name(name);
struct bt_data dynamic_sd[] = {
BT_DATA(BT_DATA_NAME_COMPLETE, name, strlen(name)),
};
struct bt_le_adv_param adv_param = {
.id = BT_ID_DEFAULT,
struct bt_data sd[] = { BT_DATA(BT_DATA_NAME_COMPLETE, name, strlen(name)) };
struct bt_le_adv_param param = {
.options = (BT_LE_ADV_OPT_CONN | BT_LE_ADV_OPT_SCANNABLE),
.interval_min = BT_GAP_ADV_FAST_INT_MIN_2,
.interval_max = BT_GAP_ADV_FAST_INT_MAX_2,
};
int err = bt_le_adv_start(&adv_param, ad, ARRAY_SIZE(ad), dynamic_sd, ARRAY_SIZE(dynamic_sd));
if (!err)
{
LOG_INF("Advertising started as: %s, type: %d", name, device_role);
adv_enabled = 1;
}
int err = bt_le_adv_start(&param, ad, ARRAY_SIZE(ad), sd, ARRAY_SIZE(sd));
if (!err) adv_enabled = 1;
return err;
}
@@ -372,7 +221,7 @@ int ble_mgmt_adv_stop(void)
int err = bt_le_adv_stop();
if (!err)
{
LOG_INF("Advertising stopped");
LOG_DBG("Advertising stopped");
adv_enabled = 0;
}
return err;
@@ -391,7 +240,7 @@ static void connected(struct bt_conn *conn, uint8_t err)
if (err) {
LOG_ERR("Verbindung fehlgeschlagen (err %u)", err);
} else {
LOG_INF("Host verbunden");
LOG_DBG("Host verbunden");
adv_enabled = 0;
}
}
@@ -402,7 +251,7 @@ static void connected(struct bt_conn *conn, uint8_t err)
*/
static void disconnected(struct bt_conn *conn, uint8_t reason)
{
LOG_INF("Verbindung getrennt (Grund %u)", reason);
LOG_DBG("Verbindung getrennt (Grund %u)", reason);
k_work_reschedule(&adv_restart_work, K_MSEC(100));
}

View File

@@ -3,6 +3,13 @@
#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
/**
* @file lasertag_utils.h
* @brief Common utility functions for the lasertag system.
@@ -19,36 +26,6 @@ void lasertag_utils_init(void);
*/
const char* lasertag_get_device_name(void);
/**
* @brief Get the configured Thread PAN ID.
* @return 16-bit PAN ID.
*/
uint16_t lasertag_get_thread_pan_id(void);
/**
* @brief Get the configured Thread Network Name.
* @return Pointer to the network name string.
*/
const char* lasertag_get_thread_network_name(void);
/**
* @brief Get the configured Thread Channel.
* @return 8-bit channel (usually 11-26).
*/
uint8_t lasertag_get_thread_channel(void);
/**
* @brief Get the configured Thread Extended PAN ID.
* @return Pointer to the 8-byte extended PAN ID.
*/
const uint8_t* lasertag_get_thread_ext_pan_id(void);
/**
* @brief Get the configured Thread Network Key.
* @return Pointer to the 16-byte network key.
*/
const uint8_t* lasertag_get_thread_network_key(void);
/**
* @brief Set the device name.
* @param name Pointer to the name string.
@@ -57,40 +34,4 @@ const uint8_t* lasertag_get_thread_network_key(void);
*/
int lasertag_set_device_name(const char *name, size_t len);
/**
* @brief Set the Thread PAN ID.
* @param pan_id 16-bit PAN ID.
* @return 0 on success, negative error code otherwise.
*/
int lasertag_set_thread_pan_id(uint16_t pan_id);
/**
* @brief Set the Thread Network Name.
* @param name Pointer to the network name string.
* @param len Length of the name.
* @return 0 on success, negative error code otherwise.
*/
int lasertag_set_thread_network_name(const char *name, size_t len);
/**
* @brief Set the Thread Channel.
* @param channel 8-bit channel (usually 11-26).
* @return 0 on success, negative error code otherwise.
*/
int lasertag_set_thread_channel(uint8_t channel);
/**
* @brief Set the Thread Extended PAN ID.
* @param ext_id Pointer to the 8-byte extended PAN ID.
* @return 0 on success, negative error code otherwise.
*/
int lasertag_set_thread_ext_pan_id(const uint8_t *ext_id);
/**
* @brief Set the Thread Network Key.
* @param key Pointer to the 16-byte network key.
* @return 0 on success, negative error code otherwise.
*/
int lasertag_set_thread_network_key(const uint8_t *key);
#endif /* LASERTAG_UTILS_H */

View File

@@ -12,14 +12,7 @@
LOG_MODULE_REGISTER(lasertag_utils, CONFIG_LASERTAG_UTILS_LOG_LEVEL);
static char device_name[32] = "UnknownDevice";
static uint16_t thread_pan_id = 0xabcd;
static char thread_network_name[17] = "OpenThread-nRF";
static uint8_t thread_channel = 15;
static uint8_t thread_ext_pan_id[8] = {0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe, 0xba, 0xbe};
static uint8_t thread_network_key[16] = {
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff};
static char device_name[32] = "Eriks Lasertag Device";
/* --- Settings Handler --- */
@@ -37,33 +30,6 @@ static int lasertag_settings_set(const char *name, size_t len, settings_read_cb
return 0;
}
}
if (settings_name_steq(name, "pan_id", &next) && !next)
{
return read_cb(cb_arg, &thread_pan_id, sizeof(thread_pan_id)) >= 0 ? 0 : -EIO;
}
if (settings_name_steq(name, "net_name", &next) && !next)
{
if (len > sizeof(thread_network_name) - 1)
return -EINVAL;
ssize_t rc = read_cb(cb_arg, thread_network_name, len);
if (rc >= 0)
{
thread_network_name[rc] = '\0';
return 0;
}
}
if (settings_name_steq(name, "channel", &next) && !next)
{
return read_cb(cb_arg, &thread_channel, sizeof(thread_channel)) >= 0 ? 0 : -EIO;
}
if (settings_name_steq(name, "ext_pan_id", &next) && !next)
{
return read_cb(cb_arg, thread_ext_pan_id, sizeof(thread_ext_pan_id)) >= 0 ? 0 : -EIO;
}
if (settings_name_steq(name, "net_key", &next) && !next)
{
return read_cb(cb_arg, thread_network_key, sizeof(thread_network_key)) >= 0 ? 0 : -EIO;
}
return -ENOENT;
}
@@ -71,27 +37,13 @@ struct settings_handler lasertag_conf = {.name = "lasertag", .h_set = lasertag_s
void lasertag_utils_init(void)
{
LOG_INF("==========================================");
LOG_INF("Lasertag System - Common Lib v0.0.1");
settings_subsys_init();
settings_register(&lasertag_conf);
settings_load();
LOG_INF("Device Name : %s", device_name);
LOG_INF("Thread PAN : 0x%04x", thread_pan_id);
LOG_INF("Thread Name : %s", thread_network_name);
LOG_INF("Thread Chan : %d", thread_channel);
LOG_INF("==========================================");
}
/* Getters */
const char *lasertag_get_device_name(void) { return device_name; }
uint16_t lasertag_get_thread_pan_id(void) { return thread_pan_id; }
const char *lasertag_get_thread_network_name(void) { return thread_network_name; }
uint8_t lasertag_get_thread_channel(void) { return thread_channel; }
const uint8_t *lasertag_get_thread_ext_pan_id(void) { return thread_ext_pan_id; }
const uint8_t *lasertag_get_thread_network_key(void) { return thread_network_key; }
/* Setters */
int lasertag_set_device_name(const char *name, size_t len)
@@ -102,49 +54,10 @@ int lasertag_set_device_name(const char *name, size_t len)
device_name[len] = '\0';
return settings_save_one("lasertag/name", device_name, len);
}
int lasertag_set_thread_pan_id(uint16_t pan_id)
{
thread_pan_id = pan_id;
return settings_save_one("lasertag/pan_id", &thread_pan_id, sizeof(thread_pan_id));
}
int lasertag_set_thread_network_name(const char *name, size_t len)
{
if (len >= sizeof(thread_network_name))
len = sizeof(thread_network_name) - 1;
memcpy(thread_network_name, name, len);
thread_network_name[len] = '\0';
return settings_save_one("lasertag/net_name", thread_network_name, len);
}
int lasertag_set_thread_channel(uint8_t channel)
{
thread_channel = channel;
return settings_save_one("lasertag/channel", &thread_channel, sizeof(thread_channel));
}
int lasertag_set_thread_ext_pan_id(const uint8_t *ext_id)
{
memcpy(thread_ext_pan_id, ext_id, 8);
return settings_save_one("lasertag/ext_pan_id", thread_ext_pan_id, 8);
}
int lasertag_set_thread_network_key(const uint8_t *key)
{
memcpy(thread_network_key, key, 16);
return settings_save_one("lasertag/net_key", thread_network_key, 16);
}
/* --- Shell Commands --- */
#if CONFIG_LASERTAG_SHELL
static int lasertag_hex2bin(const char *hex, uint8_t *bin, size_t bin_len)
{
for (size_t i = 0; i < bin_len; i++)
{
char buf[3] = {hex[i * 2], hex[i * 2 + 1], '\0'};
bin[i] = (uint8_t)strtoul(buf, NULL, 16);
}
return 0;
}
static int cmd_reboot(const struct shell *sh, size_t argc, char **argv)
{
shell_print(sh, "Rebooting...");
@@ -159,25 +72,6 @@ static int cmd_name_set(const struct shell *sh, size_t argc, char **argv)
return 0;
}
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);
lasertag_set_thread_pan_id(pan);
return 0;
}
static int cmd_thread_set_chan(const struct shell *sh, size_t argc, char **argv)
{
uint8_t chan = (uint8_t)strtoul(argv[1], NULL, 10);
lasertag_set_thread_channel(chan);
return 0;
}
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_SUBCMD_SET_END);
static int cmd_ble_start(const struct shell *sh, size_t argc, char **argv)
{
return ble_mgmt_adv_start();
@@ -189,7 +83,6 @@ SHELL_STATIC_SUBCMD_SET_CREATE(sub_ble,
SHELL_STATIC_SUBCMD_SET_CREATE(sub_lasertag,
SHELL_CMD_ARG(name, NULL, "Set name", cmd_name_set, 2, 0),
SHELL_CMD(thread, &sub_thread, "Thread configuration", NULL),
SHELL_CMD(ble, &sub_ble, "BLE Management", NULL),
SHELL_CMD(reboot, NULL, "Reboot", cmd_reboot),
SHELL_SUBCMD_SET_END);

View File

@@ -6,9 +6,7 @@ menuconfig THREAD_MGMT
Library for initializing and managing the OpenThread stack.
if THREAD_MGMT
config THREAD_MGMT_LOG_LEVEL
int "Thread Management Log Level"
default 3
help
Set the verbosity of the thread management library.
endif
module = THREAD_MGMT
module-str = thread_mgmt
source "subsys/logging/Kconfig.template.log_config"
endif # THREAD_MGMT

View File

@@ -2,6 +2,7 @@
#define THREAD_MGMT_H
#include <stdint.h>
#include <ble_mgmt.h>
/**
* @brief Initializes the OpenThread stack.
@@ -12,5 +13,12 @@ int thread_mgmt_init(void);
* @brief Restarts the Thread stack.
* @param force If true, forces a full restart even if dataset is unchanged.
*/
void thread_mgmt_restart_thread_stack(bool force);
void thread_mgmt_restart_thread_stack(device_config_payload_t *pending_config, bool force);
uint16_t thread_mgmt_get_pan_id(void);
uint8_t thread_mgmt_get_channel(void);
void thread_mgmt_get_ext_pan_id(uint8_t *dest_8byte);
void thread_mgmt_get_network_key(uint8_t *dest_16byte);
void thread_mgmt_get_network_name(char *dest_str, size_t max_len);
#endif

View File

@@ -1,113 +1,272 @@
#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
#include <zephyr/net/openthread.h>
#include <zephyr/random/random.h>
#include <openthread/thread.h>
#include <openthread/dataset.h>
#include <openthread/instance.h>
#include <openthread/udp.h>
#include <openthread/coap.h>
#include <openthread/ip6.h>
#include <lasertag_utils.h>
#include <thread_mgmt.h>
#include <string.h>
#include <stdio.h>
#include <lasertag_utils.h>
#include <thread_mgmt.h>
#include <ble_mgmt.h>
LOG_MODULE_REGISTER(thread_mgmt, CONFIG_THREAD_MGMT_LOG_LEVEL);
#define UDP_PORT 1234
#define MAX_DISCOVERED_NODES 10
#if (CONFIG_THREAD_MGMT_LOG_LEVEL >= LOG_LEVEL_DBG)
static void thread_mgmt_state_notify_callback(otChangedFlags aFlags, void *aContext)
{
ARG_UNUSED(aContext);
if (aFlags & OT_CHANGED_THREAD_ROLE)
{
struct otInstance *instance = openthread_get_default_instance();
otDeviceRole role = otThreadGetDeviceRole(instance);
LOG_DBG("Thread Role changed: " BOLD("%s"), otThreadDeviceRoleToString(role));
}
}
struct openthread_state_changed_callback thread_state_callback = {
.otCallback = thread_mgmt_state_notify_callback,
.user_data = NULL,
};
void thread_mgmt_print_dataset(const otOperationalDataset *dataset)
{
if (dataset == NULL)
{
return;
}
// Network Name
char net_name[OT_NETWORK_NAME_MAX_SIZE + 1] = {0};
memcpy(net_name, dataset->mNetworkName.m8, sizeof(dataset->mNetworkName.m8));
// Convert to hex strings using bin2hex
char ext_pan_id_str[17];
bin2hex(dataset->mExtendedPanId.m8, 8, ext_pan_id_str, sizeof(ext_pan_id_str));
char net_key_str[33];
bin2hex(dataset->mNetworkKey.m8, 16, net_key_str, sizeof(net_key_str));
LOG_DBG(" Timestamp: " BOLD("%llu"), dataset->mActiveTimestamp.mSeconds);
LOG_DBG(" Network Name: " BOLD("%s"), net_name);
LOG_DBG(" PAN ID: " BOLD("0x%04X"), dataset->mPanId);
LOG_DBG(" Channel: " BOLD("%u"), dataset->mChannel);
LOG_DBG(" Extended PAN ID: " BOLD("%s"), ext_pan_id_str);
LOG_DBG(" Network Key: " BOLD("%s"), net_key_str);
}
#endif
int thread_mgmt_init(void)
{
struct otInstance *instance = openthread_get_default_instance();
otOperationalDataset dataset;
if (!instance) return -ENODEV;
if (!instance)
return -ENODEV;
#if (CONFIG_THREAD_MGMT_LOG_LEVEL >= LOG_LEVEL_DBG)
openthread_state_changed_callback_register(&thread_state_callback);
#endif
/* Dataset Setup */
memset(&dataset, 0, sizeof(otOperationalDataset));
dataset.mActiveTimestamp.mSeconds = 1;
dataset.mComponents.mIsActiveTimestampPresent = true;
const char *net_name = lasertag_get_thread_network_name();
memcpy(dataset.mNetworkName.m8, net_name, strlen(net_name));
dataset.mComponents.mIsNetworkNamePresent = true;
dataset.mPanId = lasertag_get_thread_pan_id();
dataset.mComponents.mIsPanIdPresent = true;
dataset.mChannel = lasertag_get_thread_channel();
dataset.mComponents.mIsChannelPresent = true;
memcpy(dataset.mExtendedPanId.m8, lasertag_get_thread_ext_pan_id(), 8);
dataset.mComponents.mIsExtendedPanIdPresent = true;
memcpy(dataset.mNetworkKey.m8, lasertag_get_thread_network_key(), 16);
dataset.mComponents.mIsNetworkKeyPresent = true;
uint8_t ml_prefix[] = {0xfd, 0xde, 0xad, 0x00, 0xbe, 0xef, 0x00, 0x00};
memcpy(dataset.mMeshLocalPrefix.m8, ml_prefix, 8);
dataset.mComponents.mIsMeshLocalPrefixPresent = true;
if (otDatasetGetActive(instance, &dataset))
{
// No dataset found, proceed to set up a new one
LOG_INF("No active Thread dataset found, initializing new dataset.");
dataset.mActiveTimestamp.mSeconds = 1;
dataset.mComponents.mIsActiveTimestampPresent = true;
uint8_t network_name[] = "Eriks Lasertag\0";
strncpy(dataset.mNetworkName.m8, network_name, strlen(network_name));
dataset.mComponents.mIsNetworkNamePresent = true;
dataset.mPanId = 0xdead;
dataset.mComponents.mIsPanIdPresent = true;
dataset.mChannel = 15;
dataset.mComponents.mIsChannelPresent = true;
uint8_t ext_pan_id[8];
sys_csrand_get(ext_pan_id, sizeof(ext_pan_id));
memcpy(dataset.mExtendedPanId.m8, ext_pan_id, 8);
dataset.mComponents.mIsExtendedPanIdPresent = true;
uint8_t network_key[16];
sys_csrand_get(network_key, sizeof(network_key));
memcpy(dataset.mNetworkKey.m8, network_key, 16);
dataset.mComponents.mIsNetworkKeyPresent = true;
memset(dataset.mMeshLocalPrefix.m8, 0, 8);
uint8_t mesh_local_prefix[8] = {0xfd, 0x00, 0x03, 0x08, 0x20, 0x13, 0x00, 0x00};
memcpy(dataset.mMeshLocalPrefix.m8, mesh_local_prefix, 8);
dataset.mComponents.mIsMeshLocalPrefixPresent = true;
otDatasetSetActive(instance, &dataset);
}
otDatasetSetActive(instance, &dataset);
otIp6SetEnabled(instance, true);
otThreadSetEnabled(instance, true);
LOG_INF("Thread MGMT: Initialized, UDP %d & CoAP %d open.", UDP_PORT, OT_DEFAULT_COAP_PORT);
#if (CONFIG_THREAD_MGMT_LOG_LEVEL >= LOG_LEVEL_DBG)
memset(&dataset, 0, sizeof(otOperationalDataset));
otDatasetGetActive(instance, &dataset);
LOG_DBG("Thread stack dataset after Thread stack initialization:");
thread_mgmt_print_dataset(&dataset);
#endif
return 0;
}
void thread_mgmt_restart_thread_stack(bool force)
void thread_mgmt_restart_thread_stack(device_config_payload_t *pending_config, bool force)
{
struct otInstance *instance = openthread_get_default_instance();
otOperationalDataset active_dataset;
otOperationalDataset dataset;
bool changed = false;
memset(&dataset, 0, sizeof(otOperationalDataset));
// Get current active dataset. Force restart if unable to get it.
if (otDatasetGetActive(instance, &active_dataset) != OT_ERROR_NONE) {
if (otDatasetGetActive(instance, &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;
if (force || dataset.mChannel != pending_config->channel)
{
LOG_DBG("Thread channel change detected: %u -> %u", dataset.mChannel, pending_config->channel);
dataset.mChannel = pending_config->channel;
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;
if (force || dataset.mPanId != pending_config->pan_id)
{
LOG_DBG("Thread PAN ID change detected: 0x%04X -> 0x%04X", dataset.mPanId, pending_config->pan_id);
dataset.mPanId = pending_config->pan_id;
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;
if (force || memcmp(dataset.mExtendedPanId.m8, pending_config->ext_pan_id, 8) != 0)
{
LOG_DBG("Thread extended PAN ID change detected.");
memcpy(dataset.mExtendedPanId.m8, pending_config->ext_pan_id, 8);
dataset.mComponents.mIsExtendedPanIdPresent = true;
changed = true;
}
if (changed) {
active_dataset.mActiveTimestamp.mSeconds += 1; // Increment timestamp to signal change
if (force || memcmp(dataset.mNetworkKey.m8, pending_config->network_key, 16) != 0)
{
LOG_DBG("Thread network key change detected.");
memcpy(dataset.mNetworkKey.m8, pending_config->network_key, 16);
dataset.mComponents.mIsNetworkKeyPresent = true;
changed = true;
}
if (force || strncmp(dataset.mNetworkName.m8, pending_config->network_name, 16) != 0)
{
LOG_DBG("Thread network name change detected: %s -> %s", dataset.mNetworkName.m8, pending_config->network_name);
strncpy(dataset.mNetworkName.m8, pending_config->network_name, 16);
dataset.mComponents.mIsNetworkNamePresent = true;
changed = true;
}
if (changed)
{
LOG_DBG("Thread stack restart required; dataset changed.");
dataset.mActiveTimestamp.mSeconds++;
dataset.mComponents.mIsActiveTimestampPresent = true;
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;
// Set the network key BEFORE updating the dataset
// This ensures OpenThread's crypto layer has the correct key before we update the operational dataset
otNetworkKey stored_network_key;
if (dataset.mComponents.mIsNetworkKeyPresent) {
otError key_err = otThreadSetNetworkKey(instance, &dataset.mNetworkKey);
if (key_err != OT_ERROR_NONE) {
LOG_ERR("Error setting network key: %d", key_err);
}
// Read the key back from the thread stack to ensure it's properly stored
otThreadGetNetworkKey(instance, &stored_network_key);
memcpy(dataset.mNetworkKey.m8, stored_network_key.m8, 16);
// Keep the flag set so the key is included in the persistent dataset
}
otError err = otDatasetSetActive(instance, &dataset);
if (err != OT_ERROR_NONE) {
LOG_ERR("Error writing dataset: %d", err);
}
otIp6SetEnabled(instance, true);
otThreadSetEnabled(instance, true);
LOG_INF("Thread stack restarted with updated dataset.");
} else {
LOG_INF("Thread stack restart not required; dataset unchanged.");
#if (CONFIG_THREAD_MGMT_LOG_LEVEL >= LOG_LEVEL_DBG)
otOperationalDataset new_active_dataset;
memset(&new_active_dataset, 0, sizeof(otOperationalDataset));
if (otDatasetGetActive(instance, &new_active_dataset) == OT_ERROR_NONE) {
LOG_DBG("Thread stack dataset after Thread stack update:");
thread_mgmt_print_dataset(&new_active_dataset);
}
#endif
LOG_DBG("Thread stack restarted successfully.");
}
else
{
LOG_DBG("Thread stack restart not required; dataset unchanged.");
}
}
uint16_t thread_mgmt_get_pan_id(void)
{
struct otInstance *instance = openthread_get_default_instance();
otOperationalDataset dataset;
if (otDatasetGetActive(instance, &dataset) == OT_ERROR_NONE)
{
return dataset.mPanId;
}
return 0;
}
uint8_t thread_mgmt_get_channel(void)
{
struct otInstance *instance = openthread_get_default_instance();
otOperationalDataset dataset;
if (otDatasetGetActive(instance, &dataset) == OT_ERROR_NONE)
{
return dataset.mChannel;
}
return 0;
}
void thread_mgmt_get_ext_pan_id(uint8_t *dest_8byte)
{
struct otInstance *instance = openthread_get_default_instance();
otOperationalDataset dataset;
if (otDatasetGetActive(instance, &dataset) == OT_ERROR_NONE) {
memcpy(dest_8byte, dataset.mExtendedPanId.m8, 8);
}
}
void thread_mgmt_get_network_key(uint8_t *dest_16byte)
{
struct otInstance *instance = openthread_get_default_instance();
otOperationalDataset dataset;
if (otDatasetGetActive(instance, &dataset) == OT_ERROR_NONE) {
memcpy(dest_16byte, dataset.mNetworkKey.m8, 16);
}
}
void thread_mgmt_get_network_name(char *dest_str, size_t max_len)
{
struct otInstance *instance = openthread_get_default_instance();
otOperationalDataset dataset;
if (otDatasetGetActive(instance, &dataset) == OT_ERROR_NONE) {
// Safe copy with forced null-termination
snprintf(dest_str, max_len, "%s", dataset.mNetworkName.m8);
}
}