The Ridiculous Raspberry Pi Winamp Remote – Part II

May 14 2013 Published by under Physical Computing, Technology

Send to Kindle

Recap

As stated in my previous post, the plan was to hit a button that’s connected to my Raspberry Pi, which will trigger Winamp running on my pc laptop to play/pause. This without the Pi being physically connected to the pc – all via wifi and my local home network.

The hardware setup

Raspberry Pi with USB wifi, a small breadboard, 100k ohm resistor, a pushbutton and some hookup wire.

GPIO pin 25 goes to one side of the switch, 3.3 volts to the other. You hit the switch, pin 25 gets juice and goes HIGH. The 100k ohm resistor is also hooked to pin 25 on one side and ground on the other, connecting 25 to ground when the switch is not connected, ensuring it is in a LOW state. For those of you in the know, this is a basic pull down resistor. If you’re not familiar with the concept, as I was not last week, this is a good explanation: http://www.resistorguide.com/pull-up-resistor_pull-down-resistor/

The Python

The Raspberry Pi is running a simple program written in Python:

import urllib
import RPi.GPIO as GPIO
import time

BTN = 25
GPIO.setmode(GPIO.BCM)
GPIO.setup(BTN, GPIO.IN)


while True:
        val = GPIO.input(BTN)
        if val == GPIO.HIGH:
                urllib.urlopen("http://192.168.1.14:9000")
                time.sleep(1)

This just sets up pin 25 as an input and starts an endless loop which reads the value of pin 25. If it’s high, it pings a url and sleeps for a second. The sleep is so you don’t get multiple calls at once. There are more robust ways of handling this, but this was quick and dirty and works as long as I’m the only user and I know not to hold the button down too long.

So what is this url?

The Node

On my laptop where Winamp is playing, I started a simple node server. When a client connects to the server, it calls a function called playPause. This executes an external program called MediaControllerConsole.exe via child_process.

function playPause() {
	var path = "MediaControllerConsole.exe";

	var exec = require('child_process').exec;
	var child = exec(path, function( error, stdout, stderr) 
	{
	   if ( error != null ) {
	        console.log("err: " + stderr);
	        // error handling & exit
	   }
	});
}

var net = require("net");
var server = net.createServer();
server.on("connection", function(client) {
	playPause();
	client.end();
});

server.listen(9000);

So what is MediaControllerConsole.exe?

The C#

I had the connection from the Pi to my laptop. Now I needed Node to control Winamp. There may be some way to do this more directly, but I found that I could write a really simple C# console application that called SendKeys.Send() to trigger key presses on the machine. I looked in Winamp’s options and saw that CTRL-ALT-HOME is a global keyboard shortcut to Winamp’s pause function. It turns out that this actually acts as a play/pause toggle, which is even more perfect for my use case. Here’s the C# app I wrote:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MediaControllerConsole
{
    class Program
    {
        static void Main(string[] args)
        {
            System.Windows.Forms.SendKeys.SendWait("^%{HOME}");
        }
    }
}

Since this was a console app and not a Windows Forms app, I had to add system.windows.forms.dll as a reference to the Visual Studio project and call SendWait instead of Send (which has more hooks into the forms stuff). The string “^%{HOME}” means that while holding the CTRL key (^) and the ALT key (%), press the HOME key.

Final recap

Pressing the button connected to the Pi sends pin 25 to HIGH which triggers Python to call across the wifi network to the Node server which starts a child process running the C# program which sends keystrokes into the system which Winamp is listening for, causing it to play or pause. All so I don’t have to reach over and press two keys.

Ridiculous! But it works!

Send to Kindle

3 responses so far. Comments will be closed after post is one year old.