Customized BLE service: fail to connect with mobile phone using nRF Connect mobile app

Hello,

I'm using nRF54L15DK and nRF Connect SDK 2.9.0. I'm running a BLE peripheral code and try to pair with my iPhone using nRF Connect mobile app. After I hit "connect", I saw the error message "Failed to Connect: Peer removed pairing information".

in the virtual COM I saw

Connected
[00:00:06.508,767] <inf> myADC_demo: Timer callback triggered! Count = 1

[00:00:06.515,922] <inf> myADC_demo: ADC voltage = 499 mV, Temperature = -5.96 degreeC
[00:00:07.008,769] <inf> myADC_demo: Timer callback triggered! Count = 2

[00:00:07.015,925] <inf> myADC_demo: ADC voltage = 544 mV, Temperature = -3.90 degreeC
Disconnected, reason 0x13 

my main.c

/* BLE part */

static const struct bt_data ad[] = {
	/* Let peers know we're general discoverable and BR/EDR is not supported */
	BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
	BT_DATA_BYTES(BT_DATA_UUID16_ALL,
		      BT_UUID_16_ENCODE(BT_UUID_HTS_VAL),
		      BT_UUID_16_ENCODE(BT_UUID_DIS_VAL),
		      BT_UUID_16_ENCODE(BT_UUID_BAS_VAL)),
};

static const struct bt_data sd[] = {
	// ADV too long if add the following 128-bit UUID
	//  /* Advertise LBS (128-bit) */
	//  BT_DATA_BYTES(BT_DATA_UUID128_ALL, BT_UUID_LBS_VAL),

	//  /* Advertise ECAFE (128-bit) */
	//  BT_DATA_BYTES(BT_DATA_UUID128_ALL, BT_UUID_ECAFES_VAL),
	
	/* Also include our device name here */
    BT_DATA(BT_DATA_NAME_COMPLETE,
		CONFIG_BT_DEVICE_NAME,
		(sizeof(CONFIG_BT_DEVICE_NAME) - 1)),
};

static void connected(struct bt_conn *conn, uint8_t err)
{
	if (err) {
		printk("Connection failed, err 0x%02x %s\n", err, bt_hci_err_to_str(err));
	} else {
		printk("Connected\n");
		dk_set_led_on(CON_STATUS_LED);
	}
}

static void disconnected(struct bt_conn *conn, uint8_t reason)
{
	printk("Disconnected, reason 0x%02x %s\n", reason, bt_hci_err_to_str(reason));
	dk_set_led_off(CON_STATUS_LED);
}

BT_CONN_CB_DEFINE(conn_callbacks) = {
	.connected = connected,
	.disconnected = disconnected,
};

static int init_button(void)
{
	int err;

	/*dk_buttons_init this function is defined in <dk_buttons_and_leds.h>*/
	/*should build first and then you can see where it is defined*/
	/*pass a callback function (we name it as button_changed), so whenever button state changes, */
	/* this callback function will be called*/
	err = dk_buttons_init(button_changed);
	if (err) {
		printk("Cannot init buttons (err: %d)\n", err);
	}

	return err;
}

