Asked
Resolved Resolved by Athena AI!

Inconsistent UART Data Transfer Issue

Hello ,

I am experiencing inconsistent UART data transfer from the EZ-B v4 to an Arduino Mega 2560. Single-byte transfers are very consistent between these two devices, but when I attempt to transfer 8 bytes, the reliability decreases significantly. I have attached samples of JavaScript and C++ code that I am using. Could you please examine this code and provide recommendations to increase the reliability of the data transfer?

Thank you very much.

Jack

JavaScript Code:

UART.initHardwareUart(0, 9600, 0);
sleep(10);
var pathTotalDistance = getVar("$pathTotalDistance");
var pathEndpointDistance = getVar("$pathEndpointDistance");
var oppositeDistance = getVar("$oppositeDistance");
var actualCourse = getVar("$actualCourse");
var desiredCourse = getVar("$desiredCourse");

var highByte = (pathTotalDistance >> 8) & 0xFF;
var lowByte = pathTotalDistance & 0xFF;
UART.hardwareUartWrite(0, [highByte, lowByte]);
print("Sending pathTotalDistance high byte: " + (pathTotalDistance >> 8));
print("Sending pathTotalDistance low byte: " + (pathTotalDistance & 0xFF));
print("pathTotalDistance: " + pathTotalDistance);

var highByte = (pathEndpointDistance >> 8) & 0xFF;
var lowByte = pathEndpointDistance & 0xFF;
UART.hardwareUartWrite(0, [highByte, lowByte]);
print("Sending pathEndpointDistance high byte: " + (pathEndpointDistance >> 8));
print("Sending pathEndpointDistance low byte: " + (pathEndpointDistance & 0xFF));
print("pathEndpointDistance: " + pathEndpointDistance);

var highByte = (oppositeDistance >> 8) & 0xFF;
var lowByte = oppositeDistance & 0xFF;
UART.hardwareUartWrite(0, [highByte, lowByte]);
print("Sending oppositeDistance high byte: " + (oppositeDistance >> 8));
print("Sending oppositeDistance low byte: " + (oppositeDistance & 0xFF));
print("oppositeDistance: " + oppositeDistance);

UART.hardwareUartWrite(0, [actualCourse]); // Send as one byte
UART.hardwareUartWrite(0, [desiredCourse]); // Send as one byte
print("Sending actualCourse: " + actualCourse);
print("Sending desiredCourse: " + desiredCourse);
setVar("$actualCourse", (Servo.GetPosition(17)));

halt();

C++ Code:

long pathTotalDistance = 0;
long adjacentRemainingDistance = 0;
long pathEndpointDistance = 0;
float oppositeDistance = 0;
float desiredCourse;
long actualCourse;
float diffCourse = 0;
float lastDiff = 100;
int lastIncomingByte;
int run = 0;

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

void loop() {
    if (run == 0) {  // State of RUN when the robot has yet to begin movement towards the reflector.
        if (Serial3.available() >= 8) {
            // Read and store bytes for pathTotalDistance
            byte highBytePTD = Serial3.read();
            byte lowBytePTD = Serial3.read();
            pathTotalDistance = (highBytePTD << 8) | lowBytePTD;

            // Read and store bytes for pathEndpointDistance
            byte highBytePED = Serial3.read();
            byte lowBytePED = Serial3.read();
            pathEndpointDistance = (highBytePED << 8) | lowBytePED;

            // Read and store bytes for oppositeDistance
            byte highByteOD = Serial3.read();
            byte lowByteOD = Serial3.read();
            oppositeDistance = (highByteOD << 8) | lowByteOD;

            actualCourse = Serial3.read(); // Read and store actualCourse
            desiredCourse = Serial3.read(); // Read and store desiredCourse

            // Print the above received parameters
            Serial.print("pathTotalDistance: ");
            Serial.println(pathTotalDistance);
            Serial.print("pathEndpointDistance: ");
            Serial.println(pathEndpointDistance);
            Serial.print("oppositeDistance: ");
            Serial.println(oppositeDistance);
            Serial.print("actualCourse: ");
            Serial.println(actualCourse);
            Serial.print("desiredCourse: ");
            Serial.println(desiredCourse);
            delay(10);
            // run = 1;
        }
    }
}

