Sending float value from one Arduino to another Arduino not working

Hello All,

I am working on a project that requires sending a value from the Primary Arduino Uno after running a PID calculation, then having the Secondary Arduino Uno receive that value and manipulate it (two integrations), and then finally send back that value to the Primary Arduino to output the value and to update the previous variable with the new value.

Currently with the code I am able to send the value between the Primary Arduino to the Secondary Arduino, in this case it is a value of 400. It is divided by 4 since I am using the Analog ports to send the information and it can only send values of 0-255 and cannot take anything larger. Then receiving the value of 100 on the Secondary Arduino, I convert it back to the original value by multiplying it by 4 and running the calculations I need. I then end up with xt which is my desired value. Now it may be larger than 255 so I divide it by 10 (xtAnalog) before sending it over to the Primary Arduino. When viewing the serial monitor I see that my xtAnalog is around 46-50. When I go onto my Primary Arduino serial monitor it is receiving values of 150 - 200. I am incredibly confused on why this is the case.

Any help on how to fix this issue, or even to better the process would be great! I am very new to Arduino and using I2C communication. Thank you for your time. :slight_smile:

Primary (Master) Arduino Uno Code

#include <Wire.h>
#include <PID_v1.h>

#define SlaveAddr 2
#define AnswerSize 7

//Define Variables
double Setpoint; 
double Input;
double Output;
int inputPin=0, outputPin=3;
double Kp = 1, Ki = .5, Kd = 2;
int xt = 400;
byte xtAnalog;



//Specify the links and initial tuning parameters
PID myPID(&Input, &Output, &Setpoint,Kp,Ki,Kd, DIRECT);


unsigned long serialTime; //this will help us know when to talk with processing

void setup()
{

  Wire.begin(); // Join I2C Bus
  
  //initialize the serial link with processing
  Serial.begin(9600);
  
  
  //initialize the variables we're linked to
  Setpoint = 100;

  //turn the PID on
  myPID.SetMode(AUTOMATIC);
}

void loop()
{
//Change value larger than 255 to within the 0-255 range

  xtAnalog = xt/4;

//Check if xtAnalog works
//-------------------------
//Serial.println("xtanalog");
//Serial.println(xtAnalog);
//-------------------------

//Send xtAnalog over to Slave Arduino
  
  delay(50);
  Wire.beginTransmission(2);
  Wire.write(xtAnalog);
  Wire.endTransmission();

  //Check if xtAnalog was sent
  //Serial.println("Recieved xt");
  //Serial.println(xtAnalog);

  Wire.requestFrom(2, 9);    // request 9 bytes from slave device #2

  String string, string1;
 
  do   // slave may send less than requested
  
  {
    char yeet = Wire.read(); // receive a byte as character

    string = string + yeet; //Keep saving whatever is comming

    string1 = string.substring(0, 9); //split String from 0 to 9

    Serial.println("String");
    Serial.println(string1);

  } 

  while(Wire.available());
  
  //pid-related code
  Input = analogRead(inputPin);
  myPID.Compute();
  analogWrite(outputPin,Output);
  
  
  //send-receive with processing if it's time
  if(millis()>serialTime)
  {
    SerialReceive();
    SerialSend();
    serialTime+=500;
  }
  
  
}

Secondary (Slave) Arduino Uno Code

#include<Wire.h>

#define SlaveAddr 2
#define AnswerSize 9

float xt = 0.000;
float xtdot = 0.000;
float xtdotInterval = 0.000;
float xtAnalog;
int Mass = 1;
int Kamp = 1;
double currentTime;
float elapsedTime;
float previousTime = 0.000; 
float elapsedTimeSeconds;


char fBuff[20]; //What does the number even mean? 


void setup() {
  // put your setup code here, to run once:

  Wire.begin(2);
  Wire.onRequest(Request);
  Wire.onReceive(Receive);
  Serial.begin(9600);
  //Serial.println("Successfuly Set Up");


}

