Surface(Windows10)の加速度センサーの値をC++から取得する 

#概略
Surface Pro 4に載っているセンサーの値を取得したいと思ったのですが、思ったより情報が少なかったので、共有させて頂きます。

#経緯
普段、C++とopenFrameworksというライブラリを組み合わせて、プログラミングを使った動的なお絵描きを趣味にしています。下記は最近、SNSでリアクションが沢山いただけたものです。

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">Line滴る良いSphere <a href="https://t.co/5tizDwoxBB">https://t.co/5tizDwoxBB</a> <a href="https://twitter.com/hashtag/openframeworks?src=hash&amp;ref_src=twsrc%5Etfw">#openframeworks</a> <a href="https://t.co/PpYdSzoYMN">pic.twitter.com/PpYdSzoYMN</a></p>&mdash; junkiyoshi (@junkiyoshi) <a href="https://twitter.com/junkiyoshi/status/980936419680993281?ref_src=twsrc%5Etfw">2018年4月2日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

こんな感じの動画を作ってはTwitter/Instagramへ投稿、Blogでソースコードを公開しております。こういったものにSurfaceのセンサーを応用すればインタラクティブなモノが作れるのではと思いって、調べてみました。

#成果物
<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">surfaceのセンサの値を取得してofxBox2dの重力として設定 <a href="https://t.co/KoEzm1NwE3">https://t.co/KoEzm1NwE3</a> <a href="https://twitter.com/hashtag/openframeworks?src=hash&amp;ref_src=twsrc%5Etfw">#openframeworks</a> <a href="https://twitter.com/hashtag/createdonsurface?src=hash&amp;ref_src=twsrc%5Etfw">#createdonsurface</a> <a href="https://t.co/LPMaqkmVJQ">pic.twitter.com/LPMaqkmVJQ</a></p>&mdash; junkiyoshi (@junkiyoshi) <a href="https://twitter.com/junkiyoshi/status/983112014049390592?ref_src=twsrc%5Etfw">2018年4月8日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

3軸加速度センサーの値を取得して、傾きを検出、X, Y, Zの値を文字として描写しています。それらの値を2次元の物理エンジンライブラリの重力として設定しているので、丸いオブジェクトは傾きにつられて動いています。

描写や物理エンジン関係の部分はopenFrameworksの話題になってしまうので、今回はX, Y, Zの値を取得する所までのお話です。

#開発環境
Surface Pro 4
Windows 10
Visual Studio 2015(C++)

#本編
まずはGoogle検索で関連していると思われる各種ワードで検索をしてみました。Microsoftオフィシャルだと下記のドキュメントが該当するようです。

Sensor API
https://msdn.microsoft.com/ja-jp/library/windows/desktop/dd318953(v=vs.85).aspx

ただ、言及されているOSがWindows7で若干古そうなのと、COMなどは普段業務などでもあまり触っていないので、もうちょっと情報が欲しいなと引き続き探した所、日本語で書かれているblogを発見出来ました。

WindowsでC++で加速度センサーの値をとりたい | かずきのBlog@hatena
http://blog.okazuki.jp/entry/2015/04/07/195929

今回やりたかった事のコードが、そのまま記載されていたので参考にさせて頂きました。

ちなみに、インクルードされているSensorApi.hやSensors.hですが、PC内をファイル検索かけた所、下記3か所のディレクトリに存在していました。(Sensorsapi.libも似たような形式で配置されていました)

C:\Program Files (x86)\Windows Kits\8.1\Include\um\
C:\Program Files (x86)\Windows Kits\10\Include\10.0.16299.0\um\
C:\Program Files (x86)\Microsoft SDKs\Windows\v7.1A\Include\

私の開発環境にはVisual Studi2015以外にも2013と2017がインストールされている為かも知れません。Visual Studio2015の設定を確認すると、今回はWindows Kits 8.1のインクルードディレクトリのヘッダーを参照しているようです。

もし、この辺りのファイルが不足しているようであれば、別途Windows SDKをインストールする必要があると思います。おそらくVisual Studioをインストールがあればインストールされていると思います。

#コード

これまでの情報をもとに、作成したコードが下記です。
センサの値をダンプするコンソールアプリになります。

