RSS

Android: Collecting and Plotting Accelerometer Data

16 Jan

Today we’ll create an app to collect accelerometer reading and plot data in a XY-Line chart.
Accelerometer Reading

Android Sensors and SensorManager:
Android supports several Sensors via SensorManager, for example Accelerometer[http://en.wikipedia.org/wiki/Accelerometer].

1. Project Creation:
Project CreationProject Creation 2

2. Layout:
Now we create the following layout:





        <button>

        </button><button>

        </button><button>





We keep the layout simple by keeping
i. a button to start reading
ii. a button to stop reading and show graph
iii. a button to upload data to server (covered in next post)
iv. a placeholder in the below portion [LinearLayout] as the chart container

Layout

Layout

3. MainActivity.java

First, to listen click events to the buttons and to listen to the SensorEvents we implement the Activity as SensorEventListener and OnClickListener (android.view.View):

package com.smartapps.accel;

import java.util.ArrayList;

import android.app.Activity;
import android.content.Context;
import android.graphics.Color;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.LinearLayout;

public class MainActivity extends Activity implements SensorEventListener,
		OnClickListener {
	private Button btnStart, btnStop, btnUpload;
	private LinearLayout layout;

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		btnStart = (Button) findViewById(R.id.btnStart);
		btnStop = (Button) findViewById(R.id.btnStop);
		btnUpload = (Button) findViewById(R.id.btnUpload);
		btnStart.setOnClickListener(this);
		btnStop.setOnClickListener(this);
		btnUpload.setOnClickListener(this);
		btnStart.setEnabled(true);
		btnStop.setEnabled(false);

                layout = (LinearLayout) findViewById(R.id.chart_container);
		if (sensorData == null || sensorData.size() == 0) {
			btnUpload.setEnabled(false);
		}

	}

	@Override
	protected void onResume() {
		super.onResume();

	}

	@Override
	protected void onPause() {
		super.onPause();

	}

	@Override
	public void onAccuracyChanged(Sensor sensor, int accuracy) {

	}

	@Override
	public void onSensorChanged(SensorEvent event) {

	}

	@Override
	public void onClick(View v) {
		switch (v.getId()) {
		case R.id.btnStart:
			// start reading
			break;
		case R.id.btnStop:
			// stop reading and show data in chart
			break;
		case R.id.btnUpload:
                        // upload data to server (next post)
			break;
		default:
			break;
		}

	}

}

Next we create AccelData class: AccelData.java

package com.smartapps.accel;

public class AccelData {
	private long timestamp;
	private double x;
	private double y;
	private double z;

	public AccelData(long timestamp, double x, double y, double z) {
		this.timestamp = timestamp;
		this.x = x;
		this.y = y;
		this.z = z;
	}
	public long getTimestamp() {
		return timestamp;
	}
	public void setTimestamp(long timestamp) {
		this.timestamp = timestamp;
	}
	public double getX() {
		return x;
	}
	public void setX(double x) {
		this.x = x;
	}
	public double getY() {
		return y;
	}
	public void setY(double y) {
		this.y = y;
	}
	public double getZ() {
		return z;
	}
	public void setZ(double z) {
		this.z = z;
	}

	public String toString()
	{
		return "t="+timestamp+", x="+x+", y="+y+", z="+z;
	}
}

Now we can complete the MainActivity.java:

package com.smartapps.accel;

import java.util.ArrayList;

import org.achartengine.ChartFactory;
import org.achartengine.chart.PointStyle;
import org.achartengine.model.XYMultipleSeriesDataset;
import org.achartengine.model.XYSeries;
import org.achartengine.renderer.XYMultipleSeriesRenderer;
import org.achartengine.renderer.XYSeriesRenderer;

import android.app.Activity;
import android.content.Context;
import android.graphics.Color;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.LinearLayout;

public class MainActivity extends Activity implements SensorEventListener,
		OnClickListener {
	private SensorManager sensorManager;
	private Button btnStart, btnStop, btnUpload;
	private boolean started = false;
	private ArrayList sensorData;
	private LinearLayout layout;
	private View mChart;

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		layout = (LinearLayout) findViewById(R.id.chart_container);
		sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
		sensorData = new ArrayList();

		btnStart = (Button) findViewById(R.id.btnStart);
		btnStop = (Button) findViewById(R.id.btnStop);
		btnUpload = (Button) findViewById(R.id.btnUpload);
		btnStart.setOnClickListener(this);
		btnStop.setOnClickListener(this);
		btnUpload.setOnClickListener(this);
		btnStart.setEnabled(true);
		btnStop.setEnabled(false);
		if (sensorData == null || sensorData.size() == 0) {
			btnUpload.setEnabled(false);
		}

	}

	@Override
	protected void onResume() {
		super.onResume();

	}

	@Override
	protected void onPause() {
		super.onPause();
		if (started == true) {
			sensorManager.unregisterListener(this);
		}
	}

	@Override
	public void onAccuracyChanged(Sensor sensor, int accuracy) {

	}

	@Override
	public void onSensorChanged(SensorEvent event) {
		if (started) {
			double x = event.values[0];
			double y = event.values[1];
			double z = event.values[2];
			long timestamp = System.currentTimeMillis();
			AccelData data = new AccelData(timestamp, x, y, z);
			sensorData.add(data);
		}

	}

	@Override
	public void onClick(View v) {
		switch (v.getId()) {
		case R.id.btnStart:
			btnStart.setEnabled(false);
			btnStop.setEnabled(true);
			btnUpload.setEnabled(false);
			sensorData = new ArrayList();
			// save prev data if available
			started = true;
			Sensor accel = sensorManager
					.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
			sensorManager.registerListener(this, accel,
					SensorManager.SENSOR_DELAY_FASTEST);
			break;
		case R.id.btnStop:
			btnStart.setEnabled(true);
			btnStop.setEnabled(false);
			btnUpload.setEnabled(true);
			started = false;
			sensorManager.unregisterListener(this);
			layout.removeAllViews();
			openChart();

			// show data in chart
			break;
		case R.id.btnUpload:

			break;
		default:
			break;
		}

	}

	private void openChart() {
		if (sensorData != null || sensorData.size() > 0) {
			long t = sensorData.get(0).getTimestamp();
			XYMultipleSeriesDataset dataset = new XYMultipleSeriesDataset();

			XYSeries xSeries = new XYSeries("X");
			XYSeries ySeries = new XYSeries("Y");
			XYSeries zSeries = new XYSeries("Z");

			for (AccelData data : sensorData) {
				xSeries.add(data.getTimestamp() - t, data.getX());
				ySeries.add(data.getTimestamp() - t, data.getY());
				zSeries.add(data.getTimestamp() - t, data.getZ());
			}

			dataset.addSeries(xSeries);
			dataset.addSeries(ySeries);
			dataset.addSeries(zSeries);

			XYSeriesRenderer xRenderer = new XYSeriesRenderer();
			xRenderer.setColor(Color.RED);
			xRenderer.setPointStyle(PointStyle.CIRCLE);
			xRenderer.setFillPoints(true);
			xRenderer.setLineWidth(1);
			xRenderer.setDisplayChartValues(false);

			XYSeriesRenderer yRenderer = new XYSeriesRenderer();
			yRenderer.setColor(Color.GREEN);
			yRenderer.setPointStyle(PointStyle.CIRCLE);
			yRenderer.setFillPoints(true);
			yRenderer.setLineWidth(1);
			yRenderer.setDisplayChartValues(false);

			XYSeriesRenderer zRenderer = new XYSeriesRenderer();
			zRenderer.setColor(Color.BLUE);
			zRenderer.setPointStyle(PointStyle.CIRCLE);
			zRenderer.setFillPoints(true);
			zRenderer.setLineWidth(1);
			zRenderer.setDisplayChartValues(false);

			XYMultipleSeriesRenderer multiRenderer = new XYMultipleSeriesRenderer();
			multiRenderer.setXLabels(0);
			multiRenderer.setLabelsColor(Color.RED);
			multiRenderer.setChartTitle("t vs (x,y,z)");
			multiRenderer.setXTitle("Sensor Data");
			multiRenderer.setYTitle("Values of Acceleration");
			multiRenderer.setZoomButtonsVisible(true);
			for (int i = 0; i < sensorData.size(); i++) {

				multiRenderer.addXTextLabel(i + 1, ""
						+ (sensorData.get(i).getTimestamp() - t));
			}
			for (int i = 0; i < 12; i++) {
				multiRenderer.addYTextLabel(i + 1, ""+i);
			}

			multiRenderer.addSeriesRenderer(xRenderer);
			multiRenderer.addSeriesRenderer(yRenderer);
			multiRenderer.addSeriesRenderer(zRenderer);

			// Getting a reference to LinearLayout of the MainActivity Layout

			// Creating a Line Chart
			mChart = ChartFactory.getLineChartView(getBaseContext(), dataset,
					multiRenderer);

			// Adding the Line Chart to the LinearLayout
			layout.addView(mChart);

		}
	}
}

