Go Down

Topic: Converting int > String > Char* and back. For use with VirtualWire RF (Read 14451 times) previous topic - next topic

Makkan

Hi,

I'm trying to use the VirtualWire library to send sensor data via a RF transmitter/receiver pair but I've got stuck halfway through.

I've managed to convert my sensor data from int to a String and then to a char array that I've successfully sent and received from one unit to the other. But now I'm stuck at converting the char array back to an int.

Since my programming skills are quite limited you are more than welcome to suggest another solution if one exists.

Transmitter code:
Code: [Select]

#include <VirtualWire.h>


const int Sensor1Pin = A2;
// const int Sensor2Pin = 3;
const int ledPin = 13;

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


void setup() {

// LED
pinMode(ledPin,OUTPUT);

// Sensor(s)
pinMode(Sensor1Pin,INPUT);

// for debuggin
Serial.begin(9600);

// VirtualWire setup
vw_setup(2000); // Bits per sec


}

void loop() {
 
  // Read and store Sensor 1 data
  Sensor1Data = analogRead(Sensor1Pin);
 
  // To be able to send the data over VirtualWire RF we need to convert
  // the numeric data to a String and then a char array.
  // First Integer to String...
  String Sensor1String(Sensor1Data, DEC);
 
  // ...then string to charArray
  // BUT! We have to make room for the null-termination char.
  // So we add +1 to the length of the string, when converting it.
  Sensor1String.toCharArray(Sensor1CharMsg,(Sensor1String.length()+1));
 
  // DEBUG
  Serial.print("Sensor1 Integer: ");
  Serial.print(Sensor1Data);
  Serial.print(" Sensor1 String: ");
  Serial.print(Sensor1String);
  Serial.print(" Sensor1 Length ");
  Serial.print(Sensor1String.length());
  Serial.print(" Sensor1 CharMsg: ");
  for (int i = 0; i <= Sensor1String.length(); i++) {
    Serial.print(Sensor1CharMsg[i]);
  }
  Serial.println(" ");
  delay(1000);

  // END DEBUG

digitalWrite(13, true); // Flash 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);
delay(200);

} // END void loop...


Receiver Code
Code: [Select]

#include <VirtualWire.h>

int ledPin = 13;

int Sensor1Data;
char Sensor1CharMsg[8];

void setup() {
  Serial.begin(9600);
  pinMode(ledPin, OUTPUT);      // sets the digital pin as output

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

    vw_rx_start();       // Start the receiver PLL running

} // END void setup

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

    if (vw_get_message(buf, &buflen)) // Non-blocking
    {
int i;

        digitalWrite(13, true); // Flash a light to show received good message
// Message with a good checksum received, dump it.
Serial.print("Got: ");

for (i = 0; i < buflen; i++)
{
    Serial.print(buf[i], HEX);
            Sensor1CharMsg[i] = char(buf[i]);
    Serial.print(" ");
}
Serial.println("");
        String Sensor1String =(Sensor1CharMsg);
        Sensor1String.trim();
        Serial.print("String: ");
        Serial.println(Sensor1String);
        Sensor1String = "         ";
        digitalWrite(13, false);
    }
}

PaulS

Quote
I've managed to convert my sensor data from int to a String and then to a char array

Why use the String? You can go from int to char array (itoa()) and back (atoi()) directly.

Code: [Select]
  String Sensor1String(Sensor1Data, DEC);
The default base for conversion is 10. There is no reason to explicitly specify the default value.

Code: [Select]
  Sensor1String.toCharArray(Sensor1CharMsg,(Sensor1String.length()+1));
The second argument is the number of characters that the array can hold, NOT the number of characters in the String. The String object already knows how many characters it has.

Code: [Select]
  for (int i = 0; i <= Sensor1String.length(); i++) {
    Serial.print(Sensor1CharMsg[i]);
  }

The array being printed here is already NULL terminated. There is no reason to print it one character at a time.
Code: [Select]
Serial.print(Sensor1CharMsg);

Code: [Select]
digitalWrite(13, true); // Flash a light to show transmitting
This does not flash a light. It turns it on. As anyone can see.

On the receiver, you need to use the atoi() function to convert Sensor1CharMsg to an int.

Makkan


Quote

Quote
I've managed to convert my sensor data from int to a String and then to a char array

Why use the String? You can go from int to char array (itoa()) and back (atoi()) directly.


Because I from what I read itoa() and atoi() seemed a bit complicated. But it turned out not to be that hard at all.

Quote

