Pages: [1]   Go Down
Author Topic: Problem for convert data received from XBee  (Read 442 times)
0 Members and 1 Guest are viewing this topic.
Offline Offline
Newbie
*
Karma: 0
Posts: 4
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hello,

I'm new on this forum so Good Morning for all and thank's for read me.

This is my problem:

I have one XBee S2 connected on one Arduino with one temperature sensor (DHT22) and one button. When I press on the button I send the float simply with:

Code:
Serial.print(t);

If I connect my second XBee with the USB adapter on the second computer and run CoolTerm I can see the value received each time I press on the button, for example "25.30".

But now I'm trying to receive this value on the XBee but this time connected to an Arduino. I read the value simply with:

Code:
int dataByte = Serial.read();
Serial.print(dataByte);

So if I open the serial monitor of the Arduino program I see each time I press on the button some differents values like: 5052464948, 5053464848, 5053465048.

But I don't know how to convert it to a float value. I tried to convert it in hex values, for example:

5052464948 -> 0x12D267F34
5053464848 -> 0x12D35C110
5053465048 -> 0x12D35C1D8

But I don't understand where my float value is. If I separate the hex I don't found it either (12 D2 67 F3 4).

I know that sometimes we received an API frame and we have to separate each part but in my case I'm not using the API configuration but the ZigBee one, I perfectly see my float value with CoolTerm but not on the Arduino.

If someone could help me it would be really nice !

Thank's

Benji
Logged

Grand Blanc, MI, USA
Offline Offline
Faraday Member
**
Karma: 71
Posts: 3498
CODE is a mass noun and should not be used in the plural or with an indefinite article.
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Serial.print() on the sending unit converts t to ASCII characters. The receiving unit is reading each character and storing the value in an integer variable, so when it prints that variable, the output is the numeric ASCII code for the received character:

Code:
50 52 46 49 48 -> "24.10"
50 53 46 48 48 -> "23.00"
50 53 46 50 48 -> "23.20"

On the receiver, change Serial.print(dataByte) to Serial.write(dataByte) and you will see the characters as sent.
« Last Edit: January 19, 2014, 06:42:27 am by Jack Christensen » Logged

MCP79411/12 RTC ... "One Million Ohms" ATtiny kit ... available at http://www.tindie.com/stores/JChristensen/

Offline Offline
Newbie
*
Karma: 0
Posts: 4
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

It works ! Thank you very much !  smiley

And just another little question: If I want to save this values "24.10" in a table.

Because my "dataByte" is an int so can I easily convert it to ACII like "24.10" and then save it in something like "value[]" ?

Thank's
Logged

Seattle, WA USA
Offline Offline
Brattain Member
*****
Karma: 547
Posts: 45973
Seattle, WA USA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
Because my "dataByte" is an int so can I easily convert it to ACII like "24.10" and then save it in something like "value[]" ?
You have our permission. Of course, one byte does not a float make. So, you need to save all the bytes in an array (Hey, you know a character array would work...), and then, when the last one arrives, use atof() to convert the NULL terminated array of chars to a float. Then, store the float in an array.

Of course, this supposes that you know when the last one arrives. A small change on the sending end makes that much easier to determine.

And, of course, it supposes that saving the values in an array makes sense, which I'm not sure about. What is the purpose?
Logged

Grand Blanc, MI, USA
Offline Offline
Faraday Member
**
Karma: 71
Posts: 3498
CODE is a mass noun and should not be used in the plural or with an indefinite article.
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Another approach, to save the received values in an array, I probably would send them as binary instead of ASCII. Floats take four bytes, so that would be a little more efficient as well, one less byte would need to be transmitted. Plus it would not require converting the float to ASCII for transmission, then back to float to save in an array on the other end.

The trick would be to use a union to access the four bytes comprising the float, then reassemble them at the receiving end.

The transmitting end would be something like

Code:
///a union between a float and a four-byte array
union floatByte_t {
    float f;
    byte b[4];
};

    floatByte_t xmitTemp;

    xmitTemp.f = dht.readTemperature();     //get the temperature
    
    //send the four bytes that make up the float value
    for (int i = 0; i < 4; i++) Serial.write( xmitTemp.b[i] );

And the receiving end

Code:
//a union between a float and a four-byte array
union floatByte_t {
    float f;
    byte b[4];
};

    floatByte_t recvTemp;
    float myValues[10];

    //read four bytes that make up the float value
    for (int i = 0; i < 4; i++) recvTemp.b[i] = Serial.read();
    Serial.println(recvTemp.f);
    myValues[0] = recvTemp.f;       //save the value in an array

I would also want to include some delimiters in the message so that the receiver knows when one message ends and the next starts.
« Last Edit: January 20, 2014, 07:49:01 am by Jack Christensen » Logged

