masato-ka's diary

日々思ったこととか、やったことの備忘録。

RN4020でI2Cの値を読み取る

この記事について

この記事ではRN4020のI2Cを利用する方法について紹介します。RN4020にはI2Cポートが付いています。EEPROMなどを接続しデータを一時的にためておくために利用できます。もちろん他のマイコンと同じようにI2Cポートをもったセンサを接続することができます。そこで、RN4020のI2Cポートに温湿度センサを取り付けて動作させてみました。

RN4020のI2C仕様について

 RN4020は21Pinと22PinがI2Cのポートとなっています。データシート上では21PinがSDA, 22PinがSCAになっていますが、実際には22PinがSDA, 21PinがSCAと逆になっています。(2017/10/16現在)秋月電子のボードも他のブレークアウトボードもデータシートにならって軒並み逆に記載されています。そのため基板の印字と回路図を比較する必要があります。おそらく逆につければ大丈夫です。I2Cバスのプルアップ抵抗はRN4020についていないません。そのため、センサモジュールについていなければ別途つける必要があります。ロジックレベルは3.3Vです。  また、RN4020のI2Cはファームウェアのバージョンが1.20以降で利用できます。現在販売されているものは1.23が多いようです。(最新はバージョン1.3)念のため、シリアルからVコマンドを使ってバージョンを確認しておきましょう。  RN4020はI2Cのマスタとして動作します。スレーブ動作は設定できません。また、EEPROM の読み書きを実行するコマンドがありますが、それ以外は指定データをシリアルバスにそのまま流す低レベルなAPIしかありません。

I2Cのコマンド

RN4020でI2Cを利用するためには以下の5つのコマンドを覚えれば大丈夫です。以下の5つのコマンドの組み合わせでI2Cのスレーブにアクセスします。

コマンド 用途
]A I2Cポートの初期化
]C I2Cのイベント制御
]Z I2Cポートの開放
]W データの書き込み
]R データの取得

I2Cコマンド利用方法

 今回はHDC1000と呼ばれるI2Cインタフェースの温湿度センサから温度の値を読み取る方法を例にとり、コマンドの使い方を説明します。 このセンサは前まで秋月電子通商で手に入りましたが最近は廃盤になったようです。センサのアドレスは7bitアドレスで1000000を持っています。上記のコマンドを使ってHDC1000から温度データを取得するには以下のように実行します。

]A,1,2 //(1)
]C,0 //(2)
]W,80020000 // (3)
]W,8000 //(4)
]C,1 //(5)
]W,81//(6)
]R,02 //(7)
6541 //16進数表記
  • (1) I2Cの初期化

最初の引数1はI2Cのクロックスピードです。100kHzを指定します。4を指定すると400kHzになります。また2番目の引数はGPIOを1,2,3,4,7で指定します。指定したGPIOは出力モードとなり、I2Cのスタート時にHighになります。スレーブの電源として利用することで、普段は電源を切っておくことができます。今回はGPIOを使わずに実験のため3.3Vの電源に直接接続しています。

  • (2) I2Cのスタート

]Cの引数に0コマンドをつけ、I2Cのスタートビットをバスに流します。この時、I2Cの初期化で指定したGPIOがHighになります。

  • (3) HDC1000の初期化

HDC1000はデータの取得方法など個別に細かく設定することができます。起動時に0x02のレジスタに値を書き込みます。今回は0x0000を書き込みます。詳細は以下のデータシートを参考にしてください。

]Wコマンドは指定されたバイト列をMSB側からバスに出力するコマンドです。データにバスを流すためのコマンドですので必ずしもスレーブへのWrite要求を出すコマンドではありません。そのため、I2Cの先頭バイトには7bitのスレーズ指定のアドレスと1byteのRead(1)/Wrie(0)を指定する必要があります。今回、HDC1000は2進数7bit表記で1000000がアドレスになります。最後にWriteを表す0をつけることで書き込みを表します。その後、書き込み先レジスタアドレス0x02と書き込みデータ0x0000を付与します。  実際にこの時の出力をロジックアナライザで確認すると以下の図のようになっています。

f:id:masato-ka:20171017075459p:plain

もしデバイスアドレスが8bit表記であれば左へ1bitシフトしたものにRead/Writeのbitを付け足します。(MSB側は必ず0になっているはず。。。)

引き続き]Wコマンドでレジスタ0x00へアクセスします。HDC1000ではこのジレスタにアクセスすることで、センサないのA/Dが温度の値を変換することになっています。Write要求ですが特にデータは書き込まず問題ありません。同じようにいかに信号の状態を出力しておきます。

  • (5) I2Cリセット

温度を読み取るまでのRN4020側の書き込みは完了したため、一度I2Cのバスを解放します。

(4)から(5)までのロジックアナライザ上での動作になります。

f:id:masato-ka:20171017075506p:plain

  • (6) リード要求

RN4020からリード要求を出します。この場合もバスに信号を流すため]Wコマンドを利用します。0x81はアドレス1000000のLSB側にRead要求の1を足したものになります。 I2CバスにREAD要求のコマンドが出力されます。

  • (7) データの読み取り

I2Cのデータの読み取りはリードコマンド]Rを使います。引数は受信するバイト数でここでは2バイトを表します。HDC1000の場合結果は4桁の16進数で帰ってきます。これを指定された式で計算して温度値を読み取ります。

(5)から(7)までのロジックアナライザ上での動きは以下の通りです。前述の説明と照らし合わせてみてください。

f:id:masato-ka:20171017075453p:plain

以上でRN4020のI2Cの値の読み取りになります。]Wと]Rの使い方に気をつければ他のデバイスやアクセス方法でも問題なく利用できると思います。ロジックアナライザの出力とコマンドを照らし合わせると出力がどうなるか理解できるかと思います。

I2Cスクリプトサンプル

I2Cはもちろんスクリプトモードからも利用できます。上記の例を参考に温度データを一定間隔でNotifyするスクリプトです。キャラクタリスティックには2byteでRead Notifyが指定されています。スクリプトとプライベートサービスの詳細は過去記事を御覧ください。

