Wednesday 15 April 2009

X10 RF transmitter using PICAXE 08M

Download example code – X10RFDEMO.BAS

I came across the PICAXE devices the other day, the 08M devices are 8 pin Pics directly programmable in BASIC, a complete circuit needs just two external resistors and they cost less than two pounds each !

I also had a problem, an errant water pump which pumps our water from a well and which is supposed to automatically switch on and off, unfortunately the pressure sensor had gone wrong and it was no longer switching off, our first knowledge of this was an eye watering electricity bill, which to add insult to injury arrived a quarter late. I wanted a way to monitor the pump and already having the ability to receive and log X10 RF signals an obvious solution seemed to be to build an X10 RF transmitter, I ordered a PICAXE 08M and a 433.92Mhz transmitter wired them together and sat down to write the code - It was then I realised a slight problem, the PICAXE has a basic instruction timing of around 250 microseconds per line, and that is not fast enough to get a data byte, mask the bit to be transmitted, decide if it is a one or zero, send it and loop back, given that a zero in X10 RF is just 1.1 millisecond between rising edges.

Not wishing to waste by £2 investment I tried harder at the software and came up with a (I think) really good solution. The basic idea is to change the transmit loop to simply lookup delay values corresponding to the data to be transmitted (a one or a zero), this way the code is much faster as we do not have to isolate bits and make decisions and if we double the internal clock of the o8M it can be done within the X10 timing constraints. This technique actually allows us to transmit a number of protocols and the only limit is the size of the lookup table, which if it is to be in RAM means on the 08M devices we are limited to protocols that transmit 48 or less bits at a time, fortunately standard X10 is just 32 bits.

The x10 RF timings are well explained explained in this article and I won't repeat them here, other than to say in my experiments the bit times are not that critical (+/- 10% seems fine), but you must get all 32 bits sent in the window or the receiver will give up decoding early. Follow the CM17A link in the above article to get the data encodings you need.

I found the timings worked out well enough using the pause command, but if you need really critical timings change the pause commands to use pulsout on a dummy pin, you get within a few microseconds doing this but in my experiments it really hasn’t been necessary.

The transmit code is documented below, and takes only 39 program bytes

Transmit Code:


setfreq m8 'Set speed to 8 Mhz

for Tries = 1 to 3 'We will try to send the data three times
pulsout txPin,PreambHigh 'Send the preamble pulse
pause PreambLow 'wait 4 millisecsonds

for Memptr = BitMemLow to BitMemHigh 'Now send bits of data
high txPin 'First rising edge
peek Memptr,tmpByte 'Get the delay which sets if this will be a one or zero
pause tmpByte 'Do half the delay - You could use pulsout DummyPin,Delay
low txpin 'Let the tx drop - we are still within a bit
pause tmpByte 'Do the rest of the delay
next Memptr

pulsout txPin,BitEnd 'We need the final rising edge
pause BetweenTryDelay 'Pause between send tries
next Tries

setfreq m4 'Restore speed

The timings work out pretty well this shot from Digitrace, shows the actual output from the above code, each burst of output is a complete transmission, there is an 8 millisecond high at the beginning to allow the receiver AGC to operate.


This is the timing for a One bit, the spec asks for 2.2, but 2.09 seems fine my guess is that the receiver code the device I was using basically says – if the times between rising edges is between about .75 and 2.5 milliseconds it is a valid bit, if it is less than 1.65 it is a Zero bit, greater a One bit


and here a zero bit,


As stated above the transmit routine uses a table in memory where every byte represents a bit to be transmitted, below is small helper routine that takes the data in some registers and converts it to the tx map, it also deals with the bit transmission order which is LSB first, though in fairness this is arbitrary and you simple fiddle the tx codes, this way however the codes correspond to the ones given in the CM17 article above.

Build TxMap Code:

' Below is a short helper routine that can take a 32 bit input (2 word registers) and convert
' them to the 32 byte table that rf X10 needs. The input registers can be any but they must be usable as words.
' txWord0 is destroyed by this code.
' Note X10 sends the LSB of each byte first

symbol txWord0=w1 ' b3:b2
symbol txWord1=w2 ' b5:b4

' How to view the txWords as bytes
symbol txByte0=b3
symbol txByte1=b2
symbol txByte2=b5
symbol txByte3=b4


Memptr = BitMemLow 'Where to start the table
tmpByte = txWord0 ** 2 * OneDelay 'Shift 1 left, get the fall out bit * delay
txWord0 = txWord0 * 2 'update the actual word
poke Memptr,tmpByte 'save the bit time length
Memptr = Memptr+1
if Memptr = BitMemMiddle then let txWord0 = txWord1 endif 'swap after 16
loop while memptr <= BitMemHigh

Putting it all together, the example program at the top of this blog, is for a small demo program that wakes every 30 seconds and toggles between sending an X10 On and Off code.

1 comment:

  1. Hi,

    Do you have any objection to me deriving something from this code for the open-source TRV project (OpenTRV)?


    Damon Hart-Davis