MCP79411/12 RTC ... "One Million Ohms" ATtiny kit ... available at http://www.tindie.com/stores/JChristensen/

Offline Offline
Newbie
*
Karma: 0
Posts: 4
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hello Jack and thank you so much for your idea. But I'm not sure that I understand it.

So this is my code at the moment:

Code:
// Example testing sketch for various DHT humidity/temperature sensors
// Written by ladyada, public domain

#include "DHT.h"

#define DHTPIN 3     // what pin we're connected to

// Uncomment whatever type you're using!
//#define DHTTYPE DHT11   // DHT 11
#define DHTTYPE DHT22   // DHT 22  (AM2302)
//#define DHTTYPE DHT21   // DHT 21 (AM2301)

// 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);

const int buttonPin = 2;
const int buttonHumid = 4;
const int ledPin =  13;

int buttonState = 0;           
int buttonStateHumid = 0; 

void setup() {
  pinMode(ledPin, OUTPUT);
  pinMode(buttonPin, INPUT);
  pinMode(buttonHumid, INPUT);
  Serial.begin(9600);
  Serial.println("DHTxx test!");
 
  dht.begin();
}

void loop() {
  // Reading temperature or humidity takes about 250 milliseconds!
  // Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
  float h = dht.readHumidity();
  float t = dht.readTemperature();

  buttonState = digitalRead(buttonPin); 
  buttonStateHumid = digitalRead(buttonHumid);

  if (digitalRead(buttonPin) == HIGH) { 
    digitalWrite(ledPin, HIGH); 
   
    // 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(t);
 
  }
   
  }
 
  if (digitalRead(buttonHumid) == HIGH) {   
    digitalWrite(ledPin, HIGH); 
   
    // 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(h);
  }
 
  }
 
    digitalWrite(ledPin, LOW);
}

So it's very simple, I use the DHT example. If I press the first button I send the temperature float and if I press the second one I send the humidity.

At this point something that I don't understand is if I send a float why I can't receive directly a float on the receiver part ?

This is my receiver code:

Code:

void setup() {               

  Serial.begin(9600);
 
}

   void loop() { 
    if (Serial.available()>0) {   //If there is data in the Serial Line
    int dataByte = Serial.read(); //save data into integer variable dataByte
    Serial.write(dataByte);
    }
  }
}

So I send a float like "24.10 and I receive an int that I store in dataByte. If I use "Serial.print(dataByte)" I can see on the monitor something like "5052464948" and you told my that it's the ASCII code, so if I use "Serial.write(dataByte)" I can see what I want: "24.10".

But it's just on the screen, I don't understand in what kind of type my dataByte value is. I want to print my "24.10" on a 7seg ments 4 digit screen. I wrote a function to print a number on it, for example I call "print_function(2,3)" and we see the number "2" on the 3rd digit. If I call "print_function(5,4)" we can see the number 5 on the fourth digit.

I this situation I wanted to send my float, receive a float separate it on 4 number and store it on a tab. For 24.10 I will have something like that:

tab[0]=2
tab[1]=4
tab[2]=1
tab[3]=0

And then I can easily call my "print_function" with a loop for 0 to 3 and with each case of the tab.

I'm not sure that I understand your example, your create a type with a float and a 4 bytes. You store the float in xmitTemp.f but just after that you send the four cases of the byte type but we didn't save anything on it ? Where we convert the float in a byte type and store it in b[4] ?

And in the receiver code, we read the four parts of the byte type and we store the float on the first case of myValues "myValues[0]".

So it's just like we separate the float in 4 bytes on the sender code, we send it, we received the 4 bytes on the receiver code but we store the float. So why don't send and receive directly a float ?

Thank you so much for your help !
Logged

Seattle, WA USA
Offline Offline
Brattain Member
*****
Karma: 547
Posts: 45973
Seattle, WA USA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
I this situation I wanted to send my float, receive a float separate it on 4 number and store it on a tab. For 24.10 I will have something like that:

tab[0]=2
tab[1]=4
tab[2]=1
tab[3]=0
No. You would have something like:
tab[0]='2'
tab[1]='4'
tab[2] = '.'
tab[3]='1'
tab[4]='0'
tab[5] = 0
tab would be an array of chars, with a NULL in the last position.

Quote
But it's just on the screen, I don't understand in what kind of type my dataByte value is.
It's right there in your code. dataByte is an int, despite its name. You could store dataByte in an array, of type char, and add a NULL after each character is added.

Then, the only thing you need to know is when a value starts and when it ends.

If you change the sender from:
Code:
    Serial.print(t);
to:
Code:
    Serial.print('<');
    Serial.print(t);
    Serial.print('>');
