diff --git a/firmware/apps/leader/prj.conf b/firmware/apps/leader/prj.conf index 65686e8..0573209 100644 --- a/firmware/apps/leader/prj.conf +++ b/firmware/apps/leader/prj.conf @@ -19,7 +19,15 @@ CONFIG_NET_L2_OPENTHREAD=y CONFIG_OPENTHREAD=y CONFIG_OPENTHREAD_FTD=y CONFIG_OPENTHREAD_SHELL=y +CONFIG_OPENTHREAD_MANUAL_START=y + +# Bluetooth +CONFIG_BT=y +CONFIG_BT_PERIPHERAL=y +CONFIG_BT_DEVICE_NAME="Lasertag-Device" +CONFIG_BT_DEVICE_NAME_DYNAMIC=y # Enable Lasertag Shared Modules CONFIG_LASERTAG_UTILS=y -CONFIG_THREAD_MGMT=y \ No newline at end of file +CONFIG_THREAD_MGMT=y +CONFIG_BLE_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 40b2ef5..eb2a851 100644 --- a/firmware/apps/leader/src/main.c +++ b/firmware/apps/leader/src/main.c @@ -2,6 +2,7 @@ #include #include #include +#include LOG_MODULE_REGISTER(leader_app, CONFIG_LOG_DEFAULT_LEVEL); @@ -10,12 +11,22 @@ int main(void) /* Initialize shared project logic and NVS */ lasertag_utils_init(); + /* Initialize and start BLE management for provisioning */ + int rc = ble_mgmt_init(); + if (rc) { + LOG_ERR("BLE initialization failed (err %d)", rc); + return rc; + } else { + LOG_INF("BLE Management initialized successfully."); + } + /* Initialize and start OpenThread stack */ - int rc = thread_mgmt_init(); + rc = thread_mgmt_init(); if (rc) { LOG_ERR("Thread initialization failed (err %d)", rc); } else { LOG_INF("Leader Application successfully started with Thread Mesh."); + return rc; } while (1) { diff --git a/firmware/libs/CMakeLists.txt b/firmware/libs/CMakeLists.txt index 046b3d8..5174dc6 100644 --- a/firmware/libs/CMakeLists.txt +++ b/firmware/libs/CMakeLists.txt @@ -1,3 +1,5 @@ # Add library subdirectories -add_subdirectory(lasertag_utils) -add_subdirectory(thread_mgmt) \ No newline at end of file +# Build ble_mgmt and thread_mgmt first since lasertag_utils depends on them +add_subdirectory(ble_mgmt) +add_subdirectory(thread_mgmt) +add_subdirectory(lasertag_utils) \ No newline at end of file diff --git a/firmware/libs/Kconfig b/firmware/libs/Kconfig index d4a7500..82a4aee 100644 --- a/firmware/libs/Kconfig +++ b/firmware/libs/Kconfig @@ -1,3 +1,4 @@ # Main entry point for custom project Kconfigs rsource "lasertag_utils/Kconfig" -rsource "thread_mgmt/Kconfig" \ No newline at end of file +rsource "thread_mgmt/Kconfig" +rsource "ble_mgmt/Kconfig" \ No newline at end of file diff --git a/firmware/libs/ble_mgmt/CMakeLists.txt b/firmware/libs/ble_mgmt/CMakeLists.txt new file mode 100644 index 0000000..87323c2 --- /dev/null +++ b/firmware/libs/ble_mgmt/CMakeLists.txt @@ -0,0 +1,5 @@ +if(CONFIG_BLE_MGMT) + zephyr_library() + zephyr_sources(src/ble_mgmt.c) + zephyr_include_directories(include) +endif() \ No newline at end of file diff --git a/firmware/libs/ble_mgmt/Kconfig b/firmware/libs/ble_mgmt/Kconfig new file mode 100644 index 0000000..b0dda94 --- /dev/null +++ b/firmware/libs/ble_mgmt/Kconfig @@ -0,0 +1,11 @@ +menuconfig BLE_MGMT + bool "BLE Management" + depends on BT + help + Library for BLE provisioning of the lasertag device. + +if BLE_MGMT + config BLE_MGMT_LOG_LEVEL + int "BLE Management Log Level" + default 3 +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 new file mode 100644 index 0000000..759e6f4 --- /dev/null +++ b/firmware/libs/ble_mgmt/include/ble_mgmt.h @@ -0,0 +1,27 @@ +#ifndef BLE_MGMT_H +#define BLE_MGMT_H + +/** + * @file ble_mgmt.h + * @brief Bluetooth Low Energy management for provisioning. + */ + +/** + * @brief Initialize Bluetooth and prepare services. + * @return 0 on success. + */ +int ble_mgmt_init(void); + +/** + * @brief Start Bluetooth advertising so the web app can find the device. + * @return 0 on success. + */ +int ble_mgmt_adv_start(void); + +/** + * @brief Stop Bluetooth advertising. + * @return 0 on success. + */ +int ble_mgmt_adv_stop(void); + +#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 new file mode 100644 index 0000000..7c77a83 --- /dev/null +++ b/firmware/libs/ble_mgmt/src/ble_mgmt.c @@ -0,0 +1,151 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +LOG_MODULE_REGISTER(ble_mgmt, CONFIG_BLE_MGMT_LOG_LEVEL); + +/** + * Base UUID: 03afe2cf-6c64-4a22-9289-c3ae820cbcxx + * Service ends in 00 to match the Web App filter. + */ +#define LT_UUID_BASE_VAL \ + BT_UUID_128_ENCODE(0x03afe2cf, 0x6c64, 0x4a22, 0x9289, 0xc3ae820cbc00) + +#define BT_UUID_LT_SERVICE BT_UUID_DECLARE_128(LT_UUID_BASE_VAL) +#define BT_UUID_LT_NAME_CHAR BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x03afe2cf, 0x6c64, 0x4a22, 0x9289, 0xc3ae820cbc01)) +#define BT_UUID_LT_PANID_CHAR BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x03afe2cf, 0x6c64, 0x4a22, 0x9289, 0xc3ae820cbc02)) +#define BT_UUID_LT_CHAN_CHAR BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x03afe2cf, 0x6c64, 0x4a22, 0x9289, 0xc3ae820cbc03)) + +/* --- GATT Callbacks --- */ + +static ssize_t read_name(struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset) +{ + const char *name = lasertag_get_device_name(); + return bt_gatt_attr_read(conn, attr, buf, len, offset, name, strlen(name)); +} + +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) +{ + LOG_INF("BLE: New name received (len %d)", len); + return len; +} + +static ssize_t read_panid(struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset) +{ + uint16_t pan_id = lasertag_get_thread_pan_id(); + return bt_gatt_attr_read(conn, attr, buf, len, offset, &pan_id, sizeof(pan_id)); +} + +static ssize_t read_chan(struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset) +{ + uint8_t chan = lasertag_get_thread_channel(); + return bt_gatt_attr_read(conn, attr, buf, len, offset, &chan, sizeof(chan)); +} + +/* Service and Characteristic Definition */ +BT_GATT_SERVICE_DEFINE(provisioning_svc, + BT_GATT_PRIMARY_SERVICE(BT_UUID_LT_SERVICE), + + /* Device Name Characteristic */ + BT_GATT_CHARACTERISTIC(BT_UUID_LT_NAME_CHAR, + BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE, + BT_GATT_PERM_READ | BT_GATT_PERM_WRITE, + read_name, write_name, NULL), + + /* Thread PAN ID Characteristic */ + BT_GATT_CHARACTERISTIC(BT_UUID_LT_PANID_CHAR, + BT_GATT_CHRC_READ, + BT_GATT_PERM_READ, + read_panid, NULL, NULL), + + /* Thread Channel Characteristic */ + BT_GATT_CHARACTERISTIC(BT_UUID_LT_CHAN_CHAR, + BT_GATT_CHRC_READ, + BT_GATT_PERM_READ, + read_chan, NULL, NULL), +); + +/** + * Advertising Data + * * Note: Legacy advertising is limited to 31 bytes. + * Flags: 3 bytes + * UUID128: 18 bytes + * Total: 21 bytes (Fits within limit) + */ +static const struct bt_data ad[] = { + BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)), + /* We put the Service UUID here so the Web App can filter for it */ + BT_DATA_BYTES(BT_DATA_UUID128_ALL, + 0x00, 0xbc, 0x0c, 0x82, 0xae, 0xc3, 0x89, 0x92, + 0x22, 0x4a, 0x64, 0x6c, 0xcf, 0xe2, 0xaf, 0x03), +}; + +int ble_mgmt_init(void) +{ + int err = bt_enable(NULL); + if (err) { + LOG_ERR("Bluetooth init failed (err %d)", err); + return err; + } + + LOG_INF("Bluetooth initialized"); + return 0; +} + +int ble_mgmt_adv_start(void) +{ + const char *name = lasertag_get_device_name(); + + /* Set the stack name */ + bt_set_name(name); + + /** + * Scan Response contains the full complete name. + * This is sent in a separate packet when requested by the scanner. + */ + 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, + .sid = 0, + .secondary_max_skip = 0, + .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, + .peer = NULL, + }; + + int err = bt_le_adv_start(&adv_param, ad, ARRAY_SIZE(ad), + dynamic_sd, ARRAY_SIZE(dynamic_sd)); + if (err) { + LOG_ERR("Advertising failed to start (err %d)", err); + return err; + } + + LOG_INF("Advertising started as: %s", name); + return 0; +} + +int ble_mgmt_adv_stop(void) +{ + int err = bt_le_adv_stop(); + if (err) { + LOG_ERR("Advertising failed to stop (err %d)", err); + return err; + } + + LOG_INF("Advertising stopped"); + return 0; +} \ No newline at end of file diff --git a/firmware/libs/lasertag_utils/Kconfig b/firmware/libs/lasertag_utils/Kconfig index 36266bb..243bd5a 100644 --- a/firmware/libs/lasertag_utils/Kconfig +++ b/firmware/libs/lasertag_utils/Kconfig @@ -1,5 +1,6 @@ menuconfig LASERTAG_UTILS bool "Lasertag Utilities" + depends on BLE_MGMT help Enable shared logic for all lasertag devices (Vest, Weapon, Leader). diff --git a/firmware/libs/lasertag_utils/src/lasertag_utils.c b/firmware/libs/lasertag_utils/src/lasertag_utils.c index 9dc6084..9a1778b 100644 --- a/firmware/libs/lasertag_utils/src/lasertag_utils.c +++ b/firmware/libs/lasertag_utils/src/lasertag_utils.c @@ -6,6 +6,7 @@ #include #include #include +#include LOG_MODULE_REGISTER(lasertag_utils, CONFIG_LASERTAG_UTILS_LOG_LEVEL); @@ -107,6 +108,16 @@ static int lasertag_hex2bin(const char *hex, uint8_t *bin, size_t bin_len) { return 0; } +static int cmd_ble_start(const struct shell *sh, size_t argc, char **argv) { + shell_print(sh, "Starting BLE Advertising..."); + return ble_mgmt_adv_start(); +} + +static int cmd_ble_stop(const struct shell *sh, size_t argc, char **argv) { + shell_print(sh, "Stopping BLE Advertising..."); + return ble_mgmt_adv_stop(); +} + static int cmd_thread_set_chan(const struct shell *sh, size_t argc, char **argv) { thread_channel = (uint8_t)strtoul(argv[1], NULL, 10); settings_save_one("lasertag/channel", &thread_channel, sizeof(thread_channel)); @@ -138,6 +149,14 @@ static int cmd_name_set(const struct shell *sh, size_t argc, char **argv) { return 0; } +static int cmd_reboot(const struct shell *sh, size_t argc, char **argv) { + ARG_UNUSED(argc); + ARG_UNUSED(argv); + shell_print(sh, "Rebooting..."); + sys_reboot(SYS_REBOOT_COLD); + return 0; +} + /* Subcommands for 'lasertag thread' */ SHELL_STATIC_SUBCMD_SET_CREATE(sub_thread, SHELL_CMD_ARG(panid, NULL, "Set PAN ID ", NULL, 2, 0), @@ -148,9 +167,18 @@ SHELL_STATIC_SUBCMD_SET_CREATE(sub_thread, SHELL_SUBCMD_SET_END ); +SHELL_STATIC_SUBCMD_SET_CREATE(sub_ble, + SHELL_CMD(start, NULL, "Start BLE advertising", cmd_ble_start), + SHELL_CMD(stop, NULL, "Stop BLE advertising", cmd_ble_stop), + SHELL_SUBCMD_SET_END +); + +/* Main command 'lasertag' with thread + ble helpers */ SHELL_STATIC_SUBCMD_SET_CREATE(sub_lasertag, SHELL_CMD_ARG(name, NULL, "Set device name ", cmd_name_set, 2, 0), SHELL_CMD(thread, &sub_thread, "Thread network configuration", NULL), + SHELL_CMD(ble, &sub_ble, "Bluetooth management", NULL), + SHELL_CMD(reboot, NULL, "Reboot the device", cmd_reboot), SHELL_SUBCMD_SET_END ); diff --git a/software/provisioning/index.html b/software/provisioning/index.html new file mode 100644 index 0000000..357bd65 --- /dev/null +++ b/software/provisioning/index.html @@ -0,0 +1,66 @@ + + + + + + Lasertag Provisioning + + + +
+

Lasertag Setup

+

Connect to your device via Bluetooth to configure Thread settings.

+ +
Ready to connect...
+ +
+ Connected to:
+

Next step: Implement Characteristic Write/Read.

+
+
+ + + + \ No newline at end of file