Zephyr와 XIAO nRF54L15로 Bluetooth LE 구현하기 – nRF Connect로 GATT 통신 확인

서론

우리가 세상을 살아가다 보면 주변의 많은 기술을 그것이 어떻게 만들어졌는지도 모르는 채 사용하는 경우가 많습니다. 블루투스도 그중 하나입니다. 수많은 블루투스 제품을 사용하고 있지만 직접 구현해 볼 기회는 많지 않습니다.

저는 Zephyr RTOS를 공부하면서 Bluetooth LE를 직접 구현해 보기로 했습니다. 단순한 통신 시험보다는 실제로 사용할 수 있는 것을 만들어 보고 싶어, 취미로 하고 있는 밴드를 위한 리모트 메트로놈(Remote Metronome)을 만들어 보기로 했습니다.

구성은 간단합니다. 스마트폰에서 BPM과 Start/Stop을 설정하면 Bluetooth LE를 통해 리모트 장치로 전달되고, XIAO nRF54L15가 설정된 BPM에 맞추어 LED 조명을 점멸시킵니다. 여러 밴드 멤버가 이 조명을 보면서 같은 박자에 맞추어 연주하는 것이 목적입니다.

이번 글에서는 메트로놈 애플리케이션 자체보다는 그 기반이 되는 XIAO nRF54L15 + Zephyr의 Bluetooth LE 구현에 초점을 맞춥니다.

스마트폰의 nRF Connect for Mobile을 이용하여 장치를 검색하고 연결한 후, GATT Service와 Characteristic을 확인하고 Write와 Notify 통신까지 실제로 시험해 보겠습니다. 전체 소스 코드는 다운로드할 수 있도록 별도로 제공합니다.

Bluetooth LE의 기본 구조

Bluetooth LE(Bluetooth Low Energy)는 적은 양의 데이터를 낮은 전력으로 주고받는 데 적합한 Bluetooth 통신 방식입니다. 센서, 웨어러블 장치, 리모컨과 같이 스마트폰과 간단한 데이터를 주고받는 장치에 널리 사용됩니다.

이번 프로젝트에서도 스마트폰과 메트로놈 사이에 대용량 데이터를 전송할 필요는 없습니다. BPM 값, Start/Stop 명령, 현재 동작 상태 정도만 주고받으면 되므로 Bluetooth LE가 적합합니다.

Bluetooth LE 통신을 이해하기 위해서는 먼저 Central과 Peripheral의 관계를 알아두는 것이 좋습니다.

이번 프로젝트에서는 스마트폰이 Central, XIAO nRF54L15가 Peripheral 역할을 합니다.

Smartphone
nRF Connect
(Central)
     │
     │ Bluetooth LE
     │
     ▼
XIAO nRF54L15
(Peripheral)
Peripheral인 XIAO nRF54L15는 자신이 연결 가능한 장치라는 것을 주변에 알리는 Advertising을 합니다. 스마트폰은 주변의 BLE 장치를 Scan하여 메트로놈을 찾고 연결합니다.
XIAO nRF54L15                         Smartphone
   Peripheral                           Central
       │                                   │
       │────── Advertising ───────────────>│
       │                                   │
       │<──────── Connection ──────────────│
       │                                   │
       │<========= BLE 통신 =============>│
연결된 후 실제 데이터를 주고받을 때 중요한 개념이 GATT(Generic Attribute Profile)입니다.

GATT에서는 장치가 제공하는 기능을 ServiceCharacteristic이라는 구조로 표현합니다.

GATT
 │
 └── Service
       │
       ├── Characteristic
       │      └── Value
       │
       ├── Characteristic
       │      └── Value
       │
       └── Characteristic
              └── Value
Service는 관련된 기능들을 하나로 묶어 놓은 것이고, Characteristic은 실제 데이터를 주고받는 항목이라고 생각하면 이해하기 쉽습니다.

예를 들어 리모트 메트로놈에서는 하나의 Metronome Service 안에 BPM이나 Start/Stop, 현재 상태 등을 위한 Characteristic을 만들 수 있습니다.

Characteristic의 데이터는 여러 방법으로 주고받을 수 있는데, 이번 프로젝트에서는 주로 Write와 Notify를 사용합니다.

Write는 스마트폰에서 nRF54L15로 값을 전달하는 방법입니다. 스마트폰에서 BPM을 변경하거나 Start/Stop 명령을 보내는 경우가 이에 해당합니다.

반대로 Notify는 nRF54L15에서 값이 변경되었을 때 그 내용을 스마트폰으로 알려주는 방법입니다. 스마트폰이 계속 값을 읽어 확인할 필요 없이 장치가 필요한 시점에 새로운 상태를 전달할 수 있습니다.

따라서 이번 프로젝트의 기본적인 데이터 흐름은 다음과 같습니다.

Smartphone                         XIAO nRF54L15

BPM / Start / Stop
        ─────── Write ────────────>

        <────── Notify ───────────
             Device Status

처음에는 Bluetooth LE의 용어가 다소 복잡하게 느껴질 수 있지만, 이번 프로젝트에서는 우선 Advertising → Connection → Service → Characteristic → Write/Notify의 흐름만 이해하면 충분합니다.

이제 이 구조를 Zephyr에서 실제로 구현하고 nRF Connect를 이용하여 하나씩 확인해 보겠습니다.

개발 환경 및 하드웨어 구성

하드웨어 구성

이번 프로젝트에서는 Seeed Studio XIAO nRF54L15 보드를 사용했습니다. 보드에 대한 자세한 사양은 Seeed Studio 공식 문서에서 확인할 수 있습니다.

Seeed Studio XIAO nRF54L15 공식 문서

XIAO nRF54L15는 Nordic Semiconductor의 nRF54L15를 사용한 소형 개발 보드로 Bluetooth LE를 지원하며, Zephyr RTOS를 이용하여 애플리케이션을 개발할 수 있습니다.

아래는 하드웨어가 구성된 사진입니다.

XIAO nRF54L15의 D0 GPIO를 이용하여 TIP122 NPN 트랜지스터를 제어하고, TIP122를 통해 외부 LED 조명을 ON/OFF하도록 구성했습니다. GPIO와 TIP122의 베이스 사이에는 3.3 kΩ 저항을 사용했습니다.

Bluetooth LE를 통해 전달받은 BPM과 Start/Stop 명령에 따라 XIAO nRF54L15가 D0를 제어하고, 외부 LED 조명이 설정된 BPM에 맞추어 점멸합니다.

이번 글의 주된 목적은 LED 구동 회로 자체보다는 XIAO nRF54L15와 Zephyr를 이용한 Bluetooth LE 통신 구현에 있습니다.

소프트웨어 개발 환경

소프트웨어는 Zephyr RTOS를 기반으로 작성했습니다. Zephyr에서는 Bluetooth LE Stack과 GATT API를 제공하므로 이를 이용하여 Peripheral 장치를 구현할 수 있습니다.

Zephyr Bluetooth 공식 문서

이번 프로젝트의 개발 환경은 다음과 같습니다.

항목 사용 환경
개발 보드 Seeed Studio XIAO nRF54L15
RTOS Zephyr RTOS
개발 도구 Visual Studio Code
언어 C
무선 통신 Bluetooth LE
BLE 시험 nRF Connect for Mobile
시험 장치 Android Smartphone

펌웨어는 Visual Studio Code의 Zephyr 개발 환경에서 작성하고 빌드한 후 XIAO nRF54L15에 다운로드했습니다.

스마트폰에서는 nRF Connect for Mobile을 사용했습니다. nRF Connect를 이용하면 별도의 스마트폰 애플리케이션을 만들지 않아도 주변의 BLE 장치를 검색하고 연결하여 GATT Service와 Characteristic을 확인할 수 있습니다.

또한 Characteristic에 직접 값을 Write하거나 장치에서 전송되는 Notify를 확인할 수 있기 때문에 BLE 펌웨어를 개발하고 시험하는 데 매우 유용합니다.

이번 프로젝트에서는 먼저 nRF Connect를 이용하여 nRF54L15의 Bluetooth LE 기능을 충분히 확인한 후, 이후 단계에서 실제 메트로놈용 스마트폰 애플리케이션으로 확장하는 방법을 사용했습니다.

소프트웨어 구성

프로젝트 구성

하드웨어 구성이 끝났으므로 이제 XIAO nRF54L15에서 Bluetooth LE를 동작시켜 보겠습니다.

Zephyr에는 Bluetooth LE를 위한 Bluetooth Stack과 API가 포함되어 있기 때문에, Bluetooth 통신을 처음부터 직접 구현할 필요는 없습니다. 필요한 기능을 설정하고 Zephyr에서 제공하는 API를 이용하여 애플리케이션을 구성할 수 있습니다.

이번 프로젝트에서는 프로그램의 기능을 한 파일에 모두 넣지 않고 다음과 같이 나누어 구성했습니다.

Bluetooth LE 관련 기능은 ble.c, ble.h, LED 제어는 led.c, led.h, 메트로놈 동작은 metronome.c, metronome.h로 분리하여 구성했습니다. main.c에서는 각 모듈을 초기화하고 전체 프로그램의 실행 흐름을 관리합니다.

기능별로 소스를 분리하면 Bluetooth 통신, LED 제어, 메트로놈 동작을 서로 독립적으로 수정하고 시험하기가 쉬워집니다.

Zephyr 프로젝트에서는 어떤 소스 파일을 빌드에 포함할 것인지 CMakeLists.txt에 지정합니다. 이번 프로젝트에서는 main.c, led.c, metronome.c, ble.c를 애플리케이션 소스로 등록했습니다.

target_sources()에 등록된 소스 파일들이 함께 컴파일되어 하나의 Zephyr 애플리케이션을 구성합니다. 헤더 파일인 ble.h, led.h, metronome.h는 각 소스 파일에서 #include하여 사용하므로 별도로 target_sources()에 등록할 필요는 없습니다.

보드 설정

보드 설정은 이전 프로젝트에서 사용한 방법과 동일합니다. Zephyr에서 제공하는 XIAO nRF54L15 공식 보드 설정을 복사하여 my_xiao_nrf54l15 커스텀 보드를 만들고 이를 사용했습니다.

커스텀 보드를 만드는 자세한 과정은 이전 글에서 다루었으므로, 이번 글에서는 별도로 설명하지 않고 Bluetooth LE 구현에 집중하겠습니다.

이 프로젝트 구조를 바탕으로 다음 단계에서는 prj.conf에서 Bluetooth LE 기능을 활성화하고, 실제 BLE 동작을 위한 설정을 추가하겠습니다.

Bluetooth LE 기능 설정

Zephyr에서는 프로젝트에서 사용할 기능을 prj.conf 파일에서 설정합니다.

이번 프로젝트에서는 Bluetooth LE Peripheral로 동작하기 위한 기본 기능과 GATT 통신에 필요한 항목들을 활성화했습니다.

아래는 설정된 prj.conf 코드 입니다.

CONFIG_GPIO는 외부 LED 조명을 제어하기 위한 GPIO 기능을 활성화하고, CONFIG_PRINTKCONFIG_CONSOLE은 프로그램의 동작 상태를 확인하기 위한 콘솔 출력을 사용합니다.

Bluetooth LE와 직접 관련된 설정은 다음 세 항목입니다.

CONFIG_BT=y
CONFIG_BT_PERIPHERAL=y
CONFIG_BT_DEVICE_NAME="XIAO_NRF54L15_METRONOME"

CONFIG_BT를 통해 Zephyr의 Bluetooth 기능을 활성화하고, CONFIG_BT_PERIPHERAL을 설정하여 XIAO nRF54L15가 BLE Peripheral로 동작하도록 합니다.

CONFIG_BT_DEVICE_NAME은 BLE 장치의 이름을 지정합니다. 이후 스마트폰에서 nRF Connect로 Scan하면 XIAO_NRF54L15_METRONOME이라는 이름으로 장치를 확인할 수 있습니다.

마지막의

CONFIG_KERNEL_BIN_NAME="my_xiao_nrf54l15_metronome_ble_redesign"
은 빌드 결과로 생성되는 펌웨어의 이름을 지정하기 위한 설정입니다.

이렇게 prj.conf 설정을 마치면 Zephyr에서 Bluetooth LE를 사용할 준비가 됩니다.

