UART Byte Array Communication Explained
Hi @Athena,
I'm asking about the best way to communicate via UART between an EZB4 (JavaScript running on the EZB) and an Arduino Mega (C++). I'm a beginner and want to keep the implementation simple but reliable. My understanding is that sending a String is not necessary and that sending an array of bytes would be a better approach. I'm using the EZB4 hardware UART functions (UART.hardwareUart*) on the EZB side and Serial3 on the Arduino Mega side.
To clarify the hardware/ARC context: the Arduino Mega is connected to the EZB4 UART (hardware UART lines). The EZB is handling the real-time hardware UART I/O, and this runs inside an ARC project where ARC scripts manage the higher-level logic. The EZB code is a single JavaScript script running on the EZB (built-in ARC/EZB scripting). The Arduino code is standard C++ running on the Mega. This is not using a separate robot skill for the serial link; the UART I/O is performed by the EZB firmware calls from the script.
I have included my C++ (Arduino) TX and RX code and my JavaScript (EZB4) TX/RX code below for your evaluation. Could you rewrite the code to reliably send data using an array-of-bytes format?
Notes:
- The Arduino C++ TX and RX must be separate sketches (not combined).
- The EZB4 code runs as a single JavaScript script on the EZB (using built-in ARC/EZB scripting).
- This is part of an ARC project where the EZB handles the real-time hardware UART and ARC scripts manage higher-level logic.
C++
Data TX
if (run == 1) {
bufferFlush();
totalStepsTaken = (abs(combinedStepsTaken));
delay(50);
int byte0 = totalStepsTaken & 0xFF;
int byte1 = (totalStepsTaken >> 8) & 0xFF;
int byte2 = (totalStepsTaken >> 16) & 0xFF;
int byte3 = (totalStepsTaken >> 24) & 0xFF;
Serial3.write('T'); //Header for Total Steps Taken
// Send the four bytes one by one.
Serial3.write(byte0);
Serial3.write(byte1);
Serial3.write(byte2);
Serial3.write(byte3);
Serial.print("Sent ");
Serial.println('T');
Serial.print("Left F+B Steps: ");
Serial.println(abs(LeftFrontWheel.currentPosition()) + abs(LeftBackWheel.currentPosition()));
Serial.print("Right F+B Steps: ");
Serial.println(abs(RightFrontWheel.currentPosition()) + abs(RightBackWheel.currentPosition()));
Serial.print("Sent combinedStepsTaken: ");
Serial.println(totalStepsTaken);
Serial.println(" ");
delay(100);
}
C++
Data RX
while ( run == 1) {
if (Serial3.available() >= 1 ) {
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 == '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 out 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;
}
}
if (miss == true ) {
Serial.print( " Received Missed command: ");
Serial.println('m');
actualCourse = lastActualCourse;
}
combinedStepsTaken = abs(LeftFrontWheel.currentPosition()) + abs(LeftBackWheel.currentPosition())
+ abs(RightFrontWheel.currentPosition()) + abs(RightBackWheel.currentPosition());
if (abs(combinedStepsTaken) > combinedStepsDesired) {
stopMotors();
goto Main;
}
loopct++;
}
JavaScript Data TX and RX (single script on the EZB)
var lastActualCourse = null;
setVar("$obsDet", 0);
setVar("$missedIR", 0);
setVar("$obsScanEnd", 0);
while(true) {
//Data RX
if (UART.hardwareUartAvailable(0) >= 5) { //5
var header = UART.hardwareUartReadString(0,1);
if (header == 'D'){
var desiredCourseScaled = UART.hardwareUartRead(0, 1);
setVar("$desiredCourse", desiredCourseScaled[0]);
print("rec'd desiredCourse: " + getVar("$desiredCourse")+ " cyc: " + getVar("$cycleCount"));
} else if (header == 'T') {
print("Header: " + header);
// read all 4 bytes from uart #0 that we need for this condition into the array tmpRead
var TSTRead = UART.hardwareUartRead(0, 4);
// tmpRead[3,2,1,0] are denoted as they were sent 0,1,2,3
var totalStepsTaken = (( TSTRead[3] << 24) | ( TSTRead[2] << 16) | ( TSTRead[1] << 8) | TSTRead[0]);
print("totalStepsTaken: " + totalStepsTaken);
print( "CYC: " + getVar("$cycleCount") + " $desiredCourse: " + getVar("$desiredCourse") + " $actualCourse: " + getVar("$actualCourse"));
sleep(100);
controlCommand("$IR_1", "PauseOff");
sleep( 10 );
controlCommand("IR_STEP", "ScriptStop");
sleep( 10 );
controlCommand("NRS_71.8_58_TEST", "ScriptStop");
sleep( 10 );
halt();
}
}
// Data TX
if (getVar("$obsDet") == 1) {
UART.hardwareUartWrite(0, ['x'.charCodeAt(0)]);
sleep(5);
print( "sent to ARDU...obsDet: " + getVar("$obsDet"));
print("OBS IR STOP: " + getVar("$IRADC"));
}
if ( getVar("$missedIR") == 1) {
UART.hardwareUartWrite(0, ['m'.charCodeAt(0)]);
print("sent to ARDU...missedIR: " + getVar("$missedIR"));
}
if ( getVar("$obsScanEnd") == 1 ) {
UART.hardwareUartWrite(0, ['c'.charCodeAt(0)]);
print("sent to ARDU...Scan for obs ended: " + getVar("$obsScanEnd"));
}
if(getVar("$actualCourse") != lastActualCourse) {
var scaledValue = getVar("$actualCourse");
//Send as whole number to Arduino:
var byte0 = scaledValue & 0xFF;
var header = [64];
// Combine the header with the 4 bytes and send via UART
UART.hardwareUartWrite(0, header.concat([byte0]));
lastActualCourse = getVar("$actualCourse");
print(" $actualCourse: " + getVar("$actualCourse"));
}
sleep(200);
}
Related Hardware (view all EZB hardware)

