Implementing Bluetooth LE with Zephyr and XIAO nRF54L15 – Verifying GATT Communication with nRF Connect

Introduction

As we go through our daily lives, we often use various technologies without knowing how they actually work. Bluetooth is one of them. We use many Bluetooth products, but we rarely have the opportunity to implement the technology ourselves.

While studying the Zephyr RTOS, I decided to implement Bluetooth LE myself. Rather than creating a simple communication example, I wanted to build something that could actually be used. So I decided to make a Remote Metronome for the band I play with as a hobby.

The concept is simple. The BPM and Start/Stop settings are configured on a smartphone and sent to the remote device via Bluetooth LE. The XIAO nRF54L15 then flashes an LED light according to the configured BPM. The goal is to allow multiple band members to watch the light and play together at the same tempo.

In this article, rather than focusing on the metronome application itself, I will focus on the underlying Bluetooth LE implementation using the XIAO nRF54L15 and Zephyr.

Using nRF Connect for Mobile on a smartphone, we will discover and connect to the device, examine the GATT Service and Characteristics, and test Write and Notify communication in practice. The complete source code will also be provided separately for download.

Basic Structure of Bluetooth LE

Bluetooth LE (Bluetooth Low Energy) is a Bluetooth communication technology designed for exchanging small amounts of data with low power consumption. It is widely used in devices that exchange relatively simple data with smartphones, such as sensors, wearable devices, and remote controllers.

In this project, there is no need to transfer large amounts of data between the smartphone and the metronome. Only the BPM value, Start/Stop commands, and current operating status need to be exchanged, making Bluetooth LE well suited for this application.

To understand Bluetooth LE communication, it is useful to first understand the relationship between Central and Peripheral devices.

In this project, the smartphone acts as the Central, while the XIAO nRF54L15 acts as the Peripheral.

Smartphone
nRF Connect
(Central)
    |
    | Bluetooth LE
    |
    ▼
XIAO nRF54L15
(Peripheral)
The XIAO nRF54L15, acting as the Peripheral, performs Advertising to announce that it is available for connection. The smartphone scans for nearby BLE devices, finds the metronome, and connects to it.
XIAO nRF54L15                         Smartphone
   Peripheral                           Central
      |                                   |
      |------ Advertising --------------->|
      |                                   |
      |<--------- Connection -------------|
      |                                   |
      |<========= BLE Communication ======>|

After the connection is established, an important concept for exchanging actual data is GATT (Generic Attribute Profile).

In GATT, the functions provided by a device are organized into Services and Characteristics.

GATT
│
└── Service
      │
      ├── Characteristic
      │      └── Value
      │
      ├── Characteristic
      │      └── Value
      │
      └── Characteristic
             └── Value
A Service groups related functions together, while a Characteristic is the actual unit through which data is exchanged.

For example, in the remote metronome, a single Metronome Service can contain Characteristics for BPM, Start/Stop, and the current operating status.

Characteristic data can be exchanged in several ways. In this project, we mainly use Write and Notify.

Write is used to send a value from the smartphone to the nRF54L15. Changing the BPM or sending a Start/Stop command from the smartphone are examples of Write operations.

In contrast, Notify is used by the nRF54L15 to inform the smartphone when a value has changed. Instead of requiring the smartphone to continuously read and check the value, the device can send the updated status when needed.

Therefore, the basic data flow of this project is as follows.

Smartphone                         XIAO nRF54L15

BPM / Start / Stop
        ------ Write ------------>

        <----- Notify ------------
              Device Status
Bluetooth LE terminology may seem somewhat complicated at first, but for this project, it is enough to understand the following basic flow:

Advertising → Connection → Service → Characteristic → Write/Notify

Now, we will implement this structure in Zephyr and verify each step using nRF Connect.

Development Environment and Hardware Configuration

Hardware Configuration

In this project, I used the Seeed Studio XIAO nRF54L15 board. Detailed specifications of the board can be found in the official Seeed Studio documentation.

Seeed Studio XIAO nRF54L15 Official Documentation

