VirtualWire and variables

Hi guys

Im trying to use the cheap RF links to send a variable from an Uno to a Leonardo. The variable is the temperature which my sensors reads. Im using the LM35DT sensor. I managed to read the correct temperature - in celsius. But I can't manage to receive the right values on the receiver side.

Transmitter code:

#include <VirtualWire.h>

int tempPin = 0;
long temperature = 0;

char msg[24];

void setup()
{
  Serial.begin(9600);
  analogReference(INTERNAL);
  
  //VirtualWire setup
  vw_set_tx_pin(12);
  vw_setup(2000);
}

void loop() {
  //Finding the average temperature after 20 readings
  int aRead = 0;
  for (int i = 0; i < 20; i++) {
    aRead = aRead+analogRead(tempPin);
  }
  aRead = aRead / 20;
  
  //Converting the temperature to celsius
  temperature = ((100*1.1*aRead)/1024)*10;

//Sending the temperature value to the reciever
  sendTemp(long(temperature));
  delay(100);
}

void sendTemp(int value) {
  // prints a value of 123 as 12.3
  long hele = value / 10;
  float deci = (value % 10)/10.0;
  float calTemp = hele + deci;
  
  sprintf(msg, "%f", calTemp);
  Serial.println(msg);
  vw_send((uint8_t *)msg, strlen(msg));
}

Reciever code:

#include <VirtualWire.h>

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

  //VirtualWire Setup
  vw_setup(2000);
  vw_set_rx_pin(11);
  vw_rx_start();
}

void loop()
{
  uint8_t buflen = VW_MAX_MESSAGE_LEN; //Maximum length of the message
  uint8_t buf[buflen]; // The buffer that holds the message

  if (vw_get_message(buf, &buflen)) 
  {       
    for (int i = 0; i < buflen; i++) //Checking what the buffer holds
    {
      Serial.println(buf[i]);  
    }
  }
}

On the receiver side Im just getting the number 63 over and over again. It doesn't matter if I heat the sensor. The value won't change.

I read that to send variables I need to use the sprint() function. Am I right in assuming that this function in this case converts from a float to a string? I placed a Serial.println to check the output of this sprint() function but I am only getting a lot of question marks.

What is the deal here? I think error is the sprint function which I don't understand completely. Thanks a lot!

Hi Wasd

sprintf(msg, "%f", calTemp);

Unfortunately, sprintf does not work for floats on the Arduino. The code compiles and runs, but it puts "????" in the string. The ASCII code for '?' is 63 :slight_smile:

You can use dstrtof instead. For example.

dstrtof(calTemp, 5, 1, msg));

EDIT see typo-free code in later post :blush:

That examples formats into 3 digits, decimal point, and 1 digit after decimal point.

Regards

Ray

Thanks for reply. Well that explains my problem.

I tried you suggestion. Changing this line:

sprintf(msg, "%f", calTemp);

to your suggestion:

dstrtof(calTemp, 5, 1, msg);

But it gives me this error:

sketch_sep12c.ino: In function 'void sendTemp(int)':
sketch_sep12c:40: error: 'strtof' was not declared in this scope

Also: I couldn't find any references on dstrtof. What does this? Why are you writing 5 and 1?

Sorry, Wasd. Typo :blush: It should be

dtostrf(calTemp, 5, 1, msg);

The 5 sets the minimum print width (including minus sign, digits before decimal point, the decimal point and digits after the decimal point). The 2 sets the number of digits after the decimal point.

No problem! :slight_smile:

But still, Im not receiving the right numbers?

This is what the receiver prints in the serial monitor:

32
50
53
46
48
32
50
52
46
57

It is like it is receiving 5 lines every time.

The receiver code is the same. The transmitter code now looks like this:

#include <VirtualWire.h>

int tempPin = 0;
long temperature = 0;

char msg[24];

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

  //VirtualWire setup
  vw_set_tx_pin(12);
  vw_setup(2000);
}

void loop() {
  //Finding the average temperature after 20 readings
  int aRead = 0;
  for (int i = 0; i < 20; i++) {
    aRead = aRead+analogRead(tempPin);
  }
  aRead = aRead / 20;

  //Converting the temperature to celsius
  temperature = ((100*1.1*aRead)/1024)*10;

  //Sending the temperature value to the reciever
  sendTemp(long(temperature));
  delay(100);
}

