BLE Advertising working with custom UUID
All checks were successful
Deploy Docs / build-and-deploy (push) Successful in 25s
All checks were successful
Deploy Docs / build-and-deploy (push) Successful in 25s
This commit is contained in:
@@ -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
|
||||
CONFIG_THREAD_MGMT=y
|
||||
CONFIG_BLE_MGMT=y
|
||||
@@ -2,6 +2,7 @@
|
||||
#include <zephyr/logging/log.h>
|
||||
#include <lasertag_utils.h>
|
||||
#include <thread_mgmt.h>
|
||||
#include <ble_mgmt.h>
|
||||
|
||||
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) {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# Add library subdirectories
|
||||
add_subdirectory(lasertag_utils)
|
||||
add_subdirectory(thread_mgmt)
|
||||
# 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)
|
||||
@@ -1,3 +1,4 @@
|
||||
# Main entry point for custom project Kconfigs
|
||||
rsource "lasertag_utils/Kconfig"
|
||||
rsource "thread_mgmt/Kconfig"
|
||||
rsource "thread_mgmt/Kconfig"
|
||||
rsource "ble_mgmt/Kconfig"
|
||||
5
firmware/libs/ble_mgmt/CMakeLists.txt
Normal file
5
firmware/libs/ble_mgmt/CMakeLists.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
if(CONFIG_BLE_MGMT)
|
||||
zephyr_library()
|
||||
zephyr_sources(src/ble_mgmt.c)
|
||||
zephyr_include_directories(include)
|
||||
endif()
|
||||
11
firmware/libs/ble_mgmt/Kconfig
Normal file
11
firmware/libs/ble_mgmt/Kconfig
Normal file
@@ -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
|
||||
27
firmware/libs/ble_mgmt/include/ble_mgmt.h
Normal file
27
firmware/libs/ble_mgmt/include/ble_mgmt.h
Normal file
@@ -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 */
|
||||
151
firmware/libs/ble_mgmt/src/ble_mgmt.c
Normal file
151
firmware/libs/ble_mgmt/src/ble_mgmt.c
Normal file
@@ -0,0 +1,151 @@
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
#include <zephyr/bluetooth/bluetooth.h>
|
||||
#include <zephyr/bluetooth/hci.h>
|
||||
#include <zephyr/bluetooth/conn.h>
|
||||
#include <zephyr/bluetooth/uuid.h>
|
||||
#include <zephyr/bluetooth/gatt.h>
|
||||
#include <lasertag_utils.h>
|
||||
#include <ble_mgmt.h>
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -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).
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <lasertag_utils.h>
|
||||
#include <ble_mgmt.h>
|
||||
|
||||
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 <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 <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
|
||||
);
|
||||
|
||||
|
||||
66
software/provisioning/index.html
Normal file
66
software/provisioning/index.html
Normal file
@@ -0,0 +1,66 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Lasertag Provisioning</title>
|
||||
<style>
|
||||
body { font-family: sans-serif; display: flex; flex-direction: column; align-items: center; padding: 2rem; background: #f0f2f5; }
|
||||
.card { background: white; padding: 2rem; border-radius: 12px; box-shadow: 0 4px 6px rgba(0,0,0,0.1); width: 100%; max-width: 400px; }
|
||||
button { background: #007bff; color: white; border: none; padding: 12px 24px; border-radius: 6px; cursor: pointer; font-size: 1rem; width: 100%; }
|
||||
button:disabled { background: #ccc; }
|
||||
.status { margin-top: 1rem; color: #666; font-size: 0.9rem; }
|
||||
.device-info { margin-top: 1.5rem; padding-top: 1.5rem; border-top: 1px solid #eee; display: none; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="card">
|
||||
<h1>Lasertag Setup</h1>
|
||||
<p>Connect to your device via Bluetooth to configure Thread settings.</p>
|
||||
<button id="connectBtn">Search for Device</button>
|
||||
<div class="status" id="status">Ready to connect...</div>
|
||||
|
||||
<div class="device-info" id="deviceInfo">
|
||||
<strong>Connected to:</strong> <span id="connectedName"></span><br>
|
||||
<p>Next step: Implement Characteristic Write/Read.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const connectBtn = document.getElementById('connectBtn');
|
||||
const status = document.getElementById('status');
|
||||
const deviceInfo = document.getElementById('deviceInfo');
|
||||
const connectedName = document.getElementById('connectedName');
|
||||
|
||||
const PROVISIONING_SERVICE_UUID = '03afe2cf-6c64-4a22-9289-c3ae820cbc00';
|
||||
|
||||
connectBtn.addEventListener('click', async () => {
|
||||
try {
|
||||
status.textContent = 'Requesting Bluetooth Device...';
|
||||
const device = await navigator.bluetooth.requestDevice({
|
||||
filters: [{ services: [PROVISIONING_SERVICE_UUID] }],
|
||||
optionalServices: [PROVISIONING_SERVICE_UUID]
|
||||
});
|
||||
|
||||
status.textContent = 'Connecting to GATT Server...';
|
||||
const server = await device.gatt.connect();
|
||||
|
||||
status.textContent = 'Connected!';
|
||||
connectedName.textContent = device.name || 'Unnamed Device';
|
||||
deviceInfo.style.display = 'block';
|
||||
connectBtn.disabled = true;
|
||||
|
||||
device.addEventListener('gattserverdisconnected', () => {
|
||||
status.textContent = 'Disconnected.';
|
||||
deviceInfo.style.display = 'none';
|
||||
connectBtn.disabled = false;
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
status.textContent = 'Error: ' + error.message;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user