Feed on
Posts
Comments

A month with no new post? That’s unacceptable. Since my last trip to Shenzhen, I actually have quite a few new ideas and projects to post about. The thing is there are so many of them that I don’t know which one to start with! Anyways, I’ve decided to put a stop to this. In this blog post, I will describe some work I did a little while back about implementing HID-class serial communication for AVR microcontrollers using the V-USB library.

Introduction

First, let me explain what I am trying to do. As you probably know, V-USB is a very useful software-only implementation of low-speed USB device for AVR microcontrollers. It adds USB functionality for almost any AVR, particularly for those without hardware USB functionality. With this, it’s possible to make a very low-cost standalone Arduino with USB port and without having to use an FTDI chip. I know there is the Arduino Leonardo, which is based on ATmega32u4, and which has hardware-based USB functionality. But mega32u4 only exists in SMT package, and it’s more expensive than mega328 after all. Besides, I am fully embracing the ‘I do it because I can’ spirit, and this is actually a great motivation for me to learn about V-USB.

What do I need the USB for? Mostly for two reasons. One is to flash a program to the microcontroller, so it needs a USB-based bootloader. For this, there is a very nice open-source project called USnoobie, which can bootload mega328 as a USBasp programmer. This way you can flash a program through the USB port directly, without using a serial cable or an external AVRISP programmer. So this is all good.

The second reason to have USB is for serial communication — the ability to transfer data (e.g. strings) in and out between the device and a host computer through the USB port. This is useful especially for debugging (i.e. printing values to a serial monitor), unfortunately this feature is missing in most V-USB projects. There are some related projects. For example, I came across the AVR-CDC project, which turns a mega328 into a CDC-class USB-serial converter. But there seem to be some limitations of using V-USB to implement CDC (i.e. violates USB standard), and also CDC-class devices require installing a driver on Windows. I would like to make a HID-class USB device which does not require driver installation. So overall I didn’t find any available resource that I can use directly.

Circuit Design and V-USB

Now I’ve explained the motivation, let’s see how to go about implementing it. The first step is to learn to use V-USB. I started with the EasyLogger project downloaded from the V-USB website. It is based on the tiny45 mcu. The program reads a sensor (e.g. temperature or light) and presents itself as a USB keyboard to print out the sensor values to a host computer. This is an excellent starting point for me because USB keyboard is a standard HID-class device, and the project is simple enough that I can easily learn and make modifications.

To adapt it to mega328, I first made a circuit based on USnoobie. Here is the schematic and my build of the circuit on a breadboard:

m328-vusbIMG_3287

It’s a pretty standard V-USB setup. I assigned digital pin PD2 (INT0) to USB D+, pin PD7 to USB D-, and PD4 to a pushbutton. The pushbutton is used to enter bootloading mode. Specifically, if the button is pressed when the circuit is powered up, the bootloader is activated and the mcu will appear as a USBasp programmer. Different from USnoobie, I’ve decoupled this button from the D- line, so that I can use the button for general-purpose input (otherwise pressing the button will trigger the D- line). This requires changing the USnoobie code slightly to use pin PD4 for bootloading condition. Finally, I’ve also added a MCP9700 temperature sensor (you can replace it by any analog sensor such as photosensor) to analog pin ADC0 for testing later.

The next step is to modify the source code. First, change usbconfig.h to match the D+ and D- pin settings, specifically the following three macro defines:

Next, modify main.c. This step is pretty technical and tedious. It mainly involves changing register names to match mega328 (since the code was originally written for tiny45). Also, the calibrateOscillator(); function can be removed as the mega328 will be running on an external 12MHz clock.

I also modified the Makefile in order to compile and flash the code for mega328. After a few tweaks here and there, the EasyLogger started working on my mega328! It can successfully output numerical values to a text editor through the USB port. This is very encouraging. If these steps don’t make much sense, you can take a look at the code below, and give it a try yourself.

Learning HID

HID stands for Human Interface Device. It’s a USB class designed primarily for keyboard, mice, joystick, and similar human interface devices. The nice thing about HID is that it’s supported on all operating systems. For example, on Windows, the system uses built-in HID driver to handle USB requests, so no driver installation is required. This is why when you plug in a keyboard or mice, you never have to install a driver (imagine how annoying it would be if you had to!).

To implement HID, you first will need to construct a HID descriptor, which describes the number of reports, and the size, meaning, and (optionally) value range of each report. For example, these reports can be the ASCII code of the pressed key, the x and y offsets, and button presses of the mouse. There are also more general-purpose reports like a buffer of bytes. This is what I will be using to transfer bytes in and out between the device and host. To be honest, the whole USB descriptor thing was very obscure to me in the beginning. I didn’t know if there is one correct way to define it, or it can be flexible. As it turns out, Linux is pretty forgiving about it, but Windows is not. After many trials and errors, I finally settled with this HID descriptor:

It contains an outgoing buffer of 8 bytes (to transfer data to the host) and an incoming buffer of 32 bytes (to receive data from the host). As I said above, Linux is pretty flexible about the descriptor — you can change it in many ways and it still works. Windows, however, is very strict — if you are not careful, it will simply refuse to recognize the device.

The next step is to write functions to handle the USB requests. For transferring data out, I used the usbSetInterrupt function, which allows sending data spontaneously to the host (i.e. whenever the device has something to report). (I should mention here that I wrote the code as an Arduino library called HIDSerial. So everything gets compiled in the Arduino software. You can certainly use avr-gcc to compile the code as well). I made HIDSerial an inherited class from Arduino’s Print class, so I can make use of the many available print functions (e.g. print a string, an integer, a floating point) in the class for serial printing need, without writing extra code. For transferring data in, I implemented the usbFunctionWrite function, as described in the V-USB Document Wiki.

Now, before I can test and debug the code, I need to have some minimal host software to communicate with the device. That’s what I will be describing next.

Write Host Software using Processing

Going the HID route means the device will not appear as a standard serial COM port, so you can’t use the standard serial monitor to send and receive values. Instead, I will have to write host software myself. I can certainly do this in C or Java. But since I want to make the host software cross-platform, I have chosen to implement it in Processing, which allows me to easily export the program as standalone applications on all platforms. Long story short, to do this, I used HIDAPI library. It has all the goodies to handle communications with HID devices, such as finding a device, opening the device, reading from the device, and sending feature report to the device. Also, using the G4P library, you can easily build a GUI with buttons and text fields, and make the interface just like a standard serial monitor. Once the software is finalized, I can simply click on ‘Export Application’, and select all three platforms, and voilà, the host software is all set! Below are two screenshots of the HID serial monitor:

hidserialmonitor1hidserialmonitor2

Source Code

The source code of this project is available for download on my GitHub repository:

You can either do a git clone, or directly download the project as a zip file (see the Download .zip button on the right-hand side of the page). The folder contains circuit schematic, part list, Arduino library (HIDSerial), host software (HID serial monitor), and bootloader (optional, but recommended as it allows you to re-flash the microcontroller through USB, without any external programmer). The Arduino library consists of several starting examples, which are also demonstrated in the video above. I am sure there are bugs and issues with the code, but please feel free to leave comments and feedback below, in order for me to improve the code.

Limitations and Issues

The biggest limitation of this implementation is the data transfer speed — it’s much slower compared to a standard USB-serial converter. This will be an issue if you need to pump out data as fast as possible. But I figured that for the purpose of debugging, the transfer speed is usually not that critical. The software works pretty reliably in Linux, but I’ve found that on Windows, the host software starts to lose data after running for a while, so not all data get transferred correctly. Resetting the microcontroller and restarting the host software seem to get it back to work. I am still investigating the cause of this issue. It may still have to do with Windows being very strict with USB communication protocols. Perhaps the USB experts can take a look at the code and point me in the right direction.

Adapting the Code to Other AVRs

It’s relatively easy to adapt the code to other AVRs. For example, ATtiny45/85 is a popular V-USB platform, since it’s small, cheap, and has internal oscillator that can run at 16.5MHz, which meets the USB standard (so it saves a crystal and frees up two pins). By changing a couple of pin assignments in the source code, I was able to get the HID serial functions to work on tiny45. I really should do it on tiny85, since tiny45 has only 4KB flash space (even a simple demo would take more than 3.5K flash space). Here is a picture of the tiny45 breadboard build. You can even program tiny45 in the Arduino software by following the descriptions here.

