Friday, May 22, 2015

Arduino speaker controller

I've had this project in mind for a while, and finally got around to it. I wanted a way to turn on or off the various speakers around the house. In addition to the front room, where the main stereo is, we have speakers in the dining room, hot tub area, and back patio. Previously, I could turn on the amp, pick a playlist from Kodi, and make it play all from my phone, but if I was outside on the patio, I had to go back in the house and push a button to turn on the speakers. No more! Now I have a simple web app and an Arduino controller to turn on the speakers.

 I got a lot of inspiration and ideas for this project from There is even a link to a nice looking Android app to control the speaker relays, but I couldn't get it to work, so I just did a little web app that runs on Arduino to control the speakers. I got just about all the parts from Amazon:

Here is the wiring to control the relay board. The brown wire on the left connects to the 9V output on the Arduino to power the relays themselves. The black wire on the right is the ground, and is common to the board and the relays. The white wire connects to the 5V output on the Arduino to power the relay circuit board.

A close up of the connections on the relay board. Again, the brown wire on the right is connected to the 9V output on the Arduino and powers the relays themselves, while the white wire is connected to the 5V output on the Arduino to power the relay circuit board. The other wires are connected to pins 2 through 9 on the Arduino to actually trigger the relays.

Here are the connections on the Arduino side. You can see the 5V white wire, the black ground wire, and the 9V brown wire. The other wires are connected to pins 2 though 9 to control the 8 relays.

Just a big picture of the completed wiring between the Arduino and the relay board

A shot of everything in the box. My dremel skills aren't the best, but this will be in a cabinet and essentially hidden from view, so it's okay. There are cut outs for the ethernet, USB, and power connections.

The speakers will connect here. It might have been better if I'd found similar connectors that were all black. For this use, each speaker gets two connectors. The negative wire is cut, then one of the cut ends goes in the top row of connectors and the other cut end connects to the corresponding connector in the bottom row. The relay then completes the circuit. These are installed right to left, so the right most connector is controlled by relay 1, and the left most connector is controlled by relay 8. I did it this way so it would be easier to wire into my stereo cabinet. Note that this piece is one of about 6 that make all the speakers work. 

A shot of the inside. It looks messy, but it's pretty straightforward.

The code is a simple web server. The IP address is hard-coded to so it's easy to bookmark in my phone's browser. The web page it produces looks like this, both on my phone and on my desktop browser:

Pretty plain, but it does the right thing.

Simple web server to serve a form to turn on or off digital pins. In this case,
the pins are connected to relays to turn speakers on or off. This uses pins
2 through 9 in pairs, so 2 and 3 control the hot tub speakers, 4 and 5 control
the dining room speakers, and 6 and 7 control the portico speakers. 8 and 9 are
available in case I ever find a need to connect one more pair of speakers.

#include <SPI.h>
#include <Ethernet.h>
#include <string.h>

// MAC address can be anything that is unique within the local network.
byte mac[] = {0x00, 0x1E, 0x2A, 0x77, 0x24, 0x02 };

// Some unused IP address on the local network.
byte ip[] = {192, 168, 2, 251 }; 

// web server, nothing fancy, just port 80 for http
EthernetServer server( 80 ); 

#define RELAY_ON 0
#define RELAY_OFF 1

// true, just show speaker status or false, actually change the speaker states
boolean showStatus = false;

// which zones are on or off, initially, all are off
boolean zone1 = false;
boolean zone2 = false;
boolean zone3 = false;
boolean zone4 = false;

// pin definition, one pin per speaker, 
// so the left speaker in zone 1 is pin 2, etc
static const int zone1L = 2;
static const int zone1R = 3;
static const int zone2L = 4;
static const int zone2R = 5;
static const int zone3L = 6;
static const int zone3R = 7;
static const int zone4L = 8;
static const int zone4R = 9;

