diff --git a/src/ble/gatt-service/heart_rate_service_server.c b/src/ble/gatt-service/heart_rate_service_server.c index 3d0d7a110..0fb885fe2 100644 --- a/src/ble/gatt-service/heart_rate_service_server.c +++ b/src/ble/gatt-service/heart_rate_service_server.c @@ -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); + } } \ No newline at end of file diff --git a/src/ble/gatt-service/heart_rate_service_server.h b/src/ble/gatt-service/heart_rate_service_server.h index c3d36ca8e..c58388466 100644 --- a/src/ble/gatt-service/heart_rate_service_server.h +++ b/src/ble/gatt-service/heart_rate_service_server.h @@ -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 */ diff --git a/test/pts/hrp_server_test.c b/test/pts/hrp_server_test.c index 02c85d69c..5932a9113 100644 --- a/test/pts/hrp_server_test.c +++ b/test/pts/hrp_server_test.c @@ -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);