Usage Fault when implementing an advertiser sent callback.

NCS 2.7.0

NRF52840 DK

I am trying to trigger an advertising sent callback to indicate advertising timeout or a maximum number of events. I have tried increasing the workqueue and main thread stack sizes but still get a usage fault error. 

Here is my code for a simple Bluetooth paging service. 

#include <zephyr/kernel.h>
#include <zephyr/types.h>
#include <stddef.h>
#include <string.h>
#include <errno.h>
#include <zephyr/sys/printk.h>
#include <zephyr/sys/byteorder.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/device.h>
#include <zephyr/drivers/pwm.h>
#include <soc.h>

// For power manager
#include <zephyr/init.h>
#include <zephyr/pm/pm.h>
#include <zephyr/pm/device.h>
#include <zephyr/pm/policy.h>
#include <zephyr/sys/poweroff.h>
#include <zephyr/sys/util.h>

// Devicetree + GPIO includes
#include <nrfx_gpiote.h>
#include <helpers/nrfx_gppi.h>
#include <nrfx_ppi.h>
#include <zephyr/irq.h>

// Bluetooth include files
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/gatt.h>
#include <zephyr/bluetooth/conn.h>
#include <zephyr/bluetooth/uuid.h>
#include <zephyr/bluetooth/services/bas.h>

const struct device *const cons = DEVICE_DT_GET(DT_CHOSEN(zephyr_console));

#define GREEN_LED_NODE      DT_ALIAS(led1)
#define BLUE_LED_NODE       DT_ALIAS(led2)
#define RED_LED_NODE        DT_ALIAS(led0)
#define BUTTON_NODE         DT_ALIAS(sw0)

#if CONFIG_BOARD_PRO_FLAG
#define HAPTIC_PWM_NODE     DT_ALIAS(pwm_buzz)
#define HAPTIC_PIN_NODE     DT_ALIAS(buzz_en)
#define PWM_RED_LED_NODE    DT_ALIAS(pwm_led0)
#define PWM_GREEN_LED_NODE  DT_ALIAS(pwm_led1)
#define PWM_BLUE_LED_NODE   DT_ALIAS(pwm_led2)
#define USB_DETECT_NODE     DT_ALIAS(usb_dt)
#define USB_STAT_NODE       DT_ALIAS(chrg)
#define FACTORY_ENABLE_NODE DT_ALIAS(pwron)
#else
#define HAPTIC_PWM_NODE DT_ALIAS(pwm_led0)
#define HAPTIC_PIN_NODE DT_ALIAS(led3)
#endif

static const struct gpio_dt_spec button = GPIO_DT_SPEC_GET(BUTTON_NODE, gpios);
//static const struct gpio_dt_spec r_led =  GPIO_DT_SPEC_GET(RED_LED_NODE, gpios);
static const struct gpio_dt_spec g_led =  GPIO_DT_SPEC_GET(GREEN_LED_NODE, gpios);
static const struct gpio_dt_spec b_led =  GPIO_DT_SPEC_GET(BLUE_LED_NODE, gpios);
#if CONFIG_BOARD_PRO_FLAG
static const struct pwm_dt_spec pwm_ledr =    PWM_DT_SPEC_GET(PWM_RED_LED_NODE);
static const struct pwm_dt_spec pwm_ledg =    PWM_DT_SPEC_GET(PWM_GREEN_LED_NODE);
//static const struct pwm_dt_spec pwm_ledb =    PWM_DT_SPEC_GET(PWM_BLUE_LED_NODE);
static const struct gpio_dt_spec usb_detect = GPIO_DT_SPEC_GET(USB_DETECT_NODE, gpios);
static const struct gpio_dt_spec stat_pin =   GPIO_DT_SPEC_GET(USB_STAT_NODE, gpios);
static const struct gpio_dt_spec bat_en =     GPIO_DT_SPEC_GET(FACTORY_ENABLE_NODE, gpios);
#else
static const struct pwm_dt_spec pwm_ledr = PWM_DT_SPEC_GET(HAPTIC_PWM_NODE);
static const struct pwm_dt_spec pwm_ledg = PWM_DT_SPEC_GET(HAPTIC_PWM_NODE);
static const struct pwm_dt_spec pwm_ledb = PWM_DT_SPEC_GET(HAPTIC_PWM_NODE);
#endif
static const struct pwm_dt_spec pwm_buzz = PWM_DT_SPEC_GET(HAPTIC_PWM_NODE);
static const struct gpio_dt_spec buzz_en = GPIO_DT_SPEC_GET(HAPTIC_PIN_NODE, gpios);

