Penta creation

ホームへ
2015年12月07日

BLE NanoでiOSアプリとBluetooth通信(BLE Nano編)

RedBearLab BLE Nanoを使って、iOSアプリとArduinoをBluetooth通信させてみました。

RedBearLab BLE Nano

BLE Nanoは、日本国内で使用できるコンパクトなBLEモデムボードで、スイッチサイエンスで購入できます。
上記写真は、BLE NanoとMK20(書き込み用ボード)が一緒になっているBLE Nano Kitで、ピンは自分ではんだ付けします。BLE Nanoは、iPhoneだけはなく、Android、Windows Phoneにも対応していて、MK20と一緒に使用します。


BLE Nanoを使用する場合はプログラムを書き込む必要があります。その際に使用するのがMK20で、BLE Nanoだけ買っても、プログラムが書き込めないので要注意です。

また、プログラミングにArduinoを使用する場合は、メーカー提供のライブラリを使用しますが、2015年12月7日現在では、正式リリースではないのと登録が必要になるので注意が必要です。

セットアップ

Arduino IDEを使って、BLE Nanoのセットアップと動作の確認を行います。


(1) BLE NanoをMK20に装着して、パソコンに接続します。
(2) ライブラリーをダウンロードします。
(3) ライブラリーの「bootloader > bootloader.hex」をMK20を接続してマウントされた「MBED」にコピーして、Arduino IDEを使うためのファームウェアーをアップロードします。
(4) Arduino IDEのPreferencesで環境設定を開き、Additional Boards Manager URLsにこちらのJSONファイルのURLを入力します。
(5) 「ツール > マイコンマネージャー > ボードマネージャー」を開いて、「RedBearLab nRF51822 Boards by RedBearLab」をインストールします。すると、「ツール > マイコンボード」からBLE Nanoが選択できるようになります。
(6) Windowsの場合は、USB CDC Driverをインストールします。

上記セットアップが完了したら、Arduino IDEでBLE Nanoにプログラムを書き込むことができるようになります。
下記おなじみのプログラムを書き込むと、BLE Nanoの背面にあるLEDが光ります。

● BLENanoTest.ino

void setup()
{
  pinMode(13,OUTPUT);
}

void loop()
{
  digitalWrite(13,HIGH);
  delay(1000);
  digitalWrite(13,LOW);
  delay(1000);
}

ペリフェラルとしてのプログラムの書き込み

ここまでで、プログラムが書き込める環境をセットアップできました。BLE NanoをBLEモデムとして使用するためには、ペリフェラルとしてアドバタイジングを飛ばし、Bluetooth通信でデータをやり取りするためのプログラムを書き込む必要があります。

BLEの仕組みについては奥が深いので、ネットや書籍で調べたり下記などを参考にしてください。


また、ペリフェラルのプログラムを自前で書こうとすると大変なので、メーカーが提供しているサンプルファイルをベースにします。



● BLEPeripheral.ino

Bluetooth通信の仕組みには、セントラル(中央装置)とペリフェラル(周辺装置)という役割があり、iOSアプリをセントラルとするのでBLE NanoとArduinoはペリフェラルということになります。ペリフェラルはアドバイタイジングという小さな電波を発信することで、セントラルに認識してもらいデータのやり取りをします。

ペリフェラルはデータをやり取りするための、サービスとキャラクタリスティックという入れ子構造の入れものを持つことができます。サンプルファイルはこういった処理がいろいろ書かれているのですが、iOSでBLE Nanoを認識するためのデバイス名、ローカルネームとArduinoにシリアル通信でデータを送信するための処理をかけば、とりあえずはOKです。

ここまで書いてBLE Nanoに書き込めば、iOSアプリからBLE Nanoを認識できるようになります。

#define TXRX_BUF_LEN 20
BLE ble;
Ticker ticker;
static uint16_t value = 0;

