I'm trying to connect a wiimote to my OUYA (running Android 4.1.2). I've managed to get wiimotes to connect using other apps like Bluez-IME, so I know it is possible, but I'd like to be able to do it myself
This is what I have so far:
private void bloothoothConnect(final BluetoothAdapter mBluetoothAdapter){
// Register the BroadcastReceiver
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
registerReceiver(new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
// When discovery finds a device
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
// Get the BluetoothDevice object from the Intent
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
if(isWiiMote(device)){
mBluetoothAdapter.cancelDiscovery();
connectWiiMote(device);
}
}
}
}, filter);
mBluetoothAdapter.startDiscovery();
}
private boolean isWiiMote(BluetoothDevice device){
String name = device.getName();
return name.equalsIgnoreCase("Nintendo RVL-CNT-01");
}
private void connectWiiMote(final BluetoothDevice device){
final int tickInterval = 30;
WiimoteRunnable nWiiMote = new WiimoteRunnable(device, tickInterval){
@Override
protected void onButtonDown() { // TODO
}};
Thread nThread = new Thread(nWiiMote);
nThread.start();
}
private abstract class WiimoteRunnable implements Runnable{
private BluetoothSocket socket;
private long tickInterval;
private byte[] buffer = new byte[1024];
WiimoteRunnable(BluetoothDevice device, long tickInterval) {
socket = createL2CAPBluetoothSocket(device);
this.tickInterval = tickInterval;
}
@SuppressLint("NewApi")
@Override
public void run() {
try {
if(!socket.isConnected()){
// blocks here!
socket.connect();
}
InputStream iStream = socket.getInputStream();
while(socket.isConnected()){
iStream.read(buffer);
// do stuff with byte buffer here
Thread.sleep(tickInterval);
}
} catch (IOException e2) {
closeSocket();
} catch (InterruptedException e) {
closeSocket();
return;
}
}
private void closeSocket(){
try {
socket.close();
} catch (IOException e) {
return;
}
}
// to be implemented later
protected abstract void onButtonDown();
}
// see https://stackoverflow.com/questions/14761570/connecting-to-a-bluetooth-hid-device-mouse-using-l2cap
private static final int TYPE_L2CAP = 3;
/**
* Create a BluetoothSocket using L2CAP protocol
* Useful for HID Bluetooth devices
* @param BluetoothDevice
* @return BluetoothSocket
*/
@SuppressLint("NewApi")
private static BluetoothSocket createL2CAPBluetoothSocket(BluetoothDevice device){
int type = TYPE_L2CAP; // L2CAP protocol
int fd = -1; // Create a new socket
boolean auth = false; // No authentication
boolean encrypt = false; // Not encrypted
int port = 0; // port to use (useless if UUID is given)
ParcelUuid[] uuid = device.getUuids(); // Bluetooth UUID service
if(uuid==null){
if(!device.fetchUuidsWithSdp()){
return null;
} else {
uuid = device.getUuids();
}
}
try {
Constructor<BluetoothSocket> constructor = BluetoothSocket.class.getDeclaredConstructor(
int.class, int.class, boolean.class, boolean.class,
BluetoothDevice.class, int.class, ParcelUuid.class);
constructor.setAccessible(true);
BluetoothSocket clientSocket = (BluetoothSocket) constructor.newInstance(
type, fd, auth, encrypt, device, port, uuid[0]);
return clientSocket;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
The problem is when I get to socket.connect()
it blocks and never returns. The socket object is there and in the debugger I can see all the data for it.
I'm getting the socket as per Connecting to a bluetooth HID device (mouse) using L2CAP
Instead of using the UUID for the L2CAP socket you need to use the channel number (or port number in your code). To connect to wiimote you need to open sockets separately for control (commands from your app to wiimote) and data (input from wiimote to your app) channels.
private static final int CONTROL_CHANNEL = 0x11;
private static final int DATA_CHANNEL = 0x13;
private BluetoothSocket controlSocket;
private BluetoothSocket dataSocket;
private OutputStream os;
private InputStream is;
private static BluetoothSocket createL2CAPBluetoothSocket(BluetoothDevice device, final int channel) {
int type = TYPE_L2CAP; // L2CAP protocol
int fd = -1; // Create a new socket
boolean auth = false; // No authentication
boolean encrypt = false; // Not encrypted
try {
Constructor<BluetoothSocket> constructor = BluetoothSocket.class.getDeclaredConstructor(int.class,
int.class, boolean.class, boolean.class, BluetoothDevice.class, int.class, ParcelUuid.class);
constructor.setAccessible(true);
BluetoothSocket clientSocket = (BluetoothSocket) constructor.newInstance(type, fd, auth, encrypt, device,
channel, null);
return clientSocket;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
void connect(BluetoothDevice device) {
try {
controlSocket = createL2CAPBluetoothSocket(device, CONTROL_CHANNEL);
controlSocket.connect();
os = controlSocket.getOutputStream();
dataSocket = createL2CAPBluetoothSocket(device, DATA_CHANNEL);
dataSocket.connect();
is = dataSocket.getInputStream();
// open transmit & receive threads for input and output streams appropriately
} catch (Exception e) {
e.printStackTrace();
}
}