SoftDevice の設定 - ble/config.rs
それでは、SoftDevice を実行するために必要な設定を行いましょう。以下のコードはすべて ble/config.rs ファイル内に記述します。
インポート
#![allow(unused)] fn main() { use {defmt_rtt as _, panic_probe as _}; use core::mem; use nrf_softdevice::raw; }
デバイス名
まず、デバイス名を定義します。これは、他のデバイスが micro:bit をスキャンしたときに表示されます。有効な名前であれば自由に変更できます。
#![allow(unused)] fn main() { pub const DEVICE_NAME: &str = "implRust"; }
クロック設定
Bluetooth では、アドバタイズ、スキャン、接続の維持といった処理を行うために正確なタイミングが必要です。そのタイミングは低周波クロックに依存します。ここでは、クロックソースとして内部 RC 発振器を設定します。
#![allow(unused)] fn main() { const fn clock_config() -> Option<raw::nrf_clock_lf_cfg_t> { Some(raw::nrf_clock_lf_cfg_t { source: raw::NRF_CLOCK_LF_SRC_RC as u8, // rc_ctiv: 16, rc_ctiv: 4, rc_temp_ctiv: 2, // accuracy: raw::NRF_CLOCK_LF_ACCURACY_500_PPM as u8, accuracy: raw::NRF_CLOCK_LF_ACCURACY_20_PPM as u8, }) } }
内部 RC クロックはそれほど高精度ではなく、時間の経過や温度変化に伴って少しずつ誤差が生じることがあります(時間経過によるドリフト)。そのため、定期的に再較正するように設定します。
-
rc_ctiv は、タイミングのドリフトを補正するためにチップが定期的な再較正をどの程度の頻度で実行するかを制御します。
-
rc_temp_ctiv は温度変化に基づく追加のトリガーを設定するもので、顕著な温度変動を検出した場合にチップが再較正を行います。
-
accuracy: 20 PPM は、このクロックにどの程度の精度を期待できるかを BLE スタックに伝えます。PPM の値が低いほどタイミング精度が高くなり、Bluetooth がより安定して動作しやすくなります。
GAP 接続設定
これは、デバイスが処理できる Bluetooth 接続数と、各接続に割り当てる時間を設定します。
#![allow(unused)] fn main() { const fn gap_conn_config() -> Option<raw::ble_gap_conn_cfg_t> { Some(raw::ble_gap_conn_cfg_t { conn_count: 2, event_length: 24, }) } }
-
conn_count: 2 は、デバイスが最大 2 つの Bluetooth 接続を同時に維持できることを意味します。
-
event_length は、Bluetooth の各通信サイクルで各接続を処理するためにチップが確保する時間を設定します(単位は 1.25 ミリ秒)。値を 24 にすると 1 回の間隔あたり約 30 ミリ秒となり、接続を切らさずに安定してデータを送受信するのに十分な時間になります。
GAP デバイス名設定
この設定では、他のデバイス(たとえばスマートフォン)がスキャン時に見る Bluetooth デバイス名を設定します。SoftDevice はデフォルトでは汎用的な名前を使用しますが、ここでは独自の名前で上書きします。
#![allow(unused)] fn main() { fn gap_device_name() -> Option<raw::ble_gap_cfg_device_name_t> { Some(raw::ble_gap_cfg_device_name_t { p_value: DEVICE_NAME.as_ptr() as _, current_len: DEVICE_NAME.len() as u16, max_len: DEVICE_NAME.len() as u16, write_perm: unsafe { mem::zeroed() }, _bitfield_1: raw::ble_gap_cfg_device_name_t::new_bitfield_1( raw::BLE_GATTS_VLOC_STACK as u8, ), }) } }
- p_value は、独自のデバイス名 ("implRust") が格納されているメモリを指します。
- current_len と max_len は、名前の現在の長さと最大長を定義します。
- write_perm は、接続しているクライアントが名前を変更できるかどうかを制御します。ここでは
zeroed()を使って書き込みアクセスを無効にしています。 - _bitfield_1 には、名前の保存場所を設定する vloc が含まれます。ここでは
BLE_GATTS_VLOC_STACKを使用しており、これは名前がフラッシュメモリ(不揮発性メモリ)に保存され、実行時には書き換えできないことを意味します。
GAP ロール数
この設定では、デバイスが同時に処理できる Bluetooth の動作数の上限を定めます。
#![allow(unused)] fn main() { const fn gap_role_count() -> Option<raw::ble_gap_cfg_role_count_t> { Some(raw::ble_gap_cfg_role_count_t { adv_set_count: raw::BLE_GAP_ADV_SET_COUNT_DEFAULT as u8, periph_role_count: raw::BLE_GAP_ROLE_COUNT_PERIPH_DEFAULT as u8, }) } }
-
adv_set_count は、使用するアドバタイズハンドルの数です。ここではデフォルト値のままにしています。
-
periph_role_count は、micro:bit がペリフェラルとして動作しているときに同時接続できるデバイス数を定義します。ここでもデフォルト設定(1 接続を許可)を使用します。
GATT 接続設定
これは、ATT(Attribute Protocol)パケットのサイズを設定します。簡単に言うと、BLE で一度に送受信できるデータ量を制御します。
#![allow(unused)] fn main() { const fn gatt_conn_config() -> Option<raw::ble_gatt_conn_cfg_t> { Some(raw::ble_gatt_conn_cfg_t { att_mtu: 256 }) } }
att_mtu は Attribute Maximum Transmission Unit の略です。これは、デバイス間で一度に送受信できるデータ量を定義します。デフォルトの MTU は 23 バイトで、かなり小さい値です。ここではこれを 256 まで増やし、1 つの BLE パケットでより大きなデータ塊を送受信できるようにしています。これによりスループットが向上し、小さな断片を送る際のオーバーヘッドを減らせます
GATT 属性テーブルサイズ
この設定では、属性テーブルのサイズを設定します。属性テーブルは、サービス定義、キャラクタリスティック、ディスクリプタなど、他のデバイスが BLE 経由で読み書きできるデータを格納するために使われます。
#![allow(unused)] fn main() { const fn gatts_attr_tab_size() -> Option<raw::ble_gatts_cfg_attr_tab_size_t> { Some(raw::ble_gatts_cfg_attr_tab_size_t { attr_tab_size: raw::BLE_GATTS_ATTR_TAB_SIZE_DEFAULT, }) } }
ここではデフォルトサイズを使っています。通常はほとんどの小規模な BLE 構成で十分です。ただし、多くのサービスを追加する場合は、これを増やす必要があるかもしれません。サイズは 4 の倍数にしてください。そうしないとエラーになります。
まとめ
この関数は、上で定義したすべての設定をまとめて、Softdevice::enable() に渡す最終的な設定を構築します
#![allow(unused)] fn main() { // SoftDevice の設定 pub fn softdevice_config() -> nrf_softdevice::Config { nrf_softdevice::Config { clock: clock_config(), conn_gap: gap_conn_config(), conn_gatt: gatt_conn_config(), gatts_attr_tab_size: gatts_attr_tab_size(), gap_role_count: gap_role_count(), gap_device_name: gap_device_name(), ..Default::default() } } }
config.rs の完全な内容
```rust
use {defmt_rtt as _, panic_probe as _};
use core::mem;
use nrf_softdevice::raw;
pub const DEVICE_NAME: &str = "implRust";
const fn clock_config() -> Option<raw::nrf_clock_lf_cfg_t> {
Some(raw::nrf_clock_lf_cfg_t {
source: raw::NRF_CLOCK_LF_SRC_RC as u8,
// rc_ctiv: 16,
rc_ctiv: 4,
rc_temp_ctiv: 2,
// accuracy: raw::NRF_CLOCK_LF_ACCURACY_500_PPM as u8,
accuracy: raw::NRF_CLOCK_LF_ACCURACY_20_PPM as u8,
})
}
const fn gap_conn_config() -> Option<raw::ble_gap_conn_cfg_t> {
Some(raw::ble_gap_conn_cfg_t {
conn_count: 2,
event_length: 24,
})
}
const fn gatt_conn_config() -> Option<raw::ble_gatt_conn_cfg_t> {
Some(raw::ble_gatt_conn_cfg_t { att_mtu: 256 })
}
fn gap_device_name() -> Option<raw::ble_gap_cfg_device_name_t> {
Some(raw::ble_gap_cfg_device_name_t {
p_value: DEVICE_NAME.as_ptr() as _,
current_len: DEVICE_NAME.len() as u16,
max_len: DEVICE_NAME.len() as u16,
write_perm: unsafe { mem::zeroed() },
_bitfield_1: raw::ble_gap_cfg_device_name_t::new_bitfield_1(
raw::BLE_GATTS_VLOC_STACK as u8,
),
})
}
const fn gap_role_count() -> Option<raw::ble_gap_cfg_role_count_t> {
Some(raw::ble_gap_cfg_role_count_t {
adv_set_count: raw::BLE_GAP_ADV_SET_COUNT_DEFAULT as u8,
periph_role_count: raw::BLE_GAP_ROLE_COUNT_PERIPH_DEFAULT as u8,
})
}
const fn gatts_attr_tab_size() -> Option<raw::ble_gatts_cfg_attr_tab_size_t> {
Some(raw::ble_gatts_cfg_attr_tab_size_t {
attr_tab_size: raw::BLE_GATTS_ATTR_TAB_SIZE_DEFAULT,
})
}
// SoftDevice の設定
pub fn softdevice_config() -> nrf_softdevice::Config {
nrf_softdevice::Config {
clock: clock_config(),
conn_gap: gap_conn_config(),
conn_gatt: gatt_conn_config(),
gatts_attr_tab_size: gatts_attr_tab_size(),
gap_role_count: gap_role_count(),
gap_device_name: gap_device_name(),
..Default::default()
}
}