Android OutputStream.write combines multiple messages - android

I have an arduino car which I wish to control via Bluetooth from my android phone. I've made an application for this, but when i try to send multiple consecutive messages through my OutputStream.write function, they get sent as a single, concatinated message. For example: if I call connectedThread.write("200-100"); and then connectedThread.write("300-150"), on my serial monitor it shows up as a single message: "200-100300-150". I have seen this same problem in other StackOverflow posts ( OutputStream.write() never ends , Android outputStream.write send multiple messages ), but they don't have an answer that works for me, I tried using connectedThread.flush() and it doesn't help. Here is my code (the connecting part is in another Activity, and it works fine):
package com.clickau.cosmin.bluetootharduinocarcontroller;
import android.bluetooth.BluetoothSocket;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.widget.SeekBar;
import android.widget.Toast;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public class Controls extends AppCompatActivity {
private SeekBar speed;
private SeekBar direction;
private BluetoothSocket btSocket = DataHolder.getData();
private ConnectedThread connectedThread;
private int speedValue;
private int directionValue;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_controls);
speed = (SeekBar) findViewById(R.id.speed);
direction = (SeekBar) findViewById(R.id.direction);
speedValue = speed.getProgress();
directionValue = direction.getProgress();
speed.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
#Override
public void onProgressChanged(SeekBar seekBar, int progressValue, boolean fromUser)
{
speedValue = speed.getProgress()/5;
speedValue*=5;
directionValue = direction.getProgress()/5;
directionValue*=5;
/*StringBuilder str = new StringBuilder();
if (speedValue == 255)
str.append("STOP");
else
{
if (speedValue > 255)
{
str.append(speedValue - 255);
str.append('F');
}
else if (speedValue < 255)
{
str.append(255-speedValue);
str.append('S');
}
if (directionValue > 100)
{
str.append('D');
str.append(directionValue-100);
}
else if (directionValue < 100)
{
str.append('S');
str.append(100 - directionValue);
}
}
str.append('\n');
connectedThread.write(str.toString());*/
connectedThread.write(speedValue+ "+" + directionValue);
}
#Override
public void onStartTrackingTouch(SeekBar seekBar)
{
}
#Override
public void onStopTrackingTouch(SeekBar seekBar)
{
speed.setProgress(255);
speedValue = speed.getProgress();
}
});
direction.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
#Override
public void onProgressChanged(SeekBar seekBar, int progressValue, boolean fromUser)
{
speedValue = speed.getProgress()/5;
speedValue*=5;
directionValue = direction.getProgress()/5;
directionValue*=5;
/*StringBuilder str = new StringBuilder();
if (speedValue == 255)
str.append("STOP");
else
{
if (speedValue > 255)
{
str.append(speedValue - 255);
str.append('F');
}
else if (speedValue < 255)
{
str.append(255-speedValue);
str.append('S');
}
if (directionValue > 100)
{
str.append('D');
str.append(directionValue-100);
}
else if (directionValue < 100)
{
str.append('S');
str.append(100 - directionValue);
}
}
str.append('\n');
connectedThread.write(str.toString());*/
connectedThread.write(speedValue+ "+" + directionValue);
}
#Override
public void onStartTrackingTouch(SeekBar seekBar)
{
}
#Override
public void onStopTrackingTouch(SeekBar seekBar)
{
direction.setProgress(100);
directionValue = direction.getProgress();
}
});
connectedThread = new ConnectedThread(btSocket);
connectedThread.start();
}
private class ConnectedThread extends Thread
{
private final InputStream mmInStream;
private final OutputStream mmOutStream;
public ConnectedThread(BluetoothSocket socket)
{
InputStream tmpIn = null;
OutputStream tmpOut = null;
try{
tmpIn = socket.getInputStream();
tmpOut = socket.getOutputStream();
}catch(IOException e) {
Log.e("ERROR", "Connected Thread");
}
mmInStream = tmpIn;
mmOutStream = tmpOut;
}
/*public void run()
{
byte[] buffer = new byte[256];
int bytes;
while (true)
{
try{
bytes = mmInStream.read(buffer);
String readMessage = new String(buffer, 0, bytes);
bluetoothIn.obtainMessage(handlerState, bytes, -1 ,readMessage).sendToTarget();
} catch (IOException e)
{
break;
}
}
}*/
public void write(String input)
{
byte[] msgBuffer = input.getBytes();
try{
mmOutStream.write(msgBuffer);
mmOutStream.flush();
}catch(IOException e)
{
Toast.makeText(getBaseContext(), "Connection Failure", Toast.LENGTH_LONG).show();
finish();
}
}
}
}
I think this might be an optimization for bluetooth, but it's really annoying because I need those messages to be sent sepparatly when I move the controls, not 10 seconds afterwards. Anyone know how to solve this?

Related

set delay between send and receive data from bluetooth

