Seeed Studio XIAO nRF52840でBLEのサービスを生成する

組み込みエンジニア
本記事はプロモーションが含まれています。

こんにちは、ENGかぴです。

Seeed Studio XIAO nRF52840はBLE(Bluetooth Low Energy)を搭載しており、ArduinoBLEライブラリでBLE通信ができます。BLEサービスを実装し双方向通信を行う方法をまとめました。

本記事ではArduinoBLEライブラリで生成したBLEサービスに接続して3色LEDの点灯パターンを切り替えて動作確認を行っています。Seeed Studio XIAO nRF52840を以下ではnRF52840と表記します。

Seeeduino XIAO(Seeed Studio XIAO SAMD21)で動作確認したことについてリンクをまとめています。

Seeeduino XIAOで学べるソフト開発と標準ライブラリの使い方

Seeed Studio XIAOの開発環境を作る

Seeed Studio XIAOの開発環境はArduino環境で開発する場合はArduino IDEのインストールが必要です。Arduino IDEでSeeed Studioが公開しているボードマネージャーのURLを追加するとSeeed StudioのボードがArduino IDEでインストールできるようになります。下記記事にSeeed Studio XAIOの開発環境の作り方を説明しています。

Seeed Studio XIAO nRF52840の開発環境を作る

BLEを使用するにあたって追加するボードの選択について説明します。ボードマネージャーでSeeed nRF52 Boards mbed-enabled Boardsをインストールします。

ボードマネージャでSeeed nRF52 mbed-enabled Boardsを選択してインストール
ボードマネージャでSeeed nRF52 mbed-enabled Boardsを選択してインストール

Arduino IDEのツールからボードマネージャーを選択するとボードの検索画面に遷移します。

ボードマネージャで検索欄に「seeed」と入力するとSeeed nRF52 mbed-enabled Boardsが候補として表示されます。(URLを追加していないと表示されません)このボードマネージャをインストールします。インストール後はボードの選択を行います。

BLEで使用するArduinoBLEライブラリを使用する場合はSeeed nRF52 mbed-enabled Boardsをインストールする必要があります。

広告

ArduinoBLEライブラリを実装する

nRF52840でArduinoBLEライブラリを実装しのBLEの動作確認を行います。アドバタイジングパケットを実装してスマホなどデバイスに表示します。アドバタイジングパケットの生成方法は下記記事にまとめています。

Seeed Studio XIAO nRF52840のBLEでデバイスの存在を表示する

アドバタイジングパケットで表示したデバイスに接続(ペアリング)後の動作についての概念について必要な事項を中心に説明します。

ペリフェラルデバイスとセントラルデバイスの接続
ペリフェラルデバイスとセントラルデバイスの接続

BLE通信の大きな概念としてセントラルデバイスペリフェラルデバイスの定義があります。

セントラルデバイスはパソコンに加えてスマホやタブレットなどの高い処理とメモリを備えたデバイスです。

ペリフェラルデバイスはセントラルデバイスに接続できる小型で低電力のデバイスであり、BLE対応のイヤホンやFitBit Charge4(私が愛用しているスマートウォッチ)などです。IoT機器の多くがペリフェラルデバイスに分類されます。

アドバタイジングパケットによってペリフェラルデバイスの存在をセントラルデバイスに表示されます。接続するとアドバタイジングパケットが終了し、サービス、キャラクタリスティックの概念であるGATT(Generic ATTribute Profile)を使用して1対1の双方向通信を行います。

GATTの重要な概念はサーバーとクライアントの関係です。ペリフェラルデバイスはサービスとキャラクタリスティックを構成するGATTサーバーとして動作します。セントラルデバイスはGATTサーバーに要求を送信するGATTクライアントとして動作します。

GATTサーバーはGATTクライアントからの要求があるまで待機します。GATTクライアントから接続要求によってトランザクション(データの送受信)を開始します。

GATTのトランザクションはプロフィール、サービス、キャラクタリスティックによって構成されています。プロフィールはサービスの組み合わせたものです。サービスはデータの種別を区別するために使用され、1つ以上のキャラクタリスティックを持つことができます。

キャラクタリスティックはデータの読み書きに使用します。送受信するデータの種別(整数、テキスト形式)に応じて使い分けます。

サービスはUUIDで他のサービスと区別されます。16ビットUUIDはBlutooth認証したサービスに使用しますが、それ以外の任意のサービスは128ビットUUIDを使用します。キャラクタリスティックについても同様です。

スポンサーリンク

サービス(キャラクタリスティック)の生成

#include <ArduinoBLE.h>
//サービスのインスタンス化
BLEService ledService("ae7daac9-caf9-e3af-976b-4d3a0d6f94e3");
//キャラクタリスティックのインスタンス化
BLEByteCharacteristic switchCharacteristic("197f0bd9-de9c-6d0c-eaf4-09728681ee27", BLERead | BLEWrite);
BLEDevice central; //セントラルデバイスの接続管理

