Sensor Bonanza
Table of content
Written by Geir Vattekar, Qt Development Frameworks
Many mobile devices today come with a series of sensors. Examples of functionality that sensors provide are orientation flips, location tracking, and games that are controlled by moving the device.
Download the CodeIn the recent years, an application developer's supply of sensors has been steadily growing. For instance, one of the more recent additions are the proximity sensor, which detects whether an object is close to the device screen.
This article shows how to use sensors in Qt C++/QML applications. We will look at how to read data from the sensors, and how to minimize the sensor power usage—many sensors are massive battery drainers that can deplete the battery in a matter of hours on some devices. We have implemented three small apps for this article. They each use one sensor:
- Accelerometer QML App: Shows data from the accelerometer on the device screen.
- RotationSensor QML App: Displays an ice hockey rink with a puck that can be moved by tilting the rink.
- Compass C++ App: Displays a compass.
Sensors use the coordinate system shown below. When the device is held in this manner.The zx plane will be aligned with the ground, and the xy plane will be perpendicular to the ground.

The sensors give data relative to the coordinate system. An accelerometer will report proper acceleration for the x, y, and z axes. A rotation sensor will give angles between the device and the axes.
We will use code from the applications throughout this article. Here is a short presentation of each application:

The compass application draws a compass on the screen and uses a compass sensor to find magnetic north. It should be mentioned that digital compasses are affected by other magnetic fields and need to be calibrated before showing accurate values. The data from the compass sensor is the azimuth, which gives magnetic north in degrees from the top of the UI, i.e., top of the screen. It also reports the current calibration level.

An accelerometer measures proper acceleration relative to free-fall. It provides three values: the proper acceleration along each axes. When the device is stationary, only gravity is working on it, and we can calculate angles between the device and axes of the coordinate system. As can be seen from the image above, the application shows the accelerometer values as text on the screen. We use the values to rotate the text, so that it always will face up when the device is rotated in the xy plane (i.e, the text is aligned with the y axis).

A rotation sensor gives angles between the device and the axes of the coordinate system. We use these angles to calculate the speed of the puck on our ice hockey rink. The image below illustrates how the angles are given relative to the way the device is held.

