Shopping Cart

Posts

Arduino USB Host - Peripherals.

USB constructor

USB constructor


The time has come to start using the code to communicate to peripheral devices. There are many devices which work well with microcontrollers. HID devices, such as keyboards, joysticks, and Radio Controller look-a-likes are useful and not too difficult to implement. Another good application of embedded USB host is digital camera control. Communication using Bluetooth and WiFi peripherals are also a possibility.

Today I’m going to show how to communicate with USB keyboard. Standard USB keyboard belongs to a class of devices, called HID (Human Interface Device). The format of data generated by HID device is quite flexible and each device stores it’s data definitions in structures called Report descriptor and Physical descriptor. Generally, to work with the device, report descriptor has to be retrieved and parsed. Because of HID flexibility, a universal HID driver will take many many lines of code. Luckily for us, if all we need is to talk to the keyboard, there is an easier way.

The so-called Boot protocol was designed to provide basic keyboard and mouse data exchange for cases when operating system resources are not available, i.e., during PC boot. Keyboard, configured for boot protocol, generates 8-byte packets in pre-determined format; the system does not need to parse report descriptors to understand the packet.

Boot protocol is described in USB HID class definition. It can be downloaded from usb.org. While you are there, grab a copy of HID usage tables also – it contains key codes for the keyboard (you won’t believe it but it’s not ASCII).

Take a look at the listing below. This is configuration descriptor of USB keyboard, obtained using “Get configuration” sketch from the previous article. This particular keyboard has extra buttons to operate web browser and media player applications. You can see two interfaces – the first interface is keyboard itself, the second interface is serving extra buttons. Let’s take a look at the first interface, starting at line 9. Class field is 03, which means HID. Subclass is 01, which means keyboard. Protocol field is set to 01, which means that boot protocol is supported.

The “unknown” descriptor in the listing is HID descriptor. We don’t need it for boot protocol. The endpoint which follows (line 23), is an interrupt endpoint that we will be polling to get keyboard state. The polling interval is set to 10ms, if polled more frequently, the endpoint returns NAK. This is the way I do it – poll at will and check for NAK.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
Configuration descriptor:
Total length:       003B
Num.intf:           02
Conf.value:         01
Conf.string:        03
Attr.:              A0
Max.pwr:            32
 
Interface descriptor:
Intf.number:        00
Alt.:               00
Endpoints:          01
Class:              03
Subclass:           01
Protocol:           01
Intf.string:        00
 
Unknown descriptor:
Length:             09
Type:               21
Contents:           10010001223E000705
 
Endpoint descriptor:
Endpoint address:   81
Attr.:              03
Max.pkt size:       0008
Polling interval:   0A
 
Interface descriptor:
Intf.number:        01
Alt.:               00
Endpoints:          01
Class:              03
Subclass:           00
Protocol:           00
Intf.string:        00
 
Unknown descriptor:
Length:             09
Type:               21
Contents:           1001000122AB000705
 
Endpoint descriptor:
Endpoint address:   82
Attr.:              03
Max.pkt size:       0008
Polling interval:   0A

The format of the packet, returned by the endpoint, is as follows: first byte is a bit mask representing state of modifier key (Ctrl, Alt, Shift, Win), one bit per key, different bits for left and right keys. Second byte is so-called “OEM byte”, the value of which is constant. I have never seen it to be equal by anything but zero. Last six bytes of a packet is a buffer of sorts. It holds keys pressed since last poll. However, if the key was not released, it will stay in the buffer, therefore, the buffer restricts number of simultaneously pressed keys to 6 not counting modifier keys.

Not that we know how the protocol is working, let’s talk about programming sequence. During enumeration, our device receives an address of 1 and is ready to be put in configured mode. To switch to this mode, device needs to be told what configuration to use. This is done by sending “Set Configuration” request with appropriate value. Our device has only one possible configuration so we will be using value given in line 4 of the listing above. Next request will be HID class-specific request Set protocol with value of 0. This request tells the keyboard to switch to boot protocol. After this is done, we need to generate bulk IN request to endpoint 1 periodically to get information about key presses. Take a look at the following sketch:

/* MAX3421E USB Host controller keyboard communication */
#include <spi.h>
#include <max3421e.h>
#include <usb.h>
 
/* keyboard data taken from configuration descriptor */
#define KBD_ADDR  1
#define KBD_EP    1
#define EP_MAXPKTSIZE  8
#define EP_POLL        0x0a
/**/
EP_RECORD ep_record[ 2 ];  //endpoint record structure for the keyboard
 
void setup();
void loop();
 
MAX3421E Max;
USB Usb;
 
void setup()
{
    Serial.begin( 9600 );
    Serial.println("Start");
    Max.powerOn();
    delay( 200 );
}
 
void loop()
{
    Max.Task();
    Usb.Task();
    if( Usb.getUsbTaskState() == USB_STATE_CONFIGURING ) {  //wait for addressing state
        kbd_init();
        Usb.setUsbTaskState( USB_STATE_RUNNING );
    }
    if( Usb.getUsbTaskState() == USB_STATE_RUNNING ) {  //poll the keyboard
        kbd_poll();
    }
}
/* Initialize keyboard */
void kbd_init( void )
{
  byte rcode = 0;  //return code
/**/
    /* Initialize data structures */
    ep_record[ 0 ] = *( Usb.getDevTableEntry( 0,0 ));  //copy endpoint 0 parameters
    ep_record[ 1 ].MaxPktSize = EP_MAXPKTSIZE;
    ep_record[ 1 ].Interval  = EP_POLL;
    ep_record[ 1 ].sndToggle = bmSNDTOG0;
    ep_record[ 1 ].rcvToggle = bmRCVTOG0;
    Usb.setDevTableEntry( 1, ep_record );              //plug kbd.endpoint parameters to devtable
    /* Configure device */
    rcode = Usb.setConf( KBD_ADDR, 0, 1 );
    if( rcode ) {
        Serial.print("Error attempting to configure keyboard. Return code :");
        Serial.println( rcode, HEX );
        while(1);  //stop
    }
    /* Set boot protocol */
    rcode = Usb.setProto( KBD_ADDR, 0, 0, 0 );
    if( rcode ) {
        Serial.print("Error attempting to configure boot protocol. Return code :");
        Serial.println( rcode, HEX );
        while( 1 );  //stop
    }
}
/* Poll keyboard and print result */
void kbd_poll( void )
{
  char i, j;
  char buf[ 8 ] = { 0 };      //keyboard buffer
  static char old_buf[ 8 ] = { 0 };  //last poll
  byte rcode = 0;     //return code
    /* poll keyboard */
    rcode = Usb.inTransfer( KBD_ADDR, KBD_EP, 8, buf );
    if( rcode != 0 ) {
        return;
    }//if ( rcode..
    for( i = 0; i < 8; i++ ) {
        if( buf[ i ] != old_buf[ i ] ) {
            for( j = 0; j < 8; j++ ) {
                Serial.print( buf[ j ], HEX );
                Serial.print(" ");
                old_buf[ j ] = buf[ j ];
            }//for( j = ...
            Serial.println("");
        }//if( buf...
    }// for( i = 0...
}
This is an example of polling USB keyboard. kbd_init function sends two configuration requests. kbd_poll function requests data from a keyboard and prints keyboard buffer if anything has changed since last output. This looks very simple – and it is. See it for yourself – compile the sketch, connect the keyboard, press some buttons and see contents of the keyboard buffer printed in the terminal window after pressing or releasing the keyboard button.

First line is a key pressed. Second line is a key released. Third and fourth lines is another key pressed and released. Next 3 lines demonstrate modifier keys – I pressed Shift, Ctrl, Alt at the same time. You can see low bits in the first byte turning “1″ when I do it. Last 4 lines demonstrate the buffer behaviour when 2 non-modifier (alpha-numeric) keys are pressed simultaneously.

Obviously, the information received this way can’t be directly used in an application. Certain actions, such as “USB to ASCII” transcoding and duplicate keys removal would have to be taken. However, this has nothing to do with actual USB transfers, which I wanted to demonstrate here. Therefore, I’m leaving buffer processing functions for another article. In the mean time, try this sketch and let me know what you think.

Oleg.

keyboard buffer

Related posts:

  1. Arduino USB Host – USB Descriptors.
  2. PS3 and Wiimote Game Controllers on the Arduino Host Shield: Part 3
  3. Lightweight USB host. Part 6 – introduction to HID.
  4. PS3 and Wiimote Game Controllers on the Arduino Host Shield: Part 2
  5. HID support for USB Host Shield Library 2.0 released
  6. Arduino USB host – First programs.
  7. Arduino USB host – Pre-prototyping.
  8. Towards an FT232 Driver for the USB Host Shield- Part 0
  9. Exchanging data between USB devices and Android phone using Arduino and USB Host shield
  10. PS3 and Wiimote Game Controllers on the Arduino Host Shield: Part 1

23 comments to Arduino USB Host – Peripherals.

  • Showrov

    Hi there
    Your website is really exciting and informative.I am new to usb communication….I wanted to connect a bluetooth dongle to MAX3421E and use it for bluetooth communication.Since its possible connect any usb device to the MAX3421E…i belive it will work….But the problem is the software driver for it dongle.how exactly should i write my program to make it work……could u please advise me on this topic….thanks a lot!!! :)

  • Richard

    The practicality of this depends on what you want to do with the bluetooth. Is it for serial or other ?

    The bluetooth protocol adds further protocol layers on top of the USB layer and this is a challenge for complexity and also for the limited memory in the Arduino.

    If you want serial or some protocol for which an existing bluetooth module is available, then best to use that.

    The Max3421e will work to USB bluetooth dongle. I have it working on ARM processor and have now ordered boards from Oleg to try on Arduino. I use Max3421e here because existing modules do not support bluetooth HID which I use for PS3 and Wiimote. Even this is a challenge on smaller memory 168 Arduino. On top of the USB protocols which take almost half of the available Flash and RAM on a 168, I have to support two additional protocols for bluetooth (HCI and L2CAP) to get to the HID reports. I do not have space to include service discovery, or inquiry. It will be a challenge in the Arduino space and I will post progress here on the Arduino.

    Software may also be dependant on the make of bluetooth dongle and it is hard to make general purpose in terms of drivers. I use dongles which use the CSR chip, because they are well documented. Many dongles have very poor or no documentation available.

    Support of serial or IP would require additional protocol layers in bluetooth and on top.

    I have just written a sketch to support the PS3 controller over wired USB for Oleg’s board, which should also support some wireless PS3 controllers such as Madcatz which come with a USB dongle. This is also needed so I can eventually pair the bluetooth PS3 controller to the arduino. When I get my board and have built and tested, I will post this first. Then I can work on the bluetooth part.

    So give us some more detail of what you want to do, so we can better advise.

  • Showrov

    Hey Richard thanks for the info…
    What i wanted to do is,use the P89LPC938 microcontroller from NXP to connect to a PC using bluetooth dongle.Since the microcontroller doesn’t support usb,i decided to use the MAX3420EECJ+ chip. The microcontroller sends some data to the Hyper terminal program.But it looks pretty complicated to implement this using bluetooth.I should try to implement the usb first :) ….Can you please help me with this problem:-
    The LPC938 microcontroller supports SPI interface.I want to use the MAX3420E chip to connect the microcontroller to PC using a usb cable.For test purposes,the microcontroller should send the hello world message to the Hyper-terminal program in windows.WHAT ARE THE THINGS I NEED TO KNOW ABOUT THE MAX3420E chip to write a C code to implement this usb connection?.I did read something about some usb enumeration code..but m not quite sure waht it means….Thanks in advance for you help… :)

  • Hi evreyboy ! I have a Blutooth USB dongle that contains A CSR chip ! Richard, you say that those chip are well documented, but I don’t manage to obtain them from theri website. I would like to register, but all my email adresses did not worked. Could you send them it is possible or tell me the correct way to download them ? Thanks

  • Sprawly

    Hey guys, I just got an Arduino Deumilnove and I wanted to experiment with having it communicate through a usb bluetooth dongle. The arduino has usb connectivity built in so im hoping this could work since the bluetooth modems i saw were upwards of 60 USD where i found a dongle for 15 USD. I;d imagine that it might take some extra code to read but im uncertain of where to start. My arduino has the ATMEGA328 which i know has double the ram capacity of the 168. The dongle that i plan to use is a Ciraga BTA-6130. I chose this one because it has a 100m range. Any help would be appreciated.

    Thanks

  • Richard

    Showrov, The MAX3420E is a peripheral controller and not a host controller as well. This could be programmed to conect to a PC host, but not to a bluetooth dongle. I have never used the MAX3420 or the MAX3421 in peripheral mode. There are easier and in my opinion better solutions using readily available micros from Microchip, Atmel, Arm and others with already existing software.

    Sprawly, The USB port on Deumilanove is also a USB peripheral specific for serial communication, so will not work to a USB dongle. You need the USB host sheild to use a USb dongle. If you just want serial connection, one of the modems is a lot easier.

  • Richard, you say that the CSR chip is well documented but I don’t finf anything on it. Could you help me ?

  • Richard

    Hi Yannick, It does look like CSR have tightened their access policy, though I would suggest you talk to them to get access to bluecore 4 data. The CSR chips do comply very well to the bluetooth spec, so that is the base reference. If your device is BC41B143A as is mine, then I can send you datasheet.

    I now have HCI layer working reliably on Arduino to Wiimote, and am working on L2CAP layer.

  • Richard

    L2CAP layer is now working, so Wiimote buttons are working to Arduino over bluetooth. Still got some work to do to clean up disconnect process, only using about 70% of program and data space on AtMega168.

    So we have WiiHID, on L2CAP, on ACL, on HCI, on USB, on MAX3421e, on spi! plus lcd and freemem as well.

    • Apologieser

      Wow its awesome to finely found someone that did get a wiimote connected to an arduino directly. But could you please share your source and document it. I think it would do the community very good.

      And besides that I have an project on my own for wich I want to use the wiimote but I’m just not capable of achieving what you have done.

  • Richard

    I did find the datasheet here for the BC41B143A. The common $3 USB Bluetooth Dongles often use these.
    http://pdf1.alldatasheet.com/datasheet-pdf/view/114802/ETC/BC41B143A-ANN-E4.html

    Most programming information is in the Bluetooth spec. though for HCI and higher protocols.

  • Andy

    This is an exciting platform. I would like to setup something like this:
    1.) Arduino with bluetooth modem support to talk to
    PC.
    2.) This same Arduino to support USB host mode.

    My goal is: Gather sensor data on external sensor device with USB interface –> Arduiono USB host mode –> Bluetooth –> PC.

    Does this seem possible? The interface to the external sensor device is a simple serial interface.
    Does this seem possible?

    • This can be done. However, I’d rather set it up differently – using regular Arduino connect USB Bluetooth adapter to the host shield and connect your sensor to the serial port of Arduino. Looks like it would be cheaper than Arduino with built-in BT.

  • hussa

    hi,
    I am trying to use an Arduino with USB Host Shield (http://www.circuitsathome.com/arduino_usb_host_shield_projects)
    and a dongle bluetooth to read Wiimote for controling a DC motor.
    I have already used the code at this link:https://github.com/ribbotson/USB-Host/blob/master/examples/wiiblue.pde

    and now i’m trying to modify it .clearly it would be excellent if anayone can help
    me to find functions to programme the bluetooth and usb (like :
    Usb.setUsbTaskState( USB_STATE_RUNNING );
    …etc
    other than :http://arduino.cc/en/Reference/HomePage

    thanks.

  • Cani

    Hi
    I am trying to communicate the usb shield to a Magnetic stripe card reader. The device recognize like a keybord. Here is the descriptor but i am not able to communicate with the device. i tried the code which is demontrated in this page. idont get any error and it successfully connect but the kbd_poll() doesnt print anything. Do you have any advice?
    Thanks

    Configuration descriptor:
    Total length: 0022
    Num.intf: 01
    Conf.value: 01
    Conf.string: 00
    Attr.: A0
    Max.pwr: 32

    Interface descriptor:
    Intf.number: 00
    Alt.: 00
    Endpoints: 01
    Class: 03
    Subclass: 01
    Protocol: 01
    Intf.string: 00

    Unknown descriptor:
    Length: 09
    Type: 21
    Contents: 10010001223F000705

    Endpoint descriptor:
    Endpoint address: 81
    Attr.: 03
    Max.pkt size: 0008
    Polling interval: 0A
    Enter configuration number:

    • Have you tried to swipe a card through the reader? Some devices won’t be returning anything until new information is available.

    • Cani

      Thanks for the reply.
      Yes i did, but nothing happens. There are 2 leds on the cardreader. a red and green. When i connect it to a pc before windows recognize it the green led blinks, but after some seconds it turns off and card reader read cards in any editor such as notepad. when i connect it to the usb-shield the green led never stop blinking. What do you think?

  • The_YongGrand

    Hello there,

    I like your project, and I’m applying it on the USB keypad as well.

    When I checked around its attributes, it has only one configuration and one interrupt endpoint and nothing else. So I assume it is like any other ordinary keyboard except the alphabets and stuff.

    When it is Numlock-ed, it outputs when I pressed 1:

    00 00 53 00 00 00 00 00
    00 00 00 00 00 00 00 00
    00 00 59 00 00 00 00 00
    00 00 00 00 00 00 00 00
    00 00 53 00 00 00 00 00
    00 00 00 00 00 00 00 00

    If it doesn’t have the Numlock, it gives me when I pressed 1:

    00 00 59 00 00 00 00 00
    00 00 00 00 00 00 00 00

    I believed that the zeros in the 8-byte referred to the “key release” as what you mentioned in the blog, but I’m a bit confused of why it gives the sequence of zeros, 53, sandwiched inside is the command, and then the same sequence as the first one.

    Or I should not worry as it is the programmers who wrote those sequences, and then it is all up to the user to exploit its commands, by testing the thing one button by one button?

    Thanks.

  • Alexander

    Hello,
    I am trying to work with a device in which the entries in the configuration descriptor are reversed – Class 03, Subclass 00, Protocol 00 comes first, followed by Class 03, Subclass 01, Protocol 01. Here is the configuration descriptor:

    Configuration descriptor:
    Total length: 0049
    Num.intf: 02
    Conf.value: 01
    Conf.string: 00
    Attr.: C0
    Max.pwr: 32
    Interface descriptor:
    Intf.number: 00
    Alt.: 00
    Endpoints: 02
    Class: 03
    Subclass: 00
    Protocol: 00
    Intf.string: 00
    Unknown descriptor:
    Length: 09
    Type: 21
    Contents: 000100012220000705
    Endpoint descriptor:
    Endpoint address: 81
    Attr.: 03
    Max.pkt size: 0008
    Polling interval: 0A
    Endpoint descriptor:
    Endpoint address: 01
    Attr.: 03
    Max.pkt size: 0008
    Polling interval: 0A
    Interface descriptor:
    Intf.number: 01
    Alt.: 00
    Endpoints: 02
    Class: 03
    Subclass: 01
    Protocol: 01
    Intf.string: 00
    Unknown descriptor:
    Length: 09
    Type: 21
    Contents: 00010001223F000705
    Endpoint descriptor:
    Endpoint address: 82
    Attr.: 03
    Max.pkt size: 0007
    Polling interval: 0A
    Endpoint descriptor:
    Endpoint address: 02
    Attr.: 03
    Max.pkt size: 0001
    Polling interval: 0A

    When I run the code provided, I get “Error attempting to configure boot protocol. Return code :5″. Is there a way to look up these return codes? Any suggestions?

    Also, further information about the KBD_ADDR and KBD_EP variables would be helpful. I understand they are taken from the configuration descriptor but it is not obvious to me where you got these values from in your example.

    Thank you.

  • Rudy

    Hello,…

    is this USB shield only work with UNO? is the Duemilanove not support this?

    thanks….

Leave a Reply

 

 

 

You can use these HTML tags

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="">