Arduino + Ethernet shield + XML-RPC + Python

The Problem

In the middle of an exhibition space visitors are encouraged to play a piano. At the end of the gallery a screen and speakers blare to the music of Tiny Tim. The visitor at the piano should be able to mute the audio from the piano. Technical limitations. There is no way to run cables from the piano any distance but there is power in a floor pit. The AV at the end of the gallery is run by a Mac Mini buried in the floor.

The Solution

Button -> Arduino with ethernet shield -> ethernet over power -> XML-RPC -> Python -> apple script.

I already had some ethernet over power devices so getting one ethernet shield was cheeper than Xbee or Wi-fi.

Hardware

Basically just a box with a button and an arduino and ethernet shield. I modified a black cat5 cable to also do power over ethernet so I only had to have one cable going from the box to the floor.

Power

I discovered right at the last minute that the arduino with ethernet shield is way more fussy than lots of other projects. A 9 or 12 volt supply along with the extra draw from the ethernet shield makes for an overloaded power regulator. When I finally figured out what the problem was a 7.5 volt power supply came to the rescue.

Software

On the ardunio side I didn't have time for writing a XML-RPC library or anything fancy so I just manually created the one call I needed with basic print statements. You will need to manually calculate your content-length value if you intend to modify.

#include <Ethernet.h>

byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
byte ip[] = { blah, blah, blah, blah };
byte server[] = { blah, blah, blah, blah };

int button_pin = 3;
int buttonState = 0;
int val1;
int val2;

Client client(server, 8888);

void setup()
{
  Serial.begin(9600);
  pinMode(button_pin, INPUT);
}

void muteaudio() {
  Ethernet.begin(mac, ip);
  Serial.println("connecting...");
  if (client.connect()) {
    Serial.println("connected");
    client.println("POST /RPC2 HTTP/1.0");
    client.println("User-Agent: Arduino");
    client.println("Host: blah.blah.blah.blah");
    client.println("Content-Type: text/xml");
    client.println("Content-length: 83");
    client.println("<?xml version=\"1.0\"?>");
    client.println("<methodCall>");
    client.println("   <methodName>doMute</methodName>");
    client.println("</methodCall>");
    client.println("");
    client.println("");
    client.println("");
    client.println("");
    client.println("");
    client.println("");
    client.println("");
    client.println("");
    client.println("");
    client.println("");
    client.println("");
  } else {
    Serial.println("connection failed");
  }

  client.stop();
}

void loop()
{

// Ethernet Feedback
  if (client.available()) {
    char c = client.read();
    Serial.print(c);
  }

 // Button 

   val1 = digitalRead(button_pin);
   delay(10);
    val2 = digitalRead(button_pin);
    if ( val1 == val2){
    if (val1 != buttonState){ // state has changed
        if (val1 == 1 and buttonState == 0){
                        Serial.print("Muting Audio");
                        muteaudio();
        }
    }
    buttonState = val1 ;
  }

}

The empty carriage returns are something I accidentally discovered while testing with telnet that makes the python XMLRPC server accept the RPC request. I don't know why, but it works.

The python script is pretty basic and just takes the request and writes an applescript command to the shell to switch the audio on and off.

import modules
import SimpleXMLRPCServer
import subprocess


global toggle
toggle = 1

#The server object
class button:
    def doMute(self):
        global toggle
        if toggle == 0 :
            cl = 'osascript -e \"set volume 0\"'
            print cl
            subprocess.Popen(cl, shell=True)
            toggle = 1
        elif toggle == 1:
            cl = 'osascript -e \"set volume 10\"'
            print cl
            subprocess.Popen(cl, shell=True)
            toggle = 0
        return "Done"

button_object = button()
server = SimpleXMLRPCServer.SimpleXMLRPCServer(("bla.bla.bla.bla", 8888))
server.register_instance(button_object)
#Go into the main listener loop
print "Listening on port 8888"
server.serve_forever()

links