Sensor Framework
From Symbian Developer Community
Contents |
Introduction
There are many sensors in a mobile smartphone. As mobile smartphones continue to get more sophisticated, more sensors are required to provide users with adequate feedback and control. Sensors also provide interesting services to smartphone users. The S60 Python runtime environment includes a way to incorporate existing sensors and new ones that have not been added yet.
As an example, consider the Nokia N97 smartphone. There are many sensors in an N97. It contains an accelerometer, a sensor that measures motion. It contains a light sensor, a sensor that can measure the light level in the phone's environment. It has a magnetometer that measures magnetic field levels. It has a proximity sensor that measures how close an object -- usually a user's face -- is to the display of the phone. It has a sensor that detects the direction of magnetic north. These sensors are used for a number of applications, from detecting device orientation to dimming the display in a dark room. Obviously, because these sensors provide valuable information, the S60 Python runtime environment needs a way to address them and the data they provide.
One approach to provide access to sensors is to provide an API module for each sensor. These modules could provide the access to sensor data and the programmer could invent creative ways to handle that data. Each application would handle sensors in their own way, using the API to get at sensor data. This approach is a bit shortsighted. It ignores common elements in sensor data acquisition. For example, most sensor data APIs will include some kind of monitoring for sensor data. The operating system could, in fact, provide a more efficient way of monitoring all sensors and providing access to this monitoring "service". Also, this approach ignores the fact that the sensors available to an application change rapidly: new sensors are being added to new phone platforms and the current sensors are being upgraded all the time. This means that APIs would be changing constantly and new APIs would need to be generated.
This is why Symbian OS provides a sensor framework in S60 platforms rather than sensor APIs. A framework is a way to provide an abstract, generic way to access all sensors on a device in the same manner with efficiency of performance. The S60 sensor framework provides the following:
- a single sensor monitoring system that is available to all applications on an S60 device
- an abstract way to access sensor data, so that this data can be used in multiple ways
- an efficient way of accessing sensors based on "plug-ins", so that a single data element retrieved from a sensor can be fed to multiple recipients
The S60 sensor framework was introduced in the S60 5th Edition platform. It is also available for most devices using the S60 3rd Edition, Feature Pack 2, platform.
This chapter will overview the ways to use this sensor framework through Python.
Basic Sensor Access
Sensor data is accessed in Python through the sensor module. This module provides many different functions that access sensors through the sensor framework.
The sensor framework views sensor data as available through channels. A channel represents a specific type of data that might be available from a sensor. There could be multiple channels of data available from the same sensor. For example, the accelerometer in a Nokia N97 will give data relating to three-dimensional positioning as well as simple double tapping on the screen.
The sensor module provides a function called list_channels() that allows a programmer to list all the sensor channels available on a device. This function returns a list of dictionary objects that contain channel information. These dictionary objects have the following components:
- id contains the system id of the channel
- type contains the channel type, useful in access function calls
- name contains the system name of the channel
Consider this example. On an N97, the following session gives 10 different channels of sensor data:
>>> import sensor
>>> sensor.list_channels()
[{'type': 536929669L, 'id': 7L, 'name': 'ProximityMonitor'}, {'type': 536919830L, 'id': 8L, 'name': 'AmbientLightData'}, {'type': 270553214L, 'id': 9L, 'name': 'AccelerometerXYZAxisData'}, {'type': 270553217L, 'id': 10L, 'name': 'AccelerometerDoubleTappingData'}, {'type': 270553215L, 'id': 11L, 'name': 'TSensrvTappingData'}, {'type': 536919776L, 'id': 12L, 'name': 'MagnetometerXYZAxisData'}, {'type': 536957243L, 'id': 13L, 'name': None}, {'type': 536919775L, 'id': 14L, 'name': 'MagneticNorthData'}, {'type': 270553224L, 'id': 15L, 'name': 'OrientationData'}, {'type': 270553225L, 'id': 16L, 'name': 'RotationData'}]
Consider a single dictionary object:
{'type': 536919776L, 'id': 12L, 'name': 'MagnetometerXYZAxisData'}
The type and id of the sensor are not useful to humans, but we can parse the name of the sensor: a magnetometer that provides three-dimensional spatial information. We will use all these data in a later section.
Currently, in the 2.0.0 version of S60 Python, the following sensor data channels are supported:
- Accelerometer XYZ sensor channel
- Rotation sensor channel
- Orientation sensor channel
- Accelerometer double-tap sensor channel
- Proximity monitor sensor channel
- Ambient light sensor channel
- Magnetic North sensor channel
- Magnetometer XYZ sensor channel
Each channel of sensor data has it own Python class.
Sensor Availability
Often, a single application will be written to run on several different phone platforms. The first step is to verify whether user's phone has any sensors at all:
try:
import sensor
SENSOR_AVAILABLE = True
except ImportError:
SENSOR_AVAILABLE = False
An important step in applications dealing with sensors would be to detect the availability of specific sensors before working with them. Naturally, different phones will have different sensors. The easiest way to find the sensor you need is to use list_channels() and to walk through the available sensor channels. A routine like the one below would suffice:
def sensorPresent(testSensor):
sensors = sensor.list_channels()
for sense in sensors:
if sense['name'] == testSensor:
return True
return False
Obtaining Sensor Data
Each sensor channel is represented by a class. These classes have similar functions -- they are all derived from the same base class -- but have different class attributes that are used to obtain the value from the sensor. For example, the accelerometer XYZ data channel class, called AccelerometerXYZAxisData, has "x", "y", and "z" attributes to determine values along the X-, Y-, and Z-axes, respectively. The ambient light data channel, called AmbientLightData, has a class attribute called ambient_light that has values from 0 to 100 depicting the percentage of light it senses.
The table below lists each sensor channel, the name of the class the represents it, and the class attributes that are used to reference values of the sensor channel data.
| Sensor Channel | Class Name | Class Attributes |
|---|---|---|
| Accelerometer XYZ sensor channel | AccelerometerXYZAxisData | x gives X-axis value y gives Y-axis value |
| Accelerometer double-tap sensor channel | AccelerometerDoubleTappingData | direction gives the tap direction |
| Magnetometer XYZ sensor channel | MagnetometerXYZAxisData | x gives X-axis value y gives Y-axis value
|
| Magnetic North sensor channel | MagneticNorthData | azimuth gives the degrees clockwise from magnetic north; values 0 to 359 are possible |
| Ambient light sensor channel | AmbientLightData | ambient_light gives the light level as an integer percentage value, ranging from 0 to 100:
|
| Proximity monitor sensor channel | ProximityMonitor | proximity_state gives one of three values:
|
| Orientation sensor channel | OrientationData | device_orientation gives the orientation of the device in a range of integer values from -1 to 6:
|
| Rotation sensor channel | RotationData | x gives X-axis value y gives Y-axis value |
Much of the information in the table is self-explanatory, but a few comments should be made about it.
- Several of the sensors give three-dimensional result as (x,y,z) axis style data. It is important to remember that each three-dimensional sensor is different. The "accelerometer XYZ sensor channel" give data on the movement on each axis; the "magnetometer XYZ sensor channel" gives data about the geomagnetic fields on each axis; the "rotation sensor channel" gives the degree of rotation about each axis.
- Some sensors give values that could (or should) have names rather than values. The proximity monitor is a good example, where each integer value really means something about the proximity state. The sensor class allow you to derive these names using the get_logicalname() function. The format of the call is
get_logicalname(<classLookupName>, <value>)
- For example, to look up the logical name of the value "2" for the proximity sensor, you would use the call "sensor.get_logicalname(sensor.ProximityState, 2)" and you would get the return value "ProximityDisc", which is really not self-explanatory. Perhaps a better example would be the call "sensor.get_logicalname(sensor.SensrvAmbientLightData, 40)" giving the return value "AmbientLightTwilight", which explains the light level of 40% a little better.
- There is more data available for the accelerometer double-tap sensor channel than just the "direction" value. These data are available from other functions in the AccelerometerDoubleTappingData class:
- get_axis_active() returns a tuple of three axis activity indicators: a 0 (disabled) or a 1 (active) for each axis
- set_axis_active([x=None, y=None, z=None]) sets one or more axis as an active axis
- get_properties() returns a tuple of values indicating the TapThresholdValue, TapDurationValue, TapLatencyValue, and TapIntervalValue variables.
- set_properties([DblTapThresholdValue = None, DblTapDurationValue = None, DblTapLatencyValue = None, DblTapIntervalValue = None]) sets the properties of variables given.
- These calls will be used and more deeply explained later in the section on gestures.
- The orientation sensor channel needs some definition. Consider this diagram:
- The sides of this N97 are labelled as the sensor class sees the phone. The values returned in device_orientation, then, are as follows:
- 1 means that the "display up" side is up
- 2 means that the "display down" side is up
- 3 means that the left side is up
- 4 means that the right side is up
- 5 means that the display itself is up
- 6 means that the back of the device is up
- The AccelerometerXYZAxisData and RotationData class constructors take an optional parameter. This parameter gives the name of a function used to implement a noise filtering algorithm. The possible choices are MedianFilter() or LowPassFilter(). A median filter is a good general-purpose noise reducer while a low pass filter is good at reducing noise that is out-or-range. Leaving this out of the constructor call will allow raw data to be retrieved from the sensor.
Now let's consider a couple of examples to illustrate all the above information.
light = sensor.AmbientLightData()
level = light.ambient_light
print sensor.get_logicalname(sensor.SensrvAmbientLightData, level)
This code will get the light level from an instance of the ambient light sensor channel class and print its logical name. Likewise, the code below will get the device orientation:
side = sensor.OrientationData()
print sensor.get_logicalname(sensor.SensrvDeviceOrientation, side.device_orientation)
While these code fragments make sense, they do not work correctly. The first will print "AmbientLightVeryDark" because the light level is 0. The second will get a device orientation of -1. This is because we have not started monitoring the data; we have only sampled unmonitored data. The next section will describe how to monitor sensor data.
Monitoring Sensor Data
Sensor data is retrieved using the sensor framework by monitoring sensors and using callback functions that are called when new sensor data is available. The sequence of setting up and retrieving data is as follows:
- initialize the sensor by creating an instance of it
- register a callback function by calling the set_callback() function of the sensor object
- start listening and responding to sensor data availability by using the start_listening() function of the sensor object
- collect data for a while
- stop retrieving data (i.e., stop using the callback function) by calling the stop_listening() function of the sensor object
To monitor and retrieve the ambient light data correctly (see the previous section), we would use code like that below:
light = sensor.AmbientLightData()
def reportTheLight():
global light
level = light.ambient_light
print "Light level is", level, "or", sensor.get_logicalname(sensor.SensrvAmbientLightData, level)
light.set_callback(reportTheLight)
light.start_listening()
Now, this code will start to report the light levels in level numbers and in categories. For example, this code will report:
Light level is 60 or AmbientLightLight
A call to "light.stop_listening()" will stop the monitoring.
Consider another example from the previous section. Let's assume we want to monitor the orientation of the device. Consider the following code:
side = sensor.OrientationData()
def reportOrientation():
global side
print sensor.get_logicalname(sensor.SensrvDeviceOrientation, side.device_orientation)
side.set_callback(reportOrientation)
side.start_listening()
Now, if I start with the phone facing me in portrait orientation, then rotate it counter-clockwise, here is the output:
OrientationDisplayUp
OrientationDisplayRightUp
OrientationDisplayDown
OrientationDisplayLeftUp
OrientationDisplayUp
Again, a call to "side.stop_listening()" will stop the listening process.
Let's look at one more example. This time, we will monitor the accelerometer coordinates and rotate the phone in the same way. We can use the code below:
meter = sensor.AccelerometerXYZAxisData(data_filter=sensor.LowPassFilter())
def reportXYZ():
global meter
print "(",meter.x,",",meter.y,",",meter.z,")"
meter.set_callback(reportXYZ)
meter.start_listening()
The result is a stream of (x,y,z) coordinate data. It begins like this:
( -1 , 6 , 0 )
( -1 , 11 , 1 )
( -1 , 16 , 2 )
( -2 , 23 , 3 )
( -3 , 29 , 4 )
( -4 , 34 , 6 )
( -5 , 40 , 9 )
( -5 , 46 , 11 )
( -6 , 51 , 12 )
( -6 , 57 , 13 )
( -7 , 57 , 14 )
( -8 , 58 , 14 )
( -10 , 59 , 13 )
( -11 , 59 , 11 )
( -10 , 59 , 10 )
( -9 , 58 , 10 )
( -7 , 57 , 9 )
( -7 , 57 , 9 )
( -6 , 57 , 9 )
( -6 , 56 , 7 )
( -5 , 56 , 7 )
( -5 , 57 , 8 )
( -3 , 55 , 9 )
( 0 , 54 , 10 )
( 1 , 54 , 12 )
( 1 , 56 , 11 )
( 1 , 57 , 11 )
( 1 , 58 , 10 )
( 1 , 58 , 10 )
( 2 , 58 , 11 )
( 3 , 58 , 11 )
Obviously, my hand did not remain perfectly still along the Z axis, but let's ignore that for now. You can see the movement of the phone started slowly along the X axis but was faster along the Y axis. If we plot all 211 points on a X-Y graph, we get the graph below:
You can see the phone roughly moved in a circle, except when I had to change hands, which slowed movement along one axis.
Sensor Sample Example
Let's take a look at a more complex example. For this example, we take an example distributed with the PyS60 installation: ball.py implements a ball dropping and bouncing on the screen in reaction to arrow keys. We will replace the arrow keys with reaction to the phone's sensors.
The first question we should ask is which sensors should we access? We want the ball to respond to movement of the phone, so we could use the rotation sensor or the accelerometer. Obviously, other sensors like the ambient light sensor do not make sense here. Further, consider that the rotation sensor responds to rotation in degrees around a single axis; it will not render data that corresponds to movement along a single axis or give data that measures distances. So, the accelerometer appears to be the sensor of choice.
Consider the code below, which will implement a solution to our bouncing ball problem. We will go over this code in chunks.
import appuifw, e32, time
from sensor import *
from graphics import *
# Some initial initializations
X = 0
Y = 1
img = None
# Here, we define some callbacks.
# First, we define the callback for the accelerometer. We record the direction of the movement along
# X and Y axes.
def deviceMoved():
global accelerometer, prevX, prevY, dirx, diry
dirx = prevX - accelerometer.x
diry = prevY - accelerometer.y
prevX = accelerometer.x
prevY = accelerometer.y
# This is a callback for redrawing canvas graphics
def handleRedraw(rect):
if img:
canvas.blit(img)
# We ignore canvas events
def handleCanvasEvent(event):
pass
# Application QUIT callback
def quit():
global running
running=0
# Now we set up the accelerometer object and start listening to the accelerometer
accelerometer = AccelerometerXYZAxisData(data_filter=LowPassFilter())
accelerometer.set_callback(data_callback=deviceMoved)
accelerometer.start_listening()
# Here we set up the application screen to be the canvas and full screen
appuifw.app.screen = 'full'
appuifw.app.body = \
canvas = \
appuifw.Canvas(event_callback=handleCanvasEvent, redraw_callback=handleRedraw)
appuifw.app.exit_key_handler = quit
# New (empty) image
img = Image.new(canvas.size)
# Some final initializations before we begin. Ball is in the middle of the screen.
prevX = prevY = 0
location = [img.size[X]/2,img.size[Y]/2]
speed = [0.,0.]
blobsize = 16
width,height = img.size[X]-blobsize,img.size[Y]-blobsize
gravity = 0.03
acceleration = 0.1
frames = 0
dirx = 0
diry = 0
# Start things going. Stop the simlation whem "running" = 0
running = 1
while running:
# Clear the screen, draw the ball.
img.clear(0)
img.point((location[X]+blobsize/2,location[Y]+blobsize/2),
0x00ff00,width=blobsize)
handleRedraw(())
# Yield active object (thread) execution for a moment
e32.ao_yield()
# Adjust the speed to slow a bit and to obey gravity. Then adust the location.
speed[X] *= 0.999
speed[X] *= 0.999
speed[X] += gravity if dirx > 0 else -gravity
speed[Y] += gravity if diry > 0 else -gravity
location[X] += speed[X]
location[Y] += speed[Y]
# If we hit a wall, bounce back!
if location[X]>width:
location[X]=width-(location[X]-width)
speed[X]=-0.80*speed[X]
speed[Y]=0.90*speed[Y]
if location[X]<0:
location[X]=-location[X]
speed[X]=-0.80*speed[X]
speed[Y]=0.90*speed[Y]
if location[Y]>height:
location[Y]=height-(location[Y]-height)
speed[X]=0.90*speed[X]
speed[Y]=-0.80*speed[Y]
if location[Y]<0:
location[Y]=-location[Y]
speed[X]=0.90*speed[X]
speed[Y]=-0.80*speed[Y]
# Adjust the speed to reflect the movement of the accelerometer. Note we assume
# that the accelerometer callback has adjusted the direction variables
speed[X] += dirx*acceleration
speed[Y] -= diry*acceleration
# Only go for a finite number of iterations. Then stop.
frames += 1
if frames>400:
running = 0
accelerometer.stop_listening()
Let's go over some fragments of the code above to clarify how it works.
The code starts by initializing some data, then it sets up callbacks for the accelerometer and the canvas. For our purposes, let's look at the accelerometer callback:
def deviceMoved():
global accelerometer, prevX, prevY, dirx, diry
dirx = prevX - accelerometer.x
diry = prevY - accelerometer.y
prevX = accelerometer.x
prevY = accelerometer.y
Notice the function takes no arguments; it needs global variables to set and retain accelerometer values. This callback will be called as the main code is executing, so the sharing of variables could potentially be a problem. If the main code were to write to these variables, problems could occur; as it is, this function is the only code that changes these values, so no concurrency problems exist. This code records values to indicate direction of movement and saves values for the next call.
We set up the accelerometer with the code below:
accelerometer = AccelerometerXYZAxisData(data_filter=LowPassFilter())
accelerometer.set_callback(data_callback=deviceMoved)
accelerometer.start_listening()
Notice that a low pass filter is used to eliminate as much noise as possible and to smooth the data. We start listening to the sensor right away.
Once the sensor has data, we assume the variables dirx and diry will change "automatically" according to the movement of the phone. We use this "automatic" change when the speed along both axes is adjusted:
speed[X] += dirx*acceleration
speed[Y] -= diry*acceleration
Finally, a note should be made about concurrency and Symbian OS. We noted above that the deviceMoved() function executes concurrently with the main code in the program. It executes as an active object (in Symbian OS parlance) and interrupts the main code when it needs to execute. This interruption can cause the main code to slow down. It is wise to explicitly share the CPU with the active object. We do that in the above code with the statement below:
e32.ao_yield()
This yields execution to the active object, if it is waiting for an interruption of the main code. The effect can be dramatic. For example, in one trial, code to execute 400 "frames" or iterations ran in 36 seconds using the yield statement, but ran in 65 seconds without it.
Detecting Gestures
An interesting use of sensor data is to detect gestures: movements of a phone in three dimensions that can be used to control applications. For this example, we will focus on two gestures: a "shake" up and down and a "shake" by moving your wrist to the left and back.
To detect a shake, we have to start by observing gesture data. Let's use a little piece of code to record three-dimensional accelerometer data into a file:
import time, sensor, e32
datafile = open("E:\Python\sensordata", "w")
meter = sensor.AccelerometerXYZAxisData(data_filter=sensor.LowPassFilter())
def reportXYZ():
global meter,datafile
line = "(%d,%d,%d):%s\n" % (meter.x,meter.y,meter.z,time.strftime("%H:%M:%S"))
datafile.write(line)
meter.set_callback(reportXYZ)
meter.start_listening()
e32.ao_sleep(5)
meter.stop_listening()
datafile.close()
This code will write three coordinates and a timestamp to a file. The output is a series of lines that look like this:
(1,5,2):14:56:51
(3,10,4):14:56:51
(4,15,6):14:56:51
(6,21,8):14:56:51
(7,26,10):14:56:51
(9,31,12):14:56:51
(10,37,14):14:56:51
(11,42,16):14:56:51
(13,47,19):14:56:51
(15,53,21):14:56:51
(15,53,21):14:56:51
(15,53,21):14:56:51
(15,53,21):14:56:52
(15,53,20):14:56:52
(15,52,20):14:56:52
(15,52,21):14:56:52
(15,52,21):14:56:52
(15,53,21):14:56:52
...and so on. By scanning the data, we can discover that a downward shake takes between 1.5 and 2 seconds and varies across the Y axis by between 40 anc 70 points. So we need code that records the Y coordinates with a timestamp. If, over that last 1.8 seconds, the maximum Y point minus the minumum Y point is greater than 40 AND we are within 5 points of 1.8 seconds ago, we have a shake along the Y axis.
Consider the code below. Note we also consider a shake along the X axis.
import time,sensor,e32
meter = sensor.AccelerometerXYZAxisData(data_filter=sensor.LowPassFilter())
start = time.time()
timeWindow = [start]
xaxisWindow = [0]
yaxisWindow = [0]
def recordEvents():
global meter,timeWindow,xaxisWindow,yaxisWindow
now = time.time()
timeWindow.append(now)
xaxisWindow.append(meter.x)
yaxisWindow.append(meter.y)
while now - timeWindow[0] > 2:
timeWindow = timeWindow[1:]
xaxisWindow = xaxisWindow[1:]
yaxisWindow = yaxisWindow[1:]
if now - timeWindow[0] < 1.8: return
if xaxisWindow[0]-meter.x < 5 and max(xaxisWindow)-min(xaxisWindow)>40:
print "XSHAKE"
timeWindow = [now]
xaxisWindow = [meter.x]
return
if yaxisWindow[0]-meter.y < 5 and max(yaxisWindow)-min(yaxisWindow)>40:
print "YSHAKE"
timeWindow = [now]
yaxisWindow = [meter.y]
return
meter.set_callback(recordEvents)
meter.start_listening()
e32.ao_sleep(20)
meter.stop_listening()
The code starts by initializing several lists. timeWindow is a list of timestamps, initialized with the current time. xaxisWindow and yaxisWindow are lists of X coordinates and Y coordinates, respectively. We do not keep them in (X,Y) pairs so we can use the min() and max() functions on each axis separately. The idea here is that each window should contain recorded data for 1.8 seconds. This is a moving window, so as time shifts, data shifts in and out of each list.
The recordEvents() function maintains these moving lists and evaluates if a shake has occured. the function starts by simply recording the current time and (X,Y) coordinates:
now = time.time()
timeWindow.append(now)
xaxisWindow.append(meter.x)
yaxisWindow.append(meter.y)
The function then shifts each list until we have approximately 2 seconds of data:
while now - timeWindow[0] > 2:
timeWindow = timeWindow[1:]
xaxisWindow = xaxisWindow[1:]
yaxisWindow = yaxisWindow[1:]
At this point, the data at the start of each list is less than 2 seconds from the data at the end of the list. If the time difference is less than 1.8 seconds, we need to get more data. We simply return in this case and do not consider the data further.
If we have a valid collection of data, we simply need to apply our criteria to it:
if xaxisWindow[0]-meter.x < 5 and max(xaxisWindow)-min(xaxisWindow)>40:
print "XSHAKE"
timeWindow = [now]
xaxisWindow = [meter.x]
return
if yaxisWindow[0]-meter.y < 5 and max(yaxisWindow)-min(yaxisWindow)>40:
print "YSHAKE"
timeWindow = [now]
yaxisWindow = [meter.y]
return
When we determine that a shake has occurred, we simply print a message. We then reset the data collections.
Now, this code will detect a shake of a phone in a downward direction. The code above works because down is positive. For the code to react the same way for an upward shake, absolute values of Y axis data must be appended to the yaxisWindow list. For this, the math module will need to be imported and the math.fabs() should be used.
The same change could be applied for an X axis shake to the right.
While it is perhaps natural for a downward or upward shake to be used as a gesture, a sideways shake is not as natural. It seems more natural to move ones wrist left. This involves both X and Y axis movements. In fact, the movements combine left X axis movements with downward Y axis movements. The distance traveled is less than a single axis movement. We could detect a "wrist shake" with a small code addition:
if xaxisWindow[0]-meter.x < 5 and max(xaxisWindow)-min(xaxisWindow)>25
and yaxisWindow[0]-meter.y < 5 and max(yaxisWindow)-min(yaxisWindow)>25:
print "WRIST SHAKE"
timeWindow = [now]
xaxisWindow = [meter.x]
yaxisWindow = [meter.y]
return
if xaxisWindow[0]-meter.x < 5 and max(xaxisWindow)-min(xaxisWindow)>40:
print "XSHAKE"
timeWindow = [now]
xaxisWindow = [meter.x]
return
if yaxisWindow[0]-meter.y < 5 and max(yaxisWindow)-min(yaxisWindow)>40:
print "YSHAKE"
timeWindow = [now]
yaxisWindow = [meter.y]
return
Finally, to act on shaking, we should replace the print statements with some other kind of functionality. We would normally think of a callback function in this place, but the reportEvent() function is itself a callback and we cannot send it our own parameters (as would be need to specify our own callback). The alternative is to hardcode a call into the code and specify that function later in the program.
| Extend These Gestures Another common gesture might be to move a phone backwards or forwards along the Z axis. Extend the code above to look for these types of gestures. |
A Word About Older Sensor APIs
We should make a note about the S60 Sensor API.
The S60 Sensor API is a way of accessing sensors on earlier S60 platforms. It is the way you would access sensor data on S60 3rd Edition, S60 3rd Edition Feature Pack 1 and some S60 3rd Edition Feature Pack 2 devices that contain sensor hardware. With this API, you can access these sensors:
- an accelerometer sensor
- a tapping sensor
- a rotation sensor
In addition to a limited number of sensors, the S60 Sensor API was also limited in its capability to listen for sensor data. Like the sensor framework, it raised events and could call a callback function when sensor data was available. However, only one callback could be used for all sensor data. To limit the data that callback would receive, event filters could be used. However, there were only two defined filters: one that focused on orientation changes and one that focused on rotation changes. Other filters developers had to write by themselves for each application.
Sensor Framework is a more complete and flexible interface, one that allows multiple sensors to be tracked and more sensors to be added in the future. The sensor framework is available on most S60 3rd Edition Feature Pack 2 and later phones.
Sign in to comment…



