bluetooth read thread will not exit when bluetooth connection lost in c - android

I am writing a "race box" in C that runs on an Intel Edison. It can send or receive bluetooth data with an Android app based on Bluetooth Chat. Everything works fine. I can send and receive. I can lose the connection and reacquire and the connection comes back and data flows from the C program to the Android app. All is good except once I reconnect I can no longer send data from C to Android. I have traced this to the read statement and I assume it never returns a value because it never exits the while loop. Specifically, this line:
while(bluetooth_up == 1 && (read(client, &aa, 1) == -1) ){
Will not exit even when I know bluetooth_up == 0 (another thread fprints bluetooth_up and it is 0 when bluetooth is down). My conclusion was that read was blocking so I attempted to fix that with the line
fcntl(s, F_SETFL,sock_flags|O_NONBLOCK);
The bluetooth connection is in the bluetooth write thread. But like I said, everything works except that this while loop will not exit when bluetooth_up is 0.
All I can figure out is that the read is blocking and what I cannot figure out is how to make it not blocking so it will return the -1 and the while loop can see that bluetooth_up == 0 and exit.
Here is the global definition of bluetooth_up
volatile int bluetooth_up = 0;
I would appreciate help on this as it needs to be robust and I don't want to require people to power cycle the race box to get bluetooth working again, although that does work.
void *blueRead(void *arg){
blue_read_up = 1;
char aa;
char buffer[500];
int idx = 0;
printf("\nBlue Read started\n");
fcntl(s, F_SETFL,sock_flags|O_NONBLOCK);
while(bluetooth_up == 1){
if (new_blue_read_sentence == 0 && bluetooth_up == 1){
while(bluetooth_up == 1 && (read(client, &aa, 1) == -1) ){
if(bluetooth_up == 0){
printf("\nExiting blue read\n");
blue_read_up = 0;
return NULL;
}
}
printf("%i",aa);
if(aa != '\n')
{
buffer[idx++] = aa;
}
else
{
buffer[idx] = '\n';
buffer[idx + 1] = '\0';
idx = 0;
strcpy( blue_read_buffer, buffer);
new_blue_read_sentence = 1;
}
}
}
printf("\nExiting blue read 2\n");
blue_read_up = 0;
return NULL;
}
I think the bluetooth connection code is pretty standard but here it is
// allocate socket
s = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
printf("\nsocket = %i\n", s);
// bind socket to port 1 of the first available local bluetooth adapter
loc_addr.rc_family = AF_BLUETOOTH;
loc_addr.rc_bdaddr = *BDADDR_ANY;
loc_addr.rc_channel = (uint8_t)1;
retval = bind(s, (struct sockaddr*)&loc_addr, sizeof(loc_addr));
printf("\nbind = %i\n", retval);
// put socket into listening mode
//listen(s, 1);
retval = listen(s, 1);
printf("\nlisten = %i\n", retval);
// accept one connection
client = accept(s, (struct sockaddr*)&rem_addr, &opt);
sock_flags = fcntl(s,F_GETFL,0);
fcntl(s, F_SETFL,sock_flags|O_NONBLOCK);
printf("\n1 - client connect = %i socket %i\n", client, s);

You are setting O_NONBLOCK on the listening socket (s). Try instead on the accept socket (client) which is the one that is actually being read from.

Related

Send data from Arduino to Android not working

We are doing a project where we move a servo motor to open and close a lock with Arduino and Bluetooth HC-06. We are trying to send one number (1 or 2) as the state of the servo motor before doing anything, as soon as we connect our app to the Bluetooth HC-06 just to know if the lock is already open or close (we are using EEPROM to not lose the last position of the servo). However, with our code we receive the data but the servo stop working well. If we delete the first two lines from the loop, it will work but we will not know the initial state of the servo. How can we solve this?
char state;
Servo myservo;
int btx=3;
int brx=2;
SoftwareSerial blue(btx,brx);
int pos = 0;
void setup() {
Serial.begin(9600);
myservo.attach(9);
blue.begin(9600);
// An EEPROM value of 1 == UNLOCKED and a value of 2 == LOCKED
if(EEPROM.read(0) == 1){ //Lock opened
myservo.write(70);
delay(200);
pos = 1;
//blue.println(1); not working
}
else if(EEPROM.read(0) == 2){ //Lock closed
myservo.write(180);
delay(200);
pos = 2;
//blue.println(2); not working
}
//blue.println(pos); not working
}
void loop() {
while(!blue.available()){ // <-- The problem is in this two lines
blue.println(pos); // send state to app
}
String voice;
while(blue.available()){
delay(10);
char c = blue.read();
if (c == '#'){
break;
}
voice += c;
}
if(voice.length() > 0){
if (voice == "open"){
myservo.write(70);
EEPROM.write(0, 1);
blue.println(1);
delay(15);
}
else if (voice == "close"){
myservo.write(180);
EEPROM.write(0, 2);
blue.println(2);
delay(15);
}
}
}
Because those two lines are an infinite loop! so you should change your code to this and give your code a chance to continue :)
void loop() {
if(!blue.available()){
blue.println(pos);
}else if(blue.available()){
String voice;
while(blue.available()){
delay(10);
char c = blue.read();
if (c == '#'){
break;
}
voice += c;
if(voice.length() > 0){
if (voice == "open"){
myservo.write(70);
EEPROM.write(0, 1);
blue.println(1);
delay(15);
}
else if (voice == "close"){
myservo.write(180);
EEPROM.write(0, 2);
blue.println(2);
delay(15);
}
}
}
}
}

Sending hex values through a BluetoothLE connection

I am a novice in android development, so please forgive if the descriptions are not totally correct.
I am using the Android BTLE example app as a template and am attempting to send simple hex values to a Microchip RN4020 module. I can successfully send data (specifically time data using the calendar function) and they appear on my PuTTY window. However the problem is that the data is being 'transmitted' from the application (on my Samsung j5) as an ASCII representation.... in other words, the Calendar function returns the minute as say 20 (20hex = 32 decimal), which is somehow ending up as ASCII 20 on my PuTTY terminal (i.e. 32 30).
I am sending the data as a Byte array, which as I understand it constrains the range of values to be -127 to +127. So to combat this I have attempted to break the value into upper and lower nibbles and send these as separate bytes.....same problem. The value appearing is the ASCII 2 0....(i.e. 32 30).
I have taken the liberty of posting below my code (apologies for the 'clunkiness' of it). I strongly suspect it is a really rudimentary error, but I cant find how to transmit the 'hex values' Please help
regards
J
public void onClickTest(View v){
if(mBluetoothLeService != null) {
Calendar calendar = Calendar.getInstance();
//Data_1[0] = (Integer.toHexString((byte)calendar.get(Calendar.SECOND) & 0xFF)); //Alarm Set
int temp = 0;
int temp1 = 0;
byte first = 0;
byte second = 0;
temp = calendar.get(Calendar.HOUR_OF_DAY);
temp1 = temp;
temp = temp & 0xf0;
temp = temp >> 4;
temp1 = temp1 & 0x0f;
first = (byte)temp;
second = (byte)temp1;
Data_1[0] = first;
Data_1[1] = second; //Alarm Set
//Data_1[1] = 0xff; //Update clock
// Data_1[2] = calendar.get(Calendar.SECOND);
// Data_1[2] = calendar.get(Calendar.SECOND);
// Data_1[3] = (char)calendar.get(Calendar.MINUTE);
// Data_1[4] = (char)calendar.get(Calendar.HOUR_OF_DAY);
// Data_1[5] = (byte)calendar.get(Calendar.DAY_OF_WEEK);
// Data_1[6] = (byte)calendar.get(Calendar.MONTH);
// Data_1[7] = (byte)calendar.get(Calendar.YEAR);
//myArray = (Arrays.toString(Data_1));
mBluetoothLeService.writeCustomCharacteristicString(Data_1);
This is the actual LE service bit
public void writeCustomCharacteristicString(byte[] value) {
if (mBluetoothAdapter == null || mBluetoothGatt == null) {
Log.w(TAG, "BluetoothAdapter not initialized");
return;
}
/*check if the service is available on the device*/
BluetoothGattService mCustomService1 = mBluetoothGatt.getService(UUID.fromString("12345678-9012-3456-7890-1234567890FF"));
if(mCustomService1 == null){
Log.w(TAG, "Custom BLE Service not found");
return;
}
/*get the read characteristic from the service*/
BluetoothGattCharacteristic mWriteCharacteristic1 = mCustomService1.getCharacteristic(UUID.fromString("12345678-9012-3456-7890-123456789011"));
mWriteCharacteristic1.setValue(value);
if(mBluetoothGatt.writeCharacteristic(mWriteCharacteristic1) == false){
Log.w(TAG, "Failed to write characteristic");
}
}

arduino to android bluetooth: detecting if both are connected

The Goal:
To have the ardiuno check if it's connected to the android with bluetooth. Then to perform an act if it is connected or reconnect if it's not connected.
What I am using:
Bluesmirf silver with arduino uno and note 3
What I've done so far:
[ARDUINO CODE]
The Bluesmirf is in master mode auto connect. The arduino is supposed to check if the android app is sending an H character. If it is that means its connected. If not then it needs to keep re-connecting.
#include <SoftwareSerial.h>
#include <TextFinder.h>
int bluetoothTx = 2; // TX-O pin of bluetooth mate, Arduino D2
int bluetoothRx = 3; // RX-I pin of bluetooth mate, Arduino D3
boolean running = false;
SoftwareSerial bluetooth(bluetoothTx, bluetoothRx);
void setup()
{
Serial.begin(9600); // Begin the serial monitor at 9600bps
bluetooth.begin(115200); // The Bluetooth Mate defaults to 115200bps
bluetooth.print("$"); // Print three times individually
bluetooth.print("$");
bluetooth.print("$"); // Enter command mode
delay(100); // Short delay, wait for the Mate to send back CMD
bluetooth.println("U,9600,N"); // Temporarily Change the baudrate to 9600, no parity
delay(100);
bluetooth.begin(9600); // Start bluetooth serial at 9600
}
void loop()
{
//Check If Connected
if(bluetooth.available()) // If the bluetooth sent any characters
{
//Check if bluetooth recieved an H and store in a value
char val = bluetooth.read();
if(val == 'H')
{
running = true;
}
else if(val != 'H')
{
running = false;
}
}
else if(!bluetooth.available())
{
running = false;
}
//Actions to perform if arduino is connected or not connected
if(running == true)
{
//It's connected so wait 5 seconds
delay(5000);
}
else if(running == false)
{
//It's not connected: Attempt to reconnect
bluetooth.print("$"); // Print three times individually
bluetooth.print("$");
bluetooth.print("$"); // Enter command mode
delay(100); // Short delay, wait for the Mate to send back CMD
bluetooth.println("C,30196692D7C0");
delay(500);
bluetooth.println("---");
delay(3000);
}
}
[ANDROID CODE]
And this is the method of the android app that sends an H once the app is connected.
private void sendMessage(BluetoothSocket socket, String msg) {
OutputStream outStream;
try {
outStream = socket.getOutputStream();
byte[] byteString = (msg).getBytes();
outStream.write(byteString);
} catch (IOException e) {
Log.d("BLUETOOTH_COMMS", e.getMessage());
}
}
Side Note:
I've tried so many things to get this arduino to check if its connected or not. I only just started programming 3 weeks ago so this is becoming increasingly difficult. Any help would be appreciated.
[UPDATE #1]
I've managed to send an 'h' with the android app with this snippet here:
//call send method to send this character over bluetooth
sendMessage(socket,"h");
//Method used to send 'h' over bluetooth
private void sendMessage(BluetoothSocket socket, String msg) {
OutputStream outStream;
try {
outStream = socket.getOutputStream();
//byte[] byteString = (msg).getBytes();
byte[] byteString = stringToBytesUTFCustom(msg);
outStream.write(byteString);
} catch (IOException e) {
Log.d("BLUETOOTH_COMMS", e.getMessage());
}
}
//Method used to convert
public byte[] stringToBytesUTFCustom(String str) {
char[] buffer = str.toCharArray();
byte[] b = new byte[buffer.length << 1];
for (int i = 0; i < buffer.length; i++) {
int bpos = i << 1;
b[bpos] = (byte) ((buffer[i]&0xFF00)>>8);
b[bpos + 1] = (byte) (buffer[i]&0x00FF);
}
return b;
}
And with arduino I can properly read the 'h' using this snippet.
if (bluetooth.available() > 0) { // if the data came
char incomingByte = bluetooth.read(); // read byte
if(incomingByte == 'h') {
running = true;
}
}
New Problem
I am having trouble telling when the arduino has lost connection with the android app.

ADK: device gets stuck when reading from the ADK

I have problems when I when to send messages via USB from the board to the devicersa.
Hardware:
Arduino ADK 2011
Samsung Galaxy S3, Android 4.1.2
The problem is the read method in the Android app never terminates and makes the thread get stuck:
mFileDescriptor = mUsbManager.openAccessory(accessory);
if (mFileDescriptor != null) {
mAccessory = accessory;
FileDescriptor fd = mFileDescriptor.getFileDescriptor();
Log.d(TAG, "openAccessory(): FileDescriptor instanciated. valid " + fd.valid());
mInputStream = new FileInputStream(fd);
mOutputStream = new FileOutputStream(fd);
new Thread(null, new Runnable() {
#Override
public void run() {
int ret = 0;
byte[] buffer = new byte[255];
while (ret >= 0 && mInputStream != null) {
try {
// never terminates
ret = mInputStream.read(buffer);
} catch (IOException e) {
Log.e(TAG, "openAccessory(): Could not read inputStream: " + e);
e.printStackTrace();
break;
}
} ...
The connection works fine since I use the special USB-library. When I connect the device the app opens automatically very well. But with logs I see it never passes the read command. Also the Arduinio monitor says that:
Device addressed... Requesting device descriptor.
found possible device. swithcing to serial mode
device supports protcol 1 or above
found android acessory device
config desc
interface desc
interface desc
2
4
Sending message...
Done
disconnect
The ADK sends messages, to the device in the loop (once):
sntmsg[0] = COMMAND_TEXT;
sntmsg[1] = TARGET_DEFAULT;
sntmsg[2] = 25;
for (int i = 0; i < 25; i++) {
sntmsg[3 + i] = hello[i];
}
// schreiben (buffer, length)
Serial.println("Sending message...");
acc.write(sntmsg, 3 + 25);
Serial.println("Done");
done = true;
delay(250);
Now I figured out the problem might be the disconnect. Immedeately after running through the first loop in the Arduiino code it prints disconnect to the monitor. The code in the libraries of AndroidAccessory.cpp is:
bool AndroidAccessory::isConnected(void)
{
USB_DEVICE_DESCRIPTOR *devDesc = (USB_DEVICE_DESCRIPTOR *) descBuff;
byte err;
max.Task();
usb.Task();
if (!connected &&
usb.getUsbTaskState() >= USB_STATE_CONFIGURING &&
usb.getUsbTaskState() != USB_STATE_RUNNING) {
Serial.print("\nDevice addressed... ");
Serial.print("Requesting device descriptor.\n");
err = usb.getDevDescr(1, 0, 0x12, (char *) devDesc);
if (err) {
Serial.print("\nDevice descriptor cannot be retrieved. Trying again\n");
return false;
}
if (isAccessoryDevice(devDesc)) {
Serial.print("found android acessory device\n");
connected = configureAndroid();
} else {
Serial.print("found possible device. swithcing to serial mode\n");
switchDevice(1);
}
} else if (usb.getUsbTaskState() == USB_DETACHED_SUBSTATE_WAIT_FOR_DEVICE) {
if (connected)
Serial.println("disconnect\n");
connected = false;
}
return connected;
}
So in the second loop this method returns false, even though the smartphone is still connected via usb. Do you know why it thinks it is dosconnected after the first loop iteration?
Thanks,
FL

Using Android to Communicate with a USB HID Device

I am new to USB and to Android so please forgive me if I don't explain myself clearly.
I have a USB HID device that I can communicate with in Windows. I am trying to establish communication using an Acer Iconia A500 tablet running Android 3.1.
I am able to find the device, enumerate it, get its only available interface, get the only available endpoint (0), and determine what type of endpoint it is (transfer interrupt from device to host).
My understanding of the USB spec is that all HID devices are required at a munimum to have a control endpoint (Endpoint 0) and an interrupt IN endpoint. But it seems that endpoint 0 here is the interrupt In endpoint, not the control endpoint.
Yet in order for the device to enumerate it must successfully transfer its descriptor data across the control endpoint. I deduce that the control endpoint therefore must be getting found (and used) because the host does, in fact, enumerate the device.
This is as far as I am able to proceed, as stated above, the only interface/endpoint presented to me at the application level is an interrupt type going from device to host. No endpoint available to my app going from host to device, interrupt or control. So the device waits to be told what to do and the host waits for something to happen in the device. Not very stimulating.
Bear in mind that this device responds properly when connected to Windows, e.g. I am able to send a report containing 13 bytes of data that causes the device to light an LED. So it seems to be complying with the USB HID spec. As an act of desperation I have tried using this one endpoint as both a control endpoint and as a interrupt OUT endpoint, using controltransfer() and UsbRequest() to submit the data to the device, no response in either case.
So my question is: "The control transfer endpoint is (?) being used to set up the device, why am I not able to find & use it?"
Thanks for any insight, below is the relevant code, I can include the rest in its entirety if needed:
private UsbManager mUsbManager;
private UsbDevice mDevice;
private UsbDeviceConnection mConnectionRead;
private UsbDeviceConnection mConnectionWrite;
private UsbEndpoint mEndpointRead;
private UsbEndpoint mEndpointWrite;
// check for existing devices
for (UsbDevice device : mUsbManager.getDeviceList().values())
{
//Need to filter for my device when other HIDs are also connected, but for now...
String devName = device.getDeviceName();
if (DEBUG == 1){
Toast.makeText(UsbHidDeviceTesterActivity.this, "My device got connected: " + devName, Toast.LENGTH_LONG).show();
}
//mDevice = device;
setHIDDevice(device);
}
private boolean setHIDDevice(UsbDevice device)
{
UsbInterface usbInterfaceRead = null;
UsbInterface usbInterfaceWrite = null;
UsbEndpoint ep1 = null;
UsbEndpoint ep2 = null;
boolean UsingSingleInterface = true;
mDevice = device;
//This HID device is using a single interface
if (UsingSingleInterface)
{
//usbInterfaceRead = device.getInterface(0x00);//only 1 EP on this interface
usbInterfaceRead = findInterface(device);
//Try getting an interface at next index
//usbInterfaceWrite = device.getInterface(0x01);//throws exception
// Try using the same interface for reading and writing
usbInterfaceWrite = usbInterfaceRead;
int endPointCount = usbInterfaceWrite.getEndpointCount();
if (DEBUG == 2)
{
Toast.makeText(UsbHidDeviceTesterActivity.this, "Endpoints: " + endPointCount, Toast.LENGTH_LONG).show();
//Toast.makeText(UsbHidDeviceTesterActivity.this, "Interface: " + usbInterfaceRead, Toast.LENGTH_LONG).show();
}
if (endPointCount == 1)//only getting 1 endpoint
{
ep1 = usbInterfaceRead.getEndpoint(0);
//As an act of desperation try equating ep2 to this read EP, so that we can later attempt to write to it anyway
ep2 = usbInterfaceRead.getEndpoint(0);
}
else if (endPointCount == 2)
{
ep1 = usbInterfaceRead.getEndpoint(0);
ep2 = usbInterfaceRead.getEndpoint(1);
}
}
else // ! UsingSingleInterface
{
usbInterfaceRead = device.getInterface(0x00);
usbInterfaceWrite = device.getInterface(0x01);
if ((usbInterfaceRead.getEndpointCount() == 1) && (usbInterfaceWrite.getEndpointCount() == 1))
{
ep1 = usbInterfaceRead.getEndpoint(0);
ep2 = usbInterfaceWrite.getEndpoint(0);
}
if (DEBUG == 3)
{
Toast.makeText(UsbHidDeviceTesterActivity.this, "Using Dual Interface", Toast.LENGTH_LONG).show();
}
}
//because ep1 = ep2 this will now not cause a return unless no ep is found at all
if ((ep1 == null) || (ep2 == null))
{
if (DEBUG == 4)
{
Toast.makeText(UsbHidDeviceTesterActivity.this, "One EP is null", Toast.LENGTH_LONG).show();
}
return false;
}
// Determine which endpoint is the read, and which is the write
if (ep1.getType() == UsbConstants.USB_ENDPOINT_XFER_INT)//I am getting a return of 3, which is an interrupt transfer
{
if (ep1.getDirection() == UsbConstants.USB_DIR_IN)//I am getting a return of 128, which is a device-to-host endpoint
{
mEndpointRead = ep1;
if (DEBUG == 5)
{
Toast.makeText(UsbHidDeviceTesterActivity.this, "EP1 type: " + ep1.getType(), Toast.LENGTH_LONG).show();
}
}
if (ep1.getDirection() == UsbConstants.USB_DIR_OUT)//nope
{
mEndpointWrite = ep1;
if (DEBUG == 6)
{
Toast.makeText(UsbHidDeviceTesterActivity.this, "EP1 is a write", Toast.LENGTH_LONG).show();
}
}
}
if (ep2.getType() == UsbConstants.USB_ENDPOINT_XFER_INT)
{
if (ep2.getDirection() == UsbConstants.USB_DIR_IN)
{
//Try treating it as a write anyway
//mEndpointRead = ep2;
mEndpointWrite = ep2;
}
else if (ep2.getDirection() == UsbConstants.USB_DIR_OUT)
{
//usbEndpointWrite = ep2;
mEndpointWrite = ep2;
}
}
//check that we should be able to read and write
if ((mEndpointRead == null) || (mEndpointWrite == null))
{
return false;
}
if (device != null)
{
UsbDeviceConnection connection = mUsbManager.openDevice(device);
if (connection != null && connection.claimInterface(usbInterfaceRead, true))
{
Log.d(TAG, "open SUCCESS");
mConnectionRead = connection;
// Start the read thread
//Comment out while desperately attempting to write on this connection/interface
//Thread thread = new Thread(this);
//thread.start();
}
else
{
Log.d(TAG, "open FAIL");
mConnectionRead = null;
}
}
if (UsingSingleInterface)
{
mConnectionWrite = mConnectionRead;
}
else //! UsingSingleInterface
{
mConnectionWrite = mUsbManager.openDevice(device);
mConnectionWrite.claimInterface(usbInterfaceWrite, true);
}
return true;
}
// searches for an interface on the given USB device
private UsbInterface findInterface(UsbDevice device) {
Log.d(TAG, "findInterface " + device);
int count = device.getInterfaceCount();
if (DEBUG == 7)
{
Toast.makeText(UsbHidDeviceTesterActivity.this, "Interface count: " + count, Toast.LENGTH_LONG).show();
}
for (int i = 0; i < count; i++) {
UsbInterface intf = device.getInterface(i);
String InterfaceInfo = intf.toString();
Log.d(TAG, "Interface: " + InterfaceInfo);
//Class below is 3 for USB_HID
if (intf.getInterfaceClass() == 3 && intf.getInterfaceSubclass() == 0 &&
intf.getInterfaceProtocol() == 0) {
return intf;
}
//....try just returning the interface regardless of class/subclass
//return intf;
}
return null;
}
private boolean sendControlTransfer(byte[] dataToSend)
{
synchronized (this)
{
if (mConnectionRead != null)
{
//byte[] message = new byte[13]; // or 14?
byte[] message = dataToSend;
if (DEBUG == 9)
{
Toast.makeText(UsbHidDeviceTesterActivity.this, "Sending Control Transfer", Toast.LENGTH_LONG).show();
}
//first field ox21 is bin 00100001 which splits into 0 01 00001 for direction(1bit)/type(2b)/recipient(5b)
//To set direction as 'host to Device' we need 0, To set type to HID we need 11 (3), and for recipient we want 00001
//second field 0x09 is class specific request code, 0x09 is listed as 'reserved for future use'
//third field 0x200 is value
//int transfer = mConnectionRead.controlTransfer(0x21, 0x9, 0x200, 0, message, message.length, 0);
//try with type set to HID
int transfer = mConnectionRead.controlTransfer(0xC1, 0x9, 0x200, 0, message, message.length, 0);
if (DEBUG == 10)
{
Toast.makeText(UsbHidDeviceTesterActivity.this, "Transfer returned " + transfer, Toast.LENGTH_LONG).show();
}
}
}
return true;
}
private boolean sendInterruptTransfer(byte[] dataToSend)
{
int bufferDataLength = mEndpointWrite.getMaxPacketSize();//The write endpoint is null unless we just copy the read endpoint
if (DEBUG == 12)
{
Toast.makeText(UsbHidDeviceTesterActivity.this, "Max Packet Size: " + bufferDataLength, Toast.LENGTH_LONG).show();
}
ByteBuffer buffer = ByteBuffer.allocate(bufferDataLength + 1);
UsbRequest request = new UsbRequest();
buffer.put(dataToSend);
request.initialize(mConnectionWrite, mEndpointWrite);
request.queue(buffer, bufferDataLength);
try
{
/* only use requestwait on a read
if (request.equals(mConnectionWrite.requestWait()))
{
return true;
}
*/
}
catch (Exception ex)
{
// An exception has occurred
if (DEBUG == 13)
{
Toast.makeText(UsbHidDeviceTesterActivity.this, "Caught Write Exception", Toast.LENGTH_LONG).show();
}
}
return true;
}
So, I have been researching similar things. I cannot confirm, but what I believe is happening is:
Android does not list the control endpoint when it enumerates it's endpoints. It only lists other endpoints.
A connection to any endpoint can send control transfers to endpoint 0, through the controlTransfer method, which (quoting from the api) "Performs a control transaction on endpoint zero for this device."
So, in your above code, I would use the 0th endpoint as an interrupt input endpoint, but it will still allow for control transfers.
An example of someone using a HID device is the Missle Launcher demo, the device it uses is a HID device with an interrupt endpoint.
You can get a full list of the details of interfaces and endpoint by using the following:
UsbManager mManager = (UsbManager) getSystemService(Context.USB_SERVICE);
HashMap<String, UsbDevice> deviceList = mManager.getDeviceList();
Iterator<UsbDevice> deviceIterator = deviceList.values().iterator();
while (deviceIterator.hasNext())
{
UsbDevice device = deviceIterator.next();
Log.i(TAG,"Model: " + device.getDeviceName());
Log.i(TAG,"ID: " + device.getDeviceId());
Log.i(TAG,"Class: " + device.getDeviceClass());
Log.i(TAG,"Protocol: " + device.getDeviceProtocol());
Log.i(TAG,"Vendor ID " + device.getVendorId());
Log.i(TAG,"Product ID: " + device.getProductId());
Log.i(TAG,"Interface count: " + device.getInterfaceCount());
Log.i(TAG,"---------------------------------------");
// Get interface details
for (int index = 0; index < device.getInterfaceCount(); index++)
{
UsbInterface mUsbInterface = device.getInterface(index);
Log.i(TAG," ***** *****");
Log.i(TAG," Interface index: " + index);
Log.i(TAG," Interface ID: " + mUsbInterface.getId());
Log.i(TAG," Inteface class: " + mUsbInterface.getInterfaceClass());
Log.i(TAG," Interface protocol: " + mUsbInterface.getInterfaceProtocol());
Log.i(TAG," Endpoint count: " + mUsbInterface.getEndpointCount());
// Get endpoint details
for (int epi = 0; epi < mUsbInterface.getEndpointCount(); epi++)
{
UsbEndpoint mEndpoint = mUsbInterface.getEndpoint(epi);
Log.i(TAG," ++++ ++++ ++++");
Log.i(TAG," Endpoint index: " + epi);
Log.i(TAG," Attributes: " + mEndpoint.getAttributes());
Log.i(TAG," Direction: " + mEndpoint.getDirection());
Log.i(TAG," Number: " + mEndpoint.getEndpointNumber());
Log.i(TAG," Interval: " + mEndpoint.getInterval());
Log.i(TAG," Packet size: " + mEndpoint.getMaxPacketSize());
Log.i(TAG," Type: " + mEndpoint.getType());
}
}
}
Log.i(TAG," No more devices connected.");
}
Control transfer doesn't show any interface descriptor and its endpoint number is 0 by default, for both in and out transfer.
if you have other interfaces the index of those interfaces should start from 0 i.e. default control transfer interface does not count.
So your interface 0 holds the endpoint 1 descriptor. use the UsbEndpoint methods to find the attributes of the endpoint whether it is interrupt type or not. if it is then endpoint type by UsbEndpoint.getType() should return 0x03 and endpoint number by UsbEndpoint.getEndpointNumber() should return 0x81 which is usual value for endpoint 1.
below your code is wrong:
//first field ox21 is bin 00100001 which splits into 0 01 00001 for direction(1bit)/type(2b)/recipient(5b)
//To set direction as 'host to Device' we need 0, **To set type to HID we need 11 (3)**, and for recipient we want 00001
//second field 0x09 is class specific request code, **0x09 is listed as 'reserved for future use'**
//**third field 0x200 is value**
//int transfer = mConnectionRead.controlTransfer(0x21, 0x9, 0x200, 0, message, message.length, 0);
//try with type set to HID
int transfer = mConnectionRead.controlTransfer(0xC1, 0x9, 0x200, 0, message, message.length, 0);
Type 2 bits is used to indicate class specific request, i.e. its value is 01,
0x09 is Hid class specific request SET_REPORT, not reserved.
value is the wValue which is used as Report ID for Hid class, for your case it is probably 0, if you have only one report at you HID descriptor.
and the 4 th parameter is wIndex which should be used to indicate the recipient, for your case it should be 0x01 for interface as recipient.
So your code for control transfer for Read or receive data form device should be:
int transfer = mConnectionRead.controlTransfer(0xA1, 0x01, 0x00, 0x01, message, message.length, 0);
where 0x01 in second parameter is GET_REPORT is Hid calls specific request.
And your code for control transfer for Write or send data to device should be:
int transfer = mConnectionWrite.controlTransfer(0x21, 0x09, 0x00, 0x01, message, message.length, 0);
Since you only have Interrupt IN endpoint 1, Bulk or Interrupt transfer should be like:
int transfer = bulkTransfer (ep1, message, message.length, 0);
to have the interrupt Out endpoint there should be a endpoint descriptor for that at the interface descriptor of firmware of your device.
Maybe it is a late answer or off topic. However, I hope someone will one day find this answer useful.
Github now contains great Android libraries to communicate with custom HID devices:
Mine in Kotlin
in Java
The good thing is that if you are lucky, then you only need to know the device VID:PID and the commands it accepts. You don't need to worry about USB protocol or communication details.
If interested about how USB works, then you can have a closer look at library source code.

Categories

Resources