Start ATX power supply via IR

1. Motivation

Several years ago I built a linux based jukebox to play music and only be controlled via an infrared remote control and speech synthesis for feedback. No display or other means of input/output is used.

To start the system I wanted to have a small microcontroller waiting for a key press of an trained remote control. The project should be low-cost and low-effort so I decided to built it from scratch using just an ATtiny microcontroller. This document describes all the hardware and software aspects.

If you are interested, the whole jukebox project uses the following hardware and software components:

2. Overview

I only need a device to switch the PC on, everything else can be managed with standard linux software. The power button of an ATX based PC basically just shortens a pin on the mainboard with ground. This can be easily realized with a microcontroller too.

So the idea is to use a ATtiny microcontroller to monitor output of a TSOP infrared decoder and once the correct remote control button code is identified, it pulls the start pin to ground to start the computer. Very simple.

Infrared remote controls use a variety of encodings to transfer button codes. I don't want to implement some of those to support the remote control I want to use so I just decided to detect the raw infrared data which usually consists of something like 60-70 bits.

The final implementation shall use a hard-coded array of specific timings between bit changes to detect the button.

The hardware itself will run on the 5V standby voltage available in ATX systems.

3. Hardware

I did not want to spend money on a ATMEL flash hardware (like Arduino) but instead use direct SPI programming via parallel port cable. So the microcontroller board is designed to contain all necessary pins for power supply, TSOP connection, SPI programming and actual pin-out for ATX startup.

3.1. Components

To build the device, I used the following components:

For the ATMEL programming interface you need:

The ATtiny comes with an internal oscillator but you should use an external crystal since I rely on exact timings between edges and no other calibration. The 4 MHz crystal is actually overkill, the frequency can be quite lower (something like 400 kHz) but for me power consumption is not really an issue and it is still very low on 4 MHz.

3.2. Layout

The final board uses 5 connectors for the following purposes:

3.2.1. Main circuit board

Here is the basic layout of the components:

This is the actual board:

and this is what it looks like on the back:

Be aware that the TSOP connnector uses a different layout than the TSOP itself to make the board design it little easier. On a second thought, it should be doable without any other bridge to route the VCC around the data line but since the TSOP is not connected directly anyway, it does not matter for me.

3.2.2. Programming cable

For flashing the ATtiny I use the software UISP (see below) which supports many interface. I chose the parallel port interface since it is easy to build and I have PC around with that interface. USB-to-parallel adapters should work too, but there are also other options like a RS232 interface.

For the parallel port, I needed to connect 6 pins:

4. Software

To deploy software on the microcontroller you basically need 2 things. The C environment (compiler, binutils and libc) and the tool to flash the microcontroller with the binary.

4.1. Compiling code

To generate machine code for the ATMEL microcontroller, I use GCC as cross compiler.

You need:

First compile the binutils for AVR platform:

$ export PREFIX=$HOME/avr
$ export PATH=$PATH:$PREFIX/bin
$ bunzip2 -c binutils-2.32.tar.bz2 | tar xf -
$ cd binutils-2.32
$ mkdir obj-avr
$ cd obj-avr
$ ../configure --prefix=$PREFIX --target=avr --disable-nls --disable-werror
$ make
$ make install

Then you compile the cross-compiler:

$ bunzip2 -c gcc-4.9.4.tar.bz2 | tar xf -
$ cd gcc-4.9.4
$ mkdir obj-avr
$ cd obj-avr
$ ../configure --prefix=$PREFIX --target=avr --enable-languages=c --disable-nls --disable-libssp --with-dwarf2
$ make
$ make install

And finally the libc:

$ gunzip -c avr-libc-2.0.0.tar.gz | tar xf -
$ cd avr-libc-2.0.0
$ ./configure --prefix=$PREFIX --build=`./config.guess` --host=avr
$ make
$ make install

4.2. Flashing tool

To transfer the actual binaries to the ATMEL microcontroller I use UISP.

I needed two patches:

$ gunzip -c uisp-20050207.tar.gz | tar xf -
$ patch -p0 <uisp-20050207-avr2313.patch
$ patch -p0 <uisp-20050207-compilefix.patch
$ cd uisp-20050207
$ ./configure --prefix=$PREFIX
$ make
$ make install

4.3. Prepare ATtiny fuses

The ATMEL microcontroller uses fuses to configure some hardware parameters.

As a very first test whether the device and programming adapter works, you can use UISP to read the current fuses. To do so, put the jumper to the programming position and run the following command:

uisp -dprog=dapa -dlpt=0x378 -v=3 --rd_fuses

If everything works as expected you will see some output, basically 4 different hexadecimal values (low byte, high byte, extended byte, and checksum).

For details about the choices of the fuses, please check the ATtiny manual. Here are the values for my setup:

So the actual values for the fuses are:

To set those values:

$ uisp -dprog=dapa -dlpt=0x378 -v=3 --wr_fuse_l=0xfd --wr_fuse_h=0xdb

4.4. First test program

