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
Elevate your robot's capabilities to the next level with Synthiam ARC Pro, unlocking a world of possibilities in robot programming.
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 rewrite the entire ARC script so it’s safer for ARC and won’t drop the EZ-B offline. Thank you. Jack
Absolutely,Jack - the safest fix is to stop sending on every loop iteration and make UART traffic event-driven + rate-limited. That will greatly reduce the chance of the EZ-B dropping offline.
Below is a rewritten single ARC JavaScript script that:
NaN/ invalid valuesSafer Combined ARC UART Script
Why this version is safer
1. It does not spam UART every loop
Your previous version could send bearing updates continuously and too fast. This version uses:
MIN_SEND_INTERVAL_MSsleep(25)2. It protects against bad values
If
$actualBearingis empty or invalid, it won’t send.3. It handles TX and RX in a controlled order
It always reads incoming data first, then sends outgoing packets.
4. It uses a consistent numeric format
All bearings are sent as x100 integers, which is much safer than floats.
One important ARC note
If
$actualBearingis being updated by another skill very rapidly, that skill may be the real source of the offline issue. In that case, I’d recommend:$actualBearingonly when the value actually changes7935Suggested ARC test procedure
If it still drops offline
Then the likely cause is one of these:
$actualBearingis being updated too aggressively elsewhereIf you want, I can do the next step and give you a matching Arduino sketch revision that adds a stricter packet parser and confirms the packet lengths before acting on them.
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. The last posted ARC script does not send any of the variables when they are changed to (1) in the global variable function. EZB4 consistently drops offline during the process when these variables are changed. I would like to step back and confirm with you the following objectives and make sure going forward that we keep the code as simple as possible in accomplishing these objectives. I wonder if incorporating a 'time since last send' unduly complicates matters.
Stated Objectives:
ARC UART SCRIPT
ARDUINO UART SKETCH
I am requesting: 1. Can you review/rewrite both the ARC UART and ARDUINO UART scripts to accomplish the printed objectives? 2. Can you provide a standalone ARC script that will populate the global variables separately from the ARC UART script?
I'm not sure providing global variable values in the ARC UART script might overly complicate matters. I intend to set the global variables separately and update them as necessary. Let's keep the UART scripts primarily doing UART functions. Remember for ARC UART, the WAYPOINT DATA is always sent and then only once. The RIGHT PIVOT + STEPS or the LEFT PIVOT + STEPS may be sent, if required, and then only once after the WAYPOINT DATA.
Thank you for your continued efforts to help me. Jack.
Yes - that’s a good step back, and I agree with your instinct:
If the EZ-B is dropping offline, the first thing to do is reduce complexity, then add features back one at a time.
Recommended simplified architecture
1)
UART_TX_RXscriptThis script should only:
2)
UART_INITscriptThis separate script should only:
That is the cleaner design.
Important issue in your current ARC script
The line:
is fine conceptually, but if
$actualBearingis being updated frequently elsewhere, it can still cause rapid UART traffic.So to keep things simple, I recommend:
That keeps the logic predictable and much safer.
1) Standalone ARC global initialization script
Use this in a separate Script skill named something like
UART_INIT.2) Simplified ARC UART script
This version removes continuous bearing auto-send and uses explicit trigger variables instead.
3) Why this should be more stable
This version avoids:
parseFloat()being used on every loop for a transmit decisionInstead, it only sends when you deliberately set a trigger flag to
1.4) How to use it
Send waypoint once
Send right pivot once
Send left pivot once
Send actual bearing once
5) Arduino sketch alignment
Your Arduino sketch should also remain simple:
Your packet lengths should be:
STOP= 0MISSED IR= 0ACTUAL BEARING= 2RIGHT PIVOT= 2LEFT PIVOT= 2WAYPOINT DATA= 21 bytesOBS SCAN COMPLETE= 06) One question before I refine the Arduino code again
For WAYPOINT DATA, do you want:
desiredBearingandactualBearingincluded in the packet as you currently have, orIf you want, I can next give you:
That would make it much easier to isolate the exact point where the EZ-B goes 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.
Hi @Athena, I am very enthusiastic about the direction you are taking now. It appears you have completely understood my objectives. For the WAYPOINT DATA, please send (desiredBearing and actualBearing) from the ARC UART script to the ARDUINO UART script.
ARDUINO Structure for variable (RUN): ARDUINO UART, while in the (RUN = 0 LOOP) receives the WAYPOINT DATA, and any RIGHT PIVOT or LEFT PIVOT commands. When ARDUINO UART sends ACK (of WAYPOINT DATA) it changes RUN = 1. When RUN = 1, then forward motion is started and all other UART TX and RX functions will become operational.
Can you provide a cleaned Arduino sketch v2 that is structured as described for variable (RUN)? I intend to use the just provided Simplified ARC UART script to communicate with the Arduino sketch v2.
Thank you. Jack
Absolutely,Jack - here is a cleaned Arduino sketch v2 structured around your
RUNvariable exactly as you described.It is designed to work with your Simplified ARC UART Script and assumes:
RUN = 0RUN = 0RUN = 1RUN = 1, forward navigation logic runs and UART status handling remains activeArduino UART Sketch v2
Notes on this version
What this sketch does
RUN = 0RUN = 0RUN = 1RUN = 1available for your forward motion logicWhat you still need to add
You’ll still replace these stubs with your real code:
stopMotors()pivotRight()pivotLeft()RUN = 1Important compatibility note
Your ARC script must send exactly 21 bytes of payload for
CMD_WAYPOINT_DATA.If you want, I can next do one of these:
If you’d like, I can also give you a minimal test pair next: one waypoint packet only, so you can verify the link before adding 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, Please rewrite the Simplified ARC UART script one final time so it matches this Arduino v2 sketch exactly. Thank you. Jack
Absolutely,Jack. Below is a final simplified ARC UART JavaScript script that matches the Arduino v2 sketch exactly:
0xA50x01Stop Motors0x02Missed IR0x03Actual Bearing0x04Right Pivot0x05Left Pivot0x06Waypoint Data0x07Obstacle Scan Complete0x10NAV ACK0x11Total Steps Taken0x12Desired Bearing Update0x13Obstacle Scan RequestThis version is intentionally kept simple and trigger-based, so ARC only sends packets when you set a variable to
1.ARC UART Script v2
How to use it
Set the trigger variables from another ARC script or control:
Send waypoint once
Send right pivot once
Send left pivot once
Send actual bearing once
Send stop
One important note
This script assumes:
$actualBearingis already being set elsewhere$desiredBearing,$reflectorBearing,$pathTotalDistance, etc. are already set elsewhereSerial3on the Arduino is wired correctly to the EZ-B UARTIf you want, I can now give you a clean matching global-variable initialization script for ARC, or I can help you make a first test packet so you can verify just
WAYPOINT DATAbefore enabling the other triggers.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.