Bluetooth Notification Failure - "No ATT Channel for MTU" and "No Buffer Available" Warnings

I am encountering warnings while attempting to send Bluetooth notifications in my Zephyr-based project. The warnings are preventing successful notification of ECG data. Below are the specifics:

Warnings

  1. W: No ATT channel for MTU 33
  2. W: No buffer available to send notification

Description

I have implemented a GATT service to send notifications for ECG data. While the data can be read using the read callback, notifications are failing with the two warnings listed above.

  • Warning 1: No ATT channel for MTU 33

  • Warning 2: No buffer available to send notification

Project Details

  • Board: nRF52840
  • Zephyr Version: v2.6.1
  • Bluetooth Configuration: Using GATT services to send ECG data notifications.

Configurations in prj.conf
CONFIG_NCS_SAMPLES_DEFAULTS=y

CONFIG_DISABLE_FLASH_PATCH=y
#config basic
CONFIG_PRINTK=y
CONFIG_PWM=y
CONFIG_SHELL=y
CONFIG_LOG=y

#log configuration
CONFIG_MOOV_LOG_DEBUG=y

CONFIG_I2C=y
CONFIG_CBPRINTF_FP_SUPPORT=y

#configuration for LIS2DW12
CONFIG_LIS2DW12=y
CONFIG_LIS2DW12_TRIGGER_GLOBAL_THREAD=y
CONFIG_LIS2DW12_THRESHOLD=y

#configuration for cpu temp
CONFIG_SENSOR=y
CONFIG_ADC=y
CONFIG_SPI=y

#configuration for fuel gauge npm1300
CONFIG_REGULATOR=y
CONFIG_NRF_FUEL_GAUGE=y

#configuration for hw id
CONFIG_HW_ID_LIBRARY=y
CONFIG_HW_ID_LIBRARY_SOURCE_DEVICE_ID=y

#bluetooth configuration
CONFIG_BT=y
CONFIG_BT_SMP=y
CONFIG_BT_SMP_APP_PAIRING_ACCEPT=y
CONFIG_BT_PERIPHERAL=y
CONFIG_BT_DEVICE_NAME="VC Manoj"

CONFIG_BT_BAS=y

CONFIG_BT_DIS=y
CONFIG_BT_DIS_PNP=y
CONFIG_BT_DIS_MANUF="DotcomIoTLLP"
CONFIG_BT_DIS_PNP_VID_SRC=2
CONFIG_BT_DIS_PNP_VID=0x1915
CONFIG_BT_DIS_PNP_PID=0xEEEB
CONFIG_BT_DIS_PNP_VER=0x0100

# Enable bonding
# CONFIG_BT_SETTINGS=y
CONFIG_FLASH=y
CONFIG_FLASH_PAGE_LAYOUT=y
CONFIG_FLASH_MAP=y
CONFIG_NVS=y
CONFIG_MPU_ALLOW_FLASH_WRITE=y
CONFIG_BT_MAX_PAIRED=2

#Enable MCUBOOT bootloader build in the application
CONFIG_BOOTLOADER_MCUBOOT=y
#Set firmware image version
CONFIG_MCUBOOT_IMGTOOL_SIGN_VERSION="1.0.1"
#Include MCUMGR and the dependencies in the build
CONFIG_NCS_SAMPLE_MCUMGR_BT_OTA_DFU=y
CONFIG_MCUMGR_MGMT_NOTIFICATION_HOOKS=y
CONFIG_MCUMGR_GRP_IMG_STATUS_HOOKS=y

CONFIG_MAIN_STACK_SIZE=3072

#for power management
# Required to disable default behavior of deep sleep on timeout
CONFIG_PM_DEVICE=y
# Optional select RAM retention (nRF52 only)
CONFIG_APP_RETENTION=n
CONFIG_CRC=y
CONFIG_POWEROFF=y

#configurations for date and time
CONFIG_DATE_TIME=y
CONFIG_DATE_TIME_UPDATE_INTERVAL_SECONDS=0
CONFIG_DATE_TIME_TOO_OLD_SECONDS=0
CONFIG_DATE_TIME_NTP=y
CONFIG_NETWORKING=y
CONFIG_NET_SOCKETS=y