![選択C__Users_nakau_Desktop_SensorSample_Debug_SensorSample.exe 2018_04_24 11_20_53.png](https://qiita-image-store.s3.amazonaws.com/0/151530/b95a754a-33ee-70a7-d640-7ba3d491cb84.png)

---2018/04/25 追記 ---
msmaniaさんから教えて頂いたスマートポインタとFAILDマクロを取り入れさせて頂きました。
めっちゃスッキリして見やすくなりました。ありがとうございました！
元々記載していたコードは折りたたんで残しておきます

```c
#include <iostream>
#include <atlbase.h>
#include <SensorsApi.h>
#include <sensors.h>
#pragma comment(lib, "Sensorsapi.lib")

void get_accelerometer_3d_value(float& rx, float& ry, float& rz) {

	rx = 0.f;
	ry = 0.f;
	rz = 0.f;

	CComPtr<ISensorManager> sensor_manager;
	CComPtr<ISensorCollection> sensor_collection ;
	CComPtr<ISensor> sensor;
	CComPtr<ISensorDataReport> data;

	if (FAILED(::CoInitializeEx(NULL, COINIT_MULTITHREADED))) {

		return;
	}

	if (FAILED(::CoCreateInstance(CLSID_SensorManager, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&sensor_manager)))) {

		return;
	}

	if (FAILED(sensor_manager->GetSensorsByCategory(SENSOR_TYPE_ACCELEROMETER_3D, &sensor_collection))) {

		return;
	}

	if (FAILED(sensor_collection->GetAt(0, &sensor))) {

		return;
	}

	if (FAILED(sensor->GetData(&data))) {

		return;
	}

	PROPVARIANT x = {};
	if (FAILED(data->GetSensorValue(SENSOR_DATA_TYPE_ACCELERATION_X_G, &x))) {

		return;
	}

	PROPVARIANT y = {};
	if (FAILED(data->GetSensorValue(SENSOR_DATA_TYPE_ACCELERATION_Y_G, &y))) {

		return;
	}

	PROPVARIANT z = {};
	if (FAILED(data->GetSensorValue(SENSOR_DATA_TYPE_ACCELERATION_Z_G, &z))) {

		return;
	}

	rx = x.dblVal;
	ry = y.dblVal;
	rz = z.dblVal;
}

int main() {

	float x, y, z;
	while (true) {
		
		get_accelerometer_3d_value(x, y, z);
		std::cout << "X = " << x << " Y = " << y << " Z = " << z << std::endl;
	}
}
```

<details><summary>元々のコード</summary><div>

```c
#include <iostream>
#include <SensorsApi.h>
#include <sensors.h>
#pragma comment(lib, "Sensorsapi.lib")

void get_accelerometer_3d_value(float& rx, float& ry, float& rz) {

	rx = 0.f;
	ry = 0.f;
	rz = 0.f;

	ISensorManager* sensor_manager = nullptr;
	ISensorCollection* sensor_collection = nullptr;
	ISensor* sensor = nullptr;
	ISensorDataReport* data = nullptr;

	if (!SUCCEEDED(::CoInitializeEx(NULL, COINIT_MULTITHREADED))) {

		return;
	}

	if (!SUCCEEDED(::CoCreateInstance(CLSID_SensorManager, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&sensor_manager)))) {

		return;
	}

	if (!SUCCEEDED(sensor_manager->GetSensorsByCategory(SENSOR_TYPE_ACCELEROMETER_3D, &sensor_collection))) {

		sensor_manager->Release();
		return;
	}

	if (!SUCCEEDED(sensor_collection->GetAt(0, &sensor))) {

		sensor_collection->Release();
		sensor_manager->Release();
		return;
	}

	if (!SUCCEEDED(sensor->GetData(&data))) {

		sensor->Release();
		sensor_collection->Release();
		sensor_manager->Release();
		return;
	}

	PROPVARIANT x = {};
	if (!SUCCEEDED(data->GetSensorValue(SENSOR_DATA_TYPE_ACCELERATION_X_G, &x))) {

		data->Release();
		sensor->Release();
		sensor_collection->Release();
		sensor_manager->Release();
		return;
	}

	PROPVARIANT y = {};
	if (!SUCCEEDED(data->GetSensorValue(SENSOR_DATA_TYPE_ACCELERATION_Y_G, &y))) {

		data->Release();
		sensor->Release();
		sensor_collection->Release();
		sensor_manager->Release();
		return;
	}

	PROPVARIANT z = {};
	if (!SUCCEEDED(data->GetSensorValue(SENSOR_DATA_TYPE_ACCELERATION_Z_G, &z))) {

		data->Release();
		sensor->Release();
		sensor_collection->Release();
		sensor_manager->Release();
		return;
	}

	rx = x.dblVal;
	ry = y.dblVal;
	rz = z.dblVal;

	data->Release();
	sensor->Release();
	sensor_collection->Release();
	sensor_manager->Release();
}

int main() {

	float x, y, z;
	while (true) {
		
		get_accelerometer_3d_value(x, y, z);
		std::cout << "X = " << x << " Y = " << y << " Z = " << z << std::endl;
	}
}
```
</div></details>

#おわり
普段、業務でもWindowsAPIやCOMは触らないので完全に理解できていない部分もありますが、何とか値の取得まではいけました。今後は扱えるセンサーの種類などを増やしていきたいです。