Sunday, May 5, 2013

Remote Temperature Sensor using 434MHz RF Link and Arduinos

As another quick experiment to after my ElectricImp project, I decided to go the full opposite of the Imp's highlevel programming and automatic WiFi connectivity and use a low-tech 434MHz RF transmitter/receiver pair powered by Arduinos.

Hardware

The ElectricImp's WiFi connection and programming is wonderfully simple but price, power requirements, and its closed source aspect made me not invest more time here.  Instead I tried an old-school RF pair to transmit Temperature data between two Arduinos.

Transmitter

Since I'd like to keep using 3.3v sensors such as the TMP102, I got a low power 3.3v 8MHz Arduino Mini Pro to read the sensor and transmit the data to the RF Link Transmitter.  This way I don't have to translate the signals between 5v and 3.3v as i would have had to do with a regular 5v Arduino.  The soldering was a quick and dirty job where I connected the power and I2C link directly to the Arduino:



As you can see above I simply hardwired a signal line from the Arduino over to the RF transmitter using a white cable.  The connection to the TMP102 sensor is done over I2C using A4/A5 with connectors protruding from the back of the Arduino and looping back over the the sensor.

The main focus is the transmitter since the receiver will most likely be a single AC powered device.  Therefore I dug out my old Uno and a ProtoShield to hack together the receiver side.  I really just wanted to print the information on the screen but I also had to experiment with some error checking code in the end.  RF transmissions are NOT reliable as I found out.

Receiver

The final receiver unit ended up using not much more than the Uno, a ProtoShield, and an RF Link Receiver:


The code uses the VirtualWire Arduino library and was amazingly simple to use and setup.  With no antenna to speak of, I ended up with about a 100ft range which is not bad since I was feeding the transmitter a paltry 3.3v.  The RF Link specsheet says a range of 1.5 to 12v so I was planning to do some range experimentation, figuring out how supply voltage would affect range but ended up moving on to the next project that uses XBee radios instead.

Software

The code below is for both the receiver and transmitter with a simple #ifdef statements redirecting the Arduino compiler to build each sketch.  The LED usage made it easy to debug and do some manual range checking by just watching for blinks every second a GOOD reading was received.  Removing the checksum code allows you to see the corrupted messages as distance and interference increased.

Fun little project but I won't be going in this direction since I don't want to deal with cross talk interference with  10+ sensor nodes and the re-transmission protocols required.  I was also not happy with having to add an Arduino Pro Mini to each node even at $10 per node although eBay sells them for $6 now which is getting very tempting...


#include <Wire.h>
#include <VirtualWire.h>

static int tmp102Address = 0x48;
static byte kID = 0;
byte gCounter = 0;


void encode(byte ioBuffer[8], byte inTemp1, byte inTemp2)
{
  ioBuffer[0] = 't';
  ioBuffer[1] = 'e';
  ioBuffer[2] = 'm';
  ioBuffer[3] = 'p';
  ioBuffer[4] = gCounter++;
  ioBuffer[5] = kID;;
  ioBuffer[6] = inTemp1;
  ioBuffer[7] = inTemp2;
}

bool decode(byte ioBuffer[8], byte outTemp[2])
{
  Serial.print((char)ioBuffer[0]);
  Serial.print((char)ioBuffer[1]);
  Serial.print((char)ioBuffer[2]);
  Serial.print((char)ioBuffer[3]);

  Serial.print(" #");
  Serial.print((int)ioBuffer[4]);

  Serial.print(" id-");
  Serial.print((int)ioBuffer[5]);

  Serial.print(" [");
  Serial.print((int)ioBuffer[6]);
  Serial.print(',');
  Serial.print((int)ioBuffer[7]);
  Serial.print("] ");

  outTemp[0] = ioBuffer[6];
  outTemp[1] = ioBuffer[7];

  return ioBuffer[0] == 't' && ioBuffer[1] == 'e' && ioBuffer[2] == 'm' && ioBuffer[3] == 'p';
}

#define transmitter

#ifdef transmitter
void setup()
{
  Serial.begin(9600);
  Serial.println("Transmitter");
  Wire.begin();

  // Initialise the IO and ISR
  vw_set_ptt_inverted(true); // Required for DR3100
  vw_setup(2000); // Bits per sec
  vw_set_tx_pin(3);
}

void loop()
{
  byte message[8];

  Wire.requestFrom(tmp102Address, 2);  
  encode(message, Wire.read(), Wire.read());

  digitalWrite(13, true); // Flash a light to show transmitting
  vw_send(message, 8);
  vw_wait_tx(); // Wait until the whole message is gone
  digitalWrite(13, false);
  Serial.println(gCounter);

  delay(1000); // Send a temperature sample every second
}

#else // receiver
void setup()
{
  Serial.begin(9600);
  Serial.println("Receiver");

  // Initialize the IO and ISR
  vw_set_ptt_inverted(true); 
  vw_set_rx_pin(3);
  vw_setup(2000);  // Bits per sec

  vw_rx_start();       // Start the receiver PLL running
}

void loop()
{
  uint8_t buf[VW_MAX_MESSAGE_LEN];
  uint8_t buflen = VW_MAX_MESSAGE_LEN;

  if (vw_get_message(buf, &buflen)) // Non-blocking
  {
    digitalWrite(13, true); // Flash a light to show received good message

    byte id;
    byte counter;
    byte temp[2];
    decode(buf, temp);
   
    // 12bit int so using two's compliment for negative
    int TemperatureSum = ((temp[0] << 8) | temp[1]) >> 4;
    float celsius = TemperatureSum * 0.0625;
    Serial.print(celsius);
    Serial.print("C ");
    Serial.print(celsius * 9 / 5 + 32);
    Serial.println("F");
     
    delay(100);
    digitalWrite(13, false);
  }
}
#endif


No comments:

Post a Comment