V-USB tutorial continued: HID mouse

Wow, my AVR ATtiny USB tutorial here I got featured in Hack a Day! Motivated by the influx of readers, I decided to find out how to make a USB HID (human interface device) mouse.

V-USB examples already contain an example of this, so I digged in to see what is different in usbconfig.h compared to the one we finished in my tutorial. It seems only a few things need changing:

  1. USB_CFG_HAVE_INTRIN_ENDPOINT needs to be set to have an additional endpoint
  2. USB_CFG_INTR_POLL_INTERVAL set to 100 ms instead of 10 in template
  3. USB_CFG_IMPLEMENT_FN_WRITE is not needed, nor is …FN_READ (define both to 0)
  4. Device ID and name need to be changed. I’ll just use the same ID as they did
  5. USB_CFG_DEVICE_CLASS is set to 0, not 0xff
  6. USB_CFG_INTERFACE_CLASS set to 3 instead of 0
  7. USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH defined to match the structure’s length

That’s it! So here are the defines I changed:

#define USB_CFG_HAVE_INTRIN_ENDPOINT    1
#define USB_CFG_INTR_POLL_INTERVAL      100
#define USB_CFG_IMPLEMENT_FN_WRITE      0
#define USB_CFG_IMPLEMENT_FN_READ       0
#define USB_CFG_DEVICE_ID               0xe8, 0x03
#define USB_CFG_DEVICE_NAME     'M', 'o', 'u', 's', 'e'
#define USB_CFG_DEVICE_NAME_LEN 5
#define USB_CFG_DEVICE_CLASS        0
#define USB_CFG_INTERFACE_CLASS     3
#define USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH    52

OK. So what about main.c? Turns out the changes are rather straightforward:

  1. Report descriptor is defined for our HID device, outlining a simple mouse status report
  2. The described struct for the report is implemented
  3. Make the device to respond to a few required request
  4. In the main loop, when ever USB interrupt is ready, send our report buffer

That’s actually it. The V-USB example uses a nice 10.6 fixed point sine and cosine to draw circles with the mouse, but I chose to have some erratic motion with a pseudo-random number generator. Note that with a bit of extra motion and erratic mouse clicks, you could really drive a user insane!

You can find the complete main.c (as well as usbconfig.h and the schematic) from this project zip file and study the usbHidReportDescriptor (just a string of numbers) and usbFunctionSetup() (really straightforward) on your own, but here’s the “meat” of the code – report structure and new main function inner loop:

typedef struct{
    uchar   buttonMask;
    char    dx;
    char    dy;
    char    dWheel;
} report_t;

// ...

int main() {
    int rand = 1234; // initial value

    // ...

    while(1) {
        wdt_reset(); // keep the watchdog happy
        usbPoll();
        
        if(usbInterruptIsReady()) { // if the interrupt is ready, feed data
            // pseudo-random sequence generator, thanks to Dan Frederiksen @AVRfreaks
            // http://en.wikipedia.org/wiki/Linear_congruential_generator
            rand=(rand*109+89)%251;
            
            // move to a random direction
            reportBuffer.dx = (rand&0xf)-8; 
            reportBuffer.dy = ((rand&0xf0)>>4)-8;

            usbSetInterrupt((void *)&reportBuffer, sizeof(reportBuffer));
        }
    }
}

Thanks for reading! I might do a USB keyboard tomorrow, so I do recommend checking back soon or subscribing to the RSS feed. :)

Update: I didn’t make a USB keyboard tutorial after all, but opted instead to make a USB password generator. However, I recently stumbled upon this nice post that covers USB keyboards with V-USB and Arduino, so it might be worth to check it out:

http://petrockblog.wordpress.com/2012/05/19/usb-keyboard-with-arduino-and-v-usb-library-an-example/

