Android Tutorial Android Usb Host Tutorial - AdbTest

Status
Not open for further replies.
Android 3.1 and above devices support Usb host mode. With this feature you can connect to regular client usb devices. Some devices are automatically recognized by the OS and are simple to work with, such as: keyboard, mouse or storage devices.

Other devices require the developer to implement the specific protocol. Depending on the device and the documentation available this can be a feasible task.

This example which is based on Google's AdbTest example, connects two Android devices with a Usb cable. The host device reads the client device logs. This is done by implementing one feature of Adb debugging, similar to the Usb debugging driver.

usbhost_3.jpg


The logs of the host device show messages from the client device:

usbhost_1.png


As the Usb host features are only available starting with Android 3.1, you should configure the IDE to use android.jar level 12 or above. It can be downloaded by choosing Tools - Run AVD manager.
You should then configure the IDE paths to point to the correct android.jar:
SS-2011-09-01_18.53.22.png

The following error means that you are using the wrong platform level:
B4X:
package android.hardware.usb does not exist

Now to the code...
The first step is to use UsbManager to find all the connected devices. We are going over all devices, looking for a device with an interface with class = 255 and subclass = 66.
Assuming that such a device is found we are also looking for the in / out endpoints.
The following code iterates over the connected devices (usually there will be one) looking for the requested values:
B4X:
Sub FindAdbDevice As Boolean
   Dim usbdevices() As UsbDevice
   usbdevices = manager.GetDevices
   
   'Iterate over devices and find the correct one
   For i = 0 To usbdevices.Length - 1
      Dim ud As UsbDevice
      ud = usbdevices(i)
      Log(ud)
      'Iterate over interfaces
      For a = 0 To ud.InterfaceCount - 1
         Dim inter As UsbInterface
         inter = ud.GetInterface(a)
         If inter.InterfaceClass = 255 AND inter.InterfaceSubclass = 66 Then
            'found our device and interface
            device = ud
            interface = inter
            'Find correct endpoints
            For b = 0 To interface.EndpointCount - 1
               Dim endpoint As UsbEndpoint
               endpoint = interface.GetEndpoint(b)
               If endpoint.Type = manager.USB_ENDPOINT_XFER_BULK Then
                  If endpoint.Direction = manager.USB_DIR_IN Then 
                     inEndpoint = endpoint
                  Else If endpoint.Direction = manager.USB_DIR_OUT Then
                     outEndpoint = endpoint
                  End If
               End If
            Next
         End If
      Next
   Next
   If device.IsInitialized = False Then Log("ADB device not found.")
End Sub

The next step is to check if we have permission to access this device and request such a permission if we don't have. The permission will only be required on the first time our application runs.
B4X:
If manager.HasPermission(device) = False Then 
         manager.RequestPermission(device)
...
RequestPermission will show a dialog asking the user to approve the request.

If we do have permission we connect to the device:
B4X:
   connection = manager.OpenDevice(device, interface, True)
   Log("Starting connection")
   Dim data(), msg() As Byte
   data = ConvertStringToBytesWith0("host::" & Chr(0))
   msg = CreateMessage(A_CNXN, 0x01000000, 4096, data)
   SendOutRequest("Msg", msg, 24)
   SendOutRequest("Data", data, Data.Length)
   SendInRequest(MSG_READ, 24)
   connection.StartListening("connection")
First we connect to the device and send to OUT requests according to the ADB protocol and one IN request that will return with the result from the device.
We then call connection.StartListening to notify the listener to listen for completed requests.

Adb protocol
In order to start a connection we need to send a pair of requests. One is the "message" and the other is the "data".
These requests are sent to the OUT endpoint as we are sending data from the host to the device.
In order to get the device response we are sending another request to the IN endpoint.
Our code will continue in Sub NewData when the IN request completes.

Note that each request can be assigned a name. This is an arbitrary string that helps us identify the requests that return in NewData event. For 'out' requests the name is not really important.

