Zephyr crashing when I connect to a device

  has been helping me connect to a device and I am finally able to do that, but as soon as I do, Zephyr crashes.

This is with a lightly modified central_uart project

[00:00:00.024,810] <0x1b>[0m<inf> central_uart: Scan module initialized<0x1b>[0m␍␊

[00:00:00.024,810] <0x1b>[0m<inf> central_uart: NUS Client module initialized<0x1b>[0m␍␊

[00:00:00.029,846] <0x1b>[0m<inf> central_uart: Scanning successfully started<0x1b>[0m␍␊

[00:00:00.093,719] <0x1b>[0m<inf> central_uart: Filters matched. Address: E8:A0:DB:24:21:62 (random) connectable: 1<0x1b>[0m␍␊

[00:00:00.289,886] <0x1b>[0m<inf> central_uart: Connected: E8:A0:DB:24:21:62 (random)<0x1b>[0m␍␊

[00:00:00.290,039] <0x1b>[1;33m<wrn> central_uart: Failed to set security: -12<0x1b>[0m␍␊

[00:00:00.290,100] <0x1b>[1;31m<err> central_uart: Stop LE scan failed (err 0)<0x1b>[0m␍␊

[00:00:00.441,497] <0x1b>[0m<inf> central_uart: MTU exchange done<0x1b>[0m␍␊

ASSERTION FAIL [conn] @ WEST_TOPDIR/zephyr/subsys/bluetooth/host/gatt.c:5433␍␊

⇥ invalid parameters␍␊

␍␊

[00:00:01.791,351] <0x1b>[0m<inf> central_uart: Service discovery completed<0x1b>[0m␍␊

[00:00:01.800,994] <0x1b>[1;31m<err> os: r0/a1: 0x00000004 r1/a2: 0x00001539 r2/a3: 0x20000b60<0x1b>[0m␍␊

[00:00:01.800,994] <0x1b>[1;31m<err> os: r3/a4: 0x00000004 r12/ip: 0x00000000 r14/lr: 0x000217df<0x1b>[0m␍␊

[00:00:01.801,025] <0x1b>[1;31m<err> os: xpsr: 0x21000000<0x1b>[0m␍␊

[00:00:01.801,025] <0x1b>[1;31m<err> os: Faulting instruction address (r15/pc): 0x00031072<0x1b>[0m␍␊

[00:00:01.801,086] <0x1b>[1;31m<err> os: >>> ZEPHYR FATAL ERROR 4: Kernel panic on CPU 0<0x1b>[0m␍␊

[00:00:01.801,116] <0x1b>[1;31m<err> os: Current thread: 0x20002568 (BT RX WQ)<0x1b>[0m␍␊

[00:00:01.869,415] <0x1b>[1;31m<err> os: Halting system<0x1b>[0m␍␊

The program crashes here:

 

return snprintk(str, len, "%02X:%02X:%02X:%02X:%02X:%02X (%s)",
addr->a.val[5], addr->a.val[4], addr->a.val[3],
addr->a.val[2], addr->a.val[1], addr->a.val[0], type);

Which is in
addr.h (sic) Quite what code is doing in a header file is beyond me.

The prototype for the function is this:

int bt_addr_le_to_str(const bt_addr_le_t *addr, char *str, size_t len);

The values passed look reasonable to me.
addr->a.val[0]-[5] have the correct MAC address in it
str has (in decimal) 23 0 ... in it so that looks like it's just got a single character and anyway,
it shouldn't make a difference because, surely, that's being written into.

len = 30

type ends up with "random" in it.

I am assuming
snprintk is based on
snprint and so shouldn't copy more than len bytes anyway.

The string being used is that is passed into bt_addr_le_to_str is:
char addr[BT_ADDR_LE_STR_LEN];
and BT_ADDR_LE_STR_LEN is set to 30 so that all stacks up nicely.
Can anyone give me some pointers as to what might be wrong here?
BTW   that little project you sent me does not crash like this, but I can't
see where you set up the characteristics for that project, there is no reference to
BT_GATT_SERVICE_DEFINE anywhere in it. I would happily just mod that if I could work out how you've set up
its service and characteristics.
Thanks


Parents
  • Hello,

    ASSERTION FAIL [conn] @ WEST_TOPDIR/zephyr/subsys/bluetooth/host/gatt.c:5433

    This error message shows that an assertion is raised in gatt.c at this line: https://github.com/nrfconnect/sdk-zephyr/blob/0bc3393fb112ec80ebeab48cd023d69b1e9db757/subsys/bluetooth/host/gatt.c#L5433 which suggests that the 'conn' pointer passed to your bt_gatt_subscribe() call is set to NULL. 

    hat little project you sent me does not crash like this, but I can't
    see where you set up the characteristics for that project, there is no reference to

    I copied the NUS service and NUS client source files and placed them in the project's source directory (I also renamed every occurrence of 'nus' to 'custom').

    Best regards,

    Vidar

  • Morning,

    I did look with the debugger at that pointer and that's not the issue.

    The crash doesn't come until snprintk in bt_addr_le_to_str

    and as I say, in the first post, The values in passed to:

    return snprintk(str, len, "%02X:%02X:%02X:%02X:%02X:%02X (%s)",
    addr->a.val[5], addr->a.val[4], addr->a.val[3],
    addr->a.val[2], addr->a.val[1], addr->a.val[0], type);

    Look fine:



    This smacks more of a stack overflow to me,
    but I've massively increased the main stack size in prj.conf
    to no or little avail.

    The message on crash changes slightly:

    [00:00:07.404,510] <0x1b>[1;31m<err> os: Fault during interrupt handling␊

    <0x1b>[0m␍␊

    [00:00:07.404,571] <0x1b>[1;31m<err> os: Current thread:

    0x20002568 (BT RX WQ)<0x1b>[0m␍␊

    This looks like it;s some sort bluetooth receive issue? However, all I'm doing is

    connecting, no actual data is being sent as yet.

    On the subject of your code, it's not the peripheral_uart I'm trying, it's the

    central_uart which doesn't have those definitions in it. .

     

  • The NUS service only supports notifications on the TX characteristic (Nordic UART service). However, from the screenshot you posted earlier of the nRF Connect app, it seems that both of your characteristics support notifications. This means the GATT client must be modified to subscribe to both characteristics (i.e. custom_client.c if you're using my project).

  • So are you saying the central_uart project needs to be modified (in this case  the one you supplied) so that the Rx characteristic in central subscribes to the Tx characteristic in the remote node?

    When I use your peripheral_uart to talk to your central with my UUIDs in, I don't need to do anything else other than send data into the UART on the peripheral and it appears on the central_uart project's UART output.

    If I look with nrf connect on my Android  phone, the Tx  on the remote node - whether it be the Nordic DK running peripheral_uart, or our custom device, (which is the RX on the central_uart project) it is notify only, so why would it work on peripheral_uart and not our custom device. What am I  not understanding here?

  • I thought you were using the Nordic UART service with custom UUIDs. However, after looking at the the nRF Connect screendump you shared again, I notice several differences. For instance, your service includes three or more characteristics, while the UART service only has two. The client code I provided will only subscribe to notifications from the first characteristic with UUID:651135b0-dfe9-45a2-95c1-46552a8a77e2.

    I don't know which characteristics the peripheral is supposed to send notifications on in your case.

  • I've tried the 52840DK using your peripheral UART and that works. So I have done

    UART         |                                BLE                                                               | UART

    PC UART<->CENTRAL_UART on 52840<-><PERIPHERAL_UART on 528040<->PC UART


    When I try to connect to the custom unit, which uses the very same UUIDs as your custom program, rather than peripheral_uart, it does not work.

    The custom unit has successfully done exactly the same thing I'm trying to achieve here, but with a Raspberry Pi: 

    UART         |                                BLE                                  | UART

    PC UART <-> Raspberry Pi with BLE <-> Custom Unit<->STM32

    I want to replace that with

    UART         |                                BLE                                    | UART

    PC UART <-> CENTRAL_UART on 52840<-> Custom Unit<->STM32

    BTW, I know how crazy this is, but it's what we've been asked to do.

    The custom unit's service for which you had the screen shot has the following setup in its firmware where Rx is data going into it and Tx is data going out.

    // the service
    #define BASE_UUID128   "2a1f963c-143a-4f7d-9107-6315196a02a6"
    #define BASE_UUID16    0x963c
    // BLE Data into the custom unit (so the central_uart I'm trying to get working will write into this)
    #define RX_LENGTH      240
    #define RX_UUID128     "651135b0-dfe9-45a2-95c1-46552a8a77e2"
    #define RX_UUID16      0x35b0
    // Not sued for UART comms
    #define RX_ACK_LENGTH  2
    #define RX_ACK_UUID128 "946ba79e-3162-46d5-9ba7-8a9d08c11ff0"
    #define RX_ACK_UUID16  0xa79e
    // Data out from the custom unit into the central_uart over Bluetooth
    #define TX_LENGTH      240
    #define TX_UUID128     "e7d6274e-b765-11e9-a2a3-2a2ae2dbcce4"
    #define TX_UUID16      0x274e
    // Unused for this UART function
    #define TX_ACK_LENGTH  2
    #define TX_ACK_UUID128 "e7d629ba-b765-11e9-a2a3-2a2ae2dbcce4"
    #define TX_ACK_UUID16  0x29ba

    The custom unit is running an older Nordic device and I have successfully talked to it the custom unit by writing a Qt program on Linux to talk directly to it. So, the point being we know the custom unit works with these UUIDs

    I have tried swapping RX and TX round in custom_client.h just in case there was a BLE equivalent of a 2/3 swap needed, but that does not help.

    i.e.

    /** @brief UUID of the TX Characteristic. **/
    #define BT_UUID_CUSTOM_SRV_RX_VAL \
    BT_UUID_128_ENCODE(0x651135b0, 0xdfe9, 0x45a2, 0x95c1, 0x46552a8a77e2)

    /** @brief UUID of the RX Characteristic. **/
    #define BT_UUID_CUSTOM_SRV_TX_VAL \
    BT_UUID_128_ENCODE(0xe7d6274e, 0xb765, 0x11e9, 0xa2a3, 0x2a2ae2dbcce4)



    One ot her slight annoyance that needs sorting is this that is spat out of the UART at boot:

    *** Booting nRF Connect SDK v2.8.0-a2386bfc8401 ***␍␊

    *** Using Zephyr OS v3.7.99-0bc3393fb112 ***␍␊

    If I search for "Booting nRF Connect" in VSC it does not give me any hits. Do you

    know where that is in the source so I can get rid of it please?

  • I'm wondering if there any specific requirements for interfacing with your peripheral device or if it behaves just like the original NUS sample. For instance, if it requires notifications to be enabled on more than one characteristic to allow notifications to be sent or some kind of handshake, etc. Either way, I think it might be faster to use the nRF Connect app to determine what works and what doesn't. For example, you can check if you're able to receive data if you only enable notifications for the e7d6274e-b765-11e9-a2a3-2a2ae2dbcce4 characteristic:

    DiBosco said:
    One ot her slight annoyance that needs sorting is this that is spat out of the UART at boot:

    You can disable the boot banners by adding the following 2 lines to your project configuration file:

    CONFIG_NCS_BOOT_BANNER=n
    CONFIG_BOOT_BANNER=n

Reply
  • I'm wondering if there any specific requirements for interfacing with your peripheral device or if it behaves just like the original NUS sample. For instance, if it requires notifications to be enabled on more than one characteristic to allow notifications to be sent or some kind of handshake, etc. Either way, I think it might be faster to use the nRF Connect app to determine what works and what doesn't. For example, you can check if you're able to receive data if you only enable notifications for the e7d6274e-b765-11e9-a2a3-2a2ae2dbcce4 characteristic:

    DiBosco said:
    One ot her slight annoyance that needs sorting is this that is spat out of the UART at boot:

    You can disable the boot banners by adding the following 2 lines to your project configuration file:

    CONFIG_NCS_BOOT_BANNER=n
    CONFIG_BOOT_BANNER=n

Children
  • Thanks  for the boot banner tip.

    On the subject of writing from the Android app, I have made  some progress, This system is quite complex and the remote unit only writes when requested by a convoluted message, so I found what the standard debug request header is and wrote those 32 bytes using the BLE Scanner app by writing to characteristic UUID

    0x651135b0, 0xdfe9, 0x45a2, 0x95c1, 0x46552a8a77e2

    Now, unless and until I actually hit notify for the

    0xe7d6274e, 0xb765, 0x11e9, 0xa2a3, 0x2a2ae2dbcce4

    characteristic, I got nothing back. When I did hit notify then the
    remote node wrote some valid data into the phone.

    Is the issue that on this Zephyr app we're not subscribing
    (is that the right word) to the read characteristic?

    I'm assuming that should go on in the

    bt_custom_handles_assign

    function?

  • If it is necessary to enable notifications, how do you do that please?

  • OK, a little more info and more questions:

    Firstly - and I'm not sure this actually matters in terms of it functioning. This second line must be wrong:

    err = bt_scan_stop();
    if ((NO_ERROR == err) && (err != -EALREADY))
    {
    LOG_ERR("Stop LE scan failed (err %d)", err);
    }

    This line was originally the mind bending:

    if ((!err) && (err != -EALREADY))

    Even when written out to make it much more understandable where

    NO_ERROR is defined as 0 rather than a magic number that is
    littered throughout all this code.

    This seems like a mad line of code. If err is equal to 0
    it can't be anything else.

    However, as it's just printing out a line of debug code I
    suspect it doesn't actually matter.

    What doesn't seem to be getting called though is this:

    discovery_complete

    This is used in this:

    struct bt_gatt_dm_cb discovery_cb

    Which in turn is filled out, I think, in

    gatt_discover

    But

    gatt_discover never gets called from here:

    err = bt_conn_set_security(conn, BT_SECURITY_L1);

    if (NO_ERROR != err) // was if (err), changed for clarity
    {
    LOG_WRN("Failed to set security: %d", err);

    gatt_discover(conn);
    }

    Surely gatt_discover should be called if err *is* equal
    to 0 which is, again, defined as NO_ERROR for clarity.

    Oh, also I changed bt_conn_set_security to:

    err = bt_conn_set_security(conn, BT_SECURITY_L1);

    As we're not using security on our unit and it was giving
    me an error when it was set to L2. Does that sound right?

    gatt_discover is also called in:

    security_changed, but security_changed never gets called
    (I don't know what this is for).










  • I made some edits to the GATT client implementation I posted earlier to also enable notifications for the second characteristic. Please try it with this.

    /*
     * Copyright (c) 2018 Nordic Semiconductor ASA
     *
     * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
     */
    
    #include <zephyr/bluetooth/conn.h>
    #include <zephyr/bluetooth/uuid.h>
    #include <zephyr/bluetooth/gatt.h>
    
    #include "custom_client.h"
    
    #include <zephyr/logging/log.h>
    LOG_MODULE_REGISTER(custom_c, CONFIG_BT_CUSTOM_CLIENT_LOG_LEVEL);
    
    enum {
    	CUSTOM_C_INITIALIZED,
    	CUSTOM_C_TX_NOTIF_ENABLED,
    	CUSTOM_C_RX_NOTIF_ENABLED,
    	CUSTOM_C_RX_WRITE_PENDING
    };
    
    static uint8_t on_received_tx(struct bt_conn *conn,
    			struct bt_gatt_subscribe_params *params,
    			const void *data, uint16_t length)
    {
    	struct bt_custom_client *custom;
    
    	/* Retrieve CUSTOM Client module context. */
    	custom = CONTAINER_OF(params, struct bt_custom_client, tx_notif_params);
    
    	if (!data) {
    		LOG_DBG("[UNSUBSCRIBED]");
    		params->value_handle = 0;
    		atomic_clear_bit(&custom->state, CUSTOM_C_TX_NOTIF_ENABLED);
    		if (custom->cb.unsubscribed) {
    			custom->cb.unsubscribed(custom);
    		}
    		return BT_GATT_ITER_STOP;
    	}
    
    	LOG_DBG("[NOTIFICATION] data %p length %u", data, length);
    	if (custom->cb.received_tx) {
    		return custom->cb.received_tx(custom, data, length);
    	}
    
    	return BT_GATT_ITER_CONTINUE;
    }
    
    static uint8_t on_received_rx(struct bt_conn *conn,
    			struct bt_gatt_subscribe_params *params,
    			const void *data, uint16_t length)
    {
    	struct bt_custom_client *custom;
    
    	/* Retrieve CUSTOM Client module context. */
    	custom = CONTAINER_OF(params, struct bt_custom_client, rx_notif_params);
    
    	if (!data) {
    		LOG_DBG("[UNSUBSCRIBED]");
    		params->value_handle = 0;
    		atomic_clear_bit(&custom->state, CUSTOM_C_RX_NOTIF_ENABLED);
    		if (custom->cb.unsubscribed) {
    			custom->cb.unsubscribed(custom);
    		}
    		return BT_GATT_ITER_STOP;
    	}
    
    	LOG_DBG("[NOTIFICATION] data %p length %u", data, length);
    	if (custom->cb.received_rx) {
    		return custom->cb.received_rx(custom, data, length);
    	}
    
    	return BT_GATT_ITER_CONTINUE;
    }
    
    static void on_sent(struct bt_conn *conn, uint8_t err,
    		    struct bt_gatt_write_params *params)
    {
    	struct bt_custom_client *custom_c;
    	const void *data;
    	uint16_t length;
    
    	/* Retrieve CUSTOM Client module context. */
    	custom_c = CONTAINER_OF(params, struct bt_custom_client, tx_write_params);
    
    	/* Make a copy of volatile data that is required by the callback. */
    	data = params->data;
    	length = params->length;
    
    	atomic_clear_bit(&custom_c->state, CUSTOM_C_RX_WRITE_PENDING);
    	if (custom_c->cb.sent) {
    		custom_c->cb.sent(custom_c, err, data, length);
    	}
    }
    
    int bt_custom_client_init(struct bt_custom_client *custom_c,
    		       const struct bt_custom_client_init_param *custom_c_init)
    {
    	if (!custom_c || !custom_c_init) {
    		return -EINVAL;
    	}
    
    	if (atomic_test_and_set_bit(&custom_c->state, CUSTOM_C_INITIALIZED)) {
    		return -EALREADY;
    	}
    
    	memcpy(&custom_c->cb, &custom_c_init->cb, sizeof(custom_c->cb));
    
    	return 0;
    }
    
    int bt_custom_client_send(struct bt_custom_client *custom_c, const uint8_t *data,
    		       uint16_t len)
    {
    	int err;
    
    	if (!custom_c->conn) {
    		return -ENOTCONN;
    	}
    
    	if (atomic_test_and_set_bit(&custom_c->state, CUSTOM_C_RX_WRITE_PENDING)) {
    		return -EALREADY;
    	}
    
    	custom_c->tx_write_params.func = on_sent;
    	custom_c->tx_write_params.handle = custom_c->handles.rx;
    	custom_c->tx_write_params.offset = 0;
    	custom_c->tx_write_params.data = data;
    	custom_c->tx_write_params.length = len;
    
    	err = bt_gatt_write(custom_c->conn, &custom_c->tx_write_params);
    	if (err) {
    		atomic_clear_bit(&custom_c->state, CUSTOM_C_RX_WRITE_PENDING);
    	}
    
    	return err;
    }
    
    int bt_custom_handles_assign(struct bt_gatt_dm *dm,
    			  struct bt_custom_client *custom_c)
    {
    	const struct bt_gatt_dm_attr *gatt_service_attr =
    			bt_gatt_dm_service_get(dm);
    	const struct bt_gatt_service_val *gatt_service =
    			bt_gatt_dm_attr_service_val(gatt_service_attr);
    	const struct bt_gatt_dm_attr *gatt_chrc;
    	const struct bt_gatt_dm_attr *gatt_desc;
    
    	if (bt_uuid_cmp(gatt_service->uuid, BT_UUID_CUSTOM_SERVICE)) {
    		return -ENOTSUP;
    	}
    	LOG_DBG("Getting handles from CUSTOM service.");
    	memset(&custom_c->handles, 0xFF, sizeof(custom_c->handles));
    
    	/* CUSTOM TX Characteristic */
    	gatt_chrc = bt_gatt_dm_char_by_uuid(dm, BT_UUID_CUSTOM_TX);
    	if (!gatt_chrc) {
    		LOG_ERR("Missing CUSTOM TX characteristic.");
    		return -EINVAL;
    	}
    	/* CUSTOM TX */
    	gatt_desc = bt_gatt_dm_desc_by_uuid(dm, gatt_chrc, BT_UUID_CUSTOM_TX);
    	if (!gatt_desc) {
    		LOG_ERR("Missing CUSTOM TX value descriptor in characteristic.");
    		return -EINVAL;
    	}
    	LOG_DBG("Found handle for CUSTOM TX characteristic.");
    	custom_c->handles.tx = gatt_desc->handle;
    	/* CUSTOM TX CCC */
    	gatt_desc = bt_gatt_dm_desc_by_uuid(dm, gatt_chrc, BT_UUID_GATT_CCC);
    	if (!gatt_desc) {
    		LOG_ERR("Missing CUSTOM TX CCC in characteristic.");
    		return -EINVAL;
    	}
    	LOG_DBG("Found handle for CCC of CUSTOM TX characteristic.");
    	custom_c->handles.tx_ccc = gatt_desc->handle;
    
    	/* CUSTOM RX Characteristic */
    	gatt_chrc = bt_gatt_dm_char_by_uuid(dm, BT_UUID_CUSTOM_RX);
    	if (!gatt_chrc) {
    		LOG_ERR("Missing CUSTOM RX characteristic.");
    		return -EINVAL;
    	}
    	/* CUSTOM RX */
    	gatt_desc = bt_gatt_dm_desc_by_uuid(dm, gatt_chrc, BT_UUID_CUSTOM_RX);
    	if (!gatt_desc) {
    		LOG_ERR("Missing CUSTOM RX value descriptor in characteristic.");
    		return -EINVAL;
    	}
    	LOG_DBG("Found handle for CUSTOM RX characteristic.");
    	custom_c->handles.rx = gatt_desc->handle;
    
    	/* CUSTOM RX CCC */
    	gatt_desc = bt_gatt_dm_desc_by_uuid(dm, gatt_chrc, BT_UUID_GATT_CCC);
    	if (!gatt_desc) {
    		LOG_ERR("Missing CUSTOM TX CCC in characteristic.");
    		return -EINVAL;
    	}
    
    	LOG_DBG("Found handle for CCC of CUSTOM RX characteristic.");
    	custom_c->handles.rx_ccc = gatt_desc->handle;
    
    	/* Assign connection instance. */
    	custom_c->conn = bt_gatt_dm_conn_get(dm);
    	return 0;
    }
    
    int bt_custom_subscribe_tx_receive(struct bt_custom_client *custom_c)
    {
    	int err;
    
    	if (atomic_test_and_set_bit(&custom_c->state, CUSTOM_C_TX_NOTIF_ENABLED)) {
    		return -EALREADY;
    	}
    
    	custom_c->tx_notif_params.notify = on_received_tx;
    	custom_c->tx_notif_params.value = BT_GATT_CCC_NOTIFY;
    	custom_c->tx_notif_params.value_handle = custom_c->handles.tx;
    	custom_c->tx_notif_params.ccc_handle = custom_c->handles.tx_ccc;
    	atomic_set_bit(custom_c->tx_notif_params.flags,
    		       BT_GATT_SUBSCRIBE_FLAG_VOLATILE);
    
    	err = bt_gatt_subscribe(custom_c->conn, &custom_c->tx_notif_params);
    	if (err) {
    		LOG_ERR("Subscribe failed (err %d)", err);
    		atomic_clear_bit(&custom_c->state, CUSTOM_C_TX_NOTIF_ENABLED);
    	} else {
    		LOG_DBG("[SUBSCRIBED]");
    	}
    
    	return err;
    }
    
    int bt_custom_subscribe_rx_receive(struct bt_custom_client *custom_c)
    {
    	int err;
    
    	custom_c->rx_notif_params.notify = on_received_rx;
    	custom_c->rx_notif_params.value = BT_GATT_CCC_NOTIFY;
    	custom_c->rx_notif_params.value_handle = custom_c->handles.rx;
    	custom_c->rx_notif_params.ccc_handle = custom_c->handles.rx_ccc;
    
    
    	err = bt_gatt_subscribe(custom_c->conn, &custom_c->rx_notif_params);
    	if (err) {
    		LOG_ERR("Subscribe failed (err %d)", err);
    		atomic_clear_bit(&custom_c->state, CUSTOM_C_TX_NOTIF_ENABLED);
    	} else {
    		LOG_DBG("[SUBSCRIBED]");
    	}
    
    	return err;
    }

    custom_client.h

    diff --git a/prj.conf b/prj.conf
    index eacd454..e598756 100644
    --- a/prj.conf
    +++ b/prj.conf
    @@ -41,4 +41,6 @@ CONFIG_LOG_PRINTK=n
     CONFIG_ASSERT=y
     
     CONFIG_NCS_BOOT_BANNER=n
    -CONFIG_BOOT_BANNER=n
    \ No newline at end of file
    +CONFIG_BOOT_BANNER=n
    +
    +CONFIG_BT_CUSTOM_CLIENT_LOG_LEVEL_DBG=y
    \ No newline at end of file
    diff --git a/src/bt_client/custom_client.c b/src/bt_client/custom_client.c
    index a7dffde..f9cbe02 100644
    --- a/src/bt_client/custom_client.c
    +++ b/src/bt_client/custom_client.c
    @@ -16,10 +16,11 @@ LOG_MODULE_REGISTER(custom_c, CONFIG_BT_CUSTOM_CLIENT_LOG_LEVEL);
     enum {
     	CUSTOM_C_INITIALIZED,
     	CUSTOM_C_TX_NOTIF_ENABLED,
    +	CUSTOM_C_RX_NOTIF_ENABLED,
     	CUSTOM_C_RX_WRITE_PENDING
     };
     
    -static uint8_t on_received(struct bt_conn *conn,
    +static uint8_t on_received_tx(struct bt_conn *conn,
     			struct bt_gatt_subscribe_params *params,
     			const void *data, uint16_t length)
     {
    @@ -39,8 +40,35 @@ static uint8_t on_received(struct bt_conn *conn,
     	}
     
     	LOG_DBG("[NOTIFICATION] data %p length %u", data, length);
    -	if (custom->cb.received) {
    -		return custom->cb.received(custom, data, length);
    +	if (custom->cb.received_tx) {
    +		return custom->cb.received_tx(custom, data, length);
    +	}
    +
    +	return BT_GATT_ITER_CONTINUE;
    +}
    +
    +static uint8_t on_received_rx(struct bt_conn *conn,
    +			struct bt_gatt_subscribe_params *params,
    +			const void *data, uint16_t length)
    +{
    +	struct bt_custom_client *custom;
    +
    +	/* Retrieve CUSTOM Client module context. */
    +	custom = CONTAINER_OF(params, struct bt_custom_client, rx_notif_params);
    +
    +	if (!data) {
    +		LOG_DBG("[UNSUBSCRIBED]");
    +		params->value_handle = 0;
    +		atomic_clear_bit(&custom->state, CUSTOM_C_RX_NOTIF_ENABLED);
    +		if (custom->cb.unsubscribed) {
    +			custom->cb.unsubscribed(custom);
    +		}
    +		return BT_GATT_ITER_STOP;
    +	}
    +
    +	LOG_DBG("[NOTIFICATION] data %p length %u", data, length);
    +	if (custom->cb.received_rx) {
    +		return custom->cb.received_rx(custom, data, length);
     	}
     
     	return BT_GATT_ITER_CONTINUE;
    @@ -54,7 +82,7 @@ static void on_sent(struct bt_conn *conn, uint8_t err,
     	uint16_t length;
     
     	/* Retrieve CUSTOM Client module context. */
    -	custom_c = CONTAINER_OF(params, struct bt_custom_client, rx_write_params);
    +	custom_c = CONTAINER_OF(params, struct bt_custom_client, tx_write_params);
     
     	/* Make a copy of volatile data that is required by the callback. */
     	data = params->data;
    @@ -95,13 +123,13 @@ int bt_custom_client_send(struct bt_custom_client *custom_c, const uint8_t *data
     		return -EALREADY;
     	}
     
    -	custom_c->rx_write_params.func = on_sent;
    -	custom_c->rx_write_params.handle = custom_c->handles.rx;
    -	custom_c->rx_write_params.offset = 0;
    -	custom_c->rx_write_params.data = data;
    -	custom_c->rx_write_params.length = len;
    +	custom_c->tx_write_params.func = on_sent;
    +	custom_c->tx_write_params.handle = custom_c->handles.rx;
    +	custom_c->tx_write_params.offset = 0;
    +	custom_c->tx_write_params.data = data;
    +	custom_c->tx_write_params.length = len;
     
    -	err = bt_gatt_write(custom_c->conn, &custom_c->rx_write_params);
    +	err = bt_gatt_write(custom_c->conn, &custom_c->tx_write_params);
     	if (err) {
     		atomic_clear_bit(&custom_c->state, CUSTOM_C_RX_WRITE_PENDING);
     	}
    @@ -163,12 +191,22 @@ int bt_custom_handles_assign(struct bt_gatt_dm *dm,
     	LOG_DBG("Found handle for CUSTOM RX characteristic.");
     	custom_c->handles.rx = gatt_desc->handle;
     
    +	/* CUSTOM RX CCC */
    +	gatt_desc = bt_gatt_dm_desc_by_uuid(dm, gatt_chrc, BT_UUID_GATT_CCC);
    +	if (!gatt_desc) {
    +		LOG_ERR("Missing CUSTOM TX CCC in characteristic.");
    +		return -EINVAL;
    +	}
    +
    +	LOG_DBG("Found handle for CCC of CUSTOM RX characteristic.");
    +	custom_c->handles.rx_ccc = gatt_desc->handle;
    +
     	/* Assign connection instance. */
     	custom_c->conn = bt_gatt_dm_conn_get(dm);
     	return 0;
     }
     
    -int bt_custom_subscribe_receive(struct bt_custom_client *custom_c)
    +int bt_custom_subscribe_tx_receive(struct bt_custom_client *custom_c)
     {
     	int err;
     
    @@ -176,7 +214,7 @@ int bt_custom_subscribe_receive(struct bt_custom_client *custom_c)
     		return -EALREADY;
     	}
     
    -	custom_c->tx_notif_params.notify = on_received;
    +	custom_c->tx_notif_params.notify = on_received_tx;
     	custom_c->tx_notif_params.value = BT_GATT_CCC_NOTIFY;
     	custom_c->tx_notif_params.value_handle = custom_c->handles.tx;
     	custom_c->tx_notif_params.ccc_handle = custom_c->handles.tx_ccc;
    @@ -193,3 +231,24 @@ int bt_custom_subscribe_receive(struct bt_custom_client *custom_c)
     
     	return err;
     }
    +
    +int bt_custom_subscribe_rx_receive(struct bt_custom_client *custom_c)
    +{
    +	int err;
    +
    +	custom_c->rx_notif_params.notify = on_received_rx;
    +	custom_c->rx_notif_params.value = BT_GATT_CCC_NOTIFY;
    +	custom_c->rx_notif_params.value_handle = custom_c->handles.rx;
    +	custom_c->rx_notif_params.ccc_handle = custom_c->handles.rx_ccc;
    +
    +
    +	err = bt_gatt_subscribe(custom_c->conn, &custom_c->rx_notif_params);
    +	if (err) {
    +		LOG_ERR("Subscribe failed (err %d)", err);
    +		atomic_clear_bit(&custom_c->state, CUSTOM_C_TX_NOTIF_ENABLED);
    +	} else {
    +		LOG_DBG("[SUBSCRIBED]");
    +	}
    +
    +	return err;
    +}
    \ No newline at end of file
    diff --git a/src/bt_client/custom_client.h b/src/bt_client/custom_client.h
    index 303ddf8..062a296 100644
    --- a/src/bt_client/custom_client.h
    +++ b/src/bt_client/custom_client.h
    @@ -48,6 +48,11 @@ struct bt_custom_client_handles {
              */
     	uint16_t rx;
     
    +    /** Handle of the CCC descriptor of the CUSTOM RX characteristic,
    +	 *  as provided by a discovery.
    +     */
    +	uint16_t rx_ccc;
    +
             /** Handle of the CUSTOM TX characteristic, as provided by
     	 *  a discovery.
              */
    @@ -75,7 +80,9 @@ struct bt_custom_client_cb {
     	 * @retval BT_GATT_ITER_CONTINUE To keep notifications enabled.
     	 * @retval BT_GATT_ITER_STOP To disable notifications.
     	 */
    -	uint8_t (*received)(struct bt_custom_client *custom, const uint8_t *data, uint16_t len);
    +	uint8_t (*received_tx)(struct bt_custom_client *custom, const uint8_t *data, uint16_t len);
    +
    +	uint8_t (*received_rx)(struct bt_custom_client *custom, const uint8_t *data, uint16_t len);
     
     	/** @brief Data sent callback.
     	 *
    @@ -114,8 +121,11 @@ struct bt_custom_client {
             /** GATT subscribe parameters for CUSTOM TX Characteristic. */
     	struct bt_gatt_subscribe_params tx_notif_params;
     
    +        /** GATT subscribe parameters for CUSTOM RX Characteristic. */
    +	struct bt_gatt_subscribe_params rx_notif_params;
    +
             /** GATT write parameters for CUSTOM RX Characteristic. */
    -	struct bt_gatt_write_params rx_write_params;
    +	struct bt_gatt_write_params tx_write_params;
     
             /** Application callbacks. */
     	struct bt_custom_client_cb cb;
    @@ -189,7 +199,9 @@ int bt_custom_handles_assign(struct bt_gatt_dm *dm,
      * @retval 0 If the operation was successful.
      *           Otherwise, a negative error code is returned.
      */
    -int bt_custom_subscribe_receive(struct bt_custom_client *custom);
    +int bt_custom_subscribe_tx_receive(struct bt_custom_client *custom);
    +
    +int bt_custom_subscribe_rx_receive(struct bt_custom_client *custom);
     
     #ifdef __cplusplus
     }
    diff --git a/src/main.c b/src/main.c
    index dcfc7cf..4c65942 100644
    --- a/src/main.c
    +++ b/src/main.c
    @@ -75,7 +75,7 @@ static void ble_data_sent(struct bt_custom_client *custom, uint8_t err,
     	}
     }
     
    -static uint8_t ble_data_received(struct bt_custom_client *custom,
    +static uint8_t ble_data_received_tx(struct bt_custom_client *custom,
     						const uint8_t *data, uint16_t len)
     {
     	ARG_UNUSED(custom);
    @@ -120,6 +120,16 @@ static uint8_t ble_data_received(struct bt_custom_client *custom,
     	return BT_GATT_ITER_CONTINUE;
     }
     
    +static uint8_t ble_data_received_rx(struct bt_custom_client *custom,
    +						const uint8_t *data, uint16_t len)
    +{
    +	ARG_UNUSED(custom);
    +
    +	LOG_INF("Received data from RX characteristic");
    +
    +	return BT_GATT_ITER_CONTINUE;
    +}
    +
     static void uart_cb(const struct device *dev, struct uart_event *evt, void *user_data)
     {
     	ARG_UNUSED(dev);
    @@ -294,7 +304,8 @@ static void discovery_complete(struct bt_gatt_dm *dm,
     	bt_gatt_dm_data_print(dm);
     
     	bt_custom_handles_assign(dm, custom);
    -	bt_custom_subscribe_receive(custom);
    +	bt_custom_subscribe_tx_receive(custom);
    +	bt_custom_subscribe_rx_receive(custom);
     
     	bt_gatt_dm_data_release(dm);
     }
    @@ -380,15 +391,10 @@ static void connected(struct bt_conn *conn, uint8_t conn_err)
     		LOG_WRN("MTU exchange failed (err %d)", err);
     	}
     
    -	err = bt_conn_set_security(conn, BT_SECURITY_L2);
    -	if (err) {
    -		LOG_WRN("Failed to set security: %d", err);
    -
    -		gatt_discover(conn);
    -	}
    -
    +	gatt_discover(conn);
    +	
     	err = bt_scan_stop();
    -	if ((!err) && (err != -EALREADY)) {
    +	if ((err) && (err != -EALREADY)) {
     		LOG_ERR("Stop LE scan failed (err %d)", err);
     	}
     }
    @@ -467,7 +473,8 @@ static int custom_client_init(void)
     	int err;
     	struct bt_custom_client_init_param init = {
     		.cb = {
    -			.received = ble_data_received,
    +			.received_tx = ble_data_received_tx,
    +			.received_rx = ble_data_received_rx,
     			.sent = ble_data_sent,
     		}
     	};
    @@ -501,19 +508,17 @@ static int scan_init(void)
     {
     	int err;
     	struct bt_scan_init_param scan_init = {
    -		.connect_if_match = 0,
    +		.connect_if_match = 1,
     	};
     
     	bt_scan_init(&scan_init);
     	bt_scan_cb_register(&scan_cb);
     
    -	//err = bt_scan_filter_add(BT_SCAN_FILTER_TYPE_UUID, BT_UUID_CUSTOM_SERVICE);
    -	//if (err) {
    -	//	LOG_ERR("Scanning filters cannot be set (err %d)", err);
    -	//	return err;
    -	//}
    -
    -
    +	err = bt_scan_filter_add(BT_SCAN_FILTER_TYPE_UUID, BT_UUID_CUSTOM_SERVICE);
    +	if (err) {
    +		LOG_ERR("Scanning filters cannot be set (err %d)", err);
    +		return err;
    +	}
     
     	bt_addr_le_t addr = {
     		.type = BT_ADDR_LE_RANDOM,
    @@ -526,7 +531,7 @@ static int scan_init(void)
     		return err;
     	}
     
    -	err = bt_scan_filter_enable(BT_SCAN_ADDR_FILTER, false);
    +	err = bt_scan_filter_enable(BT_SCAN_UUID_FILTER, false);
     	if (err) {
     		LOG_ERR("Filters cannot be turned on (err %d)", err);
     		return err;
    

    This change is also required in main.c:

    @@ -294,7 +304,8 @@ static void discovery_complete(struct bt_gatt_dm *dm,
     	bt_gatt_dm_data_print(dm);
     
     	bt_custom_handles_assign(dm, custom);
    -	bt_custom_subscribe_receive(custom);
    +	bt_custom_subscribe_tx_receive(custom);
    +	bt_custom_subscribe_rx_receive(custom);
     
     	bt_gatt_dm_data_release(dm);
     }

    DiBosco said:
    if ((!err) && (err != -EALREADY))

    I assume the developer intended to write if ((err) && (err != -EALREADY)) so the error message would be printed for any error except -EALREADY. However, I don't see any obvious reasons for ignoring the EALREADY error in this sample. There is no side effect to this as only the error message is printed, which I assume is why it has not been caught by our tests. Will report this internally.

    This is how I would have expected it to be written:

    	err = bt_scan_stop();
    	if (err) {
    		LOG_ERR("Stop LE scan failed (err %d)", err);
    	}

    The use of 0 to indicate success is an established convention in C, and is not limited to Zephyr or the linux kernel. But I see your point. defining a symbol such as NO_ERROR could help improve the code readability. In the old SDK we had the NRF_SUCCESS symbol that was defined as '0'.

    DiBosco said:
    err = bt_conn_set_security(conn, BT_SECURITY_L1);

    As we're not using security on our unit and it was giving
    me an error when it was set to L2. Does that sound right?

    Since your peripheral doesn't implement BLE security, you can remove this line and call gatt_discover() unconditionally in your connected callback.

  • OK, that's crashing again now:

    [00:00:00.013,366] <0x1b>[0m<inf> bt_hci_core: No ID address. App must call settings_load()<0x1b>[0m␍␊

    [00:00:00.013,397] <0x1b>[0m<inf> central_uart: Bluetooth initialized<0x1b>[0m␍␊

    [00:00:00.014,373] <0x1b>[0m<inf> bt_hci_core: Identity: D9:AD:8D:A9:1A:A2 (random)<0x1b>[0m␍␊

    [00:00:00.014,404] <0x1b>[0m<inf> bt_hci_core: HCI: version 6.0 (0x0e) revision 0x104e, manufacturer 0x0059<0x1b>[0m␍␊

    [00:00:00.014,434] <0x1b>[0m<inf> bt_hci_core: LMP: version 6.0 (0x0e) subver 0x104e<0x1b>[0m␍␊

    [00:00:00.015,747] <0x1b>[1;31m<err> os: ***** USAGE FAULT *****<0x1b>[0m␍␊

    [00:00:00.015,747] <0x1b>[1;31m<err> os: Unaligned memory access<0x1b>[0m␍␊

    [00:00:00.015,777] <0x1b>[1;31m<err> os: r0/a1: 0x20003800 r1/a2: 0x00000000 r2/a3: 0x00000001<0x1b>[0m␍␊

    [00:00:00.015,777] <0x1b>[1;31m<err> os: r3/a4: 0x00038207 r12/ip: 0x00038207 r14/lr: 0x00014bc5<0x1b>[0m␍␊

    [00:00:00.015,808] <0x1b>[1;31m<err> os: xpsr: 0x61000000<0x1b>[0m␍␊

    [00:00:00.015,808] <0x1b>[1;31m<err> os: Faulting instruction address (r15/pc): 0x00014bd6<0x1b>[0m␍␊

    [00:00:00.015,838] <0x1b>[1;31m<err> os: >>> ZEPHYR FATAL ERROR 31: Unknown error on CPU 0<0x1b>[0m␍␊

    [00:00:00.015,899] <0x1b>[1;31m<err> os: Current thread: 0x200032c0 (main)<0x1b>[0m␍␊

    [00:00:01.547,698] <0x1b>[1;31m<err> os: Halting system<0x1b>[0m␍␊

    I can only assume that when modifying the code to allow for this new subscription I've messed something up.

     

    The use of 0 to indicate success is an established convention in C, and is not limited to Zephyr or the linux kernel. But I see your point. defining a symbol such as NO_ERROR could help improve the code readability. In the old SDK we had the NRF_SUCCESS symbol that was defined as '0'.

    I do, of course, realise this and I also know that coding style varies massively from person to person. I do appreciate that some people even like K&R style indentation.

    I've never liked this

    if (whatevs)

    type of thing, right from when I started learning C in the 90s it always made me baulk. IMHO code should be as easy as possible to read.

    Having done a medical job where they were incredibly strict about stuff like this, as well as things like never using magic numbers it really teaches you how valuable it is to not take short cuts, not to ever have ifs without ==, not to call functions with a call to a function, not use use things like the ternary operator and so on.

    I believe some of how C was originally invented was to mitigate for the fact that space to store source code was at a premium, but those days are long gone. Disk space is plentiful and cheap and there's no reason not to be verbose in the interest of clarity.   Especially when you're providing code for others to read. It especially helps when debugging and single stepping through code.

    I'll see if I can go through this code by update setting breakpoints and trying to work out where it crashes, in the meantime if there's anything you can thing of to track this down that would be great. I can't pretend to really follow exactly what's going on with these changes, let alone how the system works as whole, I get partway down one of these deep callback/macro/structure in a structure rabbit holes and soon forget where I was up to or what I was trying to do.

    Since your peripheral doesn't implement BLE security, you can remove this line and call gatt_discover() unconditionally in your connected callback.

    OK, cool, thanks!

    Thanks again for all the help, it is greatly appreciated.

Related