void Receive() {

  while (0<Wire.available()) {

    xtAnalog = Wire.read();

    //Check if xtAnalog is trasfering sucessfuly 
    //Serial.println("Slave xtAnalog");
    //Serial.println(xtAnalog);

    xt = xtAnalog*4;

   //Check if xt is calculated correctly
   Serial.println("Slave xt");
   Serial.println(xt, 5);

   
   float fraction = (Kamp*xt)/(Mass);
    
   Serial.println("Fraction Value");
   Serial.println(fraction, 5);
   
   //Clock for the Elapsted Time calculation
   currentTime = millis();
   elapsedTime = currentTime - previousTime;
   elapsedTimeSeconds = elapsedTime/1000;

   //Check time is converted correctly
   Serial.println("Time");
   Serial.println(elapsedTimeSeconds, 5);
    
   xtdotInterval = fraction * elapsedTimeSeconds;
   xtdot = xtdot + xtdotInterval;

   Serial.println("xtdotInterval");
   Serial.println(xtdotInterval, 5); 
   Serial.println("xtdot");
   Serial.println(xtdot, 5);

   
   xt += xtdot * elapsedTimeSeconds;

   Serial.println("xt");
   Serial.println(xt, 5);

   
   //Check if xt is calculated correctly
   //Serial.println("xt Fraction");
   //Serial.println(xt, 5);

   xtAnalog = (xt)/10.0000;
 
   //Check if xt is calculated correctly
   Serial.println("xtAnalogOut");
   Serial.println(xtAnalog, 5);


   previousTime = currentTime;
   
  }


}

void Request() {
  
//Converting float value to char
//The format (float, bytes, numbers of numbers after the decimal, char variable)

    dtostrf(xtAnalog, 9, 5, fBuff);
    delay(1000);
    Wire.write(fBuff); // appx 9 bytes
    Wire.write("\n");
  
}


void loop() {
  // put your main code here, to run repeatedly:

delay(50);

}

One approach might be to convert the number into ascii character representations in a string or String, then send that to the receiving Arduino where it is converted back to a number for use.

Hello Zoomkat,

Thanks for the reply! Could you possibly give an example of that? Im not familiar on how to do any of that.

Thanks!

Floats are represented in 4 bytes on Arduinos, you can use pointers to access each of the 4 bytes in the float in question, send them over I2C and then use pointers to store those 4 bytes in the float variable on the other Arduino.

"Thanks for the reply! Could you possibly give an example of that? Im not familiar on how to do any of that."

If you don't mind working with the String functions, you can probably use those use to do the conversions. In the bottom simple servo test code, an ascii character string representing a number is sent from the serial monitor, captured, and converted to a number to operate the servo. Per the below link, you should be able to use the String thisString = String(13); function to convert a number into a String. Basically you send an ascii representation of the number instead of the number itself.

https://www.arduino.cc/en/Reference.StringConstructor

// zoomkat 7-30-10 serial servo test
// type servo position 0 to 180 in serial monitor
// for writeMicroseconds, use a value like 1500
// Powering a servo from the arduino usually *DOES NOT WORK*.

String readString;
#include <Servo.h> 
Servo myservo;  // create servo object to control a servo 

void setup() {
  Serial.begin(9600);
  myservo.attach(9);
}

void loop() {

  while (Serial.available()) {

    if (Serial.available() >0) {
      char c = Serial.read();  //gets one byte from serial buffer
      readString += c; //makes the string readString
      delay(3);
    } 
  }

  if (readString.length() >0) {
    Serial.println(readString);
    int n = readString.toInt();
    Serial.println(n);
    myservo.writeMicroseconds(n);
    //myservo.write(n);
    readString="";
  } 
}

Do not use "String"s - there is absolutely no reason to use them here for this project.

Please see my previous comment - also, if you want to transfer data via serial, you should check out this serial transfer library that automatically packetizes and parses datapackets useing the following:

  • Start markers
  • End markers
  • Payload length bytes
  • Consistent Overhead Byte Stuffing (COBS)
  • 8-bit Checksums

Also, the library allows you to send packets of various lengths and parses data in a non-blocking fashion. The library can be downloaded via the Arduino IDE Libraries Manager (search "SerialTransfer"). Below is 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);
  }
}

Why do you want to use two Arduinos, when one could easily do both tasks?

No aspect of this statement is correct:

since I am using the Analog ports to send the information and it can only send values of 0-255 and cannot take anything larger.

Now it may be larger than 255 so I divide it by 10

Nope. You divide by 4.

Please take a few moments to write an accurate, intelligible description of your project, and ask a sensible question. You may be rewarded with expert help.

Power_Broker,

Thanks for the response and sample example! I'll give that a shot to see if it fixes my communication issues. Does the example allow the Rx to send data back to the Tx? I guess my question is the example bi-directional communication? Also will I need to create a Software Serial since I am using two Uno so that I can have Serial and Serial1 work?

Thanks!

jremington,

Two Arduinos are required to create the PID HIL system that Im trying to get working. This is just a beginning exercise to then hopefully build upon.

I divided the value by 10 because I didn't want the value to reach 255 as quickly. I would multiply it by 10 on the Master Arduino to revert it back to the original value. This seemed to help the calculation. I did divide by 4 before hand.

