feature(split): add support for sensors from peripheral

This commit adds a new GATT characteristics on the peripheral side
and wires it up to read sensor values. The central side subscribes
to this new characteristics and replays sensor values on its side.

Co-authored-by: Peter Johanson <peter@peterjohanson.com>
This commit is contained in:
Stephen Wan
2021-03-15 12:26:18 -07:00
committed by Pete Johanson
parent c957348e61
commit 9d44229800
7 changed files with 196 additions and 28 deletions

View File

@@ -4,6 +4,7 @@
* SPDX-License-Identifier: MIT
*/
#include <zephyr/drivers/sensor.h>
#include <zephyr/types.h>
#include <zephyr/sys/util.h>
#include <zephyr/init.h>
@@ -20,6 +21,22 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
#include <zmk/matrix.h>
#include <zmk/split/bluetooth/uuid.h>
#include <zmk/split/bluetooth/service.h>
#include <zmk/events/sensor_event.h>
#include <zmk/sensors.h>
#if ZMK_KEYMAP_HAS_SENSORS
static struct sensor_event last_sensor_event;
static ssize_t split_svc_sensor_state(struct bt_conn *conn, const struct bt_gatt_attr *attrs,
void *buf, uint16_t len, uint16_t offset) {
return bt_gatt_attr_read(conn, attrs, buf, len, offset, &last_sensor_event,
sizeof(last_sensor_event));
}
static void split_svc_sensor_state_ccc(const struct bt_gatt_attr *attr, uint16_t value) {
LOG_DBG("value %d", value);
}
#endif /* ZMK_KEYMAP_HAS_SENSORS */
#define POS_STATE_LEN 16
@@ -98,7 +115,14 @@ BT_GATT_SERVICE_DEFINE(
BT_GATT_CHRC_WRITE_WITHOUT_RESP, BT_GATT_PERM_WRITE_ENCRYPT, NULL,
split_svc_run_behavior, &behavior_run_payload),
BT_GATT_DESCRIPTOR(BT_UUID_NUM_OF_DIGITALS, BT_GATT_PERM_READ, split_svc_num_of_positions, NULL,
&num_of_positions), );
&num_of_positions),
#if ZMK_KEYMAP_HAS_SENSORS
BT_GATT_CHARACTERISTIC(BT_UUID_DECLARE_128(ZMK_SPLIT_BT_CHAR_SENSOR_STATE_UUID),
BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY, BT_GATT_PERM_READ_ENCRYPT,
split_svc_sensor_state, NULL, &last_sensor_event),
BT_GATT_CCC(split_svc_sensor_state_ccc, BT_GATT_PERM_READ_ENCRYPT | BT_GATT_PERM_WRITE_ENCRYPT),
#endif /* ZMK_KEYMAP_HAS_SENSORS */
);
K_THREAD_STACK_DEFINE(service_q_stack, CONFIG_ZMK_SPLIT_BLE_PERIPHERAL_STACK_SIZE);
@@ -151,6 +175,58 @@ int zmk_split_bt_position_released(uint8_t position) {
return send_position_state();
}
#if ZMK_KEYMAP_HAS_SENSORS
K_MSGQ_DEFINE(sensor_state_msgq, sizeof(struct sensor_event),
CONFIG_ZMK_SPLIT_BLE_PERIPHERAL_POSITION_QUEUE_SIZE, 4);
void send_sensor_state_callback(struct k_work *work) {
while (k_msgq_get(&sensor_state_msgq, &last_sensor_event, K_NO_WAIT) == 0) {
int err = bt_gatt_notify(NULL, &split_svc.attrs[8], &last_sensor_event,
sizeof(last_sensor_event));
if (err) {
LOG_DBG("Error notifying %d", err);
}
}
};
K_WORK_DEFINE(service_sensor_notify_work, send_sensor_state_callback);
int send_sensor_state(struct sensor_event ev) {
int err = k_msgq_put(&sensor_state_msgq, &ev, K_MSEC(100));
if (err) {
// retry...
switch (err) {
case -EAGAIN: {
LOG_WRN("Sensor state message queue full, popping first message and queueing again");
struct sensor_event discarded_state;
k_msgq_get(&sensor_state_msgq, &discarded_state, K_NO_WAIT);
return send_sensor_state(ev);
}
default:
LOG_WRN("Failed to queue sensor state to send (%d)", err);
return err;
}
}
k_work_submit_to_queue(&service_work_q, &service_sensor_notify_work);
return 0;
}
int zmk_split_bt_sensor_triggered(uint8_t sensor_index,
const struct zmk_sensor_channel_data channel_data[],
size_t channel_data_size) {
if (channel_data_size > ZMK_SENSOR_EVENT_MAX_CHANNELS) {
return -EINVAL;
}
struct sensor_event ev =
(struct sensor_event){.sensor_index = sensor_index, .channel_data_size = channel_data_size};
memcpy(ev.channel_data, channel_data,
channel_data_size * sizeof(struct zmk_sensor_channel_data));
return send_sensor_state(ev);
}
#endif /* ZMK_KEYMAP_HAS_SENSORS */
int service_init(const struct device *_arg) {
static const struct k_work_queue_config queue_config = {
.name = "Split Peripheral Notification Queue"};