Code: [Select]
  Sensor1String.toCharArray(Sensor1CharMsg,(Sensor1String.length()+1));
The second argument is the number of characters that the array can hold, NOT the number of characters in the String. The String object already knows how many characters it has.


This is a bit null and void since I went with the other solution but I had problems getting only 2 digits when I were supposed to get 3 and so on. Adding +1 helped.

Quote

Code: [Select]
  for (int i = 0; i <= Sensor1String.length(); i++) {
    Serial.print(Sensor1CharMsg[i]);
  }

The array being printed here is already NULL terminated. There is no reason to print it one character at a time.
Code: [Select]
Serial.print(Sensor1CharMsg);


Also not relevant anymore but for the sake of education, thank you.

Quote

Code: [Select]
digitalWrite(13, true); // Flash a light to show transmitting
This does not flash a light. It turns it on. As anyone can see.


I know, this actually comes from the VirtualWire example code. But I've changed the comment to be more correct.

Quote

On the receiver, you need to use the atoi() function to convert Sensor1CharMsg to an int.


Thanks for pointing me in the right direction. I did however get into a problem on the receiving end that I didn't describe here first. The char array I use to store the chars that I later convert to an integer will contain 4 digits once it has been given that number for instance, I send 1023 to it and when I send 906 to it it will actually show 9063, if I go directly to 0 I still have 9063 (or something similar) since the char array never got flushed. I managed to solve that in what I hope is a good enough solution.

Here is the updated transmitter code:
Code: [Select]
#include <VirtualWire.h>


const int Sensor1Pin = A2;
// const int Sensor2Pin = 3;
const int ledPin = 13;

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

void setup() {

// LED
pinMode(ledPin,OUTPUT);

// Sensor(s)
pinMode(Sensor1Pin,INPUT);

// for debuggin
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...



And updated receiver code:
Code: [Select]
#include <VirtualWire.h>

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]);
}
       
        // Convert Sensor1CharMsg Char array to integer
        Sensor1Data = atoi(Sensor1CharMsg);
       
        // DEBUG
        Serial.print("Sensor 1: ");
        Serial.println(Sensor1Data);
       
        // END DEBUG
       
        // Clear Sensor1CharMsg char array for coming messages
        // This needs to be done otherwise problems will occur
        // when the incoming messages has less digits than the
        // one before.
        for (i = 0; i < 4; i++)
        {
          Sensor1CharMsg[i] = 0;
        }
       
        // Turn off light to and await next message
        digitalWrite(13, false);
    }
}


PaulS

Quote
I did however get into a problem on the receiving end that I didn't describe here first. The char array I use to store the chars that I later convert to an integer will contain 4 digits once it has been given that number for instance, I send 1023 to it and when I send 906 to it it will actually show 9063, if I go directly to 0 I still have 9063 (or something similar) since the char array never got flushed. I managed to solve that in what I hope is a good enough solution.

The problem is that vw_get_message returns an array that is NOT NULL terminated. There is no reason for it to be, since it is not a char array that represents a string. It is only your interpretation of the data that makes it appear to be a string.

After the for loop that copies the data to a char array, you need to set Sensor1CharMsg[buflen] to NULL ('\0'). Then, the string functions, used by Serial.print() will know when to stop printing.

It is not necessary to set every position in the array to NULL.- only the first one after the valid data needs to be NULL.

Makkan


The problem is that vw_get_message returns an array that is NOT NULL terminated. There is no reason for it to be, since it is not a char array that represents a string. It is only your interpretation of the data that makes it appear to be a string.

After the for loop that copies the data to a char array, you need to set Sensor1CharMsg[buflen] to NULL ('\0'). Then, the string functions, used by Serial.print() will know when to stop printing.

It is not necessary to set every position in the array to NULL.- only the first one after the valid data needs to be NULL.


I have tried it and does work like advertised, just one problem. I get why I do this but I don't understand how it works.
Am I right to assume that "buflen" is the size of the char array sent via VirtualWire. So wouldn't buflen be the position of the last valid data not the position after? So why doesen't the null termination overwrite the last valid data?


Arrch


Am I right to assume that "buflen" is the size of the char array sent via VirtualWire. So wouldn't buflen be the position of the last valid data not the position after? So why doesen't the null termination overwrite the last valid data?




Because if buflen is 5, that means there are 5 bytes of data stored in indices 0,1,2,3,4. Therefore someData[buflen] or someData[5] would not contain meaningful data.

Makkan

Well that's simple enough.
Thanks for clearing that up for me.

Go Up