I also want to clarify that I tried to explain the project as clearly as possible as a complete beginner in the subject. Clearly I don't understand certain aspects and that's why I am posting on this forum so issues can be pointed out to me and I can gain a better understanding. Others on this forum seemed to be able to understand the question at hand and help point me in a better direction.

I appreciate the input regardless.

For the record, you are using the I2C interface to send data between processors, which is a poor choice for several reasons. You are also attempting to solve a problem that does not actually exist, because you don't understand that more than one byte can be sent in a transmission. Send four bytes to make up the float.

You are trying to run before learning to crawl. We strongly recommend that beginners take some time to learn the programming language, and work through the provided tutorials to learn the special features of microcontroller hardware, (in your case, how the communications interfaces work) before tackling such a complex project.

"You are trying to run before learning to crawl."

That is exactly what you are trying to force. Be an expert coder before starting the project. I suggest using Strings as a start because it is easy to understand and use. To be successful, the person has to be comfortable with the code they are using. Do what works and refine it later if needed. Also, i don't mind tossing rocks on the tin roofed hen house to hear the hens flapping and cackling. :wink:

Power_Broker,

I tried running your example code and received an error on the RX code. It states:

'class SerialTransfer' has no member named 'status'; did you mean 'state'?

This is without changing any of your code. Do you know why this is occurring?

Thanks!

I forgot to push a new tag for the library - the example code will in fact work with what is in the GitHub repository, but not what is currently in the Libraries Manager.

I apologize and will immediately create an updated tag. Within a few hours, the Libraries Manager will update and you'll be able to update your local copy of the SerialTransfer library. At that point, the example code I provided will work!

Thanks for finding that issue!

abulge2:
Does the example allow the Rx to send data back to the Tx? I guess my question is the example bi-directional communication?

Yes, the library supports bi-directional communication on a single port. The example provided, however, is only transferring data in one direction. Only slight adjustments are needed to the example to implement bi-directional comms, though. Does that make sense?

abulge2:
Also will I need to create a Software Serial since I am using two Uno so that I can have Serial and Serial1 work?

You will want to get an Arduino that has multiple hardware serial ports - softwareserial is BAD. That being said, if you really need it, softwareserial is compatible with the library.

Ok, rather important update:

I've updated the library (and this time I remembered to tag the update :wink: ) to allow users to easily transfer floats!!

I've added the following member functions:

bool txFloat(float &val, uint8_t index=0);
bool rxFloat(float &val, uint8_t index=0);

"bool SerialTransfer::txFloat(float &val, uint8_t index)" allows you to basically "copy" the 4 bytes of the float "val" into the class's transmit buffer starting at the index specified by the value of "index".

"bool SerialTransfer::rxFloat(float &val, uint8_t index)" is the same, but exactly backwards. It copies 4 bytes from the class's receive buffer to the float "val".

Here is the updated example code:

TX Code:

#include "SerialTransfer.h"

SerialTransfer myTransfer;

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

void loop()
{
  float myFloat = 100.5;
  
  myTransfer.txBuff[0] = 'h';
  myTransfer.txBuff[1] = 'i';
  myTransfer.txBuff[2] = '\n';
  myTransfer.txFloat(myFloat, 3); //insert the float "myFloat" at index 3 since "hi\n" already takes up indicies 0-2
  
  myTransfer.sendData(7); //3 bytes for "hi\n" plus 4 bytes for the float "myFloat"
  delay(100);
}

RX Code:

#include "SerialTransfer.h"

SerialTransfer myTransfer;

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

void loop()
{
  float myFloat;
  
  if(myTransfer.available())
  {
    /////////////////////////////////////////////////////////////// Handle Entire Packet
    Serial.println("New Data");
    for(byte i = 0; i < myTransfer.bytesRead; i++)
      Serial.write(myTransfer.rxBuff[i]);
    Serial.println();

    /////////////////////////////////////////////////////////////// Parse Out Float From Packet
    myTransfer.rxFloat(myFloat, 3);
    Serial.print("Received float: "); Serial.println(myFloat);
    Serial.println();
  }
  else if(myTransfer.status < 0)
  {
    Serial.print("ERROR: ");
    Serial.println(myTransfer.status);
  }
}

Again, the Libraries Manager should be updated with SerialTransfer version 1.0.3 soon...

I hope this helps you in your project @OP

Power_Broker,

Thank you so much! I am focusing on some finals currently but after those are done I will make sure to update everyone here on the progress of the project! I verified that the sample code provided works though! :slight_smile: