Friday 21 June 2013

Power ON PC from usb/remotely using Arduino

 Right folks, It's been a while so thought I should start to write up a few things I've been working on. Unfortunately I don't get the free time I used to because of other projects now (See another post shortly, hopefully!) but this one has been a constant pain since I moved the household TV over to XBMC+TVHEADEND.

Turning the home TV system on is done from an all-in-one remote, the Harmony from Logitech. It has a smart switch-on function that sends the various power and channel change events to the devices (TV, Sound System, channel switcher) but one thing it can't do is power the XBMC PC on. This is a Dell PC with a usb-ir receiver that is powered off most of the time unless tv watching is going on. It cannot go into standby as standby and USB infra-red devices are still problematic under linux and results in no remote control ability after wake-up.

So how to turn it on using the IR remote then? The USB ports are powered off when the system is off (All it leaves powered on is the ethernet adapter for wake-on-lan functionality). So I'm thinking that to power the system on I need to use an external device and 'press' the power button/trigger the ATX power supply to wake up. An Arduino seems the obvious choice for this, mainly because they are good little boards, low powered and can do simple tasks with input+output pins and also because I have a couple of them lying around at the moment as I've not done any projects with them for a while.

So to get the arduino to turn the PC on, I need to trace the power switch inside the PC and the control lines it uses on the ATX power supply connector. So as some of you may know, ATX is a standard an so colours and pin numbers are standard. Pick out the right ones, pull it to ground and it should power the ATX supply on. WRONG when it comes to DELL machines. They decided a while back that using a standard like ATX was silly and so invented their own ATX standard.... Hmm, so this meant the pins and wires didn't translate, and so trying to probe them or work them out might cause a blown PSU. Looking at this particular PSU it is one of Dell's finest custom units and I didn't want to replace it in a hurry so decided this option was out. Next option was to trigger the power button that we press on the front. Again in theory this is just closing a contact so should be straight forward. Well it would be if you could get to it! The power button is sat on a PCB installed at the front of the chassis. From this PCB there is a ribbon cable with around 20 connections on it! This handles the Dell diagnostic LEDs on the front of the machine also, along with some USB, firewire and other functions. So if I removed the board could probably trace them and figure it but this seemed a lot of hassle too, and again if I broke it, it was game over.

I needed a solution that wasn't doing ANYTHING custom to the Dell PC. So my only option really was to look at the Wake On Lan option. This could power the machine up 'remotely' and without modifying it.

To actually trigger the Wake On LAN, I needed to receive IR remote presses. Since the all-in-one remote handles power on for various devices I could just sniff for any of these power on signals and use them to trigger my power on function. Hooking the Arduino up to the IR module and then sniffing codes was straight forward, there are various nice IR libraries to do this. I have the 'Keyes IR Receiver module' which is the one on a small PCB with a metal can-like surrounding. Hook up the pins to the arduino (central pin is + , S is marked and the remainder is -) to one of the digital PWM pins and with the library you can start to decode IR.

Now IR itself is a bit complicated. Because you have lots of different 'standards' and methods of doing things. I finally settled on a very simple bit of code that included the IR library, and then sent the HEX code to the serial port. This worked fine and I found a unique-enough code used when power-on was sent from the IR remote. This let the Arduino do its job. How the hard part, how to get this into a Wake On LAN signal. Unfortunately I don't have an ethernet shield otherwise this would have been simple! So my next option was to use the serial/usb cable to send this HEX code to another device that could send the WOL signal. My trusty FTTC router looked ideal, its a Vigor that has an entire busybox system on it, so ssh, telnet, and most of the normal shell commands there ready, plus two USB ports. Plugging the arduino in, I happily could see the /dev/ttyACM0 appear and dmesg confirmed that it recognised the ftdi interface.

So I could now communicate with the arduino, if I cat /dev/ttyACM0 I could see codes working, so all looked good. I then got back to my dreaded problem from some time ago with the outside temperature sensors. The biggest issue with the setup, everytime you opened the serial port, the Arduino rebooted (for its code upload routine), and also it generally didn't hold the serial port open well enough to actually receive data consistently. This was and still is my biggest hurdle that I've not overcome yet. At the moment I'm trying with a bash script like this:

#!/bin/sh

while [ /bin/true ]; do
        grab=$(grep -m 1 valid /dev/ttyACM0)
        
        wget -O - http://192.168.1.1/wake_xbmc2.php >/dev/null 2>&1
        sleep 5 

done

However, this suffers from several problems. (You'll also note, I'm watching for the word valid to appear, I amended my Arduino code to only put out valid when the relevant IR code was received, this cut down noise on the serial port to try and help things along. Firstly the serial port doesn't always seem to want to send data. So sometimes this will just sit there ignoring any IR input. I'm guessing the Arduino is seeing the IR (The LEDs on the arduino show this) but the serial port just isn't seeing the reply.

Secondly if the serial port is disrupted, the wake on lan gets triggered regardless. This is just a bash scripting tweak but I've not got round to fixing this yet.

So I'm still stuck with reading from the Arduino serial port reliably in this manner, and so I'm hoping for inspiration! Arduino people, feel free to comment and assist!