Packet radio example

I received an Arduino Uno for Christmas this year, so my Dad and I spent some of the holiday time doing some projects.  His job was to get a circuit built implementing a 74HC595 Shift Register and my task was to set up some RF communication.  Dad completed his project before I did, but as I’m on vacation all this week, I was able to finally get something up and running.

Hardware

I have an XD-FST transmitter and an XD-RF-5V receiver.  I’m pretty sure it is this unit.  I’ve got my Arduino connected to my desktop PC, and I’ll hook the receiver up to that.  I’d like to use an ATTiny85 to host the transmitter.  To program the ATTiny85, I’ll be using one of my Raspberry Pis which I’ve set up using these instructions I’ve posted previously. (Although there is one caveat, which I’ll come to later in the post.)

Attempt 1. UART

The (now deprecated) VirtualWire documentation mentions that using the UART lines on the Arduino to communicate with RF units is doable, but problematic in that it does not adequately deal with noise. We’ll see that is true, but it is instructive to use the UART first to make sure that things are running.

Wiring

To minimize problems, I have the ATTiny85, XD-FST and XD-RD-5V all on one protoboard that is powered by the 5 V line of the Arduino Uno. Power and ground connects are made as needed. Pin 3 (which is I/O pin 4 – PB4) is connected to the data pin of the XD-FST and one of the data pins (it matters not, which one) of the XD-RD-5V is connected to RX (pin 0) of the Arduino. Note, I need to remove this connection each time I upload the sketch or else the Arduino IDE reports communication errors.

Software

Two very simple programs. For the ATTiny85, I am using SoftwareSerial to emulate serial communcation on I/O pins 3 (RX) and 4 (TX). Since I am only transmitting data, and the ATTiny85 only has a few I/O pins, I would like to avoid wasting the RX assignment, but that’s a project for later.

#include <SoftwareSerial.h>

SoftwareSerial mySerial(3,4); //RX, TX
int counter = 0;
void setup()
{
  mySerial.begin(2400);
}

void loop()
{
  mySerial.println(counter);
  counter = (counter + 1 ) % 256;
  delay(1000);
}

The code is straightforward: set up the software serial pins, initialize it to communicate at 2400 baud, and transmit a number every second. The counter loops after 256 iterations.  The Arduino Uno program:

int val;

void setup() {
  Serial.begin(2400);
}

void loop() {
  int incoming = Serial.available();
  if (incoming > 0)
  {
    val = Serial.parseInt();
    Serial.println(val);
  }
}

Again, we initialize serial communication to operate at 2400 baud and then continually check for data, parsing it as a number (to avoid mis-interpretation of the data as a stream of ASCII codes) and printing that number to the serial line, which we can observe with the serial monitor.

Results

The results are as predicted – while there is communication between the two microcontrollers, a lot of messages are missed.  Instead of seeing incremental numbers every second, I observed several gaps in the sequence.  I estimate that there was at least 30% packet loss, and this is with the transmitter and receiver spaced 2 cm apart from one another.  One can confirm that serial communication is working properly by bypassing the transmitter and receiver, and connecting pin 3 of the ATTiny85 directly to the Arduino pin 0.  With this connection, an error-free stream of numbers is printed to the serial monitor.

Alternatives

After a bit of web searching, there appears to be three main options for getting RF communication to work with cheap transmitters/receivers and microcontrollers.  In addition to the aforementioned VirtualWire, there’s its successor, RadioHead and Arduino Manchester Encoding.  I bounced around between these three options, trying to find a configuration that would work.  I had no problems on the Arduino side, the main issue is with the ATTiny85.  All three libraries supposedly work, although it became clear that I was not the only one having a problem.

To make a long story (well, 1.5 days of vacation time – and any vacation time spent playing with electronics is time well spent) short, my issue was with the library I have been using to compile ATTiny85 code.  Instead of the one I mentioned in the Hardware section above, I tried this one.  I have not spent any time trying to figure out why one would work and one wouldn’t, but at the end of the day, the latter library resulted in a working program.

The winner is

So for completeness, here are the two programs I am using to communicate between the ATTiny85 and the Arduino.  It’s essentially the example code from the RadioHead documentation, with an added bit from the Arduino forums that simplifies sending sensor data.  The wiring is set up a little differently for this case: the transmitter is connected to pin 2 (not pin 3) of the ATTiny85 and I have an LED connected to pin 5 of the ATTiny85 to assist in debugging.  The receiver is now connected to pin 11 of the Arduino.