masato-ka.hatenablog.com

@PW_ON
A

@CONN
]A,1,2
]C,0
]W,80020000
]W,8000
]C,1 
SM,1,00002710

@TMR1
]W,81
$VAR1=]R,02
SHW,000B,$VAR1
SM,2,00002710

@TMR2
]W,8000
]C,1
SM,1,00002710

@DISCON
]Z
A

上記例では温度の変換命令''']W,8000```と読み取り命令を別のタイマーイベントとし、相互に繰り返すようになっています。(お互いのタイマーイベントを指定している。)これはHDC1000の温度変換の結果がくるのを少し待たないといけないためです。(ハードウェア割り込みもありますが今回は不使用です。)

まとめ

今回はRN4020でI2Cを利用してみました。I2Cもスタンドアローンで利用できるので、I2Cのセンサをつないでお手軽に利用することができます。簡単なセンサーデバイスはすぐに作れそうです。注意点としてはデータシート状のI2CのピンSDAとSCLが逆になっている点、]W,]Rのそれぞれの使い方です。ここさえ押さえればすぐに利用できます。データシートは何年もほったらかしのようですが、いつか修正されるのでしょうか。。。

Web Bluetooth APIと自作BLEデバイスで戸締り確認をしてみる。

この記事について

 この記事ではWeb Bluetooth API戸締り確認アプリを紹介します。デバイス側はRN4020を使って実装しました。Web Bluetooth APIとRN4020を使ってBLEのアプリを作ってみたかったので、製作しました。実用性はありませんが、それぞれの使い方を覚えるのには大変良さそうな題材です。

戸締り確認アプリ

戸締り確認アプリは大まかに以下の仕様になっています。

  1. BLE経由で鍵の開閉を確認することができる。
  2. 鍵の状態が変わったらBLE経由で通知が来る。
  3. 鍵が開場した場合にブザーが鳴る。

バイスの紹介

今回RN4020で作るBLEデバイスにはホール素子とブザーが付いています。ホール素子は磁石が近づくと信号を出力するセンサです。自転車の速度計などに利用されています。また、ブザーについてはRN4020のデジタル出力で直接駆動できないため、トランジスタで増幅しています。抵抗値などはかなり適当です。

f:id:masato-ka:20171001123639j:plain

実体配線図っぽいのは以下の通りです。

f:id:masato-ka:20171001132146p:plain

RN4020のサービスとキャラクタリスティックは以下のように設定しました。一つのサービスに開閉のデータを送るキャラクタリスティックとブザーを制御するためのキャラクタリスティックの2つを設定しています。また開閉のキャラクタリスティックについてはReadとNotifyの2つを設定しています。後ほどスクリプトでRN4020のIOポートとつなげるため、データ長は1バイトにしています。(単純な動作なので)

  • サービス
    • UUID 123456789012345678901234567890FF
    • 鍵開閉のキャラクタリスティック
      • UUID 12345678901234567890123456789011
      • Mode Read,Notify
      • Length 1byte(0x02の場合ロック,0x00の場合アンロック)
    • ブザーのキャラクタリスティック
      • UUID 12345678901234567890123456789022
      • Mode Write(応答なし)
      • Length 1byte(0x04の場合に発音,0x00の場合に消音)

実際のコマンドは以下のように設定します。

PS,123456789012345678901234567890FF
PC12345678901234567890123456789011,12,01
PC12345678901234567890123456789022,04,01

実際に指定したスクリプトは以下のスクリプトを設定しました。

@PW_ON
|O,04,00
|O,04,%000E
A

@CONN
SM,1,000F4240

@TMR1
$VAR1 = |I,02
SHW,000B,$VAR1
SM,1,000F4240

@DISCON
A

スクリプトやその他設定の方法については以下を参照してください。

masato-ka.hatenablog.com

Webブライザ側のアプリケーションについて

Webブラウザ側のアプリケーションについては以下のUIを作りました。AugularJSとを利用して作成しています。

f:id:masato-ka:20171001120601p:plain

connectを押すとBLEの接続要求が実行されます。またreadやSubscribe/Unsubscribeでデバイス側のホール素子の値を取得します。

ソースコード全体については以下のリポジトリに置いてあります。

github.com

アプリの実行

アプリの実行時の動作を動画にしてみました。ブザーの音がするので音量は控えめにしてください。

www.youtube.com

バイスへの接続ご、サブスクライブ(デバイスからのNotifyをスタート)を行い、画面上のLock Unlock表示が変わります。また、Alertをセットすることで、Unlock時にブザーが鳴るようになります。

まとめ

Web Bluetooth API とRN4020を使うことで簡単にBLEを使ったアプリケーションを作ることができました。今回は半日程度で両方実装できたので非常にお手軽だったと思います。BLEで何かしようと思った場合、スマフォなどのアプリは作れるけど、そもそも取り扱えるBLEデバイスの種類がないので面白いことができないといった場合にはRN4020でサクッとデバイスを作ってしまうのもいいかもしれません。また、自作のBLEデバイスを作ってみたけれど、コントロールするスマフォアプリを作れない、市販デバイスを気軽にハックしたいという場合はWeb Bluetooth APIを利用するとサクッと見栄えのいいアプリが作れると思います。コンセプトを試したいときなどアイディアの試作に利用できそうです。

今回の作例を使ったワークショップを2017年10月20日に開催予定です。どなたでもご参加いただけます。

connpass.com

BLEモジュールRN4020をスクリプトから制御する。

この記事について

 この記事では前回に引き続き、RN4020を制御する方法について記載しています。前回まではシリアル通信から制御をおこなっていました。しかし、今回はRN4020のスクリプト機能を使って、スタンドアローンで制御する方法を紹介します。RN4020については以下の記事を参照してください。

masato-ka.hatenablog.com

下記写真のようにスマフォのアプリからBLE経由でLEDの制御が可能です。作業時間10分程度でここまでのことが実現できます。

f:id:masato-ka:20170927225702j:plain

RN4020のスクリプト制御

 RN4020ではシリアル制御のコマンドをRN4020上に記憶させることで外部からの制御なしにスタンドアローンで動作させることが可能となります。スクリプトの制御では予め設定されたイベント(電源ONやBLEの接続イベント、タイマーイベントなど)ごとに処理を記載してBLEデバイスとしての振る舞いを設定します。また、I/Oポートの値をキャラクタリスティックに紐づけることができます。

RN4020の初期設定

 まずはじめにRN4020をスクリプト制御する前BLEの動作モードやプライベートサービスの設定を行います。設定については前回の記事で紹介していますので参考にしてください。今回はキャラクタリスティックを1つ設定し、書き込みモードとしました。

+
- Echo On
SF,1 
-AOK
SS,00000001 
-AOK
SR,00000000 
-AOK
PZ 
-AOK
PS,123456789012345678901234567890FF
-AOK
PC,12345678901234567890123456789011,04,01
-AOK
R,1
-Reboot
-CMD

スクリプトの書き込み準備

スクリプトの書き込みを行うためには以下のコマンドを入力します。

WC //スクリプトの初期化を行う。
-AOK
WW // スクリプト書き込みモードにする。
-AOK

これで、シリアルコンソールからスクリプトを書き込む準備ができました。

スクリプトの作成

 今回は上記で設定したキャラクタリスティックに値を書き込むとLEDが光るようなサービスを指定します。以下のスクリプトを1行ずつ入力してください。

@PW_ON//(1)
|0,01,%000A//(2)
A

@CONN//(3)
SM,1,00500000//(4)

@TMR1//(5)
SM,1,00500000//(6)

@DISCON//(7)
A

上記スクリプトの入力が完了したら<ESC>キーを押してください。スクリプト入力モードから抜け出せます。

スクリプトの動作について説明します。

  • (1) 電源が入った場合に呼ばれるイベントで初期化の処理を記載します。ここではAコマンドでアドバタイズを実行しています。
  • (2) PIO1に000A のハンドラを持ったキャラクタリスティックを関連づけています。これによりキャラクタリスティックに書き込みに従ってPIO1を制御できます。
  • (3) セントラルから接続された場合に呼ばれるイベントです。
  • (4) SMコマンドでタイマーをセットしています。タイマー1を5秒ごに実行するように設定しています。
  • (5) タイマー1のイベントが発生した場合に呼び出されます。
  • (6) 再びタイマーをセットし、タイマー1イベントが繰り返し呼ばれるようにします。今回はタイマーループはからの処理ですので飛ばしても構いません。
  • (7) 切断後再度接続ができるようにアドバタイズを再開する。

上ではキャラクタリスティックに値を書き込むことでデバイスの制御をしていますが、もちろん、I/Oの値をキャラクタリスティックにわたしデータを外部に送信することも可能になっています。

例えば@TMR1イベント中で以下のように記載することでAIO0の値をキャラクタリスティックに書き込めます。 これはノティフィケーションの通知のような場合にできる記載方法です。

$VAR1 = @I,0
SHW,000B,$VAR1

また初期化時に以下のように記載すると000Eのハンドラを持つキャラクタリスティックをReadした場合にAIO2の値を読み出すことができます。

%000E = @I,2
  • スクリプトで利用できるイベントは以下のようになっています。
イベント ベントラベル
電源 ON @PW_ON
Timer1 タイムアウト @TMR1
Timer2 タイムアウト @TMR2
Timer3 タイムアウト @TMR3
接続完了 @CONN
切断完了 @DISCON
PIO4( ピン 13) の入力が Low へ変化 @PIOL
PIO4( ピン 13) の入力が High へ変化 @PIOH
高優先度アラート @ALERTH
低優先度アラート @ALERTL
アラート OFF @ALERTO

スクリプトの実行

スクリプトを実行する場合は以下のようにRN4020の動作モードをスクリプトモードにしてから再起動します。

SR,01000000 //ペリフェラルかつスクリプト実行モードにする。
R,1 

スクリプトの消去について

スクリプトモードにして再起動すると他の操作を受け付けません。再起動コマンドと初期化コマンドは受け付けるようになっているためもしスクリプトを消去したい場合は以下のコマンドを実行して初期化してください。

SF,1
R,1

2017/09/28追記:深夜の勢いで上のように書きましたが普通に上で紹介してるWCコマンド使えば消去できると思います。

実行の様子

実際に上記の動作の様子を動画にしました。撮影が下手なので分かりづらいですが、スマートフォン側からRN4020に接続し、設定したキャラクタリスティックの値を変更することでLEDが点灯または消灯する様子がわかると思います。00を書き込んだタイミングでLEDが点灯し、それ以外の値を書き込んだタイミングでLEDが消灯します。


RN4020のスクリプト動作

まとめ

 前回まではシリアルから制御していたため、必ずホストとなるコンピュータが必要になりました。しかしスクリプトを利用することでRN4020単体でもデバイスの動作を実現できることがわかりました。制御文などが十分ではないため動作としてはあまりリッチな動きができそうにありませんが、非常に単純な機能であれば使いどころはありそうです。

BLEモジュールRN4020のIOポートをシリアルから制御する。

この記事について

 この記事ではBLEモジュールRN4020のIOポートを制御する方法について紹介します。RN4020については前回の記事をみてください。

masato-ka.hatenablog.com

RN4020のI/Oポート

 RN4020のI/OポートはアナログI/O(AIO)とデジタルI/O(PIO)の2種類のポートがあります。それぞれシリアルインタフェースから制御するためのコマンドが用意されており、RN4020にスイッチやLED、アナログ出力のセンサを取り付けて値を読み出すことが可能です。

アナログI/Oについて

   RN4020のアナログI/0はAIO0からAIO2までの3ポートあります。入出力電圧のレンジは0Vから1.3Vと低電圧となっています。1mV単位の指定ができ値のレンジは(0x0000〜0x0514)となります。

デジタルI/Oについて

 デジタルI/OはPIO1,PIO2,PIO3,PIO7の4ポートあります。欠番になっているピンも存在しているのですが、基本的にシリアルのフロー制御など他の用途に使われているため使用しません。もちろんI/Oとしても利用することは可能だと思いますが、ユーザガイド上に方法が記載されていません。

ピンアサイ

 モジュール自体のピンアサインはユーザガイドをみてください。ここでは秋月電子通商のブレークアウトボードでのピンアサインを紹介します。以下図の通りのピンアサインになっています。紹介したPIO7についてはこちらでは出ていません。従ってデジタルI/OはPIO1からPIO3までの3ピンのみ利用可能です。またPIO1はボード上のLED制御ピンと兼用になっています。

f:id:masato-ka:20170926001343p:plain

シリアルからのI/O制御

I/Oの制御方法はアナログとシリアルでそれぞれ変わります。それぞれの制御コマンドについて説明していきます。

アナログI/Oの制御

  • アウトプットコマンド

アナログアウトプットのコマンドは@Oです。以下のように使います。

@O,2,0014

最初の引数がポート番号を指し、今回はAIO2を指定しています。さらに0014は出力値を16進数で表しています。20mVがAIO2に出力されるはずです。他のピンについても同様に指定できます。

  • インプットコマンド

アナログインプットは@Iです。以下のように使います。

@I,2
- 0000

最初の引数はアウトプットと同じくポートを表しています。結果としてAIO2のポートの電圧値(ここでは0)が結果として帰ってきます。

デジタルI/Oの制御

  • デジタルアウトプットコマンド

デジタルアウトプットのコマンドは|Oです。以下のように使います。

|O,07,05

最初の引数はピン番号の制御ですが、16進数で指定しています。ここではPIO1,PIO2,PIO3を指定しています。2つ目の引数は出力指定です。こちらも16進数指定でPIO1,PIO3をHighに指定し、PIO2をLowに指定しています。ブレークアウトボードの場合、ボード上のLEDが消灯するはずです。(Highで消灯、Lowで点灯)各PINとレジスタの関係は以下の表のようになっています。複数指定する場合は足し算してください。

ピン番号 レジスタ
PIO1 0x01
PIO2 0x02
PIO3 0x04
PIO7 0x08
  • デジタルインプット

デジタルインプットは同様に上記のレジスタの表に従って以下のように指定します。

|I,01,
-01

これはPIO0の値を読み取り結果が同じくレジスタの形式で帰ってきます。(-はRN4020からの応答を表す)PIO1の値が1になっています。(LEDからの入力電圧があるため)

まとめ

 以上のようにRN4020はシリアルからI/Oを制御することが可能です。しかし単純なI/O制御だけならばシリアル接続側のマイコンで行っても良いと思います。RN4020のスクリプト機能を使うとアナログI/O,デジタルI/Oともに変数に代入したり、キャラクタリスティックのハンドラにバインドすることができます。こちらについてもまとめ次第記事にしたいと思います。

1000円から始めるBLEデバイス開発、RN4020をPCから制御して使ってみる。

この記事について

 この記事では1000円代で購入出来るBLEモジュールRN4020の使い方を検証してみました。まずはシリアル通信で制御しペリフェラルとして動作させます。さらにセントラルと値をやり取りする方法についても紹介しています。

RN4020とは

RN4020はMicrochip社が製造しているBLEモジュールです。価格は1000円程度で、秋月電子通商などで購入できます。

f:id:masato-ka:20170925200254j:plain

akizukidenshi.com

また、使いやすくしたブレークアウトボード版も販売されています。(製造も秋月電子通商)今回はこちらのブレークアウトボードを利用しています。

akizukidenshi.com

RN4020はUARTインタフェースが搭載されており、シリアル通信からコマンドを入力することで、簡単にBLEデバイス(ペリフェラル動作)を実現することができます。Arduinoなどのマイコンボードなどに接続しBLEデバイスを作成することができます。また、セントラルとしても動作させることができるため、汎用的なBLEの通信モジュールとして利用することが可能です。さらにUARTインタフェースだけでなく、アナログI/O, デジタルI/Oを兼ね備えており、LEDやセンサを接続して利用することも可能です。  RN4020は専用のスクリプトを書き込むことで、BLEとI/Oを制御し、RN4020のみでBLEデバイスを構成することができます。詳細な仕様については以下のMicrosoftのデータシートを参考にしてください。また、シリアルコマンドはスクリプトのリファレンスはユーザガイドに記載されています。

  • データシート

http://ww1.microchip.com/downloads/jp/DeviceDoc/50002279A_JP.pdf

  • ユーザガイド

http://akizukidenshi.com/download/ds/microchip/70005191A_JP.pdf

RN4020をシリアル通信で制御する

 まずはじめにRN4020をUARTで制御します。今回はUSBシリアル変換を使い、PC上から制御します。マイコンなどを接続する場合も基本的に同じコマンドとなります。

接続回路

 RN4020ですが、前述のブレークアウトボードを利用する場合、シルクパターンに従って、GND, +5V, RX, TXを接続するのみです。ブレークアウトボードの場合電源が+5Vである点に注意してください。今回はUSBシリアル変換は秋月電子通商から発売されている以下のモジュールを利用しました。

akizukidenshi.com

実は上記ブレークアウトボードとこのUSBシリアル変換機のピンアサインが向かい合わせでぴったりとはまるようになっています。写真のように向かい合わせでブレットボード上に刺すとGND、電源、がお互い接続され、RXとTXはクロスに接続されるようになっています。

f:id:masato-ka:20170925200517j:plain

ですが、実際の配線は製造ロットによって変わる場合もあるので、予め確認して接続するようにしてください。接続はそれぞれのモジュールが以下の表のように対応していれば大丈夫です。お互いのRXとTXをクロスに接続することろがポイントです。

USBシリアル変換 RN4020
GND GND
+5V +5V
RX TX
TX RX

PCでの接続

 回路の接続ができたら、USBシリアル変換機とPCをUSBケーブルで接続します。WindowsであればTeraTermなどのターミナルソフトから以下のパラメータでシリアル接続します。

項目
ボーレート 115200
データビット 8bit
パリティ なし
ストップビット 1bit
フロー制御 なし
改行文字 CR

OSXの場合は以下のコマンドで接続します。

$screen /dev/tty.usbserial-A1032M5W 115200 

/dev/tty.usbserial-A1032M5Wは接続するUSBシリアル変換機ごとに変わるので予め/dev/フォルダ以下を見てそれっぽいものを確認します。

ペリフェラルとしてBLEのHeartRateサービスをアドバタイズしてみる。

ターミナルの接続が完了したら以下のようにコマンドを1行ずつ実行します。RN4020がHeartRateサービスを持ったペリフェラルとしてアドバタイズを開始するはずです。 先頭に「-」が付いている行はRN4020からの応答行です。実際には「-」は表示されていません。

+//(1)
- Echo On
SF,1//(2)
- AOK
SS,A0000000//(3)
- AOK
SR,00000000//(4)
- AOK
R,1//(5)
- Reboot
- CMD
A//(6)
  • (1) EchoをOnにしてターミナルに入力した値がエコーバックで表示されるようにする。
  • (2) デバイスの初期化を行う(デバイスの基本情報はそのまま)
  • (3) HeartRateとDeviceInformationのサービスを設定
  • (4) デバイスモードをペリフェラルに設定
  • (5) ハードウェアを再起動して設定を再読み込み
  • (6) アドバタイズ開始

 実際にBLEデバイスをLightblueなどのBLEデバイスのユーティリティアプリで見るとRN4020が見えるようになっています。  引き続き、RN4020がどういう状態になっているのか詳細を見てみましょう。まずはシリアルコンソールからLSコマンドを入力して、現在設定されているサービスとキャラクタリスクを確認してみます。そうすると以下のように返答が帰ってきます。

LS
180A
         2A25,000B,V
         2A27,000D,V
         2A26,000F,V
         2A28,0011,V
         2A29,0013,V
         2A24,0015,V
180D
         2A37,0018,V
         2A37,0019,C
         2A38,001B,V
         2A39,001D,V
END

 先頭の180Aは承認サービスとして予め定義されているDeviceInformation (180A)です。そこから一段下がってDeviceInformationのキャラクタリスティックが表示されています。また180DはHeartRateサービスとなっています。このようにSSコマンドを利用することで、承認サービスを簡単に設定することができます。

 続いてDコマンドを入力し、デバイスの詳細情報を出力します。

D
- BTA=001EC04633AB
- Name=RN33AB
- Connected=no
- Bonded=no
- Server Service=A0000000
- Features=00000000
- TxPower=4

これらの情報も別のコマンドから書き換えることができ、デバイス名やBLEとしての役割の変更(セントラル、ペリフェラルなど)を変更することが可能です。詳細はユーザマニュアルを確認してください。

キャラクタリスティクへの値の書き込み

キャラクタリスティックに値を書き込んで、接続しているセントラルにデータを送信します。今回はセントラル側としてOSX上でLightblueを使います。まず今の状態のまま、LightblueからRN4020にアクセスすると、先ほどLSコマンドで見た通りのサービスとキャラクタリスティックを確認できます。キャラクタリスティックの値はまた表示されていません。

f:id:masato-ka:20170925205434p:plain

以下のコマンドでキャラクタリスティックに値を書き込みます。2A37は心拍数の値を返すコマンドのようです。予め、Lightblue上でサブスクライブしておきます。

SUW,2A37,50

上記のコマンドはキャラクタリスティック2A37に対して0x50(80)を設定する値となります。 またはハンドラに対する書き込みだと以下のようになり結果は同じになります。

SHW,0018,50

Lightblueを見ると値が書き込まれているのがわかります。このようにキャラクタリスティックに値を書き込むとサブスクライブしているセントラルに値を通知し、またReadが許可されている場合はReadしたセントラルに値を送信します。

f:id:masato-ka:20170925205443p:plain

キャラクタリスティクからの値の読み取り

続いて、セントラルから書き込まれた値を読み出してみます。同じくHeartRateサービスの2A39が書き込み可能なキャラクタリスティックになっています。lightblueから値を書き込むと以下のようにコンソールに表示されます。

-WV,001D,50.

これは001Dのハンドラ(UUIDじゃないことに注意)に0x50が書き込まれたという表示です。

ユーザ定義のBLEサービスとキャラクタリスティックを設定する。

RN4020はDeviceInforamtionやHeartRateサービスのように承認済みサービスだけでなく、ユーザが独自に定義したサービスとキャラクタリスティックを設定することができます。以下のコマンドを実行することでユーザ定義のサービスとキャラクタリスティックを設定できます。

+
- Echo On
SF,1
- AOK
SS,00000001
- AOK
PZ //(1)
- AOK
PS,11223344556677889900AABBCCDDEEFF //(2)
- AOK
PC,010203040506070809000A0B0C0D0E0F,02,05//(3)
- AOK
R,1
- Reboot
- CMD
  • (1) ユーザ定義のサービスとキャラクタリスティックを削除
  • (2) 指定のUUIDでサービスを設定
  • (3) 指定のUUIDでキャラクタリスィックをRead Only, 5byte長として設定する。

PCコマンドでキャラクタリスティックの設定をしますが、キャラクタリスティックに許可する操作(Read, Write, Notifyなど)を2つ目の引数で設定することができます。ユーザマニュアルを読むと細かく設定できるようです。よく使いそうな値は以下の表の通りかと思います。

設定名
Notify 0x10
Write(応答あり) 0x08
Write(応答なし) 0x04
Read 0x02

例えばReadとNotifyを設定する場合は上記の例だと以下のようになります。

PC,010203040506070809000A0B0C0D0E0F,12,05

キャラクタリスティックの値の読み書きについては先ほどと同じ方法で行えます。

まとめ

 RN4020を利用することで非常に簡単にBLEのペリフェラルバイスを作成できます。Arduinoなどを使って簡単な工作ができればRN4020を接続するだけでBLE機器として利用できそうです。今回はシリアル通信からの制御を中心に紹介しました。RN4020をマイコン代わりに使い、センサやLEDを直接接続して動かすこともできます。また簡単なスクリプトを利用し、他のマイコンなどと接続せずに単独のデバイスとして動かすこともできます。これらの利用方法についてもまとめ次第紹介したいと思います。

WebブラウザからBLE接続 WEB Bluetooth APIでNotificationを受け取る方法

この記事について

 この記事ではWebBluetooth API を使い、BLEデバイスからNotificationを受け取る方法を説明します。 対象とするデバイス2JCIE-BL01を利用します。デバイスから一定間隔で環境データを取得します。

WEB Bluetooth API

 WEB Bluetooth APIはWEBブラウザ上でBLEデバイスと通信を実現するJavaScriptAPIです。実装は各ブラウザごとに 違いますが2017年9月現在、各ブラウザともそんなに違いはないそうです。iOSについては今の所非対応のようですが、OSXでは問題なく使えそうです。

WEB Bluetooth APIの実装

WEB Bluetooth API を使いデバイスからNotificationを受ける実装は非常に簡単です。以下のコードで実現できます。onStartNotifyをブラウザのUIから呼び出すことで、実行することが可能です。今回ブラウザはChromeを利用しました。

var SensorServiceUUID = "0c4c3000-7700-46f4-aa96-d5e974e32a54" // WxBeacon2のSensorService UUID
var LatestDataUUID = "0c4c3001-7700-46f4-aa96-d5e974e32a54"// 最新データ取得のためのキャラクタリスティック UUID

function onStartNotify() {

    navigator.bluetooth.requestDevice(
        { acceptAllDevices:true,optionalServices:[SensorServiceUUID] } // (1)
     ) 
        .then(device => device.gatt.connect())//(2)
        .then(server => server.getPrimaryService(SensorServiceUUID))
        .then(service => service.getCharacteristic(LatestDataUUID))
        .then(characteristic =>{
            characteristic.addEventListener('characteristicvaluechanged', onRecvSensorData); //(3)
            characteristic.startNotifications();//(4)
        })

}

  • コード解説

(1) requestDeviceでBLEデバイスの検索を開始します。引数には探索するデバイスのフィルタを指定できます。今回はデバイスの指定は行わず、すべてのデバイスを受信します。また、デフォルトでは任意のサービスに接続できないため、予め接続できるサービスのUUIDをoptionalServicesで指定しておきます。

このメソッドは実行されると以下のようなダイアログがWeb ブラウザ上に表示されます。接続したいデバイスを選択し、ペア設定を押します。

f:id:masato-ka:20170924144104p:plain

(2) (1)で指定した デバイスに対して接続処理を行います。接続後、サービスの検索、キャラクタリスティックの検索と処理が 続いていきます。

(3) キャラクタリスティックを取得後、データがNotifyされた際に呼ばれるイベントリスナcharacteristicvaluechangedにコールバックを指定します。BLEデバイスからデータが通知されたらonRecvSensorDataメソッドが呼ばれるようになります。

(4) startNotifications()メソッドでデバイスからの通知が開始されます。

WxBeacon2のデータの処理を行うonRecvSensorDataは以下のようになっています。

function onRecvSensorData(event) {//(1)

    let characteristic = event.target; //(2)
    let value = characteristic.value; //(3)

    let temp = decodeValue(value.getUint8(2), value.getUint8(1), 0.01);
    console.log(temp);
    let humid = decodeValue(value.getUint8(4),value.getUint8(3), 0.01);
    console.log(humid);
    let lumix = decodeValue(value.getUint8(6), value.getUint8(5));
    console.log(lumix);
    let uv = decodeValue(value.getUint8(8), value.getUint8(7), 0.01);
    console.log(uv);
    let atom = decodeValue(value.getUint8(10), value.getUint8(9), 0.1);
    console.log(atom);
    let noise = decodeValue(value.getUint8(12), value.getUint8(11), 0.01);
    console.log(noise);
    let disco = decodeValue(value.getUint8(14), value.getUint8(13), 0.01);
    console.log(disco);
    let heat = decodeValue(value.getUint8(16), value.getUint8(15), 0.01);
    console.log(heat);
    let battery = decodeValue(value.getUint8(18), value.getUint8(17), 0.001);
    console.log(battery);

}

function decodeValue(msb, lsb, gain){
        return ((msb << 8) + lsb) * gain
}

  • コード解説

(1) コールバックはデバイスからの通知結果を受け取るeventと言う引数を1つとります。 (2) eventからキャラクタリスティックのオブジェクトを取得します。 (3) キャラクタリスティックのvalueの値を取得します。

最終的に取得されるキャラクタリスティックの値はDataViewオブジェクトになっています。getUint8メソッドを利用することで1バイトづつデータを取得することができます。 WxBeacon2の設定を変更していなければ5分間隔でデータが出力されます。今回はコンソールログに出力しているため、開発者ツールなどから確認します。

まとめ

WEB Bluetooth APIは非常に簡単にデバイスへの接続とキャラクタリスティックに対するデータの読み書きができます。今回はNotificationの受け取りのみですが、もちろん通常のreadやwriteも可能です。プラットフォームに依存なく簡単にBLEデバイスにアクセスできます。また、WEBブラウザから実行できるという性格上、検索できるデバイスやデバイスのサービスをホワイトリスト形式で縛る、実行にはかならずユーザアクションが必要など、セキュリティ面にも気を使った仕様になっていると思いました。実際にWEBアプリケーションと組み合わせた使い方をする場合はセキュリティ含めてアプリケーションの実装上工夫が必要そうな気はしますが。  また今回Beaconの情報を取得できないか挑戦しました。WEB Bluetooth API 自体の仕様上、可能なようですが、必要な実装はChrome含めて未対応のようです。この辺りについては今後に期待でしょうか。 WEB USB API 含めてWEB ブラウザから直接ハードウェアにアクセスできるのは未来を感じます。

JavaでもBLEでIoT〜RaspberryPi Zero WからBLEデバイスにアクセスしてみる。

この記事について

 この記事ではRaspberry Pi Zero W上でJavaからBLEデバイスにアクセスする方法を記載しています。Raspberry PiからBLEインタフェースを操作する方法はNode.jsやPythonを使うやり方が多く見られます。これらのライブラリはLinuxのBlutoothスタックであるBluezのライブラリリンクして実行するか、D-BUSインタフェース経由でBuezを呼び出すことで実現されています。そのため、Bluezのインタフェースにアクセスできれば、プログラミング言語に依存せずにBLEにアクセスすることができます。  しかし、いずれの方法も一から実装すると非常に時間がかかるためすでにあるライブラリを利用する方法が良いでしょう。今回はJavaからBluezを呼び出すライブラリを調査し、利用方法をまとめましたので紹介します。サンプルコードは以下のリポジトリに置いています。Raspberry PiやRaspbianに限らずLinux上であれば動かせると思います。

github.com

JavaのBLEライブラリ

 今回調べた中で、JavaからBluezにアクセスしライブラリを呼び出すためのライブラリは以下の2つがありました。どちらもBluezをD-BUSインタフェース経由で動かしており、ライセンスはMITライセンスとなっています。この2つのライブラリの違いですが、tinybはintel-iot-devkitプロジェクトの中にあるようです。そのため、比較的作りがしっかりしていそうです。その反面、ライブラリを使うまでのコストが高く、ローカルで関連ライブラリをビルドしなければなりません。一方bluez-dbusの方は個人開発のライブラリのようです。tinybにインスパイアされたプロジェクトということですが、Mavenのセントラルリポジトリに登録されています。Maven プロジェクトからダウンロードして利用できるのが非常に使いやすく感じました。また必要なライブラリもapt-get でインストールできるため、今回はこのbluez-dbusを利用してみます。

ライブラリ名 備考
tiny C++ライブラリも同梱
bluez-dbus Maven central リポジトリに登録

Raspberry Pi Zero Wの環境構築

OSはRaspbian STRECHを利用しました。このバージョンからデフォルトインストールのBluezが5.43となります。bluez-dbusはBluez 5.43対応のため、Raspbian JESSEIなどでうまくいかない場合はBluezのバージョンを揃えることをお勧めします。

$uname -a
Linux raspberrypi 4.9.41+ #1023 Tue Aug 8 15:47:12 BST 2017 armv6l GNU/Linux

始める前にapt-getのアップデートを実施しておきます。

$sudo apt-get update & apt-get upgrade

libunixsocket-javaのインストール

bluez-dbusが唯一依存しているネイティブライブラリです。d-busのインタフェースライブラリからリンクされています。

$sudo apt-get install libunixsocket-java

Javaのインストール

JavaOracle Javaをインストールしました。Open JDKでも問題はないと思います。

$ sudo apt-get install openjdk-8-jdk

Mavenのインストール

Mavenも以下のように設定します。

$ wget http://ftp.jaist.ac.jp/pub/apache/maven/maven-3/3.5.0/binaries/apache-maven-3.5.0-bin.tar.gz
$ tar xzvf apache-maven-3.5.0-bin.tar.gz
$ sudo mv apache-maven-3.5.0 /opt/

環境変数の設定

環境変数JAVA_HOMEとPATHを設定します。~/.bashrcの末尾に以下を記載します。

$ export JAVA_HOME=$(readlink -f /usr/bin/javac | sed "s:/bin/javac::")
$ export PATH=/opt/apache-maven-3.5.0/bin:$JAVA_HOME/bin:$PATH

サンプルコードのビルド

まずはじめにサンプルコードの実行方法を説明します。https://github.com/masato-ka/bluez-dbus-sample.gitをクローンしてください。 クローン後、チェックアウトされたフォルダに入りmavenを使いビルドします。

$git clone https://github.com/masato-ka/bluez-dbus-sample.git
$cd bluez-dbus-sample
$mvn package

ビルド完了後、libunixsocket-javaのライブラリをコピーします。これはbluez-javaライブラリの仕様によるもので、このライブラリは Javaの実行パスからlibフォルダを探して、libunix-javaを読み込みます。もし存在しなければクラスパスからライブラリを読み込むのですが依存jarの一つであるmatthew.jarにlibunix-java.soが含まれていて読み込みます。しかしこのsoはRaspberry Pi Zero wでは動かないので、あらかじめ実行パスにライブラリをコピーしておきます。

$cd bluez-dbus-sample
$mkdir lib
$cp /usr/lib/jni/libunix-java.so ./lib/

実はこのライブラリの読み込みにかなりはまりました。

github.com

これで実行の準備が整いました。実行にはsudoをつけて実行します。Raspbian STRECH + Bluez 5.43ではD-BUSにBluezのD-BUSインタフェースに接続するのにbluetoothユーザグループかルート権限が必要になります。 以下のフォーラムのようにpiユーザにユーザグループを追加すればsudoは必要なくなるはずです。(私はやってみましたが結局sudoが必要でした。。。)

raspberrypi.stackexchange.com

$cd ~/bluez-dbus-sample
$sudo java -jar target/bluz-sample-0.0.1-SNAPSHOT-jar-with-dependencies.jar
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
-----------------HCI Interface---------------------
hci0
-----------------Scan BLE Device---------------------
-----------------Result BLE Device----------------
EnvSensor-BL01
ID115
Choose device:EnvSensor-BL01 //ここで検索したいデバイス名を入力する。
-----------------Characteristics----------------
00002a05-0000-1000-8000-00805f9b34fb
00002a29-0000-1000-8000-00805f9b34fb
00002a24-0000-1000-8000-00805f9b34fb
00002a25-0000-1000-8000-00805f9b34fb
00002a27-0000-1000-8000-00805f9b34fb
00002a26-0000-1000-8000-00805f9b34fb
0c4c3001-7700-46f4-aa96-d5e974e32a54
0c4c3002-7700-46f4-aa96-d5e974e32a54
0c4c3003-7700-46f4-aa96-d5e974e32a54
0c4c3004-7700-46f4-aa96-d5e974e32a54
0c4c3005-7700-46f4-aa96-d5e974e32a54
0c4c3006-7700-46f4-aa96-d5e974e32a54
0c4c3011-7700-46f4-aa96-d5e974e32a54
0c4c3012-7700-46f4-aa96-d5e974e32a54
0c4c3013-7700-46f4-aa96-d5e974e32a54
0c4c3014-7700-46f4-aa96-d5e974e32a54
0c4c3015-7700-46f4-aa96-d5e974e32a54
0c4c3016-7700-46f4-aa96-d5e974e32a54
0c4c3017-7700-46f4-aa96-d5e974e32a54
0c4c3018-7700-46f4-aa96-d5e974e32a54
0c4c3019-7700-46f4-aa96-d5e974e32a54
0c4c301a-7700-46f4-aa96-d5e974e32a54
0c4c3031-7700-46f4-aa96-d5e974e32a54
0c4c3032-7700-46f4-aa96-d5e974e32a54
0c4c3033-7700-46f4-aa96-d5e974e32a54
0c4c3034-7700-46f4-aa96-d5e974e32a54
0c4c3041-7700-46f4-aa96-d5e974e32a54
0c4c3042-7700-46f4-aa96-d5e974e32a54

実行すると上記のようにHCIインタフェース名、検出されたデバイス名、キャラクタリスティックのUUIDが出力されます。今回は前回の記事でも紹介したOMRONの2JCIE-BL01を対象としました。次にサンプルソースの説明を記載します。

masato-ka.hatenablog.com

サンプルコードの解説

サンプルコードの全文はGitHubで確認してください。やっつけで書いたので例外処理などあれな部分も含まれています。 サンプルコードの処理は全部で大きく以下の3つの部分に分かれています。

  1. DeviceManagerオブジェクトの取得
  2. BLEデバイスのスキャン
  3. BLEデバイスへの接続とキャラクタリスティックの取得

pom.xmlへblues-dbusを依存関係として追加します。

<dependency>
        <groupId>com.github.hypfvieh</groupId>
        <artifactId>bluez-dbus</artifactId>
        <version>0.0.2</version>
</dependency>

1 DeviceManagerオブジェクトの取得

try {
    DeviceManager.createInstance(false);
} catch (DBusException e1) {
    System.out.println("failed create instance caused by D-BUS.");
}
DeviceManager deviceManager = DeviceManager.getInstance();
  • (1) DeviceManagerのインスタンスを作成しています。DeviceManager::createInstance(Boolean)でtrueを設定するとD-BUSのログインセッションを利用し、falseを設定するとシステム管理用セッションを利用します。(ライブラリのコメントから解釈しています。)

2 BLEデバイスのスキャン

List<BluetoothAdapter> result = deviceManager.getAdapters();
BluetoothAdapter bluetoothAdaptor = result.get(0); //(1)
System.out.println("-----------------HCI Interface---------------------");
result.stream().map(e->e.getDeviceName()).forEach(System.out::println);
System.out.println("-----------------Scan BLE Device---------------------");
bluetoothAdaptor.startDiscovery();//(2)
Thread.sleep(5000);
bluetoothAdaptor.stopDiscovery();//(3)
  • (1) HCIデバイスのオブジェクトを取得します。今回は最初に見つかったHCIインタフェースを利用します。複数インタフェースがある場合(BLEドングルを刺しているなど)は選ぶ必要があります。
  • (2) デバイスの検索を開始します。
  • (3) 5秒待った後デバイスの検索を停止します。

3 BLEデバイスへの接続とキャラクタリスティックの取得

List<BluetoothDevice> devicies = deviceManager.getDevices(); //(1)
devicies.stream().map(e->e.getName()).forEach(System.out::println);
BufferedReader buffredReader = new BufferedReader(new InputStreamReader(System.in));
System.out.print("Choose device:");
String deviceName = buffredReader.readLine();
System.out.println("-----------------Characteristics----------------");
for(BluetoothDevice bluetoothDevice : devicies){
    if(bluetoothDevice.getName().equals(deviceName)){
        try{
            bluetoothDevice.connect();//(2)
                bluetoothDevice.refreshGattServices();
                List<BluetoothGattService> servicies = bluetoothDevice.getGattServices();//(3)
                for(BluetoothGattService service : servicies){    
                        List<BluetoothGattCharacteristic> characteristics  = service.getGattCharacteristics();//(4)
                    characteristics.stream().map(e->e.getUuid()).forEach(System.out::println);
                }
            }catch(DBusExecutionException e){
                 System.out.println("FailedConnection");
                 e.printStackTrace();
            }
       }
}
  • (1) 検索したデバイスを取得します。
  • (2) デバイスへの接続を行います。
  • (3) 接続が完了したデバイスからサービスを取得します。
  • (4) すべてのサービスからキャラクタリスティックを出力しています。

まとめ

BLEを扱えるJavaのライブラリを探していましたがMavenから追加できてなんとなく動くものが見つかりました。まだまだ発展の余地ありなライブラリですが、ちょっと遊ぶのには良さそうです。またLinux環境のみで開発するのは辛いのでOSXでも動くといいのですが。BLEについては基本的にプラットフォームに依存するためOSを超えての移植性は難しそうです。LinuxであればBluezになりますがOSXではCoreBluetoothをラップする必要があります。WIndowsは。。。Node.jsのnobleであれば全てのプラットフォームに対応できるようです。