Summary
Remotely controlling a PC to turn it on and off from software
control on another PC.
Introduction
I'm working heavily on C# Open
Source Operating System (Cosmos). While VMware is great, its
even better to test on real hardware. To do this I wanted to make
the deployment automatic, but this required some custom work.
Booting could be done using PXE, and debugging through a null modem
serial cable. But who wants to manually turn on and off the PC for
every build?
To solve this problem I needed to find a way to turn the slave PC on
and off using software on my main PC. A few alternatives were first
considered.
WOL (Wake On LAN)
A magic packet can be sent across the network containing the MAC
address of the PC to turn it on. However WOL has no facility to turn
off or reset.
USB Power Controllers
Amazon has a bunch of USB and Ethernet based power switches that
turn on and off a power socket. PCs can be programmed to restore
after a power loss. However these tended to be very expensive, and
the lower end ones had horrible comments about quality. These would
also not provide perfect control for what I needed.
Custom Solution
I found a blog that discussed several attempts by an end user to do
exactly what I was doing by using the lines of a serial port. Its
actually more complicated than one would think because isolation is
required using at minimum opto couplers. The schematic required some
very specific parts however that were hard to come by.
Relay Controllers
I found several ready to go relay controllers for purchase. I
evaluated many of them and finally settled on the CanaKit
UK1104. It costs $59.95 plus shipping. It supports 4 relays,
and also has input sensors which I can use to detect the PC state.
This is actually important, because toggling the power switch is
used to both turn on and off the PC, so I needed to know if I was
about to turn it on, or off.
To use the relays, you also need a +9V or +12V power supply. I have
hooked many 12V applications to the computer PSU, but since the
device is actually used to turn the PC on, and there are no always
on +12V lines on an ATX computer PSU, the additional power supply is
needed.
Choosing a Motherboard
Everything else in the PC is standard and many are used parts I got
from a computer repair facility. In my case I needed a few specific
items on the motherboard:
- On board Ethernet with PXE boot. Most boards have this.
- Serial port, or serial header.
- Inexpensive.
- On board video
After some evaluation I settled on the Intel
D425KT. Its a Mini-ITX board with on board serial, and a
second serial available via headers. It has an on board Atom CPU
and costs about $50 wholesale, or $60-$75 retail.
Building the PC
First, I built the PC and made sure it worked normally. Next step
was to add the relay board and make some custom changes to the
wiring. Assembled the PC looks like this.

Now lets examine the front panel header of the motherboard. Every
motherboard's pin locations are different so its important to look
in your manual for proper connections. Wrong connections can short
out your board.

The connections we are interested in are in green and red. To make
the connections easy, find an old case and get the power light and
power switch. They will have connectors that can be used to ensure
shorts do not occur. Sometimes they wont match exactly but they can
be separated and leads can be extracted with a small knife and
rearranged.

(Note, this is not the D425KT in this photo)
First we need to connect up the power switch to 6 and 8. Cut the
power cable and wire it to COM and NO on one of the relays. I used
relay 4. COM is ground, and NO is shorted to COM when the relay is
on. NC is shorted to COM when the relay is off, but we don't need
that in our application. Since it is a switch, there is no polarity
and it doesn't matter which one pins 6 and 8 are connected to, so
long as one is connected to COM and one to NO. The relays are screw
down terminals, so no soldering is required, just a small
screwdriver. I also spliced back the original physical switch and
connected it directly to the relay to form the splice so I can still
manually operate the PC.
Next we need to connect the Power LED. This is a +5 line that is
powered when the PC is on. Its used to connect the power light, but
since its +5V we can use it to sense if the PC is on or off. To do
this cut the power line and attach the +5 line to channel 1 on the
controller card. Only the +5 line is used, the ground remains only
to the motherboard. On the line, the coloured one is used for
positive and the plastic connector is usually marked as well.

If one is not coloured and the other white, they may be red
and black instead. Red is always positive, and black is always
ground. Less common in these wires is the stripe system because they
are smaller.

If they are both white, one will have a stripe. The one with the
stripe is positive.
I also spliced the light back in this as well so I have a visible
indicator as well. To attach to the board which has pins for
sensors, I cut off the plastic connector from the hard drive light
and used it. Not all the channels are equal and some use different
voltage levels. For our use probably any would work (TTL or
Schottky), but Schottky is a bit better for our use so I chose
channel 1.

