mirror of
https://github.com/bluekitchen/btstack.git
synced 2025-03-14 01:27:41 +00:00
heart rate server: add support energy extended field, fix flags construction on send
This commit is contained in:
parent
5d3bf30aab
commit
46e18d797d
@ -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;
|
||||
|
@ -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 */
|
||||
|
@ -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);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user