Creating Real-Time Graphs in Android App for Bluetooth Pulse Rate Sensor

stefbmt picture stefbmt · Sep 29, 2014 · Viewed 10k times · Source

I'm currently developing an Android Application which connects via Bluetooth Low Energy to an Arduino which has the nRF8001 Bluetooth Low Energy Module with a connect the SEN-11574 Pulse Rate Sensor available at Spark Fun ( https://www.sparkfun.com/products/11574 ).

I have been trying to implement a graph which plots the data on the graph every time I tap on 'Get Heart Rate' button but I'm only managing the following:

screenshot

I've researched a number of different resources which include the following yet unfortunately they don't answer my question:

1,2,3,4,5,6,7

I am using the Android Graph View Library to try and solve my problem as it offers the best integration with Android Studio.

I am struggling with three main things:

  1. Labelling the X and Y-Values properly
  2. Appending the Heart Rate Sensor Data to the Graph

The full project is available at : Arduino Pulse Rate

I seem to be struggling the most with these two sections of code:

     private void writeSensorData(final CharSequence text) {
    runOnUiThread(new Runnable() {
        @Override
        public void run() {
            Log.e(LOG_TAG,text.toString());
            output=text.toString().trim();

            if (output.length() > 0 && output.length() <=3) {
                pulseRateView.setText(output);
                rateSeries.appendData(new GraphView.GraphViewData(graph2LastXValue,Double.parseDouble(output)),AutoScrollX,maxDataCount);
            }
            else {
                return;
            }
        }
    });
}

     @Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_pulse);

    //Connect U.I Elements
    getPulseRate = (Button)findViewById(R.id.heartRateBtn);
    pulseRateView = (TextView) findViewById(R.id.pulseValueView);
    connectionStsView = (TextView) findViewById(R.id.connectionStsView);
    refreshButton = (Button) findViewById(R.id.refreshBtn);

    // init heart rate series data
    rateSeries = new GraphViewSeries(new GraphView.GraphViewData[] {
    });

    GraphView graphView = new LineGraphView(this, "Pulse Rate Sensor");

    graphView.addSeries(rateSeries);
    graphView.setScrollable(true);
    graphView.setScalable(true);

    LinearLayout layout = (LinearLayout) findViewById(R.id.graph1);
    layout.addView(graphView);

    getPulseRate.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            String setOutputMessage = "/bpm /";
            tx.setValue(setOutputMessage.getBytes(Charset.forName("UTF-8")));
            if (gatt.writeCharacteristic(tx)) {
                writeConnectionData("Sent: " + setOutputMessage);
            } else {
                writeConnectionData("Couldn't write TX characteristic!");
            }
        }
    });

    refreshButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            restartScan();
        }
    });
}

Would appreciate any help and thank you for your time.

Answer

csvan picture csvan · Sep 30, 2014

The solution below configures your graph to achieve the basic layout and functionality you want. Note that I have bound a sample graph update to the clicking of the refresh button - you can bind a similar procedure to your interface with the actual pulse device. All you need is the time-bpm pair to be plotted to the x and y axes, respectively.

PulseActivity#onCreate

    // Local variable for simulating the sample time
    private int x = 0;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_pulse);

        //Connect U.I Elements
        getPulseRate = (Button) findViewById(R.id.heartRateBtn);
        pulseRateView = (TextView) findViewById(R.id.pulseValueView);
        connectionStsView = (TextView) findViewById(R.id.connectionStsView);
        refreshButton = (Button) findViewById(R.id.refreshBtn);

        final GraphView graphView = new LineGraphView(
                this, // context
                "Pulse Rate Sensor" // heading
        );

        graphView.setVerticalLabels(new String[]{"high", "normal", "low"});

        graphView.setCustomLabelFormatter(new CustomLabelFormatter() {
            @Override
            public String formatLabel(double value, boolean isValueX) {
                if (isValueX) {
                    return null;
                } else {
                    if (value < 60) {
                        return "low";
                    } else if (value < 100) {
                        return "normal";
                    } else {
                        return "high";
                    }
                }
            }
        });

        // init heart rate series data
        rateSeries = new GraphViewSeries(new GraphView.GraphViewData[]{
        });
        graphView.addSeries(rateSeries);

        LinearLayout layout = (LinearLayout) findViewById(R.id.graph1);
        layout.addView(graphView);

        getPulseRate.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                String setOutputMessage = "/bpm /";
                tx.setValue(setOutputMessage.getBytes(Charset.forName("UTF-8")));
                if (gatt.writeCharacteristic(tx)) {
                    writeConnectionData("Sent: " + setOutputMessage);
                } else {
                    writeConnectionData("Couldn't write TX characteristic!");
                }
            }
        });

        refreshButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                //restartScan();
                rateSeries.appendData(new GraphView.GraphViewData(x += 3, new Random().nextInt(100)), false, 100);
                graphView.redrawAll();
            }
        });
    }

activity_pulse.xml

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">

    <LinearLayout
        android:orientation="vertical"
        android:layout_width="fill_parent"
        android:layout_height="250dip"
        android:id="@+id/graph1"
        android:layout_marginLeft="5dp"
        android:layout_marginRight="5dp" />

    <LinearLayout
        android:id="@+id/rest"
        android:layout_width="fill_parent"
        android:layout_height="250dip"
        android:orientation="vertical">

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Refresh Connection"
            android:id="@+id/refreshBtn"
            android:layout_gravity="center_horizontal" />

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Get Heart Rate"
            android:id="@+id/heartRateBtn"
            android:layout_gravity="center_horizontal" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Last Pulse Rate Value"
            android:id="@+id/pulsePlaceholderText"
            android:layout_gravity="center_horizontal" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textAppearance="?android:attr/textAppearanceLarge"
            android:text="Large Text"
            android:id="@+id/pulseValueView"
            android:layout_gravity="center_horizontal" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textAppearance="?android:attr/textAppearanceLarge"
            android:text="Connection Status"
            android:id="@+id/connectionStsView"
            android:layout_gravity="center_horizontal" />
    </LinearLayout>

</LinearLayout>