#概略
Surface Pro 4に載っているセンサーの値を取得したいと思ったのですが、思ったより情報が少なかったので、共有させて頂きます。
#経緯
普段、C++とopenFrameworksというライブラリを組み合わせて、プログラミングを使った動的なお絵描きを趣味にしています。下記は最近、SNSでリアクションが沢山いただけたものです。
Line滴る良いSphere https://t.co/5tizDwoxBB #openframeworks pic.twitter.com/PpYdSzoYMN
— junkiyoshi (@junkiyoshi) 2018年4月2日
こんな感じの動画を作ってはTwitter/Instagramへ投稿、Blogでソースコードを公開しております。こういったものにSurfaceのセンサーを応用すればインタラクティブなモノが作れるのではと思いって、調べてみました。
#成果物
surfaceのセンサの値を取得してofxBox2dの重力として設定 https://t.co/KoEzm1NwE3 #openframeworks #createdonsurface pic.twitter.com/LPMaqkmVJQ
— junkiyoshi (@junkiyoshi) 2018年4月8日
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をインストールがあればインストールされていると思います。
#コード
これまでの情報をもとに、作成したコードが下記です。
センサの値をダンプするコンソールアプリになります。
---2018/04/25 追記 ---
msmaniaさんから教えて頂いたスマートポインタとFAILDマクロを取り入れさせて頂きました。
めっちゃスッキリして見やすくなりました。ありがとうございました!
元々記載していたコードは折りたたんで残しておきます
#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;
}
}
元々のコード
#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;
}
}
#おわり
普段、業務でもWindowsAPIやCOMは触らないので完全に理解できていない部分もありますが、何とか値の取得まではいけました。今後は扱えるセンサーの種類などを増やしていきたいです。