Nrf5340 DK Page: Difference between revisions

From bibbleWiki
Jump to navigation Jump to search
 
(6 intermediate revisions by the same user not shown)
Line 23: Line 23:
west update
west update
west zephyr-export
west zephyr-export
</syntaxhighlight>
=Creating a BLE Service=
==Introduction==
The NRF connection software for BLE uses Zephyr with lots and lots of macros. For the example BLE service we first created a service and a characterics
==Service==
For me I basically create a remote control service on the device. When you press a button then the device sends a notification to the connected devices. Implementing the service involved initializing bluetooth and using macros. For the bluetooth it involved calling bt_enable and bt_le_adv_start. You can pass callbacks to this for various purposes. In the code below we pass a callback for on connection
<syntaxhighlight lang="cpp">
int bluetooth_init(struct bt_conn_cb *conn_callbacks)
{
    int err;
    LOG_INF("Initializing Bluetooth");
    err = bt_enable(bt_ready);
    if (err)
    {
        LOG_ERR("bt_enable returned %d", err);
        return err;
    }
    LOG_INF("bt_enable successfully called 1");
    k_sem_take(&bt_init_ok, K_FOREVER);
    LOG_INF("bt_enable successfully called 2");
    // Check for no callbacks
    if (conn_callbacks == NULL)
    {
        LOG_ERR("No connection callbacks provided");
        return -NRFX_ERROR_NULL;
    }
    // Register connection callbacks
    bt_conn_cb_register(conn_callbacks);
    // Start advertising
    err = bt_le_adv_start(BT_LE_ADV_CONN, ad, ARRAY_SIZE(ad), sd, ARRAY_SIZE(sd));
    if (err)
    {
        LOG_ERR("bt_le_adv_start returned %d", err);
        return err;
    }
    LOG_INF("bt_le_adv_start successfully called");
}
</syntaxhighlight>
The two key macros were
<syntaxhighlight lang="cpp">
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),
};
static const struct bt_data sd[] = {
    BT_DATA_BYTES(BT_DATA_UUID128_ALL, BT_UUID_REMOTE_SERV_VAL),
};
</syntaxhighlight>
==Characteristics==
For each service you can define data for the service known as characteristics. These are structure data sent to the connected devices. In out case this was the button pressed on the device. It really became quite ugly defining these characteristics with huge macro. Looking forward to seeing the rust approach.
<syntaxhighlight lang="cpp">
static ssize_t read_button_characteristic_cb(
    struct bt_conn *conn,
    const struct bt_gatt_attr *attr,
    void *buf,
    uint16_t len,
    uint16_t offset);
BT_GATT_SERVICE_DEFINE(remote_srv,
                      BT_GATT_PRIMARY_SERVICE(BT_UUID_REMOTE_SERVICE),
                      // This is the characteristic that will be used to send notifications
                      BT_GATT_CHARACTERISTIC(
                          BT_UUID_REMOTE_BUTTON_CHRC,
                          BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY,
                          BT_GATT_PERM_READ,
                          read_button_characteristic_cb, NULL, NULL),
                      BT_GATT_CCC(NULL, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE), );
