読者です 読者をやめる 読者になる 読者になる

熊小屋日誌

Windows 10 UWPやXamarin, Python、mbed/NetMF/Arduino/Edison, Azureなどぼちぼちと。たまにPCや勉強会、セミナーなどの話題も

mbed HRM1017を使ったBluetooth LEデバイスの開発

mbed Advent Calendar 2014の9日目です。

mbedとの関わり

mbedは2013年9月のmbed祭り in Nagoyaで初めて知って、電子回路の経験やマイコンの開発経験がない自分なりに少しずつ勉強してきてました。

mbedを使うことでどうやって始めればよいか分からなかったマイコン開発の第一歩を踏み出すことができました。ただし、ブレッドボード上での実験段階から、実際に利用できるガジェットを制作する2歩目を踏み出すまでに至れていなく、まだ勉強しないといけないことだらけです。

今回、mbedでのガジェット制作にさらに気合いをいれるべく、mbed Advent Calendar 2014に参加させていただきました。

mbedアドベントカレンダーで何を書くかは悩んだのですが、最近はmbed HRM1017を知ってその使い方を勉強しているので、Bluetooth LEを使ったデバイスとタブレットスマートフォンの連携について書くことにしました。

mbed HRM1017の配線とタブレットスマートフォン(Windows 8.1/Windows Phone 8.1)側のアプリケーションプログラムは、先日のWindows Phone Advent Calendar 2014の5日目としてまとめました。今回は、その残りのmbed HRM1017側のプログラムをまとめます。

BLE_HTM_HRM1017プログラミング

HRM1017に書き込むプログラムは、tsuboiさんが公開されているBLE_HTM_HRM1017を改造(ADT7410対応、クロック関係の修正)したバージョンで、こちらでBLE_HTM_HRM1017として公開しています。

BLE_HTM_HRM1017のプログラムはすべてmain.cppに書かれているので、冒頭からみていきます。

CharacteristicとServiceの定義

まず39-45行目で温度計のサービスを定義しています。

:::C++
/* Health Thermometer Service */ 
uint8_t thermTempPayload[5] = { 0, 0, 0, 0, 0 };
GattCharacteristic tempChar (GattCharacteristic::UUID_TEMPERATURE_MEASUREMENT_CHAR,
                             thermTempPayload, 5, 5,
                             GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_INDICATE);
GattCharacteristic *htmChars[] = {&tempChar, };
GattService htmService(GattService::UUID_HEALTH_THERMOMETER_SERVICE, htmChars, 
                       sizeof(htmChars) / sizeof(GattCharacteristic *));
  • 39行目 thermTempPayloadは温度計のデータを格納する領域です。
  • 40行目 tempCharは温度計のCharacteristicを定義します。第1引数にCharacteristicのUUIDを、第2・3引数にペイロードの初期と最大の長さを、第4引数にCharacteristicの属性としてINDICATE(値を通知する)を指定しています。
  • 43行目 htmCharsはサービスに含まれるCharacteristicの配列です。
  • 44行目 htmServiceは体温計のServiceを定義します。第1引数にServiceのUUIDを、第2・3引数にCharacteristic配列とその要素数を指定します。

同様に48-55行目でバッテリ残量のサービスを定義しています。これは、本プログラムでは擬似的な値を返すように実装されています。

:::C++
/* Battery Level Service */
uint8_t batt = 100;
uint8_t read_batt = 0;
GattCharacteristic battLevel (GattCharacteristic::UUID_BATTERY_LEVEL_CHAR, 
                             (uint8_t *)batt, 1, 1,
                             GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY);
GattCharacteristic *battChars[] = {&battLevel, };
GattService battService(GattService::UUID_BATTERY_SERVICE, battChars,
                        sizeof(battChars) / sizeof(GattCharacteristic *));

そして、127-128行目で定義した2個のサービスを登録しています。

:::C++
ble.addService(htmService);
ble.addService(battService);

また、57-58行目と118-119行目で通信相手を探すアドバタイジングに、汎用的な温度計としてサービスのIDを登録しています。

:::C++
uint16_t uuid16_list[] = {GattService::UUID_HEALTH_THERMOMETER_SERVICE,
                          GattService::UUID_BATTERY_SERVICE};
...
ble.accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, (uint8_t*)uuid16_list, sizeof(uuid16_list));
ble.accumulateAdvertisingPayload(GapAdvertisingData::GENERIC_THERMOMETER);

まだ試せていませんが、体温計(HTP)以外のプロファイルを定義する場合は、これらの定義を変更すればよさそうです。

デバイス名の定義

アドバタイジングでホスト側に表示されるデバイス名は、18行目と120行目で定義しています。

:::C++
const static char  DEVICE_NAME[] = "HRM1017_HTM";
ble.accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, (uint8_t *)DEVICE_NAME, sizeof(DEVICE_NAME));

値の更新

106行目でTicker.attachメソッドで1行毎にperiodicCallback関数を呼び出すよう定義しています。

:::C++
void updateServiceValues(void)
{
      /* Decrement the battery level. */
      batt <=50 ? batt=100 : batt--;

      /* Update the temperature. Note that we need to convert to an ieee11073 format float. */
#ifdef UseADT7410
      float temperature = healthThemometer.getTemp();
#else
      float temperature = healthThemometer.read();
#endif
      DEBUG("temp:%f\n", temperature);
      uint32_t temp_ieee11073 = quick_ieee11073_from_float(temperature);
      memcpy(thermTempPayload+1, &temp_ieee11073, 4);
      ble.updateCharacteristicValue(tempChar.getValueAttribute().getHandle(), thermTempPayload, sizeof(thermTempPayload));  //Mod
      ble.updateCharacteristicValue(battLevel.getValueAttribute().getHandle(), (uint8_t *)&batt, sizeof(batt));             //Mod
}

温度計はセンサーから取得したtemperatureを、IEEE11073形式の値temp_ieee11073に変換したのち、ペイロードthermTempPayloadに格納しています。そして、updateCharacteristicValueメソッドでCharacteristicの値を更新しています。

体温計(HTP)以外のプロファイルや、温度の取得方法が異なる場合は、このupdateServiceValuesメソッドを修正すればよさそうです。

まとめ

mbed HRM1017を使ったBluetooth LEデバイスの作成では、既存の公開プログラムを利用して手軽にプロトタイプを作ることができました。これは公開されたプログラム・ライブラリを活用できるmbedの開発環境のおかげだと思います。

mbed HRM1017の使い方はまだそのさわりを見ただけで、まだ調べることがたくさんあります。その結果はこのブログで随時紹介できればと思います。

mbed Advent Calendar 2014は続く

mbed Advent Calendar 2014はまだまだ前半戦で、この先も興味深い記事が公開されるのを楽しみにしています。 明日の10日目はアーム株式会社(@arm_link)さんです。よろしくお願いします。