1 /* 2 * Copyright (C) 2014 BlueKitchen GmbH 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. Neither the name of the copyright holders nor the names of 14 * contributors may be used to endorse or promote products derived 15 * from this software without specific prior written permission. 16 * 4. Any redistribution, use, or modification is done solely for 17 * personal benefit and not for any commercial purpose or for 18 * monetary gain. 19 * 20 * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS 21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 23 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN 24 * GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 26 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 27 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 28 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 29 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 30 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 * 33 * Please inquire about commercial licensing options at 34 * [email protected] 35 * 36 */ 37 38 #define BTSTACK_FILE__ "heart_rate_service_server.c" 39 40 41 #include "bluetooth.h" 42 #include "btstack_defines.h" 43 #include "ble/att_db.h" 44 #include "ble/att_server.h" 45 #include "btstack_util.h" 46 #include "bluetooth_gatt.h" 47 #include "btstack_debug.h" 48 49 #include "ble/gatt-service/heart_rate_service_server.h" 50 51 #define HEART_RATE_RESET_ENERGY_EXPENDED 0x01 52 #define HEART_RATE_CONTROL_POINT_NOT_SUPPORTED 0x80 53 54 typedef enum { 55 HEART_RATE_SERVICE_VALUE_FORMAT = 0, 56 HEART_RATE_SERVICE_SENSOR_CONTACT_STATUS, 57 HEART_RATE_SERVICE_ENERGY_EXPENDED_STATUS = 3, 58 HEART_RATE_SERVICE_RR_INTERVAL 59 } heart_rate_service_flag_bit_t; 60 61 typedef struct { 62 hci_con_handle_t con_handle; 63 64 // characteristic: Heart Rate Mesurement 65 uint16_t measurement_value_handle; 66 uint16_t measurement_bpm; 67 uint8_t energy_expended_supported; 68 uint16_t energy_expended_kJ; // kilo Joules 69 int rr_interval_count; 70 int rr_offset; 71 uint16_t * rr_intervals; 72 heart_rate_service_sensor_contact_status_t sensor_contact; 73 74 // characteristic descriptor: Client Characteristic Configuration 75 uint16_t measurement_client_configuration_descriptor_handle; 76 uint16_t measurement_client_configuration_descriptor_notify; 77 btstack_context_callback_registration_t measurement_callback; 78 79 // characteristic: Body Sensor Location 80 uint16_t sensor_location_value_handle; 81 heart_rate_service_body_sensor_location_t sensor_location; 82 83 // characteristic: Heart Rate Control Point 84 uint16_t control_point_value_handle; 85 } heart_rate_t; 86 87 static att_service_handler_t heart_rate_service; 88 static heart_rate_t heart_rate; 89 90 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){ 91 UNUSED(con_handle); 92 UNUSED(attribute_handle); 93 UNUSED(offset); 94 UNUSED(buffer_size); 95 96 if (attribute_handle == heart_rate.measurement_client_configuration_descriptor_handle){ 97 if (buffer && (buffer_size >= 2u)){ 98 little_endian_store_16(buffer, 0, heart_rate.measurement_client_configuration_descriptor_notify); 99 } 100 return 2; 101 } 102 103 if (attribute_handle == heart_rate.sensor_location_value_handle){ 104 if (buffer && (buffer_size >= 1u)){ 105 buffer[0] = heart_rate.sensor_location; 106 } 107 return 1; 108 } 109 return 0; 110 } 111 112 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){ 113 UNUSED(offset); 114 UNUSED(buffer_size); 115 116 if (transaction_mode != ATT_TRANSACTION_MODE_NONE){ 117 return 0; 118 } 119 120 if (attribute_handle == heart_rate.measurement_client_configuration_descriptor_handle){ 121 if (buffer_size < 2u){ 122 return ATT_ERROR_INVALID_OFFSET; 123 } 124 heart_rate.measurement_client_configuration_descriptor_notify = little_endian_read_16(buffer, 0); 125 heart_rate.con_handle = con_handle; 126 return 0; 127 } 128 129 if (attribute_handle == heart_rate.control_point_value_handle){ 130 uint16_t cmd = little_endian_read_16(buffer, 0); 131 switch (cmd){ 132 case HEART_RATE_RESET_ENERGY_EXPENDED: 133 heart_rate.energy_expended_kJ = 0; 134 heart_rate.con_handle = con_handle; 135 break; 136 default: 137 return HEART_RATE_CONTROL_POINT_NOT_SUPPORTED; 138 } 139 return 0; 140 } 141 return 0; 142 } 143 144 145 void heart_rate_service_server_init(heart_rate_service_body_sensor_location_t location, int energy_expended_supported){ 146 heart_rate_t * instance = &heart_rate; 147 148 instance->sensor_location = location; 149 instance->energy_expended_supported = energy_expended_supported; 150 151 // get service handle range 152 uint16_t start_handle = 0; 153 uint16_t end_handle = 0xffff; 154 int service_found = gatt_server_get_handle_range_for_service_with_uuid16(ORG_BLUETOOTH_SERVICE_HEART_RATE, &start_handle, &end_handle); 155 btstack_assert(service_found != 0); 156 UNUSED(service_found); 157 158 // get Heart Rate Mesurement characteristic value handle and client configuration handle 159 instance->measurement_value_handle = gatt_server_get_value_handle_for_characteristic_with_uuid16(start_handle, end_handle, ORG_BLUETOOTH_CHARACTERISTIC_HEART_RATE_MEASUREMENT); 160 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); 161 // get Body Sensor Location characteristic value handle and client configuration handle 162 instance->sensor_location_value_handle = gatt_server_get_value_handle_for_characteristic_with_uuid16(start_handle, end_handle, ORG_BLUETOOTH_CHARACTERISTIC_BODY_SENSOR_LOCATION); 163 // get Hear Rate Control Point characteristic value handle and client configuration handle 164 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); 165 166 log_info("Measurement value handle 0x%02x", instance->measurement_value_handle); 167 log_info("Client Config value handle 0x%02x", instance->measurement_client_configuration_descriptor_handle); 168 log_info("Sensor location value handle 0x%02x", instance->sensor_location_value_handle); 169 log_info("Control Point value handle 0x%02x", instance->control_point_value_handle); 170 // register service with ATT Server 171 heart_rate_service.start_handle = start_handle; 172 heart_rate_service.end_handle = end_handle; 173 heart_rate_service.read_callback = &heart_rate_service_read_callback; 174 heart_rate_service.write_callback = &heart_rate_service_write_callback; 175 176 att_server_register_service_handler(&heart_rate_service); 177 } 178 179 180 static void heart_rate_service_can_send_now(void * context){ 181 heart_rate_t * instance = (heart_rate_t *) context; 182 uint8_t flags = (1 << HEART_RATE_SERVICE_VALUE_FORMAT); 183 flags |= (instance->sensor_contact << HEART_RATE_SERVICE_SENSOR_CONTACT_STATUS); 184 if (instance->energy_expended_supported){ 185 flags |= (1u << HEART_RATE_SERVICE_ENERGY_EXPENDED_STATUS); 186 } 187 if (instance->rr_interval_count){ 188 flags |= (1u << HEART_RATE_SERVICE_RR_INTERVAL); 189 } 190 191 uint8_t value[100]; 192 int pos = 0; 193 194 value[pos++] = flags; 195 little_endian_store_16(value, pos, instance->measurement_bpm); 196 pos += 2; 197 if (instance->energy_expended_supported){ 198 little_endian_store_16(value, pos, instance->energy_expended_kJ); 199 pos += 2; 200 } 201 202 uint16_t bytes_left = btstack_min(sizeof(value), att_server_get_mtu(instance->con_handle) - 3u - pos); 203 204 while ((bytes_left > 2u) && instance->rr_interval_count){ 205 little_endian_store_16(value, pos, instance->rr_intervals[0]); 206 pos +=2; 207 bytes_left -= 2u; 208 instance->rr_intervals++; 209 instance->rr_interval_count--; 210 } 211 212 att_server_notify(instance->con_handle, instance->measurement_value_handle, &value[0], pos); 213 214 if (instance->rr_interval_count){ 215 instance->measurement_callback.callback = &heart_rate_service_can_send_now; 216 instance->measurement_callback.context = (void*) instance; 217 att_server_register_can_send_now_callback(&instance->measurement_callback, instance->con_handle); 218 } 219 } 220 221 void heart_rate_service_add_energy_expended(uint16_t energy_expended_kJ){ 222 heart_rate_t * instance = &heart_rate; 223 // limit energy expended to 0xffff 224 if (instance->energy_expended_kJ <= (0xffffu - energy_expended_kJ)){ 225 instance->energy_expended_kJ += energy_expended_kJ; 226 } else { 227 instance->energy_expended_kJ = 0xffff; 228 } 229 } 230 231 void heart_rate_service_server_update_heart_rate_values(uint16_t heart_rate_bpm, 232 heart_rate_service_sensor_contact_status_t sensor_contact, int rr_interval_count, uint16_t * rr_intervals){ 233 heart_rate_t * instance = &heart_rate; 234 235 instance->measurement_bpm = heart_rate_bpm; 236 instance->sensor_contact = sensor_contact; 237 instance->rr_interval_count = rr_interval_count; 238 instance->rr_intervals = rr_intervals; 239 instance->rr_offset = 0; 240 241 if (instance->measurement_client_configuration_descriptor_notify){ 242 instance->measurement_callback.callback = &heart_rate_service_can_send_now; 243 instance->measurement_callback.context = (void*) instance; 244 att_server_register_can_send_now_callback(&instance->measurement_callback, instance->con_handle); 245 } 246 }