how to get decimals?

Hi all,

Currently I'm building a wireless distance measuring unit, using nRF24L01.
It is working quite ok, but... At this moment the data is transferred in centimeter to the receiver.

I would like to have this in meter, so devide by 100 in my receiver code. My value is now rounded of, no decimals. f.e. if I measure 331cm, should be 3.31m, but I get 3.00m.

How do I need to fix this?

The receiver code:

#include "Arduino.h"
#include <SPI.h>
#include <RF24.h>

#include <Wire.h>
#include <LIDARLite.h>
#include <SPI.h>
#include "RF24.h"
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define OLED_RESET 4
Adafruit_SSD1306 display(OLED_RESET);

#define NUMFLAKES 10
#define XPOS 0
#define YPOS 1
#define DELTAY 2
#define LOGO16_GLCD_HEIGHT 16 
#define LOGO16_GLCD_WIDTH  16 

static const unsigned char PROGMEM logo16_glcd_bmp[] =
{ B00000000, B11000000,
 B00000001, B11000000,
 B00000001, B11000000,
 B00000011, B11100000,
 B11110011, B11100000,
 B11111110, B11111000,
 B01111110, B11111111,
 B00110011, B10011111,
 B00011111, B11111100,
 B00001101, B01110000,
 B00011011, B10100000,
 B00111111, B11100000,
 B00111111, B11110000,
 B01111100, B11110000,
 B01110000, B01110000,
 B00000000, B00110000 };

#if (SSD1306_LCDHEIGHT != 32)
#error("Height incorrect, please fix Adafruit_SSD1306.h!");
#endif

// This is just the way the RF24 library works:
// Hardware configuration: Set up nRF24L01 radio on SPI bus (pins 10, 11, 12, 13) plus pins 7 & 8
RF24 radio(7, 8);

byte addresses[][6] = {"1Node","2Node"};
float dataInMeter;

// -----------------------------------------------------------------------------
// SETUP   SETUP   SETUP   SETUP   SETUP   SETUP   SETUP   SETUP   SETUP
// -----------------------------------------------------------------------------
void setup() {
 Serial.begin(9600);
 Serial.println("THIS IS THE RECEIVER MODULE - YOU NEED THE OTHER ARDUINO TO TRANSMIT");

// by default, we'll generate the high voltage from the 3.3v line internally! (neat!)
 display.begin(SSD1306_SWITCHCAPVCC, 0x3C);  // initialize with the I2C addr 0x3C (for the 128x32)
 // init done
 
 // Show image buffer on the display hardware.
 // Since the buffer is intialized with an Adafruit splashscreen
 // internally, this will display the splashscreen.


 // Clear the buffer.
 display.clearDisplay();

 // text display tests
 display.setTextSize(2);
 display.setTextColor(WHITE);
 display.setCursor(20,0);
 display.println("Allskies");
 
 display.setTextSize(1);
 display.setTextColor(WHITE);
 display.setCursor(20,21);
 display.println("RPAS Engineering");
 display.display();
 delay(2000);
 display.clearDisplay();
 
 // Initiate the radio object
 radio.begin();

 // Set the transmit power to lowest available to prevent power supply related issues
 radio.setPALevel(RF24_PA_MAX);

 // Set the speed of the transmission to the quickest available
 radio.setDataRate(RF24_2MBPS);

 // Use a channel unlikely to be used by Wifi, Microwave ovens etc
 radio.setChannel(124);

 // Open a writing and reading pipe on each radio, with opposite addresses
 radio.openWritingPipe(addresses[0]);
 radio.openReadingPipe(1, addresses[1]);

 // Start the radio listening for data
 radio.startListening();
}