37 thoughts on “V-USB tutorial continued: HID mouse”

  1. This is very nice series. Thanks. I wonder whether it is easy to program a generic HID device to do something like flashing an LED. The advantage of HID is host driver free. Some toolkit, like Psychtoolbox for Matlab, has OS-independent HID support.

  2. great tutorial indeed!however,i have some doubts.is it possible for the mouse pointer to go to specified coordinates, like 255,10 ?and is it possible to read the coordinates of mouse pointer?also,is it possible to read the resolution of screen,like it’s width and height in pixels?please answer :-)

  3. @utkarsh: I actually do not know, you probably need to read the USB HID specification and see if it is possible to give absolute coordinates or read the coordinates – I would assume such a thing would be possible as Wacom-type tablets at least support absolute positioning.

    But of course it’s also possible that such additional control over the cursor would require PC-side drivers, Wacom at least has some that you use to tweak the settings.

  4. thanks for the reply!well,is there a way to store the id of host computer to permanent memory?if yes,then how?this is because it is possible,we can calibrate the device once and use the presets next time.

    reportbuffer.dx must update some variable,to change the pointer position.if we can directly get that variable,we would know of the position.to get the screen size,we could use calibration technique.we could keep giving dx and dy negative values,which would eventually send the pointer to (0,0).at that particular movement,the user will push a calibration button on the device,which will take another variables x=0 and y=0,count the no. of steps taken in the process,and thus calculate no. of pixels moved ,thus calculating screen size.how’s that?but it will be a lot easier if we could just read the pointer position…….again,thanks!

  5. @utkarsh: The HID mouse implemented in this example is not able to receive any identification info from PC, so storing calibrated values is out of the question. It seems absolute positioning is possible (I just googled for “usb hid mouse absolute”), you might want to check out this thread, one of the guys even posted links to a project where he made a working device with absolute coordinates:

    http://forums.obdev.at/viewtopic.php?f=8&t=2559

  6. thanks for quick reply!i am actually working on touchscreen interface,so this might help!hey,how about co-developing the device?it is a new technology,sure is revolutionary.with the idea i have,we can turn any computer into a touchsrceen computer,in less than $10 !!!

  7. just imagine the huge potential of this technology.touchscreen interfaces will become as common as mice!gone will be days of big clunky mice.the technology will require just a small setup,cheaper than $10!!!!everything,from laptops,to desktops computers,to phones,will have OUR technology.

  8. Can you please help me with the way in which hid mouse conveys data with pc .. Like a sample of code for exchanging the change in x and y coordinates

    1. The data is sent using the reportBuffer structure, which has members dx, dy, buttonMask and dWheel. dx and dy are differential coordinates, i.e. if you want to move the mouse 5 pixels up, you set reportBuffer.dy = -5 in one report. To keep the mouse still, you’d do this:

      reportBuffer.dx = 0;
      reportBuffer.dy = 0;

      You should be able to locate the lines where the example code changes reportBuffer pretty easily. Good luck!

  9. Hi,

    I enjoyed reading your tutorials a lot! I have just found your blog and I will definitely follow your posts.

    Thank you for mentioning my article in your post above :-)

  10. THE F I WANT IT!!!!!!!!!!!!!!!
    but I don’t have the materials
    – Eagle monster – 12 year old, high school student

  11. hi
    i’m trying to make an touch mouse ,
    i use a touch pad and with its output and using ADC . provide dx and dy
    but it dosent work
    windows dos not recognize my mouse,
    can you help me ?

    1. I’d recommend that you first try to make the “random mouse mover” circuit described in the tutorial, and after that works, combine it with the touch pad functionality. The USB hardware part is sometimes tricky, so having a 1:1 reference usually helps to spot small mistakes. Software side has the same thing, so it’s often easier to take something that works and gradually adapt it so you’ll see what change “breaks” the code.

      One possible reason for the mouse not working is if usbPoll() is not called often enough – the communication drops if you have something that takes too long between the polls.

  12. i found the answer

    first you need to declare in usb configuration . follow these steps:
    1- go to usbdrv.c
    2- find this line: PROGMEM char usbDescriptorConfiguration[] = { /* USB configuration descriptor */

    3- add seven bites to the length of descriptor as followed in this line:
    18 + 7 * USB_CFG_HAVE_INTRIN_ENDPOINT + (USB_CFG_DESCR_PROPS_HID & 0xff) +7, 0, //I added las +7

    4-now you need to add one ondpoint . find this line and add 1:
    USB_CFG_HAVE_INTRIN_ENDPOINT + 1 , /* endpoints excl 0: number of endpoint descriptors to follow */ // I added last +1

    5-now at the end of descriptor add these codes to make a new OUT endpoint:
    7, /* sizeof(usbDescrEndpoint) */
    USBDESCR_ENDPOINT, /* descriptor type = endpoint */
    (char)0x01, /* OUT endpoint number 1 */ //instead of 81
    0x03, /* attrib: Interrupt endpoint */
    8, 0, /* maximum packet size */
    USB_CFG_INTR_POLL_INTERVAL, /* in ms */

    You need also to configure your driver to use this interrupt-OUT no-1

    6- now go to usbconfig.h and set USB_CFG_IMPLEMENT_FN_WRITEOUT to 1

    7- find this line:
    #if USB_CFG_IMPLEMENT_FN_WRITEOUT
    if(usbRxToken < 0x10){ /* endpoint number in usbRxToken */
    and change it to this:
    if(usbRxToken == USBPID_OUT){ /* endpoint number in usbRxToken *///*
    8-every time device receives Interrupt-out, goes into usbFunctionWriteOut()
    tell me if it works for you

    1. I think that’s what the “buttonMask” variable in the struct is for. My bet would be on left button being bitmask 1 and right button 2, but you may want to experiment/google it. :)

      Oh and it might be necessary to send mask 0 afterwards to signal that buttons are no longer pressed down, just like with keyboards. Haven’t checked the spec though.

  13. I am doing projct on touchpad as HID mouse.. Bt using lpc2148… I have made lpc2148 into HID and touchpad is also interfaced with lpc2148….. nw plz can you gv me the code to convert ADC values of the touchscreen into movements of the mouse….. please….. help….

  14. Well if you can manage to adapt my tutorial to the LPC2148 as it is (sending random movements to PC), you’re 95 % there already.

    I assume the touchscreen provides two voltages that correspond to X/Y position of the finger? Let’s say the X coordinate ADC gives values between 120 (left edge) and 2050 (right edge), and you want to scale that so X coordinate has values between 0 and 999:

    x = (x_adc – 120) * 1000 / (2050 – 120);

    You need to store the previous value of x, so your HID device can send the change of x:

    reportBuffer.dx = x – x_prev;
    x_prev = x; // store for next round

    Then you need to repeat the same for Y, and probably handle cases where user takes the finger off the touchpad (stop sending reports) and puts it back (set x_prev to new “starting point” so you don’t send the change between the previous position and new, otherwise the touchpad will work like a Wacom drawing tablet with absolute coordinates).

    That’s all I can help you from the top of my head, you can probably google as much as I can if you need additional info. :)

  15. Hi, your tutorials regarding attiny and usb are very useful!! I have a simple question: What if I would like to create a serial COM over USB?
    I mean I would like my usb device to be seen from the host as if it was an Arduino device..
    This is because I have created my own arduino based application (e.g. a simple led to be turned on/off) and plugged to my smartphone using android development tool. So I have my java based App to switch the led from my smartphone.
    Now what I would like to do is to create a device simpler than arduino and based on attiny, in order to control it with the same App. Do you think it’s feasible? Can you give me some suggestion?

    1. Hi,
      You might want to take a look at FTDI chips or the ready-made cables, they have existing USB COM port drivers for most operating systems. You can probably also google around what Arduino uses, I recall they use a dedicated AVR chip in some cases, so you might be able to make a compatible solution.

      Of course, there are about dozen small form factor arduino compatibles around, Adafruit and Sparkfun have probably some, so you could probably purchase one of those for a few bucks, too.

  16. Hi.
    I don’t know why this example (HID mouse from http://www.obdev.at ) doesn’t work at
    all for me .I’ve tried it one hundred times but actually I think I don’t
    know

    which files must be added to compile hid mouse driver successfully . So
    could you send your own hid mouse project for me to fix this problem with
    the complete files such as project and usbdriv files because I’m not
    familiar very much with winavr and vusb with some more descriptions.
    i’m trying to make an touch mouse,help me!!
    Thank you for your attention

  17. Thanks a lot Joonas, your tutorial series has helped a lot in getting an insight to VUSB.
    The HID mouse is well introduced.I specifically like how the mouse arrow head jumps around but it also follows a specific path if we were to simply draw in paint whilst it is on. I guess the rand=(rand*109+89)%251 makes the random pattern repeat. Can you suggest of any way that this repetition ceases to occur and the code yields complete randomness in terms of mouse movement?
    Thanks.

    1. You could use another pseudo-random number generator (PRNG), see the Wikipedia link in the comment above that line. Unfortunately many PRNGs with better randomness characteristics are more computationally intensive, which might pose an issue with limited AVR chip.

      If your AVR has an analog-to-digital converter, you might employ some analog source for randomness, too.

Leave a Reply

Your email address will not be published. Required fields are marked *


× five = 35

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>