diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index b760389f..83d1f120 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -69,13 +69,14 @@ target_sources_ifdef(CONFIG_ZMK_RGB_UNDERGLOW app PRIVATE src/behaviors/behavior target_sources_ifdef(CONFIG_ZMK_BACKLIGHT app PRIVATE src/behaviors/behavior_backlight.c) target_sources_ifdef(CONFIG_ZMK_BLE app PRIVATE src/behaviors/behavior_bt.c) target_sources_ifdef(CONFIG_ZMK_BLE app PRIVATE src/ble.c) -target_sources_ifdef(CONFIG_ZMK_BLE app PRIVATE src/battery.c) if (CONFIG_ZMK_SPLIT_BLE AND (NOT CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL)) target_sources(app PRIVATE src/split_listener.c) target_sources(app PRIVATE src/split/bluetooth/service.c) + target_sources(app PRIVATE src/battery.c) endif() if (CONFIG_ZMK_SPLIT_BLE AND CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL) target_sources(app PRIVATE src/split/bluetooth/central.c) + target_sources(app PRIVATE src/battery_split.c) endif() target_sources_ifdef(CONFIG_USB_DEVICE_STACK app PRIVATE src/usb.c) target_sources_ifdef(CONFIG_ZMK_BLE app PRIVATE src/hog.c) diff --git a/app/include/zmk/events/battery_state_changed.h b/app/include/zmk/events/battery_state_changed.h index 6a003d8d..336faf09 100644 --- a/app/include/zmk/events/battery_state_changed.h +++ b/app/include/zmk/events/battery_state_changed.h @@ -14,4 +14,11 @@ struct zmk_battery_state_changed { uint8_t state_of_charge; }; -ZMK_EVENT_DECLARE(zmk_battery_state_changed); \ No newline at end of file +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); \ No newline at end of file diff --git a/app/src/battery_split.c b/app/src/battery_split.c new file mode 100644 index 00000000..271867d1 --- /dev/null +++ b/app/src/battery_split.c @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include +#include +#include +#include +#include +#include + +#include + +LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); + +#include +#include +#include + +static uint8_t last_state_of_charge = 0; +static uint8_t last_state_of_peripheral_charge = 41; + +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) +{ + uint8_t lvl8 = last_state_of_charge; + return bt_gatt_attr_read(conn, attr, buf, len, offset, &lvl8, + sizeof(lvl8)); +} + +static ssize_t read_peripheral_blvl(struct bt_conn *conn, + const struct bt_gatt_attr *attr, void *buf, + uint16_t len, uint16_t offset) +{ + uint8_t lvl8 = last_state_of_peripheral_charge; + return bt_gatt_attr_read(conn, attr, buf, len, offset, &lvl8, + sizeof(lvl8)); +} + +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_CHARACTERISTIC(BT_UUID_BAS_BATTERY_LEVEL, + BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY, + BT_GATT_PERM_READ, read_peripheral_blvl, NULL, + &last_state_of_peripheral_charge), + BT_GATT_CCC(blvl_ccc_cfg_changed, + BT_GATT_PERM_READ | BT_GATT_PERM_WRITE) +); + +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); + LOG_DBG("Peripheral battery level event: %u", ev->state_of_charge); + last_state_of_peripheral_charge = ev->state_of_charge; + int rc = bt_gatt_notify(NULL, &bas.attrs[3], &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)); + rc = rc == -ENOTCONN ? 0 : rc; + if (rc != 0) { + 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); diff --git a/app/src/events/battery_state_changed.c b/app/src/events/battery_state_changed.c index 435fb24d..be3a8699 100644 --- a/app/src/events/battery_state_changed.c +++ b/app/src/events/battery_state_changed.c @@ -7,4 +7,6 @@ #include #include -ZMK_EVENT_IMPL(zmk_battery_state_changed); \ No newline at end of file +ZMK_EVENT_IMPL(zmk_battery_state_changed); + +ZMK_EVENT_IMPL(zmk_peripheral_battery_state_changed); \ No newline at end of file diff --git a/app/src/split/bluetooth/central.c b/app/src/split/bluetooth/central.c index 0b171349..6f622d17 100644 --- a/app/src/split/bluetooth/central.c +++ b/app/src/split/bluetooth/central.c @@ -24,6 +24,7 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); #include #include #include +#include #include static int start_scan(void); @@ -55,6 +56,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); + int peripheral_slot_index_for_conn(struct bt_conn *conn) { for (int i = 0; i < ZMK_BLE_SPLIT_PERIPHERAL_COUNT; i++) { if (peripherals[i].conn == conn) { @@ -191,6 +195,16 @@ static uint8_t split_central_notify_func(struct bt_conn *conn, return BT_GATT_ITER_CONTINUE; } +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) { @@ -209,6 +223,11 @@ static uint8_t split_central_battery_level_notify_func(struct bt_conn *conn, LOG_DBG("[BATTERY LEVEL NOTIFICATION] data %p length %u", data, length); uint8_t battery_level = ((uint8_t *)data)[0]; + 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); LOG_DBG("Battery level: %u", battery_level); return BT_GATT_ITER_CONTINUE;