// -----------------------------------------------------------------------------
// We are LISTENING on this device only (although we do transmit a response)
// -----------------------------------------------------------------------------
void loop() {

 // This is what we receive from the other device (the transmitter)
 unsigned char data;

 // Is there any data for us to get?
 if ( radio.available()) {

   // Go and read the data and put it into that variable
   while (radio.available()) {
     radio.read( &data, sizeof(char));
   }

   // No more data to get so send it back but add 1 first just for kicks
   // First, stop listening so we can talk
   radio.stopListening();
   data;
   radio.write( &data, sizeof(char) );

   // Now, resume listening so we catch the next packets.
   radio.startListening();

   // Tell the user what we sent back (the random numer + 1)
   Serial.print("Sent response ");
   Serial.println(data);

   dataInMeter = (data/100);

   Serial.println(dataInMeter);


 }
 display.setTextSize(3.5);
 display.setTextColor(WHITE);
 display.setCursor(0,0);
 display.print(dataInMeter);
 display.println(" m");
 display.display();
delay(10);
 display.clearDisplay();
 
}

dataInMeter = (float) data/100

Please use code tags (</> button on the toolbar) when you post code or warning/error messages. The reason is that the forum software can interpret parts of your code as markup, leading to confusion, wasted time, and a reduced chance for you to get help with your problem. This will also make it easier to read your code and to copy it to the IDE or editor. If your browser doesn't show the posting toolbar then you can just manually add the code tags:
[code]``[color=blue]// your code is here[/color]``[/code]
Using code tags and other important information is explained in the How to use this forum post. Please read it.

Please always do a Tools > Auto Format on your code before posting it. This will make it easier for you to spot bugs and make it easier for us to read. If you're using the Arduino Web Editor you will not have access to this useful tool but it's still unacceptable to post poorly formatted code. I recommend you to use the standard IDE instead.

dataInMeter = data/100.0;

A float != decimal.

Better way:

Serial.print(data/100);
Serial.print.print(".");
Serial.println(data%100);

Do you want the float value to just print or do you need it in a variable ?

septillion:
A float != decimal.

decimals != decimal. I think by "decimals" OP means decimal places

Alright, float != decimals (/ decimal places)

Floats are a can of worms on it's own and are slow on an (ATmega based) Arduino.

septillion:
A float != decimal.

Better way:

Serial.print(data/100);

Serial.print.print(".");
Serial.println(data%100);

Shouldn't this

Serial.print.print(".");

be this

Serial.print(".");

instead?

Also, to handle numbers such as 1.01, you will need one more line. I make it:

Serial.print(data/100);
Serial.print(".");
if ((data%100)<10) {
  Serial.print("0");
}
Serial.println(data%100);
Serial.println((float) n / 100)

executes faster than

    Serial.print(n / 100);
    Serial.print(".");
    int d = n % 100;
    if (d < 10) {
      Serial.print("0");
    }
    Serial.println(d % 100);

Juraj:

executes faster than

Nope, not even:

The float print takes about 374us (not counting UART blocking).
The integer print takes about 184us (not counting UART blocking), more than twice as fast.
An optimized integer print takes about 23us:

      uint8_t hundreds  = n / 100;
      uint8_t remainder = n - (hundreds * 100);
      if (hundreds > 0) {
        Serial.write( hundreds + '0' );
      }
      Serial.print('.');
      if (remainder < 10) {
        Serial.write('0');
      } else {
        uint8_t tenths = remainder/10;
        remainder = remainder - (tenths*10);
        Serial.write( tenths    + '0' );
      }
      Serial.write( remainder + '0' );
      Serial.println();

Here is the complete sketch that avoids mixing in the Serial UART operations and blocking:

// Here's a quick class that doesn't really output anything to
//   Serial.  This also avoids blocking because the characters take
//   time to be transmitted.

class DevNull : public Print
{
  static volatile uint8_t consumer;

public:

  virtual size_t write(uint8_t c) { consumer = c; };

  using Print::write; // make the other overloads visible
};
volatile uint8_t DevNull::consumer;


DevNull devNull;

const uint16_t ITERATIONS = 32;