//サービス、キャラクタリスティックの生成
static const uint8_t service1_uuid[]        = {0x71, 0x3D, 0, 0, 0x50, 0x3E, 0x4C, 0x75, 0xBA, 0x94, 0x31, 0x48, 0xF1, 0x8D, 0x94, 0x1E};
static const uint8_t service1_chars1_uuid[] = {0x71, 0x3D, 0, 2, 0x50, 0x3E, 0x4C, 0x75, 0xBA, 0x94, 0x31, 0x48, 0xF1, 0x8D, 0x94, 0x1E};
static const uint8_t service1_chars2_uuid[] = {0x71, 0x3D, 0, 3, 0x50, 0x3E, 0x4C, 0x75, 0xBA, 0x94, 0x31, 0x48, 0xF1, 0x8D, 0x94, 0x1E};
static const uint8_t service1_chars3_uuid[] = {0x71, 0x3D, 0, 4, 0x50, 0x3E, 0x4C, 0x75, 0xBA, 0x94, 0x31, 0x48, 0xF1, 0x8D, 0x94, 0x1E};
static const uint8_t uart_base_uuid_rev[]   = {0x1E, 0x94, 0x8D, 0xF1, 0x48, 0x31, 0x94, 0xBA, 0x75, 0x4C, 0x3E, 0x50, 0, 0, 0x3D, 0x71};

uint8_t chars1_value[TXRX_BUF_LEN] = {0,};
uint8_t chars2_value[TXRX_BUF_LEN] = {0,};
uint8_t chars3_value[TXRX_BUF_LEN] = {0x01, 0x02, 0x03};

GattCharacteristic characteristic1(service1_chars1_uuid, chars1_value, 1, TXRX_BUF_LEN, GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE_WITHOUT_RESPONSE );
GattCharacteristic characteristic2(service1_chars2_uuid, chars2_value, 1, TXRX_BUF_LEN, GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ);
GattCharacteristic characteristic3(service1_chars3_uuid, chars3_value, 3, TXRX_BUF_LEN, GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY);
GattCharacteristic *uartChars[] = {&characteristic1, &characteristic2, &characteristic3};
GattService uartService(service1_uuid, uartChars, sizeof(uartChars) / sizeof(GattCharacteristic *));

void disconnectionCallBack(Gap::Handle_t handle, Gap::DisconnectionReason_t reason)
{
    ble.startAdvertising();
}

//データの送信処理
void writtenHandle(const GattWriteCallbackParams *Handler)
{
    uint8_t buf[TXRX_BUF_LEN];
    uint16_t bytesRead, index;

    if (Handler->handle == characteristic1.getValueAttribute().getHandle())
    {
        ble.readCharacteristicValue(characteristic1.getValueAttribute().getHandle(), buf, &bytesRead);
        for(byte index=0; index<bytesRead; index++)
        {
            //Serial1.print(buf[index], HEX);
            //ここをprintからwriteに変更することで、ArduinoにデータをDX経由でシリアル通信できるようになります。
            Serial1.write(buf[index]);
        }
    }
}

void m_1s_handle(void)
{
    value++;
    ble.updateCharacteristicValue(characteristic3.getValueAttribute().getHandle(), (uint8_t *)&value, 2);
}

void setup()
{
    ticker.attach(m_1s_handle, 1);
 
    Serial1.begin(9600);
    ble.init();
    ble.onDisconnection(disconnectionCallBack);
    ble.onDataWritten(writtenHandle);
    ble.accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED);

    //ローカルネームを設定
    ble.accumulateAdvertisingPayload(GapAdvertisingData::SHORTENED_LOCAL_NAME,(const uint8_t *)"YOUR_BLE_NAME", sizeof("YOUR_BLE_NAME") - 1);
    ble.accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_128BIT_SERVICE_IDS,(const uint8_t *)uart_base_uuid_rev, sizeof(uart_base_uuid_rev));

    //ローカルネームを設定
    ble.accumulateScanResponse(GapAdvertisingData::SHORTENED_LOCAL_NAME,(const uint8_t *)"YOUR_BLE_NAME", sizeof("YOUR_BLE_NAME") - 1);
    ble.accumulateScanResponse(GapAdvertisingData::COMPLETE_LIST_128BIT_SERVICE_IDS,(const uint8_t *)uart_base_uuid_rev, sizeof(uart_base_uuid_rev));
    ble.setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
    ble.addService(uartService);

    //デバイス名を設定
    ble.setDeviceName((const uint8_t *)"YOUR_BLE_DEVICE_NAME");
    ble.setTxPower(4);
    ble.setAdvertisingInterval(160);
    ble.setAdvertisingTimeout(0);
    ble.startAdvertising();
}

void loop() {
    ble.waitForEvent();
}
前の記事へ

RGBフルカラーLEDでLチカ

次の記事へ

BLE NanoでiOSアプリとBluetooth通信(iOSアプリ編)

ページトップへ