We will only show short code snippets from the examples. The complete code is available for download if you want to take a look at it.
Reading Sensor Data
For showing how to read data from a sensor, we turn to the Accelerometer and Compass applications.
The Qt API for sensors is available in the Qt Mobility project. The mobility APIs come with the Qt SDK, but if you would rather build from source, you might want to take a look at http://doc.qt.nokia.com/qtmobility-1.1.0/installation.html
You can access sensors from both C++ and QML. We'll start by using the C++ classes. Every sensor in Qt is a QSensor. Subclasses of QSensor implements an interface to specific sensors. QCompass, for instance, reads data from a digital compass. If you construct a QCompass with its default constructor, the default (and presumably only) compass on the system will be used.
- QCompass *compass = new QCompass;
The current values from the compass is available through the QCompass::reading() function, which returns a QCompassReading. This class contains the data from the compass; they can be fetched with azimuth() and calibrationLevel().
To start listening for data from the compass, it must be started. You can then connect to the QSensor::readingChanged() signal. Alternatively, you can use the QCompassFilter class. This class provides a callback when new data is available. This performs better than using Qt's signals and slots mechanism.
- class CompassFilter : public QCompassFilter
- {
- public:
- ...
- bool filter(QCompassReading *reading) {
- view->setAzimuth(reading->azimuth());
- return false;
- }
- ...
- };
By returning false, we avoid the QSensor::readingChanged() signal from being emitted. Note that the values will not be stored in QCompass, i.e., they can not be fetched through the QCompass::reading() function.
To install the filter on a QCompass, use the QSensor::addFilter() function. It should be noted that returning false will also stop the reading to go through other installed filters. In case you have more than one filter, return false from the last installed filter.
In QML, we have an element for each sensor type. For accelerometers this element is Accelerometer. The other sensor elements are named similarly after the C++ classes, e.g., RotationSensor and ProximitySensor.
- Accelerometer {
- id: sensor
- onReadingChanged: {
- xText.text = "X: " + reading.x.toFixed(2)
- yText.text = "Y: " + reading.y.toFixed(2)
- zText.text = "Z: " + reading.z.toFixed(2)
- var subFromAngle = screen.width > screen.height ? Math.PI : Math.PI/2
- var angle = Math.atan2(reading.y, -reading.x)
- theColumn.rotation = (angle-subFromAngle) * (180/Math.PI)
- ...
- }
- }
For reading the data, we use the onReadingChanged signal handler (called when QSensor emits QSensor::readingChanged()). The reading itself is available from the Accelerometer::reading property. theColumn is a Column that contains three Text elements, which display the data from the accelerometer.
Note that you cannot use filters in QML.
The QSensor subclasses are convenience classes that helps read data from known sensors. Let's take a look at how we can read data from the QSensor class directly. QSensor takes the name of the sensor backend to use in its constructor. You can create a QSensor for reading accelerometer data like this:
- QSensor accelerometer("QAccelerometer");
The name of the sensor is determined by the backend. Qt uses the names of the convenience classes for this. The QSensor will emit QSensor::readingChanged() when new data is available. The data for the current value of the sensor is available through the QSensor::reading() function.
- QSensorReading *reading = accelerometer.reading();
The data is read with QSensorReading's QSensorReading::reading() function. It takes an index if the sensor provides more than one value. If we access the reading with a QSensorFilter, we can do as follows.
- bool Filter::filter(QSensorReading *reading)
- {
- int x = reading.value(0).toInt();
- int y = reading.value(1).toInt();
- int z = reading.value(2).toInt();
- // Do whatever with values
- return false;
- }
The QAccelerometerReading class is simply a wrapper that has Qt properties for x, y, and z. As mentioned, you will probably not have to use the QSensor API directly. If you implement your own sensor backend, we suggest that you take the time to implement convenience classes for them.
Querying Sensor Support
You can query which sensors are available on the system with the QSensor::sensorTypes() function.
- qDebug() << sensor;
The byte arrays will contain strings for each supported sensor. The strings match the name of the sensors' convenience classes, e.g., QAccelerometer and QRotationSensor.
Checking for a specific sensor can be done as follows:
- bool hasSensor = QSensor::sensorTypes().contains("QRotationSensor");
QML has no direct way of checking the availability of its sensor elements. So if we want to make the check in QML code, we need to expose hasSensor to the QML runtime.
- QDeclarativeView view;
- bool hasSensor = QSensor::sensorTypes().contains("QOrientationSensor");
- view.rootContext()->setContextProperty("hasSensor", hasSensor);
The hasSensor bool is now exposed as a property on the global object of the QDeclarativeEngine and that makes it possible to refer to it anywhere in the QML code.
- Text {
- anchors.centerIn: parent
- opacity: hasSensor ? 0.0 : 1.0
- font.pointSize: 12
- text: "Rotation sensor not available"
- }
This simple element is visible when a rotation sensor is not available, and presents an appropriate message to the user. Notice that we set the Item::z property so that it will be displayed on top of other elements.
Power Management
Most sensors are battery hungry entities. An accelerometer, for instance, can drain the battery in a few hours on many devices. There are a few tricks that we can use to minimize battery usage.
- Turn the sensors off when the application is sent to the background.
- Turn off sensors when the user is inactive.
- Only keep the sensor active when you need its data.
The definition of inactivity is dependent on the application. If you use an accelerometer or rotation sensor, you can register whether the device is stationary or laid down on a table, for instance. In many applications, it may be reasonable to simply register for how long the user has not interacted with the GUI. For both approaches, we can use a QTimer or the Timer QML element. Here is how we can register user inactivity with an accelerometer.
- function stop()
- {
- if (sensor.active) {
- sensor.stop()
- }
- }
- Timer {
- id: inactiveTimer
- interval: 2000
- onTriggered: screen.stop()
- }
- Accelerometer {
- id: sensor
- onReadingChanged: {
- ...
- if (Math.abs(reading.x) < 2.0 && Math.abs(reading.y) < 2.0) {
- inactiveTimer.start()
- } else {
- inactiveTimer.stop()
- }
- }
- }
We are here assuming inactivity when the phone is laid down on a table (or held in that manner). We allow for some noise in the readings (which we always have). Of course, if the user is on the move, e.g., travelling in a car, this technique will fail. The rotation sensor is unaffected by acceleration, so you might consider using that instead.
Turning a sensor off when the application goes to the background is a little more involved. We need to listen for events from our top level widget. If we subclass QDeclarativeView, we can reimplement QWidget::event().
- {
- switch (event->type()) {;
- // Stop the sensor
- break;
- }
- return QDeclarativeView::event(event);
- }
Meego sends the QEvent::Leave event when the application is put in the background, while Symbian sends QEvent::WindowDeactivate. Both Symbian and Meego agree on QEvent::Activate when the application comes to the foreground again.
If you are using QML (as we are in the Accelerometer example), you can use the QDeclarativeExpression class, which allows you to execute JavaScript code in the QML runtime.
- QDeclarativeExpression expr(rootContext(), rootObject(), "stop()");
- expr.evaluate();
- if (expr.hasError()) {
- qDebug() << "Failed to stop accelerometer.";
- }
For this to work, your Accelerometer element must be a member of the root object, i. e., the item defined in QDeclarativeView::setSource(). You could also define a signal in the subclass, and expose the view itself to the QML engine. You can connect to C++ signals using the following syntax from JavaScript code.
- myExposedQObject.signalName.connect(myElement.slotFunction)
You would normally use the Component.onCompleted handler to set up your signal connections.
Wrapping Up
We have only looked at three of the available sensors in this article. Qt provides QSensors for several other sensors that you find on mobile devices. We have the ambient light sensor, for instance, which detects whether it is light or dark. No matter which sensors you need for your applications, we hope that this article has given you some tips on how to use them the right way.
Geir Vattekar is a technical writer at Qt Development Frameworks.

2 comments
August 12, 2011
Lab Rat
Qt on which devices support these sensors?
Does only Qt on Nokia-devices support these features?
Or does accelerometer work on Android(necessitas) or iOS too?
July 13, 2012
Lab Rat
privet: QSensors does work with Android (Necessitas).
I just now started integrating it with a wifi mapping app I’m writing, hosted on google code: http://code.google.com/p/wifisigmap
To integrate, I just used stock Necessitas, then added the following to my Android project’s .pro file:
Here’s the relevant snippets from my main class, just to see if I get any useful data (which I do):