Hi!
So I've got one BLE Central and one BLE Peripheral that I should communicate. Ive based the Central code on nrf Connect SDK's "peripheral_gatt_dm" sample. First I tested my new "central_gatt_dm" sample up against a "perpheral_hr" Heart Rate sample successfully. It displayed what was supposed to be displayed in terminal - list of services and characteristics etc. However, when I changed the peripheral to reveal a custom service, I could no longer perform the test. Let me first show the Central code and then the peripheral code:
(Hardware: both central and peripheral are nrf52840s)
Central:
/*
* Copyright (c) 2018 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: LicenseRef-BSD-5-Clause-Nordic
*/
#include <zephyr/types.h>
#include <stddef.h>
#include <string.h>
#include <errno.h>
#include <sys/printk.h>
#include <sys/byteorder.h>
#include <zephyr.h>
#include <drivers/gpio.h>
#include <soc.h>
#include <settings/settings.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
#include <bluetooth/conn.h>
#include <bluetooth/uuid.h>
#include <bluetooth/gatt.h>
#include <bluetooth/scan.h>
#include <bluetooth/gatt_dm.h>
#define BT_UUID_READ_WRITE_SERVICE \
BT_UUID_DECLARE_128(0x3E, 0x09, 0x99, 0x10, 0x29, 0x3F, 0x11, 0xE4, 0x93, \
0xBD, 0xAF, 0xD0, 0xFE, 0x6D, 0x1D, 0xA0)
static struct bt_conn *default_conn;
static void scan_filter_match(struct bt_scan_device_info *device_info,
struct bt_scan_filter_match *filter_match,
bool connectable)
{
int err;
char addr[BT_ADDR_LE_STR_LEN];
bt_addr_le_to_str(device_info->addr, addr, sizeof(addr));
printk("Filters matched. Address: %s connectable: %s\n",
addr, connectable ? "yes" : "no");
err = bt_scan_stop();
if (err) {
printk("Stop LE scan failed (err %d)\n", err);
}
}
static void scan_connecting_error(struct bt_scan_device_info *device_info)
{
printk("Connecting failed\n");
}
static void scan_connecting(struct bt_scan_device_info *device_info,
struct bt_conn *conn)
{
default_conn = bt_conn_ref(conn);
}
static void scan_filter_no_match(struct bt_scan_device_info *device_info,
bool connectable)
{
int err;
struct bt_conn *conn;
char addr[BT_ADDR_LE_STR_LEN];
if (device_info->adv_info.adv_type == BT_GAP_ADV_TYPE_ADV_DIRECT_IND) {
bt_addr_le_to_str(device_info->addr, addr, sizeof(addr));
printk("Direct advertising received from %s\n", addr);
bt_scan_stop();
err = bt_conn_le_create(device_info->addr,
BT_CONN_LE_CREATE_CONN,
device_info->conn_param, &conn);
if (!err) {
default_conn = bt_conn_ref(conn);
bt_conn_unref(conn);
}
}
}
BT_SCAN_CB_INIT(scan_cb, scan_filter_match, scan_filter_no_match,
scan_connecting_error, scan_connecting);
static void discover_all_completed(struct bt_gatt_dm *dm, void *ctx)
{
char uuid_str[300]; //was 37
const struct bt_gatt_dm_attr *gatt_service_attr =
bt_gatt_dm_service_get(dm);
const struct bt_gatt_service_val *gatt_service =
bt_gatt_dm_attr_service_val(gatt_service_attr);
size_t attr_count = bt_gatt_dm_attr_cnt(dm);
bt_uuid_to_str(gatt_service->uuid, uuid_str, sizeof(uuid_str));
printk("Found service %s\n", uuid_str);
printk("Attribute count: %d\n", attr_count);
bt_gatt_dm_data_print(dm);
bt_gatt_dm_data_release(dm);
bt_gatt_dm_continue(dm, NULL);
}
static void discover_all_service_not_found(struct bt_conn *conn, void *ctx)
{
printk("No more services\n");
}
static void discover_all_error_found(struct bt_conn *conn, int err, void *ctx)
{
printk("The discovery procedure failed, err %d\n", err);
}
static struct bt_gatt_dm_cb discover_all_cb = {
.completed = discover_all_completed,
.service_not_found = discover_all_service_not_found,
.error_found = discover_all_error_found,
};
static void connected(struct bt_conn *conn, u8_t err)
{
char addr[BT_ADDR_LE_STR_LEN];
if (err) {
printk("Connection failed (err 0x%02x)\n", err);
return;
}
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
printk("Connected %s\n", addr);
err = bt_gatt_dm_start(conn, NULL, &discover_all_cb, NULL);
if (err) {
printk("Failed to start discovery (err %d)\n", err);
}
}
static void disconnected(struct bt_conn *conn, u8_t reason)
{
char addr[BT_ADDR_LE_STR_LEN];
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
printk("Disconnected from %s (reason 0x%02x)\n", addr, reason);
}
static void security_changed(struct bt_conn *conn, bt_security_t level,
enum bt_security_err err)
{
char addr[BT_ADDR_LE_STR_LEN];
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
if (!err) {
printk("Security changed: %s level %u\n", addr, level);
} else {
printk("Security failed: %s level %u err %d\n", addr, level,
err);
}
}
static struct bt_conn_cb conn_callbacks = {
.connected = connected,
.disconnected = disconnected,
.security_changed = security_changed,
};
static void scan_init(void)
{
int err;
struct bt_scan_init_param scan_init = {
.connect_if_match = 1,
.scan_param = NULL,
.conn_param = BT_LE_CONN_PARAM_DEFAULT
};
bt_scan_init(&scan_init);
bt_scan_cb_register(&scan_cb);
err = bt_scan_filter_add(BT_SCAN_FILTER_TYPE_UUID, BT_UUID_READ_WRITE_SERVICE);
if (err) {
printk("Scanning filters cannot be set (err %d)\n", err);
return;
}
err = bt_scan_filter_enable(BT_SCAN_UUID_FILTER, false);
if (err) {
printk("Filters cannot be turned on (err %d)\n", err);
}
}
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 void pairing_confirm(struct bt_conn *conn)
{
char addr[BT_ADDR_LE_STR_LEN];
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
bt_conn_auth_pairing_confirm(conn);
printk("Pairing confirmed: %s\n", addr);
}
static void pairing_complete(struct bt_conn *conn, bool bonded)
{
char addr[BT_ADDR_LE_STR_LEN];
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
printk("Pairing completed: %s, bonded: %d\n", addr, bonded);
}
static void pairing_failed(struct bt_conn *conn, enum bt_security_err reason)
{
char addr[BT_ADDR_LE_STR_LEN];
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
printk("Pairing failed conn: %s, reason %d\n", addr, reason);
}
static struct bt_conn_auth_cb conn_auth_callbacks = {
.cancel = auth_cancel,
.pairing_confirm = pairing_confirm,
.pairing_complete = pairing_complete,
.pairing_failed = pairing_failed
};
void init_ble_custom_central(void)
{
int err;
printk("Starting GATT Discovery Manager example\n");
err = bt_enable(NULL);
if (err) {
printk("BLE init failed with error code %d\n", err);
return;
}
printk("Bluetooth initialized\n");
if (IS_ENABLED(CONFIG_SETTINGS)) {
settings_load();
}
scan_init();
bt_conn_cb_register(&conn_callbacks);
if (err) {
printk("Failed to register connection callbacks.\n");
return;
}
err = bt_conn_auth_cb_register(&conn_auth_callbacks);
if (err) {
printk("Failed to register authorization callbacks.\n");
return;
}
err = bt_scan_start(BT_SCAN_TYPE_SCAN_ACTIVE);
if (err) {
printk("Scanning failed to start (err %d)\n", err);
return;
}
printk("Scanning successfully started\n");
}
The only thing i've changed after the working demo with the Heart Rate service, was:
err = bt_scan_filter_add(BT_SCAN_FILTER_TYPE_UUID, BT_UUID_HRS);
changed to:
err = bt_scan_filter_add(BT_SCAN_FILTER_TYPE_UUID, BT_UUID_READ_WRITE_SERVICE);.
Now let me present the Peripheral code in case that is needed:
Peripheral main.c:
#include <bluetooth/bluetooth.h>
#include <bluetooth/conn.h>
#include <bluetooth/gatt.h>
#include <bluetooth/hci.h>
#include <bluetooth/uuid.h>
#include <device.h>
#include <errno.h>
#include <services/read_write_service.h>
#include <settings/settings.h>
#include <stddef.h>
#include <string.h>
#include <sys/byteorder.h>
#include <sys/printk.h>
#include <sys/util.h>
#include <zephyr.h>
#include <zephyr/types.h>
#define DEVICE_NAME CONFIG_BT_DEVICE_NAME
#define DEVICE_NAME_LEN (sizeof(DEVICE_NAME) - 1)
struct bt_conn *default_conn;
static const struct bt_data ad[] = {
BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
BT_DATA(BT_DATA_NAME_COMPLETE, DEVICE_NAME, DEVICE_NAME_LEN),
};
static void connected(struct bt_conn *conn, u8_t err) {
if (err) {
printk("Connection failed (err %u)\n", err);
} else {
printk("Connected\n");
default_conn = bt_conn_ref(conn);
}
}
static void disconnected(struct bt_conn *conn, u8_t reason) {
printk("Disconnected (reason %u)\n", reason);
if (default_conn) {
bt_conn_unref(default_conn);
default_conn = NULL;
}
}
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;
}
read_write_service_init();
/*if (IS_ENABLED(CONFIG_SETTINGS)) {
settings_load();
}*/
err = bt_le_adv_start(BT_LE_ADV_CONN, ad, ARRAY_SIZE(ad), NULL, 0);
if (err) {
printk("Advertising failed to start (err %d)\n", err);
return;
}
printk("Advertising successfully started\n");
}
void main(void) {
printk("starting in main \n\n");
int err;
err = bt_enable(bt_ready);
if (err) {
printk("Bluetooth initialization failed.");
return;
}
bt_conn_cb_register(&conn_callbacks);
}
With the accompanying read_write_service.c:
#include <bluetooth/bluetooth.h>
#include <bluetooth/conn.h>
#include <bluetooth/gatt.h>
#include <bluetooth/hci.h>
#include <bluetooth/uuid.h>
#include <errno.h>
#include <services/read_write_service.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include <sys/byteorder.h>
#include <sys/printk.h>
#include <zephyr.h>
#include <zephyr/types.h>
#define BT_UUID_READ_WRITE_SERVICE \
BT_UUID_DECLARE_128(0x3E, 0x09, 0x99, 0x10, 0x29, 0x3F, 0x11, 0xE4, 0x93, \
0xBD, 0xAF, 0xD0, 0xFE, 0x6D, 0x1D, 0xA0)
// input to the peripheral device
#define BT_UUID_INPUT \
BT_UUID_DECLARE_128(0x3E, 0x09, 0x99, 0x11, 0x29, 0x3F, 0x11, 0xE4, 0x93, \
0xBD, 0xAF, 0xD0, 0xFE, 0x6D, 0x1D, 0xA1)
// output from the peripheral device
#define BT_UUID_OUTPUT \
BT_UUID_DECLARE_128(0x3E, 0x09, 0x99, 0x11, 0x29, 0x3F, 0x11, 0xE4, 0x93, \
0xBD, 0xAF, 0xD0, 0xFE, 0x6D, 0x1D, 0xA2)
#define BT_UUID_NAMING_CHARACTERISTIC \
BT_UUID_DECLARE_128(0x3E, 0x09, 0x99, 0x11, 0x29, 0x3F, 0x11, 0xE4, 0x93, \
0xBD, 0xAF, 0xD0, 0xFE, 0x6D, 0x1D, 0xA3)
// BT_GATT_CHARACTERISTIC(_uuid, _props, _perm, _read, _write, _value)
static struct bt_gatt_attr attrs[] = {
BT_GATT_PRIMARY_SERVICE(BT_UUID_READ_WRITE_SERVICE),
BT_GATT_CHARACTERISTIC(BT_UUID_INPUT, BT_GATT_CHRC_WRITE,
BT_GATT_PERM_WRITE, NULL, write_input, NULL),
BT_GATT_CHARACTERISTIC(BT_UUID_OUTPUT, BT_GATT_CHRC_READ, BT_GATT_PERM_READ,
read_output, NULL, NULL),
BT_GATT_CHARACTERISTIC(BT_UUID_NAMING_CHARACTERISTIC, BT_GATT_CHRC_READ,
BT_GATT_PERM_READ, naming_output, NULL, NULL),
};
static struct bt_gatt_service read_write_service = BT_GATT_SERVICE(attrs);
u8_t a_number = 0;
const char characteristic_name[6] = {'H', 'e', 'l', 'l', 'o', '\0'};
void read_write_service_init() {
bt_gatt_service_register(&read_write_service);
}
ssize_t write_input(struct bt_conn *conn, const struct bt_gatt_attr *attr,
const void *buf, u16_t len, u16_t offset, u8_t flags) {
const u8_t *new_number = buf;
if (!len) {
return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
}
if (*new_number >= 0 && *new_number <= 10) {
a_number = *new_number;
} else {
return BT_GATT_ERR(BT_ATT_ERR_VALUE_NOT_ALLOWED);
}
printk("write_input [%d]\n", a_number);
return len;
}
ssize_t read_output(struct bt_conn *conn, const struct bt_gatt_attr *attr,
void *buf, u16_t len, u16_t offset) {
printk("read_output\n");
return bt_gatt_attr_read(conn, attr, buf, len, offset, &a_number,
sizeof(a_number));
}
ssize_t naming_output(struct bt_conn *conn, const struct bt_gatt_attr *attr,
void *buf, u16_t len, u16_t offset) {
printk("read_output\n");
return bt_gatt_attr_read(conn, attr, buf, len, offset, characteristic_name,
sizeof(characteristic_name));
}
Now, I can connect to the read/write custom service on nrf connect mobile and access the characteristics such that I know that it works. Why can I not find the peripheral when I am filtering on my UUID?
Looking forward to your response!