//Total fade up time is DELAY * PWM_STEPS
#define HAPTIC_PWM_PERIOD_NS    500000
#define HAPTIC_FADE_DELAY_MS 1
#define HAPTIC_PWM_STEPS        1000

#define POWERON_PWM_PERIOD_NS 500000
#define POWERON_FADE_UP_DELAY_MS 1
#define POWERON_PWM_STEPS 3000

#define DEVICE_NAME "rareBit PRO Flag"
#define DEVICE_NAME_LEN (sizeof(DEVICE_NAME) - 1)

#define MAX_TRANSMIT_SIZE 240//TODO figure this out

// Define the custom services and characteristics

#define PAGING_SERVICE_UUID 0x6d, 0x1b, 0xee, 0x1e, 0xee, 0x7d, 0xd0, 0xba, 0x7b, \
                            0x4b, 0xd5, 0x28, 0x01, 0x00, 0x21, 0x23

// Characteristic: Paging Characteristic UUID 23210002-28D5-4B7B-BA0F-7DEE1EEE1B6D
#define PAGE_ALERT_CHARACTERISTIC_UUID 0x6d, 0x1b, 0xee, 0x1e, 0xee, 0x7d, 0xd0, 0xba, 0x7b, \
                                 0x4b, 0xd5, 0x28, 0x02, 0x00, 0x21, 0x23

#define BT_UUID_PAGING_SERVICE              BT_UUID_DECLARE_128(PAGING_SERVICE_UUID)
#define BT_UUID_PAGE_ALERT_CHARACTERISTIC   BT_UUID_DECLARE_128(PAGE_ALERT_CHARACTERISTIC_UUID)

//Advertising Timeout period ranges from EVENTS * MIN_INTERVAL to EVENTS * MAX_INTERVAL
#define ADVERTISNG_TIMEOUT_EVENTS 200

#define WORQ_THREAD_STACK_SIZE  8192
#define WORKQ_PRIORITY   5

static bool app_button_state = false;


// Define stack area used by workqueue thread
static K_THREAD_STACK_DEFINE(my_stack_area, WORQ_THREAD_STACK_SIZE);

// Define queue structure
static struct k_work_q offload_work_q = {0};


struct work_info {
    struct k_work work;
    char name[25];
} radio_work;

struct usb_work_info
{
    struct k_work work;
    char name[50];
} usb_work;

uint8_t data_tx[MAX_TRANSMIT_SIZE];
bool debounce_check = false;


static struct bt_le_ext_adv *adv;
struct bt_conn *conn_handle = NULL;
const struct bt_gatt_attr *page_alert_attr = NULL;

bool page_flag = false;

int my_service_init(void)
{
    int err = 0;

    memset(&data_tx, 0, MAX_TRANSMIT_SIZE);

    return err;
}

// Bluetooth advertising data
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),
    BT_DATA_BYTES(BT_DATA_UUID128_ALL,
		      0x84, 0xaa, 0x60, 0x74, 0x52, 0x8a, 0x8b, 0x86,
		      0xd3, 0x4c, 0xb7, 0x1d, 0x1d, 0xdc, 0x53, 0x8d),
    BT_DATA_BYTES(BT_DATA_UUID128_ALL, PAGING_SERVICE_UUID)
};

static void advertising_max_events(struct bt_le_ext_adv *adv, struct bt_le_ext_adv_sent_info *info)
{

    uint8_t adv_events = 200;//info->num_sent;

    printk("Adertising finished with %d events.\n", adv_events);

    //Giving time for the print buffer. 
    //We need this under every print that occurs before PM_DEVICE_ACTION_SUSPEND.
    k_msleep(1000);
    
    gpio_pin_set_dt(&b_led, 0);

    //Effectively the timeout condition, but it's not a precise interval
    if (adv_events >= ADVERTISNG_TIMEOUT_EVENTS)
    {
        printk("Advertising timed out. Shutting down... \n");
    }
    else
    {
        //Else is effectively the connected condition (which we already have a callback for... idk)
        printk("Probably connected.\n");
    }
}