The XIAO nRF54L15 is a compact development board based on Nordic Semiconductor’s nRF54L15. It supports Bluetooth LE and allows applications to be developed using the Zephyr RTOS.

The hardware configuration used in this project is shown below.

The D0 GPIO of the XIAO nRF54L15 is used to control a TIP122 NPN transistor, which switches the external LED lighting ON and OFF. A 3.3 kΩ resistor is connected between the GPIO pin and the base of the TIP122.

According to the BPM and Start/Stop commands received via Bluetooth LE, the XIAO nRF54L15 controls D0, causing the external LED lighting to flash at the configured BPM.

The main purpose of this article is not the LED driver circuit itself, but rather the implementation of Bluetooth LE communication using the XIAO nRF54L15 and Zephyr.

Software Development Environment

The software was developed based on Zephyr RTOS. Zephyr provides a Bluetooth LE stack and GATT APIs, allowing us to use them to implement a Peripheral device.

Zephyr Bluetooth Official Documentation

The development environment used for this project is as follows.

Item Environment
Development Board Seeed Studio XIAO nRF54L15
RTOS Zephyr RTOS
Development Tool Visual Studio Code
Language C
Wireless Communication Bluetooth LE
BLE Testing Tool nRF Connect for Mobile
Test Device Android Smartphone

The firmware was written and built in the Zephyr development environment using Visual Studio Code, and then downloaded to the XIAO nRF54L15.

On the smartphone, I used nRF Connect for Mobile. With nRF Connect, nearby BLE devices can be scanned and connected without creating a separate smartphone application, allowing us to inspect their GATT Services and Characteristics.

It is also very useful for BLE firmware development and testing because values can be written directly to Characteristics using Write, and data sent from the device can be monitored using Notify.

In this project, I first used nRF Connect to fully verify the Bluetooth LE functionality of the nRF54L15. In the next stage, the system will be expanded into an actual metronome smartphone application.

Software Configuration

Project Structure

Now that the hardware setup is complete, we can run Bluetooth LE on the XIAO nRF54L15.

Zephyr includes a Bluetooth stack and APIs for Bluetooth LE, so there is no need to implement Bluetooth communication from scratch. We only need to enable the required features and use the APIs provided by Zephyr to build the application.

In this project, instead of placing all functionality in a single file, the program is divided into several modules as shown below.

The Bluetooth LE functionality is implemented in ble.c and ble.h, LED control in led.c and led.h, and the metronome operation in metronome.c and metronome.h. The main.c file initializes each module and manages the overall execution flow of the program.

Separating the source code by function makes it easier to modify and test the Bluetooth communication, LED control, and metronome operation independently.

In a Zephyr project, the source files to be included in the build are specified in CMakeLists.txt. In this project, main.c, led.c, metronome.c, and ble.c are registered as application source files.

The source files registered with target_sources() are compiled together and form a single Zephyr application. Header files such as ble.h, led.h, and metronome.h are included from the source files using #include, so they do not need to be registered separately with target_sources().

Board Configuration

The board configuration is the same as the one used in the previous project. I used the official XIAO nRF54L15 board configuration provided by Zephyr and applied the custom board name my_xiao_nrf54l15 based on it.

Since the detailed process of creating the custom board was covered in a previous article, I will not repeat it here and will focus on the Bluetooth LE configuration.

Based on this project structure, the next step is to enable the required Bluetooth LE features in prj.conf and configure the application for BLE operation.

Bluetooth LE Configuration

In Zephyr, the features used by a project are configured in the prj.conf file.

For this project, I enabled the basic features required to operate as a Bluetooth LE Peripheral, along with the settings needed for GATT communication.

The following is the prj.conf configuration used in this project.

CONFIG_GPIO enables GPIO functionality for controlling the external LED lighting, while CONFIG_PRINTK and CONFIG_CONSOLE are used to display simple debug messages and check the operating status.

The settings directly related to Bluetooth LE are as follows.

