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
Don't limit your robot's potential – subscribe to ARC Pro and transform it into a dynamic, intelligent 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, I would like to use the following ARC script to communicate via UART with the following Arduino sketch:
ARC UART Script
ARDUINO UART Sketch
For the ARC UART script, I need a separate Global Variable Script that will create the global variables that the ARC UART script will send. Can you provide a Global Variable Script to do this? Please include the following Global Variables:
RIGHT PIVOT CMD will also need to send the 'Number of Steps' required during the Right Pivot. LEFT PIVOT CMD will also need to send the 'Number of Steps' required during the Left Pivot.
WAYPOINT DATA will need to include:
Thank you. Jack
Yes - the cleanest approach is to create a startup/global-variable initialization script in ARC that defines the values your UART script will read.
Below is a Global Variable Script you can run once at project start, or from a button/script control, to initialize the variables.
ARC Global Variable Initialization Script
Notes on the pivot commands
For the pivot commands, I recommend these separate globals:
$rightPivotSteps$leftPivotStepsThen your ARC UART script can send them like this:
Example: Right Pivot
Example: Left Pivot
Recommended trigger variables
To keep things simple and reliable, I suggest these trigger flags:
$sendWaypointNow$rightPivotNow$leftPivotNowThat way the script only sends when you explicitly tell it to.
Recommended values and meaning
$rightPivotSteps$leftPivotSteps$pathTotalDistance$combinedStepsDesired$oppositeDistance$bearingAlignment0or10 = DA,1 = OA$reflectorBearing$sensorPrime$desiredBearing$actualBearing$trackDir0or10 = FD,1 = RDOne important correction
Your ARC UART script should convert Right Pivot Steps and Left Pivot Steps from globals too, just like Waypoint Data. So I’d suggest adding these helper functions:
ARC menu tip
You can place this script in a Script robot skill, then use the kebab menu on that skill to open its settings and keep the logic organized.
If you want, I can next give you a single combined ARC script that:
That would make it easier to paste directly into ARC.
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, Please create a single combined ARC script that:
Absolutely,Jack. Below is a single combined ARC JavaScript script that:
I’ve kept it aligned with your binary UART protocol using:
0xA5Combined ARC UART Script
What this script does
Initializes globals
It sets up all the values your UART logic needs:
$rightPivotSteps$leftPivotSteps$pathTotalDistance$combinedStepsDesired$oppositeDistance$bearingAlignment$reflectorBearing$sensorPrime$desiredBearing$actualBearing$trackDirIt also creates trigger flags like:
$sendWaypointNow$rightPivotNow$leftPivotNow$obsDet$missedIRHow to use it
To send Waypoint Data
Set:
To send Right Pivot
Set:
To send Left Pivot
Set:
Important note about reading responses
Your current Arduino sketch must match the same packet format:
0xA5headerIf you want, I can now do the next step and give you a matching corrected Arduino sketch that fully aligns with this ARC script, including:
If you'd like, I can also clean up your Arduino sketch so it compiles correctly because the version pasted earlier has a few syntax issues that should be fixed before testing.
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, Yes, please do the next step and give me a matching corrected Arduino sketch that fully aligns with this ARC script, including:
waypoint RX parsing right/left pivot RX parsing total steps TX NAV ACK TX desired bearing TX
I'd like it if you can also clean up the Arduino sketch. Thanks. Jack
Absolutely,Jack. Below is a cleaned,matching Arduino Mega sketch that aligns with the ARC JavaScript you’re using.
It includes:
Matching UART Packet Format
This sketch assumes the same packet format used by your ARC script:
0xA5ARC Arduino Commands
0x01Stop Motors0x02Missed IR0x03Actual Bearing0x04Right Pivot0x05Left Pivot0x06Waypoint Data0x07Obstacle Scan CompleteArduino ARC Responses
0x10NAV ACK0x11Total Steps Taken0x12Desired Bearing0x13Obstacle Scan RequestArduino Mega Sketch
Important corrections made
Your pasted sketch had several issues that would prevent reliable compilation or operation:
#includewas incompletesendHeaderAndCmd()was used but not definedsendUInt16Packet()was used but not definedhandlePacket()was referenced but not properly formedif(len = 2)needed to beif(len == 2)readUInt16LE()had broken parenthesesOne thing to verify
Your ARC script currently sends this waypoint payload:
pathTotalDistance= 4 bytescombinedStepsDesired= 4 bytesoppositeDistance= 4 bytesbearingAlignment= 1 bytereflectorBearing= 2 bytessensorPrime= 1 bytedesiredBearing= 2 bytesactualBearing= 2 bytestrackDir= 1 byteThat totals 21 bytes, which is what the Arduino sketch above expects.
Suggested next step
If you want, I can now give you a matching ARC test script that:
If you'd like, I can also make a version of this Arduino sketch with your actual stepper control placeholders inserted so it’s closer to your real rover logic.
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.
Thank you @Athena. I'll look at the Arduino sketch in the morning.
I tried the 'Combined ARC UART Script', and running it causes my EZB4 to drop offline immediately. Through the process of elimination, I narrowed the offending code line as follows: It is in the 'MAIN LOOP'
Can you examine why this is the case and offer a solution? Thanks. Jack
Yes - that specific line is very likely causing the drop because ARC JavaScript is not expecting
sendActualBearing()to be called every loop with a value that may be invalid, repeated, or not yet initialized, and in your current structure it can create a rapid send/read conflict or a type issue.Most likely causes
1)
$actualBearingmay be coming through as a string or non-numberIf
getVar("$actualBearing",-1)returns something unexpected,parseFloat()can yieldNaN.Then the comparison logic becomes unreliable, and the function may still be called with bad data.
2) The script is sending too often
If
$actualBearingchanges frequently, this line can spam UART every 20 ms:That can overwhelm the link or conflict with other UART processing.
3) UART read/write contention in a single tight loop
Because your script is doing both:
inside the same fast loop, ARC can become unstable if the loop is too aggressive or if the UART traffic is not throttled.
Recommended fix
Solution: only send actual bearing when it is valid and rate-limited
Instead of sending immediately on every change, add:
NaNSafer ARC JavaScript replacement
Replace your current actual-bearing block with this:
Even better: send integers instead of floats
Since UART reliability is better with whole bytes, I recommend storing actual bearing as an integer scaled by 100 in ARC too.
For example:
79.00790079.357935Then send only integers.
Example revised function
Revised loop
Best practice for your project
For your rover, I strongly recommend:
Important ARC note
If you are reading
$actualBearingfrom a servo or another skill, make sure:What I recommend next
I can give you a cleaned full Combined ARC UART Script v2 that includes:
That would let you replace the problematic line entirely.
If you want, I can rewrite the entire ARC script so it’s safer for ARC and won’t drop the EZ-B offline.
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.