Now for the NewData event handling which is a bit more complicated:
B4X:
Sub Connection_NewData (Request As UsbRequest, InDirection As Boolean)
   If Connection.IsInitialized = False Then Return 'Might happen after we close the connection
   If InDirection = False Then 
      ReleaseRequest(Request, OutRequests)
      connection.ContinueListening
      Return 'don't handle OUT requests
   End If
   Dim sendData As Boolean
   If Request.Name = MSG_READ Then
      Dim raf As RandomAccessFile
      raf.Initialize3(Request.Buffer, True)
      Dim command As Int
      command = raf.ReadInt(0)
      If command = A_CNXN OR command = A_WRTE Then
         lastCommand = command
         SendInRequest(DATA_READ, raf.ReadInt(12)) 'read the data request
         sendData = True
      Else
         DispatchMessage(command, raf.ReadInt(4), "")
      End If
   Else If Request.Name = DATA_READ Then
      Dim s As String
      s = BytesToString(Request.Buffer, 0, Request.Buffer.Length, "UTF8")
      DispatchMessage(lastCommand,0, s)
   End If
   If sendData = False AND connection.IsInitialized Then
      SendInRequest(MSG_READ, 24)
   End If
   ReleaseRequest(Request, InRequests)
   connection.ContinueListening
End Sub
We are only interested in IN requests. The InDirection boolean parameter is useful distinguish between the requests:
B4X:
   If InDirection = False Then 
      ReleaseRequest(Request, OutRequests)
      connection.ContinueListening
      Return 'don't handle OUT requests
   End If
UsbRequest object are "heavy" objects. We are using a simple pool of objects to avoid creating new requests every time.
So the above code returns the request to the pool and then call connection.ContinueListening. This tells the listener to continue listening for requests. Without calling it we will not get the event for the IN request that we are waiting for.

If the request is an IN request things are more interesting. We check the name of the request to know if it is a "message" request or a "data" request.
We then call DispatchMessage. This sub handles the message based on the message command.

After getting an IN request there are two possible cases. The first is that we got a message request and we need to also receive the data message. The length of the data message is retrieved from the content of the message request.
In the other case we send another IN message request waiting for the next update from the device.
Eventually we release the request and call connection.ContinueListening.

RandomAccessFile object is used in several occasions to prepare the buffers according to the protocol.

B4A-Bridge
It is not possible to cannot connect the IDE to the Usb port of the host device as it is taken by the client. B4A-Bridge should be used instead: B4A-Bridge a new way to connect to your device

The project is attached.
The library is available here: http://www.b4x.com/forum/additional-libraries-official-updates/11290-usb-host-library.html#post63118
 

Attachments

  • USBExample.zip
    7.2 KB · Views: 4,039

3394509365

Active Member
Licensed User
Longtime User
thanks

are these values?
If inter.InterfaceClass = 255 AND inter.InterfaceSubclass = 66 Then

how do I know which ones are the right ones?

do not you think it might be a problem of the platform is not installed correctly?
 

walterf25

Expert
Licensed User
Longtime User
Hi Erel, I followed this example and used it as a base for some projects i'm working on, I wanted to see if you could clarify a few things for me.

I'm not to sure i understand what this functions do exactly, i'am seeing some issues where the data i'm expecting sometimes does not come in in the order they should.
The device sends 64 bytes on every packet, and the first byte is the length of the actual data to be processed or retrieved.
For example if I receive the follwing...
0x01, 0x28, 0x10, 0x53, ..............................
the first byte is 1 so it tells me to grab the first byte right after the first byte, in this case it would be 0x28.

Each readings looks like this
(g5707239706230)5B
The last two numbers are the last two numbers of the checksum value.

I then continue to listen for data and put all the bytes received into an array until i have the amount of bytes that make up one reading.
I do this until i have collected all the readings available from the device, usually 500 readings.

The functions I need to understand are the following....