IMG_3322

Thanks for reading the post. Feedback, comments, and questions are welcome.

27 Responses to “HID-class USB Serial Communication for AVRs using V-USB”

  1. B@tto says:

    Hi,

    Very nice work ! Thanks for sharing, I will test it soon as possible :)

    About your lost of packet after a long time (I’m not an expert at all about USB so it’s just a remark comparing codes) : on libusbdotnet examples which are displayed and we can see that the buffer is erased every polling http://libusbdotnet.sourceforge.net/V2/Index.html and your issue seems to be a typical overflowed memory so maybe it’s a way to explore …

    Regards

  2. […] HID-class USB Serial communication for AVRs using V-USB – [Link] […]

  3. David Halterman says:

    Great job! I’m very impressed. Can you please send me/post the .sch for the attiny board you created in the video.

    Thanks

  4. Apoorv says:

    Sir,
    I want to control my atmega16 uController using a USB PS gamepad. Is it possible to do the same???

  5. […] First, the ATtiny45 (which functions as a USBtiny programmer) has been removed, partly to save space, and partly to simplify the design. Instead, the ATmega644 will be flashed with a USBasp bootloader, which allows itself to function as a USBasp programmer in bootload mode. This will take away 2KB of flash memory space, but on the plus side, it eliminates one chip, and the transfer speed of USBasp is actually noticeably faster than USBtiny. Additionally, using ATmega644 to directly handle USB tasks (thanks to the V-USB library) makes it possible to add USB serial functionality. As a result, you can do serial communication to debug the code, without any external USB serial converter. So it’s win-win-win The details can be found in my previous blog post about USB HID-class Serial Communication for AVRs. […]

  6. […] que vio, al parecer, se trataba de una tarea imposible sin un segundo microcontrolador, más capaz. Entonces tuvo una idea : si el objetivo es sólo para transferir datos de un lado a otro entre un ordenador y un […]

  7. Frank says:

    I did something very similar but my technique allows any serial terminal to be used

    http://learn.adafruit.com/trinket-fake-usb-serial

    • ray says:

      Hi Frank, thanks, I am well aware of your work on trinket fake USB serial. It’s really nice you got it working. My work was mainly driven by the goal of creating a HID-class device. It’s not standard serial, but it doesn’t require installing any driver. I’ve seen people having trouble installing libusb drivers for Windows 64-bit, that’s why I decided to go the HID route.

  8. keghn says:

    Hello,
    Very nice work.
    I am starting to use the Teensy 2.0:
    http://www.pjrc.com/teensy

    It has a fast usb for arduino clone:
    http://www.pjrc.com/teensy/benchmark_usb_serial_receive.html

    They have software their board and host pc:
    http://www.pjrc.com/teensy/usb_debug_only.html
    and:
    http://www.pjrc.com/teensy/hid_listen.html
    Best regards,
    keghn

  9. Alvydas says:

    I would like to use USB bootloader atmega 1284p processor according to your example.
    Are the following changes:
    # define USB_CFG_IOPORTNAME D
    / ** /
    # define USB_CFG_DMINUS_BIT 7
    / ** /
    # define USB_CFG_DPLUS_BIT 10 / / pin D10 – INT0, pin D2 for ATMega328
    / ** /

    Thank you.

  10. […] with the Trinket uses HID to pass raw USB messages back and forth. This technique was created by Ray’s Hobby, we’ve added this to our tutorial […]

  11. Jorge says:

    Hi, good job :D

    Do you know how can adapt the HID to work on atmega32?

    Thanks

    • ray says:

      The code is written for atmega328, which should be very similar to atmega32. You will need to modify the mcu name/type in the Makefile, and probably the main.c as well if atmega32 uses different GPIO names with atmega328. The software is based on V-USB, which should work on most AVR mcus.

      • Jorge says:

        Thanks for your reply

        I don’t know if the 328 is similar to 32. The number of pin is different. The link to datasheet is http://www.atmel.com/Images/doc2503.pdf, can you take a look?

        • ray says:

          Sorry, I don’t have an atmega32 chip and frankly I won’t have time to try it. Getting V-USB to work for any AVR chip is fairly simple, just define the pins you are using for D+ and D- in the source files and make a couple of changes to the Makefile to compile the code. If you encounter any trouble, google ‘v-usb atmega32′ and you should find plenty of information.

  12. vic says:

    excellent work !! except some more work like this on your website!!
    thanks for sharing …
    good luck!

  13. Ten5 says:

    Hello Sir,
    I’m fairly new to all this. I have an ATmega328 on a board and am using an ATmega8l USBasp to connect to it and upload and run programs. Apparently most of the Arduino examples are working just fine. Just the Serial ones aren’t running.
    So I followed your method and downloaded all the required softwares. Next as said in the tutorial I am uploading the Hello_World code and then running the host software(which is the HID Serial Monitor). However as I try to “connect” to the USBasp, it says “could not find device”. Could you please tell me which step I’m doing wrong or atleast how and where I should use that EasyLogger code for the ATmega328, cause I’m pretty sure I haven’t done anything with it except download and extract the files. Do I need to run any file? Cause I can’t see any .exe file.
    Thanks for your help in advance.

  14. ARCAdaptor says:

    Hello!
    Thank you for sharing!
    I will try your project as soon, as possible at my ARCAdaptor dev board, and if everything is ok, will try to incorportate it into my source tree at git, if your are not against (leaving all referencies to your site/git, of course).
    HID serial was the thing I missed so far.

  15. muhammad adli rizqulloh says:

    that’s great job! how if i want to make hid terminal using visual basic? i had made hid terminal using visual basic, the terminal can receive data from microcontroller but cannot send data from PC to microcontroller. please help me…

  16. Sancho says:

    Hello.
    First of all, thanks for the effort you spent on this.

    I tried to compile the library using Tiny cores from http://code.google.com/p/arduino-tiny/
    I run into trouble with line 13 of HIDserial.h – the definition of write for string.
    I replaced it to return nothing and redefined it to voin instead of size_t (fixed the HDserial.c accodringly).
    It compiled, however, something seems not to be working correctly, when run using the circuit you are providing on the last image (the voltage decreased using 2 diodes, no zeners, etc).
    I suspect the problem is with oscilator, which, I read somewhere, is not reliable on 3.3V operation at 16MHz for ATtiny85.
    Can you, please, provide an example for ATtiny45/85 arduino sketch and some schematics?
    Thanks.

    • ray says:

      Can you be more specific about ‘something seems not to be working correctly’? Does your operating system detect the device at all? Although 16.5MHz @ 3.3V is technically out of the recommended frequency scaling of ATtiny45/85, in practice there is no problem doing so (unless if you are using the device under extreme temperature conditions). Example sketches are already included in the zip file provided at the end of the post.

      • Sancho says:

        I will sound stupid, but I cannot provide any other explanation of the behavior.
        When I only plug in the resistors and the diodes to the USB port (the same way as your picture with the ATTiny), the dmesg says something like USB device connected and than Error: Device not accepting address…
        Exactly same thing happens when I try to connect the ATtiny85 with the resistors.
        As I do not have logic analyzer, I am kinda lost and don’t know how to provide more information :(
        But, I made a simple code change – I put a blinking loop into the code and I can see the LED is not working – for me this could mean that the BOD jumped in or something :(
        As I said, I am lost :)

  17. mohie says:

    Hi , u did a great job. actually,I have a project that I have to realise soon:
    I need to receive a data that is coded in NRZL code. for that, I will use the PIC18F4550 and a usb to read the data in PC.
    so please I need the source code to programm the PIC for the communication and also what u think is necessary. Thank youuu

  18. Syd says:

    Great job! As you said Avr-cdc violates some usb standard plus it also require drivers, but avr cdc can be used as universal usb2uart bridge so that we can communicate any MCU having uart with Pc using serial terminal. So I request you if you can make something similar like a simple usb2uart converter(using attiny/mega) with HID class and ur above cross platform software(no virtual com port). Is it possible?

    • ray says:

      It’s definitely possible (use avr-cdc as a starting point). But right now I don’t have any spare time to work on this. Plus, the HID serial work provided a great inspiration for me to learn V-USB. As for CDC serial, there are already existing resources so I feel less inspired because it’s more like a re-working of existing code. Not a good reason to dodge the work, but hope it’s understandable :)

Leave a Reply