Asked
Resolved Resolved by Athena AI!

Sending Float Value Via UART

Hello @Athena,

I would like some assistance with sending a floating-point value via UART from an EZ-B v4 using JavaScript to an Arduino Mega using C++. Here's a snippet of my current code:

EZ-B v4 JavaScript Code:

UART.initHardwareUart(0, 9600);

var actualCourse = 91.55;

// Need an appropriate method to send the floating-point value correctly
UART.hardwareUartWrite(0, [actualCourse]);

Arduino Mega C++ Code:

float actualCourse;

void setup() {
  resetAll();
  Serial3.begin(9600);  // Communication with EZ-B
  Serial.begin(9600);   // Debugging via Serial Monitor
}

void loop() {
  if (Serial3.available() >= 1) {
    byte incomingByte = Serial3.read();
    delay(100);
    if (incomingByte == 'A') {  // Confirm header byte
      Serial.print("incomingByte:  ");
      Serial.println('A');
      actualCourse = Serial3.read();  // This approach doesn't handle float correctly
      Serial.print("actualCourse: ");
      Serial.println(actualCourse);
    }
  }
}

My objective is to transmit the entire floating-point value of "actualCourse" (91.55) via UART without any loss of precision or rounding issues. Could you please advise on how to achieve accurate floating-point transmission between the EZ-B and Arduino?

Thank you,
Jack


Related Hardware Roli Rover
Related Control Serial Toolbox

ARC Pro

Upgrade to ARC Pro

Take control of your robot's destiny by subscribing to Synthiam ARC Pro, and watch it evolve into a versatile and responsive machine.

PRO
Canada
#1  

Below is an explanation and a couple of approaches to send a full precision float (e.g. 91.55) from an EZ-B using ARC scripting (JavaScript) to an Arduino without losing the value.

Problem

Right now, the EZ-B code uses:

UART.hardwareUartWrite(0, [actualCourse]);

This sends an array with one element (the JavaScript number) that isn’t automatically broken down into its full 4-byte IEEE 754 float representation. On the Arduino side, your code is reading only one byte, so it isn’t capturing the whole value.

Two Common Solutions

  1. Send as a String (ASCII) and Parse on Arduino

In this approach you convert the float into a string and send that. On the Arduino, you can use functions like Serial.parseFloat() or Serial.readStringUntil() to convert back to a float.

EZ-B JavaScript Example: js var actualCourse = 91.55; // Optionally send a header character to help with framing, e.g., 'A' UART.hardwareUartWrite(0, "A" + actualCourse.toString() + "\n");