/* Callbacks */
static ssize_t read_button_characteristic_cb(
    struct bt_conn *conn,
    const struct bt_gatt_attr *attr,
    void *buf,
    uint16_t len,
    uint16_t offset)
{
    return bt_gatt_attr_read(
        conn,
        attr,
        buf, len,
        offset,
        &button_value, sizeof(button_value));
};
</syntaxhighlight>
The BT_GATT_SERVICE_DEFINE defines the service, in this case this is
* UUID of the service
* Characteristics for the service
* The callbacks to implement
The code to implement the read button last pressed is shown here.
==Notifications==
Without this implement, the use could request the value of the button pressed. With notifications the value is pushed out. In the video I was shown forwards how to do this however I am going to document this backwards
===Initiate the notification===
In the main when the button is pressed we send the notification and check for errors
<syntaxhighlight lang="cpp">
LOG_INF("Button %d pressed", button_presssed);
set_button_value(button_presssed);
// Notify the connected device
int err = send_button_notification(default_conn, button_presssed, sizeof(button_presssed));
if (err)
{
    LOG_ERR("Failed to send button notification (err %d)", err);
}
</syntaxhighlight>
===Send Notification===
For this we create two functions. on_sent is a callback function for debugging and the other is the send function so nothing to hairy here to understand.
<syntaxhighlight lang="cpp">
void on_sent(struct bt_conn *conn, void *user_data)
{
    ARG_UNUSED(user_data);
    LOG_INF("Notification sent");
}
int send_button_notification(struct bt_conn *conn, uint8_t button_value, uint8_t len)
{
    int err = 0;
    struct bt_gatt_notify_params params = {
        .attr = &remote_srv.attrs[2],
        .data = &button_value,
        .len = len,
        .func = on_sent,
    };
    err = bt_gatt_notify_cb(conn, &params);
    return err;
}
</syntaxhighlight>
===Add Callback to Bluetooth Initialization===
To make this work we need to define a callback and set it when we initialize the bluetooth service. We already have the on connect callback so this is just repeating that approach. So we
* add callback to init arguments
* check callback not null
* register callback
<syntaxhighlight lang="cpp">
int bluetooth_init(struct bt_conn_cb *conn_callbacks, struct bt_remote_srv_cb *remote_cb)
...
    // Check for remote service callback
    if (remote_cb == NULL)
    {
        LOG_ERR("No remote callback provided");
        return -NRFX_ERROR_NULL;
    }
    // Register remote service callbacks
    remote_service_callbacks.notify_changed = remote_cb->notify_changed;
    // bt_remote_srv_cb_register(remote_cb);
</syntaxhighlight>
===Create Callback===
<syntaxhighlight lang="cpp">
/* Declare characteristic changed cb */
static void button_chrc_ccc_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value);
/* Callbacks */
...
void button_chrc_ccc_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value)
{
    bool notif_enabled = (value == BT_GATT_CCC_NOTIFY);
    LOG_INF("Button characteristic CCC configuration changed so %s", notif_enabled ? "enabled" : "disabled");
}
</syntaxhighlight>
</syntaxhighlight>

Latest revision as of 06:30, 18 May 2024

Introduction

This page is for information regarding the Nordic NRF5340-DK

Development Setup

  • You can download mrf-command-line-tools_10.24.2_amd64.deb from here
  • You then download nrfutil and move it to /usr/local/bin and set permissions

Install the toolchain manager with

nrfutil install toolchain-manager

List the available SDKS and install

nrfutil toolchain-manager search
nrfutil toolchain-manager install --ncs-version v2.6.1

Next start some wrapper for git I needed to fix this first with

sudo ln -s /usr/lib/x86_64-linux-gnu/libunistring.so.5 /usr/lib/x86_64-linux-gnu/libunistring.so.2
nrfutil toolchain-manager launch --shell
west init -m https://github.com/nrfconnect/sdk-nrf --mr v2.6.1
west update
west zephyr-export

Creating a BLE Service

Introduction

The NRF connection software for BLE uses Zephyr with lots and lots of macros. For the example BLE service we first created a service and a characterics

Service

For me I basically create a remote control service on the device. When you press a button then the device sends a notification to the connected devices. Implementing the service involved initializing bluetooth and using macros. For the bluetooth it involved calling bt_enable and bt_le_adv_start. You can pass callbacks to this for various purposes. In the code below we pass a callback for on connection

int bluetooth_init(struct bt_conn_cb *conn_callbacks)
{
    int err;
    LOG_INF("Initializing Bluetooth");

    err = bt_enable(bt_ready);
    if (err)
    {
        LOG_ERR("bt_enable returned %d", err);
        return err;
    }
    LOG_INF("bt_enable successfully called 1");
    k_sem_take(&bt_init_ok, K_FOREVER);
    LOG_INF("bt_enable successfully called 2");

    // Check for no callbacks
    if (conn_callbacks == NULL)
    {
        LOG_ERR("No connection callbacks provided");
        return -NRFX_ERROR_NULL;
    }

    // Register connection callbacks
    bt_conn_cb_register(conn_callbacks);

    // Start advertising
    err = bt_le_adv_start(BT_LE_ADV_CONN, ad, ARRAY_SIZE(ad), sd, ARRAY_SIZE(sd));
    if (err)
    {
        LOG_ERR("bt_le_adv_start returned %d", err);
        return err;
    }
    LOG_INF("bt_le_adv_start successfully called");
}

