GATT サービス - "service.rs"
このプログラムは、バッテリーサービスと呼ばれる単一の GATT サービスを定義します。
バッテリーサービス
nrf_softdevice クレートは、GATT サービスとキャラクタリスティックを定義するための gatt_service マクロと characteristic 属性を提供しています。サービスとそのキャラクタリスティックの両方に対して UUID を指定できます。
この例では、標準の 16 ビット UUID 0x180F を使用しています。これは、バッテリーサービスにあらかじめ定義されている識別子です。バッテリーレベルキャラクタリスティックには、現在のバッテリーレベルを表す標準 UUID 0x2A19 を使用します。
カスタムの非標準サービスまたはキャラクタリスティックを実装する場合は、代わりにカスタムの 128 ビット UUID を生成して使用する必要があります。
#![allow(unused)] fn main() { #[nrf_softdevice::gatt_service(uuid = "180f")] pub struct BatteryService { #[characteristic(uuid = "2a19", read, notify)] battery_level: i16, } }
また、read と notify キーワードを使って権限も指定します。read を指定すると、接続されたデバイスは必要なときにいつでもバッテリーレベルを取得できます。notify を指定すると、バッテリーレベルが変化したときにクライアントへ更新をプッシュできます。
GATT サーバー
GATT サーバーは、接続された GATT クライアント(たとえばスマートフォンやコンピューター)からアクセスできるサービスとキャラクタリスティックをホストする役割を担います。
BatteryService 型のフィールドを持つ Server struct を定義します。次に、この struct に #[gatt_server] 属性マクロを付けます。これにより必要なボイラープレートコードがすべて生成され、GATT サーバーを初期化するための Server::new() のような便利な関数が利用できるようになります。
#![allow(unused)] fn main() { #[nrf_softdevice::gatt_server] pub struct Server { bas: BatteryService, } }
service.rs の完全なコード
Server struct に "notify_battery_value" というメソッドを追加しました。これは接続が確立されるとバックグラウンドで実行されます。これは実際のバッテリー状態ではありません。デモのために、バッテリーレベルを増減させてシミュレートしているだけです。
この関数内では、現在のバッテリーレベルを使って self.bas.battery_level_notify() を呼び出します。クライアントがバッテリーレベルキャラクタリスティックの通知を購読していれば、自動的に更新を受け取ります。そうでない場合でも値自体は更新されており、クライアントは必要に応じて読み取れます。
#![allow(unused)] fn main() { use defmt::{info, unwrap}; use embassy_time::Timer; use nrf_softdevice::ble::Connection; #[nrf_softdevice::gatt_service(uuid = "180f")] pub struct BatteryService { #[characteristic(uuid = "2a19", read, notify)] battery_level: i16, } #[nrf_softdevice::gatt_server] pub struct Server { bas: BatteryService, } impl Server { pub async fn notify_battery_value(&self, connection: &Connection) { let mut battery_level = 100; let mut charging = false; loop { if battery_level < 20 { charging = true; } else if battery_level >= 100 { charging = false; } if charging { battery_level += 5; } else { battery_level -= 5; } match self.bas.battery_level_notify(connection, &battery_level) { Ok(_) => info!("Battery Level: {=i16}", &battery_level), Err(_) => unwrap!(self.bas.battery_level_set(&battery_level)), }; Timer::after_secs(2).await } } } }