Arduino C++ Example: cpp float actualCourse; void setup(){ Serial3.begin(9600); // Communication with EZ-B Serial.begin(9600); // Debugging via Serial Monitor } void loop(){ // Wait until a full line is received if (Serial3.available()){ String data = Serial3.readStringUntil('\n'); // Read until newline // Optionally check for header 'A' int idx = data.indexOf('A'); if(idx != -1){ String numStr = data.substring(idx+1); actualCourse = numStr.toFloat(); Serial.print("actualCourse: "); Serial.println(actualCourse); } } }

Pros: Simpler debugging (you see the ASCII data), no worry about platform-specific binary formats.
Cons: More overhead in terms of bytes sent and a bit slower parsing.

  1. Send as Binary (Typed Array) and Reconstruct on Arduino

Convert the float into its binary representation (4 bytes) on the EZ-B, then on the Arduino read all 4 bytes into a float variable using a union or memcpy.

EZ-B JavaScript Example: js // Convert a float to 4 bytes using typed arrays var actualCourse = 91.55; var buffer = new ArrayBuffer(4); var floatView = new Float32Array(buffer); floatView[0] = actualCourse; var byteView = new Uint8Array(buffer); // Optionally send a header byte for framing (for example 65 which is 'A') var header = [65]; // Convert the typed array to a regular array of numbers var dataBytes = Array.from(byteView); UART.hardwareUartWrite(0, header.concat(dataBytes));

Arduino C++ Example: cpp float actualCourse; union { byte b[4]; float f; } dataUnion; void setup(){ Serial3.begin(9600); // EZ-B communication Serial.begin(9600); // Debugging } void loop(){ // Wait until at least 5 bytes (1 header + 4 data bytes) are available if (Serial3.available() >= 5) { if (Serial3.read() == 'A') { // Check for header byte // Read the next 4 bytes into the union for (int i = 0; i < 4; i++) { while (Serial3.available() == 0) ; // Wait for byte dataUnion.b[i] = Serial3.read(); } actualCourse = dataUnion.f; Serial.print("actualCourse: "); Serial.println(actualCourse); } } }

Pros: Sends the data in a compact 4-byte form without conversion overhead.
Cons: Requires careful attention to endianness (most EZ-B systems and Arduinos use little-endian) and framing to ensure you’re reading the correct bytes.

Recommendation

If precision and speed are critical (and you can guarantee the same binary format on both sides), then using the binary approach is best.

If you prefer simplicity and easier debugging (at the expense of a few extra bytes and parsing overhead), the string method is a good approach.

Follow-up

Let me know if you need help with:

  • Adjusting the header/framing process
  • Using union vs. memcpy on the Arduino side
  • Debug logging on either side to ensure the full value is received

Feel free to ask for further assistance or additional examples.


I am a customer support GPT-powered AI and still learning. If you have doubts about my response, please use the support documentation to verify my answer.
#2  

Hello @Athena, Can you please send the "Binary Approach"(2nd option) in a form that I can cut and paste for the JavaScript and C++? Thank you so much. Jack

PRO
Canada
#3  

Below are the complete cutandpaste code examples for the binary approach. The JavaScript code converts the float into four bytes (using a TypedArray) with a header byte, and the Arduino C++ code reads the header and then the four bytes into a union to reassemble the float.


EZ-B JavaScript Code

// Initialize UART on port 0 at 9600 baud
UART.initHardwareUart(0, 9600);

// Define your float value
var actualCourse = 91.55;

// Create an ArrayBuffer to hold 4 bytes (32-bit float)
var buffer = new ArrayBuffer(4);
var floatView = new Float32Array(buffer);
floatView[0] = actualCourse;  // Store the float value

// Get the underlying bytes from the ArrayBuffer
var byteView = new Uint8Array(buffer);

// Optional: add a header byte (e.g., 65, which is the ASCII code for 'A')
var header = [65];

// Combine header and data bytes into one array
var dataBytes = Array.from(byteView);
UART.hardwareUartWrite(0, header.concat(dataBytes));

Arduino C++ Code

float actualCourse;  // Variable to store the received float

// Use a union to convert 4 bytes into a float
union {
  byte b[4];
  float f;
} dataUnion;

void setup() {
  Serial3.begin(9600);  // Communication with the EZ-B
  Serial.begin(9600);    // Debugging via Serial Monitor
}

void loop() {
  // Ensure at least 5 bytes are available: 1 header byte + 4 data bytes
  if (Serial3.available() >= 5) {
    // Read header byte and check for the expected value (65)
    if (Serial3.read() == 65) { 
      // Read the next 4 bytes and store them in the union array
      for (int i = 0; i < 4; i++) {
        while (Serial3.available() == 0);  // Wait for byte
        dataUnion.b[i] = Serial3.read();
      }
      // Retrieve the float value 
      actualCourse = dataUnion.f;
      
      // Print for debugging
      Serial.print("actualCourse: ");
      Serial.println(actualCourse);
    }
  }
}

Explanation

  1. JavaScript Side:

    • We create a 4byte buffer and use a Float32Array view to store our float.
    • A corresponding Uint8Array view allows us to extract the individual bytes.
    • A header byte (65, you can change this if needed) is prepended to help frame the data when reading on the Arduino.
  2. Arduino Side:

    • We wait until at least 5 bytes are available on Serial3.
    • The header byte is checked to ensure proper synchronization.
    • The four following bytes are read into a union, which reassembles the bytes into the original float.

Feel free to ask if you need any further adjustments or additional help with framing or debugging!


I am a customer support GPT-powered AI and still learning. If you have doubts about my response, please use the support documentation to verify my answer.
PRO
Synthiam
#4   — Edited

@athena, is it easier to send the value multiplied by 100 or 1000 into an int32 and then reverse the multiplication on the Arduino side? This is because the float may not be interpreted the same on Arduino as the float from Arc's JavaScript.

ARC uses Jint. Jint focuses on being spec-compliant with ECMAScript 5.1 and selectively implements ES6+ features-but binary data buffers like ArrayBuffer and TypedArray are not supported by default.

PRO
Canada
#5  

Below is one effective workaround-multiplying your float by 100 (or 1000) to convert it into a 32bit integer and then sending that integer’s bytes. On the Arduino side you simply reverse the scaling to recover your original value. This approach can be easier when working with ARC’s JavaScript engine (Jint), which may not support binary data buffers (ArrayBuffer/TypedArray) by default.

Below are complete cutandpaste examples.


EZB JavaScript Code

In this example, we multiply the float by 100, convert it into an integer, and then split that integer into 4 bytes using bitwise operators:

// Initialize UART on port 0 at 9600 baud
UART.initHardwareUart(0, 9600);

// Define your float value and scale it (here, using 100 for two decimals)
var actualCourse = 91.55;
var scaledValue = Math.round(actualCourse * 100); // becomes 9155

// Break the 32-bit integer into 4 separate bytes
var byte0 = scaledValue & 0xFF;
var byte1 = (scaledValue >> 8) & 0xFF;
var byte2 = (scaledValue >> 16) & 0xFF;
var byte3 = (scaledValue >> 24) & 0xFF;

// Optional header byte for framing (for example: 65 which is 'A')
var header = [65];

// Send header plus the 4 bytes
UART.hardwareUartWrite(0, header.concat([byte0, byte1, byte2, byte3]));

Arduino C++ Code

The Arduino code waits until it receives at least 5 bytes (1 header + 4 data bytes), then reconstructs the integer and reverses the scaling to obtain the original float:

#include <Arduino.h>

float actualCourse;  // Reconstructed float value
int32_t scaledValue; // Scaled integer value

void setup() {
  Serial3.begin(9600);     // Communication with EZ-B
  Serial.begin(9600);      // Debugging via Serial Monitor
}

void loop() {
  // Ensure at least 5 bytes are available: 1 header + 4 data bytes
  if (Serial3.available() >= 5) {    
    if (Serial3.read() == 65) {  // Check for header byte (65 = 'A')
      
      // Read the next 4 bytes to reconstruct the 32-bit integer
      scaledValue = 0;
      for (int i = 0; i < 4; i++) {
        while (Serial3.available() == 0);  // Wait for each byte
        scaledValue |= ((int32_t)Serial3.read() & 0xFF) << (8 * i);
      }
      
      // Reverse the scaling (dividing by 100.0 to get back the float)
      actualCourse = scaledValue / 100.0;
      
      // Print for debugging
      Serial.print("actualCourse: ");
      Serial.println(actualCourse);
    }
  }
}

Explanation

  1. On the EZB Side (JavaScript):
    The float value is multiplied by 100 and rounded to form an integer (e.g. 91.55 becomes 9155).
    The integer is split into 4 bytes using bitwise shifts and masks.
    A header byte (here, 65) is prepended for framing purposes so the Arduino knows where the data begins.
    The byte array is sent over UART.

  2. On the Arduino Side (C++):
    The code waits until 5 bytes are available.
    It checks the header byte, then reads the next 4 bytes.
    The 4 bytes are reassembled into an int32_t using bit shifting.
    Finally, the integer is divided by 100.0 to recover the original float value.

This method sidesteps the need for ArrayBuffer/TypedArray support and ensures that both sides interpret the scaled number consistently.

Feel free to ask if you need further adjustments or additional help!


I am a customer support GPT-powered AI and still learning. If you have doubts about my response, please use the support documentation to verify my answer.
PRO
Synthiam
#6   — Edited

Rewrite that but don’t use a for loop in the arduino read section so it’s easier for robojack to understand the code. Rebuild the int32 inline

#7  

Hello @Athena, Thank you for your help.  Could you please rewrite the code using the recommendation from DJ in the previous post? Thanks. Jack

PRO
Canada
#8  

Below is an updated solution that avoids the use of a for loop. The JavaScript side multiplies the float by 100, splits the scaled integer into four bytes, and sends a header plus the bytes. On the Arduino side, each byte is read individually and then reassembled inline into an int32_t without a loop.


EZ-B JavaScript Code

// Initialize UART on port 0 at 9600 baud
UART.initHardwareUart(0, 9600);

// Define your float value and scale it (using 100 for two decimals)
var actualCourse = 91.55;
var scaledValue = Math.round(actualCourse * 100); // -> 9155

// Break the 32-bit integer into 4 separate bytes using bitwise operators
var byte0 = scaledValue & 0xFF;
var byte1 = (scaledValue >> 8) & 0xFF;
var byte2 = (scaledValue >> 16) & 0xFF;
var byte3 = (scaledValue >> 24) & 0xFF;

// Optional header byte for framing (for example: 65 which is ASCII 'A')
var header = [65];

// Combine the header with the 4 bytes and send via UART
UART.hardwareUartWrite(0, header.concat([byte0, byte1, byte2, byte3]));

Arduino C++ Code

#include <Arduino.h>

float actualCourse;          // Reconstructed float value
int32_t scaledValue;         // Scaled integer value received

void setup() {
  Serial3.begin(9600);       // Communication with EZ-B
  Serial.begin(9600);        // Debugging via Serial Monitor
}

void loop() {
  // Ensure at least 5 bytes are available: 1 header + 4 data bytes
  if (Serial3.available() >= 5) {
    // Read and check the header byte (expected to be 65)
    if (Serial3.read() == 65) {
      // Read each data byte individually (wait for each byte if needed)
      while (Serial3.available() < 4);  // Wait until 4 bytes are available
      
      int32_t b0 = Serial3.read();
      int32_t b1 = Serial3.read();
      int32_t b2 = Serial3.read();
      int32_t b3 = Serial3.read();
      
      // Rebuild the scaled integer inline without a loop
      scaledValue = (b0 & 0xFF) | 
                    ((b1 & 0xFF) << 8) | 
                    ((b2 & 0xFF) << 16) | 
                    ((b3 & 0xFF) << 24);
                    
      // Reverse the scaling to obtain the original float value
      actualCourse = scaledValue / 100.0;
      
      // Print the result for debugging
      Serial.print("actualCourse: ");
      Serial.println(actualCourse);
    }
  }
}

Explanation

  1. EZ-B JavaScript:

    • Multiplies the float value by 100 (to preserve two decimal places) and rounds it to form an integer.
    • Uses bitwise operations to extract each byte from the 32-bit integer.
    • A header byte (65) is added to help the Arduino synchronize the start of the data transmission.
    • The concatenated array is sent via UART.
  2. Arduino C++:

    • Waits until at least five bytes are available (one header byte and four data bytes).
    • It verifies the header byte, then reads four bytes individually.
    • Reassembles the 32-bit integer inline without a loop using bit shifting.
    • Divides the reconstructed integer by 100.0 to recover the original float.
    • Outputs the result on the Serial Monitor for debugging.

Feel free to ask if you have any additional questions or need further clarification!


I am a customer support GPT-powered AI and still learning. If you have doubts about my response, please use the support documentation to verify my answer.
#9  

Hello @Athena, I have confirmed that the above UART code works well on stand-alone scripts.   I have incorporated the JavaScript into my existing ARC script, and it performs great. The problem I am having is incorporating the C++ into my existing Arduino script.  I have included a snippet below and have Commented out the part of my existing code that prevents receiving UART data.  I have rearranged various statements and cannot seem to get it to work. Could you please review the following C++ and incorporate the Commented-Out section so that it will run properly? Thanks. Jack

while ((LeftFrontWheel.currentPosition() + RightFrontWheel.currentPosition()) <= pathEndpointDistance) {

        // Ensure at least 5 bytes are available: 1 header + 4 data bytes
  if (Serial3.available() >= 5) {
    // Read and check the header byte (expected to be 65)
     if (Serial3.read() == 65) {
      // Read each data byte individually (wait for each byte if needed)
      while (Serial3.available() < 4);  // Wait until 4 bytes are available
      
      int32_t b0 = Serial3.read();
      int32_t b1 = Serial3.read();
     int32_t b2 = Serial3.read();
    int32_t b3 = Serial3.read();
      
      // Rebuild the scaled integer inline without a loop
      scaledValue = (b0 & 0xFF) | 
                    ((b1 & 0xFF) << 8) | 
                    ((b2 & 0xFF) << 16) | 
                    ((b3 & 0xFF) << 24);
                    
      // Reverse the scaling to obtain the original float value
      actualCourse = scaledValue / 100.0;
      
      // Print the result for debugging
      Serial.print("actualCourse: ");
      Serial.println(actualCourse);
    }
  } 
  
//if (Serial3.available() > 0) {  // Checking for incoming STOP command due to obstruction detected or inability to track reflector.
        // Read the incoming byte
       // int incomingByte = Serial3.read();
       // Check if the incoming byte is 'x'
       // if (incomingByte == 'x') {
        //  Serial.print("x :   ");
       //Serial.println('x');
        //  stopMotors();
        //  goto Main;
     // } 
//}

}




PRO
Canada
#10  

Below is an example of how you can integrate the UART floatreceiving code into your existing Arduino code while checking for an incoming stop command. In this example, we assume that your wheels’ positions are handled in a loop that waits until a specific condition is met (here, when the sum of LeftFrontWheel and RightFrontWheel positions equals 5). Inside this loop we:

  1. Check for at least 5 bytes available (one header plus four data bytes).
  2. Read the header byte and verify it is 65 (ASCII A).
  3. While waiting for the four required data bytes, also check if another incoming byte (e.g. the stop command x) is received.
  4. If a stop command is detected, we call stopMotors() and break out (using a goto label in this example for simplicity).
  5. If there is no stop command, we then read the four data bytes and reconstruct the 32bit scaled number without using a for loop.
  6. Finally, we reverse the scaling to recover the original float value.

Below is the integrated code snippet:


// Assume that LeftFrontWheel.currentPosition() and RightFrontWheel.currentPosition()
// are functions that return a current wheel position integer value.
// Also assume stopMotors() is a defined function to stop your motors.

// The stop command is assumed to be the character 'x'.
// "Main" is a label to jump to once we need to break out of this loop.

void loop() {
  // Sample condition: wait while the sum of wheel positions equals 5
  // (Change this condition according to your application)
  while ((LeftFrontWheel.currentPosition() + RightFrontWheel.currentPosition()) == 5) {
  
    // Check if we have at least 5 bytes available:
    // 1 header byte + 4 data bytes.
    if (Serial3.available() >= 5) {
    
      // Read and check the header byte (expected to be 65)
      int headerByte = Serial3.read();
      if (headerByte == 65) {
      
        // Wait here until there are at least 4 data bytes
        // or check if an incoming STOP command arrives.
        while (Serial3.available() < 4) {
          // If any other byte is pending, peek it
          if (Serial3.available() > 0) {
            int incomingByte = Serial3.peek(); // do not remove it yet
            // Check if this byte is our STOP command (assume 'x' is the stop command)
            if (incomingByte == 'x') {
              // Consume the byte and stop motors
              Serial3.read();  
              Serial.print("Received STOP command: ");
              Serial.println(incomingByte);
              stopMotors();
              goto Main; // jump out to the Main section
            }
          }
        }
        
        // Now read the 4 bytes that represent our scaled float value
        int32_t b0 = Serial3.read();
        int32_t b1 = Serial3.read();
        int32_t b2 = Serial3.read();
        int32_t b3 = Serial3.read();
        
        // Rebuild the scaled integer inline (note the shift amounts)
        int32_t scaledValue = (b0) | (b1 << 8) | (b2 << 16) | (b3 << 24);
        
        // Reverse the scaling: if we multiplied by 100 when sending, divide by 100.0
        float actualCourse = scaledValue / 100.0;
        
        // Print the result for debugging
        Serial.print("Received actualCourse: ");
        Serial.println(actualCourse);
      }
      else {
        // If header does not match, you might want to flush the UART buffer or ignore this message.
        Serial.print("Unexpected header: ");
        Serial.println(headerByte);
      }
    }
  }  // end of while

Main:
  // Continue with the rest of your code.
  // ...
}

Explanation

  1. UART Data Check & Header:
    The code only proceeds if at least five bytes are available on Serial3. It reads the header and confirms the value is 65.

  2. Stop Command Check:
    In the inner while loop (waiting for four data bytes), the program peeks for a pending byte. If it sees the stop command (here defined as 'x'), it consumes that byte, prints a message, calls stopMotors(), and uses a goto statement to exit this section (labelled Main). Adjust the stop command as needed.

  3. Rebuilding the Float Value:
    The four bytes are read into individual variables (b0, b1, b2, b3) and then reassembled using bit shifting. Finally, the scaled integer is converted back to a float by dividing by 100.0.

Feel free to adjust the condition or stop command according to the specific needs of your project. If you have further questions or need more adjustments, just let me know!


I am a customer support GPT-powered AI and still learning. If you have doubts about my response, please use the support documentation to verify my answer.
#11  

Hello @Athena, Thank you for your help.  I have been able to implement your suggested code from the previous post.  I am having trouble with performing math using the UART data sent to the Arduino.  I need to do the following:  diffCourse = (actualCourse - desiredCourse).   All are float values and diffCourse needs to display a negative or positive number.


float desiredCourse = 90.00; //example of number.  It will constantly be computed (+ or -)
float actualCourse = 87.00; //example of number.  It will constantly be computed (+ or -)   
float diffCourse = 00.00;  // can be + or -



void loop() {

float diffCourse =  actualCourse - desiredCourse;  // Determines if error state exists between actualCourse and desiredCourse.

     Serial.print("diffCourse:  ");
     Serial.println(diffCourse,2);

}

Can you help with C++ code that will allow diffCourse to display positive or negative values? Thanks. Jack

PRO
Synthiam
#12  

You are defining your diffCourse twice....

But also, don't forget how floats work. In your example there will not be any decimal digit so without knowing why you don't think it works, there's nothing wrong with what you wrote - other than defining a variable twice.. once public and once in a private scope.

PRO
Canada
#13  

Below is a complete C++ example that shows how to compute the difference between your actual and desired course values and then print the result with two decimal places. Make sure you don’t redeclare diffCourse if it’s already declared at a global scope. In this example, diffCourse is declared locally inside loop() only once:

float desiredCourse = 90.00;  // desired course value
float actualCourse  = 87.00;  // actual course value

void setup() {
  Serial.begin(9600);  // start serial communication for debugging
}

void loop() {
  // Calculate the difference (positive or negative)
  float diffCourse = actualCourse - desiredCourse;
  
  // Print the result with two decimal digits
  Serial.print("diffCourse: ");
  Serial.println(diffCourse, 2);

  // Delay to slow down the output (for demonstration purposes)
  delay(1000);
}

Explanation

  1. Variable Declaration:
    Both actualCourse and desiredCourse are defined as global (or they could be local, if you prefer) but, importantly, diffCourse is defined only once inside the loop. This ensures that you are not redefining the variable twice.

  2. Arithmetic Operation:
    The expression

    float diffCourse = actualCourse - desiredCourse;
    

    correctly computes the difference. Since both operands are floats, the result will also be a float that can be positive or negative.

  3. Displaying the Result:
    The function Serial.println(diffCourse, 2); prints the float value with two decimal places. You can adjust the number of decimal places by changing the second argument.

  4. Avoiding Variable Redeclaration:
    If you already have a global or class-level declaration of diffCourse, remove the local declaration within the loop to avoid accidental redefinition.

Feel free to adjust the values or modify the code as needed for your application. If you have any more questions or need further clarifications, let me know!


I am a customer support GPT-powered AI and still learning. If you have doubts about my response, please use the support documentation to verify my answer.
#14   — Edited

Hello @Athena, Thank you for your help and DJ as well.  I found another "local float duplicate" that was preventing proper execution.  I am so grateful for your help.  This resource is absolutely wonderful! I am working on sending more UART the other direction as well as refining the "map" function for my motors, but I'll open up a new post for that.  Again, thank you.  Jack.

PRO
Canada
#16  

That's great news, Jack! I'm really happy to hear you solved the issue. It's always rewarding when things start working correctly, and it sounds like you have some exciting projects ahead with the UART data and motor mapping.

If you run into any more questions-whether it's about sending more complex UART data or refining your motor control algorithms-please don't hesitate to reach out.

Keep up the great work, and happy building!


I am a customer support GPT-powered AI and still learning. If you have doubts about my response, please use the support documentation to verify my answer.