다음으로 프로그램에서 Bluetooth Stack을 초기화하고 Advertising을 시작하여, 실제로 스마트폰에서 XIAO nRF54L15가 검색되는지 확인해 보겠습니다.

Bluetooth LE 구현

Bluetooth 초기화와 Advertising

소프트웨어 구성이 완료되었으므로 이제 실제로 Bluetooth LE를 동작시켜 보겠습니다.

XIAO nRF54L15는 이번 프로젝트에서 Peripheral로 동작합니다. Peripheral 장치는 먼저 Bluetooth 기능을 초기화한 후 주변의 Central 장치가 자신을 발견할 수 있도록 Advertising을 시작합니다.

전체적인 동작 순서는 다음과 같습니다.

XIAO nRF54L15
      │
      ▼
Bluetooth 초기화
      │
      ▼
Advertising 시작
      │
      ▼
Smartphone에서 Scan
      │
      ▼
BLE 장치 발견

Zephyr에서는 Bluetooth Stack을 활성화하기 위해 bt_enable() 함수를 사용합니다.

bt_enable()이 정상적으로 완료되면 Bluetooth LE 기능을 사용할 준비가 된 것입니다.

다음으로 Advertising을 시작합니다. Advertising은 Peripheral 장치가 주변에 자신의 존재와 기본적인 정보를 주기적으로 알리는 과정입니다.

이번 프로젝트에서는 Advertising 데이터에 장치 이름과 GATT Service 정보를 포함시켜 스마트폰에서 장치를 검색할 수 있도록 구성했습니다.

Advertising을 시작하는 핵심 부분은 Zephyr의 bt_le_adv_start()를 이용합니다.

Advertising이 시작되면 XIAO nRF54L15는 주변에서 검색 가능한 BLE Peripheral 장치가 됩니다.

스마트폰에서는 nRF Connect for Mobile을 실행하고 Scan을 수행하여 prj.conf에서 지정한 장치 이름을 확인할 수 있습니다.

XIAO nRF54L15                     Smartphone
   Peripheral                    nRF Connect
       │                              │
       │──── Advertising ────────────>│
       │                              │
       │                         Scan / 발견
       │                              │
       │<──── Connection Request ─────│
       │                              │
       │<====== BLE Connection ======>│
여기까지 동작하면 Bluetooth 초기화 → Advertising → Scan → 장치 발견이라는 BLE 연결의 첫 번째 과정이 완료된 것입니다.

다음으로 스마트폰이 장치에 연결된 후 실제 데이터를 주고받기 위해 필요한 GATT Service와 Characteristic을 구현해 보겠습니다.

GATT Service와 Characteristic

Advertising을 통해 스마트폰에서 XIAO nRF54L15를 찾고 연결할 수 있게 되었습니다. 이제 연결된 장치 사이에서 실제 데이터를 주고받기 위한 GATT Service와 Characteristic을 구성합니다.

앞에서 살펴본 것처럼 GATT에서는 관련된 기능을 하나의 Service로 묶고, 실제 데이터는 Characteristic을 통해 주고받습니다.

이번 프로젝트에서는 메트로놈 제어를 위한 하나의 Custom Service를 만들고, 그 안에 Control CharacteristicBPM Characteristic을 구성했습니다.

Metronome Service
       │
       ├── Control Characteristic
       │       │
       │       ├── 0x00 : STOP
       │       ├── 0x01 : START
       │       └── 0x02 : STATUS REQUEST
       │
       └── BPM Characteristic
               │
               └── BPM 설정
각 Service와 Characteristic은 UUID(Universally Unique Identifier)를 이용하여 구분합니다.

Zephyr에서는 BT_GATT_SERVICE_DEFINE()를 이용하여 GATT Service와 그 안의 Characteristic을 정의할 수 있습니다.

Control Characteristic

Control Characteristic은 메트로놈의 Start/Stop 제어와 상태 확인을 위해 사용합니다.

스마트폰에서 Control Characteristic에 값을 Write하면 write_control() callback 함수가 호출되고, 전달된 값에 따라 다음과 같이 동작합니다.

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