void setup()
{
  Serial.begin( 9600 );
  Serial.println( F("Juraj test") );
  Serial.flush();

  uint8_t  n;
  uint16_t i;

  uint32_t start = micros();
  for (i=0; i < ITERATIONS; i++) {
    for (n=0; n<254; n++) {
      devNull.println((float) n / 100);
    }
  }
  Serial.println( (micros() - start)/(254.0*ITERATIONS) );
  Serial.flush();
  
  start = micros();
  for (i=0; i < ITERATIONS; i++) {
    for (n=0; n<254; n++) {
      devNull.print(n / 100);
      devNull.print(".");
      int d = n % 100;
      if (d < 10) {
        devNull.print("0");
      }
      devNull.println(d % 100);
    }
  }
  Serial.println( (micros() - start)/(254.0*ITERATIONS) );
  Serial.flush();
  
  start = micros();
  for (i=0; i < ITERATIONS; i++) {
    for (n=0; n<254; n++) {
      uint8_t hundreds = n / 100;
      uint8_t remainder = n - (hundreds * 100);
      if (hundreds > 0) {
        devNull.write( hundreds + '0' );
      }
      devNull.print('.');
      if (remainder < 10) {
        devNull.write('0');
      } else {
        uint8_t tenths = remainder/10;
        remainder = remainder - (tenths*10);
        devNull.write( tenths    + '0' );
      }
      devNull.write( remainder + '0' );
      devNull.println();
    }
  }
  Serial.println( (micros() - start)/(254.0*ITERATIONS) );
  Serial.flush();
  
  Serial.println( F("Done.") );
}

void loop() {}

Cheers,
/dev

but printing to Serial is the point. you must count the UART overhead.

if there is enough time to make Serial prints then it doesn't matter if float is slow. and the code is better readable.

Juraj:
you must count the UART overhead.

No, the UART overhead is identical in both cases. The same characters get queued, regardless of the method used to compute them.

You also have to be careful about how you measure the computation time. The measured time is inaccurate for very small intervals, so you must perform the computation hundreds of times to get a longer interval, then divide it by the number of iterations.

But because Serial can only queue 64 characters, measuring the time to print becomes overwhelmed by the slow UART baud rate. You would be measuring the baud rate, not the computation. Substituting a do-nothing device allows you to time everything except the UART overhead.

if there is enough time to make Serial prints then it doesn't matter if float is slow.

Don't forget the program size. Using float because it uses one line of code actually makes the binary program bigger. It increases the program size by 1300 bytes, because the floating-point math library has to be linked in. It "doesn't matter" only if there is enough time and enough space.

Primarily, I was correcting your misconception:

executes faster than

It does not. The float print is slower by more than 2x, so I assume that you did not actually measure the execution time. :-/

the code is better readable.

That's a different criterion. I agree that one line of code more readable. I do not agree that it executes faster.

And if you want it to go even faster, make it even less readable:

      if (n >= 200) {
        remainder = n - 200;
        devNull.write( '2' );
      } else if (n >= 100) {
        remainder = n - 100;
        devNull.write( '1' );
      } else {
        remainder = n;
        devNull.write( '0' );
      }

      devNull.write( '.' );

      if (remainder < 10) {
        devNull.write('0');
      } else {
        // See https://stackoverflow.com/questions/5558492#19076173
        uint8_t q = (remainder >> 1) + (remainder >> 2);
        q += q >> 4;
        uint8_t tenths = (q >> 3);
        remainder = remainder - (((tenths << 2) + tenths) << 1); // this is tenths*10
        if (remainder > 9) {
          remainder -= 10;
          tenths++;
        }
        devNull.write( tenths + '0' );
      }
      devNull.write( remainder + '0' );
      devNull.println();

This only takes 14us, a blistering 26 times faster than the floating-point print. Is it more readable? No.

Complete suite of tests attached.

Cheers,
/dev

PrintTest.ino (3.85 KB)