Saturday, July 7, 2012

Arduino: Sending integers over RF with VirtualWire

When I tried to send the output from a sensor over RF with VirtualWire I quickly learned that it wasn't as simple as one two three.

But eventually I got it working with a little help from the good folks at the Arduino forum

VirtualWire is a library that makes it really easy to transmit using RF modules. I've successfully used two different kinds of 434 Mhz modules with it but it has support for other types aswell. 

The code and comments below are pretty much self explanatory:

Transmitter (download source code)
/* 

Sensor Transmitter
By Markus Ulfberg 2012-07-06

Takes a sensor reading 0-1023
converts it to a char array and sends 
to RF receiver unit via VirtualWire  

*/

#include <VirtualWire.h>

// LED's
const int ledPin = 13;

// Sensors 
const int Sensor1Pin = A2;
// const int Sensor2Pin = 3; 

int Sensor1Data;
//int Sensor2Data;
char Sensor1CharMsg[4]; 

void setup() {

 // PinModes 
 // LED 
 pinMode(ledPin,OUTPUT);
 // Sensor(s)
 pinMode(Sensor1Pin,INPUT);
 
 // for debugging
 Serial.begin(9600); 
 
 // VirtualWire setup
 vw_setup(2000);     // Bits per sec


}

void loop() {
  
  // Read and store Sensor 1 data
  Sensor1Data = analogRead(Sensor1Pin);
  
  // Convert integer data to Char array directly 
  itoa(Sensor1Data,Sensor1CharMsg,10);
  
  // DEBUG
  Serial.print("Sensor1 Integer: ");
  Serial.print(Sensor1Data);
  Serial.print(" Sensor1 CharMsg: ");
  Serial.print(Sensor1CharMsg);
  Serial.println(" ");
  delay(1000);

  // END DEBUG
 
 digitalWrite(13, true); // Turn on a light to show transmitting
 vw_send((uint8_t *)Sensor1CharMsg, strlen(Sensor1CharMsg));
 vw_wait_tx(); // Wait until the whole message is gone
 digitalWrite(13, false); // Turn off a light after transmission
 delay(200); 
 
} // END void loop...

Receiver (download source code)
/* 

Sensor Receiver 
By Markus Ulfberg 2012-07-06

Gets a sensor reading 0-1023 in a char array
from RF Transmitter unit via VirtualWire 
converts char array back to integer

*/

#include <VirtualWire.h>

// LED's
int ledPin = 13;

// Sensors 
int Sensor1Data;

// RF Transmission container
char Sensor1CharMsg[4]; 

void setup() {
  Serial.begin(9600);
  
  // sets the digital pin as output
  pinMode(ledPin, OUTPUT);      
    
    // VirtualWire 
    // Initialise the IO and ISR
    // Required for DR3100
    vw_set_ptt_inverted(true); 
    // Bits per sec
    vw_setup(2000);     
    
    // Start the receiver PLL running
    vw_rx_start();       

} // END void setup

void loop(){
    uint8_t buf[VW_MAX_MESSAGE_LEN];
    uint8_t buflen = VW_MAX_MESSAGE_LEN;
    
    // Non-blocking
    if (vw_get_message(buf, &buflen)) 
    {
    int i;
        // Turn on a light to show received good message 
        digitalWrite(13, true); 
    
        // Message with a good checksum received, dump it. 
        for (i = 0; i < buflen; i++)
    {            
          // Fill Sensor1CharMsg Char array with corresponding 
          // chars from buffer.   
          Sensor1CharMsg[i] = char(buf[i]);
    }
        
        // Null terminate the char array
        // This needs to be done otherwise problems will occur
        // when the incoming messages has less digits than the
        // one before. 
        Sensor1CharMsg[buflen] = '\0';
        
        // Convert Sensor1CharMsg Char array to integer
        Sensor1Data = atoi(Sensor1CharMsg);
        
        
        // DEBUG 
        Serial.print("Sensor 1: ");
        Serial.println(Sensor1Data);
        
        // END DEBUG
                
        // Turn off light to and await next message 
        digitalWrite(13, false);
    }
}


Source code formatted for blogger by: formatmysourcecode.blogspot.com

