Hardware Volume Controller

 On 2013-05-11 and filed under: volume control hardware atmega32u4 lufa

(5/11/13) - Hey fantastic reader: This is an old how-to (from 4/2/12!) that existed in its own self-contained repository. Reflecting back, this hack was kind of silly, but i’m porting it here to keep everything in one place.

And if you’re curious about the long-term viability of this build.. it is still kicking. The detents are a little less crisp now, but everything works a treat. (And now, back to your regularly scheduled post re-run!)

It wasn’t too long ago that volume control on a keyboard was a foreign concept. After it became a ‘feature’, that control seemed pretty normal. There is however, a slight modern exception now. Many mechanical keyboards (which have been coming back into style) don’t have them.


In response, I hacked this together. It’s a volume controller which parses a rotary encoder’s input and controls the volume (tested on OS X/Windows/Linux so far).

Parts/Bill of Materials:

  • Altoids Gum sized tin - A slick enclosure. Scratches easily though.

  • Rotary Encoder - I’ve tried a bunch. A lot of them either failed over time, or the ‘clicks’ (detents) became mushy. This one comes with a cool looking knob and is panel mountable too.

  • Adafruit Atmega32u4 Breakout - Brains of the operation. Comes with headers. For this particular project, just keep the headers for a rainy day.

  • Wire - Will need these to hook up the rotary encoder. Stranded wire is preferable since it can bend and flex.

  • Perfboard - While you can go without, the perfboard is nice to relieve the stress on the rotary encoder pins. I haven’t run across an economical source of cuttable copper-clad perf; they’re usually so large that I feel guilty slicing them apart. These seem to be the right size, but are rather thick (I haven’t sliced these apart yet). Use anything that works.

Optional (or you already have these if you’re thinking this is a good weekend project):

  • Sugru - I used this stuff to attach the board to the tin. Anything non-conductive works (determine the amount of adhesion based on your projected re-use of these parts).

  • Soldering iron + Solder - Needed to join all the parts together.

  • Mini-B USB cable - This is the cable type that the breakout board accepts.

  • Drill/set of bits - To make a hole so that the encoder can poke out of.

  • Something to cut with - For perfboard cutting, wire stripping, prying the enclosure, etc.

Electronics assembly

Start by cutting perf for the rotary encoder. Put on your safety glasses (and quite possibly a dust mask just to be extra cautious). Cutting perfboard (and other things too!) can cause pieces of it to fly into your eyeballs.

To cut the perfboard by hand, score 5-7 times along the holes that mark the edges of the cut (make sure to dry fit before cutting). I had better cuts by scoring along the entire length of the perf (although this means some perf ends up being wasted).

Once you’re ready to cut, take a pair of shears (dull diagonal cutters work too) and clip a notch where your scored lines meet the edges. This should serve as a guide for your snap (the concept is similar to tearing a notch in a piece of folded paper before ripping it apart cleanly). Slowly bend the perf where the score is and the perf should snap apart. If it resists or seems like it won’t snap along the score line, score some more and repeat.

If that fails, break out a dust mask, a pair of safety glasses (you should already be wearing these!), and just start hacksawing. It won’t be very pretty, but it should work.

The next step is to solder the encoder in. Insert the encoder and tack a leg with solder to hold it in place. Ensure everything is aligned the way you like it (check that there is room for the wiring below), and solder the rest of the pins in.

The wiring can be deceptively tricky. Measure out enough wire so that you can open/close the box. Cut, strip and tin the wires. You’ll need 5 wires.

Here is the wire mapping (looking from top to down at the rotary encoder, with three pins on top and two on the bottom):

Volume Up/Down (three pins, top)

Wire color Pin on Atmega32u4 Pin on encoder
White D0 Left-most pin
Black GND Middle pin
Red D1 Right-most pin

Mute On/Off (two pins, bottom)

Wire color Pin on Atmega32u4 Pin on encoder
Black GND Left-most pin
White B0 Right-most pin

Solder all the wires together. Once the wires are attached to the perf, blob some solder to bridge the wiring and the encoder pins together.

encoder wiring


To drill a hole for the encoder, find a bit size that matches (the datasheet says it is 6mm / .236 inches or just around a 1564 drill bit (.234 inches)). Measure for dead center on both axes (or like a professor once told me: ‘eyeball it until your OCD stops’). Mark the drill point.

