Sending float ROCK SOLID over Bluetooth

I need a ROCK SOLID way to transfer floats from one Arduino with Bluetooth-Module (HC-05) to another Arduino with the same Module.

Right now I got it working…
But with my code sometimes the first float-digit gets lost (e.g. instead of receiving “21.24” the second arduino receives “1.24” ). This happens 1x / h or sometimes 1x / 2h…

ARDUINO A is sending floats with this code:

#include <SoftwareSerial.h>// import the serial library
#include <DHT.h> // weisser Temp-/Feuchtigkeitssensor

SoftwareSerial mySerial(9, 10); // DTX Pin of HC-05 to Pin 9 (=SoftSerial RX) of UNO and DRX to Pin 10 (=SoftSerial TX)
DHT dht; // fuer weissen Temp+Feuchtsensor

unsigned long time; // Um delays zu ersetzen
char c;

void setup() {
  mySerial.begin(9600); 
  dht.setup(7);
}

void loop() {

  delay(dht.getMinimumSamplingPeriod());
  float temperaturCsensorDHT = dht.getTemperature();

    mySerial.print(temperaturCsensorDHT);
    time = millis(); while (millis() <= time + 350);
}

if i play with the intervall (e.g. 500 instead of 350 ms) I loose the first digit more/less often…
But finding the exact milliseconds would take a while… and it would still not be reliable…

ARDUINO B receives the float his way:

#include <AltSoftSerial.h> // Forced to use Pins 5 and 13 on the Yun/Leonardo
AltSoftSerial mySerial;

void setup(){ 
  mySerial.begin(9600);
}

void loop() {
  float temperaturBLUETOOTH_1 = 0.00;  // falls Bluetooth-Sensor ausfaellt oder ausgeschaltet wird 0.00 C anzeige
  int i2 = 0;
  char char_arr[9];

  if(mySerial.available() >0) { // YUN LOOKS EVERY LOOP FOR INCOMING BLUETOOTH DATA
    while (mySerial.available()>0){
      char c = mySerial.read();
      char_arr[i2]=c;
      i2++;
      time = millis(); while (millis() <= time + 10); // NEW DELAY

    }
    i2 = 0;
    temperaturBLUETOOTH_1 = atof(char_arr);
  }
}
    time = millis(); while (millis() <= time + 350);

There is NO advantage that that crap over delay(350). In fact, it’s harder to see that that is what it is doing.

There is NO guaranteed way to get serial data delivered. The best you can do is use start and end delimiters and a check sum, and then discard any packet that does not contain the expected data.

At a minimum, you need to send an end-of-packet marker, and read data until that arrives, NOT until time is up.

Now I managed to include some sort of “STARTING-check”:
Basically every correctly transfered float now has a “89” infront of it, so e.g. “8912.34” or “-8926.78”. Then the receiving arduino subtracts or adds 8900 to that float.

“END-check” isn’t as “easy” as that because Arduinos (at least the UNO) only allows for 2 digits after the comma to be transfered over the serial. And adding a char to the end wasn’t as easy as I thought (…try to convert a float with no special length to a char array/String… frustrating…). So I gave up on the “End-check”.

The good part:
Now my sketch recognizes if the beginning of a float is missing.

The sad part:
My new code is still not reliable

The worst part
Now the communication is so slow, that it takes 2-4 sec to transfer and then 1 sec to process every float… !

ARDUINO UNO:

#include <SoftwareSerial.h>// import the serial library
#include <DHT.h> // weisser Temp-/Feuchtigkeitssensor

SoftwareSerial mySerial(9, 10); // DTX Pin of HC-05 to Pin 9 (=SoftSerial RX) of UNO and DRX to Pin 10 (=SoftSerial TX)
DHT dht; // fuer weissen Temp+Feuchtsensor

unsigned long time; // Um delays zu ersetzen
char c;

void setup() {
  mySerial.begin(9600); 
  dht.setup(7);
}

void loop() {

  delay(dht.getMinimumSamplingPeriod());
  float temperaturCsensorDHT = dht.getTemperature();

    mySerial.print(temperaturCsensorDHTtransfer);
    delay(350);
}

ARDUINO YUN:

#include <AltSoftSerial.h> // Forced to use Pins 5 and 13 on the Yun/Leonardo
AltSoftSerial mySerial;

void setup(){ 
  mySerial.begin(9600);
}

void loop() {
  float temperaturBLUETOOTH_1 = 0.00; 
  
int i2 = 0;
  char char_arr[10];
  
  // YUN LOOKS EVERY LOOP FOR INCOMING BLUETOOTH. IF THERE IS INCOMING DATA; 
  if (mySerial.available() > 0) {
    while (mySerial.available() > 0) {
      char c = mySerial.read();
      char_arr[i2] = c;
      i2++;
    }


// ++++++ Successfull transfers need to start with "89" or "-89" +++++

 // if positive float
    if (char_arr[0] == 56 && char_arr[1] == 57) {

      mySerial.print("3"); // Says "THANKS I GOT IT" to the UNO over bluetooth by sending a "3"
      float temperaturBLUETOOTH_1_with89 = atof(char_arr);
      temperaturBLUETOOTH_1 = temperaturBLUETOOTH_1_with89 - 8900;
     }
      // if negative float:
      if (char_arr[0] == 45 && char_arr[1] == 56 && char_arr[2] == 57) {
        mySerial.print("3"); // Says "THANKS I GOT IT" to the UNO over bluetooth by sending a "3"

        float temperaturBLUETOOTH_1_with89 = atof(char_arr);
        temperaturBLUETOOTH_1 = temperaturBLUETOOTH_1_with89 + 8900;
      }

    }

}