I set a program to send data from the android app to a microcontroller through bluetooth using SPP. mentioned microcontroller device sends a response back after 150 milisec as processing time. My app receives an appropriate response by bluetooth response handler as RecieveBuffer.And application must send data again for the case when the microcontroller doesn't send an appropriate. I used a while conditional statement like below to send data alternatively until getting a response (send flag is true after app gets an appropriate response by bluetooth response handler class).
while(!SendFlag) SendData( SendBuffer+"\r" );
there is a program that my program sends data consecutively and Bluetooth response handler never gets any response since while statement that checks send flag(send flag is true after app gets an appropriate response).
How can I set a delay that makes my app waiting for the response? I mean I have to send data and wait for the response and if response is inacceptable I have to send data again.
without while statement, I can send data and get a response. but I have to check whether received data are acceptable too.
package com.np.schoolbell;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.icu.text.SimpleDateFormat;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.EditText;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.ref.WeakReference;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Timer;
import java.util.TimerTask;
import java.util.UUID;
import ir.mirrajabi.persiancalendar.PersianCalendarView;
import ir.mirrajabi.persiancalendar.core.PersianCalendarHandler;
import ir.mirrajabi.persiancalendar.core.models.PersianDate;
public class ledControl extends AppCompatActivity {
EditText et_SendData;
static TextView tv_DataReaded;
PersianCalendarView persianCalendarView;
PersianCalendarHandler calendar;
PersianDate today;
PersianDate sampleday;
List days;
String address = null;
static String TransmiterCode="999";
static String SendBuffer=null;
static String RecieveBuffer=null;
static int Counter=0;
static boolean SendFlag=false;
BluetoothAdapter bAdapter = null;
BluetoothDevice bDevice = null;
BluetoothSocket bSocket = null;
static final UUID myUUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"); //SPP UUID. Look for it
ConnectedThread cThread;
private static BluetoothResponseHandler brHandler;
private final static int DataIsReady = 1;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_led_control);
persianCalendarView = (PersianCalendarView)findViewById(R.id.persian_calendar2);
calendar = persianCalendarView.getCalendar();
today = calendar.getToday();
//days=calendar.getDays(1);
//Toast.makeText(this, days.toString(), Toast.LENGTH_LONG).show();
//today.setDate(1398,05,01);
//Toast.makeText(this, today.toString(), Toast.LENGTH_LONG).show();
et_SendData = (EditText) findViewById(R.id.et_SendData);
tv_DataReaded = (TextView) findViewById(R.id.tv_DataReaded);
address = getIntent().getStringExtra( "device_address" );
bAdapter = BluetoothAdapter.getDefaultAdapter();
try {
Toast.makeText(getApplicationContext(), "Connecting...", Toast.LENGTH_SHORT).show();
if( bSocket == null ) {
bDevice = bAdapter.getRemoteDevice(address);
bSocket = bDevice.createInsecureRfcommSocketToServiceRecord(myUUID);
bAdapter.cancelDiscovery();
bSocket.connect();
}
Toast.makeText(getApplicationContext(), "Connected", Toast.LENGTH_SHORT).show();
}
catch (Exception e) {
Toast.makeText(getApplicationContext(), "Connection Failed. Is it a SPP Bluetooth? Try again.", Toast.LENGTH_SHORT).show();
finish();
}
cThread = new ConnectedThread(bSocket);
cThread.start();
if (brHandler == null) brHandler = new BluetoothResponseHandler(this);
else brHandler.setTarget(this);
}
public void onClick_btn_SendData( View v ) {
SendBuffer=LoadDateTimeBuffer();
while(!SendFlag) SendData( SendBuffer+"\r" );
SendBuffer=LoadAZanSetting();
while(!SendFlag) SendData( SendBuffer+"\r" );
et_SendData.setText("");
}
public String LoadDateTimeBuffer(){
SendFlag=false;
TransmiterCode="420";
SimpleDateFormat sdf = new SimpleDateFormat("HHmmss", Locale.getDefault());
String currentTime = sdf.format(new Date());
String currentDate=ParseFaDigits.convert(calendar.formatNumber(today.getYear()))+ParseFaDigits.convert(calendar.formatNumber(today.getMonth()))+ParseFaDigits.convert(calendar.formatNumber(today.getDayOfMonth()));
return TransmiterCode+currentTime+currentDate;
}
public String LoadAZanSetting(){
SendFlag=false;
TransmiterCode="421";
boolean fajrflag=true;boolean tolueflag=true;boolean zuhrflag=true;boolean maghribflag=true;boolean ishaflag=true;
String FajrFlagString = (fajrflag) ? "1" : "0";
String TolueFlagString = (tolueflag) ? "1" : "0";
String ZuhrFlagString = (zuhrflag) ? "1" : "0";
String MaghribFlagString = (maghribflag) ? "1" : "0";
String IshaFlagString = (ishaflag) ? "1" : "0";
return TransmiterCode+FajrFlagString+TolueFlagString+ZuhrFlagString+MaghribFlagString+IshaFlagString;
}
public void SendData(String Data) {
if( bSocket != null ) {
try {
bSocket.getOutputStream().write(Data.getBytes());
}
catch (Exception e) {
Toast.makeText(getApplicationContext(), "Error in Send Data", Toast.LENGTH_LONG).show();
}
}
else {
Toast.makeText(getApplicationContext(), "Bluetooth is Not Connected", Toast.LENGTH_LONG).show();
}
}
#Override
protected void onDestroy() {
super.onDestroy();
if( bSocket == null ) return;
if( bSocket.isConnected() ) {
Disconnect();
}
}
public void onClick_Bluetooth_btn_Disconnect( View v ) {
Disconnect();
}
public void Disconnect() {
if ( bSocket != null && bSocket.isConnected() ) {
try {
bSocket.close();
Toast.makeText(getApplicationContext(), "Disconnected", Toast.LENGTH_SHORT).show();
}
catch( IOException e ) {
Toast.makeText(getApplicationContext(), "Error in Disconnecting ", Toast.LENGTH_SHORT).show();
}
}
finish();
}
public static class ConnectedThread extends Thread {
private BluetoothSocket mmSocket;
private InputStream mmInStream;
private OutputStream mmOutStream;
public ConnectedThread(BluetoothSocket socket) {
mmSocket = socket;
InputStream tmpIn = null;
OutputStream tmpOut = null;
// Get the input and output streams, using temp objects because member streams are final
try {
tmpIn = socket.getInputStream();
tmpOut = socket.getOutputStream();
} catch (IOException e) {
e.printStackTrace();
}
mmInStream = tmpIn;
mmOutStream = tmpOut;
//Tell other phone that we have connected
write("connected".getBytes());
}
public void run() {
byte[] buffer = new byte[512];
int bytes;
StringBuilder readMessage = new StringBuilder();
while( !this.isInterrupted() ) {
try {
bytes = mmInStream.read(buffer);
String readed = new String(buffer, 0, bytes);
readMessage.append(readed);
if (readed.contains("\r")) {
brHandler.obtainMessage(ledControl.DataIsReady, bytes, -1, readMessage.toString()).sendToTarget();
readMessage.setLength(0);
}
} catch (Exception e) {
break;
}
}
}
// Call this from the main activity to send data to the remote device
public void write(byte[] bytes) {
try {
mmOutStream.write(bytes);
} catch (IOException e) {
e.printStackTrace();
}
}
// Call this from the main activity to shutdown the connection
public void cancel() {
if (mmInStream != null) {
try {mmInStream.close();} catch (Exception e) {}
mmInStream = null;
}
if (mmOutStream != null) {
try {mmOutStream.close();} catch (Exception e) {}
mmOutStream = null;
}
if (mmSocket != null) {
try {mmSocket.close();} catch (Exception e) {}
mmSocket = null;
}
this.interrupt();
}
}
private static class BluetoothResponseHandler extends Handler {
private WeakReference<ledControl> mActivity;
public BluetoothResponseHandler(ledControl activity) {
mActivity = new WeakReference<ledControl>(activity);
}
public void setTarget(ledControl target) {
mActivity.clear();
mActivity = new WeakReference<ledControl>(target);
}
#Override
public void handleMessage(Message msg) {
ledControl activity = mActivity.get();
String Data = (String)msg.obj;
if (activity != null) {
switch (msg.what) {
case DataIsReady :
if( Data == null ) return;
RecieveBuffer=Data;
if(RecieveBuffer.contains(SendBuffer))
{
tv_DataReaded.append(Data);
SendFlag=true;
TransmiterCode="";
SendBuffer="";
RecieveBuffer="";
}
else
{
SendFlag=false;
}
break;
}
}
}
}
}
Thank you a lot in advance.
I found a way to solve the mentioned problem so that I applied a CountDownTimer which sends data every 1 sec through 3 sec and the timer will be canceled due to avoid wasting time if sent data and response are ok both. in regular conditions, this timer sends data once since the application receives an appropriate response.
SendBuffer=LoadDateTimeBuffer();
CountDownTimer yourCountDownTimer=new CountDownTimer(3000, 1000) {
public void onFinish() {
Toast.makeText(ledControl.this, "Error", Toast.LENGTH_SHORT).show();
}
public void onTick(long millisUntilFinished) {
// millisUntilFinished The amount of time until finished.
if(!SendFlag)SendData( SendBuffer+"\r" );
else {Toast.makeText(ledControl.this, "sent", Toast.LENGTH_SHORT).show();this.cancel();}
}
}.start();

