記事の概要
NordicのBLEモジュール nRF52を用いてMTUを取得させます。
MTUとは何か
BLEにおいて、セントラル機器とペリフェラル機器間ではBLE通信(NotifyやWrite)でパケットを送受信します。
このパケットの上限サイズがMTU(Maximum Transmission Unit)です。
MTUのサイズ
BLE 4.0においてMTUは20バイトです。
そして、BLE 4.2においてMTUは257バイトになりました。
ですが、これは飽くまでも規格値の上限であり、実際にはBLE接続機器の性能に依存して150バイト前後にまで下がります。
MTUのサイズを取得する
BLEモジュールがセントラル機器と接続する時、相手のMTUに合わせてパケット分割したい場合があります。
例えば、1000バイトのデータを送信したい時、今回BLE接続したiPhoneはMTUが100バイトだからパケットを10分割して、次にBLE接続したiPhoneはMTUが200バイトだからパケットを5分割する、といったように臨機応変にMTUサイズに合わせて通信したいことがあります。
ただし、MTUサイズを取得した際、オペコードに1バイト、ハンドルに2バイトを使用するので、通信データに使用できるのは、取得値から3バイトを引いた値になります。
MTU に基づいた最大パケットサイズ - KBA203312 - Community Translated (JA)
実装
Q&Aサイトの「negotiating-max-mtu-size」を参照して作成したコードの一部を以下に示します。
#define OPCODE_LENGTH 1
#define HANDLE_LENGTH 2
static uint16_t mtu_max_size;
void gatt_evt_handler(nrf_ble_gatt_t * p_gatt, nrf_ble_gatt_evt_t const * p_evt)
{
if ((m_conn_handle == p_evt->conn_handle) && (p_evt->evt_id == NRF_BLE_GATT_EVT_ATT_MTU_UPDATED))
{
mtu_max_size = p_evt->params.att_mtu_effective - OPCODE_LENGTH - HANDLE_LENGTH;
}
}
/**@brief Function for initializing the GATT module.
*/
static void gatt_init(void)
{
ret_code_t err_code = nrf_ble_gatt_init(&m_gatt, gatt_evt_handler);
APP_ERROR_CHECK(err_code);
err_code = nrf_ble_gatt_att_mtu_periph_set(&m_gatt, NRF_SDH_BLE_GATT_MAX_MTU_SIZE); // UPDATE MTU HERE
APP_ERROR_CHECK(err_code);
}
gatt_init()
関数に割り込みハンドラーgatt_evt_handler
を設定します。
割り込みハンドラーでは、p_evt->params.att_mtu_effective
によりBLE接続したCentral側のMTUを取得できます。
これからオペコードの1バイトとハンドル部の2バイトを引いた値が、1パケットに乗せられるデータサイズの上限になります。
最後にnrf_ble_gatt_att_mtu_periph_set()
を実行することで、ペリフェラル側のMTUが更新されます。
ここでは、NRF_SDH_BLE_GATT_MAX_MTU_SIZE
をsdk_config.hにおいて247に設定しています。
規格では257バイトなのに247バイトにしている理由は、Data channel PDU headerの2バイト、Data channel PDU MICの4バイト、Basic L2CAP PDU headerの4バイトを除いたのが、257-2-4-4=247バイトだからです。
詳細は以下をご覧ください。
https://devzone.nordicsemi.com/f/nordic-q-a/28640/maximum-att-mtu-for-ble-nus
これを実行すると、BLE接続するたびに、Central機器に依存して、mtu_max_size
が20、138、152と様々な値に変動し、Central機器のMTUを知ることができます。