heart rate server: add support energy extended field, fix flags construction on send

This commit is contained in:
Milanka Ringwald 2018-05-09 16:22:21 +02:00
parent 5d3bf30aab
commit 46e18d797d
3 changed files with 114 additions and 109 deletions

View File

@ -49,13 +49,17 @@
#include "btstack_util.h"
#include "bluetooth_gatt.h"
#include "btstack_debug.h"
#include "l2cap.h"
#include "ble/gatt-service/heart_rate_service_server.h"
#define HEART_RATE_RESET_ENERGY_EXPENDED 0x01
#define HEART_RATE_CONTROL_POINT_NOT_SUPPORTED 0x80
typedef enum {
HEART_RATE_SERVICE_VALUE_FORMAT,
HEART_RATE_SERVICE_VALUE_FORMAT = 0,
HEART_RATE_SERVICE_SENSOR_CONTACT_STATUS,
HEART_RATE_SERVICE_ENERGY_EXPENDED_STATUS,
HEART_RATE_SERVICE_ENERGY_EXPENDED_STATUS = 3,
HEART_RATE_SERVICE_RR_INTERVAL
} heart_rate_service_flag_bit_t;
@ -64,8 +68,8 @@ typedef struct {
// characteristic: Heart Rate Mesurement
uint16_t measurement_value_handle;
uint8_t flags;
uint16_t measurement_bpm;
uint8_t energy_expended_supported;
uint16_t energy_expended_kJ; // kilo Joules
int rr_interval_count;
int rr_offset;
@ -82,7 +86,7 @@ typedef struct {
heart_rate_service_body_sensor_location_t sensor_location;
// characteristic: Hear Rate Control Point
// uint8_t reset_energy_expended;
// uint8_t HEART_RATE_reset_energy_expended;
uint16_t control_point_value_handle;
} heart_rate_t;
@ -90,27 +94,26 @@ typedef struct {
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){
if (buffer && buffer_size >= 2){
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){
if (buffer && buffer_size >= 1){
buffer[0] = heart_rate.sensor_location;
}
return 1;
}
printf("heart_rate_service_read_callback, not handeled read on handle 0x%02x\n", attribute_handle);
return 0;
}
@ -118,19 +121,44 @@ static int heart_rate_service_write_callback(hci_con_handle_t con_handle, uint16
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);
if (buffer_size < 2){
return ATT_ERROR_INVALID_OFFSET;
}
heart_rate.measurement_client_configuration_descriptor_notify = little_endian_read_16(buffer, 0);
heart_rate.con_handle = con_handle;
if (heart_rate.measurement_client_configuration_descriptor_notify){
printf("notify enabled\n");
} else {
printf("notify disabled\n");
}
return 0;
}
if (attribute_handle == heart_rate.control_point_value_handle){
uint16_t cmd = little_endian_read_16(buffer, 0);
switch (cmd){
case HEART_RATE_RESET_ENERGY_EXPENDED:
heart_rate.energy_expended_kJ = 0;
heart_rate.con_handle = con_handle;
break;
default:
return HEART_RATE_CONTROL_POINT_NOT_SUPPORTED;
}
return 0;
}
printf("heart_rate_service_write_callback, not handeled write on handle 0x%02x\n", attribute_handle);
return 0;
}
void heart_rate_service_server_init(heart_rate_service_body_sensor_location_t location){
void heart_rate_service_server_init(heart_rate_service_body_sensor_location_t location, int energy_expended_supported){
heart_rate_t * instance = &heart_rate;
instance->sensor_location = location;
instance->energy_expended_supported = energy_expended_supported;
// get service handle range
uint16_t start_handle = 0;
@ -142,10 +170,14 @@ void heart_rate_service_server_init(heart_rate_service_body_sensor_location_t lo
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);
instance->sensor_location_value_handle = gatt_server_get_value_handle_for_characteristic_with_uuid16(start_handle, end_handle, ORG_BLUETOOTH_CHARACTERISTIC_BODY_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);
printf("Measurement value handle 0x%02x\n", instance->measurement_value_handle);
printf("Client Config value handle 0x%02x\n", instance->measurement_client_configuration_descriptor_handle);
printf("Sensor location value handle 0x%02x\n", instance->sensor_location_value_handle);
printf("Control Point value handle 0x%02x\n", instance->control_point_value_handle);
// register service with ATT Server
heart_rate_service.start_handle = start_handle;
heart_rate_service.end_handle = end_handle;
@ -158,22 +190,23 @@ void heart_rate_service_server_init(heart_rate_service_body_sensor_location_t lo
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);
uint8_t flags = (1 << HEART_RATE_SERVICE_VALUE_FORMAT);
flags |= (instance->sensor_contact << HEART_RATE_SERVICE_SENSOR_CONTACT_STATUS);
if (instance->energy_expended_supported){
flags |= (1 << HEART_RATE_SERVICE_ENERGY_EXPENDED_STATUS);
}
if (instance->rr_interval_count){
instance->flags |= (1 << HEART_RATE_SERVICE_RR_INTERVAL);
flags |= (1 << HEART_RATE_SERVICE_RR_INTERVAL);
}
printf("heart_rate_service_can_send_now: flags 0%2x\n", flags);
uint8_t value[100];
int pos = 0;
value[pos++] = instance->flags;
value[pos++] = flags;
little_endian_store_16(value, pos, instance->measurement_bpm);
pos += 2;
if (instance->energy_expended_kJ){
if (instance->energy_expended_supported){
little_endian_store_16(value, pos, instance->energy_expended_kJ);
pos += 2;
}
@ -189,6 +222,7 @@ static void heart_rate_service_can_send_now(void * context){
instance->rr_interval_count--;
}
printf_hexdump(value, pos);
att_server_notify(instance->con_handle, instance->measurement_value_handle, &value[0], pos);
if (instance->rr_interval_count){
@ -201,25 +235,20 @@ static void heart_rate_service_can_send_now(void * context){
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){
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,
void heart_rate_service_server_update_heart_rate_values(uint16_t beats_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;
printf("update_heart_rate_values, notify %u con_handle %04x\n", instance->measurement_client_configuration_descriptor_notify, instance->con_handle);
instance->measurement_bpm = bits_per_minute;
instance->measurement_bpm = beats_per_minute;
instance->sensor_contact = sensor_contact;
instance->rr_interval_count = rr_interval_count;
instance->rr_intervals = rr_intervals;

View File

@ -51,7 +51,7 @@ extern "C" {
/* API_START */
typedef enum {
HEART_RATE_SERVICE_BODY_SENSOR_LOCATION_OTHER,
HEART_RATE_SERVICE_BODY_SENSOR_LOCATION_OTHER = 0,
HEART_RATE_SERVICE_BODY_SENSOR_LOCATION_CHEST,
HEART_RATE_SERVICE_BODY_SENSOR_LOCATION_WRIST,
HEART_RATE_SERVICE_BODY_SENSOR_LOCATION_FINGER,
@ -61,7 +61,7 @@ typedef enum {
} heart_rate_service_body_sensor_location_t;
typedef enum {
HEART_RATE_SERVICE_SENSOR_CONTACT_UNKNOWN,
HEART_RATE_SERVICE_SENSOR_CONTACT_UNKNOWN = 0,
HEART_RATE_SERVICE_SENSOR_CONTACT_UNSUPPORTED,
HEART_RATE_SERVICE_SENSOR_CONTACT_NO_CONTACT,
HEART_RATE_SERVICE_SENSOR_CONTACT_HAVE_CONTACT
@ -70,26 +70,27 @@ typedef enum {
/**
* @brief Init Battery Service Server with ATT DB
* @param heart_rate in range 0-255
* @param body_sensor_location
* @param energy_expended_supported
*/
void heart_rate_service_server_init(heart_rate_service_body_sensor_location_t location);
void heart_rate_service_server_init(heart_rate_service_body_sensor_location_t body_sensor_location, int energy_expended_supported);
/**
* @brief Add Energy Expended to the internal accumulator.
* @param energy_expended energy expended in kilo Joules since the last update
* @param energy_expended_kJ energy expended in kilo Joules since the last update
*/
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 heart_rate_bpm beats per minute
* @param contact
* @param rr_interval_count
* @param rr_intervals resolution in 1/1024 seconds
*/
void heart_rate_service_server_update_heart_rate_values(uint16_t heart_rate,
void heart_rate_service_server_update_heart_rate_values(uint16_t heart_rate_bpm,
heart_rate_service_sensor_contact_status_t contact, int rr_interval_count, uint16_t * rr_intervals);
/* API_END */

View File

@ -57,23 +57,15 @@
#include "ble/gatt-service/heart_rate_service_server.h"
#define HEARTBEAT_PERIOD_MS 1000
#define ENERGY_EXPENDED_SUPPORTED 1
static int le_notification_enabled;
static btstack_timer_source_t heartbeat;
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_status_t contact;
static uint16_t energy_expended;
static int rr_interval_count;
static uint16_t rr_intervals[10];
static int rr_interval_count = 2;
static uint16_t rr_intervals[] = {0x55, 0x66};
static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
static uint16_t att_read_callback(hci_con_handle_t con_handle, uint16_t att_handle, uint16_t offset, uint8_t * buffer, uint16_t buffer_size);
static int att_write_callback(hci_con_handle_t con_handle, uint16_t att_handle, uint16_t transaction_mode, uint16_t offset, uint8_t *buffer, uint16_t buffer_size);
static void heartbeat_handler(struct btstack_timer_source *ts);
static void beat(void);
static void heartbeat_handler(struct btstack_timer_source *ts);
const uint8_t adv_data[] = {
// Flags general discoverable, BR/EDR not supported
@ -83,72 +75,54 @@ const uint8_t adv_data[] = {
};
const uint8_t adv_data_len = sizeof(adv_data);
static int counter = 0;
static char counter_string[30];
static int counter_string_len;
static void beat(void){
counter++;
counter_string_len = sprintf(counter_string, "BTstack counter %04u", counter);
puts(counter_string);
}
static void heartbeat_handler(struct btstack_timer_source *ts){
if (le_notification_enabled) {
beat();
att_server_request_can_send_now_event(con_handle);
}
// simulate increase of energy spent
if (energy_expended < 0xFFFF) {
energy_expended++;
}
heart_rate_service_server_update_heart_rate_values(heart_rate, HEART_RATE_SERVICE_SENSOR_CONTACT_HAVE_CONTACT, 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);
}
static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
UNUSED(channel);
UNUSED(size);
#ifdef HAVE_BTSTACK_STDIN
switch (packet_type) {
case HCI_EVENT_PACKET:
switch (hci_event_packet_get_type(packet)) {
case HCI_EVENT_DISCONNECTION_COMPLETE:
le_notification_enabled = 0;
break;
case ATT_EVENT_CAN_SEND_NOW:
break;
}
static void show_usage(void){
bd_addr_t iut_address;
gap_local_bd_addr(iut_address);
printf("\n--- Bluetooth HRP Server Test Console %s ---\n", bd_addr_to_str(iut_address));
printf("c - notify heart rate measurement, no contact with sensor\n");
printf("e - add 50kJ energy expended\n");
printf("n - notify heart rate measurement, contact with sensor\n");
printf("Ctrl-c - exit\n");
printf("---\n");
}
static void stdin_process(char cmd){
switch (cmd){
case 'n':
printf("Notify heart rate measurement, contact with sensor\n");
heart_rate_service_server_update_heart_rate_values(heart_rate, HEART_RATE_SERVICE_SENSOR_CONTACT_HAVE_CONTACT, rr_interval_count, &rr_intervals[0]);
break;
case 'c':
printf("Notify heart rate measurement, no contact with sensor\n");
heart_rate_service_server_update_heart_rate_values(heart_rate, HEART_RATE_SERVICE_SENSOR_CONTACT_NO_CONTACT, rr_interval_count, &rr_intervals[0]);
break;
case 'e':
printf("Add 50kJ energy expended\n");
heart_rate_service_add_energy_expended(50);
break;
case '\n':
case '\r':
break;
default:
show_usage();
break;
}
}
static uint16_t att_read_callback(hci_con_handle_t connection_handle, uint16_t att_handle, uint16_t offset, uint8_t * buffer, uint16_t buffer_size){
UNUSED(connection_handle);
return 0;
}
static int att_write_callback(hci_con_handle_t connection_handle, uint16_t att_handle, uint16_t transaction_mode, uint16_t offset, uint8_t *buffer, uint16_t buffer_size){
UNUSED(transaction_mode);
UNUSED(offset);
UNUSED(buffer_size);
return 0;
}
#endif
int btstack_main(void);
int btstack_main(void){
// register for HCI events
hci_event_callback_registration.callback = &packet_handler;
hci_add_event_handler(&hci_event_callback_registration);
l2cap_init();
// setup le device db
@ -158,11 +132,11 @@ int btstack_main(void){
sm_init();
// setup ATT server
att_server_init(profile_data, att_read_callback, att_write_callback);
att_server_register_packet_handler(packet_handler);
att_server_init(profile_data, NULL, NULL);
// setup battery service
heart_rate_service_server_init(HEART_RATE_SERVICE_BODY_SENSOR_LOCATION_HAND);
// setup heart rate service
heart_rate_service_server_init(HEART_RATE_SERVICE_BODY_SENSOR_LOCATION_HAND, ENERGY_EXPENDED_SUPPORTED);
printf("Heart rate service: body sensor location 0x%02x\n", HEART_RATE_SERVICE_BODY_SENSOR_LOCATION_HAND);
// setup advertisements
uint16_t adv_int_min = 0x0030;
@ -174,14 +148,15 @@ int btstack_main(void){
gap_advertisements_set_data(adv_data_len, (uint8_t*) adv_data);
gap_advertisements_enable(1);
// set one-shot timer
// setup simulated heartbeat measurement
heartbeat.process = &heartbeat_handler;
btstack_run_loop_set_timer(&heartbeat, HEARTBEAT_PERIOD_MS);
btstack_run_loop_add_timer(&heartbeat);
// beat once
beat();
#ifdef HAVE_BTSTACK_STDIN
btstack_stdin_setup(stdin_process);
#endif
// turn on!
hci_power_control(HCI_POWER_ON);