The two key macros were

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),
};

static const struct bt_data sd[] = {
    BT_DATA_BYTES(BT_DATA_UUID128_ALL, BT_UUID_REMOTE_SERV_VAL),
};

Characteristics

For each service you can define data for the service known as characteristics. These are structure data sent to the connected devices. In out case this was the button pressed on the device. It really became quite ugly defining these characteristics with huge macro. Looking forward to seeing the rust approach.

static ssize_t read_button_characteristic_cb(
    struct bt_conn *conn,
    const struct bt_gatt_attr *attr,
    void *buf,
    uint16_t len,
    uint16_t offset);

BT_GATT_SERVICE_DEFINE(remote_srv,
                       BT_GATT_PRIMARY_SERVICE(BT_UUID_REMOTE_SERVICE),
                       // This is the characteristic that will be used to send notifications
                       BT_GATT_CHARACTERISTIC(
                           BT_UUID_REMOTE_BUTTON_CHRC,
                           BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY,
                           BT_GATT_PERM_READ,
                           read_button_characteristic_cb, NULL, NULL),
                       BT_GATT_CCC(NULL, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE), );

/* Callbacks */
static ssize_t read_button_characteristic_cb(
    struct bt_conn *conn,
    const struct bt_gatt_attr *attr,
    void *buf,
    uint16_t len,
    uint16_t offset)
{

    return bt_gatt_attr_read(
        conn,
        attr,
        buf, len,
        offset,
        &button_value, sizeof(button_value));
};

The BT_GATT_SERVICE_DEFINE defines the service, in this case this is

  • UUID of the service
  • Characteristics for the service
  • The callbacks to implement

The code to implement the read button last pressed is shown here.

Notifications

Without this implement, the use could request the value of the button pressed. With notifications the value is pushed out. In the video I was shown forwards how to do this however I am going to document this backwards

Initiate the notification

In the main when the button is pressed we send the notification and check for errors

LOG_INF("Button %d pressed", button_presssed);
set_button_value(button_presssed);

// Notify the connected device
int err = send_button_notification(default_conn, button_presssed, sizeof(button_presssed));
if (err)
{
    LOG_ERR("Failed to send button notification (err %d)", err);
}

Send Notification

For this we create two functions. on_sent is a callback function for debugging and the other is the send function so nothing to hairy here to understand.

void on_sent(struct bt_conn *conn, void *user_data)
{
    ARG_UNUSED(user_data);
    LOG_INF("Notification sent");
}

int send_button_notification(struct bt_conn *conn, uint8_t button_value, uint8_t len)
{
    int err = 0;

    struct bt_gatt_notify_params params = {
        .attr = &remote_srv.attrs[2],
        .data = &button_value,
        .len = len,
        .func = on_sent,
    };

    err = bt_gatt_notify_cb(conn, &params);
    return err;
}

Add Callback to Bluetooth Initialization

To make this work we need to define a callback and set it when we initialize the bluetooth service. We already have the on connect callback so this is just repeating that approach. So we

  • add callback to init arguments
  • check callback not null
  • register callback
int bluetooth_init(struct bt_conn_cb *conn_callbacks, struct bt_remote_srv_cb *remote_cb)
...
 
    // Check for remote service callback
    if (remote_cb == NULL)
    {
        LOG_ERR("No remote callback provided");
        return -NRFX_ERROR_NULL;
    }

    // Register remote service callbacks
    remote_service_callbacks.notify_changed = remote_cb->notify_changed;
    // bt_remote_srv_cb_register(remote_cb);

Create Callback

/* Declare characteristic changed cb */
static void button_chrc_ccc_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value);


/* Callbacks */
...
void button_chrc_ccc_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value)
{
    bool notif_enabled = (value == BT_GATT_CCC_NOTIFY);
    LOG_INF("Button characteristic CCC configuration changed so %s", notif_enabled ? "enabled" : "disabled");
}