Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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
        }
    }
}
}