4

I am having trouble transmitting a float across a simple 2 node Xbee Network.

I am aware that the Xbee system transmits packages via bytes, so I can send a char, but I am having trouble sending anything more than that, and I can't seem to find any documentation anywhere.

Here is my current (basic) code.

Sender:

(... appropriate setup...)
void loop()
{
  sensorValue = analogRead(analogInPin);
  sensorValueTemp = sensorValue / 9.31; //LM35 measurement into Centigrade
  Serial.print(sensorValueTemp);
  delay(1000);
}

Receiver:

(...appropriate setup...)
void loop() { 
  lcd.setCursor(0, 0);
    if (Serial.available() > 0) {
    lcd.print(incomingByte);
  }
  delay(1000);
}

Any hint as to get the float to be transmitted successfully would be great? Or if it is already being transmitted properly, how to read it properly?

Thanks

Vincenzo Pii
  • 18,961
  • 8
  • 39
  • 49
Chris M
  • 127
  • 2
  • 9

3 Answers3

3

You can send the float in bytes and reconstruct the float at the receiver.

The following example may help you:

Sender side:

float x = 1128.476;

char b[sizeof(x)];
memcpy(b, &x, sizeof(x));

// Iterate over b and send bytes
// [...]

Receiver side:

float y = 0;

char b[sizeof(x)];

// Store 4 bytes representing the float into b
// [...]

// Rebuild the float
memcpy(&y, b, sizeof(y));

At the end, you have float y on the receiver side, which has the same representation of float x on the sender side.

Vincenzo Pii
  • 18,961
  • 8
  • 39
  • 49
  • Please simplify a bit -- at least use an array of bytes instead of four separate bytes. `char b[sizeof x]; memcpy( b, &x, sizeof b);` – tomlogic Apr 15 '12 at 05:24
  • @tomlogic added the simplified approach :) – Vincenzo Pii Apr 15 '12 at 09:26
  • There's no reason to do all that memcpy() foo. If you want a byte point to the float, ((char *) &y) works just fine. And if you hate that ugliness, "char *b = ((char *) &y);" might make you feel better. – Julie in Austin Apr 15 '12 at 10:05
  • @JulieinAustin you can use that on the sender side, but, once you have 4 bytes at the receiver, how do you pack back the `float` if you don't have a `float*` but just a `float` if not with `memcpy`? – Vincenzo Pii Apr 15 '12 at 10:15
  • 2
    This solution will only work if both sender and receiver are using the same endian architecture. If one end is big-endian and the other little-endian, you wont get the same value on the other side. A better approach would be to implement (de)serialising packets of data (structures, basically) which also store any additional information required to correctly decode the incoming data on the receiver. –  Apr 15 '12 at 11:47
  • @James I assumed from the question context that the scenario is arduino+xbee both on the tx and rx side. Anyway, I think a million things can be added to the answer I wrote, starting from the obvious simplification proposed by @tomlogic. My priority was to show how to sort out the `float` transmission problem in the context of this question. – Vincenzo Pii Apr 15 '12 at 12:43
  • @puller : You do the same exact thing. On the receiver side, you have a `float remoteValue = 0; char *inputPointer = ((char *) &remoteValue);` then read `(sizeof float)` bytes into `*inputPointer++`. – Julie in Austin Apr 15 '12 at 14:32
  • @JulieinAustin that could be more efficient than `memcpy` if you pack each byte -as you receive it- inside the `float` and then increment the `char*` pointer, but less efficient if you have to use a `for` loop to pack the four bytes at once. – Vincenzo Pii Apr 15 '12 at 14:52
  • +1 to James for addressing the endianness issue that I came back to mention myself. In addition, you assume that all platforms involved store a float as a 64-bit IEEE 754 value. Much better to convert to ASCII with `sprintf()` and convert back to float with `strtof()`. – tomlogic Apr 15 '12 at 17:02
  • I would delete the first part of your answer in favor of the 'simplified approach' - the initial bit is needlessly complicated. – Nick Johnson Apr 17 '12 at 05:55
  • @NickJohnson Yes, I removed the first part and left the simplified approach only. – Vincenzo Pii Apr 18 '12 at 19:04
0

You can send all the binary data you want between two machines of an identical architecture, memory layout, byte-endianness, etc. by simply taking a byte-sized pointer (char *) on the "sending" side, and iterating over the referenced object for the number of bytes in that object. On the "receiving" side, you allocate an object of the same size (float, long, struct foo), and receiving the bytes, one by one, into a byte-sized pointer which is post-incremented after each byte is received.

On the sending side --

void sendObject(const void *object, size_t size) {
  char *outputCursor = (char *) object;

  for (;size > 0;size--)
    yourSendAByteFunction(*outputCursor++);
}

On the receiving side, assuming yourReceiveAByteFunction() return 0..255 for a valid byte and -1 for a "receiving error", you can do this --

int receiveObject(void *object, size_t size) {
  char *inputCursor = (char *) object;

  for (;size > 0;size--) {
    int nextByte = yourReceiveAByteFunction();
    if (nextByte < 0)
      return FALSE;

    *inputCursor++ = nextByte;
  }
  return TRUE;
}

You can do the same I/O error checking in the sendObject() function by declaring yourSendAByteFunction() so it returns TRUE or FALSE depending on whether or not an error occurred in the output. It all depends on how much complexity you can stand, and whether or not you have a reliable transmission link.

You can also do a bit of data encapsulation if you have bytes you can't transmit by having a "shift" byte and set of byte values that are prefixed by the "shift" byte to represent some other byte.

Julie in Austin
  • 966
  • 5
  • 21
0

Your original sender is sending an ASCII string that represents the float value.

In order to receive and display the value you need to modify the lines as shown below:

(...appropriate setup...)
void loop() { 
  lcd.setCursor(0, 0);
  while (Serial.available() > 0) {       //Changed Line
      incomingByte = Serial.read();      //Added Line
      lcd.print(incomingByte);
  }
  delay(1000);
}

Note: If would be better to terminate the serial output with a CR to synchronize the devices instead of the delay(1000);

Jeff
  • 1,364
  • 1
  • 8
  • 17