BLEを使用してI2Cセンサのデータをモバイルアプリに転送する
はじめに
現在、IoTデバイスの開発が盛んに行われております。この記事では、無線通信規格の1つであるBLE(Bluetooth Low Energy)を使用して、組み込みアプリケーションデバイスで取得したI2Cセンサのデータを、モバイルアプリケーションに転送する方法をご紹介いたします。
概要
組み込みアプリケーションは、弊社製品のμC3/Compact及びμC3/BLE Stackと、STマイクロエレクトロニクス社のツールを使用して実装します。各種モバイルアプリケーションは、Apache Cordovaというモバイルアプリケーション開発フレームワークを使用します。
BLEを用いたシステム開発を始める際の参考になればと思います。
文末には用語集を記述しましたので、こちらも参照ください。
ハードウェア
| ボード(ホストCPU) | STM32L496G-Discovery |
| ボード(BLEコントローラ) | WYSBHVGXG |
| I2Cセンサ | AE-SHT31 |
| SD – micro SD 逆変換基板 | SDB-TFA |
STM32L496G-DiscoveryとWYSBHVGXGを、SDB-TFAを使用して接続します。
STM32L496G-Discoveryには、ARM Coretex-M4のチップ:STM32L496AGが搭載されており、WYSBHVGXG及びAE-SHT31の制御を行います。
WYSBHVGXGには、NXP製のBLEコントローラチップ:88W8887が搭載されています。SDIOを介して88W8887と相互に通信し、BLE機能を制御します。
AE-SHT31には、SENSIRION 社の高精度温湿度センサ :SHT31-DISが搭載されています。I2Cを介して、SHT-31-DISから温湿度データを取得します。
以下、STM32L496G-DiscoveryをL496、AE-SHT31をSHT31とします。
ハードウェア構成図を以下に示します。

ソフトウェア
組み込みデバイス
| RTOS | μC3/Compact |
| BLE ホストスタック | μC3/BLE Stack |
| IDE | EWARM 8.50.6 |
μC3/Compactは、eForce製のμITRON仕様RTOSです。
μC3/BLE Stackは、eForce製のBLEホストスタックです。
一般的にBLEアーキテクチャは、アプリケーション層、ホスト層及びコントローラ層の3つに分けられます。μC3/BLE Stackはホスト層の機能を提供します。コントローラ層の機能を提供するデバイスと接続することで、BLEの機能が使用可能になります。μC3/BLE Stackは、コントローラ層とのインターフェースとして、SDIOをサポートしています。
組み込みデバイスのソフトウェア構成図を以下に示します。

モバイルアプリ
| OS | Android 11 |
| IDE | Android Studio Arctic Fox | 2020.3.1 |
| フレームワーク | Apache Cordova 10.0.0 |
| Cordova プラグイン | bluetoothle v6.6.1 |
このチュートリアルでは、Android StudioとApache Cordovaを使用して、Android上で動作するモバイルアプリケーションを作成します。
Apache Cordovaは、オープンソースのモバイル向けアプリケーション開発フレームワークです。
bluetoothleは、Cordovaから、デバイスのBLE機能制御を可能にするプラグインです。 Apache Cordova + Android Studioを使用したアプリケーション開発環境構築については、こちらのチュートリアルでご紹介しておりますので、必要に応じて参照してください。
処理の概要
- μC3/Compact(以下、μC3とします)の周期ハンドラを使用して、8秒周期でSHT31から温度と湿度を取得します。
- μC3/BLE Stack(以下、BLEスタックとします)を使用して、温度と湿度をGATTサーバで公開します。GATTサーバで公開する温湿度は8秒周期で更新します。このとき、サービスUUID及びキャラクタリスティックUUIDは、自動生成した適当な値を使用します。
- モバイルアプリで近くのBLEデバイスを検出し、コネクションします。
- コネクションが確立したら、同じく8秒周期でGATTサーバの温湿度キャラクタリスティック値にreadアクセスし、readした値を表示します。
ハードウェアの接続
L496とWYSBHVGXGをSD – micro SD逆変換基板で接続します。また、L496とSHT31をジャンパー線で接続します。

