User Tools

Site Tools


This is an old revision of the document!

AVR Programmer built from a USB Keyboard

Recently, my old keyboard became louder and louder while typing, so I decided to buy a new one. But what to do with the old one? Ever since I saw the AVR programmer that uses a USB hub, I thought of abusing a USB keyboard for that.


Most keyboards have at least three indicator LEDs (num lock, caps lock and scroll lock), which can be controlled from the host using a HID Set_Report request, and thus can be used as general purpose outputs. The inputs are a bit more tricky, since the keyboard uses a scan matrix divided in rows and columns. Most keyboards also do some debouncing and detect rows that are 'stuck', which means we need to simulate the keypress of a single key. If a key has been pressed, the keyboard triggers an interrupt transfer with 8 bytes of data, containing the current state of all keys. The first byte reflects the state of the modificator keys (shift, ctrl, alt etc.), which I'll be using as inputs. Additionally, the keys can be polled using a Get_Report request to the control endpoint, but the interrupt transfers need to be handled either way, since my keyboard just locked up after the first keypress when I didn't handle them first.


The keyboard I've been using is a Microsoft Digital Media Keyboard 3000, which contains the following PCB:

The fourth LED (function lock) can't be controlled from the host (although the HID specification allows two more LEDs, 'Compose' and 'Kana').

For emulating a single keypress, I'm using an optocoupler to connect the row and column of that key. Most switching PSUs use an optocoupler for the feedback loop, so I salvaged mine (KB817-B) from an old mobile phone charger:

And that's basically all that's needed, here's the finished setup:

You can see the keyboard controller PCB, the optocoupler, and an Atmel ATtiny2313 microcontroller. I desoldered the LEDs and directly connected the 5V outputs (since there were already 330Ω current limiting resistors in place) as follows:

Num lockSCLout
Caps lockMOSIout
Scroll lockResetout
row/column of right shift keyMISOin

For the optocoupler I added a 470Ω current limiting resistor.


First I wrote a small libusb-based test utility for experimenting with the keyboard. I'm directly communicating with the keyboard from userspace, unloading the kernel driver first.


Just toggling the LEDs in a while-loop gives a signal fluctuating between 500 Hz and 1.2 kHz on the output, which is faster than I assumed. Unfortunately, the input-part is much worse, since the rows are only scanned with 250 Hz on my keyboard:

In addition to that, the keyboard seems to debounce the keys, which means that in the worst case it takes 40ms for the host to know the state of the input. That's quite a bummer, since it drastically limits the not-so-bad output speed.

AVR Programmer

After some initial tests, I wrote an interface driver for avrdude. The code can be found on github:, and can be cloned with:

git clone git://

So far this has only been tested with Linux, but it should work fine on Windows and OS X as well.


$ ./avrdude -c hidkey -C avrdude.conf -pattiny2313 -U flash:w:main.hex:i -V

avrdude: AVR device initialized and ready to accept instructions

Reading | ################################################## | 100% 4.17s

avrdude: Device signature = 0x1e910a
avrdude: NOTE: "flash" memory has been specified, an erase cycle will be performed
         To disable this feature, specify the -D option.
avrdude: erasing chip
avrdude: reading input file "/home/steve/dev/hid_gpio/avr_demo/main.hex"
avrdude: writing flash (86 bytes):

Writing | ################################################## | 100% 123.79s

avrdude: 86 bytes of flash written
avrdude: verifying flash memory against /home/steve/dev/hid_gpio/avr_demo/main.hex:

Well, as you can see, programming takes quite a while, mainly because of the slow input speed I mentioned before, but is very reliable. Since I assume that many people have some old USB keyboard laying around collecting dust, it still could be used as a bootstrap flasher for solving the old chicken-and-egg problem when building a faster programmer like the USBasp, or as low-speed general purpose output for other applications. For example, many outputs could be driven using a simple serial-in, parallel-out shift register.

Reference Literature


Eduardo Capocchi, 2012/11/26 14:45

Great stuff, thanks for sharing. Will try this out!

Miklós Márton, 2012/11/26 19:27

That is brilliant man! Congratulations!

Mauro Scomparin, 2012/11/26 20:12

Really nice! Great idea!

Ondrej Kolonicny, 2012/11/29 08:50

Hi, great project. Can you post somewhere your libusb-based test utility ??


Steve M., 2012/11/30 19:04

Yes, I cleaned it up a bit and backported the new code I wrote for avrdude. You can get it here. The VID/PID of your keyboard need to be defined at compile time though, see the USB_VID and USB_PID defines in main.c.

Ondrej Kolonicny, 2012/12/03 15:53

Hi Steve, thank you, but i can't compile it on Ubuntu. I got:

cc -Wall -O2 `pkg-config –cflags libusb-1.0` -c -o main.o main.c main.c:125: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘attribute’ before ‘irq_cb’ main.c: In function ‘hidkey_alloc_transfers’: main.c:150: error: ‘irq_cb’ undeclared (first use in this function) main.c:150: error: (Each undeclared identifier is reported only once main.c:150: error: for each function it appears in.)

Any idea ??

Steve M., 2012/12/03 16:23

Seems that you have an older version of libusb-1.0 which lacks the LIBUSB_CALL macro. I pushed a small change, can you try to build it again?

Esad Delic-Ibukic, 2012/12/06 18:07

Great idea! Congratulations!

zex, 2012/12/08 19:22

Since the optocoupler is rated at 20 miliamps and between 1.2v and 1.4v as ideal operating condition, isn't a 470ohm resistor waaay to much? Shouldn't it be in the 180 to 190 range?

Steve M., 2012/12/12 20:17

Well, I had a bunch of 470Ω nearby already, so I just used that one ;) But according to Fig. 11 in the datasheet everything is fine with that, the phototransistor reaches saturation even for If=7.5mA, and that's about what you get assuming Vf=1.2V and the output voltage of the AVR being 5V.

vishu, 2012/12/19 18:10

I need to ask you a question. I have a Atmega 328P lying around since they are not bootloaded. Can I load a bootloader on to them and using your method? I have all the parts you have used (USB keyboard and Phone Charger).

Steve M., 2012/12/22 16:24

Sure, either build a programmer first, like USBasp, so you can flash the other chips faster, or install a USB or serial bootloader, there are quite a few around. As USB bootloader, I can recommend BootloadHID.

nag, 2013/01/24 13:27

Hi Steve,would you please send the programmer suited for windows xp

Steve M., 2013/01/24 14:12

Sorry, I don't have a prebuilt version for Windows, you have to build it yourself. A quick search on Google returned the following link, which might be helpful:

matrixstorm, 2013/06/11 02:33


Great idea. Recently I had some pretty similar idea using a keyboard.

It is not limited to USB only and is used by controling keyboard LEDs: (

Best regards

max, 2013/09/05 22:25

I am trying to reapeat your experiment. Could you also post what to do after cloning github? Trying to compile it with command line provided by Ondrej Kolonicny complains about missing file “ac_cfg.h”. Thanx

Enter your comment
hidkey_gpio.1353880495.txt.gz · Last modified: 2012/11/25 22:54 by steve_m