Header Ads

Get your apps to work with the Xperia™ active pressure sensor

A couple of weeks ago, Sony Ericsson announced the new Xperia™ active – a compact Android smartphone designed for consumers with an active lifestyle. A truly unique feature of the Xperia™ active lies in its pressure sensor for barometric measuring. Developers can use this sensor in apps, by accessing it through the Android sensor API.
This tutorial provides some tips and code examples to get your app to work with the pressure sensor.
Note: This article is also available as a downloadable PDF, as we are currently experiencing problems displaying code examples properly. We apologise for the inconvenience. We are working on a fix for this.
The pressure sensor can be used to generate data input for apps such as weather logs, barometric apps or similar. In this tutorial, you will learn how an app can make use of the pressure sensor. This is easily done in your Android development environment.
Learning the basics
In the following section, we will describe how to create a simple app that is reading data from the pressure sensor and displaying it in a simple text view. You can obviously create much more complex UIs yourself to display this information.
First of all, you must have an activity, to have something to display.
public class PressureSensorDemo extends Activity implements SensorEventListener{
:
As a convenience, the activity implements the sensor listener interface. Your app will connect to the sensor itself using the normal sensor interface. This can be done by the onCreate() method.
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
outView = (TextView) findViewById(R.id.output);
// Real sensor manager
sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
pressureSensor = sensorManager.getDefaultSensor(Sensor.TYPE_PRESSURE);
pressureDataList = new ArrayList();
After this, your app needs to register itself as a SensorEventListener. In this case, we will do it by overwriting the onResume method.
protected void onResume() {
super.onResume();
Log.d(TAG, "onResume");
sensorManager.registerListener(this, pressureSensor, SensorManager.SENSOR_DELAY_NORMAL);
}
Finally, your application will need to implement the SensorEventListener interface methods.
@Override
public void onAccuracyChanged(Sensor sensor, int arg1) {
// TODO Auto-generated method stub
if (sensor.equals(pressureSensor)){
Log.d(LOG_TAG,"Accuracy changed on Pressure Sensor");
} else{
Log.d(LOG_TAG,"Accuracy changed on Other Sensor, odd beahaviour");
}
}
@Override
public void onSensorChanged(SensorEvent sensEvent) {
// TODO Auto-generated method stub
float [] values = sensEvent.values;
StringBuilder sb = new StringBuilder("Received Values are: \n");
for (int i = 0; i < values.length; i++) {
sb.append(values [i] + "\n");>}
String s1 = sb.toString();
Log.d(LOG_TAG,s1);
outView.setText(s1);
}
Something a little bit more then the basics
Another, and perhaps more useful approach, could be to let a service log the data from the pressure sensor to a content provider. You should probably consider taking a few (between five or ten) samples, and select the median value to minimise impact of transient values. Transient values can occur suddenly, with temporary peak values being visible during very small time frames, for example when a door closing while a pressure value is connected. Another example could be a puff of air hitting the detector.
As a next step, we will build a small logger system where a service collecting air pressure data is running. The service is triggered to run from the starting activity first, and it is then restarted at certain intervals using the Alarm Manager.
Building a logger system
To start with, we will implement the actual data collection as a simple class, which starts a separate thread by implementing the Runnable interface. This is done in order to be able to receive a number of callbacks to the SensorListener.
The data collecting class is initialized and run from a service. It takes five samples, inserts them in a list, and selects the median value to be stored in a content provider. The content provider is called from a utility class which we will not describe further here, since there are tons of examples on the Internet on how to create a content provider.
However, when the data collecting class has written to the content provider successfully, the Alarm Manager is notified in order to send a broadcast intent in ten minutes. It will then wake up the device to make sure a sample is logged every ten minutes. The broadcast intent is received in a Broadcast receiver (find a description of it below). The service is stopped using stopSelf when the data collection is done. This is what it looks like:
private class DataCollector implements SensorEventListener, Runnable{
private static final String LOG_TAG = "DataCollectorthread";
private Sensor pressureSensor;
private Thread thr;
private ArrayList pressureDataList = null;
private int numReads;
private DataCollector() {
thr = new Thread(this);
}
public void collectData(){
thr.start();>}
@Override
public void run() {
// TODO Auto-generated method stub
SensorManager sensorManager = SensorManager)getSystemService(SENSOR_SERVICE);
pressureSensor = sensorManager.getDefaultSensor(Sensor.TYPE_PRESSURE);
pressureDataList = newArrayList();
sensorManager.registerListener(this, pressureSensor, SensorManager.SENSOR_DELAY_NORMAL);
while (numReads < PressureUtilities.MAXLENGTHOFLIST){
try {Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println("Timeout " + numReads);
}
sensorManager.unregisterListener(this);
PressuredataObject pdo =
PressureUtilities.selectMedianValue(pressureDataList);
if (PressureUtilities.insertPressureObjectToDB(PressureSensorService.this,pdo) != -1){ //If database is not full – set a new time next time to take sample.
AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
Intent it = new Intent("com.sonyericsson.examples.pressuresensor.sendbroadcast");
PendingIntent pi = PendingIntent.getBroadcast(getApplicationContext(), 5, it, PendingIntent.FLAG_CANCEL_CURRENT );
long now = System.currentTimeMillis();
long alarmTime = now + (1000 * 60 * 10); //Delay alarm 5 seconds, 10 minutes
alarmManager.set(AlarmManager.RTC_WAKEUP, alarmTime, pi); //Repeat once every minute
} else{
Log.v(LOG_TAG, "Database is full");
}
wake_lock.release();
stopSelf();
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
// TODO Auto-generated method stub
}
}
@Override
public void onSensorChanged(SensorEvent sensEvent) {
// TODO Auto-generated method stub
float [] values = sensEvent.values;
PressureUtilities.insertPressureObjectToList(new PressuredataObject(values[0],
DateFormat.getTimeFormat(getApplicationContext()).format(

);
Log.d(LOG_TAG,"Received and stored event" + numReads);
numReads++;
}
}
Now you have to make your service initiate and run the data collection. This is done by initiating a new instance of “DataCollector” class and ordering it to collect data at reception of the onStartCommand method.
public int onStartCommand(Intent intent, int flags, int startId) {
// TODO Auto-generated method stub
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
wake_lock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "PressureData");
wake_lock.acquire();
DataCollector dataCollector = new DataCollector();
dataCollector.collectData();
return startId;
}
As you can see, the service acquires a partial wake lock when it is started. The wake lock is released when the service task is finished. If you have a service handling more than one task, task branching should be considered. In this example, the Broadcast receiver does not play a very big role. Basically it only starts the service.
public class PressureSensorBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context ctxt, Intent intent) {
// TODO Auto-generated method stub
PowerManager pm =
(PowerManager) ctxt.getSystemService(Context.POWER_SERVICE);
WakeLock wake_lock =
pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "PressureData");
wake_lock.acquire();
Intent serviceIntent = new Intent(ctxt,
PressureSensorService.class);
ctxt.startService(serviceIntent);
wake_lock.release();
}
}
Please note that the broadcast receiver is running within a wake lock. What you need to do, is to set up the manifest file to handle the service, broadcast receiver, content provider. Also, don’t forget the permissions!
Example:





