さぁ、ついにやってきました!このモジュールの真骨頂 BLE です!
いきなりおことわりですが、僕自身、Bluetooth を始めとした RF 初心者ですので、正直詳しいことは分かりません。説明も非常にざっくりとしたものになりますので、その辺ご了承を。
参考資料
僕がこのマイコンの BLE 機能 (RADIO ペリフェラル)を動かすに足るための知識を得たサイトたちです。作者様、大変勉強になりました。
- 株式会社ムセンコネクトの「サルでもわかるBLE入門」シリーズ
- Classic Bluetooth も含め、基本的なトポロジーや概念に関してまず見るべきです。
- BLEビーコンを紐解く | ITエンジニアのための情報コンテンツANKET
- BLEビーコンを紐解く2 | ITエンジニアのための情報コンテンツANKET
- この2つはパケットの基本構造についてざっくり理解するのに役立った。
- Advertising(解説) - BLE Docs
- iPhoneから送出されるパケットをビット単位で説明してくれている。
- Advertising Parameter · BLEDocs
- Advertising Parameter · BLEDocs
- 上2つはアドバタイズのパラメータについて。
- 51822模拟ble广播-实践-ifndef-ChinaUnix博客
- サンプルプログラムとともに丁寧な説明があり、とても助かった。中国の方。
以下はマイコンのペリフェラルに関して参考にしたものです。
- nRF51822 raw transmit/receive not working - Nordic Q&A - Nordic DevZone - Nordic DevZone
- 「RADIO実行中はずっとHFCLK(外部クリスタルとして)を起動しておかないと」と示唆。
- 小型 Bluetooth LE モジュール AE-TYBLE16 で LINE Things Starter してみる - Qiita
- UICRを書き換えなければならないという事をここで知る。
目標
さて、何処までやるかです。まあ普通はセントラルに見つけてもらって接続までするのですが、そこまで詳しくはないのでアドバタイズのみを行うようにします。
セントラルから見てこのマイコンがペリフェラルとして認識されるようになるまでが今回扱う範疇です。
なお、ビーコンでは接続をせずにアドバタイズパケットに多少情報を含めることができますのでそういった用途では今回の記事が役に立ちそうな気がします。
完成したソースコードはこちらに置いておきますので、もし動かしたい方がいらっしゃいましたらお役立てください。ただし、記事末尾に述べるようにスタートアップ用ファイルの改造が必要です。
送信パケット(の一部)
どのようなデータを送信するか、を先に述べておきます。そもそも通信パケットはプリアンブル、アクセスアドレス、PDU、CRC で成り立つわけです。が、ここでそれぞれを述べてもアレなので、詳しいところは参考サイトにまかせておきましょう。
void radio_memwrite_adv_data(void) {
// == PDU Header ==
packetBuffer[0] = 0x42; // ADV_NONCONN_IND
packetBuffer[1] = 6 + 3 + 5; // Length。アドレス分も長さが要るっぽい?
// ===== AdvA =====
packetBuffer[2] = 0x93;
packetBuffer[3] = 0x08;
packetBuffer[4] = 0x81;
packetBuffer[5] = 0x14;
packetBuffer[6] = 0x45;
packetBuffer[7] = 0x11;
// ===== AdvData =====
// -- Data0 ---
packetBuffer[8] = 2; // Length
packetBuffer[9] = 0x01; // flag
packetBuffer[10] = 0x06; // General & Discoverable, Only for BLE
// --- Data1 ---
packetBuffer[11] = 4; // Length
packetBuffer[12] = 0x09; // Complete Local Name
packetBuffer[13] = 'n';
packetBuffer[14] = 'R';
packetBuffer[15] = 'F';
}
こんな感じで成り立っています。
PDU ヘッダは「このデバイスはスキャンできるけど接続できないよ」ということを表しています。Length はパケットの長さを示しますが、AdvA と呼ばれるアドレスの長さも含める必要があるんですかね?(必要が無いとの情報があるが、+6
して含めなければ上手く動かなかった。本来の送信パケット中ではむしろ +6
しないほうが良いと思うが...内部でその分引き去っているのか??)
AdvA はデバイス自身のアドレスを示します。本来は乱数である必要がありますが、今回は趣味用途で固定しています。6byte で表現されて、 11:45:15:81:08:93 です(数字に深い意味は無い)。このように、AdvA は逆順で記述します。
AdvData は各種ペリフェラルの情報を載せていきます。
まず Data0 では「2bytes の Flag を送るよ」と言って、BLE 専用機器であり一般にスキャン検出可能であることを示しています。この意味がどうなのかは分かりませんが、取り敢えずね。
Data1 ではデバイスの名前です。nRF という名前にしました。
スタートアップ用ファイルの改造
ハマりどころです。いや、もう本当に大ハマリして1週間弱を溶かしました。
...いくら強調してもし足りないのですが、この AE-TYBLE16 だからこそハマった内容なのでした。というかこのボードに載っている太陽誘電の EYSGJNAWY-WX を使っている人は皆等しくハマるでしょう。
この Qiita の記事に書いてあるのですが、UICR
と呼ばれるブロックに含まれるプロテクト付きのレジスタの値を書き換えなければならないらしいのです。デフォルトでは外部クリスタルを 16MHz であるとマイコン側が認識する設定になっています。以前に述べたように、この SoC には 32MHz のクリスタルが載っているので、周波数が一致せずこれではマイコンが上手く動かないらしい。
でも実際はこれまでず〜っと(タイマーや UART 含め)ちゃんと動いていましたので、どうやら RADIO ペリフェラルの問題かと思われます。
Qiita の記事では、
- J-Link じゃないと書き換えられない
- 一度書き換えたら元のファームが動かなくなる
とか言っていますが、多分違うでしょうね。前者はプロテクトをソフトウェア中で外せば良いわけですし、後者はこの部分をイレースすればまた戻りますからね。大して重大な操作ではないはずです。
具体的にどうするかですが、なんと太陽誘電の DataReport に日本語で丁寧に載っていました。中身はこんな感じ。
if (*(uint32_t *)0x10001008 == 0xFFFFFFFF)
{
NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Wen << NVMC_CONFIG_WEN_Pos;
while (NRF_NVMC->READY == NVMC_READY_READY_Busy){}
*(uint32_t *)0x10001008 = 0xFFFFFF00;
NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Ren << NVMC_CONFIG_WEN_Pos;
while (NRF_NVMC->READY == NVMC_READY_READY_Busy){}
NVIC_SystemReset();
while (true){}
}
あぁ、やっぱりソフトでプロテクト外して書き換えてますわ。これをスタートアップ用ファイルに追記すればよいです。プロジェクトルートから見て nrfx/mdk/system_nrf51.c
の SystemInit
の末尾付近、SystemCoreClockUpdaate();
の前にこれを書いてください。
そして最後にもう2行必要です。外部クリスタルを有効化するコード。なんと、なんと、裏方でこれまで色々なペリフェラルを弄ってきたのですが、ずっと内部 RC オシレータを使っていたらしいのです。一方で RADIO ペリフェラルは外部クリスタル必須ですから、そりゃ動かないわけだわ。ということで、以下の2行を SystemCoreClockUpdate();
の直前に追加しましょう。
NRF_CLOCK->TASKS_HFCLKSTART = 1;
while( !NRF_CLOCK->EVENTS_HFCLKSTARTED );
ということで、SystemInit
内部の末尾が以下のようになっていれば良いです。
void SystemInit(void)
{
...
if (*(uint32_t *)0x10001008 == 0xFFFFFFFF)
{
...
}
NRF_CLOCK->TASKS_HFCLKSTART = 1;
while( !NRF_CLOCK->EVENTS_HFCLKSTARTED );
SystemCoreClockUpdate();
}
お疲れ様でした。俺。
動作
初期化コードも含めたコード全体は「目標」のセクションにリンクを張っていますので見てください。取り敢えず上のようなパケットを書き込んで設定しました。
このデバイスは接続ができませんから、BLE 通信用アプリを使ってもデータの取得は困難です。よって、Mac で LightBlue という BLE 通信用アプリを起動させつつ、裏方で PacketLogger を動かして通信を解析することにしました。するとログとその解釈はこんな感じ。
しっかりと認識されていますね。アドレスは希望通りだし、パケットもこの通り。また、デバイス名も nRF になっています。
ちなみに接続はできませんが、検出は可能です。例えば iPhone で BlueLight を起動してみると...
こうやってデバイス名も一覧中に表示されます!!やったね!!
よくわからないもの
わからないこと・今後の課題です。
- 未だに接続時のパケットやらやり取りのフォーマットがわからない。
- 今後色々と調べてやってみようと思う。
- BLE Lチカができたら嬉しいね。他にも BLE 経由で環境センサのデータ取得とか。
- Data Whitening って何?
- RF でよく使われる手段らしい。今回はこれを有効化しないと通信ができなかった。
- 意図的にノイズを混ぜることでエラーを起こし、通信の信頼性を上げる方法??ナニソレオイシイノ