Peter
Peter

Reputation: 81

Android Bluetooth SPP - How to stop transmitting the internal send buffer ?

I am trying to control/operate a motor from an android phone in "as close as possible" realtime using the Android SPP Bluetooth socket interface. The motor ought to run in a so called 'dead man' operation mode. So the motor will only turn if a button on the android APP is touched and ought to stop immediately if the touch is released.

I implemented this by continuously sending 'keep turning' telegrams of 20 Bytes about every 20ms to keep the motor turning and to have the motor stop immediately as soon as no more telegrams are received or if a STOP telegram is received.

This seem to work acceptable well on some phone but others continue sending 'keep turning' telegrams even after the MotionEvent.ACTION_UP event has been processed and no more data are being send.

I assume that this is caused by some internal buffers that cache the transmit data and continue sending until the buffer is empty.

Simple questions:

Searching the net, I was not able to find anything that talks about BT stream buffer size of buffer management.

And Yes, I have implemented read and write functions as threads and I do not have any problems in reading all telegrams, and I do not need to deliver telegrams in real time but I should be able to stop sending 'keep turning' telegrams within about 50 to 100ms.

Any hints are very welcome.

Upvotes: 1

Views: 760

Answers (1)

Peter
Peter

Reputation: 81

I am sorry that I did not add the code, I thought it may not be necessary as it is straight forward as:

    @Override
    public boolean onTouch(final View v,MotionEvent event) {
        int eventAction = event.getAction();

        switch (eventAction) {
            case MotionEvent.ACTION_DOWN:
                if (v == btnUp || v == btnDown) {

                    // Start a thread that sends the goUP or DOWN command every 10 ms until
                    // btnUp released

                    tvCounter.setText("----");
                    action_touched = true;

                    new Thread(new Runnable() {
                        @Override
                        public void run() {
                            int counter = 1;

                            // Disable heart beat
                            ServiceRequest.send(EnRequest.REQ_SET_HEARTBEAT,0);

                            // Send GoUp command plus a wrapping counter byte every nn ms
                            // until the button is released

                            while (action_touched) {
                                try {
                                    setDeadmanMove(v==btnUp,counter);
                                    Thread.sleep(20);
                                    ++counter;
                                }
                                catch (InterruptedException ex) {
                                    action_touched = false;
                                }
                                catch (Exception ex) {
                                    action_touched = false;
                                }
                            }

                            // Send a STOP command
                            setDeadmanStop();

                            // Enable heart beat again
                            ServiceRequest.send(EnRequest.REQ_SET_HEARTBEAT,1);

                            // We are done
                        }
                    }).start();
                }
                break;

            case MotionEvent.ACTION_UP:
                // Stop Thread
                action_touched = false;
                break;
        }
        return true;
    }

The snipped below is part of the communication class that manages the Bluetooth serial communication.

 public void btWrite(DeviceRecord message) {
     if (runBTreceiver) {

         if (message.isValidRecord()) {
             try {

                 lock.lock();
                 ++lockCounter;
                 mmBufferedOut.write(message.getFullRecord());
                 mmBufferedOut.flush();
             }
             catch (IOException e) {
                 if (GlobalData.isDebugger) Log.i(TAG, "Failed sending " + message + " " + e.getMessage());
                 ServiceResponse.send(EnEvent.EVT_BT_RECEIVER_ERROR, "Error data send: " + e.getMessage());
                 resetConnection();
                 runBTreceiver=false;
             }
             finally {
                 --lockCounter;
                 lock.unlock();
             }
         }
     }
 }

The code snipped that allocates and opens the Bluetooth connection

try {
    // Set up a pointer to the remote node using it's address.
    BluetoothDevice device = myBluetoothAdapter.getRemoteDevice(myBluetoothMacId);
    if (device != null)
    {
        // Two things are needed to make a connection:
        // A MAC address, which we got above.
        // A Service ID or UUID. In this case we are using the
        // UUID for SPP.

        try {
            myBluetoothSocket = device.createRfcommSocketToServiceRecord(GlobalData.MY_UUID);
        }
        catch (IOException e) {
            sendEventStatus(EnEvent.EVT_BTADAPTER_FAIL,
                    String.format(GlobalData.rString(R.string.srv_failcrt),BTERROR_CREATE,e.getMessage()));
        }

        // Establish the connection. This will block until it connects or
        // timeout?

        try {
            if (! myBluetoothSocket.isConnected()) {
                myBluetoothSocket.connect();
            }
        }
        catch (IOException e) {
            try {
                Log.e("","trying fallback...");

                myBluetoothSocket =(BluetoothSocket) device.getClass().getMethod("createRfcommSocket", new Class[] {int.class}).invoke(device,1);
                myBluetoothSocket.connect();
            }
            catch (IOException e2) {
                sendEventStatus(EnEvent.EVT_BTADAPTER_FAIL,e2.getMessage());
            }
        }
    }
    else {
        sendEventStatus(EnEvent.EVT_BTADAPTER_FAIL,
                String.format(GlobalData.rString(R.string.srv_failcrt),BTERROR_DEVICE,"getRemoteDevice failed"));
    }
}
catch (Exception e) {
    sendEventStatus(EnEvent.EVT_BTADAPTER_FAIL, e.getMessage());
    return;
}
InputStream tmpIn = null;
OutputStream tmpOut = null;

mmSocket = socket;

// Get the input and output streams, using temp objects because
// member streams are final

try {
    tmpIn = socket.getInputStream();
    tmpOut = socket.getOutputStream();
}
catch (IOException e) {
    ServiceResponse.send(EnEvent.EVT_ERROR, GlobalData.rString(R.string.srv_failcst) + e.getMessage());
    resetConnection();
    runBTreceiver=false;
}
mmInStream = tmpIn;
// mmOutStream = tmpOut;
mmBufferedOut = new BufferedOutputStream(tmpOut,80);

// Initial request
btWrite(new DeviceRecord(0, 4));

I have never discovered any problems sending and receiving data via this code. All records are sent and received properly. Only problem was that I am unable to purge the transmit buffer at the moment the operate button was released.

To overcome this problem, I have changed the protocol in such a way, that only a single 'keep turning' telegram is send at a time, the next telegram will be send after a response from the other end (sort of handshaking), the program then continue to run this ping/pong until the button is released. This method works quite well as the transmit buffer will never hold more than one telegram at a time.

the mentioned problem is solved though but I still have no clue of whether it would be possible to purge a transmit buffer

Upvotes: 0

Related Questions