Related
Currently I am trying to deal with a strange Exception when opening a BluetoothSocket on my Nexus 7 (2012), with Android 4.3 (Build JWR66Y, I guess the second 4.3 update). I have seen some related postings (e.g. https://stackoverflow.com/questions/13648373/bluetoothsocket-connect-throwing-exception-read-failed), but none seems to provide a workaround for this issue. Also, as suggested in these threads, re-pairing does not help, and constantly trying to connect (through a stupid loop) also has no effect.
I am dealing with an embedded device (a noname OBD-II car adapter, similar to http://images04.olx.com/ui/15/53/76/1316534072_254254776_2-OBD-II-BLUTOOTH-ADAPTERSCLEAR-CHECK-ENGINE-LIGHTS-WITH-YOUR-PHONE-Oceanside.jpg). My Android 2.3.7 phone does not have any issues connecting, and the Xperia of a colleague (Android 4.1.2) also works. Another Google Nexus (I dont know if 'One' or 'S', but not '4') also fails with Android 4.3.
Here is the Snippet of the connection establishment. It is running in its own Thread, created within a Service.
private class ConnectThread extends Thread {
private static final UUID EMBEDDED_BOARD_SPP = UUID
.fromString("00001101-0000-1000-8000-00805F9B34FB");
private BluetoothAdapter adapter;
private boolean secure;
private BluetoothDevice device;
private List<UUID> uuidCandidates;
private int candidate;
protected boolean started;
public ConnectThread(BluetoothDevice device, boolean secure) {
logger.info("initiliasing connection to device "+device.getName() +" / "+ device.getAddress());
adapter = BluetoothAdapter.getDefaultAdapter();
this.secure = secure;
this.device = device;
setName("BluetoothConnectThread");
if (!startQueryingForUUIDs()) {
this.uuidCandidates = Collections.singletonList(EMBEDDED_BOARD_SPP);
this.start();
} else{
logger.info("Using UUID discovery mechanism.");
}
/*
* it will start upon the broadcast receive otherwise
*/
}
private boolean startQueryingForUUIDs() {
Class<?> cl = BluetoothDevice.class;
Class<?>[] par = {};
Method fetchUuidsWithSdpMethod;
try {
fetchUuidsWithSdpMethod = cl.getMethod("fetchUuidsWithSdp", par);
} catch (NoSuchMethodException e) {
logger.warn(e.getMessage());
return false;
}
Object[] args = {};
try {
BroadcastReceiver receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
BluetoothDevice deviceExtra = intent.getParcelableExtra("android.bluetooth.device.extra.DEVICE");
Parcelable[] uuidExtra = intent.getParcelableArrayExtra("android.bluetooth.device.extra.UUID");
uuidCandidates = new ArrayList<UUID>();
for (Parcelable uuid : uuidExtra) {
uuidCandidates.add(UUID.fromString(uuid.toString()));
}
synchronized (ConnectThread.this) {
if (!ConnectThread.this.started) {
ConnectThread.this.start();
ConnectThread.this.started = true;
unregisterReceiver(this);
}
}
}
};
registerReceiver(receiver, new IntentFilter("android.bleutooth.device.action.UUID"));
registerReceiver(receiver, new IntentFilter("android.bluetooth.device.action.UUID"));
fetchUuidsWithSdpMethod.invoke(device, args);
} catch (IllegalArgumentException e) {
logger.warn(e.getMessage());
return false;
} catch (IllegalAccessException e) {
logger.warn(e.getMessage());
return false;
} catch (InvocationTargetException e) {
logger.warn(e.getMessage());
return false;
}
return true;
}
public void run() {
boolean success = false;
while (selectSocket()) {
if (bluetoothSocket == null) {
logger.warn("Socket is null! Cancelling!");
deviceDisconnected();
openTroubleshootingActivity(TroubleshootingActivity.BLUETOOTH_EXCEPTION);
}
// Always cancel discovery because it will slow down a connection
adapter.cancelDiscovery();
// Make a connection to the BluetoothSocket
try {
// This is a blocking call and will only return on a
// successful connection or an exception
bluetoothSocket.connect();
success = true;
break;
} catch (IOException e) {
// Close the socket
try {
shutdownSocket();
} catch (IOException e2) {
logger.warn(e2.getMessage(), e2);
}
}
}
if (success) {
deviceConnected();
} else {
deviceDisconnected();
openTroubleshootingActivity(TroubleshootingActivity.BLUETOOTH_EXCEPTION);
}
}
private boolean selectSocket() {
if (candidate >= uuidCandidates.size()) {
return false;
}
BluetoothSocket tmp;
UUID uuid = uuidCandidates.get(candidate++);
logger.info("Attempting to connect to SDP "+ uuid);
try {
if (secure) {
tmp = device.createRfcommSocketToServiceRecord(
uuid);
} else {
tmp = device.createInsecureRfcommSocketToServiceRecord(
uuid);
}
bluetoothSocket = tmp;
return true;
} catch (IOException e) {
logger.warn(e.getMessage() ,e);
}
return false;
}
}
The code is failing at bluetoothSocket.connect(). I am getting a java.io.IOException: read failed, socket might closed, read ret: -1. This is the corresponding source at GitHub: https://github.com/android/platform_frameworks_base/blob/android-4.3_r2/core/java/android/bluetooth/BluetoothSocket.java#L504
Its called through readInt(), called from https://github.com/android/platform_frameworks_base/blob/android-4.3_r2/core/java/android/bluetooth/BluetoothSocket.java#L319
Some metadata dump of the used socket resulted in the following information. These are exactly the same on Nexus 7 and my 2.3.7 phone.
Bluetooth Device 'OBDII'
Address: 11:22:33:DD:EE:FF
Bond state: 12 (bonded)
Type: 1
Class major version: 7936
Class minor version: 7936
Class Contents: 0
Contents: 0
I have some other OBD-II adapters (more expansives) and they all work. Is there any chance, that I am missing something or might this be a bug in Android?
I have finally found a workaround. The magic is hidden under the hood of the BluetoothDevice class (see https://github.com/android/platform_frameworks_base/blob/android-4.3_r2/core/java/android/bluetooth/BluetoothDevice.java#L1037).
Now, when I receive that exception, I instantiate a fallback BluetoothSocket, similar to the source code below. As you can see, invoking the hidden method createRfcommSocket via reflections. I have no clue why this method is hidden. The source code defines it as public though...
Class<?> clazz = tmp.getRemoteDevice().getClass();
Class<?>[] paramTypes = new Class<?>[] {Integer.TYPE};
Method m = clazz.getMethod("createRfcommSocket", paramTypes);
Object[] params = new Object[] {Integer.valueOf(1)};
fallbackSocket = (BluetoothSocket) m.invoke(tmp.getRemoteDevice(), params);
fallbackSocket.connect();
connect() then does not fail any longer. I have experienced a few issues still. Basically, this sometimes blocks and fails. Rebooting the SPP-Device (plug off / plug in) helps in such cases. Sometimes I also get another Pairing request after connect() even when the device is already bonded.
UPDATE:
here is a complete class, containing some nested classes. for a real implementation these could be held as seperate classes.
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Method;
import java.util.List;
import java.util.UUID;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.util.Log;
public class BluetoothConnector {
private BluetoothSocketWrapper bluetoothSocket;
private BluetoothDevice device;
private boolean secure;
private BluetoothAdapter adapter;
private List<UUID> uuidCandidates;
private int candidate;
/**
* #param device the device
* #param secure if connection should be done via a secure socket
* #param adapter the Android BT adapter
* #param uuidCandidates a list of UUIDs. if null or empty, the Serial PP id is used
*/
public BluetoothConnector(BluetoothDevice device, boolean secure, BluetoothAdapter adapter,
List<UUID> uuidCandidates) {
this.device = device;
this.secure = secure;
this.adapter = adapter;
this.uuidCandidates = uuidCandidates;
if (this.uuidCandidates == null || this.uuidCandidates.isEmpty()) {
this.uuidCandidates = new ArrayList<UUID>();
this.uuidCandidates.add(UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"));
}
}
public BluetoothSocketWrapper connect() throws IOException {
boolean success = false;
while (selectSocket()) {
adapter.cancelDiscovery();
try {
bluetoothSocket.connect();
success = true;
break;
} catch (IOException e) {
//try the fallback
try {
bluetoothSocket = new FallbackBluetoothSocket(bluetoothSocket.getUnderlyingSocket());
Thread.sleep(500);
bluetoothSocket.connect();
success = true;
break;
} catch (FallbackException e1) {
Log.w("BT", "Could not initialize FallbackBluetoothSocket classes.", e);
} catch (InterruptedException e1) {
Log.w("BT", e1.getMessage(), e1);
} catch (IOException e1) {
Log.w("BT", "Fallback failed. Cancelling.", e1);
}
}
}
if (!success) {
throw new IOException("Could not connect to device: "+ device.getAddress());
}
return bluetoothSocket;
}
private boolean selectSocket() throws IOException {
if (candidate >= uuidCandidates.size()) {
return false;
}
BluetoothSocket tmp;
UUID uuid = uuidCandidates.get(candidate++);
Log.i("BT", "Attempting to connect to Protocol: "+ uuid);
if (secure) {
tmp = device.createRfcommSocketToServiceRecord(uuid);
} else {
tmp = device.createInsecureRfcommSocketToServiceRecord(uuid);
}
bluetoothSocket = new NativeBluetoothSocket(tmp);
return true;
}
public static interface BluetoothSocketWrapper {
InputStream getInputStream() throws IOException;
OutputStream getOutputStream() throws IOException;
String getRemoteDeviceName();
void connect() throws IOException;
String getRemoteDeviceAddress();
void close() throws IOException;
BluetoothSocket getUnderlyingSocket();
}
public static class NativeBluetoothSocket implements BluetoothSocketWrapper {
private BluetoothSocket socket;
public NativeBluetoothSocket(BluetoothSocket tmp) {
this.socket = tmp;
}
#Override
public InputStream getInputStream() throws IOException {
return socket.getInputStream();
}
#Override
public OutputStream getOutputStream() throws IOException {
return socket.getOutputStream();
}
#Override
public String getRemoteDeviceName() {
return socket.getRemoteDevice().getName();
}
#Override
public void connect() throws IOException {
socket.connect();
}
#Override
public String getRemoteDeviceAddress() {
return socket.getRemoteDevice().getAddress();
}
#Override
public void close() throws IOException {
socket.close();
}
#Override
public BluetoothSocket getUnderlyingSocket() {
return socket;
}
}
public class FallbackBluetoothSocket extends NativeBluetoothSocket {
private BluetoothSocket fallbackSocket;
public FallbackBluetoothSocket(BluetoothSocket tmp) throws FallbackException {
super(tmp);
try
{
Class<?> clazz = tmp.getRemoteDevice().getClass();
Class<?>[] paramTypes = new Class<?>[] {Integer.TYPE};
Method m = clazz.getMethod("createRfcommSocket", paramTypes);
Object[] params = new Object[] {Integer.valueOf(1)};
fallbackSocket = (BluetoothSocket) m.invoke(tmp.getRemoteDevice(), params);
}
catch (Exception e)
{
throw new FallbackException(e);
}
}
#Override
public InputStream getInputStream() throws IOException {
return fallbackSocket.getInputStream();
}
#Override
public OutputStream getOutputStream() throws IOException {
return fallbackSocket.getOutputStream();
}
#Override
public void connect() throws IOException {
fallbackSocket.connect();
}
#Override
public void close() throws IOException {
fallbackSocket.close();
}
}
public static class FallbackException extends Exception {
/**
*
*/
private static final long serialVersionUID = 1L;
public FallbackException(Exception e) {
super(e);
}
}
}
well, i had the same problem with my code, and it's because since android 4.2 bluetooth stack has changed. so my code was running fine on devices with android < 4.2 , on the other devices i was getting the famous exception "read failed, socket might closed or timeout, read ret: -1"
The problem is with the socket.mPort parameter. When you create your socket using socket = device.createRfcommSocketToServiceRecord(SERIAL_UUID); , the mPort gets integer value "-1", and this value seems doesn't work for android >=4.2 , so you need to set it to "1". The bad news is that createRfcommSocketToServiceRecord only accepts UUID as parameter and not mPort so we have to use other aproach. The answer posted by #matthes also worked for me, but i simplified it: socket =(BluetoothSocket) device.getClass().getMethod("createRfcommSocket", new Class[] {int.class}).invoke(device,1);. We need to use both socket attribs , the second one as a fallback.
So the code is (for connecting to a SPP on an ELM327 device):
BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter();
if (btAdapter.isEnabled()) {
SharedPreferences prefs_btdev = getSharedPreferences("btdev", 0);
String btdevaddr=prefs_btdev.getString("btdevaddr","?");
if (btdevaddr != "?")
{
BluetoothDevice device = btAdapter.getRemoteDevice(btdevaddr);
UUID SERIAL_UUID = UUID.fromString("00001101-0000-1000-8000-00805f9b34fb"); // bluetooth serial port service
//UUID SERIAL_UUID = device.getUuids()[0].getUuid(); //if you don't know the UUID of the bluetooth device service, you can get it like this from android cache
BluetoothSocket socket = null;
try {
socket = device.createRfcommSocketToServiceRecord(SERIAL_UUID);
} catch (Exception e) {Log.e("","Error creating socket");}
try {
socket.connect();
Log.e("","Connected");
} catch (IOException e) {
Log.e("",e.getMessage());
try {
Log.e("","trying fallback...");
socket =(BluetoothSocket) device.getClass().getMethod("createRfcommSocket", new Class[] {int.class}).invoke(device,1);
socket.connect();
Log.e("","Connected");
}
catch (Exception e2) {
Log.e("", "Couldn't establish Bluetooth connection!");
}
}
}
else
{
Log.e("","BT device not selected");
}
}
First, if you need to talk to a bluetooth 2.x device, this documentation states that :
Hint: If you are connecting to a Bluetooth serial board then try using
the well-known SPP UUID 00001101-0000-1000-8000-00805F9B34FB. However
if you are connecting to an Android peer then please generate your own
unique UUID.
I didn't think that it would work, but only by replacing the UUID with 00001101-0000-1000-8000-00805F9B34FB it works. However, this code seems to handle the problem of SDK version, and you can just replace the function device.createRfcommSocketToServiceRecord(mMyUuid); with tmp = createBluetoothSocket(mmDevice); after defining the following method :
private BluetoothSocket createBluetoothSocket(BluetoothDevice device)
throws IOException {
if(Build.VERSION.SDK_INT >= 10){
try {
final Method m = device.getClass().getMethod("createInsecureRfcommSocketToServiceRecord", new Class[] { UUID.class });
return (BluetoothSocket) m.invoke(device, mMyUuid);
} catch (Exception e) {
Log.e(TAG, "Could not create Insecure RFComm Connection",e);
}
}
return device.createRfcommSocketToServiceRecord(mMyUuid);
}
The source code isn't mine, but comes from this website.
I had the same symptoms as described here. I could connect once to a bluetooth printer but subsequent connects failed with "socket closed" no matter what I did.
I found it a bit strange that the workarounds described here would be necessary. After going through my code I found that I had forgot to close the socket's InputStream and OutputSteram and not terminated the ConnectedThreads properly.
The ConnectedThread I use is the same as in the example here:
http://developer.android.com/guide/topics/connectivity/bluetooth.html
Note that ConnectThread and ConnectedThread are two different classes.
Whatever class that starts the ConnectedThread must call interrupt() and cancel() on the thread.
I added mmInStream.close() and mmOutStream.close() in the ConnectedTread.cancel() method.
After closing the threads/streams/sockets properly I could create new sockets without any problem.
On newer versions of Android, I was receiving this error because the adapter was still discovering when I attempted to connect to the socket. Even though I called the cancelDiscovery method on the Bluetooth adapter, I had to wait until the callback to the BroadcastReceiver's onReceive() method was called with the action BluetoothAdapter.ACTION_DISCOVERY_FINISHED.
Once I waited for the adapter to stop discovery, then the connect call on the socket succeeded.
Well, I have actually found the problem.
The most people who try to make a connection using socket.Connect(); get an exception called Java.IO.IOException: read failed, socket might closed, read ret: -1.
In some cases it also depends on your Bluetooth device, because there are two different types of Bluetooth, namely BLE (low energy) and Classic.
If you want to check the type of your Bluetooth device is, here's the code:
String checkType;
var listDevices = BluetoothAdapter.BondedDevices;
if (listDevices.Count > 0)
{
foreach (var btDevice in listDevices)
{
if(btDevice.Name == "MOCUTE-032_B52-CA7E")
{
checkType = btDevice.Type.ToString();
Console.WriteLine(checkType);
}
}
}
I've been trying for days to solve the problem, but since today I have found the problem. The solution from #matthes has unfortunately still a few issues as he said already, but here's my solution.
At the moment I work in Xamarin Android, but this should also work for other platforms.
SOLUTION
If there is more than one paired device, then you should remove the other paired devices. So keep only the one that you want to connect (see the right image).
In the left image you see that I have two paired devices, namely "MOCUTE-032_B52-CA7E" and "Blue Easy". That's the issue, but I have no idea why that problem occurs. Maybe the Bluetooth protocol is trying to get some information from another Bluetooth device.
However, the socket.Connect(); works great right now, without any problems. So I just wanted to share this, because that error is really annoying.
Good luck!
In case somebody is having issues with Kotlin, I had to follow the accepted answer with some variations:
fun print(view: View, text: String) {
var adapter = BluetoothAdapter.getDefaultAdapter();
var pairedDevices = adapter.getBondedDevices()
var uuid = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB")
if (pairedDevices.size > 0) {
for (device in pairedDevices) {
var s = device.name
if (device.getName().equals(printerName, ignoreCase = true)) {
Thread {
var socket = device.createInsecureRfcommSocketToServiceRecord(uuid)
var clazz = socket.remoteDevice.javaClass
var paramTypes = arrayOf<Class<*>>(Integer.TYPE)
var m = clazz.getMethod("createRfcommSocket", *paramTypes)
var fallbackSocket = m.invoke(socket.remoteDevice, Integer.valueOf(1)) as BluetoothSocket
try {
fallbackSocket.connect()
var stream = fallbackSocket.outputStream
stream.write(text.toByteArray(Charset.forName("UTF-8")))
} catch (e: Exception) {
e.printStackTrace()
Snackbar.make(view, "An error occurred", Snackbar.LENGTH_SHORT).show()
}
}.start()
}
}
}
}
Hope it helps
You put
registerReceiver(receiver, new IntentFilter("android.bleutooth.device.action.UUID"));
with "bluetooth" spelled "bleutooth".
Bluetooth devices can operate in both classic and LE mode at the same time. Sometimes they use a different MAC address depending on which way you are connecting. Calling socket.connect() is using Bluetooth Classic, so you have to make sure the device you got when you scanned was really a classic device.
It's easy to filter for only Classic devices, however:
if(BluetoothDevice.DEVICE_TYPE_LE == device.getType()){
//socket.connect()
}
Without this check, it's a race condition as to whether a hybrid scan will give you the Classic device or the BLE device first. It may appear as intermittent inability to connect, or as certain devices being able to connect reliably while others seemingly never can.
i also faced with this problem,you could solve it in 2 ways , as mentioned earlier use reflection to create the socket
Second one is,
client is looking for a server with given UUID and if your server isn't running parallel to client then this happens.
Create a server with given client UUID and then listen and accept the client from server side.It will work.
Even i had the same problem ,finally understand my issue , i was trying to connect from (out of range) Bluetooth coverage range.
I ran into this problem and fixed it by closing the input and output streams before closing the socket. Now I can disconnect and connect again with no issues.
https://stackoverflow.com/a/3039807/5688612
In Kotlin:
fun disconnect() {
bluetoothSocket.inputStream.close()
bluetoothSocket.outputStream.close()
bluetoothSocket.close()
}
If another part of your code has already made a connection with the same socket and UUID, you get this error.
I've had this problem and the solution was to use the special magic GUID.
val id: UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB") // Any other GUID doesn't work.
val device: BluetoothDevice = bta!!.bondedDevices.first { z -> z.name == deviceName }
bts = device.createRfcommSocketToServiceRecord(id) // mPort is -1
bts?.connect()
// Start processing thread.
I suspect that these are the UUIDs that work:
var did: Array<ParcelUuid?> = device.uuids
However, I have not tried them all.
By adding filter action my problem resolved
// Register for broadcasts when a device is discovered
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(BluetoothDevice.ACTION_FOUND);
intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
registerReceiver(mReceiver, intentFilter);
I have also receive the same IOException, but I find the Android system demo: "BluetoothChat" project is worked. I determined the problem is the UUID.
So i replace my UUID.fromString("00001001-0000-1000-8000-00805F9B34FB") to UUID.fromString("8ce255c0-200a-11e0-ac64-0800200c9a66") and it worked most scene,only sometimes need to restart the Bluetooth device;
Currently I am trying to deal with a strange Exception when opening a BluetoothSocket on my Nexus 7 (2012), with Android 4.3 (Build JWR66Y, I guess the second 4.3 update). I have seen some related postings (e.g. https://stackoverflow.com/questions/13648373/bluetoothsocket-connect-throwing-exception-read-failed), but none seems to provide a workaround for this issue. Also, as suggested in these threads, re-pairing does not help, and constantly trying to connect (through a stupid loop) also has no effect.
I am dealing with an embedded device (a noname OBD-II car adapter, similar to http://images04.olx.com/ui/15/53/76/1316534072_254254776_2-OBD-II-BLUTOOTH-ADAPTERSCLEAR-CHECK-ENGINE-LIGHTS-WITH-YOUR-PHONE-Oceanside.jpg). My Android 2.3.7 phone does not have any issues connecting, and the Xperia of a colleague (Android 4.1.2) also works. Another Google Nexus (I dont know if 'One' or 'S', but not '4') also fails with Android 4.3.
Here is the Snippet of the connection establishment. It is running in its own Thread, created within a Service.
private class ConnectThread extends Thread {
private static final UUID EMBEDDED_BOARD_SPP = UUID
.fromString("00001101-0000-1000-8000-00805F9B34FB");
private BluetoothAdapter adapter;
private boolean secure;
private BluetoothDevice device;
private List<UUID> uuidCandidates;
private int candidate;
protected boolean started;
public ConnectThread(BluetoothDevice device, boolean secure) {
logger.info("initiliasing connection to device "+device.getName() +" / "+ device.getAddress());
adapter = BluetoothAdapter.getDefaultAdapter();
this.secure = secure;
this.device = device;
setName("BluetoothConnectThread");
if (!startQueryingForUUIDs()) {
this.uuidCandidates = Collections.singletonList(EMBEDDED_BOARD_SPP);
this.start();
} else{
logger.info("Using UUID discovery mechanism.");
}
/*
* it will start upon the broadcast receive otherwise
*/
}
private boolean startQueryingForUUIDs() {
Class<?> cl = BluetoothDevice.class;
Class<?>[] par = {};
Method fetchUuidsWithSdpMethod;
try {
fetchUuidsWithSdpMethod = cl.getMethod("fetchUuidsWithSdp", par);
} catch (NoSuchMethodException e) {
logger.warn(e.getMessage());
return false;
}
Object[] args = {};
try {
BroadcastReceiver receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
BluetoothDevice deviceExtra = intent.getParcelableExtra("android.bluetooth.device.extra.DEVICE");
Parcelable[] uuidExtra = intent.getParcelableArrayExtra("android.bluetooth.device.extra.UUID");
uuidCandidates = new ArrayList<UUID>();
for (Parcelable uuid : uuidExtra) {
uuidCandidates.add(UUID.fromString(uuid.toString()));
}
synchronized (ConnectThread.this) {
if (!ConnectThread.this.started) {
ConnectThread.this.start();
ConnectThread.this.started = true;
unregisterReceiver(this);
}
}
}
};
registerReceiver(receiver, new IntentFilter("android.bleutooth.device.action.UUID"));
registerReceiver(receiver, new IntentFilter("android.bluetooth.device.action.UUID"));
fetchUuidsWithSdpMethod.invoke(device, args);
} catch (IllegalArgumentException e) {
logger.warn(e.getMessage());
return false;
} catch (IllegalAccessException e) {
logger.warn(e.getMessage());
return false;
} catch (InvocationTargetException e) {
logger.warn(e.getMessage());
return false;
}
return true;
}
public void run() {
boolean success = false;
while (selectSocket()) {
if (bluetoothSocket == null) {
logger.warn("Socket is null! Cancelling!");
deviceDisconnected();
openTroubleshootingActivity(TroubleshootingActivity.BLUETOOTH_EXCEPTION);
}
// Always cancel discovery because it will slow down a connection
adapter.cancelDiscovery();
// Make a connection to the BluetoothSocket
try {
// This is a blocking call and will only return on a
// successful connection or an exception
bluetoothSocket.connect();
success = true;
break;
} catch (IOException e) {
// Close the socket
try {
shutdownSocket();
} catch (IOException e2) {
logger.warn(e2.getMessage(), e2);
}
}
}
if (success) {
deviceConnected();
} else {
deviceDisconnected();
openTroubleshootingActivity(TroubleshootingActivity.BLUETOOTH_EXCEPTION);
}
}
private boolean selectSocket() {
if (candidate >= uuidCandidates.size()) {
return false;
}
BluetoothSocket tmp;
UUID uuid = uuidCandidates.get(candidate++);
logger.info("Attempting to connect to SDP "+ uuid);
try {
if (secure) {
tmp = device.createRfcommSocketToServiceRecord(
uuid);
} else {
tmp = device.createInsecureRfcommSocketToServiceRecord(
uuid);
}
bluetoothSocket = tmp;
return true;
} catch (IOException e) {
logger.warn(e.getMessage() ,e);
}
return false;
}
}
The code is failing at bluetoothSocket.connect(). I am getting a java.io.IOException: read failed, socket might closed, read ret: -1. This is the corresponding source at GitHub: https://github.com/android/platform_frameworks_base/blob/android-4.3_r2/core/java/android/bluetooth/BluetoothSocket.java#L504
Its called through readInt(), called from https://github.com/android/platform_frameworks_base/blob/android-4.3_r2/core/java/android/bluetooth/BluetoothSocket.java#L319
Some metadata dump of the used socket resulted in the following information. These are exactly the same on Nexus 7 and my 2.3.7 phone.
Bluetooth Device 'OBDII'
Address: 11:22:33:DD:EE:FF
Bond state: 12 (bonded)
Type: 1
Class major version: 7936
Class minor version: 7936
Class Contents: 0
Contents: 0
I have some other OBD-II adapters (more expansives) and they all work. Is there any chance, that I am missing something or might this be a bug in Android?
I have finally found a workaround. The magic is hidden under the hood of the BluetoothDevice class (see https://github.com/android/platform_frameworks_base/blob/android-4.3_r2/core/java/android/bluetooth/BluetoothDevice.java#L1037).
Now, when I receive that exception, I instantiate a fallback BluetoothSocket, similar to the source code below. As you can see, invoking the hidden method createRfcommSocket via reflections. I have no clue why this method is hidden. The source code defines it as public though...
Class<?> clazz = tmp.getRemoteDevice().getClass();
Class<?>[] paramTypes = new Class<?>[] {Integer.TYPE};
Method m = clazz.getMethod("createRfcommSocket", paramTypes);
Object[] params = new Object[] {Integer.valueOf(1)};
fallbackSocket = (BluetoothSocket) m.invoke(tmp.getRemoteDevice(), params);
fallbackSocket.connect();
connect() then does not fail any longer. I have experienced a few issues still. Basically, this sometimes blocks and fails. Rebooting the SPP-Device (plug off / plug in) helps in such cases. Sometimes I also get another Pairing request after connect() even when the device is already bonded.
UPDATE:
here is a complete class, containing some nested classes. for a real implementation these could be held as seperate classes.
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Method;
import java.util.List;
import java.util.UUID;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.util.Log;
public class BluetoothConnector {
private BluetoothSocketWrapper bluetoothSocket;
private BluetoothDevice device;
private boolean secure;
private BluetoothAdapter adapter;
private List<UUID> uuidCandidates;
private int candidate;
/**
* #param device the device
* #param secure if connection should be done via a secure socket
* #param adapter the Android BT adapter
* #param uuidCandidates a list of UUIDs. if null or empty, the Serial PP id is used
*/
public BluetoothConnector(BluetoothDevice device, boolean secure, BluetoothAdapter adapter,
List<UUID> uuidCandidates) {
this.device = device;
this.secure = secure;
this.adapter = adapter;
this.uuidCandidates = uuidCandidates;
if (this.uuidCandidates == null || this.uuidCandidates.isEmpty()) {
this.uuidCandidates = new ArrayList<UUID>();
this.uuidCandidates.add(UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"));
}
}
public BluetoothSocketWrapper connect() throws IOException {
boolean success = false;
while (selectSocket()) {
adapter.cancelDiscovery();
try {
bluetoothSocket.connect();
success = true;
break;
} catch (IOException e) {
//try the fallback
try {
bluetoothSocket = new FallbackBluetoothSocket(bluetoothSocket.getUnderlyingSocket());
Thread.sleep(500);
bluetoothSocket.connect();
success = true;
break;
} catch (FallbackException e1) {
Log.w("BT", "Could not initialize FallbackBluetoothSocket classes.", e);
} catch (InterruptedException e1) {
Log.w("BT", e1.getMessage(), e1);
} catch (IOException e1) {
Log.w("BT", "Fallback failed. Cancelling.", e1);
}
}
}
if (!success) {
throw new IOException("Could not connect to device: "+ device.getAddress());
}
return bluetoothSocket;
}
private boolean selectSocket() throws IOException {
if (candidate >= uuidCandidates.size()) {
return false;
}
BluetoothSocket tmp;
UUID uuid = uuidCandidates.get(candidate++);
Log.i("BT", "Attempting to connect to Protocol: "+ uuid);
if (secure) {
tmp = device.createRfcommSocketToServiceRecord(uuid);
} else {
tmp = device.createInsecureRfcommSocketToServiceRecord(uuid);
}
bluetoothSocket = new NativeBluetoothSocket(tmp);
return true;
}
public static interface BluetoothSocketWrapper {
InputStream getInputStream() throws IOException;
OutputStream getOutputStream() throws IOException;
String getRemoteDeviceName();
void connect() throws IOException;
String getRemoteDeviceAddress();
void close() throws IOException;
BluetoothSocket getUnderlyingSocket();
}
public static class NativeBluetoothSocket implements BluetoothSocketWrapper {
private BluetoothSocket socket;
public NativeBluetoothSocket(BluetoothSocket tmp) {
this.socket = tmp;
}
#Override
public InputStream getInputStream() throws IOException {
return socket.getInputStream();
}
#Override
public OutputStream getOutputStream() throws IOException {
return socket.getOutputStream();
}
#Override
public String getRemoteDeviceName() {
return socket.getRemoteDevice().getName();
}
#Override
public void connect() throws IOException {
socket.connect();
}
#Override
public String getRemoteDeviceAddress() {
return socket.getRemoteDevice().getAddress();
}
#Override
public void close() throws IOException {
socket.close();
}
#Override
public BluetoothSocket getUnderlyingSocket() {
return socket;
}
}
public class FallbackBluetoothSocket extends NativeBluetoothSocket {
private BluetoothSocket fallbackSocket;
public FallbackBluetoothSocket(BluetoothSocket tmp) throws FallbackException {
super(tmp);
try
{
Class<?> clazz = tmp.getRemoteDevice().getClass();
Class<?>[] paramTypes = new Class<?>[] {Integer.TYPE};
Method m = clazz.getMethod("createRfcommSocket", paramTypes);
Object[] params = new Object[] {Integer.valueOf(1)};
fallbackSocket = (BluetoothSocket) m.invoke(tmp.getRemoteDevice(), params);
}
catch (Exception e)
{
throw new FallbackException(e);
}
}
#Override
public InputStream getInputStream() throws IOException {
return fallbackSocket.getInputStream();
}
#Override
public OutputStream getOutputStream() throws IOException {
return fallbackSocket.getOutputStream();
}
#Override
public void connect() throws IOException {
fallbackSocket.connect();
}
#Override
public void close() throws IOException {
fallbackSocket.close();
}
}
public static class FallbackException extends Exception {
/**
*
*/
private static final long serialVersionUID = 1L;
public FallbackException(Exception e) {
super(e);
}
}
}
well, i had the same problem with my code, and it's because since android 4.2 bluetooth stack has changed. so my code was running fine on devices with android < 4.2 , on the other devices i was getting the famous exception "read failed, socket might closed or timeout, read ret: -1"
The problem is with the socket.mPort parameter. When you create your socket using socket = device.createRfcommSocketToServiceRecord(SERIAL_UUID); , the mPort gets integer value "-1", and this value seems doesn't work for android >=4.2 , so you need to set it to "1". The bad news is that createRfcommSocketToServiceRecord only accepts UUID as parameter and not mPort so we have to use other aproach. The answer posted by #matthes also worked for me, but i simplified it: socket =(BluetoothSocket) device.getClass().getMethod("createRfcommSocket", new Class[] {int.class}).invoke(device,1);. We need to use both socket attribs , the second one as a fallback.
So the code is (for connecting to a SPP on an ELM327 device):
BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter();
if (btAdapter.isEnabled()) {
SharedPreferences prefs_btdev = getSharedPreferences("btdev", 0);
String btdevaddr=prefs_btdev.getString("btdevaddr","?");
if (btdevaddr != "?")
{
BluetoothDevice device = btAdapter.getRemoteDevice(btdevaddr);
UUID SERIAL_UUID = UUID.fromString("00001101-0000-1000-8000-00805f9b34fb"); // bluetooth serial port service
//UUID SERIAL_UUID = device.getUuids()[0].getUuid(); //if you don't know the UUID of the bluetooth device service, you can get it like this from android cache
BluetoothSocket socket = null;
try {
socket = device.createRfcommSocketToServiceRecord(SERIAL_UUID);
} catch (Exception e) {Log.e("","Error creating socket");}
try {
socket.connect();
Log.e("","Connected");
} catch (IOException e) {
Log.e("",e.getMessage());
try {
Log.e("","trying fallback...");
socket =(BluetoothSocket) device.getClass().getMethod("createRfcommSocket", new Class[] {int.class}).invoke(device,1);
socket.connect();
Log.e("","Connected");
}
catch (Exception e2) {
Log.e("", "Couldn't establish Bluetooth connection!");
}
}
}
else
{
Log.e("","BT device not selected");
}
}
First, if you need to talk to a bluetooth 2.x device, this documentation states that :
Hint: If you are connecting to a Bluetooth serial board then try using
the well-known SPP UUID 00001101-0000-1000-8000-00805F9B34FB. However
if you are connecting to an Android peer then please generate your own
unique UUID.
I didn't think that it would work, but only by replacing the UUID with 00001101-0000-1000-8000-00805F9B34FB it works. However, this code seems to handle the problem of SDK version, and you can just replace the function device.createRfcommSocketToServiceRecord(mMyUuid); with tmp = createBluetoothSocket(mmDevice); after defining the following method :
private BluetoothSocket createBluetoothSocket(BluetoothDevice device)
throws IOException {
if(Build.VERSION.SDK_INT >= 10){
try {
final Method m = device.getClass().getMethod("createInsecureRfcommSocketToServiceRecord", new Class[] { UUID.class });
return (BluetoothSocket) m.invoke(device, mMyUuid);
} catch (Exception e) {
Log.e(TAG, "Could not create Insecure RFComm Connection",e);
}
}
return device.createRfcommSocketToServiceRecord(mMyUuid);
}
The source code isn't mine, but comes from this website.
I had the same symptoms as described here. I could connect once to a bluetooth printer but subsequent connects failed with "socket closed" no matter what I did.
I found it a bit strange that the workarounds described here would be necessary. After going through my code I found that I had forgot to close the socket's InputStream and OutputSteram and not terminated the ConnectedThreads properly.
The ConnectedThread I use is the same as in the example here:
http://developer.android.com/guide/topics/connectivity/bluetooth.html
Note that ConnectThread and ConnectedThread are two different classes.
Whatever class that starts the ConnectedThread must call interrupt() and cancel() on the thread.
I added mmInStream.close() and mmOutStream.close() in the ConnectedTread.cancel() method.
After closing the threads/streams/sockets properly I could create new sockets without any problem.
On newer versions of Android, I was receiving this error because the adapter was still discovering when I attempted to connect to the socket. Even though I called the cancelDiscovery method on the Bluetooth adapter, I had to wait until the callback to the BroadcastReceiver's onReceive() method was called with the action BluetoothAdapter.ACTION_DISCOVERY_FINISHED.
Once I waited for the adapter to stop discovery, then the connect call on the socket succeeded.
Well, I have actually found the problem.
The most people who try to make a connection using socket.Connect(); get an exception called Java.IO.IOException: read failed, socket might closed, read ret: -1.
In some cases it also depends on your Bluetooth device, because there are two different types of Bluetooth, namely BLE (low energy) and Classic.
If you want to check the type of your Bluetooth device is, here's the code:
String checkType;
var listDevices = BluetoothAdapter.BondedDevices;
if (listDevices.Count > 0)
{
foreach (var btDevice in listDevices)
{
if(btDevice.Name == "MOCUTE-032_B52-CA7E")
{
checkType = btDevice.Type.ToString();
Console.WriteLine(checkType);
}
}
}
I've been trying for days to solve the problem, but since today I have found the problem. The solution from #matthes has unfortunately still a few issues as he said already, but here's my solution.
At the moment I work in Xamarin Android, but this should also work for other platforms.
SOLUTION
If there is more than one paired device, then you should remove the other paired devices. So keep only the one that you want to connect (see the right image).
In the left image you see that I have two paired devices, namely "MOCUTE-032_B52-CA7E" and "Blue Easy". That's the issue, but I have no idea why that problem occurs. Maybe the Bluetooth protocol is trying to get some information from another Bluetooth device.
However, the socket.Connect(); works great right now, without any problems. So I just wanted to share this, because that error is really annoying.
Good luck!
In case somebody is having issues with Kotlin, I had to follow the accepted answer with some variations:
fun print(view: View, text: String) {
var adapter = BluetoothAdapter.getDefaultAdapter();
var pairedDevices = adapter.getBondedDevices()
var uuid = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB")
if (pairedDevices.size > 0) {
for (device in pairedDevices) {
var s = device.name
if (device.getName().equals(printerName, ignoreCase = true)) {
Thread {
var socket = device.createInsecureRfcommSocketToServiceRecord(uuid)
var clazz = socket.remoteDevice.javaClass
var paramTypes = arrayOf<Class<*>>(Integer.TYPE)
var m = clazz.getMethod("createRfcommSocket", *paramTypes)
var fallbackSocket = m.invoke(socket.remoteDevice, Integer.valueOf(1)) as BluetoothSocket
try {
fallbackSocket.connect()
var stream = fallbackSocket.outputStream
stream.write(text.toByteArray(Charset.forName("UTF-8")))
} catch (e: Exception) {
e.printStackTrace()
Snackbar.make(view, "An error occurred", Snackbar.LENGTH_SHORT).show()
}
}.start()
}
}
}
}
Hope it helps
You put
registerReceiver(receiver, new IntentFilter("android.bleutooth.device.action.UUID"));
with "bluetooth" spelled "bleutooth".
Bluetooth devices can operate in both classic and LE mode at the same time. Sometimes they use a different MAC address depending on which way you are connecting. Calling socket.connect() is using Bluetooth Classic, so you have to make sure the device you got when you scanned was really a classic device.
It's easy to filter for only Classic devices, however:
if(BluetoothDevice.DEVICE_TYPE_LE == device.getType()){
//socket.connect()
}
Without this check, it's a race condition as to whether a hybrid scan will give you the Classic device or the BLE device first. It may appear as intermittent inability to connect, or as certain devices being able to connect reliably while others seemingly never can.
i also faced with this problem,you could solve it in 2 ways , as mentioned earlier use reflection to create the socket
Second one is,
client is looking for a server with given UUID and if your server isn't running parallel to client then this happens.
Create a server with given client UUID and then listen and accept the client from server side.It will work.
Even i had the same problem ,finally understand my issue , i was trying to connect from (out of range) Bluetooth coverage range.
I ran into this problem and fixed it by closing the input and output streams before closing the socket. Now I can disconnect and connect again with no issues.
https://stackoverflow.com/a/3039807/5688612
In Kotlin:
fun disconnect() {
bluetoothSocket.inputStream.close()
bluetoothSocket.outputStream.close()
bluetoothSocket.close()
}
If another part of your code has already made a connection with the same socket and UUID, you get this error.
I've had this problem and the solution was to use the special magic GUID.
val id: UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB") // Any other GUID doesn't work.
val device: BluetoothDevice = bta!!.bondedDevices.first { z -> z.name == deviceName }
bts = device.createRfcommSocketToServiceRecord(id) // mPort is -1
bts?.connect()
// Start processing thread.
I suspect that these are the UUIDs that work:
var did: Array<ParcelUuid?> = device.uuids
However, I have not tried them all.
By adding filter action my problem resolved
// Register for broadcasts when a device is discovered
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(BluetoothDevice.ACTION_FOUND);
intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
registerReceiver(mReceiver, intentFilter);
I have also receive the same IOException, but I find the Android system demo: "BluetoothChat" project is worked. I determined the problem is the UUID.
So i replace my UUID.fromString("00001001-0000-1000-8000-00805F9B34FB") to UUID.fromString("8ce255c0-200a-11e0-ac64-0800200c9a66") and it worked most scene,only sometimes need to restart the Bluetooth device;
I'm trying to improve an actual code that make a bluetooth connection with an Android phone to an Atmega with Arduino (An electronic micro-controller). I can receive and send data to the micro-controller but the bluetooth need to be put at ON before lunching my application or it will hang and close. I do check for a bluetooth adapter and request the user to change the bluetooth state if it's at OFF but it seem the program continue and try to make a connection before getting the result of the user selection. I would like some help to find a solution to either block my program until the user input their choice or even get a better solution.
I would like to say that I'm still new to Android programming and I did read the Android activity flowchart.
I can provide logcat, but I inspected it and it clearly state that I am trying to use bluetooth even if it's not enabled ...
Here is my code :
I want to thanks anybody that could point me on the right direction
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnOn = (Button) findViewById(R.id.btnOn); // button LED ON
btnOff = (Button) findViewById(R.id.btnOff); // button LED OFF
txtArduino = (TextView) findViewById(R.id.txtArduino); // for display the received data from the Arduino
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); // get Bluetooth adapter
checkBTState();
h = new Handler() {
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
case RECIEVE_MESSAGE: // if receive massage
byte[] readBuf = (byte[]) msg.obj;
String strIncom = new String(readBuf, 0, msg.arg1); // create string from bytes array
sb.append(strIncom); // append string
int endOfLineIndex = sb.indexOf("\r\n"); // determine the end-of-line
if (endOfLineIndex > 0) { // if end-of-line,
sbprint = sb.substring(0, endOfLineIndex); // extract string
sb.delete(0, sb.length()); // and clear
txtArduino.setText("Data from Arduino: " + sbprint);
Log.e(TAG, "Arduino"+sbprint);
//Test string value
if(sbprint.matches("-?\\d+(\\.\\d+)?")) {
try{
Float sensorReading = Float.parseFloat(sbprint);
Log.e(TAG, "Sensor value"+sensorReading);
}catch(NumberFormatException e){
Log.e(TAG, "No int format sorry",e);
}
}
if(sbprint.matches("test")){
Log.e(TAG, "garbage");
}
///////
btnOff.setEnabled(true);
btnOn.setEnabled(true);
}
//Log.d(TAG, "...String:"+ sb.toString() + "Byte:" + msg.arg1 + "...");
break;
}
};
};
public void onResume() {
super.onResume();
Log.d(TAG, "...onResume - try connect...");
// Set up a pointer to the remote node using it's address.
BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
// 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 {
btSocket = createBluetoothSocket(device);
} catch (IOException e) {
errorExit("Fatal Error", "In onResume() and socket create failed: " + e.getMessage() + ".");
}
/*try {
btSocket = device.createRfcommSocketToServiceRecord(MY_UUID);
} catch (IOException e) {
errorExit("Fatal Error", "In onResume() and socket create failed: " + e.getMessage() + ".");
}*/
// Discovery is resource intensive. Make sure it isn't going on
// when you attempt to connect and pass your message.
mBluetoothAdapter.cancelDiscovery();
// Establish the connection. This will block until it connects.
Log.d(TAG, "...Connecting...");
try {
btSocket.connect();
Log.d(TAG, "....Connection ok...");
} catch (IOException e) {
try {
btSocket.close();
} catch (IOException e2) {
errorExit("Fatal Error", "In onResume() and unable to close socket during connection failure" + e2.getMessage() + ".");
}
}
// Create a data stream so we can talk to server.
Log.d(TAG, "...Create Socket...");
mConnectedThread = new ConnectedThread(btSocket);
mConnectedThread.start();
}
You have all your code in onResume. onResume is going to get called right at startup of the activity, so it will execute almost immediately. I don't see any code in there that should delay it at all. If you don't want to try to connect until the user selects something, then all the connection code should be in a button's click handler or something similar.
In addition to #GabeSechan's comment about all of your code being in onResume(), you are also calling the Bluetooth connect() in your main activity thread which according to the this documentation is a blocking call and "should always be performed in a thread separate from the main activity thread".
I am writing an application which sends data to bluetooth printer. Can anyone help me ? how can I use android Bluetooth Stack for printing? or is there any external api or sdk to use?
Here is my code for searching bluetooth...
bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
registerReceiver(ActionFoundReceiver,
new IntentFilter(BluetoothDevice.ACTION_FOUND));
private final BroadcastReceiver ActionFoundReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
BluetoothDevice device = intent
.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
btArrayAdapter.add(device.getName() + "\n"
+ device.getAddress());
btArrayAdapter.notifyDataSetChanged();
}
}
};
and here is my code for sending data to printer..
BluetoothDevice mDevice = bluetoothAdapter.getRemoteDevice("00:15:FF:F2:56:A4");
Method m = mDevice.getClass().getMethod("createRfcommSocket",
new Class[] { int.class });
mBTsocket = (BluetoothSocket) m.invoke(mDevice, 1);
System.out.println("Connecting.....");
mBTsocket.connect();
System.out.println("Connected");
OutputStream os = mBTsocket.getOutputStream();
os.flush();
os.write(Receipt.getBytes());
// mBTsocket.close();
When I write socket.close() , data is not getting print to printer as socket connection getting closed before printing data..and if I didn't write socket.close() then data is getting printed only once.. I would not be able to print data second time until I restart bluetooth of my phone.
can any one have solution for it??? or is there any other way to get rid of this printing??
I got the solution of my problem...
if i want to print data more than one time then you dont need to create new Socket Connection with the device... instead call outputstream.write(bytes) method.
and in the end if you want to disconnect device then call mBTScoket.close() method to disconnect device.
You can use printooth library for any printer, printooth is simple and well documented,
https://github.com/mazenrashed/Printooth
var printables = ArrayList<Printable>()
var printable = Printable.PrintableBuilder()
.setText("Hello World") //The text you want to print
.setAlignment(DefaultPrinter.ALLIGMENT_CENTER)
.setEmphasizedMode(DefaultPrinter.EMPHASISED_MODE_BOLD) //Bold or normal
.setFontSize(DefaultPrinter.FONT_SIZE_NORMAL)
.setUnderlined(DefaultPrinter.UNDELINED_MODE_ON) // Underline on/off
.setCharacterCode(DefaultPrinter.CHARACTER_CODE_USA_CP437) // Character code to support languages
.setLineSpacing(DefaultPrinter.LINE_SPACING_60)
.setNewLinesAfter(1) // To provide n lines after sentence
.build()
printables.add(printable)
BluetoothPrinter.printer().print(printables)
If you have made connection to the devices and paired it.
So for printing, printer wants the byte. SO I have createed a mothod.
Simply call this method and pass the String inside it to get printed.
String str = new String("This is the text sending to the printer");
private void printData() {
// TODO Auto-generated method stub
String newline = "\n";
try {
out.write(str.getBytes(),0,str.getBytes().length);
Log.i("Log", "One line printed");
} catch (IOException e) {
Toast.makeText(BluetoothDemo.this, "catch 1", Toast.LENGTH_LONG).show();
e.printStackTrace();
Log.i("Log", "unable to write ");
flagCheck = false;
}
try {
out.write(newline.getBytes(),0,newline.getBytes().length);
} catch (IOException e) {
Log.i("Log", "Unable to write the new line::");
e.printStackTrace();
flagCheck = false;
}
flagCheck = true;
}
I have modified the Minddroid github Android program to read sensors on a LEGO NXT (wonderful device!). Now I would like to read and write Bluetooth messages to a Mindstorms program running in the NXT.
So that I can run a NXT program and send the results / readings to the Android when the Android asks for them.
I've created a project where the NXT sends data back to my Android device. Here's some code that should work:
This is all the Android side code:
This is a class that I wrote, that will take care of connecting and communicating via bluetooth.
public class Connector {
public static final String TAG = "Connector";
public static final boolean BT_ON = true;
public static final boolean BT_OFF = false;
public BluetoothAdapter bluetoothAdapter;
public BluetoothSocket bluetoothSocket;
public String address;
public Connector(String address) {
this.address = address;
this.bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
}
public void setBluetooth(boolean state) {
if(state == Connector.BT_ON) {
// Check if bluetooth is off
if(this.bluetoothAdapter.isEnabled() == false)
{
this.bluetoothAdapter.enable();
while(this.bluetoothAdapter.isEnabled() == false) {
}
Log.d(Connector.TAG, "Bluetooth turned on");
}
}
// Check if bluetooth is enabled
else if(state == Connector.BT_OFF) {
// Check if bluetooth is enabled
if(this.bluetoothAdapter.isEnabled() == true)
{
this.bluetoothAdapter.disable();
while(this.bluetoothAdapter.isEnabled() == true) {
}
Log.d(Connector.TAG, "Bluetooth turned off");
}
}
}
public boolean connect() {
boolean connected = false;
BluetoothDevice nxt = this.bluetoothAdapter.getRemoteDevice(this.address);
try {
this.bluetoothSocket = nxt.createRfcommSocketToServiceRecord(UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"));
this.bluetoothSocket.connect();
connected = true;
}
catch (IOException e) {
connected = false;
}
return connected;
}
public Integer readMessage() {
Integer message;
if(this.bluetoothSocket!= null) {
try {
InputStreamReader input = new InputStreamReader(this.bluetoothSocket.getInputStream());
message = input.read();
Log.d(Connector.TAG, "Successfully read message");
}
catch (IOException e) {
message = null;
Log.d(Connector.TAG, "Couldn't read message");
}
}
else {
message = null;
Log.d(Connector.TAG, "Couldn't read message");
}
return message;
}
}
In your activity class, you can create a Connector object. In the onCreate() method, you'll have to connect to establish a connection to the NXT like so:
// Establish a bluetooth connection to the NXT
this.connector = new Connector("00:16:53:12:B6:78");
this.connector.setBluetooth(Connector.BT_ON);
this.connector.connect();
Now to read a message from the NXT (an Integer object) you can do it like this:
this.connector.readMessage();
To close the connection:
this.connector.setBluetooth(Connector.BT_OFF);
This is all the NXT side code:
NOTE: Download leJOS for all the code to work (leJOS will allow you to code your NXT in java).
Define these two objects in your main class:
public static DataOutputStream dataOutputStream;
public static NXTConnection bluetoothConnection;
To connect to you phone:
bluetoothConnection = Bluetooth.waitForConnection();
bluetoothConnection.setIOMode(NXTConnection.RAW);
dataOutputStream = bluetoothConnection.openDataOutputStream();
To send data to the phone in form of an Integer object:
dataOutputStream.write(100);
dataOutputStream.flush();
To disconnect run this:
dataOutputStream.close();
bluetoothConnection.close();
I hope this helps.
I was getting slightly confused with the bluetooth commands, but now I see that you need to download leJOS!
I usually try to avoid messing with the firmware on the NXT, but java is a lot easier to deal with!
For anyone that is interested, you can send commands down to the NXT from your android in its native format, although its not as pretty as listed above. There is a great tutorial here:
http://www.robotappstore.com/Knowledge-Base/Programming-LEGO-NXT-Mindstorms/92.html
But if you want to download an app for free, here is one:
http://www.robotappstore.com/Apps/Lego-NXT-Mindstorms-Driver---Android-app.html?x=693A00AA-7F15-46E7-9616-8101068DB58D
There are a bunch more, if you just search around on there too
Hope this helps!