Introduction
This guide gives developers an overview of the Microsoft Windows 8.1 sensors application programming interfaces (APIs) for Windows 8.1 Desktop and Windows Store applications with a specific focus on the various sensor capabilities available in Windows 8.1 Desktop mode. This Development Guide summarizes the APIs that enable creating interactive applications by including some of the common sensors such as accelerometers, magnetometers, and gyroscopes with Windows 8.1.
Developers have multiple API choices to program sensors on Windows 8.1. The touch-friendly app environment is called “Windows Store apps.” Windows Store apps can run software developed with the Windows Run-Time (WinRT) interface. The WinRT sensor API represents a portion of the overall WinRT library. For more details, please refer to the MSDN Sensor API library.
Traditional Win Forms, or MFC-style apps are called “Desktop Apps” because they run in the Desktop Windows Manager environment. Desktop apps can either use the native Win32*/COM API, a .NET-style API or a subset of select WinRT APIs.
The following is a list of WinRT APIs that can be accessed by Desktop apps:
In both cases, the APIs go through a Windows middleware component called the Windows Sensor Framework. The Windows Sensor Framework defines the Sensor Object Model. The different APIs “bind” to that object model in slightly different ways.
Differences in the Desktop and Windows Store app development will be discussed later in this document. For brevity, we will consider only Desktop app development. For Windows Store app development, please refer to the API Reference for Windows Store apps.
There are many kinds of sensors, but we are interested in the ones required for Windows 8.1, namely accelerometers, gyroscopes, ambient light sensors, compass, and GPS. Windows 8.1 represents the physical sensors with object-oriented abstractions. To manipulate the sensors, programmers use APIs to interact with the objects. The following table provides information on how the sensors can be accessed from both the Windows 8 Desktop apps as well as from Windows Store apps.
Windows 8.1 Desktop Mode Apps |
Windows Store Apps |
||||
---|---|---|---|---|---|
Feature/Toolset |
C++ |
C#/VB |
JavaScript*/ HTML5 |
C++, C#, VB & XAML |
Unity* 4.2 |
Orientation Sensors |
Yes
|
Yes
|
Yes
|
Yes Yes Yes |
Yes |
Light Sensor |
Yes |
Yes |
Yes |
Yes |
Yes |
NFC |
Yes |
Yes |
Yes |
Yes |
Yes |
GPS |
Yes |
Yes |
Yes |
Yes |
Yes |
Table 1. Features Matrix for Windows* 8.1 Developer Environments
Below, Figure 1 identifies that there are more objects than actual hardware. Windows defines some “logical sensor” objects by combining information from multiple physical sensors. This is called “Sensor Fusion.”
Figure 1. Different sensors supported, starting on Windows* 8
Physical sensor chips have some inherent natural limitations. For example:
These measurements are subject to an inherent drift problem, which can be corrected by using raw data from the Gyro. Both measurements are (scaled) dependent upon the tilt of the computer from level with respect to the Earth’s surface. For example, to obtain the computer’s heading with respect to the Earth’s True North Pole (Magnetic North Pole is in a different position and moves over time), corrections must be applied.
Sensor Fusion (Figure 2) is defined by obtaining raw data from multiple physical sensors, especially the Accelerometer, Gyro, and Magnetometer, performing mathematical calculations to correct for natural sensor limitations, computing more human-usable data, and representing those as logical sensor abstractions. The application developer must implement the necessary transformations required to translate physical sensor data to the abstract sensor data. If the system design has a SensorHub, the fusion operations will take place inside the microcontroller firmware. If the system design does not have a SensorHub, the fusion operations must be done inside one or more device drivers that the IHVs and/or OEMs provide.
Figure 2. Sensor fusion via combining output from multiple sensors
To manipulate a sensor, a system is needed to identify and refer to. The Windows Sensor Framework defines a number of categories that sensors are grouped into. It also defines a large number of specific sensor types. Table 2 lists some of the sensors available for Desktop applications.
“All” |
||||||||
Biometric |
Electrical |
Environmental |
Light |
Location |
Mechanical |
Motion |
Orientation |
Scanner |
---|---|---|---|---|---|---|---|---|
Human Presence |
Capacitance |
Atmospheric Pressure |
Ambient Light |
Broadcast |
Boolean Switch |
Accelerometer 1D |
Compass 1D |
Barcode |
Human Proximity* |
Current |
Humidity |
Gps |
Boolean Switch Array |
Accelerometer 2D |
Compass 2D |
Rfid |
|
Touch |
Electrical Power |
Temperature |
Static |
Force |
Accelerometer 3D |
Compass 3D |
||
Inductance |
Wind Direction |
Multivalue Switch |
Gyrometer 1D |
Device Orientation |
||||
Potentio-meter |
Wind Speed |
Pressure |
Gyrometer 2D |
Distance 1D |
||||
Resistance |
Strain |
Gyrometer 3D |
Distance 2D |
|||||
Voltage |
Weight |
Motion Detector |
Distance 3D |
|||||
Speedometer |
Inclinometer 1D |
|||||||
Inclinometer 2D |
||||||||
Inclinometer 3D |
Table 2. Sensor types and categories
The sensor types required by Windows 8 are shown in bold font:
All of these constants correspond to Globally Unique IDs GUIDs. Below, in Table 3, is a sample of some of the sensor categories and types, the names of the constants for Win32/COM and .NET, and their underlying GUID values.
Identifier |
Constant (Win32*/COM) |
Constant (.NET) |
GUID |
---|---|---|---|
Category “All” |
SENSOR_CATEGORY_ALL |
SensorCategories.SensorCategoryAll |
{C317C286-C468-4288-9975-D4C4587C442C} |
Category Biometric |
SENSOR_CATEGORY_BIOMETRIC |
SensorCategories.SensorCategoryBiometric |
{CA19690F-A2C7-477D-A99E-99EC6E2B5648} |
Category Electrical |
SENSOR_CATEGORY_ELECTRICAL |
SensorCategories.SensorCategoryElectrical |
{FB73FCD8-FC4A-483C-AC58-27B691C6BEFF} |
Category Environmental |
SENSOR_CATEGORY_ENVIRONMENTAL |
SensorCategories.SensorCategoryEnvironmental |
{323439AA-7F66-492B-BA0C-73E9AA0A65D5} |
Category Light |
SENSOR_CATEGORY_LIGHT |
SensorCategories.SensorCategoryLight |
{17A665C0-9063-4216-B202-5C7A255E18CE} |
Category Location |
SENSOR_CATEGORY_LOCATION |
SensorCategories.SensorCategoryLocation |
{BFA794E4-F964-4FDB-90F6-51056BFE4B44} |
Category Mechanical |
SENSOR_CATEGORY_MECHANICAL |
SensorCategories.SensorCategoryMechanical |
{8D131D68-8EF7-4656-80B5-CCCBD93791C5} |
Category Motion |
SENSOR_CATEGORY_MOTION |
SensorCategories.SensorCategoryMotion |
{CD09DAF1-3B2E-4C3D-B598-B5E5FF93FD46} |
Category Orientation |
SENSOR_CATEGORY_ORIENTATION |
SensorCategories.SensorCategoryOrientation |
{9E6C04B6-96FE-4954-B726-68682A473F69} |
Category Scanner |
SENSOR_CATEGORY_SCANNER |
SensorCategories.SensorCategoryScanner |
{B000E77E-F5B5-420F-815D-0270ª726F270} |
Type HumanProximity |
SENSOR_TYPE_HUMAN_PROXIMITY |
SensorTypes.SensorTypeHumanProximity |
{5220DAE9-3179-4430-9F90-06266D2A34DE} |
Type AmbientLight |
SENSOR_TYPE_AMBIENT_LIGHT |
SensorTypes.SensorTypeAmbientLight |
{97F115C8-599A-4153-8894-D2D12899918A} |
Type Gps |
SENSOR_TYPE_LOCATION_GPS |
SensorTypes.SensorTypeLocationGps |
{ED4CA589-327A-4FF9-A560-91DA4B48275E} |
Type Accelerometer3D |
SENSOR_TYPE_ACCELEROMETER_3D |
SensorTypes.SensorTypeAccelerometer3D |
{C2FB0F5F-E2D2-4C78-BCD0-352A9582819D} |
Type Gyrometer3D |
SENSOR_TYPE_GYROMETER_3D |
SensorTypes.SensorTypeGyrometer3D |
{09485F5A-759E-42C2-BD4B-A349B75C8643} |
Type Compass3D |
SENSOR_TYPE_COMPASS_3D |
SensorTypes.SensorTypeCompass3D |
{76B5CE0D-17DD-414D-93A1-E127F40BDF6E} |
Type DeviceOrientation |
SENSOR_TYPE_DEVICE_ORIENTATION |
SensorTypes.SensorTypeDeviceOrientation |
{CDB5D8F7-3CFD-41C8-8542-CCE622CF5D6E} |
Type Inclinometer3D |
SENSOR_TYPE_INCLINOMETER_3D |
SensorTypes.SensorTypeInclinometer3D |
{B84919FB-EA85-4976-8444-6F6F5C6D31DB} |
Table 3. Example of Constants and Globally Unique IDs (GUIDs)
Above are the most commonly used GUIDs; there are many available. At first you might think that the GUIDs are silly and tedious, but there is one good reason for using them: extensibility. Since the APIs don’t care about the actual sensor names (they just pass GUIDs around), it is possible for vendors to invent new GUIDs for “value add” sensors.
Microsoft provides a tool in Visual Studio* for generating new GUIDs. Figure 3 shows a screenshot from Visual Studio for doing this. All the vendor has to do is publish them, and new functionality can be exposed without the need to change the Microsoft APIs or any operating system code at all.
Figure 3. Defining new GUIDs for value add sensors
In order for an app to use a sensor, Microsoft Sensor Framework needs a way to “bind” the object to actual hardware. It does this via Plug and Play, using a special object called the Sensor Manager Object.
An app can ask for a specific type of sensor, such as Gyrometer3D. The Sensor Manager consults the list of sensor hardware present on the computer and returns a collection of matching objects bound to that hardware. Although the Sensor Collection may have 0, 1, or more objects, it usually has only one. Below is a C++ code sample illustrating the use of the Sensor Manager object’s GetSensorsByType method to search for 3-axis Gyros and return them in a Sensor Collection. Note that a ::CoCreateInstance() must be made for the Sensor Manager Object first.
01
// Additional includes for sensors
02
#include
03
#include
04
#include
05
// Create a COM interface to the SensorManager object.
06
ISensorManager* pSensorManager = NULL;
07
HRESULT
hr = ::CoCreateInstance(CLSID_SensorManager, NULL, CLSCTX_INPROC_SERVER,
08
IID_PPV_ARGS(&pSensorManager));
09
if
(FAILED(hr))
10
{
11
::MessageBox(NULL, _T(
"Unable to CoCreateInstance() the SensorManager."
),
12
_T(
"Sensor C++ Sample"
), MB_OK | MB_ICONERROR);
13
return
-1;
14
}
15
// Get a collection of all 3-axis Gyros on the computer.
16
ISensorCollection* pSensorCollection = NULL;
17
hr = pSensorManager->GetSensorsByType(SENSOR_TYPE_GYROMETER_3D, &pSensorCollection);
18
if
(FAILED(hr))
19
{
20
::MessageBox(NULL, _T(
"Unable to find any Gyros on the computer."
),
21
_T(
"Sensor C++ Sample"
), MB_OK | MB_ICONERROR);
22
return
-1;
23
}
An app can request sensors by category, such as all motion sensors. The Sensor Manager consults the list of sensor hardware on the computer and returns a collection of motion objects bound to that hardware. The SensorCollection may have 0, 1, or more objects in it. On most computers, the collection will have two motion objects: Accelerometer3D and Gyrometer3D.
The C++ code sample below illustrates the use of the Sensor Manager object’s GetSensorsByCategory method to search for motion sensors and return them in a sensor collection.
01 |
// Additional includes for sensors |
02 |
#include |
03 |
#include |
04 |
#include |
05 |
// Create a COM interface to the SensorManager object. |
06 |
ISensorManager* pSensorManager = NULL; |
07 |
HRESULT hr = ::CoCreateInstance(CLSID_SensorManager, NULL, CLSCTX_INPROC_SERVER, |
08 |
IID_PPV_ARGS(&pSensorManager)); |
09 |
if (FAILED(hr)) |
10 |
{ |
11 |
::MessageBox(NULL, _T( "Unable to CoCreateInstance() the SensorManager." ), |
12 |
_T( "Sensor C++ Sample" ), MB_OK | MB_ICONERROR); |
13 |
return -1; |
14 |
} |
15 |
// Get a collection of all 3-axis Gyros on the computer. |
16 |
ISensorCollection* pSensorCollection = NULL; |
17 |
hr = pSensorManager->GetSensorsByCategory(SENSOR_CATEGORY_MOTION, &pSensorCollection); |
18 |
if (FAILED(hr)) |
19 |
{ |
20 |
::MessageBox(NULL, _T( "Unable to find any Motion sensors on the computer." ), |
21 |
_T( "Sensor C++ Sample" ), MB_OK | MB_ICONERROR); |
22 |
return -1; |
23 |
} |
In practice, it is most efficient for an app to request all of the sensors on the computer at once. The Sensor Manager consults the list of sensor hardware on the computer and returns a collection of all the objects bound to that hardware. The Sensor Collection may have 0, 1, or more objects in it. On most computers, the collection will have seven or more objects.
C++ does not have a GetAllSensors call, so you must use GetSensorsByCategory(SENSOR_CATEGORY_ALL, …) instead as shown in the sample code below.
01
C++ does not have a GetAllSensors call, so you must use GetSensorsByCategory(SENSOR_CATEGORY_ALL, …) instead as shown in the sample code below.
02
// Additional includes for sensors
03
#include
04
#include
05
#include
06
// Create a COM interface to the SensorManager object.
07
ISensorManager* pSensorManager = NULL;
08
HRESULT
hr = ::CoCreateInstance(CLSID_SensorManager, NULL, CLSCTX_INPROC_SERVER,
09
IID_PPV_ARGS(&pSensorManager));
10
if
(FAILED(hr))
11
{
12
::MessageBox(NULL, _T(
"Unable to CoCreateInstance() the SensorManager."
),
13
_T(
"Sensor C++ Sample"
), MB_OK | MB_ICONERROR);
14
return
-1;
15
}
16
// Get a collection of all 3sensors on the computer.
17
ISensorCollection* pSensorCollection = NULL;
18
hr = pSensorManager->GetSensorsByCategory(SENSOR_CATEGORY_ALL, &pSensorCollection);
19
if
(FAILED(hr))
20
{
21
::MessageBox(NULL, _T(
"Unable to find any sensors on the computer."
),
22
_T(
"Sensor C++ Sample"
), MB_OK | MB_ICONERROR);
23
return
-1;
24
}
On Windows, as with most hardware devices, sensors are treated as Plug and Play devices. There are a few different scenarios where sensors can be connected/disconnected:
In the context of sensors, a Plug and Play connect is called an Enter event, and disconnect is called a Leave event. Resilient apps need to be able to handle both.
If the app is already running at the time a sensor is plugged in, the Sensor Manager reports the sensor Enter event; however, if the sensors are already plugged in when the app starts running, this action will not result in Enter events for those sensors. In C++/COM, you must use the SetEventSinkmethod to hook the callback. The callback must be an entire class that inherits from ISensorManagerEvents and must implement IUnknown. Additionally, the ISensorManagerEvents interface must have callback function implementations for:
01
STDMETHODIMP OnSensorEnter(ISensor *pSensor, SensorState state);
02
// Hook the SensorManager for any SensorEnter events.
03
pSensorManagerEventClass =
new
SensorManagerEventSink();
// create C++ class instance
04
// get the ISensorManagerEvents COM interface pointer
05
HRESULT
hr = pSensorManagerEventClass->QueryInterface(IID_PPV_ARGS(&pSensorManagerEvents));
06
if
(FAILED(hr))
07
{
08
::MessageBox(NULL, _T(
"Cannot query ISensorManagerEvents interface for our callback class."
),
09
_T(
"Sensor C++ Sample"
), MB_OK | MB_ICONERROR);
10
return
-1;
11
}
12
// hook COM interface of our class to SensorManager eventer
13
hr = pSensorManager->SetEventSink(pSensorManagerEvents);
14
if
(FAILED(hr))
15
{
16
::MessageBox(NULL, _T(
"Cannot SetEventSink on SensorManager to our callback class."
),
17
_T(
"Sensor C++ Sample"
), MB_OK | MB_ICONERROR);
18
return
-1;
19
}
Code:Hook Callback for Enter event
Below is the C++/COM equivalent of the Enter callback. All the initialization steps from the main loop would be performed in this function. In fact, it is more efficient to refactor the code so that the main loop merely calls OnSensorEnter to simulate an Enter event.
01
STDMETHODIMP SensorManagerEventSink::OnSensorEnter(ISensor *pSensor, SensorState state)
02
{
03
// Examine the SupportsDataField for SENSOR_DATA_TYPE_LIGHT_LEVEL_LUX.
04
VARIANT_BOOL bSupported = VARIANT_FALSE;
05
HRESULT
hr = pSensor->SupportsDataField(SENSOR_DATA_TYPE_LIGHT_LEVEL_LUX, &bSupported);
06
if
(FAILED(hr))
07
{
08
::MessageBox(NULL, _T(
"Cannot check SupportsDataField for SENSOR_DATA_TYPE_LIGHT_LEVEL_LUX."
),
09
_T(
"Sensor C++ Sample"
), MB_OK | MB_ICONINFORMATION);
10
return
hr;
11
}
12
if
(bSupported == VARIANT_FALSE)
13
{
14
// This is not the sensor we want.
15
return
-1;
16
}
17
ISensor *pAls = pSensor;
// It looks like an ALS, memorize it.
18
::MessageBox(NULL, _T(
"Ambient Light Sensor has entered."
),
19
_T(
"Sensor C++ Sample"
), MB_OK | MB_ICONINFORMATION);
20
.
21
.
22
.
23
return
hr;
24
}
Code: Callback for Enter event
The individual sensor (not the Sensor Manager) reports when the Leave event happens. This code is the same as the previous hook callback for anEnter event.
01
// Hook the Sensor for any DataUpdated, Leave, or StateChanged events.
02
SensorEventSink* pSensorEventClass =
new
SensorEventSink();
// create C++ class instance
03
ISensorEvents* pSensorEvents = NULL;
04
// get the ISensorEvents COM interface pointer
05
HRESULT
hr = pSensorEventClass->QueryInterface(IID_PPV_ARGS(&pSensorEvents));
06
if
(FAILED(hr))
07
{
08
::MessageBox(NULL, _T(
"Cannot query ISensorEvents interface for our callback class."
),
09
_T(
"Sensor C++ Sample"
), MB_OK | MB_ICONERROR);
10
return
-1;
11
}
12
hr = pSensor->SetEventSink(pSensorEvents);
// hook COM interface of our class to Sensor eventer
13
if
(FAILED(hr))
14
{
15
::MessageBox(NULL, _T(
"Cannot SetEventSink on the Sensor to our callback class."
),
16
_T(
"Sensor C++ Sample"
), MB_OK | MB_ICONERROR);
17
return
-1;
18
}
Code: Hook Callback for Leave event
The OnLeave event handler receives the ID of the leaving sensor as an argument.
01
STDMETHODIMP SensorEventSink::OnLeave(REFSENSOR_ID sensorID)
02
{
03
HRESULT
hr = S_OK;
04
::MessageBox(NULL, _T(
"Ambient Light Sensor has left."
),
05
_T(
"Sensor C++ Sample"
), MB_OK | MB_ICONINFORMATION);
06
// Perform any housekeeping tasks for the sensor that is leaving.
07
// For example, if you have maintained a reference to the sensor,
08
// release it now and set the pointer to NULL.
09
return
hr;
10
}
Code: Callback for Leave event
Different types of sensors report different information. Microsoft calls these pieces of information Data Fields, and they are grouped together in a SensorDataReport. A computer may (potentially) have more than one type of sensor that an app can use. The app won’t care which sensor the information came from, so long as it is available.
Table 4 shows the constant names for the most commonly used Data Fields for Win32/COM and.NET. Just like sensor identifiers, these constants are just human-readable names for their associated GUIDs. This method of association provides for extensibility of Data Fields beyond those “well known” fields that Microsoft has pre-defined.
Constant (Win32*/COM) |
Constant (.NET) |
PROPERTYKEY (GUID,PID) |
---|---|---|
SENSOR_DATA_TYPE_TIMESTAMP |
SensorDataTypeTimestamp |
{DB5E0CF2-CF1F-4C18-B46C-D86011D62150},2 |
SENSOR_DATA_TYPE_LIGHT_LEVEL_LUX |
SensorDataTypeLightLevelLux |
{E4C77CE2-DCB7-46E9-8439-4FEC548833A6},2 |
SENSOR_DATA_TYPE_ACCELERATION_X_G |
SensorDataTypeAccelerationXG |
{3F8A69A2-07C5-4E48-A965-CD797AAB56D5},2 |
SENSOR_DATA_TYPE_ACCELERATION_Y_G |
SensorDataTypeAccelerationYG |
{3F8A69A2-07C5-4E48-A965-CD797AAB56D5},3 |
SENSOR_DATA_TYPE_ACCELERATION_Z_G |
SensorDataTypeAccelerationZG |
{3F8A69A2-07C5-4E48-A965-CD797AAB56D5},4 |
SENSOR_DATA_TYPE_ANGULAR_VELOCITY_X_DEGRE |
SensorDataTypeAngularVelocityXDegreesPerSecond |
{3F8A69A2-07C5-4E48-A965-CD797AAB56D5},10 |
SENSOR_DATA_TYPE_ANGULAR_VELOCITY_Y_DEGRE |
SensorDataTypeAngularVelocityYDegreesPerSecond |
{3F8A69A2-07C5-4E48-A965-CD797AAB56D5},11 |
SENSOR_DATA_TYPE_ANGULAR_VELOCITY_Z_DEGRE |
SensorDataTypeAngularVelocityZDegreesPerSecond |
{3F8A69A2-07C5-4E48-A965-CD797AAB56D5},12 |
SENSOR_DATA_TYPE_TILT_X_DEGREES |
SensorDataTypeTiltXDegrees |
{1637D8A2-4248-4275-865D-558DE84AEDFD},2 |
SENSOR_DATA_TYPE_TILT_Y_DEGREES |
SensorDataTypeTiltYDegrees |
{1637D8A2-4248-4275-865D-558DE84AEDFD},3 |
SENSOR_DATA_TYPE_TILT_Z_DEGREES |
SensorDataTypeTiltZDegrees |
{1637D8A2-4248-4275-865D-558DE84AEDFD},4 |
SENSOR_DATA_TYPE_MAGNETIC_HEADING_COMPEN |
SensorDataTypeMagneticHeadingCompen |
{1637D8A2-4248-4275-865D-558DE84AEDFD},11 |
SENSOR_DATA_TYPE_MAGNETIC_FIELD_STRENGTH_ |
SensorDataTypeMagneticFieldStrengthXMilligauss |
{1637D8A2-4248-4275-865D-558DE84AEDFD},19 |
SENSOR_DATA_TYPE_MAGNETIC_FIELD_STRENGTH_ |
SensorDataTypeMagneticFieldStrengthYMilligauss |
{1637D8A2-4248-4275-865D-558DE84AEDFD},20 |
SENSOR_DATA_TYPE_MAGNETIC_FIELD_STRENGTH_ |
SensorDataTypeMagneticFieldStrengthZMilligauss |
{1637D8A2-4248-4275-865D-558DE84AEDFD},21 |
SENSOR_DATA_TYPE_QUATERNION |
SensorDataTypeQuaternion |
{1637D8A2-4248-4275-865D-558DE84AEDFD},17 |
SENSOR_DATA_TYPE_ROTATION_MATRIX |
SensorDataTypeRotationMatrix |
{1637D8A2-4248-4275-865D-558DE84AEDFD},16 |
SENSOR_DATA_TYPE_LATITUDE_DEGREES |
SensorDataTypeLatitudeDegrees |
{055C74D8-CA6F-47D6-95C6-1ED3637A0FF4},2 |
SENSOR_DATA_TYPE_LONGITUDE_DEGREES |
SensorDataTypeLongitudeDegrees |
{055C74D8-CA6F-47D6-95C6-1ED3637A0FF4},3 |
SENSOR_DATA_TYPE_ALTITUDE_ELLIPSOID_METERS |
SensorDataTypeAltitudeEllipsoidMeters |
{055C74D8-CA6F-47D6-95C6-1ED3637A0FF4},5 |
Table 4. Data Field identifier constants
One thing that makes Data Field identifiers different from sensor IDs is the use of a data type called PROPERTYKEY. A PROPERTYKEY consists of a GUID (similar to what sensors have), plus an extra number called a “PID” (property ID). You might notice that the GUID part of a PROPERTYKEY is common for sensors that are in the same category. Data Fields have a native data type for all of their values, such as Boolean, unsigned char, int, float, double, etc.
In Win32/COM, the value of a Data Field is stored in a polymorphic data type called PROPVARIANT. In .NET, there is a CLR (Common Language Runtime) data type called “object” that does the same thing. The polymorphic data type will need to be queried and/or typecast to the “expected”/”documented” data type.
The SupportsDataField() method of the sensor should be used to check the sensors for the Data Fields of interest. This is the most common programming idiom that is used to select sensors. Depending on the usage model of the app, only a subset of the Data Field may be required. Sensors that support the desired Data Fields should be selected. Type casting will be required to assign the sub-classed member variables from the base class sensor.
01
ISensor* m_pAls;
02
ISensor* m_pAccel;
03
ISensor* m_pTilt;
04
// Cycle through the collection looking for sensors we care about.
05
ULONG
ulCount = 0;
06
HRESULT
hr = pSensorCollection->GetCount(&ulCount);
07
if
(FAILED(hr))
08
{
09
::MessageBox(NULL, _T(
"Unable to get count of sensors on the computer."
), _T(
"Sensor C++ Sample"
), MB_OK | MB_ICONERROR);
10
return
-1;
11
}
12
for
(
int
i = 0; i < (
int
)ulCount; i++)
13
{
14
hr = pSensorCollection->GetAt(i, &pSensor);
15
if
(SUCCEEDED(hr))
16
{
17
VARIANT_BOOL bSupported = VARIANT_FALSE;
18
hr = pSensor->SupportsDataField(SENSOR_DATA_TYPE_LIGHT_LEVEL_LUX, &bSupported);
19
if
(SUCCEEDED(hr) && (bSupported == VARIANT_TRUE)) m_pAls = pSensor;
20
hr = pSensor->SupportsDataField(SENSOR_DATA_TYPE_ACCELERATION_Z_G, &bSupported);
21
if
(SUCCEEDED(hr) && (bSupported == VARIANT_TRUE)) m_pAccel = pSensor;
22
hr = pSensor->SupportsDataField(SENSOR_DATA_TYPE_TILT_Z_DEGREES, &bSupported);
23
if
(SUCCEEDED(hr) && (bSupported == VARIANT_TRUE)) m_pTilt = pSensor;
24
.
25
.
26
.
27
}
28
}
Code: Use of the SupportsDataField() method of the sensor to check for supported data field
In addition to Data Fields, sensors have Properties that can be used for identification and configuration. Table 5 shows the most commonly-used Properties. Just like Data Fields, Properties have constant names used by Win32/COM and .NET, and those constants are really PROPERTYKEY numbers underneath. Properties are extensible by vendors and also have PROPVARIANT polymorphic data types. Unlike Data Fields that are read-only, Properties have the ability to Read/Write. It is up to the individual sensor’s discretion as to whether or not it rejects Write attempts. Because no exception is thrown when a write attempt fails, a write-read-verification will need to be performed.
Identification |
Identification |
PROPERTYKEY (GUID,PID) |
|
||
---|---|---|---|---|---|
SENSOR_PROPERTY_PERSISTENT_UNIQUE_ID |
SensorID |
{7F8383EC-D3EC-495C-A8CF-B8BBE85C2920},5 |
|
||
WPD_FUNCTIONAL_OBJECT_CATEGORY |
CategoryID |
{8F052D93-ABCA-4FC5-A5AC-B01DF4DBE598},2 |
|
||
SENSOR_PROPERTY_TYPE |
TypeID |
{7F8383EC-D3EC-495C-A8CF-B8BBE85C2920},2 |
|
||
SENSOR_PROPERTY_STATE |
State |
{7F8383EC-D3EC-495C-A8CF-B8BBE85C2920},3 |
|
||
SENSOR_PROPERTY_MANUFACTURER |
SensorManufacturer |
{7F8383EC-D3EC-495C-A8CF-B8BBE85C2920},6 |
|
||
SENSOR_PROPERTY_MODEL |
SensorModel |
{7F8383EC-D3EC-495C-A8CF-B8BBE85C2920},7 |
|
||
SENSOR_PROPERTY_SERIAL_NUMBER |
SensorSerialNumber |
{7F8383EC-D3EC-495C-A8CF-B8BBE85C2920},8 |
|
||
SENSOR_PROPERTY_FRIENDLY_NAME |
FriendlyName |
{7F8383EC-D3EC-495C-A8CF-B8BBE85C2920},9 |
|
||
SENSOR_PROPERTY_DESCRIPTION |
SensorDescription |
{7F8383EC-D3EC-495C-A8CF-B8BBE85C2920},10 |
|
||
SENSOR_PROPERTY_MIN_REPORT_INTERVAL |
MinReportInterval |
{7F8383EC-D3EC-495C-A8CF-B8BBE85C2920},12 |
|
||
SENSOR_PROPERTY_CONNECTION_TYPE |
SensorConnectionType |
{7F8383EC-D3EC-495C-A8CF-B8BBE85C2920},11 |