Expected Behavior

The client should receive ECG data notifications over Bluetooth successfully.

Actual Behavior

Bluetooth notifications fail with the warnings mentioned above, and no data is transmitted to the client.

Steps Taken

  1. Configured MTU size in prj.conf file as shown above.

Steps to Reproduce

  1. Set up a GATT service that supports notifications on the nRF52840 board.
  2. Attempt to notify clients with ECG data over Bluetooth.

Additional Information

  • I am using Zephyr NCS v2.6.1.
  • The board in use is nRF52840.

Any help or insights into resolving these warnings would be greatly appreciated.

in driver.c file
/*
 * Copyright (c) 2018 Nordic Semiconductor ASA
 *
 * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
 */

/** @file moov_alarm.c
 *  @brief MOOV Alarm Service.
 * 
 *  This file provides APIs related to MOOV Alarm Service.
 */

#include <zephyr/types.h>
#include <stddef.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <zephyr/sys/printk.h>
#include <zephyr/sys/byteorder.h>
#include <zephyr/kernel.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 "main.h"
#include "vc_ble_bio_data_service.h"


MOOV_MODULE_REGISTER(vc_bds_svc_log,MOOV_LOG_LEVEL);

static bool live_notif_enabled;							/** Flag indicating whether notifications are enabled. */
static bool past_notif_enabled;							/** Flag indicating whether notifications are enabled. */
uint8_t ecg_data_live[30];
uint8_t ecg_data_past[30];
char* get_ecg_data_live;
char* get_ecg_data_past; 
static struct vc_bds_svc_cb bds_svc_cb; 

bool is_ecg_live_enabled(){
	return live_notif_enabled ? true : false;
}


bool is_ecg_past_enabled(){
	return past_notif_enabled ? true : false;
}

/**
 * @brief Callback function for handling configuration change of Client Characteristic Configuration (CCC).
 *
 * This function is called when the CCC configuration changes for USB device service.
 *
 * @param attr Pointer to the attribute structure.
 * @param value New value of the CCC configuration.
 */
static void vc_ecg_data_live_ccc_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value)
{
	live_notif_enabled = (value == BT_GATT_CCC_NOTIFY);

	MOOV_INF("ECG DATA Live Notifications %s", live_notif_enabled ? "enabled" : "disabled");
}

/**
 * @brief Callback function for handling configuration change of Client Characteristic Configuration (CCC).
 *
 * This function is called when the CCC configuration changes for USB device service.
 *
 * @param attr Pointer to the attribute structure.
 * @param value New value of the CCC configuration.
 */
static void vc_ecg_data_past_ccc_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value)
{
	past_notif_enabled = (value == BT_GATT_CCC_NOTIFY);

	MOOV_INF("ECG DATA PAST Notifications %s", past_notif_enabled ? "enabled" : "disabled");
}

/**
 * @brief Read USB device status.
 *
 * This function reads the USB device status characteristic.
 *
 * @param conn Connection object.
 * @param attr Pointer to the attribute structure.
 * @param buf Pointer to the buffer to store the value.
 * @param len Length of the buffer.
 * @param offset Offset.
 *
 * @return ssize_t Length of the data read.
 */
static ssize_t read_ecg_data_live(struct bt_conn *conn, const struct bt_gatt_attr *attr, void *buf,
			   uint16_t len, uint16_t offset)
{

	if (bds_svc_cb.ecg_data_live_cb) {
		// Call the application callback function to update the get the current value of the button
		get_ecg_data_live = bds_svc_cb.ecg_data_live_cb();
		snprintf(ecg_data_live, sizeof(ecg_data_live), "%s", get_ecg_data_live);
		return bt_gatt_attr_read(conn, attr, buf, len, offset, ecg_data_live, sizeof(ecg_data_live));
	}

	return 0;
}