void setup() {

  if (!BLE.begin()) {
    //BLEの初期化ができない場合の処理
    while (1);
  }
  //アドバタイジングデータの生成
  BLE.setLocalName("LED3Control");
 //サービスの生成(アドバタイジングパケットに表示する場合)
  //BLE.setAdvertisedService(ledService);
  //キャラクタリスティックの登録
  ledService.addCharacteristic(switchCharacteristic);
  BLE.addService(ledService); //サービスを追加
  switchCharacteristic.writeValue(0); //キャラクタリスティックの初期化
  BLE.advertise(); //アドバタイジングパケットの送信
}

BLEを使用するためにArduinoBLEライブラリをインクルードします。BLEServiceクラスの変数としてledService()を宣言し、引数にカスタムのUUIDを指定してインスタンス化します。UUIDは下記リンクや検索で「UUID生成」と検索すると無料で作成することができます。

UUID生成|ランダムGUID生成 – 無料ツールサイト (doratool.com)

個人で使用する分にはUUIDは適当でも良いと思いますが、例では128ビットUUIDを生成してサービスとキャラクタリスティックを生成しています。

BLEByteCharacteristickクラスの変数としてswitchCharacteristic()を宣言し、引数にUUIDと操作の属性を指定します。第1引数はサービスと同様にUUIDを生成して指定します。第2引数はキャラクタリスティックの操作属性としてデータを書き込む場合はBLEWrite、読み込む場合はBLEReadを指定します。読み書き双方の操作の場合は例のように論理和で指定します。

BLEのアドバタイジングパケットと接続後のサービス(キャラクタリスティック)の設定を行います。

BLEのbegin()関数で初期化を行います。begin()関数による処理が正常であれば0が戻り値になるのでパケットの生成を行います。例ではBLEの初期化に失敗した場合無限ループとしています。

setLocalName()関数はアドバタイジングパケットのスキャン応答データのペイロードにデータを指定します。引数にセントラルデバイスに表示するデバイス名を指定しますが、最大で31バイト以内に収めて指定する必要があります。

setAdvertisedService()関数はアドバタイジングパケットでサービスの内容を表示する場合に使用します。引数に表示するサービスをインスタンス化した変数を指定します。例では表示しないようにコメントアウトしています。

ledService(BLEServiceクラス)のメンバー関数であるaddCharacteristic()関数で接続後のサービスで使用するキャラクタリスティックを登録します。引数にインスタンス化したキャラクタリスティックを指定します。

addService()関数で接続後のサービスを登録します。引数にインスタンス化したサービスを指定します。例ではledService(キャラクタリスティックであるswitchCharacteristicを含む)を指定しています。

キャラクタリスティックでデータを操作しますが、初期値を指定する場合はキャラクタリスティックのメンバー関数であるwriteValue()関数で0を書いて初期化しています。

advertise()関数をコールするとアドバタイジングパケットの送信をスタートし、セントラルデバイスにデバイスの存在を通知します。

広告

セントラルデバイスからの接続管理

ペリフェラルデバイスはサーバーとして動作し、セントラルデバイスからの接続要求があるまで待機します。接続と切断の動作についての例を説明します。

BLEDevice central;

void loop() {
 
  central = BLE.central(); //接続待ち
  if(central){
    if(central.connected()){
      //セントラルデバイスが接続中
      if (switchCharacteristic.written()){
        //キャラクタリスティックの値による処理
      }
    }
    else{
      //切断した場合の処理
    }
  }
}

セントラルデバイスの接続状態を管理するためBLEDeviceクラスのcentralをインスタンス化して使用します。BLEのcentral()関数でセントラルデバイスが接続されるのを待機します。セントラルデバイスが接続するとセントラルデバイスの情報が戻り値となるので、戻り値をcentralにセットして使用します。

接続後の通信は1対1の双方向通信になることや下のconnected()関数でも接続状態を確認できるためcentralでデバイスの情報がセットされたことの確認は未実装でも問題ありません。

central(BLEDeviceクラス)のメンバー関数のconnected()関数でセントラルデバイスが接続中であることを確認します。接続中であればキャラクタリスティックの値に応じた処理を行います。

スポンサーリンク

動作確認

スマホ(Android)のGoogle playでBLEのデバイス情報や接続を行うためのアプリをインストールして動作確認を行います。BLE用のアプリであるnRF Connectを使って接続しnRF52840のLEDを操作します。

nRF Connectのスキャナー結果
nRF Connectのスキャナー結果
LED3Controlに接続した結果
LED3Controlに接続した結果

nRF Connectでスキャンすると「LED3Control」が確認できます。「CONNECT」を押すとnRF52840に接続しセントラルデバイスのクライアント(CLIENT)として動作します。今回登録したサービスはUnknown Serviceでありサービスを生成する際に指定したUUIDになっています。

同様にキャラクタリスティックもUnknown Characteristicであり、キャラクタリスティックを生成した際に指定したUUIDになっています。プロパティ(Properties:)は属性として指定したREADとWRITEになっています。