0x00을 Write하면 metronome_stop()이 호출되어 메트로놈이 정지하고, 0x01을 Write하면 metronome_start()가 호출되어 메트로놈이 동작을 시작합니다.

Start 또는 Stop 명령이 처리되면 notify_control()을 통해 변경된 Control 상태를 스마트폰으로 Notify할 수도 있습니다.

0x02는 동작 명령이 아니라 현재 상태를 요청하는 명령입니다.

스마트폰에서 0x02를 Write하면 XIAO nRF54L15는 현재 Control 값과 BPM 값을 확인하여 두 Byte의 데이터로 구성한 후 Notify를 통해 스마트폰으로 전달합니다.

Smartphone
    │
    │ Control Write : 0x02
    ▼
XIAO nRF54L15
    │
    ├── 현재 Control 값
    └── 현재 BPM 값
    │
    ▼
Status Notify
    │
    ▼
Smartphone

BPM Characteristic

BPM Characteristic은 메트로놈의 BPM 값을 설정하고 현재 값을 읽기 위해 사용합니다.

스마트폰에서 BPM Characteristic에 새로운 값을 Write하면 Zephyr의 GATT 처리 과정에서 write_bpm() callback 함수가 호출됩니다.

예를 들어 nRF Connect에서 uint8 형식으로 60을 Write하면 다음과 같은 흐름으로 처리됩니다.

nRF Connect
    │
    │ BPM Characteristic
    │ Write : 60
    ▼
XIAO nRF54L15
    │
    ▼
write_bpm()
    │
    ▼
BPM = 60
    │
    ▼
Metronome 동작 주기 변경
반대로 스마트폰에서 BPM Characteristic을 Read하면 read_bpm() callback 함수가 호출되어 현재 설정된 BPM 값을 확인할 수 있습니다.

즉, Characteristic은 Bluetooth LE에서 데이터를 주고받는 통로이고, write_control()이나 write_bpm()과 같은 callback 함수는 그 통로를 통해 전달된 데이터를 실제 애플리케이션에서 처리하는 역할을 합니다.

동작 흐름

Bluetooth LE를 통해 전달받은 데이터는 실제 메트로놈 동작으로 연결됩니다. Control Characteristic으로 Start 명령을 받으면 메트로놈이 동작을 시작하고, Stop 명령을 받으면 동작을 정지합니다. BPM Characteristic으로 전달받은 값은 메트로놈의 점멸 주기를 결정하는 데 사용됩니다.

실제 박자 생성은 스마트폰에서 하는 것이 아니라 XIAO nRF54L15에서 수행합니다. 스마트폰은 BPM과 Start/Stop 명령만 Bluetooth LE를 통해 전달하고, nRF54L15가 설정된 BPM에 맞추어 외부 LED 조명을 점멸시킵니다.

전체적인 프로그램의 흐름은 다음과 같습니다.

Smartphone
     │
     │ Bluetooth LE
     │ Start / Stop / BPM
     ▼
   ble.c
     │
     ▼
metronome.c
     │
     │ BPM에 따른 박자 생성
     ▼
   led.c
     │
     ▼
외부 LED 조명
메트로놈의 타이밍 생성과 LED 제어는 각각 metronome.cled.c에서 담당합니다. 이번 글에서는 Bluetooth LE 구현에 초점을 맞추기 위해 메트로놈과 LED 제어 코드의 세부적인 내용은 설명하지 않겠습니다.

마찬가지로 GATT 구현 코드도 세부적인 내용까지 설명하지 않고 전체적인 구조와 데이터의 흐름을 중심으로 살펴보겠습니다. 전체 구현 코드는 글 마지막에서 프로젝트 소스로 제공하겠습니다.

이제 이렇게 구성한 GATT Service와 Characteristic이 실제로 동작하는지 Build 및 다운로드하고 스마트폰의 nRF Connect for Mobile을 이용하여 확인해 보겠습니다.

빌드 및 다운로드

Bluetooth LE와 메트로놈 프로그램 구성이 완료되었으므로 프로젝트를 빌드하고 XIAO nRF54L15에 다운로드합니다.

이번 프로젝트에서는 앞에서 만든 my_xiao_nrf54l15 커스텀 보드를 대상으로 빌드합니다.