/**
 * @brief Read USB device status.
 *
 * This function reads the USB device status characteristic.
 *
 * @param conn Connection object.
 * @param attr Pointer to the attribute structure.
 * @param buf Pointer to the buffer to store the value.
 * @param len Length of the buffer.
 * @param offset Offset.
 *
 * @return ssize_t Length of the data read.
 */
static ssize_t read_ecg_data_past(struct bt_conn *conn, const struct bt_gatt_attr *attr, void *buf,
			   uint16_t len, uint16_t offset)
{

	if (bds_svc_cb.ecg_data_live_cb) {
		// Call the application callback function to update the get the current value of the button
		get_ecg_data_past = bds_svc_cb.ecg_data_live_cb();
		snprintf(ecg_data_past, sizeof(ecg_data_past), "%s", get_ecg_data_past);
		return bt_gatt_attr_read(conn, attr, buf, len, offset, ecg_data_past, sizeof(ecg_data_past));
	}

	return 0;
}


BT_GATT_SERVICE_DEFINE(bds_svc,
	BT_GATT_PRIMARY_SERVICE(BT_UUID_BDS_SVC),

	BT_GATT_CHARACTERISTIC(BT_UUID_ECG_DATA_LIVE,
			       BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY,
			       BT_GATT_PERM_READ, read_ecg_data_live, NULL,
			       &ecg_data_live),
	BT_GATT_CCC(vc_ecg_data_live_ccc_cfg_changed,
		    BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),
	
	BT_GATT_CHARACTERISTIC(BT_UUID_ECG_DATA_PAST,
			       BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY,
			       BT_GATT_PERM_READ, read_ecg_data_past, NULL,
			       &ecg_data_past),
	BT_GATT_CCC(vc_ecg_data_past_ccc_cfg_changed,
		    BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),
);

int vc_bds_svc_init(struct vc_bds_svc_cb *callbacks)
{
	if (callbacks) {
		bds_svc_cb.ecg_data_live_cb = callbacks->ecg_data_live_cb;
		bds_svc_cb.ecg_data_past_cb = callbacks->ecg_data_past_cb;
	}

	return 0;
}

// int vc_ecg_send_live_data(char* ecg_data_live_buff)
// {
// 	if (!live_notif_enabled) {
// 		return -EACCES;
// 	}
// 	int rc;

// 	snprintf(ecg_data_live, sizeof(ecg_data_live), "%s", ecg_data_live_buff);
// 	MOOV_INF("%s", ecg_data_live);

// 	rc = bt_gatt_notify(NULL, &bds_svc.attrs[1], &ecg_data_live, sizeof(ecg_data_live));

// 	return rc == -ENOTCONN ? 0 : rc;
// }

static void on_sent(struct bt_conn *conn, void *user_data)
{
	
	MOOV_INF("%s", ecg_data_live);
}

int vc_ecg_send_live_data(char* ecg_data_live_buff)
{
	if (!live_notif_enabled) {
		return -EACCES;
	}
	int rc;

	snprintf(ecg_data_live, sizeof(ecg_data_live), "%s", ecg_data_live_buff);
	MOOV_INF("%s", ecg_data_live);

	struct bt_gatt_notify_params params = {0};
	const struct bt_gatt_attr *attr = &bds_svc.attrs[1];

	params.attr = attr;
	params.data = ecg_data_live;
	params.len = sizeof(ecg_data_live);
	params.func = on_sent;
	
	rc  = bt_gatt_notify_cb(NULL, &params);

	return rc == -ENOTCONN ? 0 : rc;
}

int vc_ecg_send_past_data(char* ecg_data_past_buff)
{
	if (!past_notif_enabled) {
		return -EACCES;
	}
	int rc;

	snprintf(ecg_data_past, sizeof(ecg_data_past), "%s", ecg_data_past_buff);
	MOOV_INF("%s", ecg_data_past);

	rc = bt_gatt_notify(NULL, &bds_svc.attrs[4], &ecg_data_past, sizeof(ecg_data_past));

	return rc == -ENOTCONN ? 0 : rc;
}

Related