Code and Life

Programming, electronics and other cool tech stuff

Supported by

Supported by Picotech

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/

38 comments

Xiangrui:

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.

jokkebk:

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

utkarsh singhal:

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

jokkebk:

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

utkarsh singhal:

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!

jokkebk:

@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

utkarsh singhal:

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

utkarsh singhal:

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.

jokkebk:

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

utkarsh singhal:

ok,no problem! :)

jobin:

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

jokkebk:

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!

Florian:

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

Andrew:

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

Ghassi:

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 ?

jokkebk:

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.

haitham:

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

haitham:

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

John Jennings:

Nice series. Thanks a lot.

carlos:

What about sending mouse clicks?

Thanks.

Joonas Pihlajamaa:

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.

Aniket:

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

Joonas Pihlajamaa:

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

iCarletto:

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?

Joonas Pihlajamaa:

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.

amin:

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

Rahul:

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

Joonas Pihlajamaa:

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.

MSubhanH:

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.

Joonas Pihlajamaa:

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.

MSubhanH:

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

Meir Michanie:

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.

Meir Michanie:

code availbale here:
UsbMouse

Joonas Pihlajamaa:

Cool, thank you!

sorrow:

sir, it shows many error when i try to compile the code in atmel studio 6.1 …… do you have any solution .. can you send me code after debuging the code in atmel studio 6.1 …. hope your soon response

Pero:

Hello, thanks for the nice tutorial, I am astonished by your writing style and simplicity you provide to explain such complex issues. However, I’d be glad if you could answer me couple of questions:
1. How is possible to use USB functionality on chip that has no USB hardware/interface embedded?
2. How is V-USB bootloader different from LUFA bootloaders, standard for AVR-USB chips?

Cheers,

Pero

Guru:

Sir, I used the same main file and usbconfig.h that you have published here. The device is not being recognized by my PC.
Error message:Windows has stopped this device because it has reported problems. (Code 43)

A request for the USB device descriptor failed.
What shall I do? Is it a driver problem. I’m currently using windows 10.

Joonas Pihlajamaa:

Hard to say for sure, but most of the USB errors people have reported here have been due to hardware issues. Wrong pins, nonconducting connection, zener diodes that don’t quite work how they should, etc. The mouse is a USB HID device so it should not need any drivers.