프로젝트 폴더에서 다음과 같이 빌드합니다.

west build -b my_xiao_nrf54l15 -p always -- -DBOARD_ROOT=...

-b 옵션에는 사용할 보드 이름을 지정하고, BOARD_ROOT에는 앞에서 만든 커스텀 보드가 포함된 경로를 지정합니다. 실제 BOARD_ROOT 경로는 각자의 Zephyr 개발 환경에 맞게 설정하면 됩니다.

빌드가 정상적으로 완료되면 생성된 펌웨어를 XIAO nRF54L15에 다운로드합니다.

west flash
다운로드 후 프로그램이 실행되면 Bluetooth LE가 초기화되고 Advertising이 시작됩니다. 이제 XIAO nRF54L15는 BLE Peripheral로 동작하면서 스마트폰의 연결을 기다립니다.

다음으로 스마트폰에서 nRF Connect for Mobile을 실행하여 실제 Bluetooth LE 통신이 정상적으로 이루어지는지 확인해 보겠습니다.

nRF Connect for Mobile을 이용한 동작 확인

지금까지 XIAO nRF54L15에서 Bluetooth LE를 초기화하고 Advertising, GATT Service, Characteristic을 구성했습니다.

이제 스마트폰의 nRF Connect for Mobile을 이용하여 실제로 BLE 장치를 검색하고 연결한 후, 구현한 GATT 통신이 정상적으로 동작하는지 확인해 보겠습니다.

nRF Connect는 Nordic Semiconductor에서 제공하는 Bluetooth LE 개발 및 시험용 애플리케이션입니다. 별도의 스마트폰 프로그램을 만들지 않아도 BLE 장치를 검색하고 연결하여 Service와 Characteristic을 확인하고, Read, Write, Notify 등의 기능을 직접 시험할 수 있습니다.

nRF Connect for Mobile 공식 페이지

이번 프로젝트에서는 다음 순서로 확인합니다.

nRF Connect
     │
     ▼
BLE 장치 Scan
     │
     ▼
XIAO_NRF54L15_METRONOME 발견
     │
     ▼
Connect
     │
     ▼
GATT Service / Characteristic 확인
     │
     ├── Control Write
     │      ├── 0x00 : STOP
     │      ├── 0x01 : START
     │      └── 0x02 : STATUS REQUEST
     │
     ├── BPM Write
     │
     └── Notify 확인

장치 검색과 연결

먼저 XIAO nRF54L15에 프로그램을 다운로드하고 실행합니다.

프로그램이 정상적으로 시작되면 Bluetooth LE Advertising이 시작되고, XIAO nRF54L15는 주변의 스마트폰에서 검색 가능한 Peripheral 장치가 됩니다.

스마트폰에서 nRF Connect for Mobile을 실행하고 Scan을 시작합니다.

prj.conf에서 다음과 같이 설정했던 장치 이름을 검색합니다.

XIAO_NRF54L15_METRONOME

아래는 검색을 한 화면입니다.

장치가 검색되면 CONNECT를 선택하여 XIAO nRF54L15와 연결합니다.

정상적으로 연결되면 nRF Connect에서 XIAO nRF54L15가 제공하는 GATT Service와 Characteristic을 확인할 수 있습니다.

아래는 연결된 화면입니다.

연결이 완료되면 nRF Connect는 XIAO nRF54L15가 제공하는 GATT 정보를 읽어 화면에 표시합니다.

화면에는 Bluetooth에서 기본적으로 사용하는 Generic Attribute, Generic Access Service와 함께 이번 프로젝트에서 직접 만든 Unknown Service가 나타납니다.

여기서 Unknown이라고 표시되는 이유는 오류가 있기 때문이 아닙니다. 이번 프로젝트에서 직접 정의한 Custom UUID를 사용하고 있기 때문에 nRF Connect가 해당 Service와 Characteristic의 이름을 알지 못하기 때문입니다.

이번 프로젝트에서 만든 Custom Service를 펼쳐 보면 두 개의 Characteristic을 확인할 수 있습니다.