you could use code like this:
Code:
#define SOP '<'
#define EOP '>'

bool started = false;
bool ended = false;

char inData[80];
byte index;

void setup()
{
   Serial.begin(57600);
   // Other stuff...
}

void loop()
{
  // Read all serial data available, as fast as possible
  while(Serial.available() > 0)
  {
    char inChar = Serial.read();
    if(inChar == SOP)
    {
       index = 0;
       inData[index] = '\0';
       started = true;
       ended = false;
    }
    else if(inChar == EOP)
    {
       ended = true;
       break;
    }
    else
    {
      if(index < 79)
      {
        inData[index] = inChar;
        index++;
        inData[index] = '\0';
      }
    }
  }

  // We are here either because all pending serial
  // data has been read OR because an end of
  // packet marker arrived. Which is it?
  if(started && ended)
  {
    // The end of packet marker arrived. Process the packet

    // Reset for the next packet
    started = false;
    ended = false;
    index = 0;
    inData[index] = '\0';
  }
}
on the receiver. Each packet (temperature, humidity, etc.) would end up in inData as a string. Print it as a string, or use atof() to convert the string to a float.

By the way, your code is incredibly hard to read. Put each { on a new line, and use Tools + Auto Format to make it easier to read.
Logged

Grand Blanc, MI, USA
Offline Offline
Faraday Member
**
Karma: 71
Posts: 3498
CODE is a mass noun and should not be used in the plural or with an indefinite article.
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Hello Jack and thank you so much for your idea. But I'm not sure that I understand it.

Let's step back a minute and consider the different ways that numbers are represented.

Assume that I have a floating point variable and I assign a value of 24.10 to it.  This requires four bytes of memory in the microcontroller's (MCU) static RAM memory (SRAM).

These four bytes will have the following values in decimal: 205, 204, 192 and 65, or the following values in hexadecimal: 0xCD, 0xCC, 0xC0, 0x41, neither of which looks anything at all like 24.10. This is because the value 24.10 is represented internally in the MCU using a standard like IEEE 754. But never mind that for now smiley-wink just think of it as encoded.

So the internal representation needs to be changed into something a human can read. This is what Serial.print() does. It takes the internal floating point representation and changes it to ASCII characters, suitable for sending to the serial monitor. Each character is a byte, but it is a different encoding than for the internal floating point representation. The ASCII code for the character '2' is 50. '4' is 52, '1' is 49, '0' is 48 and a decimal point (.) is 46. Now those should look familiar. Remember that the only thing that the serial monitor does is display ASCII characters. If we try and send any internal representation of a number directly to it, we will see nonsensical garbage. It has to be ASCII if we are to be able to read it. Serial.write() on the other hand, sends the binary value of a byte. This is fine for communicating between two machines (or two XBees) but is not usually what we want to send to the serial monitor for humans to read.

We need to get a floating point number from MCU "A" to MCU "B" and what I suggested was to not transmit the value in ASCII, but to send the values of the four bytes that make up the floating point number internally in the MCU.

UNION helps to do this. I define a new data type called floatByte_t that is four bytes long. Union allows us to refer to the same four bytes in two different ways, either as a float by writing foo.f or as bytes by writing foo.b.

Paul suggests a different approach, sending the floating point value in ASCII. This is perfectly fine as well, and might be easier to understand. It is less efficient, but the difference in this case is of absolutely no consequence, so it's perfectly fine to use that method if you are more comfortable with it.

Hope this helps. Try running the sketch below, it demonstrates how the union works. I'd also suggest reading up a little on number representations and on the union statement.

Code:
union floatByte_t {
    float f;
    byte b[4];
};

void setup(void)
{
    floatByte_t foo;
    foo.f = 24.10;
   
    Serial.begin(9600);
    Serial.print("\nASCII characters\t");
    Serial.println(foo.f);

    Serial.print("Four bytes, decimal\t");
    for (int i=0; i<4; i++) {
        Serial.print(foo.b[i], DEC);
        Serial.print('\t');
    }
    Serial.println();

    Serial.print("Four bytes, hexadecimal ");
    for (int i=0; i<4; i++) {
        Serial.print(foo.b[i], HEX);
        Serial.print('\t');
    }
    Serial.println();
}

void loop(void)
{
}
Logged

MCP79411/12 RTC ... "One Million Ohms" ATtiny kit ... available at http://www.tindie.com/stores/JChristensen/

Offline Offline
Newbie
*
Karma: 0
Posts: 4
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Waouw thank you so much PaulS and Jack for your time and your explanations !  smiley-eek

I understand better now and I'll do some tests with your advise.

Thank's !
Logged

Pages: [1]   Go Up
Jump to: