こんにちは、ENGかぴです。
Seeed Studio XIAO nRF52840はBLE(Bluetooth Low Energy)を搭載しており、ArduinoBLEライブラリでBLE通信ができます。デバイスに存在を認識させるアドバタイジングパケットを実装する方法をまとめました。
本記事ではアドバタイジングパケットに任意のデータを追加して動作確認を行っています。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をインストールします。
Arduino IDEのツールからボードマネージャーを選択するとボードの検索画面に遷移します。
ボードマネージャで検索欄に「seeed」と入力するとSeeed nRF52 mbed-enabled Boardsが候補として表示されます。(URLを追加していないと表示されません)このボードマネージャをインストールします。インストール後はボードの選択を行います。
ArduinoBLEライブラリを実装する
nRF52840でArduinoBLEライブラリを実装しのBLEの動作確認を行います。アドバタイジングパケットを実装してスマホなどデバイスに表示します。アドバタイジングパケットについては下記のリンクが参考になります。
Advertising(解説) – BLE Docs (google.com)
本記事ではBLEの通信仕様や概念についてArduinoBLEライブラリを使用する際に必要な事項を中心に説明します。
アドバタイジングパケットやデバイスの接続はGAP(Generic Access Profile)で定義されている概念になります。GAPはnRF52840のデバイス情報をスマホなど外部のデバイスで確認できるようにするものです。
GAPではデバイスの役割を定義していますが、大きな概念としてセントラルデバイスとペリフェラルデバイスの定義があります。
セントラルデバイスはパソコンに加えてスマホやタブレットなどの高い処理とメモリを備えたデバイスです。
ペリフェラルデバイスはセントラルデバイスに接続できる小型で低電力のデバイスであり、BLE対応のイヤホンやFitBit Charge4(私が愛用しているスマートウォッチ)などです。IoT機器の多くがペリフェラルデバイスに分類されます。
アドバタイジングパケットはアドバタイジングデータペイロード(Advertising Data payload)とセカンダリペイロードであるスキャン応答ペイロード(Scan Response payload)によって構成されています。どちらのペイロードも最大で31バイトのデータを含めることができます。
アドバタイジングパケットはペリフェラルデバイスからセントラルデバイスに向けてデータを一方向に送るためブロードキャスト通信となります。セントラルデバイスAとセントラルデバイスBにおいてBLEデバイスをスキャンすると同じアドバタイジングパケットを確認することができます。
セントラルデバイスとペリフェラルデバイスが接続完了するとGAPプロセスが完了しアドバタイジングパケットは送信できなくなります。接続後は1対1通信をサポートするGATTによる概念で通信を行います。
PR:わからないを放置せず、あなたにあった最低限のスキルを身に着けるコツを教える テックジム 「書けるが先で、理解が後」を体験しよう!
アドバタイジングパケットを生成
#include <ArduinoBLE.h>
//アドバタイジングパケットのデータをインスタンス化
BLEAdvertisingData advData;
const uint8_t manufactData[4] = {0x01, 0x02, 0x03, 0x04};
void setup() {
if (!BLE.begin()) {
//BLEの初期化ができない場合の処理
while (1);
}
//スキャン応答データの生成
advData.setLocalName("TestAdv");//スキャン時に表示する文字を設定
//アドバタイジングデータの生成
advData.setManufacturerData(0xFFFF, manufactData, sizeof(manufactData));
advData.setAdvertisedServiceData(0xfff0, serviceData, sizeof(serviceData));
BLE.setAdvertisingData(advData); //アドバタイジングパケットを登録
BLE.advertise(); //アドバタイジングパケットの送信
}
BLEを使用するためにArduinoBLEライブラリをインクルードします。BLEAdvertisingDataクラスの変数としてadvDataをインスタンス化し、アドバタイジングデータペイロードとスキャン応答ペイロードにデータを付加してアドバタイジングパケットを生成します。
BLELocalDeviceクラスのBLEで初期化とadvDataで設定したデータを反映してアドバタイジングパケットの送信を行います。
BLEのbegin()関数で初期化を行います。begin()関数による処理が正常であれば0が戻り値になるのでパケットの生成を行います。例ではBLEの初期化に失敗した場合無限ループとしています。
アドバタイジングパケットのモードを変更する場合はBLEAdvertisingDataクラスのsetFlags()関数の引数に値を指定します。初期値の0x06から変更しない場合は使用する必要はありません。初期値はLE General Discoverable ModeビットとBR/EDR Not Suportビットをセットする設定です。セントラルデバイスに対して常に通知し、Blutooth Classicとの互換性がない設定になります。
setLocalName()関数はスキャン応答データのペイロードであり最大で31バイト分のデータを付与することができます。引数にセントラルデバイスに表示するデバイス名を指定します。
setManufacturerData()関数で製造メーカと付加するデータを指定します。第1引数に製造メーカ番号を指定します。0xFFFFを指定するとNo companyになります。0xFFFF以外ではBlutooth認証しているメーカの番号になります。第2引数にメーカ名に付加するデータのアドレスを指定します。第3引数にデータの数を指定します。例ではNo companyのデータにmanufactData[]のデータを追加しています。
setAdvertisedServiceData()関数でサービスに使用するUUIDと付加するデータを指定します。第1引数にUUIDを16ビットの値で指定します。第2引数にUUIDの後方に付与するデータのアドレスを指定します。第3引数にデータ数を指定します。アドバタイジングデータペイロードの最大データ数の31バイトを超えないように調整しながら指定します。
BLEのsetAdvertisingData()関数の引数にadvDataを指定することで設定した内容を登録します。advertise()関数をコールするとアドバタイジングパケットの送信をスタートします。
PR:スキマ時間を有効に!スマホで学べる人気のオンライン資格講座【スタディング】まずは気になる講座を無料で体験しよう!
ブロードキャスト通信の例
アドバタイジングパケットはデバイスの接続を行わない場合、ブロードキャスト通信を継続します。任意のタイミングでパケットの内容を変更し、リスタートすることでセントラルデバイスに任意のデータを送信することができます。
void loop() {
if( timDataUp == TIME_UP){
timDataUp = UP_DATA_MAX;//1秒毎にデータを更新する
upData[0] = cnt++;
upData[1] = cnt++;
upData[2] = cnt++;
upData[3] = cnt++;
advData.setAdvertisedServiceData(0xfff0, upData, sizeof(upData));
BLE.setAdvertisingData(advData);
BLE.advertise();
}
}
アドバタイジングパケットのサービスUUIDに付加するデータを1秒毎に変更しながらブロードキャスト通信を行う例を説明します。
upData[]は1秒毎に値を更新する配列とします。上記のアドバタイジングパケットを生成で説明したsetAdvertisedServiceData()関数の第2引数と第3引数にupData[]のアドレスとサイズを指定します。
BLEのsetAdvertisingData()関数で変更後のadvDataを指定することでアドバタイジングパケットを更新し、advertise()関数で変更後のアドバタイジングパケットを送信します。
動作確認
スマホ(Android)のGoogle playでBLEのデバイス情報や接続を行うためのアプリをインストールして動作確認を行います。BLE用のアプリであるnRF Connectを使ってアドバタイジングパケットを確認しました。
nRF Connectでスキャンすると「TestAdv」が確認できます。Manufacturer data:No Company<0xFFFF>で、「0x01020304」が今回付加したデータです。Service Data:はUUIDが0xFFF0で付加したデータで1秒毎に更新されます。動作確認では0x1C1D1E1Fになっていますが、1秒毎に値が変更されることを確認しています。Complete Local Name:はTestAdvが表示されています。
RAWを選択するとパケットの情報が表示されます。RAWデータはアドバタイジングパケットを最上位ビットから並べたものです。最初にパケットのタイプのFlagからメーカー情報、サービス情報、ローカル名の順に表示されます。サービス名の0xFFF0はLSBから送信されるのでF0FFの順になっています。
MOREを選択するとBLEデバイスの電波強度や受信したパケットの情報がHISTORYに表示されます。FLAGS&SERVICESではパケットのモードとして初期値の0x06が表示されています。UUIDは先頭の16ビットに0000FFF0が表示されています。
ソースコード全体
以下のソースコードはコンパイルして動作確認をしております。コメントなど細かな部分で間違っていたりやライブラリの更新などにより動作しなくなったりする可能性があります。参考としてお使いいただければと思います。
#include <ArduinoBLE.h>
#define TIME_UP 0
#define TIME_OFF -1
#define BASE_CNT 10 //10msがベースタイマとなる
#define UP_DATA_MAX 100
uint32_t beforetimCnt = millis();
int8_t cnt10ms;
int16_t timDataUp = UP_DATA_MAX;
BLEAdvertisingData advData;
uint8_t cnt;
const uint8_t manufactData[4] = {0x01, 0x02, 0x03, 0x04};
const uint8_t serviceData[4] = {0x11, 0x22, 0x33,0x55};
uint8_t upData[4];
void mainTimer(void);
void setup() {
Serial.begin(9600);
while (!Serial);
if (!BLE.begin()) {
Serial.println("failed to initialize BLE!");
while (1);
}
//スキャン応答ペイロードのデバイス名称
advData.setLocalName("TestAdv");
// Set parameters for advertising packet
//advData.setFlags(0x09); //アドバタイジングパケットをモード初期値0x06
advData.setManufacturerData(0xFFFF, manufactData, sizeof(manufactData));
advData.setAdvertisedServiceData(0xfff0, serviceData, sizeof(serviceData));
BLE.setAdvertisingData(advData);
BLE.advertise();
Serial.println("advertising ...");
}
void loop() {
mainTimer();
if( timDataUp == TIME_UP){
timDataUp = UP_DATA_MAX;
upData[0] = cnt++;
upData[1] = cnt++;
upData[2] = cnt++;
upData[3] = cnt++;
advData.setAdvertisedServiceData(0xfff0, upData, sizeof(upData));
BLE.setAdvertisingData(advData);
BLE.advertise();
}
}
void mainTimer(void){
if ( millis() - beforetimCnt >= BASE_CNT ){
beforetimCnt = millis();
if(timDataUp > TIME_UP){
--timDataUp;
}
}
}
関連リンク
Arduinoのライブラリを使って動作確認を行ったことを下記リンクにまとめています。
Arduinoで学べるマイコンのソフト開発と標準ライブラリの使い方
Seeeduino XIAOで学べるソフト開発と標準ライブラリの使い方
ESP32-WROOM-32Eで学べるソフト開発と標準ライブラリの使い方
広告
マイベスト3年連続1位を獲得した実績を持つ実践型のプログラミングスクール
最後まで、読んでいただきありがとうございました。
udData[]に温度湿度センサーの情報を付加するとスキャン時に温湿度の情報が取得できるIoTセンサーになります。