CONFIG_BT=y
CONFIG_BT_PERIPHERAL=y
CONFIG_BT_DEVICE_NAME="XIAO_NRF54L15_METRONOME"

CONFIG_BT enables Zephyr’s Bluetooth functionality, while CONFIG_BT_PERIPHERAL configures the XIAO nRF54L15 to operate as a BLE Peripheral.

CONFIG_BT_DEVICE_NAME specifies the BLE device name. Later, when scanning for nearby devices with nRF Connect on a smartphone, the device can be identified by the name XIAO_NRF54L15_METRONOME.

Finally,

CONFIG_KERNEL_BIN_NAME="my_xiao_nrf54l15_metronome_ble_redesign"
specifies the name of the firmware binary generated by the build process.

With the prj.conf configuration complete, Zephyr is now ready to use Bluetooth LE.

Next, we will initialize the Bluetooth stack and start Advertising in the program, then verify that the XIAO nRF54L15 can actually be discovered from a smartphone.

Bluetooth LE Implementation

Bluetooth Initialization and Advertising

Now that the software configuration is complete, we can start running Bluetooth LE on the device.

In this project, the XIAO nRF54L15 operates as a Peripheral. A Peripheral device first initializes the Bluetooth functionality and then starts Advertising so that nearby Central devices can discover it.

The overall process is as follows.

XIAO nRF54L15
      |
      ▼
Bluetooth Initialization
      |
      ▼
Start Advertising
      |
      ▼
Scan from Smartphone
      |
      ▼
BLE Device Discovered

In Zephyr, the bt_enable() function is used to initialize and enable the Bluetooth stack.

Once bt_enable() completes successfully, the device is ready to use Bluetooth LE.

The next step is to start Advertising. Advertising is the process by which a Peripheral device periodically announces its presence and basic information to nearby devices.

In this project, the Advertising data includes the device name and GATT Service information so that the device can be discovered by a smartphone.

The key part for starting Advertising is Zephyr’s bt_le_adv_start() function.

Once Advertising starts, the XIAO nRF54L15 becomes a discoverable BLE Peripheral device.

On the smartphone, we can run nRF Connect for Mobile and perform a scan to find the device name specified in prj.conf.

XIAO nRF54L15                         Smartphone
   Peripheral                         nRF Connect
      |                                   |
      |---- Advertising ----------------->|
      |                                   |
      |                         Scan / Discovery
      |                                   |
      |<---- Connection Request ----------|
      |                                   |
      |<======= BLE Connection ==========>|

At this point, the first stage of the BLE connection process — Bluetooth Initialization → Advertising → Scan → Device Discovery — is complete.

Next, we will implement the GATT Service and Characteristics required to exchange actual data after the smartphone connects to the device.

GATT Service and Characteristics

Through Advertising, the smartphone can now discover and connect to the XIAO nRF54L15. The next step is to configure the GATT Service and Characteristics used to exchange actual data between the connected devices.

As discussed earlier, GATT groups related functions into a Service, while actual data is exchanged through Characteristics.

In this project, I created a custom Metronome Service containing a Control Characteristic and a BPM Characteristic.

Metronome Service
      |
      ├── Control Characteristic
      |       |
      |       ├── 0x00 : STOP
      |       ├── 0x01 : START
      |       └── 0x02 : STATUS REQUEST
      |
      └── BPM Characteristic
              |
              └── BPM Setting

Each Service and Characteristic is identified by a UUID (Universally Unique Identifier). In Zephyr, BT_GATT_SERVICE_DEFINE() can be used to define a GATT Service and the Characteristics it contains.

Control Characteristic

The Control Characteristic is used to control the metronome’s Start/Stop operation and to request its current status.

When the smartphone Writes a value to the Control Characteristic, the write_control() callback function is called, and the metronome operates according to the received value as follows.

0x00 : STOP
0x01 : START
0x02 : STATUS REQUEST

When 0x00 is written, metronome_stop() is called and the metronome stops. When 0x01 is written, metronome_start() is called and the metronome starts operating.

After a Start or Stop command is processed, the updated Control state can also be sent back to the smartphone through Notify using notify_control().