There's a lot of information missing from the code snippets that you shared - so it's very difficult for athena or myself to understand what's going on. It would be easier if you shared your protocol definition document. If you don't have one, it's time to create one before you start moving forward. You're storing too much in your head and there's a lot of uncertainty between byte counts.
A protocol definition would be something that you can do in excel or google sheets or even notepad. But essentially you define what a "packet" looks like for each command. Here's an example...
Protocol Notes
ARC -> Arduino Commands
These are command packet definitions ARC will send to the Arduino.
1. Move servo (5 bytes)
This will instruct a servo to be moved on a servo port to a position that is specified as a 16 bit unsigned int
2. Send Movement Command (4 bytes)
This will instruct the robot to start moving in a direction
Arduino <- ARC Commands
These are command packet definitions Arduino will send to ARC.
1. Distance Detection Response (4 bytes)
Sends a message to ARC of a distance detected on one of the ultrasonic sensors
@Athena - please create a protocol definition document similar to my example that reflects your understanding of the protocol from the original question. It will be easier to get a sense of what the protocol looks like before we start writing code.
Absolutely - based on the snippets you shared, here’s a clean protocol definition draft that reflects what I can infer from your current setup.
This is important because your current code mixes:
That makes debugging much harder than it needs to be.
Proposed UART Protocol Definition
Protocol Notes
Serial3UART.hardwareUart*()on UART index0Arduino ARC Commands
1. Total Steps Taken Report
Purpose: Send the robot’s total step count from Arduino to ARC.
Packet Size: 5 bytes
0x54('T')Data Type: Signed or unsigned 32-bit integer
Endian: Little-endian
Example: If
totalStepsTaken = 123456, Arduino sends:2. Actual Course Report
From your JS code, ARC appears to expect an Arduino packet beginning with:
0x40('@')Observed/Implied Packet Format
Packet Size: 2 bytes
0x40However, your JavaScript currently treats
actualCourselike a larger numeric value, while the Arduino RX code only reads one byte after the header.So this packet should be clarified.
Better definition if it is meant to be a single-byte value:
0x40actualCourseas 0-255If it is meant to be a multi-byte number:
Then the packet should be expanded and both sides updated to match.
ARC Arduino Commands
1. Stop Command
Purpose: Tell Arduino to stop all motors immediately.
Packet Size: 1 byte
0x78('x')Notes:
'x'and callsstopMotors()2. Missed IR Command
Purpose: Notify Arduino that IR tracking was missed.
Packet Size: 1 byte
0x6D('m')Notes:
miss = trueactualCourse = desiredCourse3. Obstacle Scan Complete Command
Purpose: Tell Arduino that obstacle scan is complete.
Packet Size: 1 byte
0x63('c')4. Desired Course Command
From the JavaScript:
64(0x40)byte0) fromgetVar("$actualCourse")Current Observed Packet Format
Packet Size: 2 bytes
0x40Notes:
0x40as an incoming header foractualCourseSET_ACTUAL_COURSESET_DESIRED_COURSECOURSE_UPDATEProtocol Issues I Observed
These are the main mismatches visible in the snippets:
1. Header confusion
You are using:
'T'for total steps'x','m','c'for commands64(0x40) for another packetBut there is no central protocol spec defining what each header means.
2. Byte-count mismatch risk
Your JavaScript has:
Then it reads:
That part is reasonable for the
'T'packet.But the Arduino RX code shown for the
0x40packet only reads:So if you intended anything larger than 1 byte there, the code is incomplete.
3.
peek()+read()logic can be fragileOn Arduino, using
peek()to inspect a single byte and then later reading again can work, but it’s easy to mis-handle if bytes arrive at different times or if the buffer contains more than one packet.A more reliable approach is:
4.
hardwareUartReadString()is not ideal for binary packet parsingIn your JS, this line:
works for ASCII-style headers, but for binary protocols it’s better to use:
That way you’re working with raw bytes consistently.
Recommended Clean Protocol Version
If you want to simplify this protocol, I’d suggest something like this:
ARC Arduino
0x6D - Missed IR
0x6D0x78 - Stop
0x780x63 - Obstacle scan complete
0x630x40 - Set course
0x400-255Arduino ARC
0x54 - Total steps taken
0x540x44 - Actual course
0x440-255Why this is better
This gives you:
Next Step
If you want, I can do one of these next:
If you’d like, I can produce the final protocol definition document first in a copy/paste-friendly format, and then follow with the code rewrite.
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.