B4X:
Sub SendOutRequest(Name As String, Data() As Byte, Length As Int)
    Dim request As UsbRequest
    request = GetRequest(True)
    request.Name = Name
    request.Queue(Data, Data.Length)
End Sub

Sub SendInRequest(Name As String, Length As Int)
    Dim request As UsbRequest
    request = GetRequest(False)
    request.Name = Name
    Dim Data(Length) As Byte
    request.Queue(Data, Data.Length)
End Sub

Sub GetRequest (Out As Boolean) As UsbRequest
    Dim r As UsbRequest
    Dim RequestList As List
    If Out Then RequestList = OutRequests Else RequestList = InRequests
    If RequestList.Size = 0 Then
        If Out Then
            r.Initialize(connection, outEndpoint)
        Else
            r.Initialize(connection, inEndpoint)
        End If
    Else
        r = RequestList.Get(0)
        RequestList.RemoveAt(0)
    End If
    Return r
End Sub

Sub ReleaseRequest(Request As UsbRequest, RequestList As List)
    RequestList.Add(Request)
End Sub

and the next question is this, Every time i send out a command to the device, does it need to be followed by the SendInRequest("name", 64) command?

I guess i'm still trying to get my head around how this works exactly, I have everything working fine so far, but i get a checksum error from time to time, since i calculate the checksum value and compare it to the checksum value sent along with each reading, if the values do not match then it is a checksum error and i have to restart the download all over again. When I get a checksum error I check the reading that generated the checksum error and it looks like this.

(g57072395B)706230
when it should look like this
(g5707239706230)5B
Notice that the 5B value which is the checksum value is inside the parenthesis and the 706230 is outside the parenthesis. I need to figure out why this happens.

I know you're busy with other important stuff Erel, but i need your help with this.

If it helps this the part of the code where i extract the data coming in.

B4X:
Dim finalstring1 As String
Dim newhex1 As String
Dim len1 As Int
Dim conv1 As ByteConverter
Dim model1() As Byte
hex1 = conv1.HexFromBytes(Request.Buffer)
Dim raf1 As RandomAccessFile
raf1.Initialize3(Request.Buffer, True)
len1 = unsigned(Request.Buffer(0))   'Extract the first byte to know how many bytes to process from this packet
Dim pattern0 As String = "\(([^)]+)\)([0-9A-F][0-9A-F])"  
Dim match01 As Matcher
If len1 < 64 Then
totalstring = totalstring + len1
Dim mDevice(len1) As Byte    'array to place received bytes in
raf1.ReadBytes(mDevice, 0, len1, raf1.CurrentPosition + 1)   'retrieve only the amount of bytes indicated by the first byte len1
newhex1 = conv1.HexFromBytes(mDevice)
model1 = conv1.hextobytes(newhex1)
finalstring1 = BytesToString(model1, 0, model1.Length, "ASCII")   'convert bytes to string.
complete.Append(finalstring1)  'collect string until one reading is complete.

match01 = Regex.Matcher(pattern0, complete)  'checks if a reading is found.

Thanks,
Walter
 

walterf25

Expert
Licensed User
Longtime User
Hi Erel, and thanks for the reply, the NewData code is this

B4X:
Sub Connection_NewData (Request As UsbRequest, InDirection As Boolean)
If InDirection = True AND Wakecmd = True Then
Dim finalstring1 As String
Dim newhex1 As String
Dim len1 As Int
Dim conv1 As ByteConverter
Dim model1() As Byte
hex1 = conv1.HexFromBytes(Request.Buffer)
Dim raf1 As RandomAccessFile
raf1.Initialize3(Request.Buffer, True)
len1 = unsigned(Request.Buffer(0))   'Extract the first byte to know how many bytes to process from this packet
Dim pattern0 As String = "\(([^)]+)\)([0-9A-F][0-9A-F])" 
Dim match01 As Matcher
If len1 < 64 Then
totalstring = totalstring + len1
Dim mDevice(len1) As Byte    'array to place received bytes in
raf1.ReadBytes(mDevice, 0, len1, raf1.CurrentPosition + 1)   'retrieve only the amount of bytes indicated by the first byte len1
newhex1 = conv1.HexFromBytes(mDevice)
model1 = conv1.hextobytes(newhex1)
finalstring1 = BytesToString(model1, 0, model1.Length, "ASCII")   'convert bytes to string.
complete.Append(finalstring1)  'collect string until one reading is complete.