/* 
 *  Demonstration of RF communication between Arduino Uno
 *  and an ATTiny85 using the XD-RF-5V and XD-FST wireless
 *  receiver/transmitters.
 *  
 *  Uses the RadioHead library: http://www.airspayce.com/mikem/arduino/RadioHead/
 *  with modifications of the example code provided therein.
 *  Includes data transfer ideas mentioned by swe-dude in the 
 *  Arduino forums: https://forum.arduino.cc/index.php?topic=313587.0
 *  
 *  This code is public domain.
 *  
 *  Receiver code intended to be run on the Arduino Uno.  RF reciever
 *  (XD-RF-5V) data pin connected to pin 11.
 *  
 */
#include <RH_ASK.h>

// Default settings for speed, rx, tx, en, inv explicitly stated
RH_ASK driver(2000,11,12,10,false);

// It is **CRITICAL** that the receiver and transmitter code use the same struct
struct packet
{
  // Naturally, additional types can be added here
  int counter;
} data;

void setup() 
{
  // Output will be sent to the serial monitor.
  Serial.begin(9600);
  if (!driver.init()){Serial.println("Driver init. failed.");}
  else {Serial.println("Driver initialized.");}
}

void loop() 
{
  // Create temporary space for data
  uint8_t buf[RH_ASK_MAX_MESSAGE_LEN];
  uint8_t buflen = sizeof(buf);

  // If data are received, copy it into the data structure
  // and print the counter to the serial line.
  if (driver.recv(buf,&buflen))
  {
      memcpy(&data,buf,sizeof(data));
      Serial.println(data.counter);
  }
}

 

/* 
 *  Demonstration of RF communication between Arduino Uno
 *  and an ATTiny85 using the XD-RF-5V and XD-FST wireless
 *  receiver/transmitters.
 *  
 *  Uses the RadioHead library: http://www.airspayce.com/mikem/arduino/RadioHead/
 *  with modifications of the example code provided therein.
 *  Includes data transfer ideas mentioned by swe-dude in the 
 *  Arduino forums: https://forum.arduino.cc/index.php?topic=313587.0
 *  
 *  This code is public domain.
 *  
 *  Transmitter code intended to be run on the ATTiny85.  RF transmitter
 *  (XD-FST) data pin connected to pin 2 and resistor/LED connected to pin 5.
 */
#include <RH_ASK.h>

// It's possible that leaving the enable pin set to the 
// default value (10) will cause some problems, but I have
// not encountered them yet.  Transmit pin is PB3 or pin 2
// on the ATTiny85.
RH_ASK driver(2000,4,3);

// It is **CRITICAL** that the receiver and transmitter code use the same struct
struct packet
{
  // Naturally, additional types can be added here
  int counter;
} data;

byte buf[sizeof(data)]={0};

// Allows the ATTiny85 to communicate via LEDs
void blink(int p, int n)
{
  int i;
  for (i = 0;i<n;i++)
  {
    digitalWrite(p,LOW);
    delay(100);
    digitalWrite(p,HIGH);
    delay(100);
  }
  digitalWrite(p,LOW);
}

void setup()
{
  // LED connected to PB0 (pin 5)
  pinMode(0,OUTPUT);
  
  // Blink 5 times if something is wrong, once if everything is OK.
  if (!driver.init()) { blink(0,5); }
  else { blink(0,1); }
  
  // Initialize the counter and any other data in the packet
  data.counter=0;
}

void loop()
{
  // Copy data packet into the buffer
  memcpy(buf, &data,sizeof(data));
  byte buflen=sizeof(data);
  // Send buffer
  driver.send((uint8_t *)buf,buflen);
  driver.waitPacketSent();
  // Update data. In real application, this would probably go first in the loop.
  data.counter = (data.counter + 1 ) % 256;
  // Indicate that loop is complete with 2 blinks.
  blink(0,2);
}

Conclusion

I found this a very fun way to spend the holiday vacation time, and since I have a few more days to play, I’m likely to explore radio communication a little further.  This cheap receiver/transmitter pair does not have a long range; in fact, I’m limited to about 10 cm.  I haven’t yet tried an antenna, which supposedly will improve the performance.  Since there are several analog inputs on the ATTiny85, I’d like to connect a few rudimentary sensors to see how far I can push remote sensing with this setup.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.