Search code examples
androidusb

Control relay over USB from Android


I have this USB Relay and I'd like to control from an Android phone.

(There is a similar post here but it explains how do it from a Linux shell. By looking at that code i figured i'd be able to solve it - apparently not.)

The device lists in lsusb:


Bus 002 Device 011: ID 16c0:05df VOTI 
Device Descriptor:
  bLength                18
  bDescriptorType         1
  bcdUSB               1.10
  bDeviceClass            0 (Defined at Interface level)
  bDeviceSubClass         0 
  bDeviceProtocol         0 
  bMaxPacketSize0         8
  idVendor           0x16c0 VOTI
  idProduct          0x05df 
  bcdDevice            1.00
  iManufacturer           1 
  iProduct                2 
  iSerial                 0 
  bNumConfigurations      1
  Configuration Descriptor:
    bLength                 9
    bDescriptorType         2
    wTotalLength           34
    bNumInterfaces          1
    bConfigurationValue     1
    iConfiguration          0 
    bmAttributes         0x80
      (Bus Powered)
    MaxPower               20mA
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        0
      bAlternateSetting       0
      bNumEndpoints           1
      bInterfaceClass         3 Human Interface Device
      bInterfaceSubClass      0 No Subclass
      bInterfaceProtocol      0 None
      iInterface              0 
        HID Device Descriptor:
          bLength                 9
          bDescriptorType        33
          bcdHID               1.01
          bCountryCode            0 Not supported
          bNumDescriptors         1
          bDescriptorType        34 Report
          wDescriptorLength      22
         Report Descriptors: 
           ** UNAVAILABLE **
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x81  EP 1 IN
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0008  1x 8 bytes
        bInterval              20

I find it strange that the end point is defined as "EP IN". In my mind it should be "EP OUT" since the direction should be "host->device".

Anyway, I use the Android USB manager to create a connection and then initialize the USbRequest for an interrupt based end point. Android permissions and all that is handled so the device is connected successfully.

Snippet for sending data. It runs in a separate thread following the Android guidelines:

UsbRequest request = new UsbRequest();
synchronized (mUsbLock) {
    if (mUsbConnection != null && mUsbEndPointIn != null) {
        if (!request.initialize(mUsbConnection, mUsbEndPointIn)) {
            Log.e(TAG, "Unable to initialize UsbRequest. Thread exits");
            return;
        } else {
            if (DEBUG) {
                Log.d(TAG, String.format("Usb request is initialized"));
            }
        }
    } else {
        Log.e(TAG, "Usb communication is not up and running. The worker thread should never be started.");
        return;
    }
}
mRunning.set(true);
while (mRunning.get()) {
    if (DEBUG) {
        Log.d(TAG, String.format("Waiting for data to be sent to end point"));
    }
    WorkPackage wp = mWorkQueue.take();
    // send the package.
    byte[] data = wp.getData();
    if (!request.queue(ByteBuffer.wrap(data), data.length)) {
        Log.e(TAG, "Unable to queue to send data on UsbRequest.");
        continue;
    } else {
        if (DEBUG) {
            Log.d(TAG, String.format("Usb request is queued on end point, ep=%s", printUsbEndpoint(mUsbEndPointIn)));
        }
    }
}

It seems everything is fine, no errors occur and the request is queued on the end point but then nothing happens at all. I don't get any message back that the request has been handled.

Since the supplier won't release the on/off commands I tried variants based on the Linux post (above). None seem to work though. Supplier only release Windows binary lib.

Sending 8 byte packages (according to max package):

public static byte[] SET_RELAY_ON = {(byte) 0xff, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00};
public static byte[] SET_RELAY_OFF = {(byte) 0xfd, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00};

Help appreciated.


Solution

  • So, i figured out how to do this. I will write the solution here in case someone else is having a similar problem.

    I had to snoop the USB traffic to understand what to actually send. It turned out that the defined interrupt end point was not used at all. So, the communication with the device was not interrupt based but instead using the control transfer type on end point 0.

    So, using the Android USB API this translates into:

    Open device (device->host direction):

    int r = mUsbConnection.controlTransfer(0xa1, 0x01, 0x0300, 0x00, buffer, buffer.length, 500);
    

    Open relay '1' (host->device direction).

    byte[] buffer = {(byte) 0xff, (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00};
    int r = mUsbConnection.controlTransfer(0x21, 0x09, 0x0300, 0x00, buffer, buffer.length, 500);
    

    Note, the second parameter in the buffer (0x01) is the relay number (in case you have >1 relay on the board). I only had one.

    close relay '1' (host->device direction):

    byte[] buffer = {(byte) 0xfd, (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00};
    int r = mUsbConnection.controlTransfer(0x21, 0x09, 0x0300, 0x00, buffer, buffer.length, 500);