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" 515d3bf30aSMilanka Ringwald #include "btstack_debug.h" 52*46e18d79SMilanka Ringwald #include "l2cap.h" 53adc3e7d5SMilanka Ringwald 54adc3e7d5SMilanka Ringwald #include "ble/gatt-service/heart_rate_service_server.h" 55adc3e7d5SMilanka Ringwald 56*46e18d79SMilanka Ringwald #define HEART_RATE_RESET_ENERGY_EXPENDED 0x01 57*46e18d79SMilanka Ringwald #define HEART_RATE_CONTROL_POINT_NOT_SUPPORTED 0x80 58*46e18d79SMilanka Ringwald 595d3bf30aSMilanka Ringwald typedef enum { 60*46e18d79SMilanka Ringwald HEART_RATE_SERVICE_VALUE_FORMAT = 0, 615d3bf30aSMilanka Ringwald HEART_RATE_SERVICE_SENSOR_CONTACT_STATUS, 62*46e18d79SMilanka Ringwald HEART_RATE_SERVICE_ENERGY_EXPENDED_STATUS = 3, 635d3bf30aSMilanka Ringwald HEART_RATE_SERVICE_RR_INTERVAL 645d3bf30aSMilanka Ringwald } heart_rate_service_flag_bit_t; 655d3bf30aSMilanka Ringwald 665d3bf30aSMilanka Ringwald typedef struct { 675d3bf30aSMilanka Ringwald hci_con_handle_t con_handle; 685d3bf30aSMilanka Ringwald 695d3bf30aSMilanka Ringwald // characteristic: Heart Rate Mesurement 705d3bf30aSMilanka Ringwald uint16_t measurement_value_handle; 715d3bf30aSMilanka Ringwald uint16_t measurement_bpm; 72*46e18d79SMilanka Ringwald uint8_t energy_expended_supported; 735d3bf30aSMilanka Ringwald uint16_t energy_expended_kJ; // kilo Joules 745d3bf30aSMilanka Ringwald int rr_interval_count; 755d3bf30aSMilanka Ringwald int rr_offset; 765d3bf30aSMilanka Ringwald uint16_t * rr_intervals; 775d3bf30aSMilanka Ringwald heart_rate_service_sensor_contact_status_t sensor_contact; 785d3bf30aSMilanka Ringwald 795d3bf30aSMilanka Ringwald // characteristic descriptor: Client Characteristic Configuration 805d3bf30aSMilanka Ringwald uint16_t measurement_client_configuration_descriptor_handle; 815d3bf30aSMilanka Ringwald uint16_t measurement_client_configuration_descriptor_notify; 825d3bf30aSMilanka Ringwald btstack_context_callback_registration_t measurement_callback; 835d3bf30aSMilanka Ringwald 845d3bf30aSMilanka Ringwald // characteristic: Body Sensor Location 855d3bf30aSMilanka Ringwald uint16_t sensor_location_value_handle; 865d3bf30aSMilanka Ringwald heart_rate_service_body_sensor_location_t sensor_location; 875d3bf30aSMilanka Ringwald 885d3bf30aSMilanka Ringwald // characteristic: Hear Rate Control Point 89*46e18d79SMilanka Ringwald // uint8_t HEART_RATE_reset_energy_expended; 905d3bf30aSMilanka Ringwald uint16_t control_point_value_handle; 915d3bf30aSMilanka Ringwald 925d3bf30aSMilanka Ringwald } heart_rate_t; 935d3bf30aSMilanka Ringwald 945d3bf30aSMilanka Ringwald static att_service_handler_t heart_rate_service; 955d3bf30aSMilanka Ringwald static heart_rate_t heart_rate; 965d3bf30aSMilanka Ringwald 975d3bf30aSMilanka 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){ 985d3bf30aSMilanka Ringwald UNUSED(con_handle); 995d3bf30aSMilanka Ringwald UNUSED(attribute_handle); 1005d3bf30aSMilanka Ringwald UNUSED(offset); 1015d3bf30aSMilanka Ringwald UNUSED(buffer_size); 1025d3bf30aSMilanka Ringwald 1035d3bf30aSMilanka Ringwald if (attribute_handle == heart_rate.measurement_client_configuration_descriptor_handle){ 104*46e18d79SMilanka Ringwald if (buffer && buffer_size >= 2){ 1055d3bf30aSMilanka Ringwald little_endian_store_16(buffer, 0, heart_rate.measurement_client_configuration_descriptor_notify); 1065d3bf30aSMilanka Ringwald } 1075d3bf30aSMilanka Ringwald return 2; 1085d3bf30aSMilanka Ringwald } 1095d3bf30aSMilanka Ringwald 1105d3bf30aSMilanka Ringwald if (attribute_handle == heart_rate.sensor_location_value_handle){ 111*46e18d79SMilanka Ringwald if (buffer && buffer_size >= 1){ 1125d3bf30aSMilanka Ringwald buffer[0] = heart_rate.sensor_location; 1135d3bf30aSMilanka Ringwald } 1145d3bf30aSMilanka Ringwald return 1; 1155d3bf30aSMilanka Ringwald } 116*46e18d79SMilanka Ringwald printf("heart_rate_service_read_callback, not handeled read on handle 0x%02x\n", attribute_handle); 1175d3bf30aSMilanka Ringwald return 0; 1185d3bf30aSMilanka Ringwald } 1195d3bf30aSMilanka Ringwald 1205d3bf30aSMilanka 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){ 1215d3bf30aSMilanka Ringwald UNUSED(transaction_mode); 1225d3bf30aSMilanka Ringwald UNUSED(offset); 1235d3bf30aSMilanka Ringwald UNUSED(buffer_size); 1245d3bf30aSMilanka Ringwald 1255d3bf30aSMilanka Ringwald if (attribute_handle == heart_rate.measurement_client_configuration_descriptor_handle){ 126*46e18d79SMilanka Ringwald if (buffer_size < 2){ 127*46e18d79SMilanka Ringwald return ATT_ERROR_INVALID_OFFSET; 128*46e18d79SMilanka Ringwald } 129*46e18d79SMilanka Ringwald heart_rate.measurement_client_configuration_descriptor_notify = little_endian_read_16(buffer, 0); 1305d3bf30aSMilanka Ringwald heart_rate.con_handle = con_handle; 131*46e18d79SMilanka Ringwald if (heart_rate.measurement_client_configuration_descriptor_notify){ 132*46e18d79SMilanka Ringwald printf("notify enabled\n"); 133*46e18d79SMilanka Ringwald } else { 134*46e18d79SMilanka Ringwald printf("notify disabled\n"); 1355d3bf30aSMilanka Ringwald } 1365d3bf30aSMilanka Ringwald return 0; 1375d3bf30aSMilanka Ringwald } 1385d3bf30aSMilanka Ringwald 139*46e18d79SMilanka Ringwald if (attribute_handle == heart_rate.control_point_value_handle){ 140*46e18d79SMilanka Ringwald uint16_t cmd = little_endian_read_16(buffer, 0); 141*46e18d79SMilanka Ringwald switch (cmd){ 142*46e18d79SMilanka Ringwald case HEART_RATE_RESET_ENERGY_EXPENDED: 143*46e18d79SMilanka Ringwald heart_rate.energy_expended_kJ = 0; 144*46e18d79SMilanka Ringwald heart_rate.con_handle = con_handle; 145*46e18d79SMilanka Ringwald break; 146*46e18d79SMilanka Ringwald default: 147*46e18d79SMilanka Ringwald return HEART_RATE_CONTROL_POINT_NOT_SUPPORTED; 148*46e18d79SMilanka Ringwald } 149*46e18d79SMilanka Ringwald return 0; 150*46e18d79SMilanka Ringwald } 1515d3bf30aSMilanka Ringwald 152*46e18d79SMilanka Ringwald printf("heart_rate_service_write_callback, not handeled write on handle 0x%02x\n", attribute_handle); 153*46e18d79SMilanka Ringwald return 0; 154*46e18d79SMilanka Ringwald } 155*46e18d79SMilanka Ringwald 156*46e18d79SMilanka Ringwald 157*46e18d79SMilanka Ringwald void heart_rate_service_server_init(heart_rate_service_body_sensor_location_t location, int energy_expended_supported){ 1585d3bf30aSMilanka Ringwald heart_rate_t * instance = &heart_rate; 1595d3bf30aSMilanka Ringwald 1605d3bf30aSMilanka Ringwald instance->sensor_location = location; 161*46e18d79SMilanka Ringwald instance->energy_expended_supported = energy_expended_supported; 1625d3bf30aSMilanka Ringwald 1635d3bf30aSMilanka Ringwald // get service handle range 1645d3bf30aSMilanka Ringwald uint16_t start_handle = 0; 1655d3bf30aSMilanka Ringwald uint16_t end_handle = 0xffff; 1665d3bf30aSMilanka Ringwald int service_found = gatt_server_get_get_handle_range_for_service_with_uuid16(ORG_BLUETOOTH_SERVICE_HEART_RATE, &start_handle, &end_handle); 1675d3bf30aSMilanka Ringwald if (!service_found) return; 1685d3bf30aSMilanka Ringwald 1695d3bf30aSMilanka Ringwald // get Heart Rate Mesurement characteristic value handle and client configuration handle 1705d3bf30aSMilanka Ringwald instance->measurement_value_handle = gatt_server_get_value_handle_for_characteristic_with_uuid16(start_handle, end_handle, ORG_BLUETOOTH_CHARACTERISTIC_HEART_RATE_MEASUREMENT); 1715d3bf30aSMilanka 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); 1725d3bf30aSMilanka Ringwald // get Body Sensor Location characteristic value handle and client configuration handle 173*46e18d79SMilanka Ringwald instance->sensor_location_value_handle = gatt_server_get_value_handle_for_characteristic_with_uuid16(start_handle, end_handle, ORG_BLUETOOTH_CHARACTERISTIC_BODY_SENSOR_LOCATION); 1745d3bf30aSMilanka Ringwald // get Hear Rate Control Point characteristic value handle and client configuration handle 1755d3bf30aSMilanka 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); 1765d3bf30aSMilanka Ringwald 177*46e18d79SMilanka Ringwald printf("Measurement value handle 0x%02x\n", instance->measurement_value_handle); 178*46e18d79SMilanka Ringwald printf("Client Config value handle 0x%02x\n", instance->measurement_client_configuration_descriptor_handle); 179*46e18d79SMilanka Ringwald printf("Sensor location value handle 0x%02x\n", instance->sensor_location_value_handle); 180*46e18d79SMilanka Ringwald printf("Control Point value handle 0x%02x\n", instance->control_point_value_handle); 1815d3bf30aSMilanka Ringwald // register service with ATT Server 1825d3bf30aSMilanka Ringwald heart_rate_service.start_handle = start_handle; 1835d3bf30aSMilanka Ringwald heart_rate_service.end_handle = end_handle; 1845d3bf30aSMilanka Ringwald heart_rate_service.read_callback = &heart_rate_service_read_callback; 1855d3bf30aSMilanka Ringwald heart_rate_service.write_callback = &heart_rate_service_write_callback; 1865d3bf30aSMilanka Ringwald 1875d3bf30aSMilanka Ringwald att_server_register_service_handler(&heart_rate_service); 188adc3e7d5SMilanka Ringwald } 189adc3e7d5SMilanka Ringwald 1905d3bf30aSMilanka Ringwald 1915d3bf30aSMilanka Ringwald static void heart_rate_service_can_send_now(void * context){ 1925d3bf30aSMilanka Ringwald heart_rate_t * instance = (heart_rate_t *) context; 193*46e18d79SMilanka Ringwald uint8_t flags = (1 << HEART_RATE_SERVICE_VALUE_FORMAT); 194*46e18d79SMilanka Ringwald flags |= (instance->sensor_contact << HEART_RATE_SERVICE_SENSOR_CONTACT_STATUS); 195*46e18d79SMilanka Ringwald if (instance->energy_expended_supported){ 196*46e18d79SMilanka Ringwald flags |= (1 << HEART_RATE_SERVICE_ENERGY_EXPENDED_STATUS); 1975d3bf30aSMilanka Ringwald } 1985d3bf30aSMilanka Ringwald if (instance->rr_interval_count){ 199*46e18d79SMilanka Ringwald flags |= (1 << HEART_RATE_SERVICE_RR_INTERVAL); 200adc3e7d5SMilanka Ringwald } 201*46e18d79SMilanka Ringwald printf("heart_rate_service_can_send_now: flags 0%2x\n", flags); 202adc3e7d5SMilanka Ringwald 2035d3bf30aSMilanka Ringwald uint8_t value[100]; 2045d3bf30aSMilanka Ringwald int pos = 0; 205adc3e7d5SMilanka Ringwald 206*46e18d79SMilanka Ringwald value[pos++] = flags; 2075d3bf30aSMilanka Ringwald little_endian_store_16(value, pos, instance->measurement_bpm); 2085d3bf30aSMilanka Ringwald pos += 2; 209*46e18d79SMilanka Ringwald if (instance->energy_expended_supported){ 2105d3bf30aSMilanka Ringwald little_endian_store_16(value, pos, instance->energy_expended_kJ); 2115d3bf30aSMilanka Ringwald pos += 2; 2125d3bf30aSMilanka Ringwald } 213adc3e7d5SMilanka Ringwald 2145d3bf30aSMilanka Ringwald // TODO: get actual MTU from ATT server 2155d3bf30aSMilanka Ringwald uint16_t bytes_left = btstack_min(sizeof(value), l2cap_max_mtu() - 3 - pos); 2165d3bf30aSMilanka Ringwald 2175d3bf30aSMilanka Ringwald while (bytes_left > 2 && instance->rr_interval_count){ 2185d3bf30aSMilanka Ringwald little_endian_store_16(value, pos, instance->rr_intervals[0]); 2195d3bf30aSMilanka Ringwald pos +=2; 2205d3bf30aSMilanka Ringwald bytes_left -= 2; 2215d3bf30aSMilanka Ringwald instance->rr_intervals++; 2225d3bf30aSMilanka Ringwald instance->rr_interval_count--; 2235d3bf30aSMilanka Ringwald } 2245d3bf30aSMilanka Ringwald 225*46e18d79SMilanka Ringwald printf_hexdump(value, pos); 2265d3bf30aSMilanka Ringwald att_server_notify(instance->con_handle, instance->measurement_value_handle, &value[0], pos); 2275d3bf30aSMilanka Ringwald 2285d3bf30aSMilanka Ringwald if (instance->rr_interval_count){ 2295d3bf30aSMilanka Ringwald instance->measurement_callback.callback = &heart_rate_service_can_send_now; 2305d3bf30aSMilanka Ringwald instance->measurement_callback.context = (void*) instance; 2315d3bf30aSMilanka Ringwald att_server_register_can_send_now_callback(&instance->measurement_callback, instance->con_handle); 2325d3bf30aSMilanka Ringwald } 2335d3bf30aSMilanka Ringwald } 2345d3bf30aSMilanka Ringwald 2355d3bf30aSMilanka Ringwald void heart_rate_service_add_energy_expended(uint16_t energy_expended_kJ){ 2365d3bf30aSMilanka Ringwald heart_rate_t * instance = &heart_rate; 2375d3bf30aSMilanka Ringwald // limit energy expended to 0xffff 2385d3bf30aSMilanka Ringwald if (instance->energy_expended_kJ <= 0xffff - energy_expended_kJ){ 2395d3bf30aSMilanka Ringwald instance->energy_expended_kJ += energy_expended_kJ; 2405d3bf30aSMilanka Ringwald } else { 2415d3bf30aSMilanka Ringwald instance->energy_expended_kJ = 0xffff; 2425d3bf30aSMilanka Ringwald } 2435d3bf30aSMilanka Ringwald } 2445d3bf30aSMilanka Ringwald 245*46e18d79SMilanka Ringwald void heart_rate_service_server_update_heart_rate_values(uint16_t beats_per_minute, 2465d3bf30aSMilanka Ringwald heart_rate_service_sensor_contact_status_t sensor_contact, int rr_interval_count, uint16_t * rr_intervals){ 2475d3bf30aSMilanka Ringwald heart_rate_t * instance = &heart_rate; 2485d3bf30aSMilanka Ringwald 249*46e18d79SMilanka Ringwald printf("update_heart_rate_values, notify %u con_handle %04x\n", instance->measurement_client_configuration_descriptor_notify, instance->con_handle); 250*46e18d79SMilanka Ringwald 251*46e18d79SMilanka Ringwald instance->measurement_bpm = beats_per_minute; 2525d3bf30aSMilanka Ringwald instance->sensor_contact = sensor_contact; 2535d3bf30aSMilanka Ringwald instance->rr_interval_count = rr_interval_count; 2545d3bf30aSMilanka Ringwald instance->rr_intervals = rr_intervals; 2555d3bf30aSMilanka Ringwald instance->rr_offset = 0; 2565d3bf30aSMilanka Ringwald 2575d3bf30aSMilanka Ringwald if (instance->measurement_client_configuration_descriptor_notify){ 2585d3bf30aSMilanka Ringwald instance->measurement_callback.callback = &heart_rate_service_can_send_now; 2595d3bf30aSMilanka Ringwald instance->measurement_callback.context = (void*) instance; 2605d3bf30aSMilanka Ringwald att_server_register_can_send_now_callback(&instance->measurement_callback, instance->con_handle); 2615d3bf30aSMilanka Ringwald } 262adc3e7d5SMilanka Ringwald }