The Road to Delphi

Delphi – Free Pascal – Oxygene

Exploring Delphi XE3 – Accesing Windows Sensors from VCL (and Firemonkey)

11 Comments

Delphi XE3 includes support for sensors in OSX and Windows, the classes and types necessaries to access such devices are defined as abstract classes in the System.Sensors unit and implemented in the System.Mac.Sensors unit for OSX and System.Win.Sensors unit for Windows. This article will focus in the Windows side implementation.

Windows 7 introduces the Sensor and Location API, which unifies the access to hardware devices like GPS, Light Sensors, Biometric Sensors and so on. Avoiding the need of use a specific dlls or SDK to control the sensor devices. this API is condensed on these files which are part of the Windows 7 Software Development Kit (SDK).

File name Description
Sensorsapi.h The main header file for the Sensor API. This header file contains the interface definitions.
Sensors.h The header file that contains definitions of platform-defined constants.
Initguid.h The header file that contains definitions for controlling GUID initialization.{
FunctionDiscoveryKeys.h The header file that defines device ID property keys that are required when you connect to logical sensors.
Sensorsapi.lib A static library that contains GUID definitions for the Sensor API.
PortableDeviceGuids.lib A static library that contains GUID definitions for Windows Portable Devices objects.

All these headers was translated by Embarcadero and included as part of the RTL of the Delphi XE3, these are the units which contains such translations Winapi.Portabledevicetypes, Winapi.Sensors, Winapi.Sensorsapi, Winapi.Locationapi. Fortunately an additional set of classes was added to wrap the sensors API, these classes are defined and implemented in the System.Sensors and System.Win.Sensors units. So you don’t need access directly interfaces like ISensor or ISensorManager to gain access to the sensors.

Enumerating Sensors

In order to gain access to the sensors you must get an instance to the TSensorManager class and then call the Activate method, from here you can iterate over the Sensors property or use the GetSensorsByCategory method to get an array of TSensor objects filtered by an TSensorCategory.

var
  LManager : TSensorManager;
  LSensors : TSensorArray;
  LSensor  : TCustomSensor;
begin
  LManager := TSensorManager.Current;
  LManager.Activate;
  try
    LSensors  := LManager.GetSensorsByCategory(TSensorCategory.Location);
    for LSensor in LSensors do
    begin
      //do something
    end;
  finally
    LManager.Deactivate;
  end;
end;

All the sensors share a common set of properties like the Manufacturer, Model, Serial number and so on. So extending the above code you can access such properties on this way :

var
  LManager : TSensorManager;
  LSensors : TSensorArray;
  LSensor  : TCustomSensor;
begin
  LManager := TSensorManager.Current;
  LManager.Activate;
  try
    LSensors  := LManager.GetSensorsByCategory(TSensorCategory.Location);
    for LSensor in LSensors do
    begin
      Writeln(Format('Description  : %s', [LSensor.Description]));
      Writeln(Format('Manufacturer : %s', [LSensor.Manufacturer]));
      Writeln(Format('Model        : %s', [LSensor.Model]));
      Writeln(Format('Serial No    : %s', [LSensor.SerialNo]));
      Writeln(Format('State        : %s', [GetEnumName(TypeInfo(TSensorState),integer(LSensor.State))]));
      Writeln(Format('TimeStamp    : %s', [DatetoStr(LSensor.TimeStamp)]));
      Writeln(Format('Unique ID    : %s', [LSensor.UniqueID]));
    end;
  finally
    LManager.Deactivate;
  end;
end;

Now depending of the sensor category, you must cast the TCustomSensor to the proper specific class, in this case we will use the TCustomLocationSensor class.

var
  LManager : TSensorManager;
  LSensors : TSensorArray;
  LSensor  : TCustomSensor;
  LLocationSensor          : TCustomLocationSensor;
begin
  LManager := TSensorManager.Current;
  LManager.Activate;
  try
    LSensors  := LManager.GetSensorsByCategory(TSensorCategory.Location);
    for LSensor in LSensors do
    begin
      Writeln(Format('Description  : %s', [LSensor.Description]));
      Writeln(Format('Manufacturer : %s', [LSensor.Manufacturer]));
      Writeln(Format('Model        : %s', [LSensor.Model]));
      Writeln(Format('Serial No    : %s', [LSensor.SerialNo]));
      Writeln(Format('State        : %s', [GetEnumName(TypeInfo(TSensorState),integer(LSensor.State))]));
      Writeln(Format('TimeStamp    : %s', [DatetoStr(LSensor.TimeStamp)]));
      Writeln(Format('Unique ID    : %s', [LSensor.UniqueID]));

        LLocationSensor:=LSensor as TCustomLocationSensor;
        LLocationSensor.Start;
        try
          Writeln(Format('Sensor Type       : %s', [GetEnumName(TypeInfo(TLocationSensorType),integer(LLocationSensor.SensorType))]));
          Writeln(Format('Authorized        : %s', [GetEnumName(TypeInfo(TAuthorizationType),integer(LLocationSensor.Authorized))]));
          Writeln(Format('Accuracy          : %n', [LLocationSensor.Accuracy]));
          Writeln(Format('Distance          : %n', [LLocationSensor.Distance]));
          Writeln(Format('Power Consumption : %s', [GetEnumName(TypeInfo(TPowerConsumption),integer(LLocationSensor.PowerConsumption))]));
          Writeln(Format('Location Change   : %s', [GetEnumName(TypeInfo(TLocationChangeType),integer(LLocationSensor.LocationChange))]));
          if TCustomLocationSensor.TProperty.Latitude in  LLocationSensor.AvailableProperties then
          Writeln(Format('Latitude          : %n', [LLocationSensor.Latitude]));
          if TCustomLocationSensor.TProperty.Longitude in  LLocationSensor.AvailableProperties then
          Writeln(Format('Longitude         : %n', [LLocationSensor.Longitude]));
          if TCustomLocationSensor.TProperty.ErrorRadius in  LLocationSensor.AvailableProperties then
          Writeln(Format('Error Radius      : %n', [LLocationSensor.ErrorRadius]));
          if TCustomLocationSensor.TProperty.Altitude in  LLocationSensor.AvailableProperties then
          Writeln(Format('Altitude          : %n', [LLocationSensor.Altitude]));
          if TCustomLocationSensor.TProperty.Speed in  LLocationSensor.AvailableProperties then
          Writeln(Format('Speed             : %n', [LLocationSensor.Speed]));
          if TCustomLocationSensor.TProperty.TrueHeading in  LLocationSensor.AvailableProperties then
          Writeln(Format('True Heading      : %n', [LLocationSensor.TrueHeading]));
          if TCustomLocationSensor.TProperty.MagneticHeading in  LLocationSensor.AvailableProperties then
          Writeln(Format('Magnetic Heading  : %n', [LLocationSensor.MagneticHeading]));
          if TCustomLocationSensor.TProperty.Address1 in  LLocationSensor.AvailableProperties then
          Writeln(Format('Address1          : %s', [LLocationSensor.Address1]));
          if TCustomLocationSensor.TProperty.Address2 in  LLocationSensor.AvailableProperties then
          Writeln(Format('Address2          : %s', [LLocationSensor.Address2]));
          if TCustomLocationSensor.TProperty.City in  LLocationSensor.AvailableProperties then
          Writeln(Format('City              : %s', [LLocationSensor.City]));
          if TCustomLocationSensor.TProperty.StateProvince in  LLocationSensor.AvailableProperties then
          Writeln(Format('State/Province    : %s', [LLocationSensor.StateProvince]));
          if TCustomLocationSensor.TProperty.PostalCode in  LLocationSensor.AvailableProperties then
          Writeln(Format('Postal Code       : %s', [LLocationSensor.PostalCode]));
          if TCustomLocationSensor.TProperty.CountryRegion in  LLocationSensor.AvailableProperties then
          Writeln(Format('Country Region    : %s', [LLocationSensor.CountryRegion]));
        finally
          LLocationSensor.Stop;
        end;
        Writeln;
    end;
  finally
    LManager.Deactivate;
  end;

end;

Not all the properties exposed by the Windows sensors and Location API are mapped directly in the TCustomSensors class, so to access this additional data you can use the HasCustomData and CustomData indexed properties and use one of the values defined in the Winapi.Sensors unit which is the translation of the Sensors.h header file.

  if LLocationSensor.HasCustomData[SENSOR_DATA_TYPE_SATELLITES_USED_COUNT]  then
    Writeln(Format('Satellites used : %d', [ Integer(LLocationSensor.CustomData[SENSOR_DATA_TYPE_SATELLITES_USED_COUNT])]));

Sample Application

Check this sample console application which enumerates all the sensors and properties.

{$APPTYPE CONSOLE}

uses
  System.TypInfo,
  System.Sensors,
  System.SysUtils;

procedure EnumerateSensors;
var
  LManager : TSensorManager;
  LCustomLocationSensor          : TCustomLocationSensor;
  LCustomLightSensor             : TCustomLightSensor;
  LCustomEnvironmentalSensor     : TCustomEnvironmentalSensor;
  LCustomMotionSensor            : TCustomMotionSensor;
  LCustomOrientationSensor       : TCustomOrientationSensor;
  LCustomMechanicalSensor        : TCustomMechanicalSensor;
  LCustomElectricalSensor        : TCustomElectricalSensor;
  LCustomBiometricSensor         : TCustomBiometricSensor;
  LCustomScannerSensor           : TCustomScannerSensor;
  LSensor  : TCustomSensor;
  i        : Integer;
begin
  LManager := TSensorManager.Current;
  LManager.Activate;
  //LSensors  := LManager.GetSensorsByCategory(TSensorCategory.Location);
  if LManager.Count > 0 then
  for i := 0 to LManager.Count-1 do
  begin
    Writeln(Format('Sensor %d',[i+1]));
    Writeln('--------');

    LSensor:= LManager.Sensors[i];
    Writeln(Format('Category     : %s', [GetEnumName(TypeInfo(TSensorCategory),integer(LSensor.Category))]));
    Writeln(Format('Description  : %s', [LSensor.Description]));
    Writeln(Format('Manufacturer : %s', [LSensor.Manufacturer]));
    Writeln(Format('Model        : %s', [LSensor.Model]));
    Writeln(Format('Serial No    : %s', [LSensor.SerialNo]));
    Writeln(Format('State        : %s', [GetEnumName(TypeInfo(TSensorState),integer(LSensor.State))]));
    Writeln(Format('TimeStamp    : %s', [DatetoStr(LSensor.TimeStamp)]));
    Writeln(Format('Unique ID    : %s', [LSensor.UniqueID]));

    case LSensor.Category of

      TSensorCategory.Location :
      begin
        LCustomLocationSensor:=LSensor as TCustomLocationSensor;
        LCustomLocationSensor.Start;
        Writeln(Format('Sensor Type       : %s', [GetEnumName(TypeInfo(TLocationSensorType),integer(LCustomLocationSensor.SensorType))]));
        Writeln(Format('Authorized        : %s', [GetEnumName(TypeInfo(TAuthorizationType),integer(LCustomLocationSensor.Authorized))]));
        Writeln(Format('Accuracy          : %n', [LCustomLocationSensor.Accuracy]));
        Writeln(Format('Distance          : %n', [LCustomLocationSensor.Distance]));
        Writeln(Format('Power Consumption : %s', [GetEnumName(TypeInfo(TPowerConsumption),integer(LCustomLocationSensor.PowerConsumption))]));
        Writeln(Format('Location Change   : %s', [GetEnumName(TypeInfo(TLocationChangeType),integer(LCustomLocationSensor.LocationChange))]));
        Writeln(Format('Latitude          : %n', [LCustomLocationSensor.Latitude]));
        Writeln(Format('Longitude         : %n', [LCustomLocationSensor.Longitude]));
        Writeln(Format('Longitude         : %n', [LCustomLocationSensor.Longitude]));
        Writeln(Format('Error Radius      : %n', [LCustomLocationSensor.ErrorRadius]));
        Writeln(Format('Altitude          : %n', [LCustomLocationSensor.Altitude]));
        Writeln(Format('Speed             : %n', [LCustomLocationSensor.Speed]));
        Writeln(Format('True Heading      : %n', [LCustomLocationSensor.TrueHeading]));
        Writeln(Format('Magnetic Heading  : %n', [LCustomLocationSensor.MagneticHeading]));
        Writeln(Format('Address1          : %s', [LCustomLocationSensor.Address1]));
        Writeln(Format('Address2          : %s', [LCustomLocationSensor.Address2]));
        Writeln(Format('City              : %s', [LCustomLocationSensor.City]));
        Writeln(Format('State/Province    : %s', [LCustomLocationSensor.StateProvince]));
        Writeln(Format('Postal Code       : %s', [LCustomLocationSensor.PostalCode]));
        Writeln(Format('Country Region    : %s', [LCustomLocationSensor.CountryRegion]));
        LCustomLocationSensor.Stop;
      end;

      TSensorCategory.Light :
      begin
        LCustomLightSensor:=LSensor as TCustomLightSensor;
        Writeln(Format('Lux          : %n', [LCustomLightSensor.Lux]));
        Writeln(Format('Temperature  : %n', [LCustomLightSensor.Temperature]));
        Writeln(Format('Chromacity   : %n', [LCustomLightSensor.Chromacity]));
        Writeln(Format('Sensor Type  : %s', [GetEnumName(TypeInfo(TLightSensorType),integer(LCustomLightSensor.SensorType))]));
      end;

      TSensorCategory.Environmental :
      begin
        LCustomEnvironmentalSensor:= LSensor as TCustomEnvironmentalSensor;
        Writeln(Format('Sensor Type    : %s', [GetEnumName(TypeInfo(TEnvironmentalSensorType),integer(LCustomEnvironmentalSensor.SensorType))]));
        Writeln(Format('Temperature    : %n', [LCustomEnvironmentalSensor.Temperature]));
        Writeln(Format('Pressure       : %n', [LCustomEnvironmentalSensor.Pressure]));
        Writeln(Format('Humidity       : %n', [LCustomEnvironmentalSensor.Humidity]));
        Writeln(Format('Wind Direction : %n', [LCustomEnvironmentalSensor.WindDirection]));
        Writeln(Format('Wind Speed     : %n', [LCustomEnvironmentalSensor.WindSpeed]));
      end;

      TSensorCategory.Motion :
      begin
        LCustomMotionSensor:= LSensor as TCustomMotionSensor;
        Writeln(Format('Sensor Type    : %s', [GetEnumName(TypeInfo(TMotionSensorType),integer(LCustomMotionSensor.SensorType))]));
        Writeln(Format('Acceleration X : %n', [LCustomMotionSensor.AccelerationX]));
        Writeln(Format('Acceleration Y : %n', [LCustomMotionSensor.AccelerationY]));
        Writeln(Format('Acceleration Z : %n', [LCustomMotionSensor.AccelerationZ]));
        Writeln(Format('Angle Accel. X : %n', [LCustomMotionSensor.AngleAccelX]));
        Writeln(Format('Angle Accel. Y : %n', [LCustomMotionSensor.AngleAccelY]));
        Writeln(Format('Angle Accel. Z : %n', [LCustomMotionSensor.AngleAccelZ]));
        Writeln(Format('Motion         : %n', [LCustomMotionSensor.Motion]));
        Writeln(Format('Speed          : %n', [LCustomMotionSensor.Speed]));
        Writeln(Format('Update Interval: %n', [LCustomMotionSensor.UpdateInterval]));
      end;

      TSensorCategory.Orientation :
      begin
        LCustomOrientationSensor:= LSensor as TCustomOrientationSensor;
        Writeln(Format('Sensor Type    : %s', [GetEnumName(TypeInfo(TOrientationSensorType),integer(LCustomOrientationSensor.SensorType))]));
        Writeln(Format('Tilt X         : %n', [LCustomOrientationSensor.TiltX]));
        Writeln(Format('Tilt Y         : %n', [LCustomOrientationSensor.TiltY]));
        Writeln(Format('Tilt Z         : %n', [LCustomOrientationSensor.TiltZ]));
        Writeln(Format('Distance X     : %n', [LCustomOrientationSensor.DistanceX]));
        Writeln(Format('Distance Y     : %n', [LCustomOrientationSensor.DistanceY]));
        Writeln(Format('Distance Z     : %n', [LCustomOrientationSensor.DistanceZ]));
        Writeln(Format('Heading X      : %n', [LCustomOrientationSensor.HeadingX]));
        Writeln(Format('Heading Y      : %n', [LCustomOrientationSensor.HeadingY]));
        Writeln(Format('Heading Z      : %n', [LCustomOrientationSensor.HeadingZ]));
        Writeln(Format('Mag. Heading   : %n', [LCustomOrientationSensor.MagHeading]));
        Writeln(Format('True Heading   : %n', [LCustomOrientationSensor.TrueHeading]));
        Writeln(Format('Comp.Heading   : %n', [LCustomOrientationSensor.CompMagHeading]));
        Writeln(Format('Comp True Head : %n', [LCustomOrientationSensor.CompTrueHeading]));
      end;

      TSensorCategory.Mechanical :
      begin
        LCustomMechanicalSensor:= LSensor as TCustomMechanicalSensor;
        Writeln(Format('Sensor Type    : %s', [GetEnumName(TypeInfo(TMechanicalSensorType),integer(LCustomMechanicalSensor.SensorType))]));
        Writeln(Format('Switch State   : %s', [BoolToStr(LCustomMechanicalSensor.SwitchState, True)]));
        Writeln(Format('Switch Array State : %d', [LCustomMechanicalSensor.SwitchArrayState]));
        Writeln(Format('Multi Value State  : %n', [LCustomMechanicalSensor.MultiValueState]));
        Writeln(Format('Force              : %n', [LCustomMechanicalSensor.Force]));
        Writeln(Format('Abs. Pressure      : %n', [LCustomMechanicalSensor.AbsPressure]));
        Writeln(Format('Gauge Pressure     : %n', [LCustomMechanicalSensor.GaugePressure]));
        Writeln(Format('Strain             : %n', [LCustomMechanicalSensor.Strain]));
        Writeln(Format('Weight             : %n', [LCustomMechanicalSensor.Weight]));
      end;

      TSensorCategory.Electrical :
      begin
        LCustomElectricalSensor:= LSensor as TCustomElectricalSensor;
        Writeln(Format('Sensor Type    : %s', [GetEnumName(TypeInfo(TElectricalSensorType),integer(LCustomElectricalSensor.SensorType))]));
        Writeln(Format('Capacitance    : %n', [LCustomElectricalSensor.Capacitance]));
        Writeln(Format('Resistance     : %n', [LCustomElectricalSensor.Resistance]));
        Writeln(Format('Inductance     : %n', [LCustomElectricalSensor.Inductance]));
        Writeln(Format('Current        : %n', [LCustomElectricalSensor.Current]));
        Writeln(Format('Voltage        : %n', [LCustomElectricalSensor.Voltage]));
        Writeln(Format('Power          : %n', [LCustomElectricalSensor.Power]));
      end;

      TSensorCategory.Biometric :
      begin
        LCustomBiometricSensor:= LSensor as TCustomBiometricSensor;
        Writeln(Format('Sensor Type    : %s', [GetEnumName(TypeInfo(TBiometricSensorType),integer(LCustomBiometricSensor.SensorType))]));
        Writeln(Format('Human Proximity: %n', [LCustomBiometricSensor.HumanProximity]));
        Writeln(Format('Human Presense : %s', [BoolToStr(LCustomBiometricSensor.HumanPresense, True)]));
        Writeln(Format('Touch          : %s', [BoolToStr(LCustomBiometricSensor.Touch, True)]));
      end;

      TSensorCategory.Scanner :
      begin
        LCustomScannerSensor:= LSensor as TCustomScannerSensor;
        Writeln(Format('Sensor Type    : %s', [GetEnumName(TypeInfo(TScannerSensorType),integer(LCustomScannerSensor.SensorType))]));
        Writeln(Format('Human Proximity: %d', [LCustomScannerSensor.RFIDTag]));
        Writeln(Format('Barcode Data   : %s', [LCustomScannerSensor.BarcodeData]));
      end;

    end;
    Writeln;
  end
  else
   Writeln('Not sensors was found');
  LManager.Deactivate;
end;

begin
  try
    EnumerateSensors;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
  Readln;
end.

Virtual Sensors

If you don’t have sensors in your machine you can play with these virtual sensors.

Author: Rodrigo

Just another Delphi guy.

11 thoughts on “Exploring Delphi XE3 – Accesing Windows Sensors from VCL (and Firemonkey)

  1. Pingback: Utilizando sensores no Delphi | Régys Borges da Silveira

  2. Thank you, Rodrigo. Bien hecho.

  3. when i try to run any of the the sensormanager code on a brand new asus vivo tab (windows 8/touch/dock tablet) i get an ‘acces denied’, even with uac turned off and running as admin…any ideas? googling got me zero results (is nobody trying this stuff out, is delphi that dead?).

  4. Hola. Hice un código tan sencillo como el que detallo a continuación, pero no funciona. Obtengo un error de tipo “acces violation”. Estoy usando un Galaxy S4 (rooteado) con Android 4.2.2.
    Se les ocurre que puede ser?
    Gracias.

    unit Unit1;

    interface

    uses
    System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
    FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, System.Sensors,
    FMX.StdCtrls, FMX.Edit;

    type
    TForm1 = class(TForm)
    btn1: TSpeedButton;
    procedure btn1Click(Sender: TObject);
    private
    { Private declarations }
    public
    { Public declarations }
    end;

    var
    Form1: TForm1;

    implementation

    {$R *.fmx}

    procedure TForm1.btn1Click(Sender: TObject);
    var
    LCustomEnvironmentalSensor : TCustomEnvironmentalSensor;
    begin
    Writeln(Format(‘Temperature : %n’, [LCustomEnvironmentalSensor.Temperature]));
    Writeln(Format(‘Pressure : %n’, [LCustomEnvironmentalSensor.Pressure]));
    Writeln(Format(‘Humidity : %n’, [LCustomEnvironmentalSensor.Humidity]));
    end;

    end.

    • Hola Pablo, el procedimiento Writeln solo debe ser usado en aplicaciones de tipo consola. Asi que debes utilizar otra forma para mostrar la informacion, te recomiendo uses un componente como el TMemo.

      • Hola. Es verdad, corregí el código pero estoy recibiendo otro tipo de error.
        Cuando ejecuto el procedimiento recibo como respuesta NAN. No llego a que el sensor me devuelva la temperatura jamás.

        procedure TForm1.btn1Click(Sender: TObject);
        var
        LManager : TSensorManager;
        //LCustomEnvironmentalSensor : TCustomEnvironmentalSensor;
        LCustomLightSensor : TCustomLightSensor;
        LSensors : TSensorArray;
        begin
        LManager := TSensorManager.Current;
        LManager.Activate;

        //LSensors := LManager.GetSensorsByCategory(TSensorCategory.Environmental);
        LSensors := LManager.GetSensorsByCategory(TSensorCategory.Light);

        if Assigned(LSensors) and (Length(LSensors)>0) then
        begin
        //LCustomEnvironmentalSensor:= LSensors[1] as TCustomEnvironmentalSensor;
        LCustomLightSensor:=LSensors[0] as TCustomLightSensor;

        //lbl1.Text := FloatToStr(LCustomEnvironmentalSensor.Temperature);LCustomLightSensor.Lux
        lbl1.Text := FloatToStr(LCustomLightSensor.Lux);
        //Writeln(Format(‘Temperature : %n’, [LCustomEnvironmentalSensor.Temperature]));
        //Writeln(Format(‘Pressure : %n’, [LCustomEnvironmentalSensor.Pressure]));
        //Writeln(Format(‘Humidity : %n’, [LCustomEnvironmentalSensor.Humidity]));

        end
        else lbl1.Text := ‘No tiene sensor de Luz!’;
        end;

        • Pablo, el problema que describes esta fuera del alcance de este articulo. Si aun tienes problemas con el codigo puedes enviarme un proyecto de ejemplo para revisarlo.

          • Muchas gracias, Rodrigo. A qué dirección de correo puedo enviarte el código? Nuevamente, muchas gracias por tu tiempo.

  5. Pingback: Sfruttare i sensori dei device - Delphi

Leave a comment