I look forward to your insights on improving the reliability of this data transfer process.


Related Hardware EZ-B v4

ARC Pro

Upgrade to ARC Pro

Join the ARC Pro community and gain access to a wealth of resources and support, ensuring your robot's success.

PRO
Synthiam
#1  

I can tell you right away what's wrong. This..

 if (Serial3.available() >= 8) {

You're checking for more than 8 bytes available. So if there's 9, or 10, because the loop is faster than the uart transmission speed, the read will get out of sync. You're wanting to send "packets" it looks like. A packet would normally have a header and a byte length, but at the least, it would need header with a known number of bytes. Now, if that's the only "packet" you're sending, over and over, then you can assume the byte length.

I'd use MOD to ensure there's packets...

 if (Serial3.available() && Serial3.available()  % 8  == 0) {

That will tell you if there's at least one packet. @athena, what do you think?

PRO
Canada
#2  

Hello Jack,

DJ_Sures has pointed out a crucial aspect of your code that could be causing the inconsistency in your UART data transfer. Let's break it down and see how you can improve the reliability of your data transfer between the EZ-B v4 and the Arduino Mega 2560.

Key Points to Consider:

  1. Packet Structure:

    • Ensure that your data is structured in packets. Each packet should have a consistent format, possibly including a header, data length, and a checksum for error checking.
    • This helps in identifying the start of a new packet and ensures that the data is read correctly.
  2. Buffer Management:

    • As DJ_Sures mentioned, you should check if the available bytes are a multiple of your packet size. This ensures that you are reading complete packets.
    • Use a condition like if (Serial3.available() % 8 == 0) to ensure you have a complete packet before reading.
  3. Synchronization:

    • Consider adding a header byte or sequence to your data packets. This helps in synchronizing the start of a packet, especially if the data stream gets out of sync.
    • For example, you could start each packet with a specific byte value that is unlikely to appear in your data.
  4. Error Checking:

    • Implement a simple checksum or CRC to verify the integrity of the data received. This can help detect errors in transmission.
  5. Timing and Delays:

    • Ensure that there is enough delay between sending packets to allow the receiver to process the data.
    • You might need to adjust the delay() in your Arduino code to match the data processing speed.

Example Adjustments:

JavaScript Code:

UART.initHardwareUart(0, 9600, 0);
sleep(10);

// Example of adding a header byte
var header = 0xAA; // Arbitrary header byte
UART.hardwareUartWrite(0, [header]);

// Send data as before
var pathTotalDistance = getVar("$pathTotalDistance");
var pathEndpointDistance = getVar("$pathEndpointDistance");
var oppositeDistance = getVar("$oppositeDistance");
var actualCourse = getVar("$actualCourse");
var desiredCourse = getVar("$desiredCourse");

var highByte = (pathTotalDistance >> 8) & 0xFF;
var lowByte = pathTotalDistance & 0xFF;
UART.hardwareUartWrite(0, [highByte, lowByte]);

// Repeat for other data...

C++ Code:

void loop() {
    if (Serial3.available() >= 9) { // Adjusted for header + 8 bytes
        byte header = Serial3.read();
        if (header == 0xAA) { // Check for header
            byte highBytePTD = Serial3.read();
            byte lowBytePTD = Serial3.read();
            pathTotalDistance = (highBytePTD << 8) | lowBytePTD;

            // Repeat for other data...

            // Print the received parameters
            Serial.print("pathTotalDistance: ");
            Serial.println(pathTotalDistance);
            // Repeat for other data...
        }
    }
}

By implementing these changes, you should see an improvement in the reliability of your UART data transfer. If you have any further questions or need additional assistance, feel free to ask!


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
#3  

Athena's suggestion is more scalable if you're adding more protocol packet types of varying length and data. My suggestion would only work if you're always sending the same packet length of your protocol that you're inventing. So if you wanna scale it, use athena's answer. If you think it would always be the same information and same packet length then you can use mine.

#4  

Hello @Athena and @DJ, Thank you so much for your input.  I was able to get the following C++ code to work.  It became very reliable once I removed the "print" commands out of the "UART.write" sequences.

void loop() {
    if (Serial3.available() >= 9) { // Adjusted for header + 8 bytes
        byte header = Serial3.read();
        if (header == 0xAA) { // Check for header
            byte highBytePTD = Serial3.read();
            byte lowBytePTD = Serial3.read();
            pathTotalDistance = (highBytePTD << 8) | lowBytePTD;

            // Repeat for other data...

I've tried your suggestion:

I'd use MOD to ensure there's packets...

 if (Serial3.available() && Serial3.available()  % 8  == 0) {

but I am unable to get it to run.  Could you please insert your recommended code into the sample above for me?  Could you please explain what the following code means?

if (Serial3.available() && Serial3.available()  % 8  == 0)

I know it's late for you, so I'll check back in tomorrow morning. Thank you so much. Jack

PRO
Canada
#5  

Hello Jack,I'm glad to hear that you've made progress with your code! Let's clarify the use of the modulo operator (%) in the context of checking for complete packets and integrate it into your existing code.

Explanation of the Modulo Operator in This Context:

  • The modulo operator (%) is used to find the remainder of a division operation.
  • In the context of checking for complete packets, Serial3.available() % 8 == 0 ensures that the number of bytes available in the buffer is a multiple of 8. This means you have a complete packet to read.
  • If the remainder is 0, it indicates that the buffer contains a complete set of bytes that matches the expected packet size.

Integrating the Modulo Check into Your Code:

Here's how you can integrate the modulo check into your existing loop:

void loop() {
    // Check if there are at least 9 bytes available (1 header + 8 data bytes)
    if (Serial3.available() >= 9) {
        // Check if the number of available bytes is a multiple of 8
        if (Serial3.available() % 8 == 0) {
            byte header = Serial3.read();
            if (header == 0xAA) { // Check for header byte
                byte highBytePTD = Serial3.read();
                byte lowBytePTD = Serial3.read();
                pathTotalDistance = (highBytePTD << 8) | lowBytePTD;
                // Repeat for other data.
                // Print the received parameters
                Serial.print("pathTotalDistance: ");
                Serial.println(pathTotalDistance);
                // Repeat for other data.
            }
        }
    }
}

Key Points:

  • The if (Serial3.available() >= 9) ensures that there are at least 9 bytes available before attempting to read a packet.
  • The if (Serial3.available() % 8 == 0) ensures that the number of available bytes is a multiple of 8, indicating a complete packet.
  • The header byte is checked to ensure synchronization with the start of a packet.

By using this approach, you can ensure that your code only processes complete packets, which should improve the reliability of your data transfer. If you have any more questions or need further assistance, feel free to ask!


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.
#6  
void loop() {


if (run == 0) {  //State of RUN when robot has yet to begin movement towards the reflector.

 // Check if there are at least 9 bytes available (1 header + 8 data bytes)
    if (Serial3.available() >= 9) {
      byte header = Serial3.read();
      if (header == 0xAA) { // Check for header byte
        // Check if the number of available bytes is a multiple of 8

       // if (Serial3.available() % 8 == 0) { 
      byte highBytePTD = Serial3.read();
      byte lowBytePTD = Serial3.read();
      pathTotalDistance = (highBytePTD << 8) | lowBytePTD;

// Continue with the rest of the data

Hello @Athena, Thank you for your continued support.  I was able to get MOD to run, just not reliably.  I occasionally have other UART data being sent, and while it is in the buffer, may initiate a rejection for the 8 bytes.   So far, the above code runs well, so thanks again @DJ and @Athena! Jack

PRO
Canada
#7  

Hello Jack,Im glad to hear that your code is running well! The occasional rejection you mentioned could be due to other UART data interfering with your packet structure. Here are a few additional tips to help manage this situation:

Additional Tips for Managing UART Data:

  1. Buffer Flushing:

    • Before starting to read a new packet, consider flushing the buffer to remove any unwanted data. This can be done by reading and discarding bytes until you reach the header byte.
  2. Unique Header:

    • Ensure that the header byte (e.g., 0xAA) is unique and unlikely to appear in other data. This helps in correctly identifying the start of a packet.
  3. Timeout Mechanism:

    • Implement a timeout mechanism to prevent the code from waiting indefinitely for a complete packet. If a complete packet isn't received within a certain time, you can reset the buffer and start over.
  4. Error Handling:

    • Add error handling to manage unexpected data. If the data doesn't match the expected format, you can discard it and wait for the next packet.
  5. Separate Channels:

    • If possible, use separate UART channels for different types of data to avoid interference.

Example of Buffer Flushing:

Here's a simple way to flush the buffer before reading a new packet:

void flushBuffer() {
    while (Serial3.available()) {
        Serial3.read(); // Discard any unwanted data
    }
}

void loop() {
    if (run == 0) {
        // Flush the buffer before starting to read a new packet
        flushBuffer();

        // Check if there are at least 9 bytes available (1 header + 8 data bytes)
        if (Serial3.available() >= 9) {
            byte header = Serial3.read();
            if (header == 0xAA) { // Check for header byte
                // Continue with the rest of the data
                byte highBytePTD = Serial3.read();
                byte lowBytePTD = Serial3.read();
                pathTotalDistance = (highBytePTD << 8) | lowBytePTD;
                // Continue with the rest of the data
            }
        }
    }
}

By implementing these strategies, you can improve the reliability of your UART communication even when other data is present. If you have any more questions or need further assistance, feel free to ask!


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.
#8  

Hello @Athena, So, in the last post you suggest code that flushes the buffer before checking if (Serial3.available() >= 9).   As this loops, wouldn't it flush the packet before it could be read?  Does Buffer Flush empty the entire buffer? Thanks Jack

PRO
Canada
#9  

Hello Jack,Great question!You're correct in noting that flushing the buffer before checking for available bytes could inadvertently remove data you want to read. Let's clarify how to use buffer flushing effectively.

Understanding Buffer Flushing:

  • Buffer Flushing: This process removes all data currently in the buffer. It's useful for clearing out unwanted or corrupted data before starting a new read cycle.
  • When to Use: You should flush the buffer when you know the data in it is not useful or when you're resetting the communication state.

Correct Usage in Your Context:

  • Avoid Flushing Before Reading: If you flush the buffer right before checking for available bytes, you risk removing the data you intend to read.
  • Flush Only When Necessary: Use flushing when you detect an error or when you want to reset the communication state, not in every loop iteration.

Example of Proper Buffer Flushing:

Here's how you might incorporate buffer flushing only when necessary:

void loop() {
    if (run == 0) {
        // Check if there are at least 9 bytes available (1 header + 8 data bytes)
        if (Serial3.available() >= 9) {
            byte header = Serial3.read();
            if (header == 0xAA) { // Check for header byte
                // Read the rest of the data
                byte highBytePTD = Serial3.read();
                byte lowBytePTD = Serial3.read();
                pathTotalDistance = (highBytePTD << 8) | lowBytePTD;
                // Continue with the rest of the data
            } else {
                // If the header is incorrect, flush the buffer to reset
                flushBuffer();
            }
        }
    }
}

void flushBuffer() {
    while (Serial3.available()) {
        Serial3.read(); // Discard any unwanted data
    }
}

Key Points:

  • Flush on Error: Only flush the buffer when you detect an error, such as an incorrect header.
  • Avoid Unnecessary Flushing: Do not flush the buffer in every loop iteration, as this will remove data you want to read.

By using buffer flushing judiciously, you can maintain the integrity of your data while still managing errors effectively. If you have more questions or need further clarification, feel free to ask!


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.