温湿度取得用I2C設定
L496のI2C1を使用します。CubeMXを使用して、PB7、PB8のPull-up, down設定をPull-upに設定してください。

BLEスタック設定
μC3/Configuratorを使用して、BLEスタックのコンフィギュレーションを行います。
今回はすべてデフォルト値のまま使用します。

OSリソース
使用するμC3リソースは以下に示します(BLEスタックが使用するOSリソースの説明は省略します)
タスク
| ID | 処理概要 |
|---|---|
| ID_SENSOR_TASK | ・温湿度センサの値を、I2Cを使用して取得します。 ・固定長メモリプール及びメールボックスを使用して、温湿度の値をID_BLE_APP_TSKに送信します。 |
| ID_BLE_APP_TSK | ・ID_SENSOR_TASKから受信した温湿度の値で、GATTサーバのキャラクタリスティック値を更新します。 ・モバイルアプリからのreadアクセスに対する応答処理を行います。 |
イベントフラグ
| ID | 処理概要 |
|---|---|
| ID_SENSOR_MBX | ・周期ハンドラ:ID_SENSOR_CYCにで、イベントフラグをセットします。ID_SENSOR_TASKは、このイベントフラグで待ち状態が解除され、温湿度の取得処理を開始します。 |
メールボックス
| ID | 処理概要 |
|---|---|
| ID_TRH_MBX | ・ID_SENSOR_TASK – ID_BLE_APP_TSK間通信に使用します。 |
固定長メモリプール
| ID | 処理概要 |
|---|---|
| ID_TRH_MPF | ・ID_SENSOR_TASKで確保し、温湿度の値を格納します。メールボックス:ID_TRH_MBXを使用して、ID_BLE_APP_TSKに送信します。 ・ID_BLE_APP_TSKは、受信した温湿度の値でGATTサーバの値を更新した後、開放します。 |
周期ハンドラ
| ID | 処理概要 |
|---|---|
| ID_SENSOR_CYC | ・8秒周期で起動されます。イベントフラグ:ID_SENSOR_FLGをセットし、ID_SENSOR_TASKの待ち状態を解除します。 |
ID_SENSOR_TASKの処理
void getSensorValue(VP_INT exinf)
{
const UB SENSOR_ADDR = 0x45;
while (1) {
UserMSG *umsg = NULL;
FLGPTN flgptn;
UB cmd[2] = {0x2c, 0x06};
SIZE cmdsize = sizeof(cmd) / sizeof(cmd[0]);
UB ret[6];
SIZE retsize = sizeof(ret) / sizeof(ret[0]);
INT ST;
INT SRH;
double T;
double RH;
// メモリ確保
get_mpf(ID_TRH_MPF, (VP *)&umsg);
// センサの反応時間待機
wai_flg(ID_SENSOR_FLG, 0x01, TWF_ANDW, &flgptn);
// I2C通信
HAL_I2C_Master_Transmit(&hi2c1, SENSOR_ADDR << 1, &cmd[0], cmdsize, 100);
HAL_I2C_Master_Receive(&hi2c1, SENSOR_ADDR << 1, ret, retsize, 100);
// 取得バイナリを文字列に変換
ST = ret[0] << 8 | ret[1];
SRH = ret[3] << 8 | ret[4];
T = 175 * ST / (pow(2.0, 16.0) - 1) - 45;
RH = 100 * SRH / (pow(2.0, 16.0) - 1);
snprintf((char *)umsg->T, 7, "%.2f", T );
snprintf((char *)umsg->RH, 6, "%.2f", RH);
// ID_BLE_APP_TSKに送信
snd_mbx(ID_TRH_MBX, (T_MSG *)umsg);
}
}
1. get_mpf()をコールしてUserMSG用のメモリを確保します。UserMSGは、ID_SENSOR_TASK – ID_BLE_APP_TSKタスク間で温湿度情報をやり取りするための構造体です。
UserMSGは、メールボックスを使用してID_SENSOR_TASKから ID_BLE_APP_TSKに渡すため、構造体の先頭に必ずT_MGS型の変数を用意します。T、RHはそれぞれ温度、湿度の値を文字列で格納するための配列です。SHT31で計測可能な温度、湿度の範囲はそれぞれ”125.00”~”-40.00”[℃]、湿度の範囲は”100.00”~”0.00”[%]であるため、7バイトの配列としています。
typedef struct
{
T_MSG msg;
UB T[7];
UB RH[7];
} UserMSG;
2. wai_flg()をコールして、SHT31の反応時間(8秒)を待ちます。ID_SENSOR_TASKの待ち状態は、8秒周期で起動する周期ハンドラ:ID_SENSOR_CYCのset_flg()コールによって解除されます。
3. HAL_I2C_Master_Transmit()、HAL_I2C_Master_Receive()を使用してI2C通信を実施し、SHT31から値を取得します。こちらの関数は、STマイクロエレクトロニクス社が提供しているHALドライバの関数です。HALドライバを使用することにより、ペリフェラルの機能を簡単に使用することができます。
4. SHT31から取得した値をSHT31の仕様にしたがって変換し、snd_mbx()によってID_BLE_APP_TSKに送信して、次のループに入ります。
ID_BLE_APP_TSKの処理
メイン処理
UB ble_temperature_buf[7] = {0};
UB ble_humidity_buf[7] = {0};
…
void ble_app_tsk(VP_INT exinf)
{
ER ercd;
T_BLE_INI_CFG cfg;
UserMSG umsg;
/* Initialize device driver */
ercd = dma_ini();
if (ercd != E_OK) {
goto ERR;
}
ercd = sdio_ini();
if (ercd != E_OK) {
goto ERR;
}
ercd = _ddr_sdio_ini(SDIO_DEV_ID);
if (ercd != E_OK) {
goto ERR;
}
/* Initialize BLE stack */
cfg.app_cbk = ble_app_callback;
ercd = ble_ini(&cfg);
if (ercd != E_OK) {
goto ERR;
}
/* start peripheral role */
ercd = ble_peripheral_start();
if (ercd != E_OK) {
goto ERR;
}
for(;;) {
rcv_mbx(ID_TRH_MBX, (T_MSG **)&umsg);
ble_strcpy((char *)&ble_temperature_buf[0], (const char *)umsg->T);
ble_strcpy((char *)&ble_humidity_buf[0], (const char *)umsg->RH);
rel_mpf(ID_TRH_MPF, (UserMSG *)umsg);
}
ERR:
ext_tsk();
return;
}
- BLEスタックの動作に必要となるDMA及びSDIOドライバを初期化します。こちらのドライバはeForce製です。
- BLEスタック初期化APIであるble_ini()を実行します。このとき、BLEスタックにアプリケーションコールバック関数を登録します。アプリケーションコールバック関数の詳細については後述します。
- ble_peripheral_start()を実行し、アドバタイジングや、GATTサーバの設定を行います。こちらも詳細は後述します。
- 無限ループにて、rcv_mbx()でID_SENSOR_TASKからUserMSGの受信を待ちます。待ち状態が解除されたら、温度、湿度の値をble_temperature_buf, ble_humidity_bufにコピーします。最後にrel_mpf()をコールして、ID_SENSOR_TASKで確保した固定長メモリプールを開放し、次のループに入ります。
アプリケーションコールバック関数
void ble_app_callback(T_BLE_APP_EVENT *evt)
{
UB status;
if (evt->group == BLE_APP_EVG_DEV) {
if (evt->type == BLE_APP_ADV_TER_EVT) {
(void)ble_ena_adv(0, &status);
} else if (evt->type == BLE_APP_CON_EVT) {
(void)ble_dis_adv(&status);
} else if (BLE_APP_CON_CLS_EVT) {
(void)ble_ena_adv(0, &status);
}
}
return;
}
BLEスタックは、コールバック関数を使用して、各種イベントの発生をアプリケーションに通知します。今回実装したアプリケーションコールバック関数は、以下のような処理になっています。
BLEコネクションが確立されたことを通知するイベント:BLE_APP_CON_EVTが発生した場合、ble_dis_adv()をコールして、アドバタイズパケットの送信を停止します。アドバタイズパケット送信がタイムアウトを通知するイベント:BLE_APP_ADV_TER_EVT及びBLEコネクションが切断されたことを通知するイベントBLE_APP_CON_CLS_EVTが発生した場合、ble_ena_adv()をコールして、アドバタイズパケットの送信を再開します。
ble_peripheral_start()
static ER ble_peripheral_start(void)
{
ER ercd;
T_BLE_ADV_CFG adv;
T_BLE_AD_DATA ad_data;
UB status;
/* Device nameを設定 */
ercd = ble_set_gap_srv_device_name(BLE_DEVICE_NAME,
(UH)ble_strlen(BLE_DEVICE_NAME),
BLE_CHAR_PERM_RD);
if (ercd != E_OK) return ercd;
ercd = ble_set_gap_srv_appearance(BLE_SENSOR_APPEARANCE, BLE_CHAR_PERM_RD);
if (ercd != E_OK) return ercd;
ercd = ble_ini_sensor_srv();
if (ercd != E_OK) return ercd;
/* Advertisementの設定 */
adv.adv_int_min = 0;
adv.adv_int_max = 0;
adv.own_addr_type = BLE_OWN_ADDR_TYPE_PUBLIC;
adv.peer_addr_type = BLE_PEER_ADDR_TYPE_PUBLIC;
ble_memset(adv.peer_addr, 0, 6);
adv.adv_ch_map = BLE_ADV_CH_MAP_ALL;
adv.adv_filter = BLE_ADV_FILTER_ALL;
ercd = ble_cfg_adv(BLE_DISC_MODE_GEN, BLE_CONN_MODE_UND, &adv, &status);
if (ercd != E_OK) return ercd;
/* Advertisement dataを設定 */
set_ad_ini(&ad_data);
set_ad_local_name(&ad_data, "sensor", 6);
set_ad_uuid(&ad_data, AD_TYPE_UUID_128_COMP, &ble_sensor_uuid, 1);
ercd = ble_set_adv_dat(ad_data.data, ad_data.len, &status);
if (ercd != E_OK) return ercd;
/* Advertisementを開始 */
return ble_ena_adv(0, &status);
}
ble_peripheral_start()では、ペリフェラルとしての動作に関する設定を行います。
- ble_set_gap_srv_device_name()、ble_set_gap_srv_appearance()をコールして、GAPサービスのDevice Nameキャラクタリスティック、Appearanceキャラクタリスティックを設定します。
- ble_ini_sensor_srv()を実行して、公開するサービスの設定を行います。ble_ini_sensor_srv()については後述します。
- Advertisementの設定を行うためのAPI:ble_cfg_adv()をコールします。ADV_CFG構造体:advの各種変数に各種設定値をセットし、ble_cfg_adv()に渡すことで、Advertisementの設定を行うことができます。
- set_ad_ini(), set_ad_local_name(), set_ad_uuid()を実行し、Advertising Dataを設定します。ここでは、Advertising DataにLocal NameとService UUIDを設定しています。
- ble_ena_adv()をコールし、ble_peripheral_start()を終了します。ble_ena_adv()は、Advertisementを開始するためのAPIです。
ble_ini_sensor_srv()
static T_BLE_SRV gt_ble_sensor_srv;
static T_BLE_CHAR gt_ble_temperature_char;
static T_BLE_CHAR_VALUE gt_ble_temperature_char_value;
static T_BLE_CHAR gt_ble_humidity_char;
static T_BLE_CHAR_VALUE gt_ble_humidity_char_value;
T_BLE_UUID ble_sensor_uuid = {
UUID_128BIT,
.value.uuid_128bit = {0xFE,0x65,0x27,0x44,0xBF,0x90,0x93,0xAE,0xDB,0x49,0x50,0x74,0x00,0x21,0x47,0xD3}
};
T_BLE_UUID ble_temperature_sensor_uuid = {
UUID_128BIT,
.value.uuid_128bit = {0xFE,0x65,0x27,0x44,0xBF,0x90,0x93,0xAE,0xDB,0x49,0x50,0x74,0x01,0x21,0x47,0xD3}
};
T_BLE_UUID ble_humidity_sensor_uuid = {
UUID_128BIT,
.value.uuid_128bit = {0xFE,0x65,0x27,0x44,0xBF,0x90,0x93,0xAE,0xDB,0x49,0x50,0x74,0x02,0x21,0x47,0xD3}
};
…
static ER ble_ini_sensor_srv(void)
{
ER ercd;
/* 1. Add Service */
ble_memset((char *)>_ble_sensor_srv, 0, sizeof(gt_ble_sensor_srv));
gt_ble_sensor_srv.cfg.p_type = &ble_primary_srv_uuid;
gt_ble_sensor_srv.cfg.p_value = &ble_sensor_uuid;
ercd = ble_add_srv(>_ble_sensor_srv);
if (ercd != E_OK) return ercd;
/* 2. Add temperature sensor characteristic */
/* Add Characteristics attribute */
ble_memset((char *)>_ble_temperature_char,
0,
sizeof(gt_ble_temperature_char));
gt_ble_temperature_char.cfg.property = BLE_CHAR_PERM_RD;
gt_ble_temperature_char.p_char_uuid = &ble_char_uuid;
ercd = ble_add_char(>_ble_sensor_srv, >_ble_temperature_char);
if (ercd != E_OK) return ercd;
/* Add Characteristics value attribute */
ble_memset((char *)>_ble_temperature_char_value,
0,
sizeof(gt_ble_temperature_char_value));
gt_ble_temperature_char_value.cfg.p_type = &ble_temperature_sensor_uuid;
gt_ble_temperature_char_value.cfg.cbk = ble_temperature_sensor_cbk;
ercd = ble_add_char_val(>_ble_temperature_char,
>_ble_temperature_char_value);
if (ercd != E_OK) return ercd;
/* 3. Add humidity sensor characteristic */
/* Add Characteristics attribute */
ble_memset((char *)>_ble_humidity_char, 0, sizeof(gt_ble_humidity_char));
gt_ble_humidity_char.cfg.property = BLE_CHAR_PERM_RD;
gt_ble_humidity_char.p_char_uuid = &ble_char_uuid;
ercd = ble_add_char(>_ble_sensor_srv, >_ble_humidity_char);
if (ercd != E_OK) return ercd;
/* Add Characteristics value attribute */
ble_memset((char *)>_ble_humidity_char_value,
0,
sizeof(gt_ble_humidity_char_value));
gt_ble_humidity_char_value.cfg.p_type = &ble_humidity_sensor_uuid;
gt_ble_humidity_char_value.cfg.cbk = ble_humidity_sensor_cbk;
ercd = ble_add_char_val(>_ble_humidity_char,
>_ble_humidity_char_value);
if (ercd != E_OK) return ercd;
/* 4. register the service */
return ble_reg_srv(>_ble_sensor_srv);
}
ble_ini_sensor_srv()では、GATTサービスの追加プロセスを実行します。
- 今回使用するサービス及びサービスが保持するキャラクタリスティックの設定を行うための構造体を、グローバル宣言で確保します。それぞれT_BLE_SRVはサービス、T_BLE_CHARはキャラクタリスティック、T_BLE_CHAR_VALUEはキャラクタリスティック値の設定のための構造体です。今回のアプリケーションでは、1つのサービスと、温度用、湿度用でそれぞれキャラクタリスティック及びキャラクタリスティック値を2つ用意します。
- サービス追加プロセスでは、T_BLE_SRVの設定を行い、ble_add_srv()をコールします。ble_add_srv()は、サービス定義プロセスを開始するためのAPIです。p_type, p_valueにそれぞれサービスタイプ、サービスUUIDを設定します。
- T_BLE_CHARの設定を行い、ble_add_char()をコールします。ble_add_char()は、サービスにキャラクタリスティックを追加するためのAPIです。cfg.property, p_char_uuidそれぞれキャラクタリスティックプロパティ、キャラクタリスティックUUIDを設定します。
- T_BLE_CHAR_VALUEの設定を行い、ble_add_char_val()をコールします。ble_add_char_val()は、キャラクタリスティックにキャラクタリスティック値を追加するためのAPIです。cfg.p_type, cfg.cbkにそれぞれキャラクタリスティック値UUID、キャラクタリスティックにアクセスされたときのコールバック関数を設定します。
- 今回は温度キャラクタリスティックと湿度キャラクタリスティックを用意するため、2~4をそれぞれに対して実行します。
なお、今回使用するサービスUUID及びキャラクタリスティックUUIDは、ツールを使用して生成したランダムな値を使用しております。
温度キャラクタリスティック値のコールバック関数
UH ble_temperature_sensor_cbk(T_BLE_GATT_HND_PARAM *param)
{
UH dat_len = 0;
switch (param->type) {
case BLE_CHAR_CBK_READ:
dat_len = ble_strlen((char *)&ble_temperature_buf[0]);
ble_strcpy((char *)param->p_dat, (char *)&ble_temperature_buf[0]);
break;
default:
param->error = BLE_ATT_ERR_WRITE_NOT_PERMITTED;
break;
}
return dat_len;
}
温度キャラクタリスティック値へアクセスがあったときの処理を記述したコールバック関数です。param->typeがBLE_CHAR_CBK_READ(readアクセス)があった場合、dat_lenにble_temperature_bufに格納されている文字列長、param->p_datにble_temperature_bufの値をコピーすることで、readアクセスを要求したデバイスにble_temperature_bufの値を返すことができます。つまり、Cordovaで実装したアプリケーションが、温度キャラクタリスティック値にreadアクセスを行うことで、L496が保持している温度の値を取得することができます。
ble_ad_char()コール時に、gt_ble_temperature_char.cfg.property = BLE_CHAR_PERM_RD(read許可)としていますので、read以外のアクセスがあった場合はエラー処理としています。
湿度キャラクタリスティック値のコールバック関数も同様の処理としています。
Cordovaアプリケーションの処理
このチュートリアルでは、CordovaのBluetoothle(https://github.com/randdusing/cordova-plugin-bluetoothle)というプラグインを使用します。下記のコマンドを実行して、Bluetoothleをインストールします。
cordova plugin add cordova-plugin-bluetoothle
bluetoothleを使用したアプリケーションの処理概要を説明します。各メソッドの詳細については、上記URLを参照してください。
- bluetootleの初期化メソッド、bluetoothle.initialize(initializeResult, params)を実行します。
- スキャン開始メソッド、bluetoothle.startScan(startScanSuccess, startScanError, params)を実行します。このとき、paramsのservicesにサービスUUIDを設定することで、スキャン対象のデバイスをフィルタリングすることができます。また、startScanResultのresultから、スキャンしたデバイスのDevice Nameやデバイスアドレスといった情報が得られます。
- コネクション開始メソッド、bluetoothle.connect(connectSuccess, connectError, params)を実行します。このとき、paramsのaddressにstartScanResultから取得したデバイスアドレスを指定します。
- 3でコネクションに成功したら、デバイスのサービス、キャラクタリスティック及びキャラクタリスティックディスクリプタを検出するメソッド、bluetoothle.discover(discoverSuccess, discoverError, params)を実行します。このとき、paramsのaddressに、startScanResultから取得したデバイスアドレスを指定します。discoverSuccessのresult.services.uuidからサービスUUID、result.services.characteristic.uuidからキャラクタリスティックUUIDを取得できます。
- キャラクタリスティック値へのreadアクセスメソッド、bluetoothle.read(readSuccess, readError, params)を8秒周期で実行するインターバルを起動します。このとき、paramsのaddress, service, characteristicにそれぞれデバイスアドレス、サービスUUID、キャラクタリスティックUUIDを指定します。readアクセスに成功すると、readSuccessのstatus.valueからreadした値を取得できます。
- raedSuccessのstatus.valueは、base64でエンコードされているため、bluetoothle.encodedStringToBytes(string)にstatus.valueを渡し、デコードします。デコードした値をテキストボックスに表示します。
- キャラクタリスティック値のreadを8秒おきに実行し、取得した値でテキストボックスの表示値を更新します。
モバイルアプリの動作概要を以下に示します。

アプリ起動時の表示です。右上のStart Scanをタップすると、周辺デバイスのスキャンを開始します。Start ScanはStop Scanという表示に変わります。Stop Scanをタップすることで、スキャンを停止します。

検出したデバイスを表示します。connectをタップすることで、そのデバイスとのコネクションを開始します。
※sensorの下部には、BLEコントローラのパブリックデバイスアドレスが表示されますが、セキュリティの観点から黒塗りとしています。

前の画面で、connectをタップすると、表示がconnect→connecting→closeの順番で変化します。表示がcloseになると、コネクションの確立に成功したことを示します。コネクションに成功すると、デバイスの温湿度キャラクタリスティック値をreadできるようになります。
closeをタップすると、デバイスとのコネクション切断を開始します。

コネクションが確立した状態で、左側のタブをScan DeviceからMonitorに切り替えます。Temperature及びHumidity横のテキストボックスに、それぞれreadした温度キャラクタリスティック値、湿度キャラクタリスティック値が表示されます。
readアクセスを8秒周期で繰り返すので、テキストボックスの表示も8秒周期で更新されます。
まとめ
このチュートリアルでは、デバイスで取得したI2Cセンサのデータを、BLEを使用してモバイルアプリに転送する方法をご紹介いたしました。
組み込みアプリケーションは、μC3 ConfigratorでμC3及びBLEスタックのコンフィグレーションを行い、Cube MXでI2Cの設定を行いました。ID_SENSOR_TASKでI2C通信を行い、温湿度センサの値を取得し、ID_BLE_APP_TSKに送信します。ID_BLE_APP_TSKでは、ID_SENSOR_TASKから受信した値を保持します。非コネクション時はペリフェラルとして動作し、セントラルからのコネクションを待ち受けます。コネクション確立後はGATTサーバとして動作し、ID_SENSOR_TASKから受信したセンサ値を公開します。
モバイルアプリケーションは、Cordovaと、Cordovaで使用可能なプラグイン:bluetoohleを使用しました。非コネクション状態はセントラルとして動作し、スキャン及びコネクションの開始を実行します。コネクション確立後は、サービス等の検出を実行し、定期的にキャラクタリスティック値のreadし、組み込みアプリケーションが公開するセンサの値をアプリ画面に表示します。
用語集
BLE
Bluetooth Low Energy。Bluetooth 4.0で追加されたBluetooth規格の一部で、従来のBluetooth規格(Bluetooth Classic)とは互換性はありません。低消費電力、低コストといった特徴があります。
なお、μC3/BLE Stackは、Bluetooth Low Evergy 4.2をサポートしています。
GATT
General Attriblute Profile。コネクションしたBLEデバイス間でデータのやり取りを行うための、役割、データ構造、アクセス手法などの規定。GATTサーバデバイスがデータを公開し、GATTクライアントが公開されたデータにアクセスすることで、データのやり取りを行います。
サービス、キャラクタリスティック
GATTで使用されるデータ構造。GATTサーバは1つ以上のサービスを持ちます。サービスは1つ以上のキャラクタリスティックを持ちます。キャラクタリスティックは1つのキャラクタリスティック値を持ちます。キャラクタリスティック値は、アプリケーションデータとしてデバイス間でやりとりされます。
この記事では、組み込みアプリケーションが1つのサービスを持ち、そのサービスは温度キャラクタリスティックと湿度キャラクタリスティックを持ちます。それぞれのキャラクタリスティック値が、温度、湿度としてデバイス間でやりとりされます。

アドバタイジング
BLEデバイスがアドバタイズメントパケットを、ブロードキャストで送信している状態を指します。
スキャン
他のBLEデバイスが送信しているアドバタイズメントパケットをスキャンし、パケットの内容を取得します。
I2C
Inter-Integrated Circuit。シリアル通信規格の1つです。SDA(Serial Data)とSCL(Serial Clock)の2本の信号線を使用する、クロック同期方式の通信規格です。
参考文献
STM32L496G-Discovery
https://www.st.com/en/evaluation-tools/32l496gdiscovery.html
WYSBHVGXG
https://www.yuden.co.jp/jp/product/category/module/wlan/WYSBHVGXG.html
AE-SHT31
https://akizukidenshi.com/catalog/g/gK-12125/
bluetoothle
https://github.com/randdusing/cordova-plugin-bluetoothle
Bluetooth Core Specification 4.2
https://www.bluetooth.com/specifications/specs/core-specification-4-2/