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
With ARC Pro, your robot is not just a machine; it's your creative partner in the journey of technological exploration.
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. Please send a clean matching global-variable initialization script for ARC.
Thanks. Jack
Absolutely,Jack - here’s a clean matching ARC global-variable initialization script for your UART project.
It keeps all protocol values and trigger flags separate from the UART script, which is the simpler and safer approach.
ARC Global Variable Initialization Script
How this fits your setup
RUNNAV ACKRecommended use
or
Important note
If you want the project to stay easy to manage, I’d recommend:
That gives you a much cleaner architecture and makes debugging far easier.
If you want, I can next provide a final cleaned pair of:
formatted together so you can copy them side-by-side and test them as a complete set.
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 send a final cleaned pair of:
ARC UART script v2 Arduino UART sketch v2
Thanks. Jack
Absolutely,Jack - below is a final cleaned pair that matches your current design:
RUN = 0waypoint/setup phase, thenRUN = 1forward navigation phaseI’ve kept the code as simple and direct as possible.
1) ARC UART Script v2
2) Arduino UART Sketch v2
Important notes
1) This pair uses fixed packet lengths
That is what makes it reliable.
2) WAYPOINT DATA length is 21 bytes
This matches your Arduino v2 sketch exactly.
3)
desiredBearingandactualBearingThese are included in the waypoint packet, as requested.
4)
RUNbehaviorRUN = 0receives:WAYPOINT DATARIGHT PIVOTLEFT PIVOTRUN = 1is for forward motion and status behaviorOne question before you test
Do you want me to also provide a very small test script that only sends:
WAYPOINT DATANAV ACKconfirmationNAV ACKThat would be the safest first test before enabling pivots and bearing updates.
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. All three scripts (ARC Global Variable Initialization Script, ARC UART Script V-2, ARDUINO UART Sketch V-2) work beautifully as far as I have tested so far. I have tested all functions of ARC Global Variable Initialization Script. The triggers cause the appropriate UART TX & RX to take place in ARC and ARDUINO.
I have not tested the following and hope you can provide guidance on how to TX from ARDUINO UART the following objectives:
DESIRED BEARING UPDATE // OBSTACLE SCAN REQUEST Could you please explain how I can mod the ARDUINO UART to send this data?
Also, I would really like an explanation of the code (remember I am a beginner) for both the ARC UART and the ARDUINO UART. It seems a perfect time to learn, now that it is sending and receiving data according to my stated objectives. Can you describe the code for me one more time?
Thank you. Jack
Great to hear it’s working, Jack - that’s a strong sign your packet structure and UART wiring are solid.
How the Arduino should send Desired Bearing Update and Obstacle Scan Request
In your current protocol:
Desired Bearing Update = Arduino ARC
0xA50x12uint16bearing × 100Obstacle Scan Request = Arduino ARC
0xA50x13Because you’re using binary packets, the Arduino just needs to write the header, then command byte, then payload bytes.
1) Add these helper functions to the Arduino sketch
Here’s the clean way to send both messages:
2) When to call them
Send a desired bearing update
Call this whenever your Arduino computes a new desired bearing and you want ARC to display it:
If you only want to send it when the value changes:
Send an obstacle scan request
Call this when your Arduino reaches the scan trigger condition, for example a percentage of
pathTotalDistance:A typical pattern would be:
3) ARC side behavior for those packets
Your ARC UART script already knows how to handle these:
0x12prints the desired bearing0x13sets$obsScan = 1So once the Arduino sends them, ARC will react automatically.
Beginner explanation of the ARC UART script
Here’s the purpose of each section in plain language.
A. Constants at the top
These are just labels for the packet bytes.
Think of them as named numbers so the code is easier to read.
B. Helper functions
These convert numbers into bytes and send packets.
u16ToBytes(value)Takes a number from
0to65535and splits it into 2 bytes.u32ToBytes(value)Takes a bigger number and splits it into 4 bytes.
sendPacket(cmd, payload)Builds a packet like this:
Then writes it to UART.
C. TX functions
These are the actions ARC can send to the Arduino.
sendStopMotors()sendMissedIR()sendActualBearing()sendRightPivot()sendLeftPivot()sendWaypointData()Each one packages the correct data and sends it out.
D. RX function
This reads packets coming back from Arduino.
When ARC sees:
0x10NAV ACK0x11total steps0x12desired bearing0x13obstacle scan requestit prints or updates globals.
E. Main loop
This loop does two jobs:
$sendWaypointNowThat’s why it works well:
Beginner explanation of the Arduino UART sketch
A. Constants
These are the same packet IDs ARC uses.
Both sides must agree on the same values.
B. Parser state machine
This is the most important concept.
The Arduino is waiting for bytes in this order:
That’s why the code uses states like:
WAIT_HEADERWAIT_CMDWAIT_PAYLOADThis prevents confusion if a byte is lost or noise appears on the line.
C.
handleWaypointPacket()This function receives the full waypoint packet and stores the values into variables:
Then it sends:
NAV ACKand switches to:
RUN = 1D.
handlePacket()This is the command dispatcher.
It looks at the command byte and decides what to do:
E.
RUN = 0andRUN = 1This is your operating mode system.
RUN = 0Used for:
RUN = 1Used for:
That’s a very good structure.
What I recommend next
To make your Arduino even clearer, I can provide a commented Arduino v2 sketch where I add beginner-friendly comments line by line, and I can also do the same for the ARC script.
If you want, I can produce:
That would make it much easier to learn while keeping your project stable.
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, After studying the existing ARDUINO UART sketch, I found the following:
This precluded the need for any additional 'update' since you have already taken this into account in the sketch.
I simply called for 'sendHeaderAndCmd(RSP_DESIRED_BEARING)' and it produced the expected results.
Thank you again for the explanation of the UART code. Both work very well in communicating the desired UART Data. The more I study this code the clearer it will become.
Now, I would like to develop the ARDUINO Sketch and the ARC Script to their ultimate completion by adding PWM and computation of both Desired Bearing and Actual Bearing. I'm not sure this thread is the best place for continuing because my UART questions have been answered. Can you suggest a thread 'Name' that would be a good choice going forward?
Thanks. Jack
That’s a great next step,Jack - and yes,for the new topic it’s better to start a fresh thread so the discussion stays focused and easier to follow.
A good thread name would be something like:
Arduino Mega PWM + Desired/Actual Bearing Computation for ARC UART RoverOther good options:
ARC Arduino Rover: PWM and Bearing Control IntegrationArduino Mega Stepper PWM and Bearing Logic in ARC ProjectUART Rover Expansion: PWM Correction and Bearing MathARC Rover Navigation: Arduino PWM and Bearing Update LogicIf you want the most descriptive and beginner-friendly title,I’d recommend:
ARC Rover: Arduino PWM Control and Bearing ComputationThat tells people immediately:
When you start the new thread, include:
That will make it much easier to help you build the next phase cleanly.
If you’d like,I can also help you draft the first post for that new thread so you can paste it in directly.
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.