Introduction: Standalone WiFi Radio Control Panel (Arduino-Powered)

I wanted an Internet Radio for a long time, and was delighted to find Tinkernut's Wifi Radio project ( ), which I built and have enjoyed for a few months. 

However, I didn't really care for the Ario based control interface, which required a computer up and running to start, stop, or change the channel.  I wanted to build a stand-alone controller, and found MightyOhm's Wifi Radio ( ), on which the Tinkernut project is based.  MightyOhm's control panel looks very nice, but it seemed more involved than I was ready for.  I don't have a non-Arduino AVR programmer, and I wanted to use things I had on hand and avoid opening up my router if possible.  Please note that this is an intermediate project, which will require basic multimeter experimentation to determine how the rotary encoder functions, and intermediate breadboarding, since there are a lot of connections involved.

So, using an Arduino-compatible controller board (Adafruit's Boardiuno , actually), a standard Arduino Ethernet Shield , a 16x2 character LCD, and a rotary encoder knob, I was able to quickly put together a controller that could select and play from a number of preset channels, turn off the radio, and display station, artist, and title information for the currently playing station and song.

Step 1: Parts

For the radio:
First, you need some kind of MPD-based WiFi radio.  I recommend the following projects, an OpenWRT-based WiFi radio, such as Tinkernut's ( ) or MightyOhm's ( , on which Tinkernut's is based), or a laptop running Music Player Daemon (mpd: )

For the Controller:
* Arduino-compatible microcontroller platform (such as Arduino UNO , I used my Boarduino )
* Arduino Ethernet library compatible network controller (such as the Arduino Ethernet Shield )
* LCD Character Display (16x2 characters, or larger, such as this one from Adafruit , I got one from Ebay )
* Absolute Rotary Encoder (mine has 10 positions) (for choosing the station)
* Header pins
* Hookup wire
* 0.1" female wire connectors of varying degree (optional), which can help when putting everything in the enclosure
* breadboard
* ~7.5-9V wall wart power adaptor

* one cardboard box that has a lid and is big enough for the breadboard, or some other enclosure you can modify to hold the selector knob and LCD, and cut ports for the Ethernet and power.

Step 2: Schematic

This is the complete schematic for the entire project.  The design and reasoning behind each of the parts are explained in the subsequent steps, but there are a few system-wide design factors I wanted to mention here.

Since the LCD and Ethernet Shield together take up so many I/O pins, and since I wanted to leave D0 and D1 unconnected so they wouldn't interfere with serial debugging, I ended up using 4 of the analog inputs (A0-A3) as digital inputs.  To do this, just treat them normally as Digital I/O pins, with A0-A5 corresponding to 14-19 for normal pin operations (pinMode, digitalWrite, digitalRead, etc.).

Step 3: Build WiFi Radio

Set up the radio as described by tinkernut .
Make sure you can control it from a remote host using mpc or Ario or telnet. 

To test with telnet, open a command prompt, and enter the following command sequence.  The commands are in bold, followed by the responses in normal weight text.  It assumes your radio has the IP address, substitute with the correct one if necessary.

C:\>telnet 6600
OK MPD 0.15.0

You should hear one of my favorite stations playing.  If it doesn't work, you'll have to troubleshoot, which is beyond the scope of this instructable, but the MPD and OpenWRT documentation is your friend.

You can see some pictures of the radio I have, and the cabinet I'm using, before I had built the control panel.

Step 4: Make and Code Rotary Encoder Input Subsystem

It took me some time to figure the correct pinout of my rotary encoder, since I couldn't find a datasheet.  It's an absolute rotary encoder that has 10 positions, and 5 unlabeled pins.  To figure out how it worked, I had to get out my continuity tester and construct a truth table.

Starting at what I supposed was the top, I checked all pins for continuity.  Since it was supposed to be a simple encoder, I supposed (correctly) that there would not be two disjoint sets of connected pins for a given encoder position.  I therefore made a map like the following, noting 0 for pins that did not connect, and 1 for pins that were connected. 

Position:  Connectivity for pins 12345)
0 - 00000
1 - 01100
2 - 10100
3 - 11100
4 - 00101
5 - 01101
6 - 10101
7 - 11101
8 - 00110
9 - 01110

Knowing that one pin had to be common, I looked for the only pin that was connected for all but the "0" position, which happened to be pin 3, in the center.  I then tried to discern some pattern to the other connected numbers, and as you can see, it became evident that the left two pins were the two least significant bits, and the right two pins were the two most significant bits (in both pairs, the more significant bits were to the left).  In other words, with pin 3 as common, I wanted to determine the integer value comprising the pins 4512.

To read this value in, I had to wire those pins to digital inputs of the Arduino, and then read the values and shift them into a byte.  Since the contacts don't always change immediately to the expected values when changing the selection of the encoder, I added some debounce code.  The following will report the state of the encoder after it has remained unchanged for DEBOUNCE_TIME milliseconds (in this case, 10).  Note the following wiring, from the schematic:
encoder pin 2 -> ENC_BIT_0
encoder pin 1 -> ENC_BIT_1
encoder pin 5 -> ENC_BIT_2
encoder pin 4 -> ENC_BIT_3

// four bits used by the rotary encoder
// the analog pins can actually be used as digital IO,
// just refer to the numbers A0-A5 as their digital equivalents: 14-19
#define ENC_BIT_0 14
#define ENC_BIT_1 15
#define ENC_BIT_2 16
#define ENC_BIT_3 17

// duration of debounce delay, in milliseconds
// i.e., time that input value must remain consistent
// before it is counted as a new "input"
#define DEBOUNCE_TIME 10
uint8_t debounceEncoder() {
  static uint32_t timer = 0;
  static uint8_t debounced = false;
  static uint8_t currentValue = 0;

  uint8_t bit0 = !digitalRead(ENC_BIT_0);
  uint8_t bit1 = !digitalRead(ENC_BIT_1);
  uint8_t bit2 = !digitalRead(ENC_BIT_2);
  uint8_t bit3 = !digitalRead(ENC_BIT_3);
  uint8_t newValue = 0;
  newValue = (bit3 << 3) | (bit2 << 2) | (bit1 << 1) | bit0;
  if (newValue != currentValue && debounced) {
    timer = millis() + DEBOUNCE_TIME;
    debounced = false;
  if (millis() > timer && !debounced) {
    currentValue = newValue;
    debounced = true;
  return currentValue;
// ------end------

Step 5: Add Ethernet Interface

If you're using an Arduino with a standard form-factor (such as the Uno) and the standard Ethernet Shield with the same form factor, then hooking up the ethernet interface is as simple as plugging the shield into the Arduino main board.

In my case, using the Boarduino, I had to wire it by hand.  Since the Ethernet Shield does not use all pins, I only had to wire the important ones.  You can see in the schematic, the ones that count are:

5V (both of them)
GND (all of them)
pins 9-13 (for SPI and control)

That was enough to get the Ethernet example sketches up and running on my breadboard.

Step 6: Add LCD

Using an LCD can be kind of a pain, because they require so many pins.  Fortunately, LadyAda has a great tutorial about driving a Character LCD from an Arduino , which really helps.  I was rather longing for one of their I2C-capable LCD backpacks , since it would require only two I/O pins to drive the LCD, but I didn't have one or the parts on hand. 

I did not have any potentiometers to use for the contrast adjustment, but I have read several places that just tying that pin to ground works fine.

If you want a condensed version of the tutorial, here are the pin hookups of the 16-pin LCD header (or 14-pin, if yours doesn't have the LED backlight).  Note how the data input pin names kind of match the Arduino digital input pin numbers (by name), which was completely serendipitous:

Arduino pin - LCD pin
5V - 2, 15
GND - 1, 3, 5
D2 - 4 (RS)
D3 - 6 (E)
D4 - 11 (DB4)
D5 - 12 (DB5)
D6 - 13 (DB6)
D7 - 14 (DB7)

Step 7: Scrolling on the LCD

Rather few of the station names and song artist and title strings fit within the 16-character limit of the LCD, and so I wanted some kind of nice-looking automatic scroller for strings that were too long, one that was independent per-line (so a short station name would not be waiting for a long artist and title to scroll), and one that could easily be extended to handle different-sized LCDs (with more or fewer lines or columns).

I was able to design such a scroller, but it is beyond the scope of this instructable.  For details about its design and operation, see this LCD Scroller writeup I put together, or look at the code (in the final step).

Step 8: Put Into Enclosure

The final step is to cut the holes in the cardboard box and put everything into the enclosure.  As you can see, I was able to position things on the breadboard so that they were up against the sides of the box, and it was a simple matter to cut holes of the proper size.

Step 9: Software

Attached is the Arduino sketch I used to power this control panel.  I tried to document the code thoroughly.  I used Arduino-0018, which is a bit out-of-date, but I imagine it would build just fine with newer versions.

[Update]:  I have moved the code (with improving documentation, and bugfixes) to my github repository . Please look there for the most recent version.

Step 10: Future Improvements

There are many possible improvements I hope to keep working on for this project.  See my blog for details about the design and implementation, and any updates, for this Radio Control Panel project .

Some things that would be nice
* use all 4 lines of the LCD (mine is 16x4, but I am only using the top two)
* power down the LCD in stop mode (to indicate that it is stopped, and to save power)
* add potentiometer for contrast adjust (could remain inside the enclosure)
* fix DC power problem (currently, it doesn't run on DC power, which might be a problem with my Boarduino.  I still have to debug this.)
* build integrated Ethernet controller, e.g., , with breakout shield for encoder and LCD
* use Adafruit's I2C backpack for LCD, to simplify the schematic

Microcontroller Contest

Participated in the
Microcontroller Contest