To test whether all components work as expected, I wrote two very simple programs.

4.4.1. Simple pin toggle test

This program basically just periodically toggles output pin PB6 (MISO, mapped to parallel port pin 11).

This is the sourcecode (0 kB). To compile and install:

$ avr-gcc avr_demo.c -c -o avr_demo.o -Os -g -mmcu=attiny2313
$ avr-gcc avr_demo.o -o avr_demo.elf -mmcu=attiny2313
$ avr-objcopy -j .text -j .data -O ihex avr_demo.elf avr_demo.hex
$ uisp -dprog=dapa -dlpt=0x378 --erase --upload if=avr_demo.hex -v=3 --verify

The programm should run directly after flashing, you can leave the jumper at the programming position.

4.4.2. PC verify program

To see the actual pin change, I also wrote a little linux program to read pin 11 of the parallel port (using parapin).

You can downlad the tool (239 kB). To build extract and run:

$ make compile_pp all
$ ./inputtest

Whenever the pin value changes, two lines are written with the value, the current timestamp and the relative timestamp. The AVR test programm busy-loops for 10000 iterations which takes 4 cycles each so at 4 MHz it will toggle the pin every 10ms. You should be able to see that in the output. Example output looks like this:

New val 0 @ 1556962992.133203 = 512
  diff: 2034142  --  timer @4 Mhz/256: 31783
New val 1 @ 1556962992.142272 = 513
  diff: 9069  --  timer @4 Mhz/256: 141

4.4.3. IR pass-through test program

Next test is to see that the IR decoder TSOP works. The test program (0 kB) reads the connected data pin of the TSOP and write the value the output pin PB6.

You can use the some PC program. When you press a button on any remote control, you should see multiple lines of output, probably like 60-70 value changes. This means that it works as expected.

5. Remote control learning

The next step is to actually learn the remote control button sequence to start the PC by setting the output pin to ground.

The main idea is pretty simply and dumb, but it is good enough for the purpose. During learning, I use a timer on the ATtiny to measure the time between rising and falling signal edges from the TSOP. Regardless of the actual encoding scheme, this will give a unique fingerprint of the pressed button. This timing fingerprint is then stored in the final binary and the microcontroller uses a simple state machine to match the fingerprint. If the button is recognized, it will set the output pin to ground to start the computer.

Here is a sample output of a LIRC helper tool to visualize the IR data:

You can see at the beginning a very long high value followed by a shorted low frame. Then the actual bits are transfered using very small delays for a zero bit and a slightly longer delay for one bits. There a different encodings so the signals from another remote control will probably look different but the main concept is the same.

The learning AVR program (1 kB) works as follows:

First step is to identify the maximum value to trigger the data dump. Use the IR passthrough program to measure the time between edges. The very first output will probably show very large relative values as it will be the time to the previous last button press. All consecutive values should be rather similar. For instance for my remote control and the 4 MHz clock, the values will be between 4 and 12 except for the two initial values which are often much longer and something around 180 for my remote control. So 256 is a good trigger value which is used in the learner program. Of course this means that you have to press the button twice to actually trigger the output of the measurement values of the first button press.

To decode the values, use the linux program (239 kB). It basically reads the AVR pin values, tries to resync if necessary, and writes all values one after another. It should be easy to see a unique pattern for each button.

6. Integration

The final program (1 kB) will use the values found by the previous step to put them into an array. Then the microcontroller checks each time difference to match the values in the array. It uses a state machine and checks the first two values separately since they are special start identifier values.

If the complete chain of values is matched, it will pull the output pin to ground for some milliseconds to start the computer.

Since the device basically simulates the press of the power button anytime the IR button is detected, it can be used to power down the PC again. If this is behavior is unwanted you can change the linux configuration to ignore the ACPI event of the power button.

If everything works as expected, you can remove the programming cable, put the jumper at the right position so reset is always high.

7. Possible code changes

Some changes may be necessary if some hardware parameter are changed.

If the clock is less than 4 MHz, the timer divider 256 may be to large to measure the time between edges. In this case I recommend to lower then clock divider to 64. But in this case the new values might be too large to fit into an 8 bit array. There should be enough space to use 16 bit arrays, but you can also shift the values by one bit to get an actual timer divider of 128.

8. Final thoughts

This implementation is very simple and does not use any advanced features of the microcontroller. A lot of power can be saved when using an interrupt based measurement so the device sleeps as much as possible. Also, actual decoding of the infrared encoding scheme would make it easier to change the button code. But both aspects weren't important for me. Power consumption is low anyway and more complex code just takes longer to implement and the outcome would basically the same.

The hardware design can be simplified too. If you use a separate development board, the final board does not have to have an SPI interface and a jumper. You basically only need VCC, TSOP connection and the output pin. You can also use a smaller oscillator to save some more space. Finally, you can also try to remove all the capacitors and the resistor. The larger capacitor and the resistor is only needed to smooth VCC, but in standby mode it should not be that noisy anyway. The two smaller capacitors are for the oscillator, but it probably works without them too.