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
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)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
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)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
heart_rate_service_server_init(heart_rate_service_body_sensor_location_t location,int energy_expended_supported)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
heart_rate_service_can_send_now(void * context)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
heart_rate_service_add_energy_expended(uint16_t energy_expended_kJ)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
heart_rate_service_server_update_heart_rate_values(uint16_t heart_rate_bpm,heart_rate_service_sensor_contact_status_t sensor_contact,int rr_interval_count,uint16_t * rr_intervals)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 }