Sensor APIを使って人感センサー(SENSOR-HM/ECO)を試す(・∀・)

冬休みと言うことですっかりだらけモードだったんですが、I-O DATAの人感センサー(SENSOR-HM/ECO)を入手したので、Windows 7のSensor APIを使って入力を受け取ってみました(・∀・)
http://www.iodata.jp/product/lcd/option/sensor-hmeco/


Sensor APIはCOMベースで実装されていますが、今回はWindows API Code Packを使ってC#からの利用です。
http://code.msdn.microsoft.com/WindowsAPICodePack
Code Pack中のMicrosoft.WindowsAPICodePack.Sensors.dllにSensor API関連の定義が実装されているので、それを使うと楽ちんぽんっとSensor APIの利用が可能ですね(・∀・)


っで、センサー一覧の列挙と、センサーからの値の取得を行うわけですが、これにはMicrosoft.WindowsAPICodePack.Sensors名前空間のSensorManager、Sensor、SensorReportあたりのクラスを使用します。
まずは手始めにセンサー一覧の列挙ですが、とりあえずこんな感じでOK。

using Microsoft.WindowsAPICodePack.Sensors;

namespace SensorTest
{
    static class Program
    {
        static void Main()
        {
            SensorList<Sensor> list = SensorManager.GetAllSensors();
...
        }
    }
}

SensorManager.GetAllSensors()では接続されている全てのセンサーが取得されるので、センサの種別(HumanPresence:人感センサー)を指定して取得するには、センタータイプのGuidを指定してSensorManager.GetSensorsByTypeId(Guid typeId)を使用します。
ちなみに、センタータイプのGuidについてはCode Pack中のSensorTypes.csに定義されていますので、その辺も参照してくださいな(・ω・)


また、センサーからの値の取得方法については、以下のようにSensorからSensorReportを取得して、プロパティのGuidを指定して値を取得します。

// 値の取得
Sensor sensor = list[ 0 ];

SensorReport report = sensor.DataReport;

object value = report.Values[ SensorPropertyKeys.SENSOR_DATA_TYPE_XXXX.FormatId ];

っで、今回はこれらの処理をラッピングして、以下のようなクラスを作って使用することにしました(`・ω・´)

[SensorDescription( "C138C12B-AD52-451C-9375-87F518FF10C6" )]
public class HumanPresenceSensor : Sensor
{
    public HumanPresence CurrentHumanPresence
    {
        get { return new HumanPresence( this.DataReport ); }
    }
}

public class HumanPresence
{
    private bool detected;

    public bool Detected
    {
        get { return this.detected; }
    }

    public HumanPresence(SensorReport report)
    {
        this.detected = (bool)report.Values[ SensorPropertyKeys.SENSOR_DATA_TYPE_HUMAN_PRESENCE.FormatId ][ 0 ];
    }
}

これはCode Pack中のAmbientLightSensor(照度センサー)、Accelerometer3D(加速度センサー)クラスのサンプル実装に習っていて。
SensorDescription属性付きのSensor派生クラスを用意すると、SensorManager.GetSensorsByTypeId()を使って該当するタイプのセンサー一覧を取得することができます。


そして使用例(・ω・)

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();

        SensorManager.SensorsChanged += SensorManager_SensorsChanged;

        HookUpSensor();
    }

    // 非UIスレッドから
    private void SensorManager_SensorsChanged(SensorsChangedEventArgs e)
    {
        // センサー抜き差し等の変化
        BeginInvoke( new MethodInvoker( () =>
            Log( "SensorsChanged: " + e.SensorId + " " + e.Change )
        ) );

        BeginInvoke( new MethodInvoker( HookUpSensor ) );
    }

    private void HookUpSensor()
    {
        try
        {
            // 人感センサ一覧の取得とプロパティの表示
            SensorList<HumanPresenceSensor> list = SensorManager.GetSensorsByTypeId<HumanPresenceSensor>();
            foreach( HumanPresenceSensor sensor in list )
            {
                Log( "AutoUpdateDataReport: " + sensor.AutoUpdateDataReport );
                Log( "CategoryId: " + sensor.CategoryId );
                Log( "ConnectionType: " + sensor.ConnectionType ); // patched
                Log( "Description: " + sensor.Description );
                Log( "CategoryId: " + sensor.CategoryId );
                Log( "DevicePath: " + sensor.DevicePath );
                Log( "FriendlyName: " + sensor.FriendlyName );
                Log( "Manufacturer: " + sensor.Manufacturer );
                Log( "MinimumReportInterval: " + sensor.MinimumReportInterval );
                Log( "ReportInterval: " + sensor.ReportInterval );
                Log( "SensorId: " + sensor.SensorId );
                Log( "SerialNumber: " + sensor.SerialNumber );
                Log( "TypeId: " + sensor.TypeId );

                Log( "GetSupportedProperties:" );
                foreach( PropertyKey key in sensor.GetSupportedProperties() )
                {
                    Log( key.ToString() );
                }

                sensor.DataReportChanged += DataReportChanged;
            }
        }
        catch( SensorPlatformException )
        {
        }
    }

    // 非UIスレッドから
    private void DataReportChanged(Sensor sender, EventArgs e)
    {
        HumanPresenceSensor sensor = sender as HumanPresenceSensor;

        // 状態取得
        BeginInvoke( new MethodInvoker( () =>
            Log( "DataReportChanged: " + ( sensor.CurrentHumanPresence.Detected ? "Detected" : "Leave" ) )
        ) );
    }

    private void Log(string message)
    {
...
    }
}

起動時及びSENSOR-HM/ECO接続時にセンサーの情報を表示して、センサーが人を検知したら「Detected」、検知しなくなったら「Leave」とログを表示するだけのプログラムですが(´д`)


