From 2b7a5229f2fa2d7403e1be38edde461b3eab507c Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 5 Feb 2016 14:24:57 +0200 Subject: Bluetooth: samples: Add dedicated Heartrate peripheral sample app This can be used to focus on demonstrating a simple heartrate service. Change-Id: Ib59c653ebe356784592c8468767b45c5240ec67a Signed-off-by: Johan Hedberg Signed-off-by: Anas Nashif --- samples/bluetooth/peripheral_hr/Makefile | 7 + samples/bluetooth/peripheral_hr/README | 4 + samples/bluetooth/peripheral_hr/prj.conf | 9 + samples/bluetooth/peripheral_hr/prj.mdef | 5 + samples/bluetooth/peripheral_hr/prj_nble.conf | 4 + samples/bluetooth/peripheral_hr/src/Makefile | 1 + samples/bluetooth/peripheral_hr/src/main.c | 268 ++++++++++++++++++++++++++ samples/bluetooth/peripheral_hr/testcase.ini | 20 ++ 8 files changed, 318 insertions(+) create mode 100644 samples/bluetooth/peripheral_hr/Makefile create mode 100644 samples/bluetooth/peripheral_hr/README create mode 100644 samples/bluetooth/peripheral_hr/prj.conf create mode 100644 samples/bluetooth/peripheral_hr/prj.mdef create mode 100644 samples/bluetooth/peripheral_hr/prj_nble.conf create mode 100644 samples/bluetooth/peripheral_hr/src/Makefile create mode 100644 samples/bluetooth/peripheral_hr/src/main.c create mode 100644 samples/bluetooth/peripheral_hr/testcase.ini (limited to 'samples') diff --git a/samples/bluetooth/peripheral_hr/Makefile b/samples/bluetooth/peripheral_hr/Makefile new file mode 100644 index 000000000..fdc0101e3 --- /dev/null +++ b/samples/bluetooth/peripheral_hr/Makefile @@ -0,0 +1,7 @@ +BOARD ?= qemu_x86 +MDEF_FILE = prj.mdef +KERNEL_TYPE = micro +CONF_FILE ?= prj.conf +QEMU_EXTRA_FLAGS = -serial unix:/tmp/bt-server-bredr + +include $(ZEPHYR_BASE)/Makefile.inc diff --git a/samples/bluetooth/peripheral_hr/README b/samples/bluetooth/peripheral_hr/README new file mode 100644 index 000000000..a09758188 --- /dev/null +++ b/samples/bluetooth/peripheral_hr/README @@ -0,0 +1,4 @@ +For building shell with Nordic Bluetooth Low Energy stack (NBLE) +use prj_nble.conf: + +$ make CONF_FILE=prj_nble.conf BOARD=arduino_101 diff --git a/samples/bluetooth/peripheral_hr/prj.conf b/samples/bluetooth/peripheral_hr/prj.conf new file mode 100644 index 000000000..f86a01fd7 --- /dev/null +++ b/samples/bluetooth/peripheral_hr/prj.conf @@ -0,0 +1,9 @@ +CONFIG_TEST_RANDOM_GENERATOR=y +CONFIG_BLUETOOTH=y +CONFIG_BLUETOOTH_LE=y +CONFIG_BLUETOOTH_DEBUG=y +CONFIG_BLUETOOTH_SMP=y +CONFIG_TINYCRYPT=y +CONFIG_TINYCRYPT_AES=y +CONFIG_BLUETOOTH_PERIPHERAL=y +CONFIG_BLUETOOTH_GATT_DYNAMIC_DB=y diff --git a/samples/bluetooth/peripheral_hr/prj.mdef b/samples/bluetooth/peripheral_hr/prj.mdef new file mode 100644 index 000000000..a1bef7a2d --- /dev/null +++ b/samples/bluetooth/peripheral_hr/prj.mdef @@ -0,0 +1,5 @@ +% Application : Bluetooth heartrate peripheral sample + +% TASK NAME PRIO ENTRY STACK GROUPS +% =================================================== + TASK MAIN 7 mainloop 2048 [EXE] diff --git a/samples/bluetooth/peripheral_hr/prj_nble.conf b/samples/bluetooth/peripheral_hr/prj_nble.conf new file mode 100644 index 000000000..ef0af4e30 --- /dev/null +++ b/samples/bluetooth/peripheral_hr/prj_nble.conf @@ -0,0 +1,4 @@ +CONFIG_TEST_RANDOM_GENERATOR=y +CONFIG_NBLE=y +CONFIG_ARC_INIT=n +CONFIG_BLUETOOTH_DEBUG=y diff --git a/samples/bluetooth/peripheral_hr/src/Makefile b/samples/bluetooth/peripheral_hr/src/Makefile new file mode 100644 index 000000000..00066e156 --- /dev/null +++ b/samples/bluetooth/peripheral_hr/src/Makefile @@ -0,0 +1 @@ +obj-y = main.o diff --git a/samples/bluetooth/peripheral_hr/src/main.c b/samples/bluetooth/peripheral_hr/src/main.c new file mode 100644 index 000000000..0e08e8fce --- /dev/null +++ b/samples/bluetooth/peripheral_hr/src/main.c @@ -0,0 +1,268 @@ +/* main.c - Application main entry point */ + +/* + * Copyright (c) 2015 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define DEVICE_NAME "Heartrate Monitor" +#define DEVICE_NAME_LEN (sizeof(DEVICE_NAME) - 1) +#define HEART_RATE_APPEARANCE 0x0341 + +static int read_name(struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset) +{ + const char *name = attr->user_data; + + return bt_gatt_attr_read(conn, attr, buf, len, offset, name, + strlen(name)); +} + +static int read_appearance(struct bt_conn *conn, + const struct bt_gatt_attr *attr, void *buf, + uint16_t len, uint16_t offset) +{ + uint16_t appearance = sys_cpu_to_le16(HEART_RATE_APPEARANCE); + + return bt_gatt_attr_read(conn, attr, buf, len, offset, &appearance, + sizeof(appearance)); +} + +static struct bt_gatt_ccc_cfg hrmc_ccc_cfg[CONFIG_BLUETOOTH_MAX_PAIRED] = {}; +static uint8_t simulate_hrm; + +static void hrmc_ccc_cfg_changed(uint16_t value) +{ + simulate_hrm = (value == BT_GATT_CCC_NOTIFY) ? 1 : 0; +} + +static int read_blsc(struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset) +{ + uint8_t value = 0x01; + + return bt_gatt_attr_read(conn, attr, buf, len, offset, &value, + sizeof(value)); +} + +static struct bt_gatt_ccc_cfg blvl_ccc_cfg[CONFIG_BLUETOOTH_MAX_PAIRED] = {}; +static uint8_t simulate_blvl; +static uint8_t battery = 100; + +static void blvl_ccc_cfg_changed(uint16_t value) +{ + simulate_blvl = (value == BT_GATT_CCC_NOTIFY) ? 1 : 0; +} + +static int read_blvl(struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset) +{ + const char *value = attr->user_data; + + return bt_gatt_attr_read(conn, attr, buf, len, offset, value, + sizeof(*value)); +} + +static int read_model(struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset) +{ + const char *value = attr->user_data; + + return bt_gatt_attr_read(conn, attr, buf, len, offset, value, + strlen(value)); +} + +static int read_manuf(struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset) +{ + const char *value = attr->user_data; + + return bt_gatt_attr_read(conn, attr, buf, len, offset, value, + strlen(value)); +} + +static struct bt_gatt_attr gap_attrs[] = { + BT_GATT_PRIMARY_SERVICE(BT_UUID_GAP), + BT_GATT_CHARACTERISTIC(BT_UUID_GAP_DEVICE_NAME, BT_GATT_CHRC_READ), + BT_GATT_DESCRIPTOR(BT_UUID_GAP_DEVICE_NAME, BT_GATT_PERM_READ, + read_name, NULL, DEVICE_NAME), + BT_GATT_CHARACTERISTIC(BT_UUID_GAP_APPEARANCE, BT_GATT_CHRC_READ), + BT_GATT_DESCRIPTOR(BT_UUID_GAP_APPEARANCE, BT_GATT_PERM_READ, + read_appearance, NULL, NULL), +}; + +/* Heart Rate Service Declaration */ +static struct bt_gatt_attr hrs_attrs[] = { + BT_GATT_PRIMARY_SERVICE(BT_UUID_HRS), + BT_GATT_CHARACTERISTIC(BT_UUID_HRS_MEASUREMENT, BT_GATT_CHRC_NOTIFY), + BT_GATT_DESCRIPTOR(BT_UUID_HRS_MEASUREMENT, BT_GATT_PERM_READ, NULL, + NULL, NULL), + BT_GATT_CCC(hrmc_ccc_cfg, hrmc_ccc_cfg_changed), + BT_GATT_CHARACTERISTIC(BT_UUID_HRS_BODY_SENSOR, BT_GATT_CHRC_READ), + BT_GATT_DESCRIPTOR(BT_UUID_HRS_BODY_SENSOR, BT_GATT_PERM_READ, + read_blsc, NULL, NULL), + BT_GATT_CHARACTERISTIC(BT_UUID_HRS_CONTROL_POINT, BT_GATT_CHRC_WRITE), + /* TODO: Add write permission and callback */ + BT_GATT_DESCRIPTOR(BT_UUID_HRS_CONTROL_POINT, BT_GATT_PERM_READ, NULL, + NULL, NULL), +}; + +/* Battery Service Declaration */ +static struct bt_gatt_attr bas_attrs[] = { + BT_GATT_PRIMARY_SERVICE(BT_UUID_BAS), + BT_GATT_CHARACTERISTIC(BT_UUID_BAS_BATTERY_LEVEL, + BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY), + BT_GATT_DESCRIPTOR(BT_UUID_BAS_BATTERY_LEVEL, BT_GATT_PERM_READ, + read_blvl, NULL, &battery), + BT_GATT_CCC(blvl_ccc_cfg, blvl_ccc_cfg_changed), +}; + +/* Device Information Service Declaration */ +static struct bt_gatt_attr dis_attrs[] = { + BT_GATT_PRIMARY_SERVICE(BT_UUID_DIS), + BT_GATT_CHARACTERISTIC(BT_UUID_DIS_MODEL_NUMBER, BT_GATT_CHRC_READ), + BT_GATT_DESCRIPTOR(BT_UUID_DIS_MODEL_NUMBER, BT_GATT_PERM_READ, + read_model, NULL, CONFIG_SOC), + BT_GATT_CHARACTERISTIC(BT_UUID_DIS_MANUFACTURER_NAME, + BT_GATT_CHRC_READ), + BT_GATT_DESCRIPTOR(BT_UUID_DIS_MANUFACTURER_NAME, BT_GATT_PERM_READ, + read_manuf, NULL, "Manufacturer"), +}; + +static const struct bt_data ad[] = { + BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)), + BT_DATA_BYTES(BT_DATA_UUID16_ALL, 0x0d, 0x18, 0x0f, 0x18, 0x05, 0x18), +}; + +static const struct bt_data sd[] = { + BT_DATA(BT_DATA_NAME_COMPLETE, DEVICE_NAME, DEVICE_NAME_LEN), +}; + +static void connected(struct bt_conn *conn, uint8_t err) +{ + if (err) { + printk("Connection failed (err %u)\n", err); + } else { + printk("Connected\n"); + } +} + +static void disconnected(struct bt_conn *conn, uint8_t reason) +{ + printk("Disconnected (reason %u)\n", reason); +} + +static struct bt_conn_cb conn_callbacks = { + .connected = connected, + .disconnected = disconnected, +}; + +static void bt_ready(int err) +{ + if (err) { + printk("Bluetooth init failed (err %d)\n", err); + return; + } + + printk("Bluetooth initialized\n"); + + bt_gatt_register(gap_attrs, ARRAY_SIZE(gap_attrs)); + bt_gatt_register(hrs_attrs, ARRAY_SIZE(hrs_attrs)); + bt_gatt_register(bas_attrs, ARRAY_SIZE(bas_attrs)); + bt_gatt_register(dis_attrs, ARRAY_SIZE(dis_attrs)); + + err = bt_le_adv_start(BT_LE_ADV(BT_LE_ADV_IND), ad, ARRAY_SIZE(ad), + sd, ARRAY_SIZE(sd)); + if (err) { + printk("Advertising failed to start (err %d)\n", err); + return; + } + + printk("Advertising successfully started\n"); +} + +static void auth_cancel(struct bt_conn *conn) +{ + char addr[BT_ADDR_LE_STR_LEN]; + + bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr)); + + printk("Pairing cancelled: %s\n", addr); +} + +static struct bt_conn_auth_cb auth_cb_display = { + .cancel = auth_cancel, +}; + +#ifdef CONFIG_MICROKERNEL +void mainloop(void) +#else +void main(void) +#endif +{ + int err; + + err = bt_enable(bt_ready); + if (err) { + printk("Bluetooth init failed (err %d)\n", err); + return; + } + + bt_conn_cb_register(&conn_callbacks); + bt_conn_auth_cb_register(&auth_cb_display); + + /* Implement notification. At the moment there is no suitable way + * of starting delayed work so we do it here + */ + while (1) { + static uint8_t hrm[2]; + + task_sleep(sys_clock_ticks_per_sec); + + /* Heartrate measurements simulation */ + if (simulate_hrm) { + hrm[0] = 0x06; /* uint8, sensor contact */ + hrm[1] = 90 + (sys_rand32_get() % 20); + + bt_gatt_notify(NULL, &hrs_attrs[3], &hrm, sizeof(hrm)); + } + + /* Battery level simulation */ + if (simulate_blvl) { + battery -= 1; + + if (!battery) { + /* Software eco battery charger */ + battery = 100; + } + + bt_gatt_notify(NULL, &bas_attrs[3], &battery, + sizeof(battery)); + } + } +} diff --git a/samples/bluetooth/peripheral_hr/testcase.ini b/samples/bluetooth/peripheral_hr/testcase.ini new file mode 100644 index 000000000..a88d972fd --- /dev/null +++ b/samples/bluetooth/peripheral_hr/testcase.ini @@ -0,0 +1,20 @@ +[test_x86] +tags = bluetooth +build_only = true +arch_whitelist = x86 +# FIXME Doesn't work for ia32_pci +config_whitelist = CONFIG_SOC="ia32" + +[test_arm] +tags = bluetooth +build_only = true +arch_whitelist = arm +platform_exclude = arduino_due + +[test_nble] +tags = bluetooth +build_only = true +extra_args = CONF_FILE="prj_nble.conf" +arch_whitelist = x86 +config_whitelist = CONFIG_SOC_QUARK_SE +platform_whitelist = arduino_101 -- cgit v1.2.3