So after we are done it should look something like this.

Or the real life version.

Finally since we will boot the PC over the network and also use the
serial for debugging, we have a few important connections on the
back as well.

Software
OK, now we are ready to go! The relay controller installs as a USB
to serial, which means it can be addressed with a terminal program
or very easily with any programming language by using a serial port.

The relay controller operates on series of text commands listed in
the manual. To test it we can now open a terminal program and issue
some commands. This is TeraTerm,
a free terminal program.


First we check the state of Channel 1. Its 0, this means the
computer is off.
Then we have to toggle the switch since power switches are push and
release. That is we simulate it being pushed and released by turning
on the relay and turning it off. In software make sure you delay
about 500ms between on and off for the motherboard to recognize the
press. Remember, we are simulating a human finger.
Finally we check channel 1 again, and we can see the computer is now
on.
One of the other relays could be hooked to the reset pins, but its
not needed. To reset the PC, simply turn it off, then back on. To
turn it off is the same as it is to turn it on, just "push" the
button.
Video
I have also posted a video of this system in action.

Source
The source code used in this video can be extracted easily from the Cosmos project source. Here is the relevant code as used in Cosmos (C# Open Source Operating System).
<pre wrap="true">using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Text;
using System.Threading;
using System.IO.Ports;
using Cosmos.Build.Common;
namespace Cosmos.Debug.VSDebugEngine.Host {
public class Slave : Base {
string mPortName;
SerialPort mPort;
Thread mPowerStateThread;
public Slave(NameValueCollection aParams, bool aUseGDB)
: base(aParams, aUseGDB) {
var xPort = mParams[BuildProperties.SlavePortString];
if (xPort == "None") {
throw new Exception("No slave port is set.");
}
var xParts = xPort.Split(' ');
mPortName = xParts[1];
}
string WaitForPrompt() {
var xSB = new StringBuilder();
char xLastChar = ' ';
char xChar = ' ';
while (true) {
xLastChar = xChar;
xChar = (char)mPort.ReadChar();
xSB.Append(xChar);
if (xChar == ':' && xLastChar == ':') {
break;
}
}
xSB.Length = xSB.Length - 2;
return xSB.ToString();
}
void TogglePowerSwitch() {
Send("REL4.ON");
Thread.Sleep(500);
Send("REL4.OFF");
}
bool IsOn() {
var xResult = Send("CH1.GET").Split('\n');
return xResult[1][0] == '1';
}
string Send(string aData) {
mPort.Write(aData + "\r\n");
return WaitForPrompt();
}
void WaitPowerState(bool aOn) {
int xCount = 0;
while (IsOn() == !aOn) {
Thread.Sleep(250);
xCount++;
if (xCount == 20) {
throw new Exception("Slave did not respond to power command.");
}
}
}
public override void Start() {
mPort = new SerialPort(mPortName);
mPort.Open();
Send("");
Send("CH1.SETMODE(2)");
if (IsOn()) {
TogglePowerSwitch();
WaitPowerState(false);
Thread.Sleep(1000);
}
TogglePowerSwitch();
WaitPowerState(true);
if (OnShutDown != null) {
mPowerStateThread = new Thread(delegate() {
while (true) {
Thread.Sleep(1000);
if (!IsOn()) {
mPort.Close();
OnShutDown(this, EventArgs.Empty);
break;
}
}
});
mPowerStateThread.Start();
}
}
public override void Stop() {
if (mPowerStateThread != null) {
mPowerStateThread.Abort();
mPowerStateThread.Join();
}
if (IsOn()) {
TogglePowerSwitch();
WaitPowerState(false);
}
mPort.Close();
}
}
}
Chad Z. Hower, a.k.a. Kudzu
"Programming is an art form that fights back"
www.KudzuWorld.com
Formerly the Regional Developer Adviser (DPE) for Microsoft Middle East and Africa, he was responsible for 85 countries spanning 4 continents and 10 time zones. Now Chad is a Microsoft MVP.
Chad is the chair of several popular open source projects including
Indy and
Cosmos (C# Open Source Managed Operating System).
Chad is the author of the book Indy in Depth and has contributed to several other books on network communications and general programming.
Chad has lived in Canada, Cyprus, Switzerland, France, Jordan, Russia, Turkey, and the United States. Chad has visited more than 60 countries, visiting most of them several times.