0x02 is not an operating command. Instead, it is used to request the current status.

When 0x02 is written from the smartphone, the XIAO nRF54L15 checks the current Control value and BPM value, combines them into two bytes of data, and sends the result back to the smartphone through Notify.

Smartphone
    |
    | Control Write : 0x02
    ▼
XIAO nRF54L15
    |
    ├── Current Control Value
    ├── Current BPM Value
    |
    ▼
Status Notify
    |
    ▼
Smartphone

BPM Characteristic

The BPM Characteristic is used to set the metronome’s BPM value and read the current value.

When a new value is Written to the BPM Characteristic from the smartphone, the write_bpm() callback function is called by Zephyr’s GATT handling process.

For example, when a value of 60 is written as uint8 from nRF Connect, it is processed as follows.

nRF Connect
    |
    | BPM Characteristic
    | Write : 60
    ▼
XIAO nRF54L15
    |
    ▼
write_bpm()
    |
    ▼
BPM = 60
    |
    ▼
Change Metronome Timing
Conversely, when the BPM Characteristic is Read from the smartphone, the read_bpm() callback function is called, allowing the currently configured BPM value to be checked.

In other words, a Characteristic acts as a channel through which data is exchanged in Bluetooth LE, while callback functions such as write_control() and write_bpm() handle the actual data received through that channel.

Operation Flow

The data received through Bluetooth LE is directly connected to the actual metronome operation. When a Start command is received through the Control Characteristic, the metronome starts operating. When a Stop command is received, the operation stops. The value received through the BPM Characteristic is used to determine the flashing interval of the metronome.

The actual beat generation is performed by the XIAO nRF54L15, not by the smartphone. The smartphone only sends the BPM and Start/Stop commands through Bluetooth LE, and the nRF54L15 flashes the external LED lighting according to the configured BPM.

The overall program flow is as follows.

Smartphone
    |
    | Bluetooth LE
    | Start / Stop / BPM
    ▼
ble.c
    |
    ▼
metronome.c
    |
    | Generate beat according to BPM
    ▼
led.c
    |
    ▼
External LED Lighting

The metronome timing generation and LED control are handled separately in metronome.c and led.c. Since this article focuses on the Bluetooth LE implementation, the details of the metronome and LED control code will not be covered here.

Likewise, rather than explaining every detail of the GATT implementation code, I have focused on the overall structure and data flow. The complete source code will be provided separately at the end of this article.

Now that the GATT Service and Characteristics have been configured, we will build and download the firmware and verify their operation on a smartphone using nRF Connect for Mobile.

Build and Download

Now that the Bluetooth LE and metronome program configuration is complete, we can build the project and download it to the XIAO nRF54L15.

In this project, the build targets the my_xiao_nrf54l15 custom board created earlier.

From the project directory, build the project using the following command.

west build -b my_xiao_nrf54l15/nrf54l15/cpuapp -p always -- -DBOARD_ROOT="D:/Zephyr/workspace/my_boards"
The -b option specifies the board target, which in this project is my_xiao_nrf54l15/nrf54l15/cpuapp.

The BOARD_ROOT option specifies the location of the custom board definition. In my development environment, the custom board is located in:

D:/Zephyr/workspace/my_boards
Once the build is completed successfully, download the generated firmware to the XIAO nRF54L15 using the following command.
west flash
After the firmware is downloaded and the program starts running, Bluetooth LE is initialized and Advertising begins. The XIAO nRF54L15 now operates as a BLE Peripheral and waits for a connection from a smartphone.

Next, we will run nRF Connect for Mobile on the smartphone and verify that Bluetooth LE communication is working correctly.

Testing with nRF Connect for Mobile

So far, we have initialized Bluetooth LE on the XIAO nRF54L15 and configured Advertising, the GATT Service, and Characteristics.

Now, we will use nRF Connect for Mobile on a smartphone to discover and connect to the BLE device, and then verify that the implemented GATT communication works correctly.

nRF Connect is a Bluetooth LE development and testing application provided by Nordic Semiconductor. Without creating a separate smartphone application, it allows us to scan for and connect to BLE devices, inspect their Services and Characteristics, and directly test functions such as Read, Write, and Notify.

nRF Connect for Mobile Official Page

In this project, we will verify the operation in the following sequence.

nRF Connect
    |
    ▼
Scan for BLE Devices
    |
    ▼
Discover XIAO_NRF54L15_METRONOME
    |
    ▼
Connect
    |
    ▼
Check GATT Service / Characteristics
    |
    ├── Control Write
    |       ├── 0x00 : STOP
    |       ├── 0x01 : START
    |       └── 0x02 : STATUS REQUEST
    |
    ├── BPM Write
    |
    └── Check Notify

Device Discovery and Connection

First, download the program to the XIAO nRF54L15 and run it.

Once the program starts successfully, Bluetooth LE Advertising begins, and the XIAO nRF54L15 becomes a Peripheral device that can be discovered by nearby smartphones.

Run nRF Connect for Mobile on the smartphone and start a scan.

Search for the device name configured in prj.conf:

XIAO_NRF54L15_METRONOME

The screen below shows the device discovered during the scan.

Once the device is discovered, select CONNECT to establish a connection with the XIAO nRF54L15.

After the connection is successfully established, nRF Connect displays the GATT Services and Characteristics provided by the XIAO nRF54L15.

The screen below shows the connected device.

Once the connection is established, nRF Connect reads the GATT information provided by the XIAO nRF54L15 and displays it on the screen.

Along with the Generic Attribute and Generic Access Services that are commonly used in Bluetooth, the Unknown Service created specifically for this project also appears.

The reason it is displayed as Unknown does not indicate an error. Since this project uses Custom UUIDs defined specifically for the application, nRF Connect does not know the names of the corresponding Service and Characteristics.

Expanding the Custom Service created in this project reveals two Characteristics.

Unknown Service
(Metronome Service)
      |
      ├── Unknown Characteristic
      |       └── Control Characteristic
      |
      └── Unknown Characteristic
              └── BPM Characteristic
The first Characteristic is the Control Characteristic, which is used to send Start, Stop, and Status Request commands.

The second is the BPM Characteristic, which is used to set the metronome’s BPM value or read the current value.

On the nRF Connect screen, the functions available for each Characteristic are also indicated by icons. The Control Characteristic supports Read, Write, and Notify, while the BPM Characteristic is configured to support Read and Write.

Therefore, even without creating a separate smartphone application, nRF Connect allows us to directly access the GATT Service and Characteristics implemented in Zephyr and exchange data with them.

Now, we will first Write a value to the Control Characteristic to Start and Stop the metronome.

Metronome Operation

Now, we will use nRF Connect to operate the metronome.

Start/Stop

Click the upward arrow icon displayed on the right side of the Control Characteristic. The following screen will appear.

When the Write button for the Control Characteristic is selected, the Write value screen appears as shown above.

In this project, the Control commands are defined as 1-byte values.

0x00 : STOP
0x01 : START
0x02 : STATUS REQUEST
First, to start the metronome, enter 0x01 and press SEND.
Control Characteristic
        |
        | Write : 0x01
        ▼
XIAO nRF54L15
        |
        ▼
      START
        |
        ▼
According to the configured BPM,
the external LED lighting flashes
When 0x01 is received, the write_control() callback function on the XIAO nRF54L15 is called. The Start command is then processed, and the metronome begins operating.

Next, write 0x00 in the same way. Since 0x00 is the Stop command, the metronome stops operating and the LED stops flashing.

This confirms that the actual metronome running on the XIAO nRF54L15 can be Started and Stopped simply by writing values directly to the Control Characteristic from nRF Connect.

Next, we will Write a value to the BPM Characteristic to change the LED flashing rate.

Now that we have confirmed that the metronome can Start and Stop correctly, we will change the BPM value.

Changing the BPM

The second BPM Characteristic is used to configure the BPM. In nRF Connect, select the Write button for the BPM Characteristic to open the screen where a value can be entered.