SensorManager lets you access the device’s sensors. Get an instance of this class by calling Context.getSystemService() with the argument SENSOR_SERVICE. [http://developer.android.com/reference/android/hardware/SensorManager.html]

Always make sure to disable sensors you don’t need, especially when your activity is paused. Failing to do so can drain the battery in just a few hours. Note that the system will not disable sensors automatically when the screen turns off.

Here we have used aChartEngine to draw chart. To use aChartEngine library you have to download a jar file from here: http://achartengine.googlecode.com/files/achartengine-1.0.0.jar

Copy the downloaded jar file to the libs folder of your project and then add the jar to build path:

Adding jar to Build Path of the Project

Adding jar to Build Path of the Project

Please let me know if you face problems in comments.

Download source code from here: http://www.mediafire.com/?rq49zovwij74w4m

In the next tutorial we’ll upload these data to a php server.

 
17 Comments

Posted by on January 16, 2013 in Uncategorized

 

17 responses to “Android: Collecting and Plotting Accelerometer Data

  1. basakung

    January 25, 2013 at 1:37 pm

    Could you please upload the next tutorial ” upload sensors data from android phone to a php server” as soon as possible? I need your guide to do my project, your article is very useful for me.
    Thank you very much

     
  2. Peter

    March 11, 2013 at 4:30 am

    I would be interested in seeing a good method for removing gravity from the accelerometer measurements/sensor events. I know there is the Sensor.TYPE_LINEAR_ACCELERATION recently added to the api, but a lot of androids do not support this sensor type.

     
  3. marwanissa

    May 26, 2013 at 2:56 am

    thank you Sir I try to implement you code as new project in eclips the views are working but the char part is not working and it terminate the application giving me these errors

    05-26 03:44:57.570: E/AndroidRuntime(16824): FATAL EXCEPTION: main
    05-26 03:44:57.570: E/AndroidRuntime(16824): java.lang.NoClassDefFoundError: org.achartengine.model.XYMultipleSeriesDataset
    05-26 03:44:57.570: E/AndroidRuntime(16824): at com.ncl.accel.MainActivity.openChart(MainActivity.java:126)
    05-26 03:44:57.570: E/AndroidRuntime(16824): at com.ncl.accel.MainActivity.onClick(MainActivity.java:110)
    05-26 03:44:57.570: E/AndroidRuntime(16824): at android.view.View.performClick(View.java:4211)
    05-26 03:44:57.570: E/AndroidRuntime(16824): at android.view.View$PerformClick.run(View.java:17267)
    05-26 03:44:57.570: E/AndroidRuntime(16824): at android.os.Handler.handleCallback(Handler.java:615)
    05-26 03:44:57.570: E/AndroidRuntime(16824): at android.os.Handler.dispatchMessage(Handler.java:92)
    05-26 03:44:57.570: E/AndroidRuntime(16824): at android.os.Looper.loop(Looper.java:137)
    05-26 03:44:57.570: E/AndroidRuntime(16824): at android.app.ActivityThread.main(ActivityThread.java:4898)
    05-26 03:44:57.570: E/AndroidRuntime(16824): at java.lang.reflect.Method.invokeNative(Native Method)
    05-26 03:44:57.570: E/AndroidRuntime(16824): at java.lang.reflect.Method.invoke(Method.java:511)
    05-26 03:44:57.570: E/AndroidRuntime(16824): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1006)
    05-26 03:44:57.570: E/AndroidRuntime(16824): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:773)
    05-26 03:44:57.570: E/AndroidRuntime(16824): at dalvik.system.NativeStart.main(Native Method)

     
    • Veli Burak Çelen

      July 24, 2013 at 10:06 pm

      I have the same problem too. I am using eclipse + adt. There is a achartengine.jar under the /libs class I right click the project and than “Properties” “”Configure build path” and check the box of achartengine-1.0.0.jar . I clean the project and run than it works.

       
  4. marwanissa

    May 26, 2013 at 3:09 am

    I import our code to eclips also it doesnt work I think the problem from the chart jar(achartengine)

     
    • androidstream

      May 29, 2013 at 9:00 am

      Can you please post the console output/errors in problems window?

       
  5. mwaka

    May 29, 2013 at 5:50 am

    HI I’m new to this. I was able to successfully run this app on my phone but could you telling me what the Upload button is doing? Is it saving the data somewhere else?

     
    • androidstream

      May 29, 2013 at 8:59 am

      That button is non-functional. Not implemented in this tutorial.

       
      • mwaka

        May 29, 2013 at 4:25 pm

        Oh i see
        thanks! cant wait for the next tutorial 😀

         
  6. Yan Chua

    July 11, 2013 at 1:38 pm

    please…
    may i know how to save the accelerometer data?

     
  7. aravinth4blue

    August 25, 2013 at 7:13 am

    Type Mismatch Cannot convert from element type Object to AccelData

     
  8. qtmimi8

    February 2, 2014 at 9:09 pm

    Hi, this is a good one. Honestly, I am too new of this and need your help for my project. I have downloaded the JAR file and put it in the build path. But it gives me error. Do you mind to post the xml too? Cus I dont find any way to build the chart interface. Please help!

     
  9. qtmimi8

    February 3, 2014 at 1:59 am

    Owh, finally I managed to run the application by putting ArrayList(). But unluckily, when I push stop, the chart does not comes out and crashed. What should I do?

     
  10. ashokslsk

    February 15, 2014 at 1:53 am

    Hi ,
    This is awesome i am amazed with this tuts, Sir can you just give me hint how can i turn this code into realtime accelerometer plotting graph and how can i overlay this graph on some other data that would help my academic project as well thanks in adavance hope you will help me .

    Regards
    Ashok Kumar.S

     
  11. shawping

    April 8, 2014 at 4:53 pm

    hi, im new to this too, your code is great and have you posted the upload function. i wish to have a guide on it ..thanks a lot

     
  12. V.Vamsa vardhana

    April 30, 2014 at 10:40 am

    Is there any coding required in xml for R.id.btnStart, R.id.btnPause and R.id.btnStop? Because I am getting the errors-
    btnStart cannot be resolved or is not a field.
    btnPause cannot be resolved or is not a field.
    btnStop cannot be resolved or is not a field.

     

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

 
%d bloggers like this: