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


  • 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.

  • Actually, one more thing that might help me.

    Those debug messages that are turned on by having:

    CONFIG_LOG_BACKEND_UART=y

    In the conf file are very useful, but no good when I'm actually
    trying to ship traffic through the BLE "UART". Could I route
    it through to a different UART, preferably one of the
    virtual com ports that are on the debug USB interface?
    I do see that I have

    /dev/ttyACMx where x is couple of a numbers per dev kit.


    Would be great to have one of those take the traffic and send it to Cutecom to see what's going on at the same time I'm trying to use the BLE "UART".

Related