It’s been a while since I’ve blogged. Its not like I haven’t been trying – my drafts pane in Windows Live Writer is full of half-finished posts. Maybe I’ll complete them and post them before the summer ends. Or maybe not.
This post is about using those cheap 300-bucks-a-pair modules. For international readers, INR300~USD6. If you look around where you live you’ll find the same or similar modules which do the same thing. Sparkfun carries these variants:
SeeedStudio stocks these:
The difference between Sparkfun’s and Seeed’s is that the former has a receiver that is PLL synthesized, and the latter uses a relatively cheaper, albeit less reliable LC circuit. I have both types of modules, bought locally. Both types use a SAW resonator for transmission.
These dirt cheap RF modules work at 433MHz (you’ll also find the 315MHz variety), which puts them squarely in the ISM transmission band. They use ASK modulation to send data across. You can mix and match PLL/LC transmitters and receivers without a problem, just so long as they’re all on the same frequency (duh!).
A lot of people use these modules for low cost wireless control mainly because they’re a breeze to interface. The easiest way to work with the modules is to buy the HT12-E and HT12-D encoder-decoder pair. These chips are essentially parallel-to-serial shifters and output a user set 8-bit ‘address’ along with 4-bits of useful data.
But what if you want to send more that just 4 bits? Suppose you want to send an arbitrary text string? Or maybe the 8-bit brightness value of a wireless night lamp? In such situations these chips are useless. You could buy the HT640-HT648 encoder-decoder pair, but again, that would limit you to 8-bits of data at a time.
The best option (IMHO) is to use microcontrollers as the encoder and decoder. Using a micro will, in a manner of speaking, allow you to kill several birds with one stone. You can use the same micro to decode the received data, as well as perform control functions at the RX end.
So how do you go about using a micro? I’ll start at the beginning:
The reason why these modules are cheap is because they are very simple. They are, at most, just a crude pipe through which RF data passes. There is absolutely no form of error correction, no data protocol whatsoever, and certainly no guarantee of data transmission.
One of the biggest problems these modules suffer from is that of DC-drift. Think of digital data being passed through a series capacitor. Let us say that you try to send several 1s, the capacitor will soon charge up until it is saturated. Once it gets saturated you can’t transmit data any longer, since there is no charge mobility. Similarly if you send a long string of 0s the capacitor will get discharged, and again, you won’t be able to transmit data. The trick is to send a balanced number of 1s and 0s. Over a short period of time, if the number of 1s is equal to the number of 0s transmitted, then the ‘DC level’ of the RF module remains balanced.
Clever, you say. But how do I put this into practice? You could use the Manchester encoding scheme to transmit data. Put very simply, in Manchester encoding, each bit is encoded as two bits. A 1 is encoded as binary 01, and a 0 is encoded as a binary 10. Thus, regardless of what data is sent, the transmission is DC-balanced.
A very obvious disadvantage is that the data rate is immediately halved (or one can say that the bandwidth doubles). There are more complex coding schemes which are more bandwidth efficient such as 8B/10B encoding, but I’ve stuck with Manchester encoding, since it’s much easier to do.
Now I didn’t really do any Manchester coding or decoding – I used a bit of a cheat. Most micros have a UART as well, so I included that too in my ‘hack’ – using built-in hardware to do dirty work greatly reduces my effort :-) Since I’m using the UART, I can only send 8-bit chunks of data. The data transmitted must satisfy these conditions:
- Data is to be balanced, so each byte should have four 1s and four 0s.
- There should be no more than two consecutive 1s or 0s. This is a constraint which ensures that data is Manchester-like.
Of the 256 possible numbers, there are 26 8-bit binary strings that fulfil the above criteria. I’ve arranged the numbers in Gray-code-like order.
|00110011 0x33 |
When using this scheme a few other things need to be kept in mind. The transmitter will only be turned on when transmitting data. However, the receiver is always on. And that’s a problem. The receiver will happily ‘lock-on’ to the strongest RF signal it can find. It doesn’t care if that signal is meaningless data – it will demodulate whatever it finds. So as it sits there, the receiver is demodulating junk. Suppose we now start sending data, how does the receiver UART know that this infact, is actual data, and not junk?
The way to solve this is to first send a ‘look-at-me’ sequence. The LAM sequence is just a series of balanced numbers sent by the transmitter to ‘wake’ it up. By doing this, the transmitter establishes itself as the strongest signal source. The LAM also serves as a kind or reference for the receiver’s UART to ‘lock’-onto. After much trial and error, and forum searching, I found that the LAM sequence of 0xBA 0xBE 0xFA 0xCE (or hex BABE FACE) works wonderfully, though it is NOT balanced!
After the LAM, a ‘start’ code is sent (I use the numbers in green, but any sequence may be used). Only once the ‘start’ is received does the receiver accept further data.
The data is followed by a simple checksum. The checksum is the 8-bit sum of all the data. If the checksum calculated at the receiver is not the same as the checksum transmitted, then it implies that some data has been lost in transmission.
Transmission ends when the number 0x99 (red) is received.
I am still working on error checking using data prediction. I’m also working on making this code adaptable to use in a multi-transmitter setup using arbitration.
The code in its current form is very robust and works very well. My setup has two transmitters and one receiver. One transmitter causes a red LED to blink on the receiver side; the other make a white one blink. Understandably, there are times when the transmitters interfere, but this doesn’t cause spurious results.
As testament to the robustness of the protocol (and maybe even the transmitter modules!) here is some data: though the datasheet specifies using a λ/4 monopole antenna (approx 17cm of wire @433MHz or 23cm of wire @315MHz), both transmitters and receivers are happily TX-ing and RX-ing without any external antennae. I suppose I could increase range and reliability by sticking on a wire, but I’m getting a 10m range with a 1.5v battery!
If you want the code ask in the comments. I’ll be posting pics of my setup soon.