notify value callback for simple client produces MPU fault

I'm creating a simple service and client application with two nRF52840 DKs that resembles the central_hr_codes nrf sample, but instead it's a custom service and characteristic that the client subscribes to. 

I'm having trouble on the client side, specifically in the notify value callback as defined in the bt_gatt_subscribe_params struct. 

I've successfully connected, discovered, and subscribed to my custom characteristic. But when the notify_process callback is called, I get a BUS fault when the program tries to call the actual notify callback in main. Similar to line 23 in the on_hrs_measurement_notify function below. 

static uint8_t on_hrs_measurement_notify(struct bt_conn *conn,
					 struct bt_gatt_subscribe_params *params,
					 const void *data, uint16_t length)
{
	int err;
	struct bt_hrs_client *hrs_c;
	struct bt_hrs_client_measurement hr_measurement;

	hrs_c = CONTAINER_OF(params, struct bt_hrs_client, measurement_char.notify_params);

	if (!data) {
		atomic_clear_bit(&hrs_c->state, HRS_MEASUREMENT_NOTIFY_ENABLED);
		LOG_DBG("[UNSUBSCRIBE] from Heart Rate Measurement characterictic");

		return BT_GATT_ITER_STOP;
	}

	LOG_HEXDUMP_DBG(data, length, "[NOTIFICATION] Heart Rate Measurement: ");

	err = hrs_measurement_data_parse(&hr_measurement, data, length);

	if (hrs_c->measurement_char.notify_cb) {
		hrs_c->measurement_char.notify_cb(hrs_c, &hr_measurement, err);
	}

	return BT_GATT_ITER_CONTINUE;
}

Obviously I'm implementing this incorrectly. Here's my client objects defined (paging client).

typedef void (*bt_pag_notify_cb)(struct bt_pag_client *pag,
				 uint8_t page_alert);

struct bt_page_alert_charcteristic {

	struct bt_gatt_subscribe_params notify_params;
	
	/** Notify callback. */
	bt_pag_notify_cb notify_cb;

	/** Properties of the service. */
	uint8_t properties;

	uint16_t ccc_handle;
	
    /** handle of the page alert value*/
	uint16_t val_handle;
    
};

/** @brief Paging Client instance. */
//TODO add property that IDs which device sent the page alert
struct bt_pag_client {

	/** Connection handle. */
	struct bt_conn *conn;
	
	struct bt_page_alert_charcteristic page_chrc;
	
	
	/** Notification supported. */
	bool notify;
};

This is my implementation of the notify value callback.

static uint8_t notify_process(struct bt_conn *conn, struct bt_gatt_subscribe_params *params, const void *data, uint16_t length)
{
	//test byte to forward to main when notified
	uint8_t byte = 0x01;
	struct bt_pag_client *pag_c;
	

	pag_c = CONTAINER_OF(params, struct bt_pag_client, page_chrc.notify_cb);
	
	//Parse the data

	if (pag_c->page_chrc.notify_cb) {
		pag_c->page_chrc.notify_cb(pag_c, byte);
	}

	return BT_GATT_ITER_CONTINUE;
}

Where line 13 above is causing this error

E: ***** BUS FAULT *****
E:   Instruction bus error
E: r0/a1:  0xffffffe5  r1/a2:  0x00000001  r2/a3:  0x00000000
E: r3/a4:  0x9920003c r12/ip:  0x2000261c r14/lr:  0x0004bab3
E:  xpsr:  0x20000000
E: Faulting instruction address (r15/pc): 0x9920003c
E: >>> ZEPHYR FATAL ERROR 0: CPU exception on CPU 0
E: Current thread: 0x20001ce0 (BT RX)

After a lot of trouble shooting I'm still at a loss. I've tried increasing the BT_RX stack size to 8192 and eliminating the arguments passed to the notify callback. When no arguments are passed, I get an MPU fault in the same thread. 

Any help would be appreciated!

Thanks

Sam

