Iam trying to plot realtime signal from arduino to my android tablet. I've using a potentiometer for testing purpose and ADC sampling rate is 256Hz. My issue is that the graph plotted on the android app is not smooth. The serial communication is working properly but the plotting is not very good.It feels like the App is lagging and it lags further as the data gets plotted.
Below is the app image
package com.example.ecgauth;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.IntentFilter;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbManager;
import android.os.Bundle;
import android.Manifest;
import android.app.Activity;
import android.bluetooth.BluetoothSocket;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.graphics.Color;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
//import android.support.v4.app.ActivityCompat;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.ToggleButton;
import com.felhr.usbserial.UsbSerialDevice;
import com.felhr.usbserial.UsbSerialInterface;
import com.jjoe64.graphview.GraphView;
import com.jjoe64.graphview.GraphView.GraphViewData;
import com.jjoe64.graphview.GraphView.LegendAlign;
import com.jjoe64.graphview.GraphViewSeries;
import com.jjoe64.graphview.GraphViewSeries.GraphViewStyle;
import com.jjoe64.graphview.LineGraphView;
import java.util.HashMap;
import java.util.Map;
public class MainActivity extends Activity implements View.OnClickListener{
private static final String ACTION_USB_PERMISSION = "com.example.ecg.USB_PERMISSION";
TextView hr_value_text;
UsbDevice device;
UsbDeviceConnection connection;
UsbManager usbManager;
UsbSerialDevice serialPort;
PendingIntent pendingIntent;
// private Handler mHandler;
public static final int MESSAGE_FROM_SERIAL_PORT = 0;
final static int gMAX_BLOCKS = 32; //Maximum blocks
final static int gMAX_SAMPLES = 8; //'Maximum samples
final static int gMAX_CHANNELS = 4; //'Maximum channels
final static int gSampFreq = 256; //Sampling frequency
final static int gRawBufferSize = gMAX_BLOCKS * gMAX_SAMPLES ;
final static float swpfactor = 0;
final static int gSec =0;
int sample =0;
private static boolean acq1 = false;
static int block =0;
int g_intRawBuffer[][] = new int[gMAX_BLOCKS][gMAX_SAMPLES];
double g_sngDnOutBuf[] = new double[gMAX_SAMPLES ];
@Override
public void onBackPressed() {
// // TODO Auto-generated method stub
// if (Bluetooth.connectedThread != null) {
// Bluetooth.connectedThread.write("Q");}//Stop streaming
super.onBackPressed();
}
//toggle Button
static boolean Lock;//whether lock the x-axis to 0-5
static boolean AutoScrollX;//auto scroll to the last x value
static boolean Stream;//Start or stop streaming
int old_interval=0;
int new_interval=0;
int mean_interval=20;
//Button init
Button bXminus;
Button bXplus;
ToggleButton tbLock;
ToggleButton tbScroll;
ToggleButton tbStream;
boolean startthread = true;
//GraphView init
static LinearLayout GraphView;
static GraphView graphView;
static GraphViewSeries Series;
//graph value
private static double graph2LastXValue = 0;
private static int Xview=1023;
Button bConnect, bDisconnect;
private TextView connectionstatus;
public int control_a = 0;
public int TotalSample=0;
char chdata = 0;
int i =0;
String finaldata = "";
String rawdata = "";
byte highbyte=0;
int data = 0 ;
int plotdata = 0 ;
int databuff = 0;
public int[] buffer_bt = new int[768];
public int[] copy_buffer_bt = new int[768];
public int ind_bt = 0;
UsbSerialInterface.UsbReadCallback mCallback = new UsbSerialInterface.UsbReadCallback() { //Defining a Callback which triggers whenever data is read.
@Override
public void onReceivedData(byte[] arg0) {
// lowbyte = arg0;
// highbyte = arg0;
byte[] buffer = arg0;
for (int i = 0; i <= (buffer.length - 1); i++) {
if (buffer[i] != 13) {
if (buffer[i] == 10) {
finaldata = rawdata;
rawdata = "";
} else {
chdata = (char) buffer[i];
rawdata += chdata;
}
}
}
data = Integer.parseInt(finaldata);
mHandler.obtainMessage(MESSAGE_FROM_SERIAL_PORT, data).sendToTarget();
control_a = 1;
buffer_bt[databuff] = data;
// }
//lowbyte = buffer;
//highbyte = buffer;
databuff = databuff + 1;
if (databuff == 767){
databuff=0;
}
//
// for (int uu=0; uu<=767; uu++) {
// copy_buffer_bt[uu] = buffer_bt[uu];
// }
// databuff=0;
// ready_bt=1;
// threadon = true;
// }
}
};
@Override
protected void onPause() {
super.onPause();
unregisterReceiver(broadcastReceiver);
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
requestWindowFeature(Window.FEATURE_NO_TITLE);//Hide title
this.getWindow().setFlags(WindowManager.LayoutParams.
FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN);//Hide Status bar
setContentView(R.layout.activity_main);
//set background color
LinearLayout background = (LinearLayout)findViewById(R.id.bg);
background.setBackgroundColor(Color.BLACK);
// mHandler = new Handler();
//GraphView = (LinearLayout) findViewById(R.id.Graph);
usbManager = (UsbManager) getSystemService(this.USB_SERVICE);
connectionstatus = (TextView) findViewById(R.id.tvBluetooth);
bConnect = (Button)findViewById(R.id.bConnect);
bConnect.setOnClickListener(this);
bDisconnect = (Button)findViewById(R.id.bDisconnect);
bDisconnect.setOnClickListener(this);
//X-axis control button
bXminus = (Button)findViewById(R.id.bXminus);
bXminus.setOnClickListener(this);
bXplus = (Button)findViewById(R.id.bXplus);
bXplus.setOnClickListener(this);
//
tbLock = (ToggleButton)findViewById(R.id.tbLock);
tbLock.setOnClickListener(this);
tbScroll = (ToggleButton)findViewById(R.id.tbScroll);
tbScroll.setOnClickListener(this);
tbStream = (ToggleButton)findViewById(R.id.tbStream);
tbStream.setOnClickListener(this);
//init toggleButton
Lock=true;
AutoScrollX=true;
Stream=true;
IntentFilter filter = new IntentFilter();
filter.addAction(ACTION_USB_PERMISSION);
filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
registerReceiver(broadcastReceiver, filter);
init();
}
private final BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(ACTION_USB_PERMISSION)) {
boolean granted =
intent.getExtras().getBoolean(UsbManager.EXTRA_PERMISSION_GRANTED);
if (granted) {
connection = usbManager.openDevice(device);
serialPort = UsbSerialDevice.createUsbSerialDevice(device, connection);
if (serialPort != null) {
if (serialPort.open()) { //Set Serial Connection Parameters.
//setUiEnabled(true); //Enable Buttons in UI
serialPort.setBaudRate(57600);
serialPort.setDataBits(UsbSerialInterface.DATA_BITS_8);
serialPort.setStopBits(UsbSerialInterface.STOP_BITS_1);
serialPort.setParity(UsbSerialInterface.PARITY_NONE);
serialPort.setFlowControl(UsbSerialInterface.FLOW_CONTROL_OFF);
serialPort.read(mCallback); //
//connectionstatus.setText("Connected");
tvAppend(connectionstatus, " Opened!\n");
} else {
Log.d("SERIAL", "PORT NOT OPEN");
}
} else {
Log.d("SERIAL", "PORT IS NULL");
}
} else {
Log.d("SERIAL", "PERM NOT GRANTED");
}
} else if (intent.getAction().equals(UsbManager.ACTION_USB_DEVICE_ATTACHED)) {
onConnect(bConnect);
} else if (intent.getAction().equals(UsbManager.ACTION_USB_DEVICE_DETACHED)) {
// onClickStop(buttonRestart);
}
}
};
public void onConnect(View view){
HashMap<String, UsbDevice> usbDevices = usbManager.getDeviceList();
if (!usbDevices.isEmpty()) {
boolean keep = true;
for (Map.Entry<String, UsbDevice> entry : usbDevices.entrySet()) {
device = entry.getValue();
int deviceVID = device.getVendorId();
if (deviceVID == 0x10C4)//Arduino Vendor ID//0x10C4 cp2102 VID
{
PendingIntent pi = PendingIntent.getBroadcast(this, 0, new Intent(ACTION_USB_PERMISSION), 0);
usbManager.requestPermission(device, pi);
keep = false;
} else {
connection = null;
device = null;
}
if (!keep)
break;
}
}
}
private void tvAppend (TextView tv, CharSequence text){
connectionstatus.setText(" ");
final TextView ftv = tv;
final CharSequence ftext = text;
runOnUiThread(new Runnable() {
@Override
public void run() {
ftv.append(ftext);
}
});
}
void init(){
//Bluetooth.gethandler(mHandler);
//init graphview
GraphView = (LinearLayout) findViewById(R.id.Graph);
// init example series data-------------------
Series = new GraphViewSeries("Signal",
new GraphViewStyle(Color.YELLOW, 2),//color and thickness of the line
new GraphViewData[] {new GraphViewData(0, 0)});
graphView = new LineGraphView(
this // context
, "Electrocardiogram AUTH" // heading
);
graphView.setViewPort(0, Xview);
graphView.setScrollable(true);
graphView.setScalable(false);
graphView.setShowLegend(false);
//graphView.setLegendAlign(LegendAlign.BOTTOM);
graphView.setManualYAxis(true);
graphView.setManualYAxisBounds(2560, 0);
graphView.addSeries(Series); // data
GraphView.addView(graphView);
}
Handler mHandler = new Handler(){
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
switch (msg.what){
case MESSAGE_FROM_SERIAL_PORT:
plotdata = data;
g_intRawBuffer[block][TotalSample] = plotdata;
TotalSample = TotalSample+1;
if (TotalSample == 8) {
TotalSample = 0;
for (int sample = 0; sample <= (gMAX_SAMPLES - 1); sample++) {
g_sngDnOutBuf[sample] = g_intRawBuffer[block][sample];
}
for (int sample = 0; sample <= (gMAX_SAMPLES - 1); sample++) {
Series.appendData(new GraphViewData(graph2LastXValue, g_sngDnOutBuf[sample]), AutoScrollX);
if (graph2LastXValue >= Xview && Lock == true) {
Series.resetData(new GraphViewData[]{});
graph2LastXValue = 0;
}else
graph2LastXValue += 0.5;
if (Lock == true)
graphView.setViewPort(0, Xview);
else
graphView.setViewPort(graph2LastXValue - Xview, Xview);
//refresh
GraphView.removeView(graphView);
GraphView.addView(graphView);
}
}
}
}
};
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
switch(v.getId()){
case R.id.bConnect:
HashMap<String, UsbDevice> usbDevices = usbManager.getDeviceList();
if (!usbDevices.isEmpty()) {
boolean keep = true;
for (Map.Entry<String, UsbDevice> entry : usbDevices.entrySet()) {
device = entry.getValue();
int deviceVID = device.getVendorId();
if (deviceVID == 0x2341)//Arduino Vendor 2341 ID//0x10C4 cp2102 VID
{
PendingIntent pi = PendingIntent.getBroadcast(this, 0, new Intent(ACTION_USB_PERMISSION), 0);
usbManager.requestPermission(device, pi);
keep = false;
} else {
connection = null;
device = null;
}
if (!keep)
break;
}
}
break;
case R.id.bDisconnect:
serialPort.close();
tvAppend(connectionstatus, "Serial Connection Closed!\n");
break;
case R.id.bXminus:
if (Xview<30) Xview++;
break;
case R.id.bXplus:
if (Xview>1) Xview--;
break;
case R.id.tbLock:
if (tbLock.isChecked()){
Lock = true;
}else{
Lock = false;
}
break;
case R.id.tbScroll:
if (tbScroll.isChecked()){
AutoScrollX = true;
}else{
AutoScrollX = false;
}
break;
case R.id.tbStream:
if (tbStream.isChecked()){
serialPort.write("a".getBytes());
}else{
serialPort.write("z".getBytes());
control_a = 0;
}
break;
}
}
}