39

I have an app that connects to a RaspberryPi via Bluetooth and loops the same data to it while it receives some data back.

I had some issues with the connection so this workaround is needed to connect my android phone to the RaspberryPi: IOException: read failed, socket might be closed - Bluetooth on Android 4.3

For some reason, the android phone is receiving its own output. The String "Hello Raspberry. It's me, AndroidPhone" is sent to the output in a never-ending loop. The incoming data (from the RaspberryPi) is also read in a never-ending loop.

But somehow I don't only receive the data from the RaspberryPi but also the string sends via smartphone. This is my code:

public class MainActivity extends AppCompatActivity {
    private BluetoothAdapter bluetoothAdapter;
    UUID SERIAL_UUID = UUID.fromString("00001101-0000-1000-8000-00805f9b34fb");

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // (...)
        // Only GUI-stuff until this point

        bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        BluetoothDevice raspberryPi = bluetoothAdapter.getRemoteDevice("B8:27:EB:56:DC:B2");
        BluetoothSocket btSocket;

        try {
            btSocket = raspberryPi.createRfcommSocketToServiceRecord(SERIAL_UUID);
            btSocket.connect();
        } catch (IOException e) {
            Log.e("BTError", e.getMessage());
            // Workaround, found on: https://stackoverflow.com/questions/18657427/ioexception-read-failed-socket-might-closed-bluetooth-on-android-4-3
            try {
                Log.e("BTError", "Trying fallback...");
                btSocket = (BluetoothSocket) raspberryPi.getClass().getMethod("createRfcommSocket", new Class[]{int.class}).invoke(raspberryPi, 1);
                btSocket.connect();

                (new Thread(new SendingThread(btSocket))).start();
                (new Thread(new ReceivingThread(btSocket))).start();
            } catch (Exception e2) {
                Log.e("BTError", e2.getMessage());
                Log.e("BTError", "Couldn't establish Bluetooth connection!");
            }
        }
    }

    private class SendingThread extends Thread {
        private OutputStream out;

        public SendingThread(BluetoothSocket btSocket) {
            try {
                out = btSocket.getOutputStream();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void run() {
            try {
                int delay = 100000000;
                while (true) {
                    if (delay == 0) {
                        Log.i("WRT", "Written to RaspberryPi");
                        out.write("Hello Raspberry. It's me, AndroidPhone".getBytes());
                        delay = 100000000;
                    }
                    delay--;
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private class ReceivingThread extends Thread {
        private InputStream in;

        public ReceivingThread(BluetoothSocket btSocket) {
            try {
                in = btSocket.getInputStream();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void run() {
            int data = 0;
            while (true) {
                try {
                    data = in.read();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                Log.i("RCV", String.valueOf((char) data));
            }
        }
    }

On the RaspberryPi end, everything looks normal. A simple java program starts the Linux command rfcomm listen /dev/rfcomm0 and reads from/writes to the file /dev/rfcomm0 with FileReader and FileWriter. The only relevant lines on this end are:

run {
    // Inside writer-thread
    bluetoothWriter = new BufferedWriter(new FileWriter("/dev/rfcomm0"));
    while(true) {
        bluetoothWriter.write("This is RaspPi");
        bluetoothWriter.flush();
    }
}

and

run {
    // Inside reader-thread
    bluetoothReader = new BufferedReader(new FileReader("/dev/rfcomm0"));
    while(true) {
        int incData = bluetoothReader.read();
        System.out.print((char) incData);
    }
}

Thank you for your help!

edit: Still no solution to this problem. I suspected that the RaspberryPi is somehow sending back what it received. But when I disabled that it sends out anything, the smartphone still directly receives what it has sent out.

Shehan Dhaleesha
  • 627
  • 1
  • 10
  • 30
DeMo
  • 683
  • 5
  • 14
  • 1
    Have you tried to change the parameter of the reflection method which calls `createRfcommSocket`? It should change the channel of the bluetooth socket, maybe this is where something goes wrong. – The_Dude Apr 25 '18 at 09:41
  • Yes, I already tried to change the channels used in the android app and also (just to be completely sure) the one used by the program that runs on the RaspberryPi. Sadly it didn't fix the problem. – DeMo Apr 25 '18 at 09:43
  • Have you tried severely decreasing your send with like 30 seconds between send, just to really analyze what is going out and coming in inside the debugger as well? It sure seems like something is echoing back. Have you tried adding a pre-fix of "my rasberry pi prefix" to the message on the output write from rasberry pi to see if you have the full message on the rasberry pi? Also have you inspected the byte decimal values going out and received to compare what arrives at Rasberri Pi. I would be more interested in what is getting to rasberry pi and what it is sending back "exactly sending". – Sam May 01 '18 at 15:31
  • 1
    I'm trying to basically say, are you 100% positive that you have inspected the bytes leaving rasberri pi and confirmed that they do not contain your message. Because if they do, then you issue is no the rasberri side and not Android. – Sam May 01 '18 at 15:32

3 Answers3

1

I scoured over the Bluetooth classes sources. The workaround seems legit from the first glances. Try this first:

if (delay == 0) {
    Log.i("WRT", "Written to RaspberryPi");
    out.write("Hello Raspberry. It's me, AndroidPhone".getBytes());
    out.flush(); // <-- You are not flushing 
    delay = 100000000;
}

And the message sticks in you socket for you to read over and over again.

If that does not fix it the other option I can think of is that somehow the socket is initialized to be a socket to your Android device. The .createRfcommSocket() method seems to create a socket to your own device if the Bluetooth device is null when the socket was being created. I'm not sure how this would exactly happen, but if the Raspberry Pi's state is somehow mangled after exception I suppose it could be something to look into.

On the raspy side: If you are just starting both of those threads doesn't it mean that you are constantly sending messages to /dev/rfcomm0 and flushing. I recommend that you change it so that raspy reacts to a received message by sending back the wanted message instead of spamming all the time. I'm not sure if this is part of your problem but it would at least make debugging & development a bit easier.

user3666197
  • 1
  • 6
  • 50
  • 92
KinGin
  • 56
  • 4
-1

I am not sure if this is the solution you need, because I don't know if you are using bluetooth classic or bluetooth 4.0>+, but I wrote a library for text based BLE and WiFi P2P 2-way communication for android (and I know the Raspberry Pi is capable of BLE communication), I don't create a socket connection for BLE communication though, but I do for WiFi P2P. Take a look, I hope it helps. It isn't published yet, so you would have to clone/fork the repo.

-1

I think you have trouble writing
As far as I know, for buffer, should use \n and ...

bluetoothWriter.write("This is RaspPi\n");

But I prefer to use a combination of DataOutputStream and BufferedReader

For Read:

    BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
        try {
          String line = bufferedReader.readLine();
System.out.println(line);
        } catch (IOException e) {
          e.printStackTrace();
        }

for write:

DataOutputStream dataOutputStream = new DataOutputStream(outputStream);
    String s = "Hi\n";
    try {
      dataOutputStream.write(s.getBytes());
    } catch (IOException e) {
      e.printStackTrace();
    }

It is better to correct the point made by a dear friend about flush() ...

I'm not sure, please test yourself ...

Hossein
  • 36
  • 5