match01 = Regex.Matcher(pattern0, complete)  'checks if a reading is found.
end if
end if
End Sub

this is just part of it, i'm attaching the service file which takes care of all the incoming data, can you please take a look at it and let me know what i'm doing wrong.

Thanks,
Walter


OutRequests and InRequests are two lists that hold unused UsbRequest objects. The idea is to reuse requests instead of creating new ones. This is an optimization.

Where is your NewData code?
 

Attachments

  • Check_USB.bas
    35.8 KB · Views: 524

walterf25

Expert
Licensed User
Longtime User
Why are you converting the bytes to a string? Do they represent a string?
Hi Erel, yes the response is actually a string, for example the response from a wake Up command is "(^*)D9" and the response from a getmodel command is "(iMAR)9A" etc....

Thanks,
Walter
 

Erel

B4X founder
Staff member
Licensed User
Longtime User
Every time i send out a command to the device, does it need to be followed by the SendInRequest("name", 64) command?
You need to send an "in" request whenever you want to fetch data from the from USB device. This is how the USB protocol works. The device cannot sent messages to the host. Only the host can send messages to the device, so everytime you want to receive something you need to first send an in request.
 

walterf25

Expert
Licensed User
Longtime User
You need to send an "in" request whenever you want to fetch data from the from USB device. This is how the USB protocol works. The device cannot sent messages to the host. Only the host can send messages to the device, so everytime you want to receive something you need to first send an in request.
Yes i understand that part, the issue i'm seeing is that sometimes if i send a wake up command, i sometimes also receive the reponse from the previous command, it seems like i need to flush the buffer or something, but don't know how to do that on the device.

Thanks,
Walter
 

JTmartins

Active Member
Licensed User
Longtime User
Correct me if I'm wrong, please

Not all phones or tablets will support USB host.

In my case I have here a android 4.1.1 phone & tablet. However nothing happens when I connect a mouse using the OTG cable.

So I've used a little app available in google play called USB Host Check, wich presented me with a couple of red crosses saying that my devices did not have USB host support enaled. The red crosses pointed to

android.hardware.usb.host.xml
handheld_core_hardware.xml
tablet_core_hardware.xml

So I believe that, without rooting, I won't be able to use anything I connect to the USB port in my devices, so it will be a waste of time to try B4A to "talk" with my USB smartcard reader using this 2 devices I have (I do not want to root this ones, at the moment)

Am I correct ?

Thanks.
 

Erel

B4X founder
Staff member
Licensed User
Longtime User
USB host is a "non-mainstream" feature in Android. Which means that not all manufacturers implemented this feature.

On many devices you can enable this feature by editing android.hardware.usb.host.xml. However it requires rooting the device.

If the mouse is not detected then your device probably doesn't support USB host mode.
 

ViMeAv ICT

Member
Licensed User
Longtime User
I now use printershare to print directly with OTG cable to usb printer.
I need to print to a thermal transfer printer (ZPL) which is not supported by printershare.
How can I use this lib to send ZPL to a usb printer by OTG cable?
 

ViMeAv ICT

Member
Licensed User
Longtime User
A few years ago I implemented many times the zpl protocol to send this serial or by copy file.txt to lpt1.
But my question is, how can I send this by usb, or is this not possible?

The guys of printershare also have a generic print solution for unknown printers,
I assume otherwise (known printer) they render the pages and send them as image over the usb.
 
Status
Not open for further replies.
Top