If it makes it easier, it is possible to remove the lid from the body. Simply bend the hinged tabs outward and the lid will slide off. Reverse to fit the lid back on.

You may need to brace the back of the drill area with a block of scrap wood to prevent warping. Drill from the outside in (so that all the unsightly edges are hidden somewhat). Don’t apply too much pressure (you won’t be able to undent things easily).

Once you’re done, dry fit the encoder but don’t screw it into place yet.


For the USB port, buttress the breakout board against the bottom of the enclosure and mark an approximate outline. Using a drill, begin drilling into the case (the hole won’t be large enough, but we’ll fix that in a bit). With the hole made, take your shears and carefully pry to make an opening wide enough for the USB plug to poke out. Continue to check the fit while slowly prying. Don’t worry if it looks too ugly. The USB plug covers up most of the mess.

Final assembly

Before adhering the breakout board (permanently, depending on your preference); lets test everything.


Unfortunately I haven’t had all that much luck compiling on OS X (using Homebrew packages), and I haven’t tried on Windows at all. Only Linux has worked so far. All hope is not lost however, you can manually program the breakout with avrdude! Read on to ‘Skipping all of that’.

These are the packages I have installed for Fedora which are needed to compile:

$ sudo yum -y install avr-libc avrdude avr-gcc avr-binutils git

Next, grab a copy of the LUFA git repository that includes the necessary modifications:

$ git clone git://github.com/davidk/lufa-lib.git
$ cd lufa-lib
$ git checkout -b atmega32u4-mediacontroller origin/atmega32u4-mediacontroller
$ cd trunk/Projects/MediaController
$ make all

If the compile was successful, connect the breakout board to your computer. Type the following into your console, hit enter, but don’t type in your password yet.

$ sudo make program

Going back to the breakout board, hit the white button (a green light should slowly start fading in and out indicating that the breakout is ready to be programmed). Type in your password and watch it go. If avrdude had problems finding your 32u4 breakout, run dmesg and check the output for something along the lines of ttyACM. The last part should be a number. Modify the makefile with the new port and re-run the line.

Here is what my dmesg looks like:


Skipping all of that:

If you’re on OS X or Windows.. or just don’t like compiling, get the hex file, press the white bootloader button, and run the following (a working example for OS X):

$ avrdude -p atmega32u4 -P /dev/tty.usbmodem621 -c avr109 -U flash:w:MediaController.hex

Change /dev/tty.usbmodem621 as appropriate for your platform. On OS X, if at first /dev/tty.usbmodem612 doesn’t work, use tab-completion to fill in the remaining section. Enter /dev/tty.usb and tap tab to get relevant entries when the bootloader light is active.

Finishing up: Do volume up/down and mute (pushing down on the encoder) work properly? If so, time to finish up! If not, make sure that the board is programmed (it shows up as LUFA Media Controller in dmesg, and similarly under the OS X System Profiler). Also ensure the connections between all the parts are nice and solid.

If everything is finished, open a pack of Sugru (or the adhesive you’re using), and stick things together. Next, poke the rotary encoder through the hole on the lid. Then secure it with the hardware provided and add the nice looking control knob to top it off.

Here is what mine looks like now; it is magnetically attached to the keyboard (no ill effects yet).



This project uses code and examples provided by LUFA (which is MIT licensed). Thats by Dean Camera. The code to read the rotary code comes from a posting by circuitsathome here.

Improvements, chaos, and cost reductions:

From a performance and cost perspective, the breakout used for this project is overkill; building an ATTiny breakout might be more worthwhile. Also, it might be possible to just rip the guts out of an old keyboard and use its media controller functions with some modding.

AVR Programming with Adafruit's Atmega32u4

 On 2012-10-01 and filed under: avr programming lufa usb avrisp


This guide will help you build your own LUFA AVRISP-MKII clone using the Adafruit Atmega32u4 breakout board.

Build Materials


You will need Linux as a base for programming and compiling the LUFA project’s code. Using another operating system is possible, but won’t be covered here (sorry!) If you don’t have a dedicated Linux installation, try a Linux live CD and install the packages below to program.

Installation of AVR tools are provided for the Ubuntu and Fedora Linux distributions.

Adafruit Atmega32u4 breakout

A modified LUFA CDC bootloader comes pre-installed on these. This allows the chip to be programmed without an external programmer (at the cost of some memory space).

Atmega32u4 with ISP and programming reset line connected

