Asked — Edited

Arduino Serial Communication Troubleshooting

Hi @Athena,

I'm using the following code snippet on an Arduino Mega that is communicating via UART with an EZB4:


if (Serial3.available() >= 1) {
    int incomingByte = Serial3.peek(); // Do not remove it yet
    // Check if this byte is our STOP command (assuming 'x' is the stop command)

    if (incomingByte == 'm') {
        Serial3.read();
        miss = true;
        actualCourse = desiredCourse;
    }

    if (incomingByte == 'x') {
        // Consume the byte and stop motors
        Serial3.read();
        Serial.print("Received STOP command: ");
        Serial.println('x');
        stopMotors();
        goto Main; // Jump to the Main section
    }

    if (incomingByte == 'c') {
        Serial3.read();
        Serial.print("(Received obs scan complete) command ");
        Serial.println('c');
    }
}

if (Serial3.available() >= 1) {
    // Read and check the header byte (expected to be 64)
    int headerByte = Serial3.read();

    if (headerByte == 64) { 
        int32_t b0 = Serial3.read();
        int32_t scaledValue = (b0);   
        actualCourse = scaledValue;
        Serial.print(" ..............---------------------------- rec'd actualCourse: ");
        Serial.println(actualCourse, 4); 

        lastActualCourse = actualCourse;
        initial = true;    
        miss = false;
        loopct = 0;
    }
}

The lower half of the code sometimes returns gibberish for the "actualCourse". I suspect the issue may be related to the Serial3.peek() and Serial3.available() statements before the actual Serial3.read(). Could you please review the structure of my code and suggest a solution for reliable data transfer?

Thank you,
Jack



Related Hardware (view all EZB hardware)
Synthiam ARC enables Arduino Mega 2560 control with 8 ADC, 13 PWM, 21 digital ports and 24 servos; includes firmware, shield and tutorials
USB
Servos 24
Camera
Audio
UART 3
I2C
ADC 8
Digital 21

Related Robot Skills (view all robot skills)
EZB UART parser and Serial-TCP bridge to connect EZ-Robot cameras via Teensy USB, enabling ARC camera control and USB-to-serial bridging.
Serial Terminal by Synthiam
PC and EZB serial terminals for ARC: connect, monitor and send text/ASCII to serial devices. Supports COM, flow control, baud, DTR/RTS, hex

ARC Pro

Upgrade to ARC Pro

Unleash your creativity with the power of easy robot programming using Synthiam ARC Pro

Author Avatar
PRO
Canada
Twitter YouTube GitHub
#1  

Good catch - the symptoms and your code both point to framing and timing issues in how you read the serial stream. A few problems to fix:

  • You sometimes read only one byte but treat it as a full value. Serial3.read() returns a single byte (or -1), but you assign it to an int32 and treat it like the full 32-bit value.
  • Calling available() in separate places and using peek() can leave you attempting to read a header/payload before the whole packet has arrived (race condition).
  • If the start/header byte gets out of sync (noise / dropped bytes), the parser will return garbage.
  • peek() doesn't consume a byte; it’s useful but you still must ensure the full packet is present before consuming.

Two reliable approaches:

  1. Binary fixed-length packets with explicit start byte + payload length + checksum (recommended for speed/efficiency).
  2. ASCII/text lines terminated by newline (easier to debug with Serial Monitor / Serial Terminal).

Below are concrete examples for both approaches and extra tips.

  1. Robust binary packet parser (start byte 0x40, 4-byte signed big-endian int payload)
  • Wait until you have the full packet before reading.
  • Discard garbage until you find the start byte.
  • Read all payload bytes atomically (use readBytes or check available >= payloadLen).
  • Optionally add checksum to detect corruption.

Example:

const uint8_t PACKET_START = 0x40; // 64
const size_t PAYLOAD_LEN = 4;      // 4 bytes => int32_t

void loop() {
  // Process as many full packets as possible
  while (Serial3.available() > 0) {
    // 1) Find start byte
    if (Serial3.peek() != PACKET_START) {
      // discard one byte of garbage and continue searching
      Serial3.read();
      continue;
    }

    // 2) Make sure full packet is available (start byte + payload)
    if (Serial3.available() < 1 + PAYLOAD_LEN) {
      // not enough bytes yet, wait for more
      break;
    }

    // 3) consume start byte
    Serial3.read();

    // 4) read payload
    uint8_t buf[PAYLOAD_LEN];
    size_t got = Serial3.readBytes(buf, PAYLOAD_LEN); // will return immediately because we checked available
    if (got != PAYLOAD_LEN) {
      // read error - handle/reset
      continue;
    }

    // 5) assemble big-endian signed 32-bit
    int32_t val = (int32_t)((uint32_t)buf[0] << 24 | (uint32_t)buf[1] << 16 | (uint32_t)buf[2] << 8 | (uint32_t)buf[3]);

    // Now use val (scale if needed)
    actualCourse = val; // or scale/convert as appropriate
    Serial.print("received actualCourse: ");
    Serial.println(actualCourse);
  }

  // ... other loop processing ...
}

Notes:

  • Replace the bit-assembly with little-endian if your sender sends little-endian (i.e., buf[0] is LSB).
  • If payload length or type differs, adjust accordingly.
  • Consider adding a one-byte checksum after payload and validate before accepting value.
  1. ASCII/line protocol (human-readable, easier debug) If you can change the sender, one of the easiest robust protocols is a newline-terminated ASCII string like: "ACT:1234\n" or just "1234\n"

Parsing example:

void loop() {
  if (Serial3.available()) {
    String line = Serial3.readStringUntil('\n'); // blocking until '\n' or timeout
    line.trim();
    if (line.length() == 0) return;

    // optional: handle single-byte commands first
    if (line == "x") {
      Serial.println("Received STOP command: x");
      stopMotors();
      goto Main;
    }

    // parse integer
    long val = line.toInt(); // simple conversion
    actualCourse = (int32_t)val;
    Serial.print("received actualCourse: ");
    Serial.println(actualCourse);
  }
}

Advantages: easy to inspect with Serial Monitor or the Serial Terminal skill, and less fragile.

Other suggestions and troubleshooting checklist

  • Ensure baud rates match on both ends and no flow-control mismatch.
  • Use the Serial Terminal skill in ARC to watch raw bytes if using USB COM: https://synthiam.com/Support/Skills/Communication/Serial-Terminal?id=16071
  • If you stay binary, add a checksum or CRC so you can detect and discard corrupted frames.
  • Keep single-purpose packet handling: handle single-byte commands (x / c / m) and full packets in the same parsing loop so they don't step on each other.
  • Avoid goto for flow control if possible - use state machine or flags; it’s harder to reason about when dealing with async serial data.

If you want a minimal state-machine parser that handles both single-byte commands and fixed binary packets, I can provide that.

Quick questions to tailor a final solution

  1. Is the EZB (sender) sending binary or ASCII? How many bytes represent actualCourse, and what endianness?
  2. What exact baud rate and flow control are you using?
  3. Can you change the sender format (e.g., add checksum or newline)?
  4. If you can, please paste a short hex dump of a few packets (e.g., from ARC Serial Terminal) so I can match the parser exactly.

If you want, I can:

  • Provide a full Arduino parser state machine that also handles commands 'x','c','m' and recovers from out-of-sync conditions.
  • Convert this to little-endian assembly if your sender is little-endian.
  • Give a simple checksum/CRC example.

References:

Tell me the sender packet format (binary vs ASCII, payload length and endianness) and I’ll produce the exact parser code you can drop in.


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.