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)
Related Robot Skills (view all robot skills)
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.
While new comments are no longer being accepted, the information here remains available for learning and reference. Check the most recent comment for additional details.

Hi @Athena, So, it appears that in the State Machine Concept, each step (Header, Command, Payload, etc.) has been preprogrammed into the UART receiver code so that it recognizes first the Command which triggers it to look for the appropriate remaining Command and Payload. Incredible, logical. I am thrilled to be at this step in my robot development. My present code, a real mishmash of bits, proved my robot concept but hardly adequate to move forward. I am unbelievably excited to learn how to transform it into properly structured logic. Can you please continue with the Arduino TX sketch and then the ARC JavaScript loop? Thanks, Jack
1) Arduino TX Sketch
What this sketch does
This sketch is the Arduino sending data back to ARC.
It will send packets like:
2) Arduino TX Flowchart
3) Arduino TX Sketch
Clean TX template
4) Beginner Explanation of Arduino TX
What this code is doing
Think of the Arduino as saying:
It builds a packet like this:
0xA5Why helper functions exist
Each packet size is different:
Helpers prevent mistakes and keep things clean.
5) Example TX Flow (TOTAL STEPS)
Step 1
Step 2
Arduino decides to report it.
Step 3
Packet:
Step 4
Step 5
ARC reads:
Step 6
ARC reacts:
6) ARC JavaScript Loop
What it does
7) ARC Flow
8) ARC JavaScript Code
9) Key Concept
The loop does two jobs:
10) Important Tip
Even inside one loop, think in 3 layers:
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.
Hi @Athena, Two requests.
2. In the JavaScript TX/RX code, can you expound on the Loop three layers you refer to? (event logic, Packet building, Packet parsing)? Can you provide an example? Thank you. Jack
Absolutely,Jack - both requests make sense.
1) Combine Arduino TX + RX into one sketch
Yes. That is actually the cleaner way to do it for your project, because:
RUN == 0andRUN == 1can both be handled in oneloop()The key idea is:
RUNSo the Arduino becomes one unified communication node.
2) The three layers in the ARC JavaScript loop
Think of the ARC loop as three jobs happening repeatedly:
Layer 1: Event logic
This asks:
Examples:
$obsDet == 1$missedIR == 1$actualBearingchangedThis layer is the decision layer.
Layer 2: Packet building
If the event logic says "yes, send something," then this layer:
Example:
79.357935This layer is the packing layer.
Layer 3: Packet parsing
This layer looks at incoming UART data and asks:
This layer is the reception layer.
Combined Arduino sketch
Below is a single Arduino sketch that includes both TX and RX behavior, with a parser state machine.
ARC JavaScript: the three-layer loop
Here’s the beginner-friendly way to think about it:
Below is a single script using that structure.
Example of the three layers in action
Example: ARC sends
STOPLayer 1: Event logic
ARC sees:
$obsDet == 1So it decides to stop the motors.
Layer 2: Packet building
It builds:
Layer 3: Packet parsing
Arduino receives:
0xA50x01Then Arduino:
Beginner takeaway
The best way to think about it is:
ARC side
Arduino side
If you want, I can next give you:
$obsDet,$missedIR,$actualBearing, and$obsScanEnd.If you’d like, I recommend the next step be the rewritten Arduino sketch with your RUN logic inserted, because that will make the architecture feel very real.
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.
Hi @Athena, Can you please send a flow chart for the whole system? This way I can confirm it meets my objectives before writing code. Jack
Absolutely,Jack - here’s a whole-system flowchart for the UART architecture before you write code.
I’m keeping it at the system level first so you can confirm the logic matches your rover objectives.
Whole-System UART Flowchart
1) High-level architecture
2) Overall control loop
3) ARC Arduino flowchart
This is the direction for:
STOPMISSED IRACTUAL BEARINGRIGHT PIVOTLEFT PIVOTWAYPOINT DATAOBSTACLE SCAN COMPLETE4) Arduino ARC flowchart
This is the direction for:
NAV ACKTOTAL STEPS TAKENDESIRED BEARING UPDATEOBSTACLE SCAN REQUEST5) Example command path:
WAYPOINT DATAThis is the best "big" example because it shows the full system working.
6) Example command path:
TOTAL STEPS TAKEN7) Example command path:
ACTUAL BEARING8) What RUN = 0 and RUN = 1 mean in the system
RUN = 0
This is the setup / alignment / pre-move phase.
RUN = 1
This is the forward navigation phase.
9) The simplest "big picture" summary
That is the entire system in one sentence.
10) Does this match your objective?
Based on what you described, yes - this flow supports your objective:
If you want, I can do the next step and turn this into a clean numbered protocol document with:
That would make it much easier to start coding from.
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.
Hi @Athena, I am trying to verify ARC to Arduino UART functionality. I need help getting the proper values to the ARC script for the UART to send. I would like the ARC script to pick up global variables for the Waypoint Data. Can you provide ARC code that will allow the previous posted ARC script to send the 'Waypoint Data' to the previous posted Arduino sketch?
Thank you. Jack
Absolutely,Jack - yes,ARC can pick up global variables and use them to build the Waypoint Data packet for your Arduino sketch.
The cleanest way is:
UART.hardwareUartWrite()1)Recommended ARC global variables
Use these globals in ARC:
$pathTotalDistance$combinedStepsDesired$oppositeDistance$bearingAlignment$reflectorBearing$sensorPrime$desiredBearing$actualBearing$trackDir$sendWaypointNowtrigger flagSuggested value formats
$pathTotalDistance120000$combinedStepsDesired84000$oppositeDistance24000$bearingAlignment0or1$reflectorBearing97.00$sensorPrime4$desiredBearing79.35$actualBearing79.00$trackDir0or12)Encoding note
To keep the packet reliable, I recommend encoding like this:
uint32for the three large distancesuint8for alignment, sensor prime, track directionuint16for bearings, multiplied by 100So:
79.35becomes793597.00becomes97003)ARC JavaScript: Waypoint Data sender
This script reads the globals and sends the waypoint packet when
$sendWaypointNowis1.4)How to use it in ARC
Set the global variables first
You can set them from:
Then set the trigger
When you are ready to send the waypoint packet:
That tells the script to send the data once.
5)Example of setting the globals
Here is a simple example script that loads test values:
6)Important beginner note
Your Arduino sketch must decode the packet in the same byte order you send it.
That means the Arduino should expect:
pathTotalDistanceas 4 bytescombinedStepsDesiredas 4 bytesoppositeDistanceas 4 bytesbearingAlignmentas 1 bytereflectorBearingas 2 bytessensorPrimeas 1 bytedesiredBearingas 2 bytesactualBearingas 2 bytestrackDiras 1 byte7)One thing I want to confirm
Do you want me to now write the matching Arduino waypoint RX handler so it decodes this exact ARC packet correctly?
If yes, I can give you the Arduino code next in a clean beginner-friendly format.
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.