Cheap Arduino Wireless Communications

I was looking for a way to handle wireless communications between two Arduino boards. Other options like Xbee or Bluetooth were going to cost $50 to over $100. Then I found a cheap RF transmitter and receiver at Sparkfun. The total cost is only $9!

Here are a few limitations to RF solution:

  • Communications is only one way. If you wanted two way communications you’d need to buy two receivers and two transmitters which would raise the cost to $18. This is still cheaper than other solutions I found.
  • The variable gain on the receiver causes it to pick up lots of background noise. I had to do some processing with the Arduino to filter out this noise. More details about this below in the code section.
  • Bandwidth maxes out at 2400 bps, but there is a version with 4800 bps. A large portion of this bandwidth is used for network protocol I wrote that handles error detection.
  • Range is limited to a max of 500 feet.

The advantages are that it is cheap and it is pretty easy to use.

Below are some images showing how I hooked up the receiver and transmitter to two different Arduino boards. When wiring the receiver/transmitter you only need is to give them power/ground and then a pin for the TX (serial transmit) or RX (serial receive) pin. I also wired a button to the Arduino doing the transmitting, and used the LED on pin 13 that is built into my Arduino boards on the receiver so I could test this setup. The test app just flashes a button on the receiving board when a button is pressed on the transmitting board.

Here’s a picture of the my actual bread boarded circuit.

Code

