ヒューマンインタフェイスデバイス(HID)を使うには

ヒューマンインターフェイスデバイス

ヒューマンインターフェイスデバイス(HID)とは、マウスやキーボードなど、人とPCとの基本的なやりとりをする装置を統一的なプロファイルで表したもので、HID対応デバイスならばどのようなデバイスであっても統一的な処理でアクセスすることができます。

 

どのようなHIDデバイスがあるのかはUSB.orgのサイトにあるHID Usage tableのPDFを見るとわかります。

非常に沢山あり、KeyboardやGame ControlsからVR Controlsまで、様々なデバイスをサポートしています。

自分のPCにどのようなHIDデバイスがあるのかは、デバイスマネージャーからヒューマンインターフェイスデバイスの項目を見るとわかります。

34

 

 

WindowsRuntimeでは標準でHIDデバイスの入出力をサポートしており、HIDDeviceクラスによって扱うことができます。

今回はHIDデバイスの一つであるXBOX360のコントローラを使って入力値をとってみたいと思います。

20140922_203400705_iOS

 

デバイスを特定するID

HID対応のデバイスを特定するには4つのIDが必要となっています。

  • UsagePage(デバイスのカテゴリID)
  • UsageId(デバイスの種類ID)
  • VendorId(デバイスの製造者ID)
  • ProductId(デバイスの製品ID)

の4つが必要となります。

 

UsagePageとUsageIdについてはUSB.orgのサイトにあるHID Usage tableのPDFを見て調べます。

GamePadはGeneric DesktopPage(0x01)のGame Pad(0x05)なので

UsagePageは0x01、UsageIdは0x05となります。

 

続いてVendorIdとProductIdです。

これはデバイスのプロパティを見るとわかる場合が多いです。

デバイスマネージャーからXBox360コントローラのプロパティを見て、詳細タブのプロパティからハードウエアIDの項目を見ると、VIDとPIDがわかります。

36

したがってこの場合、VendorIDはVIDのところを見て、0x045E、ProductIDはPIDのところを見て0x028Eということがわかります。

 

もしこの方法でVendorIDとProductIDが取得できない場合、取得ツールが公開されているのでMicrosoftの太田さんの記事を呼んでVendorIDとProductIDを取得してください。

WindowsストアアプリからHIDデバイスを制御する

 

マニフェストファイルを編集する

4つのIDを取得できたら、マニフェストファイルに4つのIDを書いておく必要があります。

ソリューションエクスプローラーからPackage.manifestファイルを右クリックして、コードを表示します。

37

 

Packageタグにxmlns:wb=”http://schemas.microsoft.com/appx/2013/manifest”属性を追加します。

<Package xmlns="http://schemas.microsoft.com/appx/2010/manifest"
         xmlns:m2="http://schemas.microsoft.com/appx/2013/manifest"
         xmlns:wb="http://schemas.microsoft.com/appx/2013/manifest"
         >

そしてCapabilityタグ内に<wb:DeviceCapability>タグを追加します。

DeviceタグのId属性には”vidpid:{vendorId} {productID}”の形式でVendorIDとProductIDを入力します。

FunctionタグのType属性には”usage:{UsagePage} {UsageId}”の形でUsagePageとUsageIDを指定します。

 

<wb:DeviceCapability Name="humaninterfacedevice">
  <wb:Device Id="vidpid:045E 028E">
    <wb:Function Type="usage:0001 0005"/>
  </wb:Device>

</wb:DeviceCapability>

 

今回の場合Package.manifestファイルはこのようになりました。