static void bt_ready(void)
{
	int err;

	printk("Bluetooth initialized\n");

	hts_init();

	/* start advertising */

	err = bt_le_adv_start(BT_LE_ADV_CONN_ONE_TIME, 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,
};

my BLE modules:

hts.c

/** @file
 *  @brief HTS Service sample
 */

/*
 * Copyright (c) 2020 SixOctets Systems
 * Copyright (c) 2019 Aaron Tsui <[email protected]>
 *
 * SPDX-License-Identifier: Apache-2.0
 */
#include <zephyr/types.h>

#include <stdio.h>
#include <stddef.h>
#include <string.h>
#include <errno.h>

#include <zephyr/kernel.h>
#include <zephyr/drivers/sensor.h>
#include <zephyr/sys/printk.h>
#include <zephyr/sys/byteorder.h>
#include <zephyr/logging/log.h>

#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/hci.h>
#include <zephyr/bluetooth/conn.h>
#include <zephyr/bluetooth/uuid.h>
#include <zephyr/bluetooth/gatt.h>

#include "hts.h"

/* if want to use LOG in this .c file, should declare the registerd LOG module in main.c*/
LOG_MODULE_DECLARE(myADC_demo);

/* if is nRF5, use the temperature sensor on board*/
#ifdef CONFIG_TEMP_NRF5
static const struct device *temp_dev = DEVICE_DT_GET_ANY(nordic_nrf_temp);
#else
static const struct device *temp_dev;
#endif

static char my_sensor_desc[] = "My ADC Characteristic";

static uint8_t simulate_htm;
static bool indicate_myadc_enabled;
static bool indicate_lbs_enabled;
static bool button_state;
static struct my_lbs_cb lbs_cb;


static uint8_t indicating;
static struct bt_gatt_indicate_params ind_params;
static struct bt_gatt_indicate_params ind_params_adc;

/* Storage for encoded temperature values (5 bytes per SFLOAT format) */
static uint8_t sensor1_value[5];
static uint8_t sensor2_value[5];

/* Temperature Presentation Format data for the ADC Temperature Characteristic.*/
static const struct bt_gatt_cpf temp_cpf_data = {
    .format = BT_GATT_CPF_FORMAT_SFLOAT,
    .exponent = -2,
    .unit = BT_GATT_CPF_UNIT_TEMP_CELSIUS,
    .description = 0,
};

/* Implement the configuration change callback function */
/* This function is called when a GATT client enables or disables indication.*/
static void htmc_ccc_cfg_changed(const struct bt_gatt_attr *attr,
				 uint16_t value)
{
	simulate_htm = (value == BT_GATT_CCC_INDICATE) ? 1 : 0;
}

static void htmc_ccc_myadc_cfg_changed(const struct bt_gatt_attr *attr,
	uint16_t value)
{
	indicate_myadc_enabled = (value == BT_GATT_CCC_INDICATE);
}

/*This function is called when a GATT client enables or disables indication.*/
static void mylbsbc_ccc_cfg_changed(const struct bt_gatt_attr *attr,
	uint16_t value)
{
	indicate_lbs_enabled = (value == BT_GATT_CCC_INDICATE);
}

/* LBS service, should be placed before SERVICE definition */
/* callback of writing value in LED */
static ssize_t write_led(struct bt_conn *conn, const struct bt_gatt_attr *attr, const void *buf,
	uint16_t len, uint16_t offset, uint8_t flags)
{
	LOG_INF("Attribute write, handle: %u, conn: %p", attr->handle, (void *)conn);

	if (len != 1U) {
		LOG_INF("Write led: Incorrect data length");
		return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
	}

	if (offset != 0) {
		LOG_INF("Write led: Incorrect data offset");
		return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
	}

	if (lbs_cb.led_cb) {
		// Read the received value
		uint8_t val = *((uint8_t *)buf);

		if (val == 0x00 || val == 0x01) {
			// Call the application callback function to update the LED state
			lbs_cb.led_cb(val ? true : false);
			LOG_INF("Write led: %u", val);
	} else {
		LOG_INF("Write led: Incorrect value");
		return BT_GATT_ERR(BT_ATT_ERR_VALUE_NOT_ALLOWED);
		}
	}

	return len;
}

static ssize_t read_button(struct bt_conn *conn, const struct bt_gatt_attr *attr, void *buf,
	  uint16_t len, uint16_t offset)
{
	// get a pointer to button_state which is passed in the BT_GATT_CHARACTERISTIC() and stored in attr->user_data
	const char *value = attr->user_data;

	LOG_INF("Attribute read, handle: %u, conn: %p", attr->handle, (void *)conn);

	if (lbs_cb.button_cb) {
		// Call the application callback function to update the get the current value of the button
		button_state = lbs_cb.button_cb();
		return bt_gatt_attr_read(conn, attr, buf, len, offset, value, sizeof(*value));
	}

	return 0;
}

/* Data transmission: indication */

static void indicate_cb(struct bt_conn *conn,
			struct bt_gatt_indicate_params *params, uint8_t err)
{
	printk("Indication %s\n", err != 0U ? "fail" : "success");
}

static void indicate_destroy(struct bt_gatt_indicate_params *params)
{
	printk("Indication complete\n");
	indicating = 0U;
}

/* LED Button Service Declaration */
BT_GATT_SERVICE_DEFINE(
	my_lbs_svc, BT_GATT_PRIMARY_SERVICE(BT_UUID_LBS),
	/* STEP 1 - Modify the Button characteristic declaration to support indication */
	BT_GATT_CHARACTERISTIC(BT_UUID_LBS_BUTTON,
		BT_GATT_CHRC_READ | BT_GATT_CHRC_INDICATE,
		BT_GATT_PERM_READ, read_button, NULL,
		&button_state),
	/* STEP 2 - Create and add the Client Characteristic Configuration Descriptor */
	BT_GATT_CCC(mylbsbc_ccc_cfg_changed, // configuration changed callback
		BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),
	BT_GATT_CHARACTERISTIC(BT_UUID_LBS_LED, BT_GATT_CHRC_WRITE, BT_GATT_PERM_WRITE, NULL,
			       write_led, NULL),

);

/* Health Thermometer Service Declaration */
BT_GATT_SERVICE_DEFINE(hts_svc,
	BT_GATT_PRIMARY_SERVICE(BT_UUID_HTS),
	/* attribute 2*/
	BT_GATT_CHARACTERISTIC(BT_UUID_HTS_MEASUREMENT, // UUID
		BT_GATT_CHRC_READ | BT_GATT_CHRC_INDICATE, 
		BT_GATT_PERM_READ, //permission
		NULL, // read callback function pointer
		NULL, // write callback
		NULL), // user data
	BT_GATT_CCC(htmc_ccc_cfg_changed, // configuration changed callback
		BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),
	
	/* more optional Characteristics */

	/*  */
	/* ADC temperature measurement characteristic */
	/* using the same UUID doesn't work, unless create another service? */
	BT_GATT_CHARACTERISTIC(BT_UUID_MY_ADC, // UUID
		BT_GATT_CHRC_INDICATE, 
		BT_GATT_PERM_NONE, //permission
		NULL, // read callback function pointer
		NULL, // write callback
		NULL), // user data
	BT_GATT_CCC(htmc_ccc_myadc_cfg_changed, // configuration changed callback
		BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),
	BT_GATT_CPF(&temp_cpf_data),
	/* Characteristic User Description Descriptor Declaration.
     * This descriptor informs the client that the characteristic's user-friendly name is "xxx".
     */
	BT_GATT_DESCRIPTOR(BT_UUID_DECLARE_16(0x2901),  /* UUID for Characteristic User Description */
		BT_GATT_PERM_READ,
		NULL, NULL,
		my_sensor_desc),
);

/* A function to register application callbacks for the LED and Button characteristics  */
int my_lbs_init(struct my_lbs_cb *callbacks)
{
	if (callbacks) {
		lbs_cb.led_cb = callbacks->led_cb;
		lbs_cb.button_cb = callbacks->button_cb;
	}

	return 0;
}


double get_temperature_sensor1(void)
{
    /* Temperature measurements simulation */
	struct sensor_value temp_value;

	static double temperature = 20U;

	int r;

	/* if temp_dev == NULL, means no temperature device. Then simulate a fake temp value*/
	if (!temp_dev) {
		temperature++;
		if (temperature == 30U) {
			temperature = 20U;
		}
		/* if no temperature device, jump over the following sensor data fetch code blocks*/
		goto ready_return;
	}

	/* fetch temperature data from on-board temperature sensor */
	/* begin of data fetch code blocks */
	r = sensor_sample_fetch(temp_dev);
	if (r) {
		printk("sensor_sample_fetch failed return: %d\n", r);
	}

	r = sensor_channel_get(temp_dev, SENSOR_CHAN_DIE_TEMP,
					&temp_value);
	if (r) {
		printk("sensor_channel_get failed return: %d\n", r);
	}

	temperature = sensor_value_to_double(&temp_value);
	
ready_return:
	printf("temperature is %gC\n", temperature);
    return temperature;
}

static void encode_temperature(double temperature, uint8_t *out)
{
    uint32_t mantissa = (uint32_t)(temperature * 100);  // scale temperature
    uint8_t exponent = (uint8_t)-2;
    
    out[0] = 0;  /* 0 means that the temperature value is in celsius */
	/*Put a 24-bit integer as little-endian to arbitrary location.*/
    sys_put_le24(mantissa, &out[1]);  // Write 3 bytes (little-endian)
    out[4] = exponent;
}

void hts_init(void)
{
	if (temp_dev == NULL || !device_is_ready(temp_dev)) {
		printk("no temperature device; using simulated data\n");
		temp_dev = NULL;
	} else {
		printk("temp device is %p, name is %s\n", temp_dev,
		       temp_dev->name);
	}
}

void hts_indicate(double sensor_data)
{
	/*If indications are enabled, populate ind_params.*/
	if (simulate_htm == 0) {
		return;
	}
	
	if (indicating) {
		return;
	}
	
	encode_temperature(sensor_data, sensor1_value);

	/* configue ind_param */
	ind_params.attr = &hts_svc.attrs[2];
	ind_params.func = indicate_cb;
	ind_params.destroy = indicate_destroy;
	ind_params.data = &sensor1_value;
	ind_params.len = sizeof(sensor1_value);

	if (bt_gatt_indicate(NULL, &ind_params) == 0) {
		/* if indicating, set flag so that don't start the next one*/
		indicating = 1U;
	}
}


hts.h

/** @file
 *  @brief HTS Service sample
 */

/*
 * Copyright (c) 2019 Aaron Tsui <[email protected]>
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#ifdef __cplusplus
extern "C" {
#endif

#include <zephyr/types.h>




/* 0x272F means Temperature in Celsius */
#ifndef BT_GATT_CPF_UNIT_TEMP_CELSIUS
#define BT_GATT_CPF_UNIT_TEMP_CELSIUS 0x272F
#endif

/* Define the SFLOAT format constant if not already defined */
#ifndef BT_GATT_CPF_FORMAT_SFLOAT
#define BT_GATT_CPF_FORMAT_SFLOAT 0x09
#endif

/* Define the Bluetooth SIG namespace if not already defined */
#ifndef BT_GATT_CPF_NAMESPACE_BTSIG
#define BT_GATT_CPF_NAMESPACE_BTSIG 1
#endif

#define MY_TEMP_SERVICE_UUID 0x1809
#define TEMP_CHAR_UUID       0x2A1C


double get_temperature_sensor1(void);
static void encode_temperature(double temperature, uint8_t *out);
void hts_init(void);
void hts_indicate(double sensor_data);

/** @brief ADC temp Characteristic UUID. */
/* random UUID generator */
#define BT_UUID_MY_ADC_VAL   \
	BT_UUID_128_ENCODE(0x1f1d06ff, 0xdf38, 0x4bfa, 0xa42e, 0xc3fb6628b8ee)

#define BT_UUID_MY_ADC BT_UUID_DECLARE_128(BT_UUID_MY_ADC_VAL)


/* LBS (LED and button) service */
/** @brief LBS Service UUID. */
#define BT_UUID_LBS_VAL BT_UUID_128_ENCODE(0x00001523, 0x1212, 0xefde, 0x1523, 0x785feabcd123)

/** @brief Button Characteristic UUID. */
#define BT_UUID_LBS_BUTTON_VAL   \
	BT_UUID_128_ENCODE(0x00001524, 0x1212, 0xefde, 0x1523, 0x785feabcd123)

/** @brief LED Characteristic UUID. */
#define BT_UUID_LBS_LED_VAL \
	BT_UUID_128_ENCODE(0x00001525, 0x1212, 0xefde, 0x1523, 0x785feabcd123)

/* STEP 11.1 - Assign a UUID to the MYSENSOR characteristic */
/** @brief LED Characteristic UUID. */
#define BT_UUID_LBS_MYSENSOR_VAL \
	BT_UUID_128_ENCODE(0x00001526, 0x1212, 0xefde, 0x1523, 0x785feabcd123)



#define BT_UUID_LBS BT_UUID_DECLARE_128(BT_UUID_LBS_VAL)
#define BT_UUID_LBS_BUTTON BT_UUID_DECLARE_128(BT_UUID_LBS_BUTTON_VAL)
#define BT_UUID_LBS_LED BT_UUID_DECLARE_128(BT_UUID_LBS_LED_VAL)

/* STEP 11.2 - Convert the array to a generic UUID */
#define BT_UUID_LBS_MYSENSOR       BT_UUID_DECLARE_128(BT_UUID_LBS_MYSENSOR_VAL)


/** @brief Callback type for when an LED state change is received. */
typedef void (*led_cb_t)(const bool led_state);

/** @brief Callback type for when the button state is pulled. */
typedef bool (*button_cb_t)(void);

/** @brief Callback struct used by the LBS Service. */
struct my_lbs_cb {
	/** LED state change callback. */
	led_cb_t led_cb;
	/** Button read callback. */
	button_cb_t button_cb;
};

/** @brief Initialize the LBS Service.
 *
 * This function registers application callback functions with the My LBS
 * Service
 *
 * @param[in] callbacks Struct containing pointers to callback functions
 *			used by the service. This pointer can be NULL
 *			if no callback functions are defined.
 *
 *
 * @retval 0 If the operation was successful.
 *           Otherwise, a (negative) error code is returned.
 */
int my_lbs_init(struct my_lbs_cb *callbacks);



// /** @brief Send the button state as indication.
//  *
//  * This function sends a binary state, typically the state of a
//  * button, to all connected peers.
//  *
//  * @param[in] button_state The state of the button.
//  *
//  * @retval 0 If the operation was successful.
//  *           Otherwise, a (negative) error code is returned.
//  */
// int my_lbs_send_button_state_indicate(bool button_state);



// /** @brief Send the button state as notification.
//  *
//  * This function sends a binary state, typically the state of a
//  * button, to all connected peers.
//  *
//  * @param[in] button_state The state of the button.
//  *
//  * @retval 0 If the operation was successful.
//  *           Otherwise, a (negative) error code is returned.
//  */
// int my_lbs_send_button_state_notify(bool button_state);

// /** @brief Send the sensor value as notification.
//  *
//  * This function sends an uint32_t  value, typically the value
//  * of a simulated sensor to all connected peers.
//  *
//  * @param[in] sensor_value The value of the simulated sensor.
//  *
//  * @retval 0 If the operation was successful.
//  *           Otherwise, a (negative) error code is returned.
//  */
// int my_lbs_send_sensor_notify(uint32_t sensor_value);

/* end of LBS service */


#ifdef __cplusplus
}
#endif

Previously I have successfully built the connection using the same code and same hardwares (DK board and my phone)

What may cause this issue and what would you recommend me to do? Thank you

Parents
  • This solved my problem. Thank you so much Kenneth.

    Previously I flashed a CGMS sample project and used nRF toolbox app to connect with the development board. I didn't realize that it also created an item in my bluetooth device list. Later I flashed my customized project and tried to use nRF Connect app to establish connection and failed. 

    I'm thinking that I'm not the first person who have tried these series of operations and encountered this problem. I would suggest Nordic can better coordinate the two mobile phone apps in later development. Many thanks!

Reply
  • This solved my problem. Thank you so much Kenneth.

    Previously I flashed a CGMS sample project and used nRF toolbox app to connect with the development board. I didn't realize that it also created an item in my bluetooth device list. Later I flashed my customized project and tried to use nRF Connect app to establish connection and failed. 

    I'm thinking that I'm not the first person who have tried these series of operations and encountered this problem. I would suggest Nordic can better coordinate the two mobile phone apps in later development. Many thanks!

Children
No Data
Related