:

android:authorities="com.sonyericsson.developerworld.example.pressuresensor.db" />
























If you want to learn more about the Alarm Manager, check out Android’s Alarm Manager references. You can also find this Alarm Manager tutorial and other resources on the Internet.
Now you need to find a way to display your data. However, we leave this up to you since there are so many ways to do this. But we did some test to draw the values to the canvas of a view, with some adjustments to make a graph display values within a fair value range. One thing to keep in mind while doing this, is that normal air pressure most often lies within 980 ~1030 mBar or so on sea level (1 mBar is 1 hPa).
Verifying your pressure sensor application
Now, having created an app that makes use of the pressure sensor, you are obviously interested in verifying it all. The best way to verify your application is of course to use an Xperia™ active device. But as you might know, Xperia™ active is still not available in stores. But until then, there is a way to do some verification anyhow with a bit of tweaking in your code!
Normally your typical app consists of at least two parts. One part that collects data, and one logical part that displays the data. In this case, it is tricky to verify the collection of data, but you can verify how the data is displayed. What you can do, is to feed the data using another sensor, perhaps the accelerometer, and adjust the values to the appropriate air pressure. You can then use this data as input. Since the accelerometer tends to give values between +10 to -10, it is roughly a matter of adding 1013 to the sensor value to get appropriate values.
What apps can you create using the pressure sensor?
We think it is going to be really interesting to see what kind of apps that will make use of this sensor. When brainstorming, we could for example imagine a crowd source weather data collection service, where users send in barometric data to a central server (using text messages, email etc) within certain time intervals.
Another cool idea is to use the pressure sensor to calculate altitude, for example, changes in pressure relative to the altitude. Below is a known formula describing how to calculate the altitude.

Pressure to Altitude Conversion (Troposphere)
1976 US Standard Atmosphere is based on the assumptions:
Zero altitude pressure P0 = 101325 [Pa] (= 1’013.25 mbar)
Zero altitude temperature T0 = 288.15 K
Temperature gradient Tgradient = 6.5/1000 [K/m] in the troposphere
Temperature in the stratosphere = -56.46°C
R is the specific gas constant R=R*/M0 R = 287.052 [J/(K * kg)]
100 Pa = 1 mbar
An altitude change of 8 metres will roughly give a 1mBar difference. At higher altitudes, the pressure will be lower. This could for example be really interesting in workout sessions. In combination with the GPS, some interesting services can probably be created – this is only limited by your creativity!
If you want to more about pressure, check out the mathematics and data in Wikipedia’s Atmospheric pressure article.

No comments:

Powered by Blogger.