Serial Communication between Arduinos - Sending and Receiving Floats

Hello,
Working on a project where one Arduino Nano measures the RPM from a hall sensor and sends that value to a Mega via the TX and RX pins. The code used to measure the RPM outputs the value as a float. I cant figure out how to Serial.write a float value to be read on the Mega via the Serial Communication. I've tried to convert the float to a integer and then send it, but the values once converted are incorrect. Im pretty new to coding, and keep seeing the use of char arrays and atoi in other forums but its all a bit confusing and nothing has worked so far. I concerned about write and read speeds within the loops, so if there a simple and easy way to transmit these values, that would be great. Heres the code I have this far:

//Sending:

int hall_pin = 13;
float hall_thresh = 20.0;

void setup() {
Serial.begin(9600);
pinMode(hall_pin, INPUT);
}

void loop() {
RPM_Counter();
Transmit();
delay(5);
}

void RPM_Counter() {
float hall_count = 1.0;
float start = micros();
bool on_state = false;
while (true) {
if (digitalRead(hall_pin) == 0) {
if (on_state == false) {
on_state = true;
hall_count += 1.0;
}
} else {
on_state = false;
}
if (hall_count >= hall_thresh) {
break;
}
}
float end_time = micros();
float time_passed = ((end_time - start) / 1000000.0);
float rpm_val = (hall_count / time_passed) * 60.0;

//Serial.println(rpm_val); Used as reference

delay(50);
}

void Transmit() {
/*
String myStr;
int rpm_val;
myStr = String(rpm_val);
Serial.println(myStr);
delay(50);
*/

Serial.write(rpm_val / 256);
Serial.write(rpm_val % 256);
delay(50);
}

//Receiving

//String inString="";

void setup() {
Serial.begin(9600);
Serial1.begin(9600);
/*
while (!Serial) {
}
Serial.println("\n\nString to Int():");
Serial.println();
*/
}

void loop() {
/*
if (Serial1.available() > 0) {
int inChar = Serial1.read();
if (isDigit(inChar)) {
inString += (char)inChar;
}
if (inChar == 'n') {
Serial.println(inString.toInt());
inString = "";
}
}
*/
int rpm_val;
while (Serial1.available()) {}
delay(20);
byte b1 = Serial1.read();
while (!Serial1.available()) {}
delay (20);
byte b2 = Serial1.read();
rpm_val = b2 + b1 * 256;
Serial.println(rpm_val);
}

It is not a good idea to use the String (capital S) class on an Arduino as it can cause memory corruption in the small memory on an Arduino. This can happen after the program has been running perfectly for some time. Just use cstrings - char arrays terminated with '\0' (NULL).

Have a look at the examples in Serial Input Basics - simple reliable ways to receive data. There is also a parse example to illustrate how to extract numbers from the received text.

The technique in the 3rd example will be the most reliable. It is what I use for Arduino to Arduino and Arduino to PC communication.

You can send data in a compatible format with code like this (or the equivalent in any other programming language)

Serial.print('<'); // start marker
Serial.print(value1);
Serial.print(','); // comma separator
Serial.print(value2);
Serial.println('>'); // end marker

Sending data as text makes debugging much easier and I would only send binary data if it was essential for project performance.

The Arduino millis() function produces an unsigned long value. I suspect you will make life much easier or yourself if you stick with those except when you need to produce a value for display to a human. In other words, send unsigned-longs rather than floats.

...R

Check if the following sketches work (untested):
Setup:
DPin-2 of NANO (SRX) <------------- DPin-18 of MEGA (TX1)
DPin-3 of NANO (STX) --------------> DPin-19 of MEGA (RX1)
GND of NANO <---------------------> GND of MEGA

NANO-TX:

//Sending:
#include<SoftwareSerial.h>
SoftwareSerial SUART (2, 3); //SRX =2, STX = 3
int hall_pin = 13;
float hall_thresh = 20.0;
float rpm_val;

void setup()
{
  Serial.begin(9600);
  SUART.begin(9600);
  pinMode(hall_pin, INPUT);
}

void loop()
{
  RPM_Counter();
  SUART.println(rpm_val, 2);
  delay(1000);
}

void RPM_Counter()
{
  float hall_count = 1.0;
  float start = micros();
  bool on_state = false;
  while (true)
  {
    if (digitalRead(hall_pin) == 0)
    {
      if (on_state == false)
      {
        on_state = true;
        hall_count += 1.0;
      }
    }
    else
    {
      on_state = false;
    }

    if (hall_count >= hall_thresh)
    {
      break;
    }
  }
  float end_time = micros();
  float time_passed = ((end_time - start) / 1000000.0);
  rpm_val = (hall_count / time_passed) * 60.0;

  //Serial.println(rpm_val); Used as reference
  delay(50);
}

MEGA-RX:

//Receiving
char myRPM[5];

void setup() 
{
  Serial.begin(9600);
  Serial1.begin(9600);
}

void loop() 
{
  byte m = Serial1.readBytesUntil('\n', myRPM, 5);
  myRPM[m] = '\0';
  float rpm = atof(myRPM);
  Serial.println(rpm, 2);
  }

float end_time = micros();micros DOES NOT return a float.
This should not need to be pointed out to @GolamMostafa

cjr7315:
Just figured it out rn, used a if statement to print the rpm above the filtering threshold

Thanks for all of your guys help!

Have you noted down the comment of Post#5 on micros()? Please, give some time to sort it out -- why the data type of this variable: end_time should be unsigned long rather than float though float also assigns 32-bit space for the said variable.

Seems I'm late to the party, but you should consider using a robust serial transfer library to packetize and parse your data automatically. You can even send floats if you do some messing around with pointers and such.

You can find the library in the Arduino IDE's "Libraries Manager" under "SerialTransfer".

Here's the example code:

TX Arduino:

#include "SerialTransfer.h"

SerialTransfer myTransfer;

void setup()
{
  Serial.begin(115200);
  Serial1.begin(115200);
  myTransfer.begin(Serial1);
}

void loop()
{
  myTransfer.txBuff[0] = 'h';
  myTransfer.txBuff[1] = 'i';
  myTransfer.txBuff[2] = '\n';
  
  myTransfer.sendData(3);
  delay(100);
}

RX Arduino:

#include "SerialTransfer.h"

SerialTransfer myTransfer;

void setup()
{
  Serial.begin(115200);
  Serial1.begin(115200);
  myTransfer.begin(Serial1);
}

void loop()
{
  if(myTransfer.available())
  {
    Serial.println("New Data");
    for(byte i = 0; i < myTransfer.bytesRead; i++)
      Serial.write(myTransfer.rxBuff[i]);
    Serial.println();
  }
  else if(myTransfer.status < 0)
  {
    Serial.print("ERROR: ");
    Serial.println(myTransfer.status);
  }
}

P.S. I know several people who posted here have already seen this library but seem to refuse to suggest it to people who might benefit from it. If there is some constructive criticism of the library, I would be happy to hear it. Just not sure why such a useful library is being largely ignored...

Power_Broker:
Seems I'm late to the party, but you should consider using a [...]

We may address the data type issue of OP:
end_time variable should be declared as unsigned long instead of float. Now, the OP complains that such declaration provides him incorrect RPM reading.