Since the receiver is constantly picking up random noise I add a few extra bytes to every data packet. I add two bytes to signify the start of a data packet. Then I send the a byte address. This address allows multiple devices to work in the same area without interfering with each other. Next is the data (in my example code it’s an unsigned int (2 bytes). Lastly I send a checksum which is a simple xor of all the data bytes to make sure the data got received without being corrupted.

I broke the Arduino code into two files. If you’ve never used two files before with Arduino all you need to to is keep both files in the same directory and the Arduino IDE merges them for you. Here is the full code for the main application, and here is the full code that does the network error catching.

Increasing the Range

I did all of my initial testing without any of these improvement and everything worked fine with these devices inside the same room.

  • Add an antenna. All you need is a 23 cm piece of wire. I did this and it made it so I could reliably transmit data from one corner of my house to the other (3 floor town house).
  • Increase the voltage for the transmitter. The transmitter can use 2-12 volts. With 5 volts I got pleanty of range for my use case, but increase this if you need more range.
  • Reduce the baud rate. My test app runs at 1200 bps out of the max 2400 bps. You could drop this even further to something like 300 bps and that should help reduce transfer errors and hopefully increase range.

Thanks

I got a lot of help from the transmitter/receiver data sheets, and from this article.

I’ll mention an even cheaper idea I had while doing this. If you will always have line of sight between your devices you could setup something very similar to this with an IR LED and IR transistor. It would work like a TV remote. It won’t work for me because I want this to go through walls, but if line of sight is fine for you then you could use this code and an IR LED/transistor would probably cost under $3.

28 Comments »

  1. Pototo said,

    August 31, 2009 @ 12:42 am

    Could you connect the transmitter trough USB, per say, to the computer, and use it as a UART in order to do wireless programming of your microcontroller?????

    thanks

  2. tz said,

    August 31, 2009 @ 2:36 am

    You would need to go from USB to UART. I have the 4800 baud set. On the breadboard next to this computer.

    1. You need to constantly transmit something so the receiver can stop trying to increase the gain.
    2. It can go 2400 (or 4800) baud, but a space (mark?) is the only time there is an RF signal.
    3. You can transmit ANY train of pulses. You could do a 1/3 – 2/3 width modulation, NRZ, PWM, or some of the hard drive RLL codes using the pulse stuff to more reliably transmit data. It appears to have a higher bandwidth/throughput when you do something like this.

  3. x893 said,

    August 31, 2009 @ 2:55 am

    I use TR24A modules (SPI) with 4$ cost per module.
    http://www.spiriton.com.tw/download/TR24.pdf

  4. madsci said,

    August 31, 2009 @ 8:28 am

    You can find similar devices all over ebay – most of which not only have 4 data pins (send a nibble at a time), but come with an encoder/decoder chip to set device-specific channel. By setting the encoder pins directly from the microcontroller, you could increase effective bandwidth by removing addressing (at the expense of 8 I/O pins on the transmitter).

  5. ArduinoHN said,

    August 31, 2009 @ 9:40 am

    I found this ones
    http://www.seeedstudio.com/depot/315mhz-rf-link-kit-p-76.html
    http://www.seeedstudio.com/depot/433mhz-rf-link-kit-p-127.html
    And this one of 2KM range with encoder and decoder
    http://www.seeedstudio.com/depot/2km-long-range-rf-link-kits-w-encoder-and-decoder-p-321.html

  6. reader via hackaday said,

    August 31, 2009 @ 11:40 am

    A very nice blog! Have you considered using lower resolution images that link to the high res pictures?
    Because you’re putting hi-res images directly into the html, it takes a LONG time to load each page!
    If you put up resized (72/96 dpi) images, your bandwidth use will go down and your page loading speed will decrease…

    And yes, I know it seems fast enough to you, but you probably have everything cached. :)

    Anyway, nice work!

  7. Angus said,

    August 31, 2009 @ 7:20 pm

    Nice! Not that there’s anything wrong with rolling your own, but for completeness sake I wanted to point out the VirtualWire Arduino library:

    http://www.open.com.au/mikem/arduino/

    Designed for these same transmitter/receiver pairs, broadcast/receive variable length datagrams in ASK encoding with a simple checksum.

  8. Maurice Ribble said,

    August 31, 2009 @ 7:27 pm

    Angus, someone suggested this solution at http://hackaday.com/2009/08/30/cheap-wireless-for-microcontrollers/. The problem I see is you can’t seem to use hardware UART. Am I missing anything?

  9. the_geek_guy said,

    September 1, 2009 @ 7:26 pm

    I think yo can get a transmitter/reciever pair for $5 at robotshop.com Which may be cheaper.

  10. kevin mcguigan said,

    September 2, 2009 @ 9:31 am

    can you teach me how to write code for the ardunio?

  11. Maurice Ribble said,

    September 2, 2009 @ 10:34 am

    There are tutorials about getting started here: http://arduino.cc/en/Tutorial/HomePage

    You could also ask on this forum for suggestions on how to get started: http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl

  12. Paul said,

    September 4, 2009 @ 5:15 pm

    Hello,

    I’m having trouble compiling your code. I combined the two source files into one file (with functions at the beginning of the file). Any thoughts?

    Here is a screenshot: http://71.250.239.19:86/temp.jpg

    Thanks!

  13. Maurice Ribble said,

    September 4, 2009 @ 6:44 pm

    Hi Paul,

    The problem is that you’re using an old version of Arduino (0015). Version 0016 added some new functions to the serial library that I used. I suggest upgrading to 0017 like I used (I believe I mentioned this in the comments).

  14. Paul said,

    September 8, 2009 @ 7:09 pm

    Thanks Maurice. The code now compiles after upgrading to version 0017.

  15. Andres said,

    October 2, 2009 @ 9:37 pm

    Hi Maurice,

    Great code! I just have one question, I want to send an array of bytes. I call the first writeUInt make a small delay, and then call it again to send the next int. The reciever, however, only gets the first number. What am I doing wrong?

    This is the piece of code i’m using:

    if ((prev_button == BUTTON_NOT_PRESSED) && (cur_button == BUTTON_PRESSED))
    {
    writeUInt(271); // Put any number you want to send here (71 is just a test)
    delay(50);
    writeUInt(280);
    }

  16. Maurice Ribble said,

    October 3, 2009 @ 6:08 am

    Andres, that does seem to be strange. I don’t have this setup anymore currently, but I do plan to use this on future projects so if there is a problem like this I’ll eventually hit it and have to fix it. Unfortunately, that doesn’t help you right now. I really don’t know what would be causing this issue and I’m sure I tested something like that. Maybe you’ve found some corner case bug. If you need this solved soon, all I can suggest is trying to debug the problem yourself by looking at what data is being send and what data is being received. Do you have access to a second set of hardware to verify that’s not the problem?

  17. Michael said,

    October 10, 2009 @ 7:41 pm

    Hi Maurice,

    I opened RFDriver.pde on Arduino 0017 and it tells me that

    ” The file “RFDriver.pde” needs to be inside a sketch folder named “RFDriver”. Create this folder, move the file, and continue? ”

    So I press ‘OK’ and it moves it into it’s own folder, same thing with RFLink.pde

    Then when I tried to compile the code and upload it, it says that there is an ‘Error compiling’.

    o: In function `main’:
    E:\Arduino\Arduino Sketches\RF_Link\RFDriver\applet/RFDriver.cpp:90: undefined reference to `setup’

    E:\Arduino\Arduino Sketches\RF_Link\RFDriver\applet/RFDriver.cpp:93: undefined reference to `loop’

    Any idea what’s going on?

  18. Maurice Ribble said,

    October 11, 2009 @ 6:33 am

    You should create a folder called RFLink and put both RFLink.pde and RFDriver.pde in that folder. Then from the Arduino gui open RFLink.pde. This should basically markRFLink as the project and you can then open supporting files like RFDriver from this RFLink project. If you keep having trouble you can just move the code from both of these files into a single file.

  19. cjgaus said,

    October 14, 2009 @ 9:21 pm

    I have a arduino mega. Will the code still be the same for the receiving? If so i only have a basic stamp and a arduino. How could i transmit on the bs2?

  20. Nick said,

    October 16, 2009 @ 1:07 pm

    I have this type of transmitter set up with a PIC micro talking to another PIC micro. On the receiver end I used a comparator op-amp with a pretty high voltage reference of 4 volts. This makes for a much cleaner data signal to the micro. Using Asynchronous communication you need to sync up the reciever to the transmitter. I used Hex 0×55 because it gives an even number of oneses and zeroses (as one of my old instructors used to refer the them as) . Then I send the data byte, then the checksum byte. The checksum is just Here is an example of basic reciever test code I used:
    Using some sort of system (checksum or what have you) to filter out the junk.
    Hope this helps some people out with these nifty toys!

    *******************************************************************************************
    #include
    #fuses NOWDT, NOPROTECT, NOBROWNOUT, NOLVP, NOPUT
    #use delay (clock = 8M)
    #use rs232(uart1, baud= 2400, xmit=PIN_A2,rcv=PIN_A3, Errors)
    char first = ‘z’;
    char second = ‘z’;
    char third = ‘z’;
    char data = ‘z’;

    void main(){
    printf(“Hello!\r\n”);
    while(1){// the forever loop
    //////////////////reset data/////////////////////
    data = ‘z’;
    first = ‘z’;
    second = ‘z’;
    third = ‘z’;
    ////////////////////////////////////////////////////

    if (!kbhit()); //Waits until the UART receives a byte
    first = getc(); // Takes the first byte after it is received and stores it in variable first

    if (first == 0×55){ //if the first byte is equal to the start byte, continue…
    second = getc(); //recive data
    third = getc(); //recieve checksum
    if (first + second == third) data = second;// only accept the data as valid if the received data + 0×55 is
    //equal to the received checksum byte (simple)
    }

    printf(“%c, %c, %c, %c \r\n”, first, second, third, data); // outputs recieved bytes.

    }
    }

    *******************************************************************************************

  21. Paul said,

    October 18, 2009 @ 12:21 pm

    Maurice,

    Do you see any way for the receiver to be connected to an Arduino board at the same time as an ethernet shield? I tried a work-around ’soft serial’ solution, but it seems messy and not very reliable. Thanks in advance for your input!

    Paul

  22. Maurice Ribble said,

    October 18, 2009 @ 1:51 pm

    I presume the ethernet shield uses the hardware gart? If that’s true there are a few solutions.

    1) Software Serial. I’ve used this and as long as you structure your code so you can keep polling for a transmission and there is nothing too CPU intensive this works fine.
    2) You could use put a switch controlled by a different pin that selects whether to use the ethernet shield or the radio receiver. There are lots a different switch options, but two 5 cent transistors would be the cheapest digitally controlled switch.
    3) The Arduino mega has multiple hw garts and could run both that the same time.

  23. Andres said,

    October 21, 2009 @ 11:15 am

    Do you have any references for antennas? I’ve been using a simple cable antenna, but I’m building a nice prototype and can’t use a cable antenna. I’ve tried using radio antennas, but they don’t seem to work. I’m not looking to extend the range that much, ~100 meters is fine, but I do need a nice antenna to fit in a small platic case. Any ideas?

    Thanks!

  24. Maurice Ribble said,

    October 21, 2009 @ 11:40 am

    I used a 23 cm piece of wire. This seemed to work pretty good for me. You can try this and if it’s not good enough then look for a better solution.

  25. Bryan said,

    November 11, 2009 @ 2:52 pm

    When compiling it throws the following flag associated with the line that reads “writeUInt(271);”

    In function ‘void loop()’:
    error: ‘writeUInt’ was not declared in this scope

    Any ideas how to fix this compiling error? Thanks!

  26. Anonymous said,

    November 12, 2009 @ 7:08 am

    It sounds like your project only has the RFLink.pde file above and not the RFDriver.pde file. If you are having the multiple files working feel free to copy and past the functions you need from RFDriver into RFLink.

  27. mo said,

    November 21, 2009 @ 5:39 pm

    Hi ..
    thanks for the tip…
    i just have some questions:

    on the transmitter side: what is the DATA transmitted? is it bitwise Data or bytes or what exactly? like sending 10101010… and so on?

    on the reciever side: will i do have to read it out as a bus? how will the data arrive ?

    just one question:
    what if i dont have the Arduino board ?
    i just need to send an array of binary bits and recieve them on the other side and show them on a lcd?
    is it possible with those boards?
    thanks in advance

  28. Maurice Ribble said,

    November 21, 2009 @ 7:20 pm

    The software sends the data one byte at a time, but it is a serial connection so at the hardware level it is one bit at a time.

    You don’t need Arduino, but you will need some sort of microcontroller. You need something to run a driver for the LCD and you need something to do the noise filtering that I did in the software for this project.

RSS feed for comments on this post

Leave a Comment