void sendTemp(int value) {
  // prints a value of 123 as 12.3
  long hele = value / 10;
  float deci = (value % 10)/10.0;
  float calTemp = hele + deci;

  dtostrf(calTemp, 5, 1, msg);
  Serial.println(msg);
  vw_send((uint8_t *)msg, strlen(msg));
  vw_wait_tx();
  delay(2000);
}

In your receiver code, change to this

  if (vw_get_message(buf, &buflen)) 
  {       
    for (int i = 0; i < buflen; i++) //Checking what the buffer holds
    {
      Serial.print((char)buf[i]);  
    }

Those numbers you are getting are ASCII codes for the characters in the string version of the float. 48 is '0', 49 is '1', etc.

Ahh yes.. Of course! :sweat_smile:

Last question. Im receiving the right numbers now. But they are displayed like this:

2
5
.
5
 
2
5
.
4
 
2
5
.
3

How to change them to something like this:

25.5
25.4
25.3

Also: The dtostrf function converts my temperature value into ASCII values which the VirtualWire Library understands. The function looks like this:

dtostrf(calTemp, 5, 1, msg);

calTemp = the "input" value
5 = It outputs 5 "numbers" including minus
1 = ??? no clue
msg = the "name" which the output values are stored in

Am i right?
Thanks a lot!

Last question. Im receiving the right numbers now. But they are displayed like this:

What do you suppose the ln on the end of println() means? What do you supposed would happen if you called print() instead?

While you ponder those, why don't you make the array a string (by NULL terminating it) and just print the string, instead of each individual character?

calTemp = the "input" value
5 = It outputs 5 "numbers" including minus
1 = ??? no clue
msg = the "name" which the output values are stored in

calTemp is the variable that holds the input value to the function
5 is the minimum number of characters it will use when formatted (in your example " 25.4" is five characters)
1 is the number of digits after the decimal point (the '4' in this example)
msg is the variable (a character array) in which the output is stored

PaulS:
What do you suppose the ln on the end of println() means? What do you supposed would happen if you called print() instead?

Im fully aware of the difference. My problem is that I'm not sure how to separate each individual character so it can be printed.

PaulS:
While you ponder those, why don't you make the array a string (by NULL terminating it) and just print the string, instead of each individual character?

Not sure how this is done either.

Not sure how this is done either.

You know the amount of data, right?

  if (vw_get_message(buf, &buflen))

Yep, right there in buflen.

So, buf[buflen] = '\0'; will put a NULL after the last character. Then, just Serial.println(buf); to get everything on one line.

Or, Serial.print() each character, then Serial.println() with no argument to just print a carriage return and line feed.

Thanks. It gave me an error:

call of overloaded 'println(uint8_t [(((unsigned int)(((int)buflen) + -0x000000001)) + 1)])' is ambiguous

So I changed the the code to this:

Serial.println((char *)buf);

Now the results will be printed on the same line. But it prints the same value 5 times:

23.9
 23.9
 23.9
 23.9
 23.9

Now the results will be printed on the same line. But it prints the same value 5 times:

You can delete the for loop, since you are printing all of buf in one print statement.

Thanks a lot. It all works now!

I was wondering about one question more: What if I want to send multiple variables?

This is how the code looks like:

Transmitter:

#include <VirtualWire.h>

int tempPin = 0;
float calTemp;

char msg[24];


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

  //VirtualWire setup
  vw_set_tx_pin(12);
  vw_setup(2000);
}

void loop() {
  temp(analogRead(tempPin)); //Calculating temperature in celcius
  sendData();
  delay(100);
}

void temp(int value) {
  long temperature = 0;
  //Reading the sensor 20 times and finding the average reading
  int aRead = 0;
  for (int i = 0; i < 20; i++) {
    aRead = aRead+value;
  }
  aRead = aRead / 20;

  //Converting the temperature to celsius
  temperature = ((100*1.1*aRead)/1024)*10;

  // prints a value of 123 as 12.3
  long hele = temperature / 10;
  float deci = (temperature % 10)/10.0;
  calTemp = hele + deci;
}

int sendData (){
  dtostrf(calTemp, 5, 1, msg);
  Serial.println(msg);
  vw_send((uint8_t *)msg, strlen(msg));
  vw_wait_tx();
}

Receiver:

#include <VirtualWire.h>

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

  //VirtualWire Setup
  vw_setup(2000);
  vw_set_rx_pin(11);
  vw_rx_start();
}

void loop()
{
  uint8_t buflen = VW_MAX_MESSAGE_LEN; //Maximum length of the message
  uint8_t buf[buflen]; // The buffer that holds the message

  if (vw_get_message(buf, &buflen)) 
  {       
    
      buf[buflen] = '\0';
      Serial.println((char *)buf);
    
  }
}

