diff --git a/src/ble/gatt-service/Makefile.inc b/src/ble/gatt-service/Makefile.inc index 241a6ec26..89e046c10 100644 --- a/src/ble/gatt-service/Makefile.inc +++ b/src/ble/gatt-service/Makefile.inc @@ -2,6 +2,7 @@ SRC_BLE_GATT-SERVICE_FILES = \ battery_service_server.c \ - hids_device.c \ device_information_service_server.c \ + heart_rate_service_server.c \ + hids_device.c \ diff --git a/src/ble/gatt-service/heart_rate_service.gatt b/src/ble/gatt-service/heart_rate_service.gatt new file mode 100644 index 000000000..503fb489f --- /dev/null +++ b/src/ble/gatt-service/heart_rate_service.gatt @@ -0,0 +1,9 @@ +// Specification Type org.bluetooth.service.heart_rate +// https://www.bluetooth.com/api/gatt/xmlfile?xmlFileName=org.bluetooth.service.heart_rate.xml + +// Heart Rate 180D +PRIMARY_SERVICE, ORG_BLUETOOTH_SERVICE_HEART_RATE +CHARACTERISTIC, ORG_BLUETOOTH_CHARACTERISTIC_HEART_RATE_MEASUREMENT, DYNAMIC | NOTIFY, +CLIENT_CHARACTERISTIC_CONFIGURATION, READ | WRITE, +CHARACTERISTIC, ORG_BLUETOOTH_CHARACTERISTIC_BODY_SENSOR_LOCATION, DYNAMIC | READ, +CHARACTERISTIC, ORG_BLUETOOTH_CHARACTERISTIC_HEART_RATE_CONTROL_POINT, DYNAMIC | WRITE, diff --git a/src/ble/gatt-service/heart_rate_service_server.c b/src/ble/gatt-service/heart_rate_service_server.c new file mode 100644 index 000000000..3d0d7a110 --- /dev/null +++ b/src/ble/gatt-service/heart_rate_service_server.c @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2014 BlueKitchen GmbH + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * 4. Any redistribution, use, or modification is done solely for + * personal benefit and not for any commercial purpose or for + * monetary gain. + * + * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS + * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Please inquire about commercial licensing options at + * contact@bluekitchen-gmbh.com + * + */ + +#define __BTSTACK_FILE__ "heart_rate_service_server.c" + +/** + * Implementation of the GATT Battery Service Server + * To use with your application, add '#import + +#if defined __cplusplus +extern "C" { +#endif + +/** + * Implementation of the GATT Heart Rate Server + * To use with your application, add '#import +#include +#include +#include + +#include "hrp_server_test.h" +#include "btstack.h" +#include "ble/gatt-service/heart_rate_service_server.h" + +#define HEARTBEAT_PERIOD_MS 1000 + +static int le_notification_enabled; +static btstack_timer_source_t heartbeat; +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 uint16_t energy_expended; +static int rr_interval_count; +static uint16_t rr_intervals[10]; + +static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size); +static uint16_t att_read_callback(hci_con_handle_t con_handle, uint16_t att_handle, uint16_t offset, uint8_t * buffer, uint16_t buffer_size); +static int att_write_callback(hci_con_handle_t con_handle, uint16_t att_handle, uint16_t transaction_mode, uint16_t offset, uint8_t *buffer, uint16_t buffer_size); +static void heartbeat_handler(struct btstack_timer_source *ts); +static void beat(void); + +const uint8_t adv_data[] = { + // Flags general discoverable, BR/EDR not supported + 0x02, 0x01, 0x06, + // Name + 0x12, 0x09, 'H', 'e', 'a', 'r', 't', ' ', 'R', 'a', 't', 'e', ' ', 'S', 'e', 'r', 'v', 'e','r' +}; +const uint8_t adv_data_len = sizeof(adv_data); + + +static int counter = 0; +static char counter_string[30]; +static int counter_string_len; + +static void beat(void){ + counter++; + counter_string_len = sprintf(counter_string, "BTstack counter %04u", counter); + puts(counter_string); +} + +static void heartbeat_handler(struct btstack_timer_source *ts){ + if (le_notification_enabled) { + beat(); + att_server_request_can_send_now_event(con_handle); + } + + // simulate increase of energy spent + if (energy_expended < 0xFFFF) { + 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]); + + btstack_run_loop_set_timer(ts, HEARTBEAT_PERIOD_MS); + btstack_run_loop_add_timer(ts); +} + +static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ + UNUSED(channel); + UNUSED(size); + + switch (packet_type) { + case HCI_EVENT_PACKET: + switch (hci_event_packet_get_type(packet)) { + case HCI_EVENT_DISCONNECTION_COMPLETE: + le_notification_enabled = 0; + break; + case ATT_EVENT_CAN_SEND_NOW: + break; + } + break; + } +} + +static uint16_t att_read_callback(hci_con_handle_t connection_handle, uint16_t att_handle, uint16_t offset, uint8_t * buffer, uint16_t buffer_size){ + UNUSED(connection_handle); + + return 0; +} + +static int att_write_callback(hci_con_handle_t connection_handle, uint16_t att_handle, uint16_t transaction_mode, uint16_t offset, uint8_t *buffer, uint16_t buffer_size){ + UNUSED(transaction_mode); + UNUSED(offset); + UNUSED(buffer_size); + + return 0; +} + + +int btstack_main(void); +int btstack_main(void){ + // register for HCI events + hci_event_callback_registration.callback = &packet_handler; + hci_add_event_handler(&hci_event_callback_registration); + + l2cap_init(); + + // setup le device db + le_device_db_init(); + + // setup SM: Display only + sm_init(); + + // setup ATT server + att_server_init(profile_data, att_read_callback, att_write_callback); + att_server_register_packet_handler(packet_handler); + + // setup battery service + heart_rate_service_server_init(HEART_RATE_SERVICE_BODY_SENSOR_LOCATION_HAND); + + // setup advertisements + uint16_t adv_int_min = 0x0030; + uint16_t adv_int_max = 0x0030; + uint8_t adv_type = 0; + bd_addr_t null_addr; + memset(null_addr, 0, 6); + gap_advertisements_set_params(adv_int_min, adv_int_max, adv_type, 0, null_addr, 0x07, 0x00); + gap_advertisements_set_data(adv_data_len, (uint8_t*) adv_data); + gap_advertisements_enable(1); + + // set one-shot timer + heartbeat.process = &heartbeat_handler; + btstack_run_loop_set_timer(&heartbeat, HEARTBEAT_PERIOD_MS); + btstack_run_loop_add_timer(&heartbeat); + + // beat once + beat(); + + // turn on! + hci_power_control(HCI_POWER_ON); + + return 0; +} +/* EXAMPLE_END */ diff --git a/test/pts/hrp_server_test.gatt b/test/pts/hrp_server_test.gatt new file mode 100644 index 000000000..02204b7c6 --- /dev/null +++ b/test/pts/hrp_server_test.gatt @@ -0,0 +1,5 @@ +PRIMARY_SERVICE, GAP_SERVICE +CHARACTERISTIC, GAP_DEVICE_NAME, READ, "Heart Rate Server" + +// add Heart Rate Service +#import