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(¶ms, &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;
}
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