Shaun Gruenig(記)、Matthew Bon(2016年10月7日校正)、Scott Raeker(2021年3月x日改定)
2021年3月9日の更新情報:Silicon Labsのコメント「Bluegiga Legacy Bluetooth Low Energy(BLE)モジュールは、2011年の発売以来、市場で非常に高い評価を得ており、現在も大量に生産されています。しかし、新しいBluetooth 5の設計には、当社のEFR32BG SoCをベースとした最新のBGM13 BluetoothモジュールとBGX13 Bluetoothモジュールをお勧めします。」
目的
このページでは、Silicon Labs BLE112 Bluetooth Low EnergyモジュールとマイクロコントローラがUARTで通信できるように設定する方法を説明します。
クイックリンク
購入リンク
- Silicon Labs BLE112 Bluetooth Low Energy Module
- Texas Instruments CC-Debugger - BLE112のファームウェアの更新に使用します。
- Silicon Labs BLE112 Development Kit - 評価用として有用です。ピンを手動でブレークアウトすることなくBLE112を使用でき、その他多くのオンボードデバイスを搭載しています。
参考資料
- BLE112 Documentation and Software - このチュートリアルで使用するSDKやBGLib APIのリファレンスガイドなど、BLE112モジュールを使用する際に役立つ多くのリソースが含まれています。
- Silicon Lab’s Forum search for BLE112 for Bluetooth Smart - BLE112の使用開始と使用方法に関する有用なスレッドが多数あります。
- BLE112 Datasheet
ダウンロード
- BLE112 Breakout Board Layout - BLE112用に作成したブレークアウトボードを作製するために必要なファイルが入ったzipファイルです。
はじめに
(BlueGiga BLE112の製品情報ページより引用)BLE112は、低消費電力のセンサやアクセサリをターゲットにしたBluetooth Smartモジュールです。Bluetoothラジオ、ソフトウェアスタック、GATTベースのプロファイルなど、Bluetooth Smartアプリケーションに必要なすべての機能を統合しています。 Bluetooth Smartモジュールはまた、エンドユーザーアプリケーションをホストすることができるため、サイズや価格に制約のあるデバイスにおいて、外部マイクロコントローラが不要になります。さらに、さまざまな周辺機器やセンサに接続できる柔軟なハードウェアインターフェースを備え、標準的な3Vコイン電池や単4乾電池2本から直接電源を供給することが可能です。最も消費電力の少ないスリープモードでは500nAを消費するだけで、数百マイクロ秒でスリープモードが解除されます。
このチュートリアルでは、BGAPI(BLE112にプリロードされている)とBGLib(マイクロコントローラなどの外部デバイスを介してBLE112とインターフェースするために使用するBlueGigaが提供するCライブラリ)を使用して、マイクロコントローラとBLE112モジュールの間の通信を確立する方法について説明します。BGLibには、BLE112とのインターフェースに必要なコードのほとんどが含まれています。マイクロコントローラでライブラリを動作させるには、マイクロコントローラのUARTやUSBペリフェラルを使ってBLE112と通信する方法をBGLibに伝える送信関数と受信関数を記述すればよいだけです。今回は、マイクロコントローラのUARTペリフェラルを使ってモジュールと通信することを中心に説明します。BLE112とBGLibがどのように動作するかの背景を知るために、あるいはBLE112やBGLibに関するさらなる情報が必要な場合はチュートリアルのどの時点であっても、上記リンクの「参考資料」を確認することをお勧めします。一部の資料は、BlueGiga社のホームページで無料アカウントを取得する必要がありますが、BLE112の使い方を理解する上で非常に有用な資料となっています。
チュートリアル
ここでは、初めてBLE112を起動させる方法をわかりやすく解説していきたいと思います。
ハードウェアインターフェースを設定する
ピンをブレークアウトする
BLE112との通信を開始するために、まず、ワイヤとブレッドボードを使って接続できるように、ピンをブレークアウトする必要があります。そのために、モジュールをはんだ付けするための基板を作りました。このような設備をお持ちでない方は、コンタクトにワイヤをハンダ付けすることも可能です(どのピンを使用するかは、下の配線図を見てください)。Bluegiga BLE112 Development Kitでは、BLE112モジュールのピンへのアクセスも可能です。ブレークアウトボードを自作する場合は、データシートの17ページにあるランドパターンを参考にすることができます。これは、下図に示すブレークアウトボードを作成する際に使用したフットプリントと同じものです。
このボードを製作するためのファイルは、上記クイックリンクのセクションで見つけることができます。
ファームウェアを更新する
UARTで通信するようにプログラムされたピンは、BLE112のファームウェアに依存するため、ピンのブレークアウトができたら、デバイスのファームウェアを更新したいと思います。BLE112モジュールの多く(すべてではありません)には、BlueGigaの「UARTDemo」のサンプル用のファームウェアがプリロードされています。BGLibのサンプルとCコードは、こちら:Software ->Bluetooth Low Energy Software and SDKでご覧いただけます。デバイスのファームウェアをアップデートするためには、おそらくTIのCC-Debuggerを使用する必要があります。このデバイスは、BlueGigaのBleUpdateプログラム(前述のSDKに含まれています)と併用すると、ファームウェアのアップデートが非常に簡単に行えるようになります。このチュートリアルでは、「UARTDemo」の例と同じピン設定を使用します。それはこのハードウェアレイアウト(hardware.xml ファイルに含まれます)は、BLE112 との通信に UART を使用するほとんどのプロジェクトで機能するためです。参考までに、「UARTDemo」の例に含まれる hardware.xml ファイルを以下に示します。
’UARTDemo’ hardware.xml
<?xml version="1.0" encoding="UTF-8" ?>
<hardware>
<sleeposc enable="true" ppm="30" />
<usb enable="false" endpoint="none" />
<txpower power="15" bias="5" />
<usart channel="1" alternate="1" baud="57600" endpoint="api" />
<wakeup_pin enable="true" port="0" pin="0" />
<port index="0" tristatemask="0" pull="down" />
<pmux regulator_pin="7" />
</hardware>
上のxmlを見るとわかるように、UARTのチャンネル1がイネーブル(具体的にはチャンネル1、オルタネイト1)になっていることがわかります。データシート(下のスクリーンショット)を見てください。
これにより、P0_4がTxとして設定され、P0_5がRxとして設定されていることがわかります。BLE112のデフォルトの通信モードでは、フロー制御がオンになっており、RT(RTS - ready to send)およびCT(CTS - clear to send)ピンがそのために使用されます。残念ながら、多くのUARTペリフェラルにはフロー制御がありません。ありがたいことに、BLE112には「パケットモード」というものがあり、フロー制御を無効にし、代わりにBLE112モジュールに送信するパケットのサイズを指定する必要があります。このチュートリアルではパケットモードを使用しますので、以下のように hardware.xml ファイル、具体的には「usart channel」で始まる行を修正します。
hardware.xmlの修正
<usart channel="1" alternate="1" baud="57600" endpoint="api" mode="packet" flow="false" />
これはフロー制御をオフにし、パケットモードに設定します。さて、データを送る関数を定義するとき、まずパケットに含まれるバイト数を送らなければなりません(BGLibはこれをきわめて簡単にしてくれます。チュートリアルのソフトウェア項目でより詳しく説明します)。この修正したhardware.xmlをBLE112に送信するために、CC-debuggerとBleUpdateを使用する必要があります。
モジュールを配線する
ファームウェアの更新が完了したら、マイクロコントローラのUART RxピンとBLE112のUART Txピン、マイクロコントローラのTxピンとBLE112のRxピンを接続します。UART通信では、アイドル時にデータラインをハイに保つ必要があるので、各ラインにも10Kのプルアップ抵抗を追加します(下図参照)。データシートを読むと、リセットピンはアクティブローであることがわかります。このやり方でデバイスをリセットできるようにしたい場合は、マイクロコントローラのGPIOピンを使ってこのピンを制御することができます。BLE112はソフトウェアコマンドでリセットすることもできるので、このチュートリアルでは、リセットピンを電源に接続するだけにします。hardware.xmlファイルでは、P0_0ピンがウェイクアップピンとして設定されていることも確認できます。BLE112と通信している間にスリープにならないように、このピンを電源に接続します。電流リークを避けるために、特定のピンをグランドまたは3.3Vに接続する必要があるかもしれませんが、簡易化するために、このチュートリアルではこれを行いません(より詳しい情報はデータシートに記載されています)。完全なピン接続図を以下に示します。
ソフトウェアインターフェースを設定する
BGLibをUARTに接続する
ソフトウェアインターフェースの設定を開始するには、まず、BGLib Cライブラリ(SDKに含まれます)をプロジェクトコードにインクルードする必要があります。必要なファイルは「commands.c」、「cmd_def.c」、「cmd_def.h」、「apitypes.h」の4つです。BLE112と通信するのに必要なもの(ソフトウェアに関するもの)がほぼすべて入っています。しかし、これらのライブラリは、UARTやUSBインターフェースを持つあらゆるマイクロコントローラで動作するように設定されているため、特定のマイクロコントローラのUARTペリフェラルと通信する方法をライブラリに指示する必要があります。2つの関数が必要です。1つはUART経由でデータを送信する方法をライブラリに伝える関数、もう1つはUART(またはUSB、ただしこのチュートリアルではUART経由の通信に焦点を当てます)経由でデータを読み取る方法をライブラリに伝える関数です。
送信関数
送信/出力関数については、関数ポインタが存在し、自分の書いた送信関数を指す必要があります。「cmd_def.c」に次のように定義されています。
送信関数ポインタ
void (*bglib_output)(uint8 len1,uint8* data1,uint16 len2,uint8* data2) = 0;
したがって、同じ引数と戻り値の型を持つように、送信関数を定義する必要があります。以下は、この関数がどのようなものであるかの基本的な擬似コードです。
送信関数の擬似コード
void output (uint8 len1, uint8* data1, uint16 len2, uint8* data2)
//since we are using packet mode, we need to begin every packet with the size of the packet
send character(len1 + len2)
//send bluetooth message header
for i = 0 to len1
send character(data1[i])
//send bluetooth message payload
for i = 0 to len2
send character(data2[i])
ここでは、TI Tiva TM4C123GH6PMマイクロコントローラ用に書かれた送信関数の例を示します。この関数は、UART 5ペリフェラルを介してbluetoothモジュールと通信することができます。
UART送信関数の例
//this is the callback function which tells the BLE API how to
//send messages via the 123G's UART peripheral library. The messages
//are sent over UART 5
void SendBTMessage(uint8 len1,uint8* data1,uint16 len2,uint8* data2)
{
//this line assumes the BLE module is in packet mode, meaning the
//length of the packet must be specified immediately before sending
//the packet; this line does that
UARTCharPut(UART5_BASE, len1 + len2);
//this loop sends the header of the BLE message
for(int i = 0; i < len1; i++)
{
UARTCharPut(UART5_BASE, data1[i]);
}
//this loop sends the payload of the BLE message
for(int i = 0; i < len2; i++)
{
UARTCharPut(UART5_BASE, data2[i]);
}
//wait until UART is finished sending before continuing
while(UARTBusy(UART5_BASE));
}
出力関数を書いたら、「cmd_def.h」にある関数ポインタが自分の関数を指すようにする必要があります。上の例を使うと、C言語のコードは次のようになります。
関数ポインタのポインティング
bglib_output = &SendBTMessage;
これでBGLibは、マイクロコントローラのUARTペリフェラル経由でbluetoothモジュールにデータを送信できます。
読み取り関数
次に、UART経由でBLE112からデータを読み込む方法を指示する必要があります。この関数は、UARTでデータを受信するたびに呼び出されるため、関数ポインタはありません。その代わり、データを受信するたびに起動する割り込みを設定し、割り込みハンドラとして受信/入力/読み取り関数を使用するのが一般的でしょう。この関数は送信関数よりも少し複雑です。これは、BGLibによって定義された構造を使用して、BGLibが提供する機能を利用するためです。以下は、使用する必要がある構造です(これは「cmd_def.h」にあります)。
BGLibメッセージとヘッダ構造
typedef void (*ble_cmd_handler)(const void*);
struct ble_header
{
uint8 type_hilen;
uint8 lolen;
uint8 cls;
uint8 command;
};
struct ble_msg
{
struct ble_header hdr;
uint32 params;
ble_cmd_handler handler;
};
ご覧のように、「ble_header」構造体は4つの項目から構成されています。メッセージのタイプ(コマンド、応答、イベント)を含むtype_hilen(ペイロード長のMSBも含まれますが、BLEメッセージではペイロードが255バイトを超えることはないので気にする必要はありません)、メッセージのペイロード長/サイズ(具体的には8つのLSB)を含むlolen、メッセージのクラスコード(属性クライアント、接続、システム等)を含むcls、そしてメッセージのID番号を含むcommandの4項目です。type_hilen、cls、commandの各バイトは、BLE112との間で送受信可能な各メッセージを一意に識別するために使用されます。lolenバイトは、ペイロードの大きさを知るために使用され、メッセージヘッダを読んだ後に、そのバイト数だけ読み取ります。「ble_msg」構造体は、ble_header構造体、ble_cmd_handler(ble_header で指定されたメッセージの種類を処理するために、 BGLibに適切な関数を指す)、そして4バイトのparams(ble_mgs構造体に含まれるメッセージをBGLibで解析するために使用)から構成されています。これらの構造体を使用して格納されるさまざまなメッセージとメッセージヘッダについて理解を深めるには、Bluetooth Smart Software API Referenceのマニュアルを参照してください。
読み取り関数を記述する前に知っておくべきもう1つの項目は「ble_get_msg_hdr(struct ble_header hdr)」 関数です。この関数は単に、bluetoothモジュールから受け取ったヘッダに対応する特定のble_msgを返します(名前と少し矛盾しています)。「cmd_def.h」では、BGLibが個々のメッセージごとにble_msg構造体を持っていることがわかります。ble_get_msg_hdrは一般の「ble_msg」と特定のメッセージ構造体を交換するだけです。
コード内でこれらの独自構造体を使用しなければならないため、受信関数の一般的な疑似コードを記述することは困難です。代わりに、TM4C123GH6PMを使用した例を示します。123GのUART受信関数を自身のMCUのUART関数に置き換えるのは、かなり簡単だと思います。
受信関数の例
//this is the Bluetooth interrupt handler; it reads any messages
//sent by the bluetooth LE module over UART
void ReadBTMessage( void )
{
const struct ble_msg *BTMessage; //holds BLE message
struct ble_header BTHeader; //holds header of message
unsigned char data[256] = "\0";//holds payload of message
//clear the UART 5 interrupt flag
UARTIntClear(UART5_BASE, UART_INT_RX);
//read BLE message header
BTHeader.type_hilen = UARTCharGet(UART5_BASE);
BTHeader.lolen = UARTCharGet(UART5_BASE);
BTHeader.cls = UARTCharGet(UART5_BASE);
BTHeader.command = UARTCharGet(UART5_BASE);
//wait for UART to finish reading header to ensure data
//is valid before using it in the code below
while(UARTBusy(UART5_BASE));
//read the payload of the BLE message
for(uint8_t i = 0; i < BTHeader.lolen; i++)
data[i] = UARTCharGet(UART5_BASE);
//find the appropriate message based on the header, which allows
//the ble112 library to call the appropriate handler
BTMessage = ble_get_msg_hdr(BTHeader);
//print error if the header doesn't match any known message header
if(!BTMessage)
{
//handle error here
return;
}
//call the handler for the received message, passing in the received payload data
BTMessage->handler(data);
}
コマンドを送信し、レスポンスとイベントを受信する
送信関数と受信関数を設定し、bglib_output関数ポインタは送信関数を指し、受信関数を起動するための割り込みを設定したら、BLE112と通信するコードの完成です。すべてが正常に機能していれば、BLE112との通信は、「cmd_def.h」の最下部付近にあるマクロ(「#define ble_cmd…」で始まる行です)を呼び出すだけで簡単にできるはずです。これらの関数を呼び出せば、残りの送信処理はBGLibがやってくれます。データを受信したら、受信した特定のメッセージのハンドラを呼び出す必要があります(上記のサンプルコードでは、「BTMessage->handler(data)」という行でこれを実行します)。
ハンドラが呼ばれたときに何か意味のあることが起こるようにするには、「commands.c」にリストされているハンドラ関数に適切なコードを追加する必要があります。ハンドラは、レスポンス(ble_rsp…で始まるもの)とイベント(ble_evt…で始まるもの)に分かれています。BLE112からのレスポンスは、コマンドを受信したことを確認するためのものであり、コマンドがモジュールによって正常に実行されたかどうかを知るためのエラー番号も含まれます。イベントはBLE112 から送信され、コマンドを実行したこと、または別の Bluetoothデバイスからデータを受信したことを知らせます。こちらのAPIリファレンスガイドに、想定されるすべてのイベントとレスポンス、それに対応するBGLib Cライブラリのハンドラ関数、およびそれらのレスポンスとイベントに含まれる情報のリストがあります。BGLibハンドラ関数の使用例を以下に紹介します(BTFlags構造体は、私が定義した構造体で、BLE112の現在の状態を把握するための各種フラグを単純に格納したものです)。
イベントハンドラの例
//set ready flag once BLE112 is ready for commands
//this event will occur after sending the ble_cmd_system_reset command
void ble_evt_system_boot(const struct ble_msg_system_boot_evt_t *msg)
{
BTFlags.ready = 1;
}
//set connected flag once a connection has been established to a remote device
void ble_evt_connection_status(const struct ble_msg_connection_status_evt_t *msg)
{
UARTprintf("\n\rconnection established");
BTFlags.connected = 1;
}
//this event occurs after the ble_cmd_attclient_find_information command is sent
//it assumes that information was obtained from a remote device
void ble_evt_attclient_find_information_found(const struct ble_msg_attclient_find_information_found_evt_t *msg)
{
UARTprintf("\n\rHandle: %02x\n\rUUID: ", msg->chrhandle);
uint8_t length = msg->uuid.len;
for(int i = 0; i < length; i++)
UARTprintf("%02x", msg->uuid.data[length - i - 1]);
}
次はどうする?
1つのモジュールで通信できるようになったので、2つのモジュールで通信できるようにしてみましょう。2台のデバイス間で通信を行う場合、一方の機器をスレーブ/サーバ、もう一方の機器をマスター/クライアントとして設定することになります。スレーブは通常、何らかの周辺機器やデータ収集装置に接続され、その周辺機器からのデータをメモリに格納します。マスターデバイスは、スレーブからこのデータを取得したり、特定のデータをサブスクライブしたりすることができます。マスターがあるデータ(別名は「プロファイル」の「特性」)をサブスクライブしている場合、スレーブはそのデータが更新されるたびに自動的にマスターに送信します。アプリケーション固有のデータを保存するためには、独自のGATTプロファイルを作成し、それをスレーブに置く必要があります(BlueGigaの記事でこれらのプロファイルとその作成方法について説明しています)。