キャラクタリスティック横の↓マークはクライアントから見た時にデータを引き込む向きでありデータの読み込みになります。↑マークはクライアントから見た時にデータを出すためデータの送信(書き込み)になります。↑マークを選択してLEDの点灯パターンを変更します。

データの送信(書き込み)
データの送信(書き込み)
データのWRITE及びREADの結果
データのWRITE及びREADの結果

データの送信(書き込み)はデータの型がBYTE ARRAYが初期で設定されていますが、UINT 8に変更します。変更後送信するデータを入力します。LEDの点灯のパターンは1~7(この範囲以外は消灯)で指定します。入力後「SEND」を押すとデータが送信されます。

データを送信するとUnknow Characteristic欄のValue:に値が表示されます。↑マークを押すとnRF52840からキャラクタリスティックの値を取得して表示します。

BLEでデータを書き込んだ場合のLEDの点灯の様子
BLEでデータを書き込んだ場合のLEDの点灯の様子

1を送信すると赤色で点灯し、2を送信すると緑色になることが確認できました。他にも3では黄色、4では青色、5では紫色、6では水色、7では薄紫(白に近い)色に点灯し他の値ではLEDが消灯するのが確認できました。

広告

ソースコード全体

以下のソースコードはコンパイルして動作確認をしております。コメントなど細かな部分で間違っていたりやライブラリの更新などにより動作しなくなったりする可能性があります。参考としてお使いいただければと思います。

#include <ArduinoBLE.h>

BLEService ledService("ae7daac9-caf9-e3af-976b-4d3a0d6f94e3"); // Bluetooth® Low Energy LED Service
// Bluetooth® Low Energy LED Switch Characteristic - custom 128-bit UUID, read and writable by central
BLEByteCharacteristic switchCharacteristic("197f0bd9-de9c-6d0c-eaf4-09728681ee27", BLERead | BLEWrite);
BLEDevice central;
bool notiflg = false;
bool notiflg_dis = false;

void ledSelect(bool r,bool g, bool b);

void setup() {
  Serial.begin(9600);

  pinMode( LED_RED, OUTPUT);
  pinMode( LED_GREEN, OUTPUT);
  pinMode( LED_BLUE, OUTPUT);

  digitalWrite(LED_RED,HIGH);
  digitalWrite(LED_GREEN,HIGH);
  digitalWrite( LED_BLUE,HIGH);

  if (!BLE.begin()) {
    Serial.println("starting Bluetooth® Low Energy module failed!");
    while (1);
  }

  BLE.setLocalName("LED3Control");
  //BLE.setAdvertisedService(ledService);
  ledService.addCharacteristic(switchCharacteristic);
  BLE.addService(ledService);
  switchCharacteristic.writeValue(0);
  BLE.advertise();
  Serial.println("BLE LED Peripheral");
}

void loop() {

  central = BLE.central();

  if (central){
    if(central.connected()){
      if(notiflg == false){
        notiflg = true;
        notiflg_dis = false;
        Serial.print("Connected to central: ");
        Serial.println(central.address());
      }

      if (switchCharacteristic.written()){
        switch (switchCharacteristic.value()){
          case 1:
            ledSelect(true,false,false);
            break;
          case 2:
            ledSelect(false,true,false);
            break;
          case 3:
            ledSelect(true,true,false);
            break;  
          case 4:
            ledSelect(false,false,true);
            break;
          case 5:
            ledSelect(true,false,true);
            break;
          case 6:
            ledSelect(false,true,true);
            break;
          case 7:
            ledSelect(true,true,true);
            break;                          
          default:
            ledSelect(false,false,false);
            break;
        }
      }
    }
    else{
      if(notiflg_dis == false){
        notiflg_dis = true;
        notiflg = false;        
        Serial.print(F("Disconnected from central: "));
        Serial.println(central.address());
      }
    }
  }
}

/*LEDの選択*/
void ledSelect(bool r,bool g, bool b){

  if( r ){
    digitalWrite(LED_RED,LOW);
  }
  else{
    digitalWrite(LED_RED,HIGH);
  }

  if( g ){
    digitalWrite(LED_GREEN,LOW);
  }
  else{
    digitalWrite(LED_GREEN,HIGH);
  }

  if( b ){
    digitalWrite(LED_BLUE,LOW);
  }
  else{
    digitalWrite(LED_BLUE,HIGH);
  }
}

関連リンク

Arduinoのライブラリを使って動作確認を行ったことを下記リンクにまとめています。

Arduinoで学べるマイコンのソフト開発と標準ライブラリの使い方

Seeeduino XIAOで学べるソフト開発と標準ライブラリの使い方

ESP32-WROOM-32Eで学べるソフト開発と標準ライブラリの使い方

PR:【CreatorsFactory】転職率96%!Webスクール説明会申し込み

最後まで、読んでいただきありがとうございました。

タイトルとURLをコピーしました