static void create_advertising_coded(void)
{
    int err;

    struct bt_le_adv_param params = BT_LE_ADV_PARAM_INIT(BT_LE_ADV_OPT_CONNECTABLE |
                                                        BT_LE_ADV_OPT_CODED |
                                                        BT_LE_ADV_OPT_EXT_ADV,
                                                        BT_GAP_ADV_FAST_INT_MIN_2,
                                                        BT_GAP_ADV_FAST_INT_MAX_2,
                                                        NULL);
    struct bt_le_ext_adv_cb advertising_cb =
        {
            .sent = advertising_max_events
        };

    err = bt_le_ext_adv_create(&params, &advertising_cb, &adv);
    if(err)
    {
        printk("Error %d could not create ext_adv.\n", err);
    }
    else printk("CODED PHY Advertising Configured.\n");

    err = bt_le_ext_adv_set_data(adv, ad, ARRAY_SIZE(ad), NULL, 0);
    if(err)
    {
        printk("Error %d could not set_data.\n", err);
    }
    else printk("CODED PHY adv data set.\n");
}

void start_advertising_coded(void)
{
    int err;

    //timeout value doesn't seem to work here, the advertising sent function gets triggered even when the timeout doesn't occur
    struct bt_le_ext_adv_start_param start_params;

    //Minimum advertising intverval is 100ms, thus a timeout period of AT LEAST num_events*100ms
    start_params.num_events = ADVERTISNG_TIMEOUT_EVENTS; 

    err = bt_le_ext_adv_start(adv, &start_params);
    if(err)
    {
        printk("Error: Advertising NOT started. return %d\n", err);

    }
    else 
	{
		printk("Bluetooth advertising started!\n");
        gpio_pin_set_dt(&b_led, 1);
		
	}

}

// Instantiate the service and its characteristics
BT_GATT_SERVICE_DEFINE(
    paging_service,
    
    // Simple Service
    BT_GATT_PRIMARY_SERVICE(BT_UUID_PAGING_SERVICE),

    // Page Alert Characteristic
    BT_GATT_CHARACTERISTIC(BT_UUID_PAGE_ALERT_CHARACTERISTIC,
                    BT_GATT_CHRC_NOTIFY, 
                    BT_GATT_PERM_NONE,
                    NULL,
                    NULL,
                    NULL),
    BT_GATT_CCC(NULL, BT_GATT_PERM_WRITE | BT_GATT_PERM_READ)
   
);


// Connected callback function
static void connected_cb(struct bt_conn *conn, uint8_t err)
{

    struct bt_conn_info info;
    char addr[BT_ADDR_LE_STR_LEN];

    conn_handle = conn;

    page_alert_attr = &paging_service.attrs[2];

	if (err) 
	{
		printk("Connection failed (err %u)\n", err);
		return;
	}
	else if(bt_conn_get_info(conn, &info))
	{
		printk("Could not parse connection info\n");
	}
	else
	{
		bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
		
		printk("\n \n Connection established!\n\
		Connected to: %s					 \n\
		Role: %u							 \n\
		Connection interval: %u				 \n\
		Slave latency: %u					 \n\
		Connection supervisory timeout: %u	 \n"
		, addr, info.role, info.le.interval, info.le.latency, info.le.timeout);
	}
}

// Disconnected callback function
static void disconnected_cb(struct bt_conn *conn, uint8_t reason)
{
    printk("Disconnected with reason %d\n", reason);
    k_sleep(K_MSEC(100));

    conn_handle = NULL;
    
    gpio_pin_set_dt(&b_led, 0);

    start_advertising_coded();

    gpio_pin_set_dt(&g_led, 0);

    
}

#if LE_PARAMETER_REQUESTS_ENABLED
static bool le_param_req(struct bt_conn *conn, struct bt_le_conn_param *param)
{
	//If acceptable params, return true, otherwise return false.
	return true; 
}

