コンテンツにスキップ

Serialization

概要

nRF51に直接実装せずに外部MCUなどからシリアル通信で制御する方式をBLE serializationと呼ぶ。

ドキュメントで紹介されている方法は開発ボードを二台接続して、ApplicationBoard(制御する側)とConnectivityBoard(制御される側)に分けて実装を行う方法になる。
実際はApplicationBoard側はnRF51を使用せずにARMを直で使用することが多くなるはず。
接続はUART, SPI, HCIが利用できる。

ApplicationBoardではSoftdeviceを使用しないが専用ライブラリによりSoftdeviceを使用した時と同じコードでConnectivityBoardを制御することができる。
ConnectivityBoardには、Softdeviceと接続用のアプリケーションをインストールする。

Arduinoとつなぐ

ArduinoをApplicationBoardとしてつなぐ場合は専用ライブラリが利用できないので、直にシリアル通信を行う必要がある。
また、Arduinoはフローコントロールができないなど、シリアル通信の設定も変える必要もある。

接続アプリのコンパイル

接続アプリはコード量が多いのでKeilの無料版でばビルドできないので、今回はGCCを用います。

  1. こちらからArm用のGCCをダウンロードし、任意の場所へ置く。

  2. 下記のGNU_INSTALL_ROOTにGCCの場所を記載する。

  3. [SDK]/components/toolchain/gcc/Makefile.posix

  4. 下記のヘッダにシリアル通信の設定があるので、Arduinoに合わせた設定にする。

  5. [SDK]/components/serialization/common/ser_config.h

``` /* UART transmission parameters / //#define SER_PHY_UART_FLOW_CTRL APP_UART_FLOW_CONTROL_ENABLED //#define SER_PHY_UART_PARITY true //#define SER_PHY_UART_BAUDRATE UART_BAUDRATE_BAUDRATE_Baud1M

define SER_PHY_UART_FLOW_CTRL APP_UART_FLOW_CONTROL_DISABLED

define SER_PHY_UART_PARITY false

define SER_PHY_UART_BAUDRATE UART_BAUDRATE_BAUDRATE_Baud115200

```

  1. 接続アプリは利用するSoftdevice毎に下記フォルダに用意されているので、移動してmakeを実行するとhexファイルが生成される。

  2. [SDK]/examples/ble_central_and_peripheral/ble_connectivity [S130]

  3. [SDK]/examples/ble_peripheral/ble_connectivity [S110]
  4. [SDK]/examples/ble_central/ble_connectivity [S120]

  5. 開発ボードにSoftdeviceと先ほどのhexファイルを書き込む。

接続

表の通りにGPIOを接続する。

開発ボード Arduino
GND GND
P0.12 P2
P0.13 P3

Arduinoプログラム

iBeaconを発信するプログラム。

```

include

SoftwareSerial serial(2, 3);

int state = 0; int count = 0;

void setup() { // BLEとの通信用 serial.begin(115200); // ログ出力用 Serial.begin(9600); Serial.write("*Start!\n"); // BLEを有効化 Serial.write("sd_ble_enable()\n"); sd_ble_enable(); }

void loop() { while (serial.available()) { Serial.print(serial.read(), HEX); if (count++ > 6) { Serial.write("\n"); count = 0; switch(state++) { case 0: Serial.write("sd_ble_gap_adv_data_set()\n"); sd_ble_gap_adv_data_set(); break; case 1: Serial.write("sd_ble_gap_adv_start()\n"); sd_ble_gap_adv_start(); break; case 2: Serial.write("*Finish!\n"); break; } } else { Serial.write(","); } } }

// データ送信 void send_data(byte *data) { int len = data[0] + 2; for (int i=0; i<len; i++) { serial.write(data[i]); } }

// BLEを有効化 void sd_ble_enable() { byte data[] = { // パケットサイズ 0x08, 0x00, // タイプ(0:コマンド, 1:レスポンス) 0x00, // コマンドタイプ 0x60, // コマンドの内容 0x01, 0x00, 0x00, 0x00, 0x00, 0x00 }; send_data(data); }

// Advertisingのデータを設定 void sd_ble_gap_adv_data_set() { byte data[] = { 0x24, 0x00, 0x00, 0x72, 0x1e, 0x01, // Header 0x02, 0x01, 0x06, 0x1a, 0xff, 0x4c, 0x00, 0x02, 0x15, // UUID 0x01, 0x12, 0x23, 0x34, 0x45, 0x56, 0x67, 0x78, 0x89, 0x9a, 0xab, 0xbc, 0xcd, 0xde, 0xef, 0xf0, // Major 0x01, 0x02, // Minor 0x03, 0x4, 0xc3, 0x00, 0x00}; send_data(data); }

// Advertising開始 void sd_ble_gap_adv_start() { byte data[] = { 0x0c, 0x00, 0x00, 0x73, 0x01, // ADV_NONCONN_IND 0x03, 0x00, 0x00, 0x00, // Interval(0.625 ms units) 0xa0, 0x00, 0x00, 0x00, 0x00}; send_data(data); }

```