// set up pins, initially all speakers in all zones are off
void setupPins() {
    digitalWrite(zone1L, RELAY_OFF);
    digitalWrite(zone1R, RELAY_OFF);
    digitalWrite(zone2L, RELAY_OFF);
    digitalWrite(zone2R, RELAY_OFF);
    digitalWrite(zone3L, RELAY_OFF);
    digitalWrite(zone3R, RELAY_OFF);
    digitalWrite(zone4L, RELAY_OFF);
    digitalWrite(zone4R, RELAY_OFF);

    pinMode( zone1L, OUTPUT );
    pinMode( zone1R, OUTPUT );
    pinMode( zone2L, OUTPUT );
    pinMode( zone2R, OUTPUT );
    pinMode( zone3L, OUTPUT );
    pinMode( zone3R, OUTPUT );
    pinMode( zone4L, OUTPUT );
    pinMode( zone4R, OUTPUT );

void setup() {
    Ethernet.begin( mac, ip );


// set up buffer for reading web requests
static const int bufferMax = 128;
int bufferSize;
char buffer[ bufferMax ];

void loop() {
    EthernetClient client = server.available();
    if ( client ) {
        waitForRequest( client );

void waitForRequest( EthernetClient client ) 
    bufferSize = 0;

    while ( client.connected() ) {
        if ( client.available() ) {
            char c =;
            if ( c == '\n' ) {
            else {
                if ( bufferSize < bufferMax ) {
                    buffer[ bufferSize++ ] = c;
                else {

void handleRequest() {
    // Received buffer contains a standard HTTP GET line, something like
    // "GET /?X=X&Y=Y HTTP/1.1". 
    // Could have up to 4 parameters, 1=on&2=on&3=on&4=on,
    // one for each set of speakers. All that is necessary here is to
    // extract the query string and check for each of the zone numbers.
    String request = String(buffer);
    int firstSpace = request.indexOf(" ");   // right after GET
    int lastSpace = request.indexOf(" ", firstSpace + 1); // just after the query string
    request = request.substring(firstSpace, lastSpace);
    showStatus = request.indexOf("?") == -1;
    zone1 = request.indexOf("1") > -1;
    zone2 = request.indexOf("2") > -1;
    zone3 = request.indexOf("3") > -1;
    zone4 = request.indexOf("4") > -1;

    if (request.indexOf("1=on") > -1) {
      zone1 = true;
    else if (request.indexOf("1=off") > -1) {
      zone1 = false;
    if (request.indexOf("2=on") > -1) {
      zone2 = true;
    else if (request.indexOf("2=off") > -1) {
      zone2 = false;
    if (request.indexOf("3=on") > -1) {
      zone3 = true;
    else if (request.indexOf("3=off") > -1) {
      zone3 = false;
    if (request.indexOf("4=on") > -1) {
      zone4 = true;
    else if (request.indexOf("4=off") > -1) {
      zone4 = false;

    if (!showStatus) {

void setSpeakerState() {
    digitalWrite(zone1L, zone1 ? RELAY_ON : RELAY_OFF);
    digitalWrite(zone1R, zone1 ? RELAY_ON : RELAY_OFF);
    digitalWrite(zone2L, zone2 ? RELAY_ON : RELAY_OFF);
    digitalWrite(zone2R, zone2 ? RELAY_ON : RELAY_OFF);
    digitalWrite(zone3L, zone3 ? RELAY_ON : RELAY_OFF);
    digitalWrite(zone3R, zone3 ? RELAY_ON : RELAY_OFF);
    digitalWrite(zone4L, zone4 ? RELAY_ON : RELAY_OFF);
    digitalWrite(zone4R, zone4 ? RELAY_ON : RELAY_OFF);

void sendPage() {

// the 'bootstrap' css makes the page look good in desktop browsers and on phones
void printHead() {
    server.print("<html>\n<head>\n<title>Speaker Control</title>\n");
    server.print("<meta name='viewport' content='width=device-width, initial-scale=1'>\n");
    server.print("<link rel='stylesheet' href=''>\n");

void printForm() {
   server.print("<div class='container'>\n");
   server.print("<h2>Speaker Control</h2><br/>\n");
   server.print("<form action='/' role='form'>\n");
   int pinState = digitalRead(zone4L);                                           
   server.print("<div class='checkbox'><label><input type='checkbox' name='4'");
   server.print(pinState == RELAY_ON ? " checked='checked'" : "");
   server.print(">Hot tub</label></div><br/>\n");

   pinState = digitalRead(zone3L);                                           
   server.print("<div class='checkbox'><label><input type='checkbox' name='3'");
   server.print(pinState == RELAY_ON ? " checked='checked'" : "");               
   server.print(">Dining room</label></div><br/>\n");                            
   pinState = digitalRead(zone2L);                                           
   server.print("<div class='checkbox'><label><input type='checkbox' name='2'");
   server.print(pinState == RELAY_ON ? " checked='checked'" : "");               
   pinState = digitalRead(zone1L);
   server.print("<div class='checkbox'><label><input type='checkbox' name='1'");
   server.print(pinState == RELAY_ON ? " checked='checked'" : "");               
   server.print("<button type='submit' class='btn btn-default'>Save</button>\n");

void printTail() {


John Buginas said...

Great project. Where does the audio from the amp enter the circuit?

I see four sets of output connectors, but no input connectors.

Also, do you use anything to protect the amp or speakers during switching?

Thank you.

John Buginas said...

Ah, I figured the speaker wiring out.

Do you need to use some sort of device to protect the speakers or amp during switching?

Anonymous said...

Hi, Nice to meet you.

My name is Oscar, at WIZnet in Korea.

We have been searching some application references in which WIZnet solution is applied, and found your project “Arduino speaker controller“ using Ethernet Shield. In the Ethernet Shield WZnet’s W5100 chip is embedded. Your development looks very cool & smart.

Recently we opened WIZnet Museum ( site. This is a academic-purposed collection of open projects, tutorials, articles and etc from our global customers.

If you are O.K. we would like to introduce your projects in here. Hopefully, you will allow this.

Hopefully, keep contacting us for the friendship.

Thank you very much.

Dale Anson said...

John, I have one of these ahead of the speaker controller:

It has circuit protection built it. Before I built the arduino box, I had to go push buttons on this box. Now I just leave all the buttons on all the time and let the arduino do the switching. I also have 4 of these downstream (after the arduino) to adjust volume on the individual speaker pairs.

Dale Anson said...

Oscar, that's fine with me.

Scotty Winter said...

Any chance you want to build another and sell it to me? :)

Dale Anson said...

@Scotty -- What's it worth to you? Drop me a line at danson at if you're serious.

Etara said...

Hi, i would like to know where or how do you connect your amplifier to your relay? I understand that the relay output and switch speakers but where is the "common" / main signal carrying the source? How to connect it?

Anonymous said...

Great job Dale!

One question however: you are using ISS4. Can I use SSVC4 instead of ISS4 as a feeder? I want to use this solution to route L/C/R channels between 2 sets of acoustics so I need to have a feeder with dual input (input 1 will be L/R and input 2 will be C).

I suppose SSVC4 uses some kind of more advanced circuitry because it has built-in volume control - but won't this circuitry malfunction when the output is 'blocked' by the relay? I.e. zone X output is set to 'On' at SSVC4 so the SSVC device 'assumes' that load is active but actually there is no load because the relay shuts the chain down - that's what I'm a little bit afraid of.

p.s. I'm in Europe, so all my speakers are 6 Ohm

Dale Anson said...

Hi Oleg,

I don't see any reason why your SSVC4 wouldn't work in place of the ISS4. I don't think it would make any assumptions, there is either a load or there isn't. It would be like having the switch on but there is no speaker connected to the other end, I can't imagine that would cause any problem, but then, I don't have SSVC4 to test with, so don't hold me to this :)


Anonymous said...

Thanks a million! I will definitely give it a try :)


Mike V said...

Hi Dale,

First off, let me say what an amazing project. This is exactly what I was looking for to control my speakers. However I'm trying to modify your code to include up 6 zones but I've run into a roadblock.

For whatever reason the relays on pins 10, 11 and 12 are always on and won't turn off. I will be the first to admit I have no idea what I am doing as I'm just starting out on my road to coding so all I did was copy and paste your code and add in Zone 5 and 6. Then I went through adding in what I thought was the relevant additions but those three relays won't turn off.

Any idea what I might be doing wrong? I apologize if this is a basic question but I'm a little stumped. (I didn't post the code because I didn't want to take over your comment section.)

Thank you in advance for any help.


Rodger said...

I do not see a link in your post to what you used for volume control. Can you control volume via WiFi? we

Dale Anson said...

Mike V - sorry for the really late response, maybe you've figured it out by now, but pins 10, 11, and 12 are for the serial interface and don't work like the other pins. To run more than 4 relays, you'll need a port expander. See the post I did about version 2 for some details.

Dale Anson said...

Rodger - I use Kodi to play music, and have the Yatse app on my phone to control Kodi, so I use Kodi via Yatse to control the volume. I've looked into adding volume controls to this speaker controller, but it is somewhat difficult and time is lacking.