mirror of
https://github.com/bluekitchen/btstack.git
synced 2025-01-25 09:35:42 +00:00
heart rate server: handle notify, read, write
This commit is contained in:
parent
adc3e7d5ae
commit
5d3bf30aab
@ -48,26 +48,186 @@
|
||||
#include "ble/att_server.h"
|
||||
#include "btstack_util.h"
|
||||
#include "bluetooth_gatt.h"
|
||||
#include "btstack_debug.h"
|
||||
|
||||
#include "ble/gatt-service/heart_rate_service_server.h"
|
||||
|
||||
void heart_rate_service_server_init(heart_rate_service_body_sensor_location_t location){
|
||||
UNUSED(location);
|
||||
}
|
||||
typedef enum {
|
||||
HEART_RATE_SERVICE_VALUE_FORMAT,
|
||||
HEART_RATE_SERVICE_SENSOR_CONTACT_STATUS,
|
||||
HEART_RATE_SERVICE_ENERGY_EXPENDED_STATUS,
|
||||
HEART_RATE_SERVICE_RR_INTERVAL
|
||||
} heart_rate_service_flag_bit_t;
|
||||
|
||||
void heart_rate_service_register_reset_energy_expended_callback(btstack_packet_handler_t callback){
|
||||
UNUSED(callback);
|
||||
}
|
||||
typedef struct {
|
||||
hci_con_handle_t con_handle;
|
||||
|
||||
uint8_t heart_rate_service_server_update_heart_rate_values(uint16_t heart_rate,
|
||||
heart_rate_service_sensor_contact_t contact, uint16_t energy_expended,
|
||||
int rr_interval_count, uint16_t * rr_intervals){
|
||||
// characteristic: Heart Rate Mesurement
|
||||
uint16_t measurement_value_handle;
|
||||
uint8_t flags;
|
||||
uint16_t measurement_bpm;
|
||||
uint16_t energy_expended_kJ; // kilo Joules
|
||||
int rr_interval_count;
|
||||
int rr_offset;
|
||||
uint16_t * rr_intervals;
|
||||
heart_rate_service_sensor_contact_status_t sensor_contact;
|
||||
|
||||
UNUSED(heart_rate);
|
||||
UNUSED(contact);
|
||||
UNUSED(energy_expended);
|
||||
UNUSED(rr_interval_count);
|
||||
UNUSED(rr_intervals);
|
||||
// characteristic descriptor: Client Characteristic Configuration
|
||||
uint16_t measurement_client_configuration_descriptor_handle;
|
||||
uint16_t measurement_client_configuration_descriptor_notify;
|
||||
btstack_context_callback_registration_t measurement_callback;
|
||||
|
||||
// characteristic: Body Sensor Location
|
||||
uint16_t sensor_location_value_handle;
|
||||
heart_rate_service_body_sensor_location_t sensor_location;
|
||||
|
||||
return ERROR_CODE_SUCCESS;
|
||||
// characteristic: Hear Rate Control Point
|
||||
// uint8_t reset_energy_expended;
|
||||
uint16_t control_point_value_handle;
|
||||
|
||||
} heart_rate_t;
|
||||
|
||||
static att_service_handler_t heart_rate_service;
|
||||
static heart_rate_t heart_rate;
|
||||
|
||||
static btstack_packet_handler_t heart_rate_service_callback;
|
||||
|
||||
static uint16_t heart_rate_service_read_callback(hci_con_handle_t con_handle, uint16_t attribute_handle, uint16_t offset, uint8_t * buffer, uint16_t buffer_size){
|
||||
UNUSED(con_handle);
|
||||
UNUSED(attribute_handle);
|
||||
UNUSED(offset);
|
||||
UNUSED(buffer_size);
|
||||
|
||||
if (attribute_handle == heart_rate.measurement_client_configuration_descriptor_handle){
|
||||
if (buffer){
|
||||
little_endian_store_16(buffer, 0, heart_rate.measurement_client_configuration_descriptor_notify);
|
||||
}
|
||||
return 2;
|
||||
}
|
||||
|
||||
if (attribute_handle == heart_rate.sensor_location_value_handle){
|
||||
if (buffer){
|
||||
buffer[0] = heart_rate.sensor_location;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int heart_rate_service_write_callback(hci_con_handle_t con_handle, uint16_t attribute_handle, uint16_t transaction_mode, uint16_t offset, uint8_t *buffer, uint16_t buffer_size){
|
||||
UNUSED(transaction_mode);
|
||||
UNUSED(offset);
|
||||
UNUSED(buffer_size);
|
||||
|
||||
if (attribute_handle == heart_rate.measurement_client_configuration_descriptor_handle){
|
||||
heart_rate.measurement_client_configuration_descriptor_handle = little_endian_read_16(buffer, 0);
|
||||
heart_rate.con_handle = con_handle;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void heart_rate_service_server_init(heart_rate_service_body_sensor_location_t location){
|
||||
heart_rate_t * instance = &heart_rate;
|
||||
|
||||
instance->sensor_location = location;
|
||||
|
||||
// get service handle range
|
||||
uint16_t start_handle = 0;
|
||||
uint16_t end_handle = 0xffff;
|
||||
int service_found = gatt_server_get_get_handle_range_for_service_with_uuid16(ORG_BLUETOOTH_SERVICE_HEART_RATE, &start_handle, &end_handle);
|
||||
if (!service_found) return;
|
||||
|
||||
// get Heart Rate Mesurement characteristic value handle and client configuration handle
|
||||
instance->measurement_value_handle = gatt_server_get_value_handle_for_characteristic_with_uuid16(start_handle, end_handle, ORG_BLUETOOTH_CHARACTERISTIC_HEART_RATE_MEASUREMENT);
|
||||
instance->measurement_client_configuration_descriptor_handle = gatt_server_get_client_configuration_handle_for_characteristic_with_uuid16(start_handle, end_handle, ORG_BLUETOOTH_CHARACTERISTIC_HEART_RATE_MEASUREMENT);
|
||||
// get Body Sensor Location characteristic value handle and client configuration handle
|
||||
instance->sensor_location_value_handle = gatt_server_get_value_handle_for_characteristic_with_uuid16(start_handle, end_handle, ORG_BLUETOOTH_CHARACTERISTIC_SENSOR_LOCATION);
|
||||
// get Hear Rate Control Point characteristic value handle and client configuration handle
|
||||
instance->control_point_value_handle = gatt_server_get_value_handle_for_characteristic_with_uuid16(start_handle, end_handle, ORG_BLUETOOTH_CHARACTERISTIC_HEART_RATE_CONTROL_POINT);
|
||||
|
||||
// register service with ATT Server
|
||||
heart_rate_service.start_handle = start_handle;
|
||||
heart_rate_service.end_handle = end_handle;
|
||||
heart_rate_service.read_callback = &heart_rate_service_read_callback;
|
||||
heart_rate_service.write_callback = &heart_rate_service_write_callback;
|
||||
|
||||
att_server_register_service_handler(&heart_rate_service);
|
||||
}
|
||||
|
||||
|
||||
static void heart_rate_service_can_send_now(void * context){
|
||||
heart_rate_t * instance = (heart_rate_t *) context;
|
||||
instance->flags |= (1 << HEART_RATE_SERVICE_VALUE_FORMAT);
|
||||
instance->flags |= (instance->sensor_contact << HEART_RATE_SERVICE_SENSOR_CONTACT_STATUS);
|
||||
if (instance->energy_expended_kJ){
|
||||
instance->flags |= (1 << HEART_RATE_SERVICE_ENERGY_EXPENDED_STATUS);
|
||||
}
|
||||
if (instance->rr_interval_count){
|
||||
instance->flags |= (1 << HEART_RATE_SERVICE_RR_INTERVAL);
|
||||
}
|
||||
|
||||
uint8_t value[100];
|
||||
int pos = 0;
|
||||
|
||||
value[pos++] = instance->flags;
|
||||
little_endian_store_16(value, pos, instance->measurement_bpm);
|
||||
pos += 2;
|
||||
if (instance->energy_expended_kJ){
|
||||
little_endian_store_16(value, pos, instance->energy_expended_kJ);
|
||||
pos += 2;
|
||||
}
|
||||
|
||||
// TODO: get actual MTU from ATT server
|
||||
uint16_t bytes_left = btstack_min(sizeof(value), l2cap_max_mtu() - 3 - pos);
|
||||
|
||||
while (bytes_left > 2 && instance->rr_interval_count){
|
||||
little_endian_store_16(value, pos, instance->rr_intervals[0]);
|
||||
pos +=2;
|
||||
bytes_left -= 2;
|
||||
instance->rr_intervals++;
|
||||
instance->rr_interval_count--;
|
||||
}
|
||||
|
||||
att_server_notify(instance->con_handle, instance->measurement_value_handle, &value[0], pos);
|
||||
|
||||
if (instance->rr_interval_count){
|
||||
instance->measurement_callback.callback = &heart_rate_service_can_send_now;
|
||||
instance->measurement_callback.context = (void*) instance;
|
||||
att_server_register_can_send_now_callback(&instance->measurement_callback, instance->con_handle);
|
||||
}
|
||||
}
|
||||
|
||||
void heart_rate_service_add_energy_expended(uint16_t energy_expended_kJ){
|
||||
heart_rate_t * instance = &heart_rate;
|
||||
// limit energy expended to 0xffff
|
||||
if ( instance->energy_expended_kJ <= 0xffff - energy_expended_kJ){
|
||||
instance->energy_expended_kJ += energy_expended_kJ;
|
||||
} else {
|
||||
instance->energy_expended_kJ = 0xffff;
|
||||
}
|
||||
|
||||
if (instance->measurement_client_configuration_descriptor_notify){
|
||||
instance->measurement_callback.callback = &heart_rate_service_can_send_now;
|
||||
instance->measurement_callback.context = (void*) instance;
|
||||
att_server_register_can_send_now_callback(&instance->measurement_callback, instance->con_handle);
|
||||
}
|
||||
}
|
||||
|
||||
void heart_rate_service_server_update_heart_rate_values(uint16_t bits_per_minute,
|
||||
heart_rate_service_sensor_contact_status_t sensor_contact, int rr_interval_count, uint16_t * rr_intervals){
|
||||
|
||||
heart_rate_t * instance = &heart_rate;
|
||||
|
||||
instance->measurement_bpm = bits_per_minute;
|
||||
instance->sensor_contact = sensor_contact;
|
||||
instance->rr_interval_count = rr_interval_count;
|
||||
instance->rr_intervals = rr_intervals;
|
||||
instance->rr_offset = 0;
|
||||
|
||||
if (instance->measurement_client_configuration_descriptor_notify){
|
||||
instance->measurement_callback.callback = &heart_rate_service_can_send_now;
|
||||
instance->measurement_callback.context = (void*) instance;
|
||||
att_server_register_can_send_now_callback(&instance->measurement_callback, instance->con_handle);
|
||||
}
|
||||
}
|
@ -65,7 +65,7 @@ typedef enum {
|
||||
HEART_RATE_SERVICE_SENSOR_CONTACT_UNSUPPORTED,
|
||||
HEART_RATE_SERVICE_SENSOR_CONTACT_NO_CONTACT,
|
||||
HEART_RATE_SERVICE_SENSOR_CONTACT_HAVE_CONTACT
|
||||
} heart_rate_service_sensor_contact_t;
|
||||
} heart_rate_service_sensor_contact_status_t;
|
||||
|
||||
|
||||
/**
|
||||
@ -76,25 +76,21 @@ void heart_rate_service_server_init(heart_rate_service_body_sensor_location_t lo
|
||||
|
||||
|
||||
/**
|
||||
* @brief Register callback to receive event "Reset Energy Expended"
|
||||
* @param heart_rate in range 0-8
|
||||
* @brief Add Energy Expended to the internal accumulator.
|
||||
* @param energy_expended energy expended in kilo Joules since the last update
|
||||
*/
|
||||
void heart_rate_service_register_reset_energy_expended_callback(btstack_packet_handler_t callback);
|
||||
void heart_rate_service_add_energy_expended(uint16_t energy_expended_kJ);
|
||||
|
||||
/**
|
||||
* @brief Update heart rate (unit: beats per minute)
|
||||
* @note triggers notifications if subscribed
|
||||
* @param heart_rate beats per second, range 0-255
|
||||
* @param contact
|
||||
* @param energy_expended accumulated energy expended in kilo Joules since the last time it was reset
|
||||
* @param rr_interval_count
|
||||
* @param rr_intervals resolution in 1/1024 seconds
|
||||
* @return status ERROR_CODE_SUCCESS if succesfully queued
|
||||
*/
|
||||
uint8_t heart_rate_service_server_update_heart_rate_values(uint16_t heart_rate,
|
||||
heart_rate_service_sensor_contact_t contact, uint16_t energy_expended,
|
||||
int rr_interval_count, uint16_t * rr_intervals);
|
||||
|
||||
void heart_rate_service_server_update_heart_rate_values(uint16_t heart_rate,
|
||||
heart_rate_service_sensor_contact_status_t contact, int rr_interval_count, uint16_t * rr_intervals);
|
||||
|
||||
/* API_END */
|
||||
|
||||
|
@ -64,7 +64,7 @@ static btstack_packet_callback_registration_t hci_event_callback_registration;
|
||||
static hci_con_handle_t con_handle;
|
||||
|
||||
static uint16_t heart_rate = 100;
|
||||
static heart_rate_service_sensor_contact_t contact;
|
||||
static heart_rate_service_sensor_contact_status_t contact;
|
||||
static uint16_t energy_expended;
|
||||
static int rr_interval_count;
|
||||
static uint16_t rr_intervals[10];
|
||||
@ -105,7 +105,7 @@ static void heartbeat_handler(struct btstack_timer_source *ts){
|
||||
energy_expended++;
|
||||
}
|
||||
|
||||
heart_rate_service_server_update_heart_rate_values(heart_rate, HEART_RATE_SERVICE_SENSOR_CONTACT_HAVE_CONTACT, energy_expended, rr_interval_count, &rr_intervals[0]);
|
||||
heart_rate_service_server_update_heart_rate_values(heart_rate, HEART_RATE_SERVICE_SENSOR_CONTACT_HAVE_CONTACT, rr_interval_count, &rr_intervals[0]);
|
||||
|
||||
btstack_run_loop_set_timer(ts, HEARTBEAT_PERIOD_MS);
|
||||
btstack_run_loop_add_timer(ts);
|
||||
|
Loading…
x
Reference in New Issue
Block a user