Receiving X10 Commands with the PSC05/TW523

Overview:

Since the "dark ages" the Arduino IDE has included the X10 library and examples. Although the documentation says that "This library enables you to send and receive X10 commands from an Arduino module." in reality, the library only allows you to send commands. This article will give you the ability to receive commands. Together, you can now do some pretty interesting stuff like make an X10 "controller" with "macros" or log X10 signals. (Or in my case, count how many times the cat door opens!)

Perhaps someone will want to fold this into the X10 library at some point ([3/30/2010 BH] - Someone did! See link above.) Alternatively, I've tested this receive code with the X10 library's send code and they coexist well. (Note that the interrupt used here should be temporarily detached when sending.)

The receive capability requires the PSC05/TW523 X10 module. (The PL513/TW513 can only send.) Also note that PSC05/TW523 uses a slightly different connection from the one shown in the X10 library documentation.

For those of you in 220V and/or 50Hz land, during my search I found low cost ways to modify the much much cheaper 115V modules to your voltage and frequency. You will find instructions in the best X10 source I've come across -http://www.idobartana.com/hakb/.

Below is a description of how it works and the source code for a simple example. Please feel free to use it more cleverly and to improve it, . . . and to share your improvements.

Background:

You've asked "What time is it?" and I'm going to tell you how to build a clock! So if you don't care how it works, you can skip this part and grab the working example below. I'm writing this because there's not much on how to receive from the PSC05/TW523, and nothing for the AVR. There is very good info on what the PSC05/TW523 sees on the power line, but relating that to what the PSC05/TW523 outputs, involves some challenges. Besides, it's nice to have it all in one place.

So I'll start by describing what takes place on the power line when X10 commands are sent, and then describe how the outputs of the PSC05/TW523 relate to that. (The best source on the power line signals is http://www.hometoys.com/htinews/feb99/articles/kingery/kingery13.htm ) This will help you understand what the Check_Rcvr() function does.

Suppose an A1-ON button is pressed. In the X10 world, this actually transmits two commands – (#1) The house code 'A' and the unit code '1' and (#2) The house code 'A' (again) and the command code "ON". A "Start Code" (SC) is also added before each frame. In addition, the data for each command is sent twice, in two identical "frames". So what you have on the line is:

SC…HOUSE-A…UNIT-1 / SC…HOUSE-A…UNIT-1 | SC…HOUSE-A…ON / SC…HOUSE-A…ON

Command 1 | Command 2
Frame 1 / Frame 2 | Frame 1 / Frame 2

The bits in frame are transmitted by sending a 1 ms burst of 120KHz at the "zero crossings" of the AC waveform. (These bursts actually start about 100us after the zero crossing.)

OK, one last level of complexity and your done with this part! After the Start Code (SC) – which is 4 bits (1110), each of the rest of the bits is followed by a parity bit. (So a bit '1' will send '1 0' and a bit '0' will send '0 1'.)

The Start Code is 4 bits with no parity. The House Code is 4 bits with parity – so 8 bits. The Unit Code and the Command Code is 5 bits with parity – so 10 bits. So a "frame" will consist of 22 bits but you will only care about 13 of them.

(I lied a little. There's really one more level of complexity but it doesn’t concern us - all the data bits described above is repeated 3 times / 1/2 cycle for power lines that use 3 Phase AC. The PSC05/TW523 isolates us from this.)

With the above background in place, let's talk about what you get out of the PSC05/TW523. (About the only reference on this is a few paragraphs and a diagram from X10 - http://www.x10pro.com/pro/pdf/technote.pdf )

(Pin 2 is ground and must be connected to Arduino's ground. Pin 4 is Transmit and this can be used by the X10.lib.)

Both the Zero Crossing and Receive outputs are "low active" and should use the internal pullup resistors. The Zero Crossing will go LOW for each zero crossing and must be connected to an interrupt pin (2 or 3) and the interrupts should be triggered on CHANGE. The Receive output will go LOW for each '1' bit and can be connected to any pin.

Remember that the data for each frame is sent twice on the power line? Well the PSC05/TW523 only sends the data from the second set. It does send the parity bits, however, and it sends all bits MSB first. (This is the Rosetta Stone.)

So putting this all together and using the actual bits for the house code and unit code/command, A1-ON looks like:

I have shown the parity bits right next to the actual bit, but in reality each bit (including the parity) will be sent at the zero crossing – which is when the ISR fires. Remember that a '1' bit is a LOW on the Receive pin.

The Check_Rcvr() function does not record the parity bits, so it counts the zero crossings and after the first 4 bits of the start code, it only records every odd bit. The rest of the function is easy to understand. One last note is that a command can be distinguished from a unit code because commands end in a '1' bit and units end in a '0' bit.

Example Code:

The comments in the example code below should provide additional help in understanding the X10 receive process. It also includes transmitting using the X10.lib to show how they can coexist.

/* Arduino Interface to the PSC05 X10 Receiver. BroHogan 3/24/09

* SETUP: X10 PSC05/TW523 RJ11 to Arduino (timing for 60Hz)

* - RJ11 pin 1 (BLK) -> Pin 2 (Interrupt 0) = Zero Crossing

* - RJ11 pin 2 (RED) -> GND

* - RJ11 pin 3 (GRN) -> Pin 4 = Arduino receive

* - RJ11 pin 4 (YEL) -> Pin 5 = Arduino transmit (via X10 Lib)

* NOTES:

* - Must detach interrup when transmitting with X10 Lib

*/

#include "WProgram.h" // this is needed to compile with Rel. 0013

#include <x10.h> // X10 lib is used for transmitting X10

#include <x10constants.h> // X10 Lib constants

#define RPT_SEND 2 // how many times transmit repeats if noisy set higher

#include "PSC05.h" // constants for PSC05 X10 Receiver

#define TRANS_PIN 5 // YEL pin 4 of PSC05

#define RCVE_PIN 4 // GRN pin 3 of PSC05

#define ZCROSS_PIN 2 // BLK pin 1 of PSC05

#define LED_PIN 13 // for testing

volatileunsignedlong mask;// MSB first - bit 12 - bit 0

volatileunsignedint X10BitCnt =0;// counts bit sequence in frame

volatileunsignedint ZCrossCnt =0;// counts Z crossings in frame

volatileunsignedlong rcveBuff;// holds the 13 bits received in a frame

volatile boolean X10rcvd =false;// true if a new frame has been received

boolean newX10 =false;// both the unit frame and the command frame received

Notes & Limitations:

Because a new transmission can cause an interrupt at any time you should process the command as quickly as possible. Adding an input queue or the like might be a good idea.

According to the datasheet, the PSC05/TW523 should receive the same command it has just sent. However, this doesn't happen in the example.

The PSC05/TW523 itself does not support all of the "Extended Commands" (see it's datasheet). However, having control over what is sent and what is received should give some options. For example commands for a certain house code could be interpreted as 2 4 bit values.

I've received some very good feedback from someone who is using this in the 50Hz world. To convert from 60Hz to 50Hz:

define OFFSET_DELAY from 500 to 800

define HALF_CYCLE_DELAY from 8334 to 10000

A corresponding change can be made to the X10.lib in X10constants.h:

define BIT_DELAY from 1778 to 2133

define BIT_LENGTH from 800 to 900

I have a blog that describes Arduino X10 projects I've made using this, as well as my interface to the "Fire Cracker" (CM17A). It's at http://brohogan.blogspot.com/