In this exercise, we will cover core Bluetooth LE APIs in nRF Connect SDK. We will first learn how to enable the Bluetooth LE stack and the default configurations associated with it.
Then we will dive into how to configure our hardware to broadcast non-connectable advertising, making the device act as a beacon so that neighboring scanning devices can see the data.
0. Prepare the project and build and flash it to your board
0.1 Clone the GitHub repository for this course.
Copy the link to the repository and use VS Code’s Command Palette (Go to View -> Command Palette -> type Git Clone and paste in the repository link) to clone the https://github.com/NordicDeveloperAcademy/bt-fund
repository somewhere close to your root directory (Ex: C:\myfw\btfund
).
Avoid storing the repo in locations with long paths, as the build system might fail on some operating systems (Windows) if the application path is too long.
0.2 In the nRF Connect extension in VS Code, select Open an existing application, and open the base code for this exercise, found in bt-fund/<version>/l2/l2_e1
, <version>
is whichever version directory you are using.
The course repository contains two version directories depending on which nRF Connect SDK version you are using: v2.8.x-v2.7.0
and v2.3.0-v2.6.2
. Select the directory that matches the nRF Connect SDK version you are using. Some of the exercises will also have varying exercise texts depending on which version you are using, this will be reflected by tabs at the beginning of the exercise text.
1. Include the Bluetooth LE stack in your project.
In order to include the Bluetooth LE stack in your nRF Connect SDK project, you need to enable CONFIG_BT
in prj.conf
. This option will already be enabled in all upcoming exercises.
CONFIG_BT=y
KconfigEnabling this symbol will apply a set of default configurations for the stack.
The highlights of the default configuration are listed below:
BT_BROADCASTER
) is enabled.BT_LL_CHOICE
= BT_LL_SOFTDEVICE
).BT_CTLR_TX_PWR = BT_CTLR_TX_PWR_0
)2. Set the Bluetooth LE device name.
The name is a C string that can theoretically be up to 248 bytes long (excluding NULL termination). In practice, it is highly recommended to keep it as short as possible, as when we include it in the advertising data, we have only 31 bytes, and these 31 bytes are shared by all advertising data. It can also be an empty string. In this exercise, we will call our device Nordic_Beacon
.
Add the following line in prj.conf
:
CONFIG_BT_DEVICE_NAME="Nordic_Beacon"
KconfigWe will include the device name in the advertising data in a later step.
3. Include the header files of the Bluetooth LE stack needed
Include the following header files needed for enabling the stack, populating the advertising data, and starting advertising.
Add the following lines in main.c
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/gap.h>
C4. Prepare the advertising data.
For the advertising data, we will use both the advertising packet and the scan response packet.
4.1 Prepare the advertising packet.
4.1.1 Declare an array ad[]
of type struct bt_data
, which will be the advertising packet to send out.
Add the following lines in main.c
static const struct bt_data ad[] = {
/* STEP 4.1.2 - Set the advertising flags */
/* STEP 4.1.3 - Set the advertising packet data */
};
C4.1.2 Populate the flags using the helper macro BT_DATA_BYTES()
.
The first thing we need to prepare in the advertising packet is the advertising flags, BT_DATA_FLAGS
.
To help us populate these flags, we will use the helper macro BT_DATA_BYTES()
, which has the following signature
In this exercise, we are creating a broadcaster with non-connectable advertising. Therefore, we will only set the advertising flag BT_LE_AD_NO_BREDR
, to indicate that classic Bluetooth (BR/EDR) is not supported.
Since Nordic’s products only support Bluetooth LE, this flag should always be set to this value.
Add the following line in main.c
BT_DATA_BYTES(BT_DATA_FLAGS, BT_LE_AD_NO_BREDR),
C4.1.3 Set the advertising packet data using the helper macro BT_DATA()
.
There are numerous standard data types that can be included in the advertising data (either in the advertising packet or the scan response). These data types are defined in the Bluetooth Supplement to the Core Specification.
Below are a few commonly used ones, that we will be using in following exercises. The complete list of advertising data types can be found here.
BT_DATA_NAME_COMPLETE
): This is simply the device name. We will include the Complete Local Name in the advertising packet of this exercise.BT_DATA_NAME_SHORTENED
): A shorter version of the Complete Local name. BT_DATA_URI
): You can use this type for advertising URI like website addresses (URLs). We will include the URL of Nordic Developer Academy (https://academy.nordicsemi.com/) in the scan response packet of this exercise. BT_DATA_MANUFACTURER_DATA
). This is a popular type that enables companies to define their own custom advertising data, as in the case of iBeacon. We will cover using this data type in the exercise of the next lesson.For now, we want to include the complete local name, BT_DATA_NAME_COMPLETE
, in the advertising packet.
We will use the macro BT_DATA()
to populate data into the advertising packet. The macro expects three parameters as shown in the signature below
Add the following line in main.c
BT_DATA(BT_DATA_NAME_COMPLETE, DEVICE_NAME, DEVICE_NAME_LEN),
CNote in legacy advertising we have only 31 octets available. To send more than 31 octets in advertisements, we will need to use the scan response, as we will see next.
4.2. Prepare the scan response.
In the scan response, we will include the URL for Nordic Developer Academy, for the sake of demonstration.
4.2.1 Declare the scan response packet.
Same as we did for the advertising packet, we will declare it as an array sd[]
of type struct bt_data
.
Add the following code in main.c
static const struct bt_data sd[] = {
/* 4.2.3 Include the URL data in the scan response packet*/
};
C4.2.2 Declare the URL data to include in the scan response packet.
We will declare the URL as an array of static unsigned char
. In the first byte, we need to specify the URI Scheme Name String Mapping as specified in the Assigned Numbers Document from the Bluetooth SIG. The scheme is used to save data transmitted over the air. So for example, instead of transmitting advertising of 6 bytes for the “https:”, we only need to send one byte (0x17).
Add the following lines in main.c
static unsigned char url_data[] ={0x17,'/','/','a','c','a','d','e','m','y','.',
'n','o','r','d','i','c','s','e','m','i','.',
'c','o','m'};
C4.2.3 Include the URL data in the scan response packet. Add the following line inside the sd
packet.
BT_DATA(BT_DATA_URI, url_data,sizeof(url_data)),
C5. Enable the Bluetooth LE stack.
The function bt_enable()
is used to enable the Bluetooth LE stack in the application. This function must be called before any other calls that require communication with the Bluetooth LE hardware (for example, start advertising).
bt_enable()
is blocking when passing NULL to it, and non-blocking if you pass a bt_ready_cb_t
callback.
Add the following lines in main()
err = bt_enable(NULL);
if (err) {
LOG_ERR("Bluetooth init failed (err %d)\n", err);
return -1;
}
LOG_INF("Bluetooth initialized\n");
C6. Start advertising.
Now that we have prepared the advertising data (both the advertising packet and the scan response packet), we are ready to start advertising.
Do this by calling the function, bt_le_adv_start()
, which has the following signature
The first parameter this function expects is the advertising parameters. Here, we can either use predefined macros that cover the most common cases. Or we can declare a variable of type bt_le_adv_param
and set the parameters manually.
For now, we will use one of the predefined macros. In exercise 2, we will set this parameter manually. Available macros are documented here. In our case, we will be using BT_LE_ADV_NCONN
– non-connectable advertising with a minimum advertising interval of 100 ms and a maximum advertising interval of 150 ms.
The second and third parameters are the advertising packet (created in step 4.1) and its size, while the fourth and fifth parameters are the scan response (created in step 4.2) and its size.
Add the following lines inside main()
err = bt_le_adv_start(BT_LE_ADV_NCONN, ad, ARRAY_SIZE(ad), sd, ARRAY_SIZE(sd));
if (err) {
LOG_ERR("Advertising failed to start (err %d)\n", err);
return -1;
}
C7. Build and flash the application on your board.
You should notice that LED1 (LED0 on nRF54 Series devices) on your board is blinking, indicating that your board is advertising.
8. Open nRF Connect for Mobile on your smartphone.
In the SCANNER tab press on the SCAN button to begin scanning
9. Tap on Nordic_Beacon
to view the advertising data.
The first thing to notice is that there is no CONNECT button displayed. This is because we are advertising non-connectable packets (BT_LE_ADV_NCONN
). Let’s now spend some time interpreting the data.
BT_LE_ADV_NCONN
, the advertising interval is between 100-150 msWe encourage you to try and change these parameters in the code, build and flash your board to see how easy it is to control the advertising data.
In the Scanner tab, select the play icon to begin scanning
9. Tap on Nordic_Beacon
to view the advertising data.
The first thing to notice is that there is no Connect button displayed. This is because we are advertising non-connectable packets (BT_LE_ADV_NCONN
). Let’s now spend some time interpreting the data.
BT_LE_ADV_NCONN
, the advertising interval is between 100-150 msSince the iOS Bluetooth stack filters out a lot of the advertising data, this is all the information we can see.
We encourage you to try and change these parameters in the code, build and flash your board to see how easy it is to control the advertising data.