My own idea is sort of prefixing the data I send. Like sending a T right before sending the temperature data. Then sending an H before humidity. It isn't hard. I would just put something like this before vw_send command:

char t = T;
vw_send((uint8_t *)t, 1);

Right?

I think my problem is on the receiver side. How to make the Arduino detect and separate what it is receiving?

I might be completely wrong about this idea?

char t = T;

How is T defined? The letter T is 'T'.

You need to send the data like "T,xxx,H,yyy", all in one packet, using sprintf().

Use strtok() to parse the buffer on the other end.

Thanks.

Am I correct when assuming using the sprintf() function would look like this:

sprintf(Sensor1CharMsg, "%f,%f,%f,%f,", Sensor1Data, Sensor2Data, Sensor3Data, Sensor4Data);

If this is right, the problem is that thing with floats in sprintf and Arduino.

Could I just swap the sprintf() with dtostrf() like this??

dtostrf(Sensor1CharMsg, "%f,%f,%f,%f,", Sensor1Data, Sensor2Data, Sensor3Data, Sensor4Data);

On the receiver side could the code then look like this:

 void loop(){
    uint8_t buf[VW_MAX_MESSAGE_LEN];
    uint8_t buflen = VW_MAX_MESSAGE_LEN;
      
    if (vw_get_message(buf, &buflen)) 
    {
 int i;
        for (i = 0; i < buflen; i++)
 {            
          StringReceived[i] = char(buf[i]);
 }
  
      sscanf(StringReceived, "%f,%f,%f,%f,",&Sensor1Data, &Sensor2Data,&SensorData3,&SensorData4);

    }
}

Am I correct when assuming using the sprintf() function would look like this:

No. sprintf() on the Arduino doesn't support the %f format.

Could I just swap the sprintf() with dtostrf() like this??

Of course not.

But, you could use 4 calls to dtostrf() to create 4 strings, and then sprintf() to combine the strings into one, with separators.

On the receiver side could the code then look like this:

NO! Stop copying the damned data to another array.

I've tried to create the transmitter code - Am I complete screwed?

#include <VirtualWire.h>
#include <DHT.h>

//Defining DHT sensor
#define DHTPIN 2 
#define DHTTYPE DHT11  
DHT dht(DHTPIN, DHTTYPE);
float H = 0;

int tempPin = 0;
float calTemp;

char tString[24];
char hString[24];
char msg[27];


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

  //VirtualWire setup
  vw_set_tx_pin(12);
  vw_setup(2000);


  dht.begin();
}

void loop() {
  H = dht.readHumidity();
  analogReference(INTERNAL);
  temp(analogRead(tempPin)); //Calculating temperature in celcius
  analogReference(DEFAULT);
  sendData();
  delay(100);
}

void temp(int value) {
  long temperature = 0;
  //Reading the sensor 20 times and finding the average reading
  int aRead = 0;
  for (int i = 0; i < 20; i++) {
    aRead = aRead+value;
  }
  aRead = aRead / 20;

  //Converting the temperature to celsius
  temperature = ((100*1.1*aRead)/1024)*10;

  // prints a value of 123 as 12.3
  long hele = temperature / 10;
  float deci = (temperature % 10)/10.0;
  calTemp = hele + deci;
}

int sendData (){
  //Converting temperature to a string
  dtostrf(calTemp, 5, 1, tString);
  //Converting humidity to a string
  dtostrf(H, 5, 1,hString);
  //Combining to one string
  sprintf(msg, "%s, %s,", tString, hString);
  //Sending the string
  vw_send((uint8_t *)msg, strlen(msg));
  vw_wait_tx();
}

I have tried to understand the strtok() function which you recommend me using on the receiver side. However I got some problems understanding what it is and does - specially what token and null pointer are.

I've tried to create the transmitter code - Am I complete screwed?

Serial.print("message to send: [");
Serial.print(msg);
Serial.println("]");

after

  sprintf(msg, "%s, %s,", tString, hString);

would answer that, wouldn't it?

However I got some problems understanding what it is and does - specially what token and null pointer are.

Work on getting the data from point A to point B before you worry about how to parse it.

What, exactly, are you sending? What, exactly, are you receiving?

If you are sending something like "12.3, 25.4", then

char *token = strtok(msg, ",");

should result in token printing 12.3, and

token = strtok(NULL, ","); // Keep parsing same string

should result in token printing 25.4.