Parents
  • Hello,

    Seeing that your R15(Program counter) says 0x9920003c, it would suggest that it is trying to run some sort of function located at this address. Since the nRF52840 has 1MB flash, it only goes from 0x00000000 to 0x00100000, so it looks like you have a bad pointer somewhere.

    It is a bit difficult to tell only from your snippets, but perhaps you can try to comment out this line:

    hrs_c->measurement_char.notify_cb(hrs_c, &hr_measurement, err);

    (only for debugging purposes, of course). Do you still get the hard fault? If not, try to either debug or print the address of this callback to the log. Is it the same value that is printed on R15 in your log?

    Also, stepping through these callbacks may be a good idea. You may need to reset from time to time, to do some digging on what function that is called that causes the issue.

    If you are stuck, is this something that you can reproduce on a DK? If so, can you please upload the project so that I can have a look?

    Best regards,

    Edvin

  • Hi Edvin, 

    I have already done what you suggested. It is definitively this line of code that is causing the error:

    pag_c->page_chrc.notify_cb(pag_c, byte);

    I know this because I've commented it out, the error does not happen, but my notification callback does not trigger as it does with the hrs_client. I only posted the hrs_client code snippet to demonstrate that I'm following the hrs_client implementation to achieve a notification callback when a characteristic the client has subscribed to has changed. I'm asking what the correct implementation is. 

    I'm already testing this on two nRF52840 DKs. 

  • Did you include the overlay-bt files for both applications I sent? That should be everything that's needed?

    It builds first time without a problem. 

    After some debugging, I'm still not able to really identify the problem. However, I get the strong feeling it's due to this CONTAINER_OF macro where I get the bad pointer. Is there a way around this CONTAINER_OF macro?

    pag_c = CONTAINER_OF(params, struct bt_pag_client, page_chrc.notify_cb);
    
    if (pag_c->page_chrc.notify_cb) {
    	pag_c->page_chrc.notify_cb(pag_c, byte);
    }

  • Ok, I see that your callback is triggered twice. First it is called through the proper mechanism, but it actually looks like your call to notifu_cb() calls itself recursively. I don't know exactly why, but it looks like you have not set this up properly. Can you please try to compare your bt_page_alert_subscribe() with bt_nus_subscribe_receive() from the central_uart sample. Look at how it has a parameter inside *nus_c that holds the notify callback, while yours only writes it to the bt_gatt_subscribe_params *params (which will no longer exist after you exit bt_page_alert_subscribe(). So whatever that happens to be in the flash where your "params" parameter were will determine the behavior of the application.

    Best regards,

    Edvin

  • Hi Edvin, 

    Thank you for giving this direction. I have modified my application such that it implements the user given notification callback through the client structure. As you suggested, the application now compares closely with the central_uart sample.

    However, I'm still getting a fatal error when trying to call the callback. 

    E: ***** USAGE FAULT *****
    E:   Illegal use of the EPSR
    E: r0/a1:  0x20002604  r1/a2:  0x00000001  r2/a3:  0x00000000
    E: r3/a4:  0x00000000 r12/ip:  0x2000261c r14/lr:  0x0002b015
    E:  xpsr:  0x20000000
    E: Faulting instruction address (r15/pc): 0x00000000
    E: >>> ZEPHYR FATAL ERROR 0: CPU exception on CPU 0
    E: Current thread: 0x20001ce0 (BT RX)
    

    I will continue debugging but I thought I'd post my application again to see if you can tell what my implementation is missing. 

    peripheral_pag.zip

    central_pag.zip

    Thanks for your time,

    Sam

  • Hello,

    I tried the application you just uploaded. While I was able to reproduce your last issue, I am not able to reproduce this one. Are you sure you didn't change any of the SDK files? Do I need to do anything (button presses?) Try deleting and reinstalling NCS, and copy your project folder into that one. 

    However, it looks like a similar issue. Now it is a function that is trying to get called, but it thinks the function is located at 0x00000000, which is not the case. Probably some callback that is not set correctly. First try a fresh install, and then if that doesn't work, try to debug to see how far you get. What function is it trying to call when it executes the NULL pointer?

  • Hi Edvin, 

    After more debugging, I was able to figure out where the bad pointer was coming from. I was calling the bt_client_initi function at the wrong time and kept accidentally passing a 0 for the callback member.

    Now I'm able to successfully connect and subscribe to the characteristic. However, after the subscribe function finishes, the notify_process function triggers once and only once. The peripheral is logging successful notifications on the server side, but still nothing is happening on the client side. So it appears I'm still implementing this incorrectly. 

    Here is my updated central application. 

    1106.central_pag.zip

    Thanks again for the guidance. 

Reply
  • Hi Edvin, 

    After more debugging, I was able to figure out where the bad pointer was coming from. I was calling the bt_client_initi function at the wrong time and kept accidentally passing a 0 for the callback member.

    Now I'm able to successfully connect and subscribe to the characteristic. However, after the subscribe function finishes, the notify_process function triggers once and only once. The peripheral is logging successful notifications on the server side, but still nothing is happening on the client side. So it appears I'm still implementing this incorrectly. 

    Here is my updated central application. 

    1106.central_pag.zip

    Thanks again for the guidance. 

Children
  • Hello,

    It was not possible to enable notifications on your peripheral device (at least the one you sent me in your previous reply. From it's main.c file:

    BT_GATT_CCC(NULL, BT_GATT_PERM_READ)

    This means that you are able to read the characteristic descriptor saying whether or not notifications are enabled or not. Try changing it to:

        BT_GATT_CCC(NULL, BT_GATT_PERM_WRITE | BT_GATT_PERM_READ)
       
    And see if that helps.
    If you are trying to write your peripheral and central at the same time, I can recommend that you check out the nRF Connect for Desktop -> Bluetooth Low Energy, which is a desktop application that you can use as your central. It is at least useful for basic functionality like this, so that you know that your peripheral is working as intended before you start debugging on your central.
    Note that in order to connect to it from the nRF Connect for Desktop  -> Bluetooth Low Energy app, you need to advertise "normally", at 1MBPS. I just removed all the UUID from your bt_data ad[], and commented out the coded phy and extended advertising for testing purposes:
    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 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);
    But after changing the CCC to have write permission, it seems like everything is working.
    BR,
    Edvin
  • Thanks Edvin, I was missing the write permissions. This solved the above issue. 

    Also appreciate the guidance on the implementation. 

Related