You are not actually sending a float. You are sending some text.

It seems to me, your receiving code is not very reliable.

You are not doing anything to terminate the character array that you are receiving. You never check if you are receiving more than 9 characters.

You have the character array in some potentially indeterminate state every time loop() starts again. It probably should have zero bytes in it, but I don't know if you should rely on this. I can't be bothered remembering which compilers and which operating systems and which hardware guarantee null bytes when an array comes into scope, so I always clear them explicitly if it is important.

To illustrate what I am taking about, if you receive "127.3" OK, and then the next time you receive a "9" , and nothing else within 10 milliseconds, then you might get 927.3 from your char to float calculation.

You also don't seem to consider getting any character ( possibly with a bit error ), which is not a digit. I never use atof() so I don't know what it will do, but if reliability is important to you, you should consider that possibility.

Your sending piece of code also looks strange. You don't seeming to be "printing" the value of the variable you supposedly got from the sensor.

michinyon,
SORRY it took too long to correct my post ! I didn't see that you already answered !! Please look into my last post again, the code is now modified !

Now I send "8912.34" and "-8912.34" (if negative float)

"END-check" isn't as "easy" as that because Arduinos (at least the UNO) only allows for 2 digits after the comma to be transfered over the serial. And adding a char to the end wasn't as easy as I thought (....try to convert a float with no special length to a char array/String... frustrating...). So I gave up on the "End-check".

None of this is true. When the argument is a float, you can supply a second argument defining how many characters after the decimal point.

You could use println() instead of print(), to automatically append a carriage return and line feed - they are good end of packet markers.

You could use dtostrf() to convert the float to a string, and then send the string, formatted however you like.

You could use more than one call to Serial.print() to send a start of packet marker, to send the float, and then to send an end of packet marker.

There are many ways to skin this cat.

Your second attempt has the problem, that you try and process the data before you might have received all of it.

michinyon:
Your second attempt has the SAME problem, that you try and process the data before you might have received all of it.

I fixed that for you. :slight_smile:

michinyon:
(...)
Your sending piece of code also looks strange. You don't seeming to be "printing" the value of the variable you supposedly got from the sensor.

You are absolutly right, what I posted up to now was imo the only important part for the transfer.
My real sketch is a lot larger than this. But I was afraid that no one would even bother looking at it...

If you guys want, I can post both sketches completely.... But be aware and please don't run away :sweat_smile:

PaulS:
None of this is true. When the argument is a float, you can supply a second argument defining how many characters after the decimal point.

forum.arduino.cc/index.php/topic,42733.0.html
See Yot's (Global Moderator) comment. He says that we can't transfer more than 2 digits (after the comma) over serial

You could use println() instead of print(), to automatically append a carriage return and line feed - they are good end of packet markers.

Thats a great idea! So I could send with println instead of print.
But how do I even receive the carriage return?! will it be a ascii-character like the others?!

You could use dtostrf() to convert the float to a string, and then send the string, formatted however you like.

Yes, I read about that (and about itoa, atoi, atof....), but dtostrf() needs parameters like the length of the char-array/String...but how do I get the length of a float...! That was the reason why I searched for another solution

You could use more than one call to Serial.print() to send a start of packet marker, to send the float, and then to send an end of packet marker.

Thought about that too, but I would need to implement 3 times the while (mySerial.available() >0) (see my YUN-sketch), then each time read the received data and then each time check wether its the "start", the float or the "end" (or non) ... That must be a lot more unreliable than what I already got ...and take longer. So I forgot about that idea

There are many ways to skin this cat.
I hope to find at least one! :slight_smile:

But how do I even receive the carriage return?! will it be a ascii-character like the others?!

Yes. The carriage return and line feed are '\n' and '\r'.

Store the data in an array, and increment an index until the end-of-packet marker arrives. Then, convert the value to a float, reset the index and reset the array.

No need to wait between characters. Many iterations of loop() may happen before the end of the string arrives.

but dtostrf() needs parameters like the length of the char-array

There is a maximum value that you will be sending, isn't there? I mean that 3432412626262.7 is probably out of range. So, figure out a reasonable number of digits before the decimal point and a reasonable number for after the decimal point. Add them up, and add 3 - for the sign, the decimal point, and the terminating NULL. You have the length of the array then.

Thought about that too, but I would need to implement 3 times the while (mySerial.available() >0) (see my YUN-sketch), then each time read the received data and then each time check wether its the "start", the float or the "end" (or non) ... That must be a lot more unreliable than what I already got ...and take longer. So I forgot about that idea

Three Serial.print() statements, or one, makes no difference to the receiving code. It can't tell whether the 12 characters it gets are from one Serial.print() statement or from 12 of them. So, unforget that idea.