For programming breakout boards, a 6-pin cable is attached to the programmer. A wire is hooked up to B4 (which connects to the target AVR’s RESET pin).

Note that without modification, the RESET line will trigger on the 6-pin cable as a part of the programming process through B4. Since the 6-pin cable connects RESET between both target and programmer, the programmer itself will reset when B4 is asserted (which ends the attempt). This gets worked around a little bit later by splicing into the wire.

This method is mostly optional, but useful since some breakouts are only programmed easily through the header.

Breadboard, wires, and maybe a ZIF socket

It is also possible to use a breadboard to program a chip. A completed layout looks something like this.

Breadboard Atmega32u4 ISP programmer targeting ATmega328P

There are problems with this setup:

  1. Removing the programmed chip may be difficult/annoying. A ZIF (zero insertion force) socket may be appropriate if programming a large quantity of chips.

  2. Depending on fuse settings, the recovery clock (or, as seen in the picture, a resonator) may be needed in order to program the chip properly. Breakout boards usually have some sort of clock source installed.

For most, it may be better to program using a dedicated PCB that has the 6-pin header broken out. This might be an Arduino for Atmega328Ps, or the BB313 for ATTiny chips.

Here are the necessary connections from the Atmega32u4 breakout to the chip being programmed (if using a breadboard):

Connections for breadboard

32u4 -to-> Target chip
B1   ----> SCK    (Arduino Digital Pin: 13)
B3   ----> MISO   (Arduino Digital Pin: 12)
B2   ----> MOSI   (Arduino Digital Pin: 11)
B4   ----> RESET  (Arduino Analog  Pin: A6)

GND  ----> GND    (Note: 1)
VCC  ----> VCC    (Note: 1)

Recovery Clock    (Note: 2)
B5   ----> XTAL1  (not needed for Arduino boards)

1. There may be extra tie in points for these.
   Check the datasheet to be sure.
2. The recovery clock can be used if your fuses are set incorrectly.

   If using the recovery clock, be sure to set the programing speed to 125KHz
   1/125000Hz = 8x10^-6 seconds (or 8 microseconds, since 1 microsecond -> 1x10^-6 seconds)  
   For avrdude add this as a parameter to set the microsecond bitclock period:
            -B 8
  • Optional (for a more compact setup): 6-pin IDC cable

A 6-pin cable will stop the programmer from becoming a bundled mess of wires. This works since most of the necessary pins (MISO/SCK/VCC/MOSI/GND) are broken out to the header. However, since both RESET lines are connected, the programmer itself will be reset if connected without modification.

Modified 6-pin IDC cable

Here is an example of the 6-pin cable which has been cut with a sharp knife. The line to cut is the 5th wire, starting from the red line as shown in the picture. If you aren’t sure, verify with a multimeter that the line is indeed the one used for RESET.

To verify: With your spare Arduino or other breakout board, just plug a wire into the RESET line (connected to a lead on your multimeter in continuity test mode – aka ‘beep beep beep’ mode), and connect the 6-pin header (red stripe to small dot, or box connector “tab” to the proper place). Poke the small wires coming out of the box to check. You may need to press with a bit of force.

Probing 6-pin IDC cable

For the absolutely paranoid, pulling apart the box connector may be a good idea.

Other ways around cutting up your cable: fuse manipulation, designing your own PCB (as some have), cutting header pins, etc. If you end up using this particular method, soldering up a detachable wire to the cable’s RESET (then connecting to the programmer’s B4 pin) might be a worthwhile project.

  • Female/Female jumper wire(s) (Female/Male, Male/Male, etc)

A wire to connect the reset line (B4) and other pins. You might need a single piece of male header to tie the jumper wire to your destination board. The RESET line is triggered using this connection.

Finding your board


I’m using Linux, so when I plug in the board this shows up after typing dmesg into a terminal:

[15138.776119] usb 3-1: new full-speed USB device number 51 using xhci_hcd
[15138.791800] usb 3-1: New USB device found, idVendor=239a, idProduct=0001
[15138.791806] usb 3-1: New USB device strings: Mfr=0, Product=1, SerialNumber=0
[15138.791808] usb 3-1: Product: AVR CDC Bootloader
[15138.793350] cdc_acm 3-1:1.0: ttyACM0: USB ACM device

