heart rate server: handle notify, read, write

This commit is contained in:
Milanka Ringwald 2018-05-08 14:38:13 +02:00
parent adc3e7d5ae
commit 5d3bf30aab
3 changed files with 183 additions and 27 deletions

View File

@ -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);
}
}

View File

@ -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 */

View File

@ -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);