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