Transferring doubles via xbee as 8 chars [solved]

08 Sep 2010 . Edited: 08 Sep 2010

I've got two mbeds, each with an xbee. I'm trying to send double (8 byte) values as 8 characters with mbed master, receieve them with mbed client and put them together again.

/*
* Send double as 8 characters
*/
void xbee_send_double(double number){

    long long * ptr = (long long *) (& number); //pointer to number address to fool compiler

    pc.printf("length of long long: %d\n", sizeof(long long));
    pc.printf("num = %f\n", number);
   
    for (int i = 0; i < sizeof(long long); ++i){
        xbee.putc((*ptr >> (8 * i)) & 0xFF);
        pc.printf("%d\n", (*ptr >> (8 * i)) & 0xFF);
    }
}

int main() {
    
    xbee_send_double(12.3456);

}

This prints to the pc:

length of long long: 8
num = 12.345600

197
254
178
123

242
176
40
64

--

On the other mbed, I've got this code:

/*
* Receive double as 8 characters
*/
double xbee_get_double(){

    long long data = 0;
   
    for (int i = 0; i < sizeof(double); ++i){
   
        char temp = xbee.getc();
        pc.printf("temp = \'%d\'\n", temp);
       
        data = data | (temp << (8*i));
       
        pc.printf("data = \'%lld\'\n", data);
       
    }
   
    double * ptr = (double *) (&data); //pointer to data address
   
    return *ptr;
   
}

int main() {

    while (1) {
        if (xbee.readable()) {
       
            double data =  xbee_get_double();
           
            pc.printf("float = %f\n", data);
            pc.printf("float = %d\n", data);
            pc.printf("float = %e\n", data);
           
        }
    }
}

This prints the following to the terminal:

temp = '197'
data = '197'
temp = '254'
data = '65221'
temp = '178'
data = '11730629'
temp = '123'
data = '2075328197'

temp = '242'
data = '2075328197'
temp = '176'
data = '2075328197'
temp = '40'
data = '2075328197'
temp = '64'
data = '2075328197'

float = 0.000000
float = 2075328197
float = 1.025348e-314

As you can see, the 'temp' characters transfer correctly, but data stays at the same value for the 5th-8th character (in red).

Since this method works for ints (4 bytes = 32 bits), I'm wondering if it's a problem using 8 bytes = 64 bits and bitwise operations?

08 Sep 2010

You should use 'unsigned char' when manipulating binary data. Even better, include stdint.h and use uint8_t. Signed integers introduce all kinds of issues when you do shifts on them. I think that's the reason you get wrong results.

That said, it might be simpler and better to use a union. Something like this:

#define DOUBLE_SIZE sizeof(double)

union double_bytes
{
  double d;
  uint8_t b[DOUBLE_SIZE];
};

void xbee_send_double(double number)
{
    double_bytes db;
    db.d = number;

    for (int i = 0; i < DOUBLE_SIZE; ++i)
        xbee.putc(db.b[i]);
}

double xbee_get_double()
{
    double_bytes db;
   
    for (int i = 0; i < DOUBLE_SIZE); ++i)
        db.b[i] = xbee.getc();
   
    return db.d;   
}

08 Sep 2010

Actually, I take my words back. Now that I looked closer at the trace I can see it. The reason is this line:

data = data | (temp << (8*i));

Since "temp" is a char, it is only promoted to 32-bit int when the shift is applied. From the fourth byte, the shift becomes 32 and all 32 bits of the number are shifted away to produce a zero. Thus the data value is not changed anymore.
To fix it, you could explicitly promote "temp" to 64 bits, so that the shift becomes 64-bit one:

data = data | (uint64_t(temp) << (8*i));

But I think the union method is better anyway.

08 Sep 2010

Hi Alex,

I think your problem is in this line:

data = data | (temp << (8*i));
temp is a char (8-bits), and it'll only ever get cast up to an int (32-bits) implicitly, and hence once you get to shifting up by more than 3 bytes, you'll always end up with 0. So at that point, you are just or-ing in 0, hence the result you see.

A quick fix would be to make temp a 64-bit value.

Simon

08 Sep 2010 . Edited: 08 Sep 2010

Thanks for that!

I haven't used unions before, but from what I gather the double and the uint8_t are sharing the same memory, similar to my pointer ptr and double number?

The other option I thought of was to send the data using xbee.printf and xbee.scanf

Edit:

Thanks guys - I knew I had to use a 64 bit var, but I forgot about char!

But I'll use the union method, it's definitely a better way of doing things.