add more nimble example

1. make heart rate sensor example really work
using nRF Toolbox HRM
2. add master device scan example
3. add gatt server example(seems not working)
This commit is contained in:
daishengdong
2019-11-12 19:56:52 +08:00
parent 044d1c7246
commit 0700d27b70
80 changed files with 145868 additions and 33754 deletions

View File

@@ -0,0 +1,40 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
#ifndef H_BLEHR_SENSOR_
#define H_BLEHR_SENSOR_
#include "nimble/ble.h"
#include "modlog/modlog.h"
#ifdef __cplusplus
extern "C" {
#endif
#define GATT_DEVICE_INFO_UUID 0x180A
#define GATT_MANUFACTURER_NAME_UUID 0x2A29
#define GATT_MODEL_NUMBER_UUID 0x2A24
int gatt_svr_init(void);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,280 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 <assert.h>
#include <stdio.h>
#include <string.h>
#include "host/ble_hs.h"
#include "host/ble_uuid.h"
#include "blehr_sens.h"
#include "services/dis/ble_svc_dis.h"
#include "services/bas/ble_svc_bas.h"
/* UUID = 1bce38b3-d137-48ff-a13e-033e14c7a335 */
static const ble_uuid128_t gatt_svr_svc_rw_demo_uuid
= BLE_UUID128_INIT(0x35, 0xa3, 0xc7, 0x14, 0x3e, 0x03, 0x3e, 0xa1, 0xff,
0x48, 0x37, 0xd1, 0xb3, 0x38, 0xce, 0x1b);
/* UUID = 35f28386-3070-4f3b-ba38-27507e991762 */
static const ble_uuid128_t gatt_svr_chr_rw_demo_write_uuid
= BLE_UUID128_INIT(0x62, 0x17, 0x99, 0x7e, 0x50, 0x27, 0x38, 0xba, 0x3b,
0x4f, 0x70, 0x30, 0x86, 0x83, 0xf2, 0x35);
/* UUID = ccdd113f-40d5-4d68-86ac-a728dd82f4aa */
static const ble_uuid128_t gatt_svr_chr_rw_demo_readonly_uuid
= BLE_UUID128_INIT(0xaa, 0xf4, 0x82, 0xdd, 0x28, 0xa7, 0xac, 0x86, 0x68,
0x4d, 0xd5, 0x40, 0x3f, 0x11, 0xdd, 0xcc);
#define REPLY_BUFFER_SIZE 100
static char reply[REPLY_BUFFER_SIZE];
static char rm_demo_write_data[64] = "This characteristic is readable and writeable!";
static int gatt_svr_chr_access_device_info_manufacturer(
uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt, void *arg);
static int gatt_svr_chr_access_device_info_model(
uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt, void *arg);
static int gatt_svr_chr_access_rw_demo(
uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt, void *arg);
/* define several bluetooth services for our device */
static const struct ble_gatt_svc_def gatt_svr_svcs[] = {
/*
* access_cb defines a callback for read and write access events on
* given characteristics
*/
{
/* Service: Device Information */
.type = BLE_GATT_SVC_TYPE_PRIMARY,
.uuid = BLE_UUID16_DECLARE(GATT_DEVICE_INFO_UUID),
.characteristics = (struct ble_gatt_chr_def[]) { {
/* Characteristic: * Manufacturer name */
.uuid = BLE_UUID16_DECLARE(GATT_MANUFACTURER_NAME_UUID),
.access_cb = gatt_svr_chr_access_device_info_manufacturer,
.flags = BLE_GATT_CHR_F_READ,
}, {
/* Characteristic: Model number string */
.uuid = BLE_UUID16_DECLARE(GATT_MODEL_NUMBER_UUID),
.access_cb = gatt_svr_chr_access_device_info_model,
.flags = BLE_GATT_CHR_F_READ,
}, {
0, /* No more characteristics in this service */
}, }
},
{
/* Service: Read/Write Demo */
.type = BLE_GATT_SVC_TYPE_PRIMARY,
.uuid = (ble_uuid_t*) &gatt_svr_svc_rw_demo_uuid.u,
.characteristics = (struct ble_gatt_chr_def[]) { {
/* Characteristic: Read/Write Demo write */
.uuid = (ble_uuid_t*) &gatt_svr_chr_rw_demo_write_uuid.u,
.access_cb = gatt_svr_chr_access_rw_demo,
.flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_WRITE,
}, {
/* Characteristic: Read/Write Demo read only */
.uuid = (ble_uuid_t*) &gatt_svr_chr_rw_demo_readonly_uuid.u,
.access_cb = gatt_svr_chr_access_rw_demo,
.flags = BLE_GATT_CHR_F_READ,
}, {
0, /* No more characteristics in this service */
}, }
},
{
0, /* No more services */
},
};
static int gatt_svr_chr_access_device_info_manufacturer(
uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt, void *arg)
{
printf("service 'device info: manufacturer' callback triggered\n");
snprintf(reply, REPLY_BUFFER_SIZE, "This is TencentOS tiny\n");
printf("reply: %s\n", reply);
int rc = os_mbuf_append(ctxt->om, reply, strlen(reply));
printf("\n");
return rc;
}
static int gatt_svr_chr_access_device_info_model(
uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt, void *arg)
{
printf("service 'device info: model' callback triggered\n");
snprintf(reply, REPLY_BUFFER_SIZE, "running TencentOS tiny on nordic ble board");
printf("reply: %s\n", reply);
int rc = os_mbuf_append(ctxt->om, reply, strlen(reply));
printf("\n");
return rc;
}
static int gatt_svr_chr_access_rw_demo(
uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt, void *arg)
{
printf("service 'rw demo' callback triggered\n");
int rc = 0;
ble_uuid_t* write_uuid = (ble_uuid_t*) &gatt_svr_chr_rw_demo_write_uuid.u;
ble_uuid_t* readonly_uuid = (ble_uuid_t*) &gatt_svr_chr_rw_demo_readonly_uuid.u;
if (ble_uuid_cmp(ctxt->chr->uuid, write_uuid) == 0) {
printf("access to characteristic 'rw demo (write)'\n");
switch (ctxt->op) {
case BLE_GATT_ACCESS_OP_READ_CHR:
printf("read from characteristic\n");
printf("current value of rm_demo_write_data: '%s'\n",
rm_demo_write_data);
/* send given data to the client */
rc = os_mbuf_append(ctxt->om, &rm_demo_write_data, strlen(rm_demo_write_data));
break;
case BLE_GATT_ACCESS_OP_WRITE_CHR:
printf("write to characteristic\n");
printf("old value of rm_demo_write_data: '%s'\n", rm_demo_write_data);
uint16_t om_len;
om_len = OS_MBUF_PKTLEN(ctxt->om);
/* read sent data */
rc = ble_hs_mbuf_to_flat(ctxt->om, &rm_demo_write_data,
sizeof rm_demo_write_data, &om_len);
/* we need to null-terminate the received string */
rm_demo_write_data[om_len] = '\0';
printf("new value of rm_demo_write_data: '%s'\n",
rm_demo_write_data);
break;
case BLE_GATT_ACCESS_OP_READ_DSC:
printf("read from descriptor\n");
break;
case BLE_GATT_ACCESS_OP_WRITE_DSC:
printf("write to descriptor\n");
break;
default:
printf("unhandled operation!\n");
rc = 1;
break;
}
printf("\n");
return rc;
}
else if (ble_uuid_cmp(ctxt->chr->uuid, readonly_uuid) == 0) {
printf("access to characteristic 'rw demo (read-only)'\n");
if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) {
char random_digit;
/* get random char between '0' and '9' */
random_digit = 48 + (rand() % 10);
snprintf(reply, REPLY_BUFFER_SIZE, "new random number: %c", random_digit);
printf("reply: %s\n", reply);
rc = os_mbuf_append(ctxt->om, &reply, strlen(reply));
printf("\n");
return rc;
}
return 0;
}
printf("unhandled uuid!\n");
return 1;
}
void
gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg)
{
char buf[BLE_UUID_STR_LEN];
switch (ctxt->op) {
case BLE_GATT_REGISTER_OP_SVC:
MODLOG_DFLT(DEBUG, "registered service %s with handle=%d\n",
ble_uuid_to_str(ctxt->svc.svc_def->uuid, buf),
ctxt->svc.handle);
break;
case BLE_GATT_REGISTER_OP_CHR:
MODLOG_DFLT(DEBUG, "registering characteristic %s with "
"def_handle=%d val_handle=%d\n",
ble_uuid_to_str(ctxt->chr.chr_def->uuid, buf),
ctxt->chr.def_handle,
ctxt->chr.val_handle);
break;
case BLE_GATT_REGISTER_OP_DSC:
MODLOG_DFLT(DEBUG, "registering descriptor %s with handle=%d\n",
ble_uuid_to_str(ctxt->dsc.dsc_def->uuid, buf),
ctxt->dsc.handle);
break;
default:
assert(0);
break;
}
}
int
gatt_svr_init(void)
{
int rc;
rc = ble_gatts_count_cfg(gatt_svr_svcs);
if (rc != 0) {
return rc;
}
rc = ble_gatts_add_svcs(gatt_svr_svcs);
if (rc != 0) {
return rc;
}
return 0;
}

222
examples/nimble_gatt/main.c Normal file
View File

@@ -0,0 +1,222 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "nimble/nimble_port_tencentos_tiny.h"
#include "host/ble_hs.h"
#include "host/util/util.h"
#include "host/ble_gatt.h"
#include "services/gap/ble_svc_gap.h"
#include "services/gatt/ble_svc_gatt.h"
#include "blehr_sens.h"
static const char device_name[] = "Nimble Gatt";
static uint8_t ble_addr_type;
static void start_advertise(void);
static int blegatt_gap_event(struct ble_gap_event *event, void *arg)
{
switch (event->type) {
case BLE_GAP_EVENT_CONNECT:
if (event->connect.status) {
start_advertise();
}
break;
case BLE_GAP_EVENT_DISCONNECT:
start_advertise();
break;
}
return 0;
}
static void start_advertise(void)
{
int rc;
struct ble_gap_adv_params advp;
memset(&advp, 0, sizeof advp);
advp.conn_mode = BLE_GAP_CONN_MODE_UND;
advp.disc_mode = BLE_GAP_DISC_MODE_GEN;
rc = ble_gap_adv_start(ble_addr_type, NULL, BLE_HS_FOREVER,
&advp, blegatt_gap_event, NULL);
assert(rc == 0);
}
static void
put_ad(uint8_t ad_type, uint8_t ad_len, const void *ad, uint8_t *buf,
uint8_t *len)
{
buf[(*len)++] = ad_len + 1;
buf[(*len)++] = ad_type;
memcpy(&buf[*len], ad, ad_len);
*len += ad_len;
}
static void
update_ad(void)
{
uint8_t ad[BLE_HS_ADV_MAX_SZ];
uint8_t ad_len = 0;
uint8_t ad_flags = BLE_HS_ADV_F_DISC_GEN | BLE_HS_ADV_F_BREDR_UNSUP;
put_ad(BLE_HS_ADV_TYPE_FLAGS, 1, &ad_flags, ad, &ad_len);
put_ad(BLE_HS_ADV_TYPE_COMP_NAME, sizeof(device_name), device_name, ad, &ad_len);
ble_gap_adv_set_data(ad, ad_len);
}
#if 1
/* UUID = 1bce38b3-d137-48ff-a13e-033e14c7a335 */
static const ble_uuid128_t gatt_svr_svc_rw_demo_uuid
= BLE_UUID128_INIT(0x35, 0xa3, 0xc7, 0x14, 0x3e, 0x03, 0x3e, 0xa1, 0xff,
0x48, 0x37, 0xd1, 0xb3, 0x38, 0xce, 0x1b);
static ble_uuid128_t svc_adv_uuids_128[] =
{
{ BLE_UUID_TYPE_128, { 0x35, 0xa3, 0xc7, 0x14, 0x3e, 0x03, 0x3e, 0xa1, 0xff,
0x48, 0x37, 0xd1, 0xb3, 0x38, 0xce, 0x1b } },
};
#endif
static ble_uuid16_t svc_adv_uuids[] =
{
{ BLE_UUID_TYPE_16, GATT_DEVICE_INFO_UUID },
};
void ble_set_fields(void)
{
struct ble_gap_adv_params adv_params;
struct ble_hs_adv_fields fields;
int rc;
/*
* Set the advertisement data included in our advertisements:
* o Flags (indicates advertisement type and other general info)
* o Advertising tx power
* o Device name
*/
memset(&fields, 0, sizeof(fields));
/*
* Advertise two flags:
* o Discoverability in forthcoming advertisement (general)
* o BLE-only (BR/EDR unsupported)
*/
fields.flags = BLE_HS_ADV_F_DISC_GEN |
BLE_HS_ADV_F_BREDR_UNSUP;
/*
* Indicate that the TX power level field should be included; have the
* stack fill this value automatically. This is done by assigning the
* special value BLE_HS_ADV_TX_PWR_LVL_AUTO.
*/
fields.tx_pwr_lvl_is_present = 1;
fields.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO;
fields.name = (uint8_t *)device_name;
fields.name_len = strlen(device_name);
fields.name_is_complete = 1;
/*** 0x02,0x03 - 16-bit service class UUIDs. */
#if 1
fields.num_uuids16 = 1;
fields.uuids16 = svc_adv_uuids;
fields.uuids16_is_complete = 1;
#endif
#if 0
fields.num_uuids128 = 1;
fields.uuids128 = svc_adv_uuids_128;
fields.uuids128_is_complete = 1;
#endif
rc = ble_gap_adv_set_fields(&fields);
if (rc != 0) {
MODLOG_DFLT(ERROR, "error setting advertisement data; rc=%d\n", rc);
return;
}
/*** 0xff - Manufacturer specific data. */
memset(&fields, 0, sizeof(fields));
fields.mfg_data = (uint8_t*)"GZH:Bluetooth-BLE,177341833";
fields.mfg_data_len = 27;
rc = ble_gap_adv_rsp_set_fields(&fields);
if (rc != 0) {
MODLOG_DFLT(ERROR, "error setting advertisement data; rc=%d\n", rc);
return;
}
}
int ble_boot(void)
{
int rc = 0;
extern void nimble_port_run(void);
nimble_port_tencentos_tiny_init(nimble_port_run);
/* make sure synchronization of host and controller is done */
while (!ble_hs_synced()) {
;
}
rc = ble_hs_util_ensure_addr(0);
assert(rc == 0);
rc = ble_hs_id_infer_auto(0, &ble_addr_type);
assert(rc == 0);
ble_svc_gap_init();
ble_svc_gatt_init();
/* verify and add our custom services */
rc = gatt_svr_init();
assert(rc == 0);
/* set the device name */
rc = ble_svc_gap_device_name_set(device_name);
assert(rc == 0);
/* reload the GATT server to link our added services */
ble_gatts_start();
update_ad();
ble_set_fields();
start_advertise();
return 0;
}
k_task_t ble_boot_task;
k_stack_t ble_boot_stack[2048];
int main(void)
{
board_init();
/* Initialize OS */
tos_knl_init();
nimble_port_init();
/*
in this case, ble_boot_task's priority must lower than MYNEWT_VAL_BLE_HOST_TASK_PRIORITY && MYNEWT_VAL_BLE_LL_TASK_PRIORITY,
numerically bigger
to give the host and ll task a chance to run first just after the nimble_port_tencentos_tiny_init.
*/
tos_task_create(&ble_boot_task, "boot", ble_boot, NULL,
6,
ble_boot_stack, sizeof(ble_boot_stack),
0);
tos_knl_start();
}

View File

@@ -0,0 +1,6 @@
# NimBLE心率传感器案例
本案例通过实现一个模拟的BLE心率传感器展示如何通过GATT notification来实现异步的数据传输。
编译工程并烧写后使用手机或任何能支持心率传感器app建议使用Nordic官方的`nRF Toolbox`工具的设备连接开发板创建的Heart Rate Sensor蓝牙设备app界面上可以获取到传感器所在位置以及上传过来的心率信息。

View File

@@ -0,0 +1,13 @@
NimBLE Heart Rate Sensor Example
================================
This example demonstrates how to implement asynchronous data transfer using GATT
notifications by implementing a mock-up BLE heart rate sensor. Once flashed on
a node, you can connect to it using any phone or device capable of using such a
heart rate sensor, e.g. Nordics `nRF Toolbox` for Android
(https://play.google.com/store/apps/details?id=no.nordicsemi.android.nrftoolbox).
The heart rate sensor is simulated by simply providing an up- and down counter
for its BPM value.
On top of the heart rate service, this example implements the device information
as well as the battery service.

View File

@@ -24,18 +24,30 @@
#include "host/ble_uuid.h"
#include "blehr_sens.h"
static const char *manuf_name = "Apache Mynewt";
static const char *model_num = "Mynewt HR Sensor";
#include "services/dis/ble_svc_dis.h"
#include "services/bas/ble_svc_bas.h"
static const char *manuf_name = "TencentOS tiny";
static const char *model_num = "TOS HR Sensor";
uint16_t hrs_hrm_handle;
static const char *serial_num = "deadbeef-baddad";
static const char *firmware_ver = "1.0.0";
static const char *hardware_ver = "V1.0";
static int
gatt_svr_chr_access_heart_rate(uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt, void *arg);
struct ble_gatt_access_ctxt *ctxt, void *arg);
static int
gatt_svr_chr_access_battery_level(uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt, void *arg);
static int
gatt_svr_chr_access_device_info(uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt, void *arg);
/* GATT service definitions */
static const struct ble_gatt_svc_def gatt_svr_svcs[] = {
{
/* Service: Heart-rate */
@@ -71,14 +83,37 @@ static const struct ble_gatt_svc_def gatt_svr_svcs[] = {
.uuid = BLE_UUID16_DECLARE(GATT_MODEL_NUMBER_UUID),
.access_cb = gatt_svr_chr_access_device_info,
.flags = BLE_GATT_CHR_F_READ,
}, {
.uuid = BLE_UUID16_DECLARE(BLE_SVC_DIS_CHR_UUID16_SERIAL_NUMBER),
.access_cb = gatt_svr_chr_access_device_info,
.flags = BLE_GATT_CHR_F_READ,
}, {
.uuid = BLE_UUID16_DECLARE(BLE_SVC_DIS_CHR_UUID16_FIRMWARE_REVISION),
.access_cb = gatt_svr_chr_access_device_info,
.flags = BLE_GATT_CHR_F_READ,
}, {
.uuid = BLE_UUID16_DECLARE(BLE_SVC_DIS_CHR_UUID16_HARDWARE_REVISION),
.access_cb = gatt_svr_chr_access_device_info,
.flags = BLE_GATT_CHR_F_READ,
}, {
0, /* No more characteristics in this service */
}, }
},
{
0, /* No more services */
},
{
/* Service: Battery Level */
.type = BLE_GATT_SVC_TYPE_PRIMARY,
.uuid = BLE_UUID16_DECLARE(BLE_SVC_BAS_UUID16),
.characteristics = (struct ble_gatt_chr_def[]) { {
.uuid = BLE_UUID16_DECLARE(BLE_SVC_BAS_CHR_UUID16_BATTERY_LEVEL),
.access_cb = gatt_svr_chr_access_battery_level,
.flags = BLE_GATT_CHR_F_READ,
}, {
0, /* No more characteristics in this service */
}, }
},
{
0, /* No more services */
},
};
static int
@@ -104,27 +139,53 @@ gatt_svr_chr_access_heart_rate(uint16_t conn_handle, uint16_t attr_handle,
static int
gatt_svr_chr_access_device_info(uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt, void *arg)
struct ble_gatt_access_ctxt *ctxt, void *arg)
{
uint16_t uuid;
int rc;
uuid = ble_uuid_u16(ctxt->chr->uuid);
if (uuid == GATT_MODEL_NUMBER_UUID) {
rc = os_mbuf_append(ctxt->om, model_num, strlen(model_num));
return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
}
if (uuid == GATT_MANUFACTURER_NAME_UUID) {
rc = os_mbuf_append(ctxt->om, manuf_name, strlen(manuf_name));
return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
}
if (uuid == GATT_MODEL_NUMBER_UUID) {
rc = os_mbuf_append(ctxt->om, model_num, strlen(model_num));
return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
}
if (uuid == BLE_SVC_DIS_CHR_UUID16_SERIAL_NUMBER) {
rc = os_mbuf_append(ctxt->om, serial_num, strlen(serial_num));
return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
}
if (uuid == BLE_SVC_DIS_CHR_UUID16_FIRMWARE_REVISION) {
rc = os_mbuf_append(ctxt->om, firmware_ver, strlen(firmware_ver));
return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
}
if (uuid == BLE_SVC_DIS_CHR_UUID16_HARDWARE_REVISION) {
rc = os_mbuf_append(ctxt->om, hardware_ver, strlen(hardware_ver));
return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
}
assert(0);
return BLE_ATT_ERR_UNLIKELY;
}
static int
gatt_svr_chr_access_battery_level(uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt, void *arg)
{
MODLOG_DFLT(DEBUG, "[READ] battery level service: battery level value");
uint8_t level = 42;
int res = os_mbuf_append(ctxt->om, &level, sizeof(level));
return (res == 0) ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
}
void
gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg)
{

View File

@@ -17,6 +17,7 @@
* under the License.
*/
#include <assert.h>
#include <string.h>
#include <stdio.h>
@@ -25,10 +26,13 @@
#include "console/console.h"
#include "config/private_syscfg.h"
#include "nimble/ble.h"
#include "host/ble_hs.h"
#include "services/gap/ble_svc_gap.h"
#include "blehr_sens.h"
#include "host/ble_hs.h"
#include "host/ble_gatt.h"
#include "services/gap/ble_svc_gap.h"
#include "services/gatt/ble_svc_gatt.h"
#include "blehr_sens.h"
#include "nimble/nimble_port_tencentos_tiny.h"
static bool notify_state;
@@ -36,11 +40,11 @@ static bool notify_state;
/* Connection handle */
static uint16_t conn_handle;
static const char *device_name = "blehr_sensor";
static const char *device_name = "Heart Rate Sensor";
static int blehr_gap_event(struct ble_gap_event *event, void *arg);
static uint8_t blehr_addr_type;
static uint8_t ble_addr_type;
/* Sending notify data timer */
static struct ble_npl_callout blehr_tx_timer;
@@ -48,6 +52,11 @@ static struct ble_npl_callout blehr_tx_timer;
/* Variable to simulate heart beats */
static uint8_t heartrate = 90;
static ble_uuid16_t svc_adv_uuids[] =
{
{ BLE_UUID_TYPE_16, GATT_HRS_UUID }
};
/*
* Enables advertising with parameters:
* o General discoverable mode
@@ -88,17 +97,33 @@ blehr_advertise(void)
fields.name_len = strlen(device_name);
fields.name_is_complete = 1;
/*** 0x02,0x03 - 16-bit service class UUIDs. */
fields.num_uuids16 = 1;
fields.uuids16 = svc_adv_uuids;
fields.uuids16_is_complete = 1;
rc = ble_gap_adv_set_fields(&fields);
if (rc != 0) {
MODLOG_DFLT(ERROR, "error setting advertisement data; rc=%d\n", rc);
return;
}
/*** 0xff - Manufacturer specific data. */
memset(&fields, 0, sizeof(fields));
fields.mfg_data = (uint8_t*)"GZH:Bluetooth-BLE,177341833";
fields.mfg_data_len = 27;
rc = ble_gap_adv_rsp_set_fields(&fields);
if (rc != 0) {
MODLOG_DFLT(ERROR, "error setting advertisement data; rc=%d\n", rc);
return;
}
/* Begin advertising */
memset(&adv_params, 0, sizeof(adv_params));
adv_params.conn_mode = BLE_GAP_CONN_MODE_UND;
adv_params.disc_mode = BLE_GAP_DISC_MODE_GEN;
rc = ble_gap_adv_start(blehr_addr_type, NULL, BLE_HS_FOREVER,
rc = ble_gap_adv_start(ble_addr_type, NULL, BLE_HS_FOREVER,
&adv_params, blehr_gap_event, NULL);
if (rc != 0) {
MODLOG_DFLT(ERROR, "error enabling advertisement; rc=%d\n", rc);
@@ -109,11 +134,7 @@ blehr_advertise(void)
static void
blehr_tx_hrate_stop(void)
{
#if 0
os_callout_stop(&blehr_tx_timer);
#else
ble_npl_callout_stop(&blehr_tx_timer);
#endif
}
/* Reset heartrate measurment */
@@ -122,11 +143,7 @@ blehr_tx_hrate_reset(void)
{
int rc;
#if 0
rc = os_callout_reset(&blehr_tx_timer, OS_TICKS_PER_SEC);
#else
rc = ble_npl_callout_reset(&blehr_tx_timer, OS_TICKS_PER_SEC);
#endif
assert(rc == 0);
}
@@ -213,7 +230,6 @@ blehr_gap_event(struct ble_gap_event *event, void *arg)
event->mtu.conn_handle,
event->mtu.value);
break;
}
return 0;
@@ -225,53 +241,24 @@ blehr_on_sync(void)
int rc;
/* Use privacy */
rc = ble_hs_id_infer_auto(0, &blehr_addr_type);
rc = ble_hs_id_infer_auto(0, &ble_addr_type);
assert(rc == 0);
/* Begin advertising */
blehr_advertise();
}
void sysinit(void)
int ble_boot(void)
{
k_err_t err;
err = tos_knl_init();
if (err != K_ERR_NONE) {
return;
}
}
/*
* main
*
* The main task for the project. This function initializes the packages,
* then starts serving events from default event queue.
*
* @return int NOTE: this function should never return!
*/
#if 0
int
main(void)
{
int rc;
board_init();
/* Initialize OS */
sysinit();
int rc = 0;
/* Initialize the NimBLE host configuration */
ble_hs_cfg.sync_cb = blehr_on_sync;
#if 0
os_callout_init(&blehr_tx_timer, os_eventq_dflt_get(),
blehr_tx_hrate, NULL);
#else
ble_npl_callout_init(&blehr_tx_timer, nimble_port_get_dflt_eventq(),
blehr_tx_hrate, NULL);
#endif
blehr_tx_hrate, NULL);
/* verify and add our custom services */
rc = gatt_svr_init();
assert(rc == 0);
@@ -279,59 +266,15 @@ main(void)
rc = ble_svc_gap_device_name_set(device_name);
assert(rc == 0);
/* As the last thing, process events from default event queue */
#if 0
while (1) {
os_eventq_run(os_eventq_dflt_get());
}
#else
/* run an event loop for handling the heart rate update events */
extern void nimble_port_run(void);
nimble_port_tencentos_tiny_init(nimble_port_run);
#endif
return 0;
}
#else
int blehr_boot(void)
{
int rc;
/* Initialize the NimBLE host configuration */
ble_hs_cfg.sync_cb = blehr_on_sync;
#if 0
os_callout_init(&blehr_tx_timer, os_eventq_dflt_get(),
blehr_tx_hrate, NULL);
#else
ble_npl_callout_init(&blehr_tx_timer, nimble_port_get_dflt_eventq(),
blehr_tx_hrate, NULL);
#endif
rc = gatt_svr_init();
assert(rc == 0);
/* Set the default device name */
rc = ble_svc_gap_device_name_set(device_name);
assert(rc == 0);
/* As the last thing, process events from default event queue */
#if 0
while (1) {
os_eventq_run(os_eventq_dflt_get());
}
#else
extern void nimble_port_run(void);
nimble_port_tencentos_tiny_init(nimble_port_run);
#endif
return 0;
}
#endif
k_task_t blehr_task;
k_stack_t blehr_stack[512];
k_task_t ble_boot_task;
k_stack_t ble_boot_stack[512];
int main(void)
{
@@ -342,9 +285,9 @@ int main(void)
nimble_port_init();
tos_task_create(&blehr_task, "blehr_boot", blehr_boot, NULL,
tos_task_create(&ble_boot_task, "boot", ble_boot, NULL,
4,
blehr_stack, sizeof(blehr_stack),
ble_boot_stack, sizeof(ble_boot_stack),
0);
tos_knl_start();
}

View File

@@ -0,0 +1,317 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "nimble/nimble_port_tencentos_tiny.h"
#include "nimble/ble.h"
#include "host/ble_gap.h"
#define BLE_RAW_ADV_DATA_LEN (31U)
typedef enum nimble_scan_status_en {
NIMBLE_SCAN_STATUS_STOPPED,
NIMBLE_SCAN_STATUS_SCANNING,
} nimble_scan_status_t;
typedef struct nimble_scan_item_st {
k_list_t list;
ble_addr_t addr; /* ble address */
uint8_t adv_data[BLE_RAW_ADV_DATA_LEN]; /* advertising data received */
uint8_t adv_data_len; /* length of the advertising data */
int8_t rssi; /* last RSSI of a scanned node */
uint8_t type; /* advertising packet type */
} nimble_scan_item_t;
/*
type: type of advertising packet, e.g BLE_HCI_ADV_TYPE_ADV_IND
disc_addr: advertising address of the source node
disc_rssi: RSSI value for the received packet
disc_adv_data: received advertising data
disc_adv_data_len: length of disc_adv_data in bytes
*/
typedef void(*nimble_scan_cb_t)(uint8_t type,
const ble_addr_t *disc_addr, int8_t disc_rssi,
const uint8_t *disc_adv_data, size_t disc_adv_data_len);
static uint8_t ble_addr_type;
static nimble_scan_cb_t scan_disc_cb = NULL;
static struct ble_gap_disc_params scan_params = { 0 };
static nimble_scan_item_t entry_pool[200];
TOS_LIST_DEFINE(free_entry);
TOS_LIST_DEFINE(scan_entry);
void nimble_scan_info_list_init(void)
{
int i = 0;
for (i = 0; i < sizeof(entry_pool) / sizeof(entry_pool[0]); ++i) {
tos_list_init(&entry_pool[i].list);
tos_list_add(&entry_pool[i].list, &free_entry);
}
}
void nimble_scan_item_addr_display(ble_addr_t *addr)
{
int i = 0;
printf("%02x", (int)addr->val[5]);
for (i = 4; i >=0; --i) {
printf(":%02x", addr->val[i]);
}
switch (addr->type) {
case BLE_ADDR_PUBLIC:
printf(" (PUBLIC)");
break;
case BLE_ADDR_RANDOM:
printf(" (RANDOM)");
break;
case BLE_ADDR_PUBLIC_ID:
printf(" (PUB_ID)");
break;
case BLE_ADDR_RANDOM_ID:
printf(" (RAND_ID)");
break;
default:
printf(" (UNKNOWN)");
break;
}
}
void nimble_scan_item_type_display(uint8_t type)
{
switch (type) {
case BLE_HCI_ADV_TYPE_ADV_IND:
printf(" [IND]");
break;
case BLE_HCI_ADV_TYPE_ADV_DIRECT_IND_HD:
printf(" [DIRECT_IND_HD]");
break;
case BLE_HCI_ADV_TYPE_ADV_SCAN_IND:
printf(" [SCAN_IND]");
break;
case BLE_HCI_ADV_TYPE_ADV_NONCONN_IND:
printf(" [NONCONN_IND]");
break;
case BLE_HCI_ADV_TYPE_ADV_DIRECT_IND_LD:
printf(" [DIRECT_IND_LD]");
break;
default:
printf(" [INVALID]");
break;
}
}
void nimble_scan_item_display(nimble_scan_item_t *item)
{
nimble_scan_item_addr_display(&item->addr);
nimble_scan_item_type_display(item->type);
printf(", rssi: %d\n", item->rssi);
printf("\n");
}
void nimble_scan_info_list_display(void)
{
k_list_t *curr;
nimble_scan_item_t *iter;
TOS_LIST_FOR_EACH(curr, &scan_entry) {
iter = TOS_LIST_ENTRY(curr, nimble_scan_item_t, list);
nimble_scan_item_display(iter);
}
}
nimble_scan_item_t *nimble_scan_item_alloc(void)
{
return TOS_LIST_FIRST_ENTRY_OR_NULL(&free_entry, nimble_scan_item_t, list);
}
int nimble_scan_item_is_exist(ble_addr_t *addr)
{
k_list_t *curr;
nimble_scan_item_t *iter;
TOS_LIST_FOR_EACH(curr, &free_entry) {
iter = TOS_LIST_ENTRY(curr, nimble_scan_item_t, list);
if (ble_addr_cmp(&addr, &iter->addr) == 0) {
return K_TRUE;
}
}
return K_FALSE;
}
void nimble_scan_info_save(uint8_t type, const ble_addr_t *disc_addr, int8_t disc_rssi,
const uint8_t *disc_adv_data, size_t disc_adv_data_len)
{
int i = 0;
nimble_scan_item_t *item;
if (nimble_scan_item_is_exist(disc_addr)) {
return;
}
item = nimble_scan_item_alloc();
if (!item) {
return;
}
memcpy(&item->addr, disc_addr, sizeof(ble_addr_t));
if (disc_adv_data) {
memcpy(&item->adv_data, disc_adv_data, disc_adv_data_len);
}
item->adv_data_len = disc_adv_data_len;
item->rssi = disc_rssi;
item->type = type;
tos_list_del(&item->list);
tos_list_add(&item->list, &scan_entry);
}
static int blescan_gap_event(struct ble_gap_event *event, void *arg)
{
/* only interested in the DISC event */
switch (event->type) {
case BLE_GAP_EVENT_DISC:
scan_disc_cb(event->disc.event_type, &event->disc.addr, event->disc.rssi,
event->disc.data, (size_t)event->disc.length_data);
break;
default:
/* should never happen */
MODLOG_DFLT(DEBUG, "unexpected event: (%d)\n", (int)event->type);
assert(0);
break;
}
return 0;
}
int nimble_scan_start(void)
{
int rc;
if (ble_gap_disc_active() != 0) {
return 0;
}
rc = ble_gap_disc(ble_addr_type, BLE_HS_FOREVER,
&scan_params, blescan_gap_event, NULL);
if (rc != 0) {
MODLOG_DFLT(DEBUG, "[scanner] err: start failed (%i)\n", rc);
return -1;
}
return 0;
}
void nimble_scan_stop(void)
{
int rc;
if (ble_gap_disc_active() != 1) {
return;
}
rc = ble_gap_disc_cancel();
assert(rc == 0);
}
nimble_scan_status_t nimble_scan_status(void)
{
return ble_gap_disc_active() == 1 ? NIMBLE_SCAN_STATUS_SCANNING : NIMBLE_SCAN_STATUS_STOPPED;
}
int nimble_scan_init(const struct ble_gap_disc_params *params, nimble_scan_cb_t disc_cb)
{
assert(disc_cb);
if (params) {
memcpy(&scan_params, params, sizeof(scan_params));
} else {
memset(&scan_params, 0, sizeof(scan_params));
}
scan_disc_cb = disc_cb;
return 0;
}
int ble_boot(void)
{
int rc;
extern void nimble_port_run(void);
nimble_port_tencentos_tiny_init(nimble_port_run);
/* make sure synchronization of host and controller is done */
while (!ble_hs_synced()) {
;
}
rc = ble_hs_util_ensure_addr(0);
assert(rc == 0);
rc = ble_hs_id_infer_auto(0, &ble_addr_type);
assert(rc == 0);
/* do a 'full time' scan, set the window equal the interval */
struct ble_gap_disc_params scan_params = {
.itvl = BLE_GAP_LIM_DISC_SCAN_INT,
.window = BLE_GAP_LIM_DISC_SCAN_WINDOW,
.filter_policy = 0, /* dummy */
.limited = 0, /* no limited discovery */
.passive = 0, /* no passive scanning */
.filter_duplicates = 0, /* no duplicate filtering */
};
nimble_scan_info_list_init();
/* initialize the nimble scanner */
nimble_scan_init(&scan_params, nimble_scan_info_save);
rc = nimble_scan_start();
assert(rc == 0);
tos_task_delay(2000);
nimble_scan_stop();
nimble_scan_info_list_display();
return 0;
}
k_task_t ble_boot_task;
k_stack_t ble_boot_stack[2048];
int main(void)
{
board_init();
/* Initialize OS */
tos_knl_init();
nimble_port_init();
/*
in this case, ble_boot_task's priority must lower than MYNEWT_VAL_BLE_HOST_TASK_PRIORITY && MYNEWT_VAL_BLE_LL_TASK_PRIORITY,
numerically bigger
to give the host and ll task a chance to run first just after the nimble_port_tencentos_tiny_init.
*/
tos_task_create(&ble_boot_task, "ble_boot", ble_boot, NULL,
6,
ble_boot_stack, sizeof(ble_boot_stack),
0);
tos_knl_start();
}