static void le_param_updated(struct bt_conn *conn, uint16_t interval, uint16_t latency, uint16_t timeout)
{
	struct bt_conn_info info; 
	char addr[BT_ADDR_LE_STR_LEN];
	
	if(bt_conn_get_info(conn, &info))
	{
		printk("Could not parse connection info\n");
	}
	else
	{
		bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
		
		printk("Connection parameters updated!	\n\
		Connected to: %s						\n\
		New Connection Interval: %u				\n\
		New Slave Latency: %u					\n\
		New Connection Supervisory Timeout: %u	\n"
		, addr, info.le.interval, info.le.latency, info.le.timeout);
	}
}
#endif

// Connection callback structure
static struct bt_conn_cb conn_callbacks = {
    .connected = connected_cb,
    .disconnected = disconnected_cb
#if LE_PARAMETER_REQUESTS_ENABLED
    ,
    .le_param_req			= le_param_req,
	.le_param_updated		= le_param_updated
#endif
};


void bt_pageAlert(struct bt_conn *conn, const uint8_t *data, uint16_t len)
{
    
    // Send the notification
    if(bt_gatt_notify(conn, page_alert_attr, &data, len))
    {
        printk("Error, unable to send notification\n");
        
    }
    else {
        printk("Notified Peer of %d\n", (int) &data);
    }

}

void offload_function(struct k_work *work_tem)
{
    app_button_state = !app_button_state;

    uint8_t page_d[] = {0x01};
	uint8_t * page_p = page_d;

	if ((conn_handle != NULL) & app_button_state)
    {
        bt_pageAlert(conn_handle, page_p, 1);
    }
    
    printk("Work thread executed. \n");
    k_yield();
}

static void button_changed(const struct device *dev, struct gpio_callback *cb,
                           uint32_t pins)
{

    k_work_submit_to_queue(&offload_work_q, &radio_work.work);
    k_yield();
    
}

static void init_interrupts(void)
{
    int err;

    err = gpio_pin_interrupt_configure_dt(&button, GPIO_INT_EDGE_BOTH);
    if (err < 0)
    {
        printk("Button pin interrupt configure failed with err %d\n", err);
    }
    else
    {
        printk("Button pin interrupt configured.\n");
    }

    static struct gpio_callback button_cb_data;

    gpio_init_callback(&button_cb_data, button_changed, BIT(button.pin));

    err = gpio_add_callback(button.port, &button_cb_data);
    if (err < 0)
    {
        printk("Button pin interrupt callback add failed with err %d\n", err);
    }
    else
    {
        printk("Button pin interrupt callback added.\n");
    }
}

static void init_pins(void)
{
    int err;

    err = gpio_pin_configure_dt(&button, GPIO_INPUT);
    if (err < 0)
    {
        printk("GPIO Input Button failed (err %d)\n", err);
    }
    else
        printk("Button input configured\n");


}

int main(void)
{
    
    init_pins();
    init_interrupts();

    k_work_queue_start(&offload_work_q, my_stack_area, K_THREAD_STACK_SIZEOF(my_stack_area), WORKQ_PRIORITY, NULL);
    strcpy(radio_work.name, "Bluetooth Notify thread");
    k_work_init(&radio_work.work, offload_function);

    int err;

    err = bt_enable(NULL);
    if (err)
    {
        printk("Bluetooth init failed (err %d)\n", err);
    }

    err = my_service_init();
    if (err)
    {
        printk("Could not initialize simple service \n");
    }

    // Register for connection callbacks
    bt_conn_cb_register(&conn_callbacks);

    // Create the Extended Advertising
    create_advertising_coded();

    // Start advertising
    start_advertising_coded();

    return 0;
}
5852.prj.conf

Error Message:

[00:00:20.874,969] <err> os: ***** USAGE FAULT *****
[00:00:20.875,000] <err> os:   Illegal use of the EPSR
[00:00:20.875,000] <err> os: r0/a1:  0x200025c8  r1/a2:  0x2000685c  r2/a3:  0x000000c8
[00:00:20.875,030] <err> os: r3/a4:  0x000185e0 r12/ip:  0x00001407 r14/lr:  0x0001bd0f
[00:00:20.875,030] <err> os:  xpsr:  0x20000000
[00:00:20.875,030] <err> os: Faulting instruction address (r15/pc): 0x000185e0
[00:00:20.875,091] <err> os: >>> ZEPHYR FATAL ERROR 35: Unknown error on CPU 0
[00:00:20.875,122] <err> os: Current thread: 0x200024f0 (BT RX WQ)
[00:00:20.953,125] <err> os: Halting system

Related