Merge PR#1243 (Split battery reporting over BLE GATT)
This commit is contained in:
commit
fa2eee585f
8 changed files with 274 additions and 15 deletions
17
.vscode/c_cpp_properties.json
vendored
Normal file
17
.vscode/c_cpp_properties.json
vendored
Normal file
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Linux",
|
||||
"includePath": [
|
||||
"${workspaceFolder}/**"
|
||||
],
|
||||
"defines": [],
|
||||
"compilerPath": "/usr/bin/gcc",
|
||||
"cStandard": "gnu17",
|
||||
"cppStandard": "c++17",
|
||||
"intelliSenseMode": "linux-gcc-arm",
|
||||
"compileCommands": "${workspaceFolder}/app/build/compile_commands.json"
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
|
@ -72,11 +72,18 @@ if ((NOT CONFIG_ZMK_SPLIT) OR CONFIG_ZMK_SPLIT_ROLE_CENTRAL)
|
|||
endif()
|
||||
endif()
|
||||
|
||||
if (CONFIG_ZMK_BLE)
|
||||
if (CONFIG_ZMK_SPLIT_ROLE_CENTRAL)
|
||||
target_sources(app PRIVATE src/battery_split.c)
|
||||
else()
|
||||
target_sources(app PRIVATE src/battery.c)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
target_sources_ifdef(CONFIG_ZMK_RGB_UNDERGLOW app PRIVATE src/behaviors/behavior_rgb_underglow.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_BACKLIGHT app PRIVATE src/behaviors/behavior_backlight.c)
|
||||
|
||||
target_sources_ifdef(CONFIG_ZMK_BLE app PRIVATE src/events/battery_state_changed.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_BLE app PRIVATE src/battery.c)
|
||||
|
||||
target_sources_ifdef(CONFIG_ZMK_SPLIT app PRIVATE src/events/split_peripheral_status_changed.c)
|
||||
add_subdirectory(src/split)
|
||||
|
|
|
@ -114,7 +114,7 @@ menuconfig ZMK_BLE
|
|||
select BT_SMP_APP_PAIRING_ACCEPT
|
||||
select BT_PERIPHERAL
|
||||
select BT_DIS
|
||||
select BT_BAS
|
||||
select BT_BAS if !ZMK_SPLIT_ROLE_CENTRAL
|
||||
select BT_SETTINGS
|
||||
select SETTINGS
|
||||
|
||||
|
|
|
@ -14,4 +14,11 @@ struct zmk_battery_state_changed {
|
|||
uint8_t state_of_charge;
|
||||
};
|
||||
|
||||
ZMK_EVENT_DECLARE(zmk_battery_state_changed);
|
||||
ZMK_EVENT_DECLARE(zmk_battery_state_changed);
|
||||
|
||||
struct zmk_peripheral_battery_state_changed {
|
||||
// TODO: Other battery channels
|
||||
uint8_t state_of_charge;
|
||||
};
|
||||
|
||||
ZMK_EVENT_DECLARE(zmk_peripheral_battery_state_changed);
|
145
app/src/battery_split.c
Normal file
145
app/src/battery_split.c
Normal file
|
@ -0,0 +1,145 @@
|
|||
/*
|
||||
* Copyright (c) 2020 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <device.h>
|
||||
#include <init.h>
|
||||
#include <sys/types.h>
|
||||
#include <kernel.h>
|
||||
#include <drivers/sensor.h>
|
||||
#include <bluetooth/gatt.h>
|
||||
|
||||
#include <logging/log.h>
|
||||
|
||||
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||
|
||||
#include <zmk/event_manager.h>
|
||||
#include <zmk/battery.h>
|
||||
#include <zmk/events/battery_state_changed.h>
|
||||
|
||||
const uint8_t NULL_BATTERY_LEVEL = 0xFF;
|
||||
|
||||
// Initialize the charge level to a special value indicating no sampling has been made yet.
|
||||
static uint8_t last_state_of_charge = NULL_BATTERY_LEVEL;
|
||||
static uint8_t last_state_of_peripheral_charge = NULL_BATTERY_LEVEL;
|
||||
|
||||
static void blvl_ccc_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value) {
|
||||
ARG_UNUSED(attr);
|
||||
|
||||
bool notif_enabled = (value == BT_GATT_CCC_NOTIFY);
|
||||
|
||||
LOG_INF("BAS Notifications %s", notif_enabled ? "enabled" : "disabled");
|
||||
}
|
||||
|
||||
static ssize_t read_blvl(struct bt_conn *conn, const struct bt_gatt_attr *attr, void *buf,
|
||||
uint16_t len, uint16_t offset) {
|
||||
const char *lvl8 = attr->user_data;
|
||||
return bt_gatt_attr_read(conn, attr, buf, len, offset, lvl8, sizeof(uint8_t));
|
||||
}
|
||||
|
||||
BT_GATT_SERVICE_DEFINE(
|
||||
bas, BT_GATT_PRIMARY_SERVICE(BT_UUID_BAS),
|
||||
BT_GATT_CHARACTERISTIC(BT_UUID_BAS_BATTERY_LEVEL, BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY,
|
||||
BT_GATT_PERM_READ, read_blvl, NULL, &last_state_of_charge),
|
||||
BT_GATT_CCC(blvl_ccc_cfg_changed, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),
|
||||
BT_GATT_CUD("Central", BT_GATT_PERM_READ),
|
||||
BT_GATT_CHARACTERISTIC(BT_UUID_BAS_BATTERY_LEVEL, BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY,
|
||||
BT_GATT_PERM_READ, read_blvl, NULL, &last_state_of_peripheral_charge),
|
||||
BT_GATT_CCC(blvl_ccc_cfg_changed, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),
|
||||
BT_GATT_CUD("Peripheral", BT_GATT_PERM_READ));
|
||||
|
||||
const struct device *battery;
|
||||
|
||||
int peripheral_batt_lvl_listener(const zmk_event_t *eh) {
|
||||
const struct zmk_peripheral_battery_state_changed *ev =
|
||||
as_zmk_peripheral_battery_state_changed(eh);
|
||||
if (ev == NULL) {
|
||||
return ZMK_EV_EVENT_BUBBLE;
|
||||
};
|
||||
LOG_DBG("Peripheral battery level event: %u", ev->state_of_charge);
|
||||
last_state_of_peripheral_charge = ev->state_of_charge;
|
||||
|
||||
// TODO: super fragile because of hardcoded attribute index
|
||||
int rc = bt_gatt_notify(NULL, &bas.attrs[5], &last_state_of_peripheral_charge,
|
||||
sizeof(last_state_of_peripheral_charge));
|
||||
return rc;
|
||||
};
|
||||
|
||||
ZMK_LISTENER(peripheral_batt_lvl_listener, peripheral_batt_lvl_listener);
|
||||
ZMK_SUBSCRIPTION(peripheral_batt_lvl_listener, zmk_peripheral_battery_state_changed);
|
||||
|
||||
uint8_t zmk_battery_state_of_charge() { return last_state_of_charge; }
|
||||
|
||||
static int zmk_battery_update(const struct device *battery) {
|
||||
struct sensor_value state_of_charge;
|
||||
|
||||
int rc = sensor_sample_fetch_chan(battery, SENSOR_CHAN_GAUGE_STATE_OF_CHARGE);
|
||||
|
||||
if (rc != 0) {
|
||||
LOG_DBG("Failed to fetch battery values: %d", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = sensor_channel_get(battery, SENSOR_CHAN_GAUGE_STATE_OF_CHARGE, &state_of_charge);
|
||||
|
||||
if (rc != 0) {
|
||||
LOG_DBG("Failed to get battery state of charge: %d", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (last_state_of_charge != state_of_charge.val1) {
|
||||
last_state_of_charge = state_of_charge.val1;
|
||||
|
||||
LOG_DBG("Setting BAS GATT battery level to %d.", last_state_of_charge);
|
||||
|
||||
rc = bt_gatt_notify(NULL, &bas.attrs[1], &last_state_of_charge,
|
||||
sizeof(last_state_of_charge));
|
||||
if (rc != 0 && rc != -ENOTCONN) {
|
||||
LOG_WRN("Failed to set BAS GATT battery level (err %d)", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = ZMK_EVENT_RAISE(new_zmk_battery_state_changed(
|
||||
(struct zmk_battery_state_changed){.state_of_charge = last_state_of_charge}));
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void zmk_battery_work(struct k_work *work) {
|
||||
int rc = zmk_battery_update(battery);
|
||||
|
||||
if (rc != 0) {
|
||||
LOG_DBG("Failed to update battery value: %d.", rc);
|
||||
}
|
||||
}
|
||||
|
||||
K_WORK_DEFINE(battery_work, zmk_battery_work);
|
||||
|
||||
static void zmk_battery_timer(struct k_timer *timer) { k_work_submit(&battery_work); }
|
||||
|
||||
K_TIMER_DEFINE(battery_timer, zmk_battery_timer, NULL);
|
||||
|
||||
static int zmk_battery_init(const struct device *_arg) {
|
||||
battery = device_get_binding("BATTERY");
|
||||
|
||||
if (battery == NULL) {
|
||||
LOG_DBG("No battery device labelled BATTERY found.");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
int rc = zmk_battery_update(battery);
|
||||
|
||||
if (rc != 0) {
|
||||
LOG_DBG("Failed to update battery value: %d.", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
k_timer_start(&battery_timer, K_MINUTES(1), K_MINUTES(1));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
SYS_INIT(zmk_battery_init, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY);
|
|
@ -58,8 +58,9 @@ void battery_status_update_cb(struct battery_status_state state) {
|
|||
}
|
||||
|
||||
static struct battery_status_state battery_status_get_state(const zmk_event_t *eh) {
|
||||
const struct zmk_battery_state_changed *ev = as_zmk_battery_state_changed(eh);
|
||||
return (struct battery_status_state) {
|
||||
.level = bt_bas_get_battery_level(),
|
||||
.level = ev->state_of_charge,
|
||||
#if IS_ENABLED(CONFIG_USB_DEVICE_STACK)
|
||||
.usb_present = zmk_usb_is_powered(),
|
||||
#endif /* IS_ENABLED(CONFIG_USB_DEVICE_STACK) */
|
||||
|
|
|
@ -7,4 +7,6 @@
|
|||
#include <kernel.h>
|
||||
#include <zmk/events/battery_state_changed.h>
|
||||
|
||||
ZMK_EVENT_IMPL(zmk_battery_state_changed);
|
||||
ZMK_EVENT_IMPL(zmk_battery_state_changed);
|
||||
|
||||
ZMK_EVENT_IMPL(zmk_peripheral_battery_state_changed);
|
|
@ -24,6 +24,7 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
|||
#include <zmk/split/bluetooth/service.h>
|
||||
#include <zmk/event_manager.h>
|
||||
#include <zmk/events/position_state_changed.h>
|
||||
#include <zmk/events/battery_state_changed.h>
|
||||
#include <init.h>
|
||||
|
||||
static int start_scan(void);
|
||||
|
@ -41,6 +42,8 @@ struct peripheral_slot {
|
|||
struct bt_conn *conn;
|
||||
struct bt_gatt_discover_params discover_params;
|
||||
struct bt_gatt_subscribe_params subscribe_params;
|
||||
struct bt_gatt_subscribe_params batt_lvl_subscribe_params;
|
||||
struct bt_gatt_read_params batt_lvl_read_params;
|
||||
struct bt_gatt_discover_params sub_discover_params;
|
||||
uint16_t run_behavior_handle;
|
||||
uint8_t position_state[POSITION_STATE_DATA_LEN];
|
||||
|
@ -54,6 +57,9 @@ static const struct bt_uuid_128 split_service_uuid = BT_UUID_INIT_128(ZMK_SPLIT_
|
|||
K_MSGQ_DEFINE(peripheral_event_msgq, sizeof(struct zmk_position_state_changed),
|
||||
CONFIG_ZMK_SPLIT_BLE_CENTRAL_POSITION_QUEUE_SIZE, 4);
|
||||
|
||||
K_MSGQ_DEFINE(peripheral_batt_lvl_msgq, sizeof(struct zmk_peripheral_battery_state_changed),
|
||||
CONFIG_ZMK_SPLIT_BLE_CENTRAL_POSITION_QUEUE_SIZE, 4);
|
||||
|
||||
void peripheral_event_work_callback(struct k_work *work) {
|
||||
struct zmk_position_state_changed ev;
|
||||
while (k_msgq_get(&peripheral_event_msgq, &ev, K_NO_WAIT) == 0) {
|
||||
|
@ -206,14 +212,77 @@ static uint8_t split_central_notify_func(struct bt_conn *conn,
|
|||
return BT_GATT_ITER_CONTINUE;
|
||||
}
|
||||
|
||||
static void split_central_subscribe(struct bt_conn *conn) {
|
||||
void peripheral_batt_lvl_change_callback(struct k_work *work) {
|
||||
struct zmk_peripheral_battery_state_changed ev;
|
||||
while (k_msgq_get(&peripheral_batt_lvl_msgq, &ev, K_NO_WAIT) == 0) {
|
||||
LOG_DBG("Triggering peripheral battery level change %u", ev.state_of_charge);
|
||||
ZMK_EVENT_RAISE(new_zmk_peripheral_battery_state_changed(ev));
|
||||
}
|
||||
}
|
||||
|
||||
K_WORK_DEFINE(peripheral_batt_lvl_work, peripheral_batt_lvl_change_callback);
|
||||
|
||||
static uint8_t split_central_battery_level_notify_func(struct bt_conn *conn,
|
||||
struct bt_gatt_subscribe_params *params,
|
||||
const void *data, uint16_t length) {
|
||||
struct peripheral_slot *slot = peripheral_slot_for_conn(conn);
|
||||
|
||||
if (slot == NULL) {
|
||||
LOG_ERR("No peripheral state found for connection");
|
||||
return;
|
||||
return BT_GATT_ITER_CONTINUE;
|
||||
}
|
||||
|
||||
int err = bt_gatt_subscribe(conn, &slot->subscribe_params);
|
||||
if (!data) {
|
||||
LOG_DBG("[UNSUBSCRIBED]");
|
||||
params->value_handle = 0U;
|
||||
return BT_GATT_ITER_STOP;
|
||||
}
|
||||
|
||||
LOG_DBG("[BATTERY LEVEL NOTIFICATION] data %p length %u", data, length);
|
||||
uint8_t battery_level = ((uint8_t *)data)[0];
|
||||
LOG_DBG("Battery level: %u", battery_level);
|
||||
struct zmk_peripheral_battery_state_changed ev = {.state_of_charge = battery_level};
|
||||
k_msgq_put(&peripheral_batt_lvl_msgq, &ev, K_NO_WAIT);
|
||||
k_work_submit(&peripheral_batt_lvl_work);
|
||||
|
||||
return BT_GATT_ITER_CONTINUE;
|
||||
}
|
||||
static uint8_t split_central_battery_level_read_func(struct bt_conn *conn, uint8_t err,
|
||||
struct bt_gatt_read_params *params,
|
||||
const void *data, uint16_t length) {
|
||||
if (err > 0) {
|
||||
LOG_ERR("Error during reading peripheral battery level: %u", err);
|
||||
return BT_GATT_ITER_STOP;
|
||||
}
|
||||
|
||||
struct peripheral_slot *slot = peripheral_slot_for_conn(conn);
|
||||
|
||||
if (slot == NULL) {
|
||||
LOG_ERR("No peripheral state found for connection");
|
||||
return BT_GATT_ITER_CONTINUE;
|
||||
}
|
||||
|
||||
if (!data) {
|
||||
LOG_DBG("[READ COMPLETED]");
|
||||
return BT_GATT_ITER_STOP;
|
||||
}
|
||||
|
||||
LOG_DBG("[BATTERY LEVEL READ] data %p length %u", data, length);
|
||||
|
||||
uint8_t battery_level = ((uint8_t *)data)[0];
|
||||
|
||||
LOG_DBG("Battery level: %u", battery_level);
|
||||
|
||||
struct zmk_peripheral_battery_state_changed ev = {.state_of_charge = battery_level};
|
||||
k_msgq_put(&peripheral_batt_lvl_msgq, &ev, K_NO_WAIT);
|
||||
k_work_submit(&peripheral_batt_lvl_work);
|
||||
|
||||
return BT_GATT_ITER_CONTINUE;
|
||||
}
|
||||
|
||||
static void split_central_subscribe(struct bt_conn *conn,
|
||||
struct bt_gatt_subscribe_params *subscribe_params) {
|
||||
int err = bt_gatt_subscribe(conn, subscribe_params);
|
||||
switch (err) {
|
||||
case -EALREADY:
|
||||
LOG_DBG("[ALREADY SUBSCRIBED]");
|
||||
|
@ -251,23 +320,35 @@ static uint8_t split_central_chrc_discovery_func(struct bt_conn *conn,
|
|||
if (!bt_uuid_cmp(((struct bt_gatt_chrc *)attr->user_data)->uuid,
|
||||
BT_UUID_DECLARE_128(ZMK_SPLIT_BT_CHAR_POSITION_STATE_UUID))) {
|
||||
LOG_DBG("Found position state characteristic");
|
||||
slot->discover_params.uuid = NULL;
|
||||
slot->discover_params.start_handle = attr->handle + 2;
|
||||
slot->discover_params.type = BT_GATT_DISCOVER_CHARACTERISTIC;
|
||||
|
||||
slot->subscribe_params.disc_params = &slot->sub_discover_params;
|
||||
slot->subscribe_params.end_handle = slot->discover_params.end_handle;
|
||||
slot->subscribe_params.value_handle = bt_gatt_attr_value_handle(attr);
|
||||
slot->subscribe_params.notify = split_central_notify_func;
|
||||
slot->subscribe_params.value = BT_GATT_CCC_NOTIFY;
|
||||
split_central_subscribe(conn);
|
||||
split_central_subscribe(conn, &slot->subscribe_params);
|
||||
} else if (!bt_uuid_cmp(((struct bt_gatt_chrc *)attr->user_data)->uuid,
|
||||
BT_UUID_DECLARE_128(ZMK_SPLIT_BT_CHAR_RUN_BEHAVIOR_UUID))) {
|
||||
LOG_DBG("Found run behavior handle");
|
||||
slot->run_behavior_handle = bt_gatt_attr_value_handle(attr);
|
||||
} else if (!bt_uuid_cmp(((struct bt_gatt_chrc *)attr->user_data)->uuid,
|
||||
BT_UUID_BAS_BATTERY_LEVEL)) {
|
||||
LOG_DBG("Found battery level characteristics");
|
||||
slot->batt_lvl_subscribe_params.disc_params = &slot->sub_discover_params;
|
||||
slot->batt_lvl_subscribe_params.end_handle = slot->discover_params.end_handle;
|
||||
slot->batt_lvl_subscribe_params.value_handle = bt_gatt_attr_value_handle(attr);
|
||||
slot->batt_lvl_subscribe_params.notify = split_central_battery_level_notify_func;
|
||||
slot->batt_lvl_subscribe_params.value = BT_GATT_CCC_NOTIFY;
|
||||
split_central_subscribe(conn, &slot->batt_lvl_subscribe_params);
|
||||
|
||||
slot->batt_lvl_read_params.func = split_central_battery_level_read_func;
|
||||
slot->batt_lvl_read_params.handle_count = 1;
|
||||
slot->batt_lvl_read_params.single.handle = bt_gatt_attr_value_handle(attr);
|
||||
slot->batt_lvl_read_params.single.offset = 0;
|
||||
bt_gatt_read(conn, &slot->batt_lvl_read_params);
|
||||
}
|
||||
|
||||
bool subscribed = (slot->run_behavior_handle && slot->subscribe_params.value_handle);
|
||||
bool subscribed = (slot->run_behavior_handle && slot->subscribe_params.value_handle &&
|
||||
slot->batt_lvl_subscribe_params.value_handle);
|
||||
|
||||
return subscribed ? BT_GATT_ITER_STOP : BT_GATT_ITER_CONTINUE;
|
||||
}
|
||||
|
@ -297,7 +378,6 @@ static uint8_t split_central_service_discovery_func(struct bt_conn *conn,
|
|||
LOG_DBG("Found split service");
|
||||
slot->discover_params.uuid = NULL;
|
||||
slot->discover_params.func = split_central_chrc_discovery_func;
|
||||
slot->discover_params.start_handle = attr->handle + 1;
|
||||
slot->discover_params.type = BT_GATT_DISCOVER_CHARACTERISTIC;
|
||||
|
||||
int err = bt_gatt_discover(conn, &slot->discover_params);
|
||||
|
|
Loading…
Add table
Reference in a new issue