コンストラクタ設定しているSensorManager.SensorsChangedイベントは、センサー一覧に変化があった時に発生するイベントです。
SensorManager_SensorsChangedでログを表示していますが、このプログラムの実行中にセンサーのUSBコネクタを抜き差しすると、以下のようなログが表示されます。

// センサーのUSBコネクタ引っこ抜き時のログ
SensorsChanged: 4f48cd88-6387-4d62-a3c5-5545754d8255 Removal

// センサーのUSBコネクタぶち込み時のログ
SensorsChanged: 4f48cd88-6387-4d62-a3c5-5545754d8255 Addition

なお、イベントは非UIスレッドからの起動なので、UIを操作する場合にはBeginInvoke()してあげる必要があります(・ω・)


っで、HookUpSensor()メソッドで行っているのは、人感センサー一覧の列挙と情報表示、DataReportChangedイベントの設定になります。
SensorManagerのセンサー一覧取得メソッドは、センサーが1件も接続されていないときはSensorPlatformExceptionを投げるのでtryで囲んでいます。
見つかったセンサー一覧についてはそのプロパティをログに出していますが、各プロパティの意味については名前からだいたい類推できるとして、SENSOR-HM/ECOのログは下記のようになります。

AutoUpdateDataReport: True
CategoryId: ca19690f-a2c7-477d-a99e-99ec6e2b5648
ConnectionType: External
Description: I-O DATA SENSOR-HM
CategoryId: ca19690f-a2c7-477d-a99e-99ec6e2b5648
DevicePath: \\?\usb#vid_04bb&pid_0f04#5&969876e&0&2#{ba1bb692-9b7a-4833-9a1e-525ed134e7e2}\{ba1bb692-9b7a-4833-9a1e-525ed134e7e2}
FriendlyName: I-O DATA SENSOR-HM
Manufacturer: I-O DATA DEVICE, INC.
MinimumReportInterval: 16
ReportInterval: 100
SensorId: 4f48cd88-6387-4d62-a3c5-5545754d8255
SerialNumber: 00000000
TypeId: c138c12b-ad52-451c-9375-87f518ff10c6
GetSupportedProperties:
{db5e0cf2-cf1f-4c18-b46c-d86011d62150}, 2
{2299288a-6d9e-4b0b-b7ec-3528f89e40af}, 2

ちょっとだけ補足をすると、SensorIdはセンサーをユニークに識別するIDで、同種のセンサーを複数接続する場合はこれをキーにして管理します。
GetSupportedPropertiesはセンサーがサポートするプロパティの一覧ですが、この定義はCode Pack中のSensorPropertyKeys.csにあります。
SensorPropertyKeys.csを見ると、これはSENSOR_DATA_TYPE_TIMESTAMPとSENSOR_DATA_TYPE_HUMAN_PRESENCEで、SensorReportからSENSOR_DATA_TYPE_HUMAN_PRESENCEのプロパティを取得するとセンサーの検知状態をboolで取得することができます。


っで、DataReportChangedイベントの設定により、センサーの検知状態が変化したときにDataReportChanged()が発生するようにしています。
プログラムを実行している状態でセンサーに近づいたり離れたりすると、以下のようなログが表示されます。

// センサーに近づいた時にイベント発生
DataReportChanged: Detected

// センサーから離れた時にイベント発生
DataReportChanged: Leave

今回の例だとイベント通知を利用しているわけですが、使用するセンサーや行う処理によってはポーリング方式の実装になりますね(・ω・)


っということで、Sensor APIを使用すれば基本的に同様のモデルでアプリケーションの作成が可能なので、センサーの利用も楽ちんぽんというお話でした(・∀・)


…ところで、今月号のInterfaceでWindows 7のドライバ開発の特集をやっていますが、その中にセンサー&ロケーションフレームワークドライバの記事もありますね。
っということで買ってきた(`・ω・´)

Interface ( インターフェース ) 2010年 02月号 [雑誌]

Interface ( インターフェース ) 2010年 02月号 [雑誌]

特集はWindowsドライバ入門的なところもあって割と面白いですが(・∀・)
っで、センサー&ロケーションの記事を見てWDK中のSensorSkeletonのソースを眺めたり。
UMDFってドライバというよりもCOMな感じなので、むしろManagedで実装してみたいよね〜、っつって。