Streaming in VideoView without delay via rtsp

Hello I have problem with delay in stream video on android.
I have a delay of about 8 seconds and it is very annoying. There is no delay on the computer.
This is a stream from camera like gopro via wifi --> rtsp.
I found this topic on SO...
Streaming videos on VideoView
And I have the same problem as Akeshwar Jha. Unfortunately, I don't know Java. Could someone help me modify the design according to what Akeshwar Jha gave ??
package com.teststream.robotclient;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.UnknownHostException;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.net.Uri;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.util.Pair;
import android.widget.VideoView;
import com.jmedeisis.bugstick.Joystick;
import com.jmedeisis.bugstick.JoystickListener;
public class MainActivity extends Activity {
private String host;
private int port;
private DatagramSocket socket;
private VideoView videoView;
private static final String TAG = "MainActivity";
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
WifiManager wifiManager = (WifiManager) getApplicationContext().getSystemService(WIFI_SERVICE);
WifiInfo info = wifiManager.getConnectionInfo();
String ssid = info.getSSID();
String desiredSsid = getString(R.string.ssid);
Log.i(TAG, desiredSsid);
if(!ssid.equals(desiredSsid) && !desiredSsid.equals("*")) {
exitDialog("Wrong SSID", "The SSID of the WIFI network is wrong. Desired SSID: " + desiredSsid);
}
host = getString(R.string.host);
port = Integer.parseInt(getString(R.string.port));
//new CheckConnectivity().execute();
videoView = findViewById(R.id.videoView);
Uri videoUri = Uri.parse(getString(R.string.video_uri));
videoView.setVideoURI(videoUri);
videoView.start();
Joystick left = findViewById(R.id.joystick_left);
Joystick right = findViewById(R.id.joystick_right);
left.setJoystickListener(new JoystickListener() {
#Override
public void onDown() {
log("down");
}
#Override
public void onDrag(float degrees, float offset) {
log("deg: " + degrees + ", off: " + offset);
float upValue = (float) Math.sin(Math.toRadians(degrees)) * offset;
log("" + upValue);
int intValue = normalisedFloatToByte(upValue);
new UpdateServo().execute(Pair.create(0xff, intValue));
}
#Override
public void onUp() {
log("up");
}
});
right.setJoystickListener(new JoystickListener() {
#Override
public void onDown() {
log("down");
}
#Override
public void onDrag(float degrees, float offset) {
log("deg: " + degrees + ", off: " + offset);
float upValue = (float) Math.sin(Math.toRadians(degrees)) * offset;
log("" + upValue);
int intValue = normalisedFloatToByte(upValue);
new UpdateServo().execute(Pair.create(0xfe, intValue));
}
#Override
public void onUp() {
log("up");
}
});
}
private int normalisedFloatToByte(float value) {
if(value < -1.0 || value > 1.0) {
return 0;
}
return 127 + Math.round(value * 127);
}
private void log(String text) {
Log.d(TAG, text);
}
private void exitDialog(String title, String text) {
new AlertDialog.Builder(MainActivity.this)
.setTitle(title)
.setMessage(text)
.setNeutralButton("OK", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
finish();
}
})
.show();
}
class CheckConnectivity extends AsyncTask<Void, Void, Boolean> {
protected Boolean doInBackground(Void ...params) {
try {
if(!InetAddress.getByName(host).isReachable(2000)) {
return false;
}
} catch (UnknownHostException e) {
return false;
} catch (IOException e) {
return false;
}
return true;
}
protected void onPostExecute(Boolean result) {
if(result == false) {
exitDialog("Can't connect", "Can't connect with host " + host);
}
}
}
class UpdateServo extends AsyncTask<Pair<Integer, Integer>, Void, Void> {
protected Void doInBackground(Pair<Integer, Integer>... requests) {
byte[] payload = {
requests[0].first.byteValue(),
requests[0].second.byteValue()
};
try {
InetAddress addr = InetAddress.getByName(host);
DatagramPacket sendPacket = new DatagramPacket(payload, 0, payload.length, addr, port);
if (socket != null) {
socket.disconnect();
socket.close();
}
socket = new DatagramSocket(port);
socket.send(sendPacket);
} catch (UnknownHostException e) {
Log.e(TAG, "getByName failed");
} catch (IOException e) {
Log.e(TAG, "send failed");
}
return null;
}
}
#Override
public void onDestroy() {
super.onDestroy();
}
}
Thank you in advance for your help.

MediaPlayer doesn't continue showing subtitle (.SRT) after pause or seekTo

I'm trying to implement subtitles to videoview. I've used this example project :
package com.example.media.timedtexttest;
import java.io.Closeable;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Locale;
import android.app.Activity;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnTimedTextListener;
import android.media.MediaPlayer.TrackInfo;
import android.media.TimedText;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.widget.TextView;
public class MainActivity extends Activity implements OnTimedTextListener {
private static final String TAG = "TimedTextTest";
private TextView txtDisplay;
private static Handler handler = new Handler();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
txtDisplay = (TextView) findViewById(R.id.txtDisplay);
MediaPlayer player = MediaPlayer.create(this, R.raw.video);
try {
player.addTimedTextSource(getSubtitleFile(R.raw.sub),
MediaPlayer.MEDIA_MIMETYPE_TEXT_SUBRIP);
int textTrackIndex = findTrackIndexFor(
TrackInfo.MEDIA_TRACK_TYPE_TIMEDTEXT, player.getTrackInfo());
if (textTrackIndex >= 0) {
player.selectTrack(textTrackIndex);
} else {
Log.w(TAG, "Cannot find text track!");
}
player.setOnTimedTextListener(this);
player.start();
} catch (Exception e) {
e.printStackTrace();
}
}
private int findTrackIndexFor(int mediaTrackType, TrackInfo[] trackInfo) {
int index = -1;
for (int i = 0; i < trackInfo.length; i++) {
if (trackInfo[i].getTrackType() == mediaTrackType) {
return i;
}
}
return index;
}
private String getSubtitleFile(int resId) {
String fileName = getResources().getResourceEntryName(resId);
File subtitleFile = getFileStreamPath(fileName);
if (subtitleFile.exists()) {
Log.d(TAG, "Subtitle already exists");
return subtitleFile.getAbsolutePath();
}
Log.d(TAG, "Subtitle does not exists, copy it from res/raw");
// Copy the file from the res/raw folder to your app folder on the
// device
InputStream inputStream = null;
OutputStream outputStream = null;
try {
inputStream = getResources().openRawResource(resId);
outputStream = new FileOutputStream(subtitleFile, false);
copyFile(inputStream, outputStream);
return subtitleFile.getAbsolutePath();
} catch (Exception e) {
e.printStackTrace();
} finally {
closeStreams(inputStream, outputStream);
}
return "";
}
private void copyFile(InputStream inputStream, OutputStream outputStream)
throws IOException {
final int BUFFER_SIZE = 1024;
byte[] buffer = new byte[BUFFER_SIZE];
int length = -1;
while ((length = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, length);
}
}
// A handy method I use to close all the streams
private void closeStreams(Closeable... closeables) {
if (closeables != null) {
for (Closeable stream : closeables) {
if (stream != null) {
try {
stream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
#Override
public void onTimedText(final MediaPlayer mp, final TimedText text) {
if (text != null) {
handler.post(new Runnable() {
#Override
public void run() {
int seconds = mp.getCurrentPosition() / 1000;
txtDisplay.setText("[" + secondsToDuration(seconds) + "] "
+ text.getText());
}
});
}
}
// To display the seconds in the duration format 00:00:00
public String secondsToDuration(int seconds) {
return String.format("%02d:%02d:%02d", seconds / 3600,
(seconds % 3600) / 60, (seconds % 60), Locale.US);
}
}
and created exactly the same implementation and it somehow works, but if i stop video and continue subtitle doesn't continue or if i seek video to some time subtitle doesn't continue too.
What i have tried:
Every time before player.start() i set player.selectTrack(textTrackIndex);
I've tried to reregister listener when i'm doing player.start(); player.setOnTimedTextListener(this);
Please help i've spent 4 days on subtitle features... If you have some project example or snippet it would be nice :)
we have the same problem and we must implements OnSeekCompleteListener even if you nothing to do into override method
it's our code:
#Override
public void onSeekComplete(MediaPlayer mediaPlayer) {
}
and it work !!!
I hope that help you

Why does my application crash sometimes?

All,
I would greatly appreciate some help. We have a system that is being sent data at 2 Hz (every 0.5 sec). My application is supposed to start acquiring this data upon the press of a "start" button through a handler, and upon pressing the "stop" button, the user can choose to save the data collected. If save is chosen, a new activity is SUPPOSED to be started, however, my application crashes. The error message:
java.lang.NullPointerException: Attempt to invoke virtual method 'int java.lang.String.indexOf(int)' on a null object reference
at com.example.cherryjp.buttonapp.StartNewSessionActivity$2.onClick(StartNewSessionActivity.java:130)
at com.android.internal.app.AlertController$ButtonHandler.handleMessage(AlertController.java:162)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5254)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)
and my code:
package com.example.cherryjp.buttonapp;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import android.bluetooth.BluetoothSocket;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.AbstractCollection;
import java.util.ArrayList;
import java.util.Set;
import java.util.Timer;
import java.util.UUID;
/**
*/
public class StartNewSessionActivity extends AppCompatActivity {
final int MSG_START_TIMER = 0;
final int MSG_STOP_TIMER = 1;
final int MSG_UPDATE_TIMER = 2;
private static final UUID MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805f9b34fb");
long startTime;
Double endTime;
TextView watch;
Double totalSeconds;
Double seconds;
Double minutes;
Double hours;
String time;
boolean recording = false;
String fromBluetooth;
BluetoothAdapter mBluetoothAdapter;
ArrayAdapter mArrayAdapter;
final Context context = this;
final int REFRESH_RATE = 100;
Stopwatch timer = new Stopwatch();
Handler mHandler = new Handler()
{
#Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
byte[] writeBuf = (byte[]) msg.obj;
int begin = (int)msg.arg1;
int end = (int)msg.arg2;
switch (msg.what) {
case MSG_START_TIMER:
timer.start(); //start timer
mHandler.sendEmptyMessage(MSG_UPDATE_TIMER);
break;
case MSG_UPDATE_TIMER:
watch.setText("" + timer.toString());
mHandler.sendEmptyMessageDelayed(MSG_UPDATE_TIMER, REFRESH_RATE); //text view is updated every second,
if(writeBuf == null){
break;
}
String writeMessage = new String(writeBuf);
writeMessage = writeMessage.substring(begin, end);
fromBluetooth += writeMessage;
fromBluetooth += "\t";
break;
//though the timer is still running
case MSG_STOP_TIMER:
mHandler.removeMessages(MSG_UPDATE_TIMER); // no more updates.
timer.stop();//stop timer
//send reset signal for bluetooth
if(recording) {
launchSaveDialog();
}
recording = false;
watch.setText("" + timer.toString());
break;
default:
break;
}
}
};
private BluetoothDevice mDevice;
public void launchSaveDialog(){
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage("Would you like to save that run?")
.setTitle("Save confirmation");
AlertDialog.Builder builder1 = builder.setPositiveButton("Save", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
Entry entry = new Entry();
entry.setDate(MySQLiteHelper.getCurrentDateTime(context));
entry.setRiderId(getIntent().getExtras().getLong("RIDER_ID"));
//TODO: loop to pull values from bluetooth transmission
//String fromBluetooth = "5\t1\t3\t2\t5\t3\t3\t4";
String tempString = fromBluetooth;
tempString = fromBluetooth.substring(fromBluetooth.indexOf('\t')+1);
entry.setForceAndTimeTabSeparated(tempString);
MainActivity.riderdb.addEntry(entry);
Toast.makeText(getApplicationContext(),
"Run saved!", Toast.LENGTH_SHORT).show();
showGraph(entry);
}
});
builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dialog.dismiss();
}
});
AlertDialog dialog = builder.create();
dialog.show();
}
public void showGraph(Entry entry){
Intent immediateGraphIntent = new Intent(this, ImmediateGraph.class);
final int result = 1;
//Just getEntry on next intent instead of storing extras.
immediateGraphIntent.putExtra("ENTRY_ID", entry.getId());
//immediateGraphIntent.putExtra("PASSTIME", timeArray);
//immediateGraphIntent.putExtra("PASSFORCE", forceArray);
startActivity(immediateGraphIntent);
}
#Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.session_start_page);
Button startButton = (Button) findViewById(R.id.startsessionbutton);
Button endButton = (Button) findViewById(R.id.endsessionbutton);
watch = (TextView)findViewById(R.id.watch);
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (!mBluetoothAdapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, 1);
}
Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices();
if (pairedDevices.size() > 0) {
for (BluetoothDevice device : pairedDevices) {
mDevice = device;
}
}
final ConnectThread mConnectThread = new ConnectThread(mDevice);
mConnectThread.start();
startButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//START recording stuff.
recording = true;
while(mConnectThread.mConnectedThread == null){
}
mConnectThread.mConnectedThread.write("begin".getBytes());
mHandler.sendEmptyMessage(MSG_START_TIMER);
}
});
endButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//END recording stuff
mHandler.sendEmptyMessage(MSG_STOP_TIMER);
}
});
}
public class Stopwatch {
private long startTime = 0;
private boolean running = false;
private long currentTime = 0;
public void start() {
this.startTime = System.currentTimeMillis();
this.running = true;
}
public void stop() {
this.running = false;
}
public void pause() {
this.running = false;
currentTime = System.currentTimeMillis() - startTime;
}
public void resume() {
this.running = true;
this.startTime = System.currentTimeMillis() - currentTime;
}
//elaspsed time in milliseconds
public long getElapsedTimeMili() {
long elapsed = 0;
if (running) {
elapsed =((System.currentTimeMillis() - startTime)/100) % 10 ;
}
return elapsed;
}
//elaspsed time in seconds
public long getElapsedTimeSecs() {
long elapsed = 0;
if (running) {
elapsed = ((System.currentTimeMillis() - startTime) / 1000) % 60;
}
return elapsed;
}
//elaspsed time in minutes
public long getElapsedTimeMin() {
long elapsed = 0;
if (running) {
elapsed = (((System.currentTimeMillis() - startTime) / 1000) / 60 ) % 60;
}
return elapsed;
}
//elaspsed time in hours
public long getElapsedTimeHour() {
long elapsed = 0;
if (running) {
elapsed = ((((System.currentTimeMillis() - startTime) / 1000) / 60 ) / 60);
}
return elapsed;
}
public String toString() {
return String.format("%1$02d:%2$02d:%3$02d.%4$01d", getElapsedTimeHour(), getElapsedTimeMin(), getElapsedTimeSecs(), getElapsedTimeMili());
}
}
private class ConnectThread extends Thread {
private final BluetoothSocket mmSocket;
private final BluetoothDevice mmDevice;
private ConnectedThread mConnectedThread;
public ConnectThread(BluetoothDevice device) {
BluetoothSocket tmp = null;
mmDevice = device;
try {
tmp = device.createRfcommSocketToServiceRecord(MY_UUID);
} catch (IOException e) { }
mmSocket = tmp;
}
public void run() {
mBluetoothAdapter.cancelDiscovery();
try {
mmSocket.connect();
} catch (IOException connectException) {
try {
mmSocket.close();
} catch (IOException closeException) { }
return;
}
mConnectedThread = new ConnectedThread(mmSocket);
mConnectedThread.start();
}
public void cancel() {
try {
mmSocket.close();
} catch (IOException e) { }
}
}
private class ConnectedThread extends Thread {
private final BluetoothSocket mmSocket;
private final InputStream mmInStream;
private final OutputStream mmOutStream;
public ConnectedThread(BluetoothSocket socket) {
mmSocket = socket;
InputStream tmpIn = null;
OutputStream tmpOut = null;
try {
tmpIn = socket.getInputStream();
tmpOut = socket.getOutputStream();
} catch (IOException e) { }
mmInStream = tmpIn;
mmOutStream = tmpOut;
}
public void run() {
byte[] buffer = new byte[1024];
int begin = 0;
int bytes = 0;
while (true) {
try {
bytes += mmInStream.read(buffer, bytes, buffer.length - bytes);
for(int i = begin; i < bytes; i++) {
if(buffer[i] == "\n".getBytes()[0]) {
mHandler.obtainMessage(MSG_UPDATE_TIMER, begin, i, buffer).sendToTarget();
begin = i + 1;
if(i == bytes - 1) {
bytes = 0;
begin = 0;
}
}
}
} catch (IOException e) {
break;
}
}
}
public void write(byte[] bytes) {
try {
mmOutStream.write(bytes);
} catch (IOException e) { }
}
public void cancel() {
try {
mmSocket.close();
} catch (IOException e) { }
}
}
}
Any thoughts on this? I'm completely out of ideas on what is causing this to crash... something with the bluetooth connectivity, maybe?
Thanks,
John
fromBluetooth is null, and you can't call indexOf(...) on a null String.
You must therefore ensure that fromBluetooth isn't null (good) or handle the case where fromBluetooth is null (better) (e.g. if (fromBluetooth != null) execute logic).
Enjoy
Replace your code
if(fromBluetooth!=null){
tempString = fromBluetooth.substring(fromBluetooth.indexOf('\t')+1);
}
with
fromBluetooth.substring(fromBluetooth.indexOf('\t')+1);
because you not checking string is null or not and directly findvalue using indexOf();

ADK AndroidAccessory Read Function on Arduino should be getting something, per DemoKit sketch?

I'm using a Freeduino (Arduino Uno compatible) with a Samsung Galaxy Tab 10.1 running ICS (4) and I have succeeded in writing from the Arduino to the Android, but I have not been able to read from the Android in the Arduino sketch.
Here's the Android class for the USB Accessory:
package com.kegui.test;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringWriter;
import java.io.Writer;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.util.ArrayList;
import org.apache.http.NameValuePair;
import org.apache.http.message.BasicNameValuePair;
import com.kegui.test.Scripto;
import android.app.Activity;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.hardware.usb.UsbAccessory;
import android.hardware.usb.UsbManager;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.ParcelFileDescriptor;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
public class USBAccess extends Activity implements Runnable, OnClickListener {
private static final String ACTION_USB_PERMISSION = "com.google.android.DemoKit.action.USB_PERMISSION";
protected static final String TAG = "KegUI";
private UsbManager mUsbManager;
private PendingIntent mPermissionIntent;
private boolean mPermissionRequestPending;
private TextView debugtext = null;
private Button button1 = null;
private Button button2 = null;
private boolean button2visible = false;
UsbAccessory mAccessory;
ParcelFileDescriptor mFileDescriptor;
FileInputStream mInputStream;
FileOutputStream mOutputStream;
private static final int MESSAGE_BUTTON_PRESSED = 1;
private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
Log.i("KegApp", "***********************Received*************************");
if (ACTION_USB_PERMISSION.equals(action)) {
synchronized (this) {
UsbAccessory accessory = (UsbAccessory) intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
if (intent.getBooleanExtra(
UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
openAccessory(accessory);
} else {
Log.d(TAG, "permission denied for accessory "
+ accessory);
}
mPermissionRequestPending = false;
}
} else if (UsbManager.ACTION_USB_ACCESSORY_DETACHED.equals(action)) {
UsbAccessory accessory = (UsbAccessory) intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
if (accessory != null && accessory.equals(mAccessory)) {
closeAccessory();
}
}
}
};
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mUsbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
mPermissionIntent = PendingIntent.getBroadcast(this, 0, new Intent(
ACTION_USB_PERMISSION), 0);
IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION);
filter.addAction(UsbManager.ACTION_USB_ACCESSORY_DETACHED);
setContentView(R.layout.main);
final Button button = (Button) findViewById(R.id.button1);
button.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
// Perform action on click
new TempUpdateTask().execute("testing");
}
});
registerReceiver(mUsbReceiver, filter);
Log.d(TAG,"on Create'd");
}
#Override
public void onResume() {
super.onResume();
if (mInputStream != null && mOutputStream != null) {
return;
}
UsbAccessory[] accessories = mUsbManager.getAccessoryList();
UsbAccessory accessory = (accessories == null ? null : accessories[0]);
if (accessory != null) {
if (mUsbManager.hasPermission(accessory)) {
openAccessory(accessory);
} else {
synchronized (mUsbReceiver) {
if (!mPermissionRequestPending) {
mUsbManager.requestPermission(accessory,
mPermissionIntent);
mPermissionRequestPending = true;
}
}
}
} else {
Log.d(TAG, "mAccessory is null");
}
}
#Override
public void onPause() {
super.onPause();
closeAccessory();
}
#Override
public void onDestroy() {
super.onDestroy();
unregisterReceiver(mUsbReceiver);
}
private void openAccessory(UsbAccessory accessory) {
mFileDescriptor = mUsbManager.openAccessory(accessory);
if (mFileDescriptor != null) {
mAccessory = accessory;
FileDescriptor fd = mFileDescriptor.getFileDescriptor();
mInputStream = new FileInputStream(fd);
mOutputStream = new FileOutputStream(fd);
Thread thread = new Thread(null, this, "KegApp");
thread.start();
//enableControls(true);
} else {
Log.d(TAG, "accessory open fail");
}
}
public void run() {
int ret = 0;
byte[] buffer = new byte[16384];
int i;
Log.i("KegApp", "***********************in run*************************");
while (ret >= 0) {
try {
ret = mInputStream.read(buffer); // this will be always positive, as long as the stream is not closed
} catch (IOException e) {
break;
}
i = 0;
while (i < ret) {
Message m = Message.obtain(messageHandler, MESSAGE_BUTTON_PRESSED);
m.obj = buffer[i];
messageHandler.sendMessage(m);
i++;
}
}
}
private void closeAccessory() {
//enableControls(false);
try {
if (mFileDescriptor != null) {
mFileDescriptor.close();
}
} catch (IOException e) {
} finally {
mFileDescriptor = null;
mAccessory = null;
}
}
public void sendCommand(FileOutputStream mStream) {
BufferedOutputStream bo = new BufferedOutputStream(mStream);
// if (mStream != null && message.length > 0) {
try {
Log.i("KegApp", "***********************sending command now*************************");
bo.write(1); //message, 0, 3);
} catch (IOException e) {
Log.e(TAG, "write failed", e);
}
// }
}
#Override
public void onClick(View v) {
// Send some message to Arduino board, e.g. "13"
Log.e(TAG, "write failed");
}
// Instantiating the Handler associated with the main thread.
private Handler messageHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
Log.i("KegApp", "***********************message handler before " + msg.what + "************************");
try {
String load = msg.obj.toString();
Log.i("KegApp", "***********************in message handler*************************");
/*
if (button2visible==false) {
debugtext.setText("Received message: "+String.valueOf(load));
button2.setVisibility(View.VISIBLE);
button2visible = true;
} else {
debugtext.setText("");
button2.setVisibility(View.GONE);
button2visible = false;
}
*/
new TempUpdateTask().execute(load);
} catch (Exception e) {
Log.e(TAG, "message failed", e);
}
}
};
// UpdateData Asynchronously sends the value received from ADK Main Board.
// This is triggered by onReceive()
class TempUpdateTask extends AsyncTask<String, String, String> {
// Called to initiate the background activity
protected String doInBackground(String... sensorValue) {
try {
Log.i("KegApp", "***********************calling sendcommand*********************");
sendCommand(mOutputStream);
Log.i("KegApp", "*********************incoming-sensorValue*********************" );
ArduinoMessage arduinoMessage = new ArduinoMessage(sensorValue[0]);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
String returnString = String.valueOf(sensorValue[0]);//String.valueOf(sensorValue[0]) + " F";
publishProgress(String.valueOf(sensorValue[0]));
return (returnString); // This goes to result
}
// Called when there's a status to be updated
#Override
protected void onProgressUpdate(String... values) {
// Init TextView Widget to display ADC sensor value in numeric.
TextView tvAdcvalue = (TextView) findViewById(R.id.tvTemp);
tvAdcvalue.setText(String.valueOf(values[0]));
// Not used in this case
}
// Called once the background activity has completed
#Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
closeAccessory();
}
}
}
Here's my Arduino Sketch:
#include <FHB.h>
#include <Max3421e.h>
#include <Max3421e_constants.h>
#include <Max_LCD.h>
#include <Usb.h>
//FREEDUINO ONLY------------------------------------
//END FREEDUINO ONLY------------------------------------
int pin_light_sensor_1=A0;
int pin_output_sensor=9;
int currLightLevel=0;
//int prevLightLevel=0;
//int lightStableRange=90;
int delayTime=3000;
int loopCtr=0;
char cSTX=char(2);
char cSOH=char(1);
char cEOT=char(4);
char cGS=char(29);
char cRS=char(30);
char cCR=char(13);
char cLF=char(10);
//FREEDUINO ONLY------------------------------------
AndroidAccessory acc("Company Inc.",
"Kegbot5K datawriter",
"Kegbot 5000 data writer",
"1.0",
"http://companyinc.com",
"0000000012345678");
//END FREEDUINO ONLY------------------------------------
void setup()
{
pinMode(pin_light_sensor_1, INPUT);
pinMode(pin_output_sensor, OUTPUT);
Serial.begin(57600);
//FREEDUINO ONLY------------------------------------
Serial.println("pre-power");
acc.powerOn();
Serial.println("post-power");
//END FREEDUINO ONLY------------------------------------
}
void loop()
{
byte msg[3];
if (acc.isConnected()) {
currLightLevel = analogRead(pin_light_sensor_1);
//sysPrint(delayTime*loopCtr);
//sysPrint(",");
//sysPrint(currLightLevel);
writeDataMessage("LGH01", currLightLevel);
}
delay(1000);
if (acc.isConnected()) {
int len = acc.read(msg, sizeof(msg), 1);
Serial.println(len);
if (len > 0){
for (int index=0; index < len; index++){
digitalWrite(pin_output_sensor, 1);
delay(500);
digitalWrite(pin_output_sensor, 0);
}
}
}
loopCtr++;
delay(delayTime);
}
void writeHeader(String msgType)
{
sysPrint(cSTX);
sysPrint(cSTX);
sysPrint(cSOH);
sysPrint(msgType);
sysPrint(cGS);
}
void writeFooter()
{
sysPrint(cEOT);
sysPrintLn(cEOT);
}
void writeDataMessage(String sensorID, int value)
{
writeHeader("DATA");
sysPrint(sensorID);
sysPrint(cRS);
sysPrint(value);
writeFooter();
}
void writeAlarmMessage(String sensorID, String message)
//Do we need to enforce the 5 char sensorID here? I don't think so...
{
writeHeader("ALRM");
sysPrint(sensorID);
sysPrint(cRS);
sysPrint(message);
writeFooter();
}
void sysPrint(String whatToWrite)
{
int len=whatToWrite.length();
char str[len];
whatToWrite.toCharArray(str, len);
acc.write((void *)str, len);
// acc.write(&whatToWrite, whatToWrite.length());
}
void sysPrint(char whatToWrite)
{
acc.write((void *)whatToWrite, 1);
// acc.write(&whatToWrite, 1);
}
void sysPrint(int whatToWrite)
{
acc.write((void *)&whatToWrite, 1);
// acc.write(&whatToWrite, 1);
}
void sysPrintLn(String whatToWrite)
{
int len=whatToWrite.length();
char str[len];
whatToWrite.toCharArray(str, len);
acc.write((void *)str, len);
acc.write((void *)&cCR, 1);
acc.write((void *)&cLF, 1);
// acc.write(&whatToWrite, whatToWrite.length());
// acc.write(&cCR, 1);
// acc.write(&cLF, 1);
}
void sysPrintLn(char whatToWrite)
{
acc.write((void *)whatToWrite, 1);
acc.write((void *)&cCR, 1);
acc.write((void *)&cLF, 1);
// acc.write(&whatToWrite, 1);
// acc.write(&cCR, 1);
// acc.write(&cLF, 1);
}
The sendCommand function in the Android is logging that it was sent. The acc.read function is printing length to Serial output as -1, as in no input. Speaking of which, the Android logs do not show errors, so I'm thinking this might be an Arduino thing.
My Manifest has an Intent filter that's registering the device, although permissions might have something to do with it.
<intent-filter>
<action android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED" />
</intent-filter>
<meta-data android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED"
android:resource="#xml/accessory_filter" />
</activity>
Thanks in advance for any thoughts,
Sara
I have developed an RC helicopter that can be controlled using android by sending messages from the android to the arduino located on top of the Helicopter using Bluetooth Stick, i did not use usb yet for any project but when you are sending messages to the arduino you are using serial communication weather connected threw USB or Bluetooth i suggest to simply use Serial.read and every message you send to arduino end it with some symbol '#' for example just to separate messages and get the message character by character
this is the code to read the full message ended with '#' from the Serial port:
String getMessage()
{
String msg=""; //the message starts empty
byte ch; // the character that you use to construct the Message
byte d='#';// the separating symbol
if(Serial.available())// checks if there is a new message;
{
while(Serial.available() && Serial.peek()!=d)// while the message did not finish
{
ch=Serial.read();// get the character
msg+=(char)ch;//add the character to the message
delay(1);//wait for the next character
}
ch=Serial.read();// pop the '#' from the buffer
if(ch==d) // id finished
return msg;
else
return "NA";
}
else
return "NA"; // return "NA" if no message;
}
this function checks if there any message in the buffer if not it returns "NA".
if that did not help please inform.
Figured out what's going wrong - sendCommand is executing from the asynchronous TempTask so it doesn't get passed a valid reference to the FileOutputStream. I can execute sendCommand onClick from the main thread and receive on the Arduino just fine.

Categories

Resources