The BPM value is transferred as a 1-byte uint8 value. By selecting UINT8 as the input format in nRF Connect, the BPM value can be entered directly as a decimal number without converting it to hexadecimal.

For example, to set the BPM to 90, enter the following:

Data type : UINT8
Value     : 90
After entering the value, press SEND. The value 90 is then transferred to the XIAO nRF54L15 through the BPM Characteristic.
nRF Connect
    |
    | BPM Characteristic
    | Write : 90
    ▼
XIAO nRF54L15
    |
    ▼
write_bpm()
    |
    ▼
BPM = 90
    |
    ▼
Change Metronome Timing
    |
    ▼
External LED Lighting Flashes

On the XIAO nRF54L15, when a value is received through the BPM Characteristic, the write_bpm() callback function is called and the new BPM value is applied to the metronome.

By changing the BPM to values such as 90 or 120, you can verify that the flashing rate of the external LED lighting changes accordingly.

The screen below shows the value entry process.

Sending Status Using Notify

In Bluetooth LE, communication is not limited to Write, where the smartphone sends data to the device. The Notify function can also be used to send data from the device to the smartphone.

In this project, Notify functionality was added to the Control Characteristic. When Notify is enabled on the smartphone, the XIAO nRF54L15 can send changes in the metronome status to the smartphone.

In addition, when 0x02 is Written to the Control Characteristic, the current Control status and BPM value are sent through Notify.

nRF Connect
    |
    | Write : 0x02
    ▼
XIAO nRF54L15
    |
    ├── Control Status
    └── BPM Value
    |
    | Notify
    ▼
nRF Connect

This confirms that communication works in both directions: the smartphone can control the device, and the device can send its current status back to the smartphone.

The detailed implementation of Notify is not covered in this article, but it can be found in the complete project source code.

Project Source Code Download

The complete source code for the XIAO nRF54L15 + Zephyr Bluetooth LE Metronome project used in this article is available for download.

The project includes not only the Bluetooth LE implementation but also the metronome and external LED control code, allowing you to test the complete operation.

src/
├── main.c
├── ble.c
├── ble.h
├── metronome.c
├── metronome.h
├── led.c
└── led.h

CMakeLists.txt
prj.conf

The main Bluetooth LE implementation can be found in ble.c and ble.h, while the BPM processing and LED flashing intervals can be found in metronome.c and led.c.

[Download the Complete Project Source Code]

When using the downloaded project, check the board configuration and BOARD_ROOT path according to your own Zephyr development environment before building the project.

Conclusion

In this article, we implemented Bluetooth LE using the Seeed Studio XIAO nRF54L15 and Zephyr RTOS.

We first looked at the basic Bluetooth LE structure, including the relationship between Central and Peripheral devices, Advertising, GATT Services, and Characteristics. We then created a Custom GATT Service in Zephyr and added Control and BPM Characteristics to exchange data with a smartphone.

Using nRF Connect for Mobile, we were able to discover and connect to the XIAO nRF54L15 without creating a separate smartphone application. We also verified that Start/Stop commands and BPM values could be sent directly through Characteristic Write operations to control the external LED lighting.

Using Notify, data can also be sent in the opposite direction, allowing the XIAO nRF54L15 to report its current status to the smartphone.

Through this experiment, we were able to verify the basic Bluetooth LE communication flow on actual hardware:

Advertising → Connection → GATT → Write / Notify

Most importantly, Zephyr already provides the Bluetooth stack, so there is no need to implement the Bluetooth protocol itself from scratch. By configuring Services and Characteristics using the APIs provided by Zephyr, a BLE device can be implemented relatively easily.

At this stage, nRF Connect serves as the BLE Central and testing tool between the smartphone and the XIAO nRF54L15.

In the next stage, this role will be replaced by a custom smartphone application. Using Flutter and Dart, we will develop a dedicated application for the Tok Tok Metronome, allowing the BPM and Start/Stop functions to be controlled via BLE directly from the smartphone.

댓글 남기기