Android Adventures, part 3: hardware detection in Processing
The main reason I got my Android phone was to get Processing running on it, and start grabbing the sensor data. There were no complete examples (I could find) on how to actually do this though. After two days of research I got it working. Some takeaways:
- I learned that Processing is ran as an Android “Activity“: It enters the Activity at the onResume() state, and exits it at the onPause() state. You need to override these in your sketch to do what you want. See the above link to a nice image that shows the state tree.
- All the code you need to author to setup your SensorManagers, SensorEventListeners, and Sensors, needs to happen in the onResume() function: Putting this stuff in the sketch’s setup() function won’t work.
- You can make a single SensorManager, but for each sensor you want to track you need to make a unique SensorEventListener, and Sensor.
- Once I figured it out I realized how easy it actually is. Like most things 😉
Here is a list of resources I pulled from, in order of discovery/usefulness:
- http://wiki.processing.org/w/Android – The main ‘Processing Android’ wiki page.
- http://forum.processing.org/topic/reading-the-compass – Forum reply by Ben Fry that got me started.
- http://developer.android.com/resources/samples/ApiDemos/src/com/example/android/apis/graphics/Compass.html – Android API example. Outdated (uses deprecated code), but good concepts.
- http://developer.android.com/reference/packages.html – the Android API docs.
- http://www.netmite.com/android/mydroid/cupcake/development/samples/Compass/src/com/example/android/compass/CompassActivity.java – Another example I pulled from.
A (cropped) screenshot off the Phone of the exciting final product!
And the code:
// android_sensorData // Eric Pavey - 2010-10-10 // http://www.akeric.com // // Query the phone's accelerometer and magnetic field data, display on screen. // Made with Android 2.1, Processing 1.2 //----------------------------------------------------------------------------------------- // Imports required for sensor usage: import android.content.Context; import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorManager; import android.hardware.SensorEventListener; //----------------------------------------------------------------------------------------- // Screen Data: float sw, sh; // Font Data: String[] fontList; PFont androidFont; // Setup variables for the SensorManager, the SensorEventListeners, // the Sensors, and the arrays to hold the resultant sensor values: SensorManager mSensorManager; MySensorEventListener accSensorEventListener; MySensorEventListener magSensorEventListener; Sensor acc_sensor; float[] acc_values; Sensor mag_sensor; float[] mag_values; //----------------------------------------------------------------------------------------- void setup() { size(screenWidth, screenHeight, A2D); sw = screenWidth; sh = screenHeight; // Set this so the sketch won't reset as the phone is rotated: orientation(PORTRAIT); // Setup Fonts: fontList = PFont.list(); androidFont = createFont(fontList[0], 16, true); textFont(androidFont); } //----------------------------------------------------------------------------------------- void draw() { fill(0); rect(0,0,sw,sh); fill(255); if (acc_values != null) { text(("Accelerometer: " + acc_values[0] + " " + acc_values[1] + " " + acc_values[2]), 8, 20); } else { text("Accelerometer: null", 8, 20); } if(mag_values != null) { text(("Magnetic Field: " + mag_values[0] + " " + mag_values[1] + " " + mag_values[2]), 8, 40); } else { text("Magnetic Field: null", 8, 40); } } //----------------------------------------------------------------------------------------- // Override the parent (super) Activity class: // States onCreate(), onStart(), and onStop() aren't called by the sketch. Processing is entered at // the 'onResume()' state, and exits at the 'onPause()' state, so just override them: void onResume() { super.onResume(); println("RESUMED! (Sketch Entered...)"); // Build our SensorManager: mSensorManager = (SensorManager)getSystemService(Context.SENSOR_SERVICE); // Build a SensorEventListener for each type of sensor: magSensorEventListener = new MySensorEventListener(); accSensorEventListener = new MySensorEventListener(); // Get each of our Sensors: acc_sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); mag_sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD); // Register the SensorEventListeners with their Sensor, and their SensorManager: mSensorManager.registerListener(accSensorEventListener, acc_sensor, SensorManager.SENSOR_DELAY_GAME); mSensorManager.registerListener(magSensorEventListener, mag_sensor, SensorManager.SENSOR_DELAY_GAME); } void onPause() { // Unregister all of our SensorEventListeners upon exit: mSensorManager.unregisterListener(accSensorEventListener); mSensorManager.unregisterListener(magSensorEventListener); println("PAUSED! (Sketch Exited...)"); super.onPause(); } //----------------------------------------------------------------------------------------- // Setup our SensorEventListener class MySensorEventListener implements SensorEventListener { void onSensorChanged(SensorEvent event) { int eventType = event.sensor.getType(); if(eventType == Sensor.TYPE_ACCELEROMETER) { acc_values = event.values; } else if(eventType == Sensor.TYPE_MAGNETIC_FIELD) { mag_values = event.values; } } void onAccuracyChanged(Sensor sensor, int accuracy) { // do nuthin'... } }