Unknown Service
(Metronome Service)
        │
        ├── Unknown Characteristic
        │      └── Control Characteristic
        │
        └── Unknown Characteristic
               └── BPM Characteristic

첫 번째 Characteristic은 Control Characteristic으로 Start, Stop, Status Request 명령을 전달하는 데 사용합니다.

두 번째 Characteristic은 BPM Characteristic으로 메트로놈의 BPM 값을 설정하거나 현재 값을 읽는 데 사용합니다.

nRF Connect 화면에는 각 Characteristic에서 사용할 수 있는 기능도 아이콘으로 표시됩니다. Control Characteristic은 Read, Write, Notify를 지원하고, BPM Characteristic은 Read와 Write를 지원하도록 구성되어 있습니다.

따라서 별도의 스마트폰 애플리케이션을 만들지 않은 상태에서도 nRF Connect를 이용하여 우리가 Zephyr에서 구현한 GATT Service와 Characteristic에 직접 접근하고 데이터를 주고받을 수 있습니다.

이제 먼저 Control Characteristic에 값을 Write하여 메트로놈을 Start/Stop시켜 보겠습니다.

메트로놈 동작

이제 nRF Connect를 이용해서 메트로놈을 동작 시켜보겠습니다.

Start/Stop

Control Characteristic의 오른쪽에 표시된 위로 된 화살표를 클릭하면 아래와 같은 화면이 나타납니다.

Control Characteristic의 Write 버튼을 선택하면 위와 같이 Write value 화면이 나타납니다.

이번 프로젝트에서는 Control 명령을 1 Byte 값으로 정의했습니다.

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

먼저 메트로놈을 시작하기 위해 0x01을 입력하고 SEND를 누릅니다.

Control Characteristic
        │
        │ Write : 0x01
        ▼
XIAO nRF54L15
        │
        ▼
     START
        │
        ▼
설정된 BPM에 따라
외부 LED 조명 점멸

0x01이 전달되면 XIAO nRF54L15의 write_control() callback 함수가 호출되고, Start 명령으로 처리되어 메트로놈이 동작하기 시작합니다.

이번에는 같은 방법으로 0x00을 Write합니다. 0x00은 Stop 명령이므로 메트로놈 동작이 정지하고 LED 점멸도 멈춥니다.

따라서 nRF Connect에서 Control Characteristic에 값을 직접 Write하는 것만으로 실제 XIAO nRF54L15의 메트로놈을 Start/Stop할 수 있음을 확인할 수 있습니다.

다음으로 BPM Characteristic에 값을 Write하여 LED의 점멸 속도를 변경해 보겠습니다.

메트로놈이 정상적으로 Start/Stop되는 것을 확인했으므로 이번에는 BPM 값을 변경해 보겠습니다.

BPM 변경

BPM 설정에는 두 번째 BPM Characteristic을 사용합니다. nRF Connect에서 BPM Characteristic의 Write 버튼을 선택하면 값을 입력할 수 있는 화면이 나타납니다.

BPM 값은 1 Byte의 uint8 형식으로 전달합니다. nRF Connect의 입력 형식을 UINT8로 선택하면 16진수로 변환할 필요 없이 BPM 값을 십진수로 직접 입력할 수 있습니다.

예를 들어 BPM을 90으로 설정하려면 다음과 같이 입력합니다.

Data type : UINT8
Value     : 90

값을 입력한 후 SEND를 누르면 BPM Characteristic을 통해 90이 XIAO nRF54L15로 전달됩니다.

nRF Connect
     │
     │ BPM Characteristic
     │ Write : 90
     ▼
XIAO nRF54L15
     │
     ▼
  write_bpm()
     │
     ▼
   BPM = 60
     │
     ▼
메트로놈 주기 변경
     │
     ▼
외부 LED 조명 점멸

XIAO nRF54L15에서는 BPM Characteristic으로 값이 전달되면 write_bpm() callback 함수가 호출되고 새로운 BPM 값이 메트로놈에 적용됩니다.

BPM을 90, 120 등으로 변경해 보면 외부 LED 조명의 점멸 속도가 그에 맞추어 변하는 것을 확인할 수 있습니다.

아래는 입력하는 화면입니다.

Notyfy를 이용한 상태 전달