パケットフォーマット

例)sd_ble_enableのパケットフォーマット
他のパケットフォーマットはこちら

サイズ タイプ コマンド コマンド パラメータ
2Byte 1byte 1byte 1byte 5byte
0x08, 0x00 0x00 0x60 0x01 0x00...
以降8Byte コマンドタイプ sd_ble_enableを表す BLEを有効化する 5byte

byte data[] = { // パケットサイズ 0x08, 0x00, // タイプ(0:コマンド, 1:レスポンス) 0x00, // コマンドタイプ 0x60, // コマンドの内容 0x01, 0x00, 0x00, 0x00, 0x00, 0x00 };

スキャンする

準備

Advertisingパケットをスキャンするにはsd_ble_gap_scan_startを利用する。
これはS110のソフトデバイスでは対応していないので、S130を書き込む。

Arduinoプログラム

```

include

SoftwareSerial serial(2, 3);

int state = 0; int count = 0; uint8_t buff[255]; int buff_pos = 0; int data_len = 0; int data_com = 0;

void setup() { // BLEとの通信用 serial.begin(115200); // ログ出力用 Serial.begin(38400); Serial.write("*Start!\n"); // BLEを有効化 Serial.write("sd_ble_enable()\n"); sd_ble_enable(); }

// パケットの内容をパース void parse(uint8_t data) { buff[buff_pos++] = data; if (buff_pos == 2) { data_len = buff[0] + (buff[1] << 8); Serial.write("len:"); Serial.print(data_len, DEC); Serial.write("\n"); } if (buff_pos == 3) { Serial.write("type:"); Serial.print(data, HEX); Serial.write("\n"); } if (buff_pos == 4) { data_com = data; Serial.write("command:"); Serial.print(data, HEX); Serial.write("\n"); } if (buff_pos > 4) { Serial.print(data, HEX); } else { return; } if (data_len == buff_pos -2) { buff_pos = 0; data_len = 0; state++; Serial.write("\n"); } else { Serial.write(","); } }

void loop() { if (serial.overflow()) { // SoftwareSerialはバッファが64byteしかなくオーバーフローしてしまうので、回避策が必要。 Serial.println("\nSoftwareSerial overflow!"); delay(100); exit(1); } while (serial.available()) { parse(serial.read()); if (state == 1 && buff_pos == 0) { Serial.println("sd_ble_gap_scan_start()"); sd_ble_gap_scan_start(); } } }

// データ送信 void send_data(byte *data) { int len = data[0] + 2; for (int i=0; i<len; i++) { serial.write(data[i]); } }

// BLEを有効化 void sd_ble_enable() { byte data[] = { // パケットサイズ 0x08, 0x00, // タイプ(0:コマンド, 1:レスポンス) 0x00, // コマンドタイプ 0x60, // コマンドの内容 0x01, 0x00, 0x00, 0x00, 0x00, 0x00 }; send_data(data); }

// Scan開始 void sd_ble_gap_scan_start() { byte data[] = { // パケットサイズ 0x0b, 0x00, // タイプ(0:コマンド, 1:レスポンス) 0x00, // コマンドタイプ 0x86, // コマンドの内容 0x01, // present 0x00, // Active Scanning 0x00, // Whitelist 0x40, 0x06, // Interval(0.625 ms units) 0x50, 0x00, // Scan window(0.625 ms units) 0x00, 0x00 // Timeout(No timeout) }; send_data(data); } ```

改良

SimpleFIFOを利用してSoftwareSerialのオーバーフローを回避する。

```

include

SimpleFIFO sFIFO;

// ~~略~~

void loop() {

// シリアルから読み取ったデータはすぐにQueueに入れる while (serial.available()) { sFIFO.enqueue(serial.read()); }

// シリアルからの読み込みが無いタイミングで処理する if (sFIFO.count() > 0) { parse(sFIFO.dequeue()); if (state == 1 && buff_pos == 0) { Serial.println("sd_ble_gap_scan_start()"); sd_ble_gap_scan_start(); } }

}

```

より見やすく

parse関数の内容を変更してBLE_GAP_EVT_ADV_REPORTのイベントをより詳細に表示するように改良。

``` // ~~略~~ if (buff_pos > 4) { if (data_com == 0x1b) { // BLE_GAP_EVT_ADV_REPORT if (buff_pos > 16) { Serial.print(data, HEX); } else if (buff_pos == 16) { Serial.write("handle:"); Serial.print(buff[5], HEX); Serial.print(buff[6], HEX); Serial.write("\n"); Serial.write("type:"); Serial.print(buff[7], HEX); Serial.write("\n"); Serial.write("address:"); Serial.print(buff[8], HEX); Serial.print(buff[9], HEX); Serial.print(buff[10], HEX); Serial.print(buff[11], HEX); Serial.print(buff[12], HEX); Serial.print(buff[13], HEX); Serial.write("\n"); Serial.write("rssi:"); Serial.print(buff[14], HEX); Serial.write("\n"); return; } else { return; } } else { Serial.print(data, HEX); } } else { return; } // ~~略~~

```