23 comments:

  1. Hi Markus,

    I will use a DHT22 sensor with RF and Virtual wire. Is it possible read an digital sensor (DHT22) instead of an analog sensor? Can you help me?

    Greetings,
    Jack
    Holland

    Next the sketch I use for the DHT22 sensor alone:

    // DHT22 Sketch from ADAFRUIT

    #include "DHT.h"

    #define DHTPIN 2 // what pin we're connected to
    #define DHTTYPE DHT22 // DHT 22 (AM2302)

    // Connect pin 1 (on the left) of the sensor to +5V
    // Connect pin 2 of the sensor to whatever your DHTPIN is
    // Connect pin 4 (on the right) of the sensor to GROUND
    // Connect a 10K resistor from pin 2 (data) to pin 1 (power) of the sensor

    DHT dht(DHTPIN, DHTTYPE);

    void setup() {
    Serial.begin(9600);

    dht.begin();
    }

    void loop() {
    float h = dht.readHumidity();
    float t = dht.readTemperature();

    // check if returns are valid, if they are NaN (not a number) then something went wrong!
    if (isnan(t) || isnan(h)) {
    Serial.println("Failed to read from DHT");
    } else {
    Serial.print("Humidity: ");
    Serial.print((h), 1);
    Serial.print(" %\t");
    Serial.print("Temperature: ");
    Serial.print((t), 1);
    Serial.println(" C");

    delay(2000);
    }
    }

    ReplyDelete
  2. Hi Jack,

    With my code you can send any 4 digit integer with virtual wire it doesn't matter if it's from an analog sensor, digital sensor or just made up. I suggest you try this code first with a simple potentiometer or something to make sure you connection works. If you want to send a longer number you just need to change the number of chars in the char array.

    However I guess you want to send both humidity and temperature. I'm no expert and there are probably better solutions than the one I will suggest but I'll try to point you in a good direction.

    I would send the data in two separate messages. The messages would be just as the one in my code but with one char added to identify the message on the receiving end. Let's say the letter "T" for Temperature and "H" for Humidity.

    Like this:
    T14
    H33

    Then it's just a matter of reading the first char in the array to find out if it's the temperature or humidity being sent, strip that char away and use the rest of the char array as in my code.

    Exactly how you would accomplish this I don't know but if I recall correctly it's not that hard to mess around with char arrays. You can always hijack the thread I've linked to in the beginning of the post to see if someone has another solution.

    If you do work it out you're more than welcome to post your result here.

    Best of luck
    Markus

    ReplyDelete
    Replies
    1. Hi Markus,

      Thanks for your information. When I received my second Arduino from China I will try too make a sketch based on your sketch.

      Greetings,
      Jack

      Delete
    2. Good Luck, and do keep me posted of your progress.

      Delete
  3. Thanks for this code Markus.

    @jack, I am very interested in the same thing you are trying to acomplish. Any progress?

    ReplyDelete
  4. Really great stuff here. Just got a wireless rf light dimmer working from this!

    ReplyDelete
    Replies
    1. Hi Callil! Good to hear, let me ask you a question. Are you using PWM to control the dimming? If you are how is your range?

      The reason I'm asking is that I tried to control an RGB LED via PWM but the PWM caused interference that limited the range severely and sometimes froze the board completely.

      Delete
  5. how to change the code for two sensors transmission and reception

    ReplyDelete
    Replies
    1. Hi Leo Paul Dinesh!
      I was actually going to do this myself initially but I never got around to it. However the way I planned was to add a char say A or B to each string before sending it. That way you could identify what sensor is sending what on the receiving end.

      Delete
  6. i tried a lot but was not successful
    will u help me out

    ReplyDelete
  7. I'm trying to do something very similar to this. I'd love to get your advice.

    I'd like to read digital input from pin 2, store it as a value, then write that value to pin X. Ultimately I'd like to be able to look at the value that was sent so I could hard-code it and transmit it over and over again when I wanted to. I tried setting sensor 1 pin to 2, and changing analogread to digital read, but it didnt' work.

    It's for a remote controlled ceiling fan.

    ReplyDelete
    Replies
    1. That shouldn't be a problem. If you want to transmit a digital value you could simply send a string or even a single char such as "1" for ON and "0" for OFF.
      If I understand you correctly this is how would go about this. First I would make the Arduino read pin 2 into a variable, then check the value of that variable. Depending on if it's 1 (true/high) or 0 (false/low) I'd send one of two predefined messages. You could just repeat that message until you get a different read on pin 2.

      Hope this helps, and good luck with your project.

      Delete
  8. This comment has been removed by the author.

    ReplyDelete
    Replies
    1. I guess a question I have is how do I write to and send strings of binary?

      I created a "connector" that connects the "button board" (transmitter of code) with the actual transmitter (receiver of the code, but is what transmits the code to the light) with:

      int inpinState = digitalRead(inpin); //reads the state of the input pin
      digitalWrite(outpin, inpinState); //writes the state of the input pin on the output pin

      And it works great. The Arduino passes the message and the light responds to button presses.

      If I use your above receive code unmodified or even with char Sensor1CharMsg[4]; up to Sensor1CharMsg[200]; nothing happens.

      I can use digitalread and print to the console. Then I copy the transmit code and try to transmit it with

      digitalWrite(outpin, 0)
      digitalWrite(outpin, 0)
      digitalWrite(outpin, 1)
      digitalWrite(outpin, 1)
      digitalWrite(outpin, 0)
      ..etc

      didn't work.

      What could I be missing?

      Delete
    2. I think the problem here is that you are trying to transmit by using digitalWrite, that is not how the code works. Sending data is done with the virtualWire library i.e this part of the code:

      digitalWrite(13, true); // Turn on a light to show transmitting
      vw_send((uint8_t *)Sensor1CharMsg, strlen(Sensor1CharMsg));
      vw_wait_tx(); // Wait until the whole message is gone
      digitalWrite(13, false); // Turn off a light after transmission
      delay(200);

      In essence you should read whatever data you want to send to Sensor1Data. Then process this data to a char array. I do this by first declaring that sensor1Data will contain an integer. Then read the analog pin to store its value into the Sensor1Data variable.

      A bit further down the code you then see how this integer is converted to a char array by the use of "itoa" before it is stored in the Sensor1CharMsg variable. This is the variable that is later sent via virtualWire.

      You would probably have to declare Sensor1Data as a boolean variable instead and perhaps use "btoa" to convert it into a char array. Here's good info on boolean variables: http://arduino.cc/en/Reference/BooleanVariables

      However I don't actually know if there is such a thing as a "btoa" function, a quick google yielded some results so you might as well try it just to see if it works. If not a work around that could be to just do a simple if, then else function and check if Sensor1Data is "true" and if so set Sensor1CharMsg to "1" and if "false" set Sensor1CharMsg to "0". Check this link on how to set char arrays: http://arduino.cc/en/Reference/String

      Also try to make use of the debug parts in the code above. By checking your serial monitor you should be able to tell what's working and not.

      I hope this puts you on the right track.
      Best of luck with your project.
      // Markus

      Delete
  9. Alright, here's my stupid question. what pin and i connecting the data line of the RF transmitter to? and i might as well ask what pin of the receiver arduino aswell.

    ReplyDelete
    Replies
    1. Not a stupid question at all. I think I use the default VirtualWire pins which would mean pin 11 for the receiver and pin 12 for the transmitter.
      http://www.airspayce.com/mikem/arduino/VirtualWire.pdf

      Delete
    2. thanks! Is there any reason why the data sent unmapped stops the reciever when it reaches anything above 1000? when it's mapped it works fine.

      Also, I'm trying to power a servo and i've noticed that these 2 libraries conflict, is there anyway to disable virtualwire pins?

      Delete
    3. What do you mean by mapped? I do recall that I did have some sort of similar problem and I think it might have been some sort of memory/string related problem.

      Regarding the transmit and receive pins just check the link to the virtualwire pdf I posted in my previous comment.

      Delete
  10. Hey, I tried to implement your code, but I'm getting some strange results, maybe they'll make sense to you though.

    I've tested the RF setup, and it successfully transmits a message I choose. However, when I tried to send an integer using your method, the receiver never gets the message.

    Then, while debugging, I accidentally left the itoa line of code and msg = "2" like this.

    itoa(LightValue,msg,10);
    msg = "2";

    Now it sends only the first digit of a truncated version of LightValue, so for example, if LightValue is 234, msg becomes 34, and then the receiver only gets the first digit..3 in this case.

    Any idea what I'm doing wrong?

    ReplyDelete
    Replies
    1. Hi CoopGuy,

      First of all have you declared msg as a char array? char msg[4];

      If not try that. Also when you're dealing with char arrays the number in the brackets are the position so you would only get character/digit located on the second position of the array. In the case of 234 that would be 3.

      Also someone mentioned to me that I might have used too little space in my char array to send four digits (0 to 1023) since they have to end with a null char. So if you want to send a maximum of four digits try using a char array with 5 positions i.e. char msg[5];

      Try that and tell me how it works.
      And good luck!

      Delete