Bluetooth LE에서는 스마트폰에서 장치로 데이터를 보내는 Write뿐만 아니라, 장치에서 스마트폰으로 데이터를 전달하는 Notify 기능도 사용할 수 있습니다.

이번 프로젝트에서는 Control Characteristic에 Notify 기능을 추가했습니다. 스마트폰에서 Notify를 활성화하면 XIAO nRF54L15가 메트로놈의 상태 변화를 스마트폰으로 전달할 수 있습니다.

또한 Control Characteristic에 0x02를 Write하면 현재 Control 상태와 BPM 값을 Notify로 전달하도록 구성했습니다.

nRF Connect
     │
     │ Write : 0x02
     ▼
XIAO nRF54L15
     │
     ├── Control 상태
     └── BPM 값
     │
     │ Notify
     ▼
nRF Connect
이를 통해 스마트폰에서 장치를 제어하는 것뿐만 아니라, 반대로 장치의 현재 상태를 스마트폰에서 확인할 수 있음을 확인했습니다.

Notify의 세부적인 구현 방법은 이번 글에서는 다루지 않고 전체 프로젝트 소스에서 확인할 수 있도록 하겠습니다.

프로젝트 소스 다운로드

이번 글에서 사용한 XIAO nRF54L15 + Zephyr Bluetooth LE Metronome 프로젝트의 전체 소스 코드를 다운로드할 수 있도록 제공합니다.

프로젝트에는 Bluetooth LE뿐만 아니라 실제 동작을 확인할 수 있도록 메트로놈과 외부 LED 제어 코드도 함께 포함되어 있습니다.

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

CMakeLists.txt
prj.conf

Bluetooth LE와 관련된 주요 구현은 ble.c, ble.h에서 확인할 수 있으며, 메트로놈의 BPM 처리와 LED 점멸은 각각 metronome.c, led.c에서 확인할 수 있습니다.

[전체 프로젝트 소스 다운로드]

다운로드한 프로젝트를 사용할 때는 자신의 Zephyr 개발 환경에 맞게 보드 설정과 BOARD_ROOT 경로를 확인한 후 빌드하면 됩니다.

결론

이번 글에서는 Seeed Studio XIAO nRF54L15와 Zephyr RTOS를 이용하여 Bluetooth LE를 구현해 보았습니다.

처음에는 Bluetooth LE의 기본 구조인 Central과 Peripheral, Advertising, GATT Service와 Characteristic의 관계를 살펴보았습니다. 이후 Zephyr에서 Custom GATT Service를 구성하고 Control과 BPM Characteristic을 이용하여 실제 메트로놈을 제어했습니다.

스마트폰에서는 nRF Connect for Mobile을 이용하여 별도의 애플리케이션을 만들지 않고도 XIAO nRF54L15를 검색하고 연결할 수 있었습니다. 또한 Characteristic에 Start/Stop 명령과 BPM 값을 직접 Write하여 외부 LED 조명의 동작이 변경되는 것도 확인했습니다.

Notify를 이용하면 반대 방향으로 XIAO nRF54L15의 상태를 스마트폰에 전달하는 것도 가능합니다.

이번 시험을 통해 다음과 같은 Bluetooth LE의 기본적인 통신 흐름을 실제 하드웨어에서 확인할 수 있었습니다.

Advertising → Connection → GATT → Write / Notify

무엇보다 Zephyr에서는 Bluetooth Stack이 이미 제공되기 때문에 Bluetooth 프로토콜 자체를 처음부터 구현하지 않고도 Service와 Characteristic을 구성하여 비교적 쉽게 BLE 장치를 만들 수 있다는 점을 확인할 수 있었습니다.

현재는 nRF Connect가 스마트폰과 XIAO nRF54L15 사이의 BLE Central 및 시험 도구 역할을 하고 있습니다.

다음 단계에서는 이 역할을 직접 만든 스마트폰 애플리케이션으로 대체해 볼 예정입니다. Flutter와 Dart를 이용하여 Tok Tok Metronome 전용 애플리케이션을 만들고, 스마트폰에서 BLE를 통해 BPM과 Start/Stop을 제어하는 과정으로 이어가겠습니다.

댓글 남기기