ttyACM0: USB ACM device on the last line is where data gets accepted. There are a myriad of other ways to figure that out, but parsing dmesg will tell us if it failed to enumerate somehow.

Download LUFA (Linux)

The next step from here is to download the files necessary to load the programmer. Run the following (after acquiring git [1]).

$ git clone https://github.com/davidk/lufa.git
Cloning into 'lufa'...
remote: Counting objects: 52669, done.
remote: Compressing objects: 100% (10637/10637), done.
remote: Total 52669 (delta 42901), reused 51050 (delta 41412)
Receiving objects: 100% (52669/52669), 9.49 MiB | 497 KiB/s, done.
Resolving deltas: 100% (42901/42901), done.

This is a small fork of Dean Camera’s LUFA project (a framework for developing USB-enabled AVRs). Of interest is Dean’s clean-room copy of the AVRISP-MKII programmer, which turns the Atmega32u4 breakout into a programmer for other AVRs (egg, meet chicken). Run the following from outside the lufa directory.

$ cd lufa/Projects/AVRISP-MKII/
$ git checkout atmega32u4-avrisp
  Branch atmega32u4-avrisp set up to track remote branch
  atmega32u4-avrisp from origin.
  Switched to a new branch 'atmega32u4-avrisp'

Once you’re in the branch, ensure that your avr utilities are installed.

For Fedora users:

$ sudo yum install avr-binutils avr-gcc avr-libc avrdude

Ubuntu users:

$ sudo aptitude install gcc-avr avr-libc avrdude

Programming (check to see if your boot light is fading in and out, if not, press the white button and run the following):

$ sudo make avrdude AVRDUDE_PROGRAMMER=avr109 AVRDUDE_PORT=/dev/ttyACM0

Connect the programmer

If it all went well, you now have a programmer.

ISP programming Arduino Uno

Remember: The single red wire goes from B4 to the Arduino’s RESET pin.

Start your engines

At this point, all the hard work is done! Before programming, make sure that the RESET line is connected to B4. avrdude can be used to program a board by running it as such:

# For Atmega328P chips
$ sudo avrdude -patmega328p -cavrispmkii -Pusb -Uflash:w:Blink.cpp.hex

# For an Attiny4313
$ sudo avrdude -pattiny4313 -cavrispmkii -Pusb -Uflash:w:blinky.hex

An example session (targeting an Attiny4313 with the blink program from the bb313 website):

$ sudo avrdude -pattiny4313 -cavrispmkii -Pusb -Uflash:w:blinky.hex
[sudo] password for davidk: hunter2

avrdude: AVR device initialized and ready to accept instructions

Reading | ################################################## | 100% 0.00s

avrdude: Device signature = 0x1e920d
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 "blinky.hex"
avrdude: input file blinky.hex auto detected as Intel Hex
avrdude: writing flash (60 bytes):

Writing | ################################################## | 100% 0.02s

avrdude: 60 bytes of flash written
avrdude: verifying flash memory against blinky.hex:
avrdude: load data flash data from input file blinky.hex:
avrdude: input file blinky.hex auto detected as Intel Hex
avrdude: input file blinky.hex contains 60 bytes
avrdude: reading on-chip flash data:

Reading | ################################################## | 100% 0.02s

avrdude: verifying ...
avrdude: 60 bytes of flash verified

avrdude: safemode: Fuses OK

avrdude done.  Thank you.

programming a bb313

Programming without using ‘sudo’ or root (Linux)

Running avrdude as root is kind of excessive, even temporarily. To run it as a normal user, we can eck out the following to make it work properly:

First run groups to get the list of groups your user currently belongs to:

$ groups
davidk dialout wheel

This determines what we set in the GROUP=“” field below (in this case i’ve picked wheel):

$ sudo su
# echo 'ATTR{idVendor}=="03eb", ATTR{idProduct}=="2104", GROUP="wheel", MODE="0666"'
>> /etc/udev/rules.d/60-avrispmkii.rules
# udevadm trigger


Special thanks to Dean Camera, who saw that I was making changes to my fork on Github (which is a mirror of his SVN repo) and stopped by my lowly branch to suggest a small change. Good guy (he did it again too, on an unrelated branch).


Tack on something here. I will try to help or spin you in the right direction.


[1] Get git via your preferred package manager, or check out http://git-scm.com.


01-15-13: Added information on using avrdude in a non-root fashion for Linux. Also threw in some magic to make console stuff pretty.