<?xml version="1.0" encoding="utf-8"?>
<Package xmlns="http://schemas.microsoft.com/appx/2010/manifest"
         xmlns:m2="http://schemas.microsoft.com/appx/2013/manifest"
         xmlns:wb="http://schemas.microsoft.com/appx/2013/manifest"
         >

  <Identity Name="372526d1-5d17-4ba8-ac7c-c3e14a802a5e"
            Publisher="CN=Ryota"
            Version="1.0.0.0" />

  <Properties>
    <DisplayName>HIDTest</DisplayName>
    <PublisherDisplayName>Ryota</PublisherDisplayName>
    <Logo>Assets\StoreLogo.png</Logo>
  </Properties>

  <Prerequisites>
    <OSMinVersion>6.3.0</OSMinVersion>
    <OSMaxVersionTested>6.3.0</OSMaxVersionTested>
  </Prerequisites>

  <Resources>
    <Resource Language="x-generate"/>
  </Resources>

  <Applications>
    <Application Id="App"
        Executable="$targetnametoken$.exe"
        EntryPoint="HIDTest.App">
        <m2:VisualElements
            DisplayName="HIDTest"
            Square150x150Logo="Assets\Logo.png"
            Square30x30Logo="Assets\SmallLogo.png"
            Description="HIDTest"
            ForegroundText="light"
            BackgroundColor="#464646">
            <m2:SplashScreen Image="Assets\SplashScreen.png" />
        </m2:VisualElements>
    </Application>
  </Applications>
  <Capabilities>
    <Capability Name="internetClient" />
    
    <wb:DeviceCapability Name="humaninterfacedevice">
      <wb:Device Id="vidpid:045E 028E">
        <wb:Function Type="usage:0001 0005"/>
      </wb:Device>

    </wb:DeviceCapability>
    
  </Capabilities>
</Package>

 

デバイスを取得する

デバイスを取得するには、HidDeviceのGetDeviceSelectorメソッドで、UsagePageとUsageIdとVendorIdとProductIdを指定します。

そしてDeviceInformationクラスのFindAllAsyncメソッドでデバイスリストを取得します。

もしIDが間違っているか、デバイスが接続されていない場合、取得できるリストの数が0になるのでそれで判断します。

ushort usagePage = 0x1;
ushort usageId = 0x5;
ushort venderId = 0x045E;
ushort productId = 0x028E;

var selector = HidDevice.GetDeviceSelector(usagePage, usageId, venderId, productId);
var deviceInfoList = await DeviceInformation.FindAllAsync(selector);

デバイスを決定することができたらHidDeviceクラスのFromIdAsyncメソッドでIdとアクセスモードを指定してHidDeviceクラスを初期化します。

if (deviceInfoList.Count > 0)
{
    DeviceInformation deviceInfo = deviceInfoList.First();

    HidDevice hidDevice = await HidDevice.FromIdAsync(deviceInfo.Id, FileAccessMode.Read);



}

デバイスからの入力を検知するにはInputReportReceivedイベントを取得します。

入力として受け取ったデータをDataReaderでバイトデータを読み込みます。

hidDevice.InputReportReceived += async (s, arg) =>
{
    HidInputReport inputReport = arg.Report;
    var bytes = new byte[inputReport.Data.Length];

    DataReader reader = DataReader.FromBuffer(inputReport.Data);
    reader.ReadBytes(bytes);

    await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
    {
        listBox.Items.Clear();
        for (int i = 0; i < bytes.Length; i++)
        {
            listBox.Items.Add(string.Format("data[{0}]={1}", i, bytes[i]));
        }
    });

};

ゲームコントローラーは15byteずつデータが来ました。

各バイトデータは8bitずつあるので2^8で0~255の値で各ボタンやスティックのデータが送られてきました。

38

 

 

各バイト値が取りうる値はこんな感じでした。

39

 

各ボタンに関しては、図の右下にあるような値の和が12バイト目で取得できます。

各ボタンはすべて2^nの値が設定されていて、2^nの和というのは、必ず加算する前の両辺に分解することができます。

つまり12バイト目は押しているボタンの加算値であり、それらはすべて2^nで値が設定されているのでどのボタンを押しているのかが決定できます。

 

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.