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 Comments or trackbacks to V-USB tutorial continued: HID mouse

Xiangrui:
February 13, 2012 at 19:48

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.

reply

jokkebk:
February 13, 2012 at 21:22

@Xiangrui
Thanks! And yes, I think there’s an example in V-USB bundle on how to use HID descriptors for driver-less data. It’s definitely less of a hassle without drivers. :)

reply

utkarsh singhal:
March 15, 2012 at 0:48

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 :-)

reply

jokkebk:
March 15, 2012 at 0:55

@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.

reply

utkarsh singhal:
March 15, 2012 at 11:38

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!

reply

jokkebk:
March 15, 2012 at 12:00

@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

reply

utkarsh singhal:
March 15, 2012 at 12:20

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 !!!

reply

utkarsh singhal:
March 15, 2012 at 12:43

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.

reply

jokkebk:
March 15, 2012 at 13:24

@utkarsh: Thanks for the offer, but I think I will pass this time. :) Good luck with the project in any case!

reply

utkarsh singhal:
March 15, 2012 at 13:49

ok,no problem! :)

reply

jobin:
April 2, 2012 at 7:04

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

reply

jokkebk says:
April 3, 2012 at 0:08

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!

reply

Florian:
May 29, 2012 at 20:26

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 :-)

reply

Andrew:
June 2, 2012 at 9:59

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

reply

Ghassi:
June 30, 2012 at 15:56

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 ?

reply

jokkebk says:
July 7, 2012 at 9:52

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.

reply

haitham:
August 3, 2012 at 0:37

when i activate end point 3 it shows error in device manager ?
How can i handle interrupt out using HID
Eng.Haitham Khairy
Egypt

reply

haitham:
August 3, 2012 at 0:47

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

reply

John Jennings:
April 1, 2013 at 21:53

Nice series. Thanks a lot.

reply

carlos:
December 16, 2013 at 6:53

What about sending mouse clicks?

Thanks.

reply

Joonas Pihlajamaa says:
December 16, 2013 at 9:24

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.

reply

Aniket:
February 4, 2014 at 6:54

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….

reply

Joonas Pihlajamaa:
February 4, 2014 at 9:35

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. :)

reply

iCarletto:
March 17, 2014 at 15:00

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?

reply

Joonas Pihlajamaa says:
March 22, 2014 at 14:06

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.

reply

amin:
March 25, 2014 at 16:30

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

reply

Rahul:
April 13, 2014 at 4:50

Hi,
I was wondering whether we can use V-USB for connecting bluetooth USB dongle with AVR.

reply

Joonas Pihlajamaa says:
April 13, 2014 at 9:02

If you mean to use an USB bluetooth dongle with AVR, then probably no, because V-USB is not a USB host, but a device. Host would need additional functionality.

reply

MSubhanH:
April 19, 2014 at 12:49

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.

reply

Joonas Pihlajamaa says:
April 20, 2014 at 12:51

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.

reply

MSubhanH says:
June 6, 2014 at 14:55

Thanks Joonas for the idea of using the ADC feature. I’ll try that on Atmega32.

reply

Meir Michanie:
August 10, 2014 at 12:18

I took your code and mixed it with UsbKeyboard library to allow me to use arduino IDE to do the hid-mouse.
I included code for clicks as well.
Thanks for sharing.

reply

Meir Michanie:
August 10, 2014 at 12:19

code availbale here:
UsbMouse

reply

Joonas Pihlajamaa says:
August 10, 2014 at 14:40

Cool, thank you!

reply

Leave a Reply

Your e-mail address will not be published.


five × 1 =

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>