Variable State Saver icon Variable State Saver Persist and auto-load specified global variables between ARC sessions for consistent, personalized robot behavior and quick state restoration. Try it →
Asked — Edited
Resolved Resolved by Athena!

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)
EZ-B V4 by EZ-Robot
EZ-B v4/2 robot controller: dual Cortex ARM, Wi-Fi, audio/video, 24 servo/digital ports, I2C/UART, camera and Synthiam ARC control for custom robots
Wi-Fi / USB
Servos 24
Camera
Audio
UART 3
I2C
ADC 8
Digital 24

Related Robot Skills (view all robot skills)
Add custom EZ-Script functions in C# or JS; intercept unknown calls, parse parameters and return values via AdditionalFunctionEvent.
Script by Synthiam
Multi-language ARC Script: build, run and debug Blockly, JavaScript, EZ-Script or Python with Intellisense, run/save/load and Roboscratch support

ARC Pro

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.

#25  

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

Author Avatar
PRO
Canada
Twitter YouTube GitHub
#26  

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:

  • initializes globals
  • sends Right Pivot / Left Pivot / Waypoint Data
  • reads Arduino responses
  • sends Actual Bearing only when needed
  • rate-limits UART transmissions
  • avoids NaN / invalid values
  • keeps the loop light so ARC stays stable

Safer Combined ARC UART Script

var UART_INDEX = 0;
var UART_HEADER = 0xA5;

// ARC -> Arduino
var CMD_STOP_MOTORS = 0x01;
var CMD_MISSED_IR = 0x02;
var CMD_ACTUAL_BEARING = 0x03;
var CMD_RIGHT_PIVOT = 0x04;
var CMD_LEFT_PIVOT = 0x05;
var CMD_WAYPOINT_DATA = 0x06;
var CMD_OBS_SCAN_COMPLETE = 0x07;

// Arduino -> ARC
var RSP_NAV_ACK = 0x10;
var RSP_TOTAL_STEPS = 0x11;
var RSP_DESIRED_BEARING = 0x12;
var RSP_OBS_SCAN_REQUEST = 0x13;

// ----------
// State
// ----------
var lastActualBearingSent100 = -1;
var lastWaypointSentTime = 0;
var lastActualBearingSentTime = 0;
var lastRightPivotSentTime = 0;
var lastLeftPivotSentTime = 0;

// Minimum spacing between sends so we don't flood UART
var MIN_SEND_INTERVAL_MS = 100;

// ----------
// Helpers
// ----------
function safeParseInt(value, defaultValue) {
    var n = parseInt(value);
    if (isNaN(n)) return defaultValue;
    return n;
}

function safeParseFloat(value, defaultValue) {
    var n = parseFloat(value);
    if (isNaN(n)) return defaultValue;
    return n;
}

function u16ToBytes(value) {
    value = value & 0xFFFF;
    return [
        value & 0xFF,
        (value >> 8) & 0xFF
    ];
}

function u32ToBytes(value) {
    value = value >>> 0;
    return [
        value & 0xFF,
        (value >> 8) & 0xFF,
        (value >> 16) & 0xFF,
        (value >> 24) & 0xFF
    ];
}

function canSend(lastTime) {
    var now = new Date().getTime();
    return (now - lastTime) >= MIN_SEND_INTERVAL_MS;
}

function sendPacket(cmd, payload) {
    var packet = [UART_HEADER, cmd].concat(payload);
    UART.hardwareUartWrite(UART_INDEX, packet);
}

function sendStopMotors() {
    sendPacket(CMD_STOP_MOTORS, []);
}

function sendMissedIR() {
    sendPacket(CMD_MISSED_IR, []);
}

function sendActualBearing100(bearing100) {
    sendPacket(CMD_ACTUAL_BEARING, u16ToBytes(bearing100));
}

function sendRightPivot(steps) {
    sendPacket(CMD_RIGHT_PIVOT, u16ToBytes(steps));
}

function sendLeftPivot(steps) {
    sendPacket(CMD_LEFT_PIVOT, u16ToBytes(steps));
}

function sendWaypointDataFromGlobals() {
    var pathTotalDistance = safeParseInt(getVar("$pathTotalDistance", 0), 0);
    var combinedStepsDesired = safeParseInt(getVar("$combinedStepsDesired", 0), 0);
    var oppositeDistance = safeParseInt(getVar("$oppositeDistance", 0), 0);
    var bearingAlignment = safeParseInt(getVar("$bearingAlignment", 0), 0) & 0xFF;
    var reflectorBearing = safeParseFloat(getVar("$reflectorBearing", 0), 0);
    var sensorPrime = safeParseInt(getVar("$sensorPrime", 0), 0) & 0xFF;
    var desiredBearing = safeParseFloat(getVar("$desiredBearing", 0), 0);
    var actualBearing = safeParseFloat(getVar("$actualBearing", 0), 0);
    var trackDir = safeParseInt(getVar("$trackDir", 0), 0) & 0xFF;

    var payload = [];
    payload = payload.concat(u32ToBytes(pathTotalDistance));
    payload = payload.concat(u32ToBytes(combinedStepsDesired));
    payload = payload.concat(u32ToBytes(oppositeDistance));
    payload.push(bearingAlignment);
    payload = payload.concat(u16ToBytes(Math.round(reflectorBearing * 100)));
    payload.push(sensorPrime);
    payload = payload.concat(u16ToBytes(Math.round(desiredBearing * 100)));
    payload = payload.concat(u16ToBytes(Math.round(actualBearing * 100)));
    payload.push(trackDir);

    sendPacket(CMD_WAYPOINT_DATA, payload);

    print("Sent Waypoint Data");
    print("pathTotalDistance=" + pathTotalDistance);
    print("combinedStepsDesired=" + combinedStepsDesired);
    print("oppositeDistance=" + oppositeDistance);
    print("bearingAlignment=" + bearingAlignment);
    print("reflectorBearing=" + reflectorBearing);
    print("sensorPrime=" + sensorPrime);
    print("desiredBearing=" + desiredBearing);
    print("actualBearing=" + actualBearing);
    print("trackDir=" + trackDir);
}

// ----------
// Global initialization
// ----------
function initGlobals() {
    setVar("$rightPivotSteps", 0);
    setVar("$leftPivotSteps", 0);

    setVar("$pathTotalDistance", 120000);
    setVar("$combinedStepsDesired", 84000);
    setVar("$oppositeDistance", 24000);
    setVar("$bearingAlignment", 0); // 0=DA,1=OA
    setVar("$reflectorBearing", 97.00);
    setVar("$sensorPrime", 4);
    setVar("$desiredBearing", 79.35);
    setVar("$actualBearing", 79.00);
    setVar("$trackDir", 0); // 0=FD,1=RD

    setVar("$sendWaypointNow", 0);
    setVar("$rightPivotNow", 0);
    setVar("$leftPivotNow", 0);
    setVar("$obsDet", 0);
    setVar("$missedIR", 0);
    setVar("$obsScanEnd", 0);

    print("UART globals initialized");
}

// ----------
// UART RX parsing
// ----------
function readIncomingPacket() {
    while (UART.hardwareUartAvailable(UART_INDEX) >= 2) {
        var header = UART.hardwareUartRead(UART_INDEX, 1)[0];

        if (header != UART_HEADER) {
            continue;
        }

        var cmd = UART.hardwareUartRead(UART_INDEX, 1)[0];

        if (cmd == RSP_NAV_ACK) {
            print("ARC received NAV ACK");
            continue;
        }

        if (cmd == RSP_TOTAL_STEPS) {
            while (UART.hardwareUartAvailable(UART_INDEX) < 4) {
                sleep(1);
            }
            var data = UART.hardwareUartRead(UART_INDEX, 4);
            var steps = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
            print("Total steps received: " + steps);
            continue;
        }

        if (cmd == RSP_DESIRED_BEARING) {
            while (UART.hardwareUartAvailable(UART_INDEX) < 2) {
                sleep(1);
            }
            var data2 = UART.hardwareUartRead(UART_INDEX, 2);
            var bearing100 = data2[0] | (data2[1] << 8);
            print("Desired bearing received: " + (bearing100 / 100.0));
            continue;
        }

        if (cmd == RSP_OBS_SCAN_REQUEST) {
            print("Arduino requested obstacle scan");
            setVar("$obsScanEnd", 1);
            continue;
        }

        print("Unknown Arduino cmd: " + cmd);
    }
}

// ----------
// Main
// ----------
initGlobals();

while (true) {
    // 1) Handle incoming UART first
    readIncomingPacket();

    // 2) Event-driven outgoing commands
    if (getVar("$obsDet", 0) == 1) {
        sendStopMotors();
        setVar("$obsDet", 0);
        print("Sent STOP MOTORS");
    }

    if (getVar("$missedIR", 0) == 1) {
        sendMissedIR();
        setVar("$missedIR", 0);
        print("Sent MISSED IR");
    }

    if (getVar("$rightPivotNow", 0) == 1 && canSend(lastRightPivotSentTime)) {
        var rightSteps = safeParseInt(getVar("$rightPivotSteps", 0), 0);
        sendRightPivot(rightSteps);
        setVar("$rightPivotNow", 0);
        lastRightPivotSentTime = new Date().getTime();
        print("Sent RIGHT PIVOT: " + rightSteps);
    }

    if (getVar("$leftPivotNow", 0) == 1 && canSend(lastLeftPivotSentTime)) {
        var leftSteps = safeParseInt(getVar("$leftPivotSteps", 0), 0);
        sendLeftPivot(leftSteps);
        setVar("$leftPivotNow", 0);
        lastLeftPivotSentTime = new Date().getTime();
        print("Sent LEFT PIVOT: " + leftSteps);
    }

    if (getVar("$sendWaypointNow", 0) == 1 && canSend(lastWaypointSentTime)) {
        sendWaypointDataFromGlobals();
        setVar("$sendWaypointNow", 0);
        lastWaypointSentTime = new Date().getTime();
    }

    // 3) Send actual bearing only when it changes meaningfully and is valid
    var currentActualBearing = safeParseFloat(getVar("$actualBearing", -1), -1);
    if (currentActualBearing >= 0) {
        var currentActualBearing100 = Math.round(currentActualBearing * 100);

        // Only send if changed and rate-limited
        if (currentActualBearing100 != lastActualBearingSent100 && canSend(lastActualBearingSentTime)) {
            sendActualBearing100(currentActualBearing100);
            lastActualBearingSent100 = currentActualBearing100;
            lastActualBearingSentTime = new Date().getTime();
            print("Sent ACTUAL BEARING x100: " + currentActualBearing100);
        }
    }

    sleep(25);
}

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_MS
  • value-change checking
  • a lighter sleep(25)

2. It protects against bad values

If $actualBearing is 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 $actualBearing is being updated by another skill very rapidly, that skill may be the real source of the offline issue. In that case, I’d recommend:

  • update $actualBearing only when the value actually changes
  • or store it as a scaled integer like 7935
  • or send it only on a timer, not every sensor poll

Suggested ARC test procedure

  1. Run the script with all send flags at 0
  2. Confirm ARC stays online
  3. Set only:
    setVar("$sendWaypointNow", 1);
    
  4. Confirm the packet sends
  5. Then test:
    setVar("$rightPivotSteps", 2500);
    setVar("$rightPivotNow", 1);
    
  6. Then test actual bearing updates slowly

If it still drops offline

Then the likely cause is one of these:

  • UART TX/RX wiring issue
  • Arduino sending malformed packets
  • another ARC script also touching UART
  • $actualBearing is being updated too aggressively elsewhere
  • a packet length mismatch between ARC and Arduino

If 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.
#27  

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:

Whole-System UART Flowchart
1) High-level architecture
+-------------------+        UART        +----------------------+
| ARC JavaScript    | <----------------> | Arduino Mega         |
| (runs on EZ-B v4) |                    | (stepper + nav logic) |
+-------------------+                    +----------------------+
         |                                           |
         |                                           |
         v                                           v
  Reads sensors / variables                  Controls motors
  Decides what to send                       Computes bearings
  Builds packets                             Sends status packets
  Parses Arduino replies                     Parses ARC commands
2) Overall control loop
                         +----------------------+
                         |        START         |
                         +----------------------+
                                   |
                                   v
                    +-------------------------------+
                    | ARC and Arduino are powered on |
                    +-------------------------------+
                                   |
                                   v
            +------------------------------------------------+
            | ARC JavaScript loop begins running continuously |
            +------------------------------------------------+
                                   |
                                   v
            +----------------------------------------------+
            | Arduino loop begins running continuously      |
            | RX parser always listens for UART bytes      |
            +----------------------------------------------+
                                   |
                                   v
                 +-----------------------------------+
                 | Which side has something to send? |
                 +-----------------------------------+
                          /                     \
                         /                       \
                        v                         v
        +---------------------------+   +---------------------------+
        | ARC sends command to      |   | Arduino sends response to |
        | Arduino when an event     |   | ARC when a status/event   |
        | happens                    |   | changes                  |
        +---------------------------+   +---------------------------+
                        \                         /
                         \                       /
                          v                     v
                 +-----------------------------------+
                 | Receiver parses packet safely     |
                 +-----------------------------------+
                                   |
                                   v
                     +------------------------------+
                     | Receiver performs the action |
                     +------------------------------+
                                   |
                                   v
                 +-----------------------------------+
                 | Optional response back the other   |
                 | way for confirmation/status       |
                 +-----------------------------------+
                                   |
                                   v
                         +----------------------+
                         |      LOOP AGAIN      |
                         +----------------------+
3) ARC Arduino flowchart
This is the direction for:

STOP
MISSED IR
ACTUAL BEARING
RIGHT PIVOT
LEFT PIVOT
WAYPOINT DATA
OBSTACLE SCAN COMPLETE
[ARC detects event]
        |
        v
[Choose command]
        |
        v
[Convert values to bytes]
        |
        v
[Build packet:
Header + Command + Payload]
        |
        v
[Send packet over UART]
        |
        v
[Arduino RX reads bytes]
        |
        v
[Arduino parser waits for:
Header -> Command -> Payload]
        |
        v
[Arduino decodes packet]
        |
        v
[Arduino updates motion/nav state]
        |
        v
[Arduino may send response back]
4) Arduino ARC flowchart
This is the direction for:

NAV ACK
TOTAL STEPS TAKEN
DESIRED BEARING UPDATE
OBSTACLE SCAN REQUEST
[Arduino detects internal event]
        |
        v
[Choose response command]
        |
        v
[Convert data to bytes if needed]
        |
        v
[Build packet:
Header + Command + Payload]
        |
        v
[Send packet over Serial3]
        |
        v
[ARC UART loop sees bytes available]
        |
        v
[ARC reads header]
        |
        v
[ARC reads command]
        |
        v
[ARC reads exact payload length]
        |
        v
[ARC decodes packet]
        |
        v
[ARC prints, logs, or changes state]
5) Example command path: WAYPOINT DATA
This is the best "big" example because it shows the full system working.

ARC decides a new waypoint is ready
        |
        v
ARC builds WPT packet
        |
        v
ARC sends packet to Arduino
        |
        v
Arduino RX parser receives bytes
        |
        v
Arduino recognizes:
Header + WPT command + 23-byte payload
        |
        v
Arduino stores:
distance, steps, bearings, direction
        |
        v
Arduino confirms with NAV ACK
        |
        v
ARC receives NAV ACK
        |
        v
ARC starts the navigation script
        |
        v
Arduino begins RUN = 1 movement logic
6) Example command path: TOTAL STEPS TAKEN
Arduino reaches a condition:
- waypoint complete
- pivot complete
- stop command received
        |
        v
Arduino computes total steps
        |
        v
Arduino builds:
Header + TOTAL STEPS command + 4-byte value
        |
        v
Arduino sends packet to ARC
        |
        v
ARC receives bytes
        |
        v
ARC parser reads packet safely
        |
        v
ARC reconstructs the 32-bit step count
        |
        v
ARC uses the value:
- display
- stop
- transition to next state
7) Example command path: ACTUAL BEARING
ARC gets a new actual bearing value
        |
        v
ARC compares it to previous value
        |
        v
If changed, ARC scales it x100
        |
        v
ARC builds packet:
Header + ACTUAL BEARING + 2-byte value
        |
        v
ARC sends to Arduino
        |
        v
Arduino parses packet
        |
        v
Arduino updates PWM correction logic
        |
        v
Motor correction continues
8) What RUN = 0 and RUN = 1 mean in the system
RUN = 0
This is the setup / alignment / pre-move phase.

ARC may send:
- pivot commands
- waypoint data
- stop
- bearing updates

Arduino may:
- wait for data
- pivot wheels
- confirm with NAV ACK
RUN = 1
This is the forward navigation phase.

Arduino may:
- count steps
- calculate desired bearing
- adjust PWM
- send total steps or desired bearing updates

ARC may:
- monitor status
- send missed IR
- send actual bearing updates
- stop motion if needed
9) The simplest "big picture" summary
ARC decides -> builds packet -> sends UART
Arduino receives -> parses packet -> acts
Arduino decides -> builds packet -> sends UART
ARC receives -> parses packet -> reacts
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:

reliable UART communication
ARC as the higher-level controller
Arduino handling stepper/motion timing
clear command separation
one protocol for both directions
room to monitor navigation and course correction

ARC UART SCRIPT

ARC UART Script

var UART_INDEX = 0;
var UART_HEADER = 0xA5;

// ARC -> Arduino
var CMD_STOP_MOTORS       = 0x01;
var CMD_MISSED_IR         = 0x02;
var CMD_ACTUAL_BEARING    = 0x03;
var CMD_RIGHT_PIVOT       = 0x04;
var CMD_LEFT_PIVOT        = 0x05;
var CMD_WAYPOINT_DATA     = 0x06;
var CMD_OBS_SCAN_COMPLETE = 0x07;

// Arduino -> ARC
var RSP_NAV_ACK           = 0x10;
var RSP_TOTAL_STEPS       = 0x11;
var RSP_DESIRED_BEARING   = 0x12;
var RSP_OBS_SCAN_REQUEST  = 0x13;

// Example state
var lastActualBearing = -1;
var lastDesiredBearing = -1;

function u16ToBytes(value) {
    return [
        value & 0xFF,
        (value >> 8) & 0xFF
    ];
}

function u32ToBytes(value) {
    return [
        value & 0xFF,
        (value >> 8) & 0xFF,
        (value >> 16) & 0xFF,
        (value >> 24) & 0xFF
    ];
}

function sendPacket(cmd, payload) {
    var packet = [UART_HEADER, cmd].concat(payload);
    UART.hardwareUartWrite(UART_INDEX, packet);
}

function sendStopMotors() {
    sendPacket(CMD_STOP_MOTORS, []);
}

function sendMissedIR() {
    sendPacket(CMD_MISSED_IR, []);
}

function sendActualBearing(bearingDegrees) {
    var bearing100 = Math.round(bearingDegrees * 100);
    sendPacket(CMD_ACTUAL_BEARING, u16ToBytes(bearing100));
}

function sendRightPivot(steps) {
    sendPacket(CMD_RIGHT_PIVOT, u16ToBytes(steps));
}

function sendLeftPivot(steps) {
    sendPacket(CMD_LEFT_PIVOT, u16ToBytes(steps));
}

function sendWaypointData(pathTotalDistance, combinedStepsDesired, oppositeDistance, bearingAlignment, reflectorBearing, sensorPrime, desiredBearing, actualBearing, trackDir) {
    var payload = [];

    payload = payload.concat(u32ToBytes(pathTotalDistance));
    payload = payload.concat(u32ToBytes(combinedStepsDesired));
    payload = payload.concat(u32ToBytes(oppositeDistance));
    payload.push(bearingAlignment & 0xFF);
    payload = payload.concat(u16ToBytes(Math.round(reflectorBearing * 100)));
    payload.push(sensorPrime & 0xFF);
    payload = payload.concat(u16ToBytes(Math.round(desiredBearing * 100)));
    payload = payload.concat(u16ToBytes(Math.round(actualBearing * 100)));
    payload.push(trackDir & 0xFF);

    sendPacket(CMD_WAYPOINT_DATA, payload);
}

function readIncomingPacket() {
    var available = UART.hardwareUartAvailable(UART_INDEX);
    if (available < 2) return;

    var header = UART.hardwareUartRead(UART_INDEX, 1)[0];
    if (header != UART_HEADER) return;

    var cmd = UART.hardwareUartRead(UART_INDEX, 1)[0];

    if (cmd == RSP_NAV_ACK) {
        print("ARC received NAV ACK");
        return;
    }

    if (cmd == RSP_TOTAL_STEPS) {
        if (UART.hardwareUartAvailable(UART_INDEX) < 4) return;

        var data = UART.hardwareUartRead(UART_INDEX, 4);
        var steps = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
        print("Total steps received: " + steps);
        return;
    }

    if (cmd == RSP_DESIRED_BEARING) {
        if (UART.hardwareUartAvailable(UART_INDEX) < 2) return;

        var data2 = UART.hardwareUartRead(UART_INDEX, 2);
        var bearing100 = data2[0] | (data2[1] << 8);
        var bearing = bearing100 / 100.0;
        print("Desired bearing received: " + bearing);
        return;
    }

    if (cmd == RSP_OBS_SCAN_REQUEST) {
        print("Arduino requested obstacle scan");
        return;
    }
}

while (true) {
    // -----------------------
    // Layer 1: Event logic
    // -----------------------
    if (getVar("$obsDet") == 1) {
        sendStopMotors();
        setVar("$obsDet", 0);
    }

    if (getVar("$missedIR") == 1) {
        sendMissedIR();
        setVar("$missedIR", 0);
    }

    var currentActualBearing = getVar("$actualBearing", -1);
    if (currentActualBearing != lastActualBearing && currentActualBearing != -1) {
        sendActualBearing(currentActualBearing);
        lastActualBearing = currentActualBearing;
    }

    var currentDesiredBearing = getVar("$desiredBearing", -1);
    if (currentDesiredBearing != lastDesiredBearing && currentDesiredBearing != -1) {
        // If you want ARC to report desired bearing immediately:
        // send it as actual-bearing-style packet if your protocol uses that command
        lastDesiredBearing = currentDesiredBearing;
    }

    // -----------------------
    // Layer 2: Packet building
    // -----------------------
    // This already happens inside sendStopMotors(), sendActualBearing(), sendWaypointData(), etc.

    // Example: send waypoint once when some project variable says to start
    if (getVar("$sendWaypointNow") == 1) {
        sendWaypointData(120000, 84000, 24000, 0, 97, 4, 79.35, 79.00, 0);
        setVar("$sendWaypointNow", 0);
    }

    // -----------------------
    // Layer 3: Packet parsing
    // -----------------------
    readIncomingPacket();

    sleep(20);
}

ARDUINO UART SKETCH

ARDUINO UART Sketch

#include 

// --------------------
// UART Protocol
// --------------------
static const uint8_t UART_HEADER = 0xA5;

// ARC -> Arduino
static const uint8_t CMD_STOP_MOTORS         = 0x01;
static const uint8_t CMD_MISSED_IR           = 0x02;
static const uint8_t CMD_ACTUAL_BEARING      = 0x03;
static const uint8_t CMD_RIGHT_PIVOT         = 0x04;
static const uint8_t CMD_LEFT_PIVOT          = 0x05;
static const uint8_t CMD_WAYPOINT_DATA       = 0x06;
static const uint8_t CMD_OBS_SCAN_COMPLETE   = 0x07;

// Arduino -> ARC
static const uint8_t RSP_NAV_ACK             = 0x10;
static const uint8_t RSP_TOTAL_STEPS         = 0x11;
static const uint8_t RSP_DESIRED_BEARING     = 0x12;
static const uint8_t RSP_OBS_SCAN_REQUEST    = 0x13;

// --------------------
// Run state
// --------------------
int RUN = 0;

// --------------------
// Parser state machine
// --------------------
enum ParserState {
  WAIT_HEADER,
  WAIT_CMD,
  WAIT_PAYLOAD
};

ParserState state = WAIT_HEADER;
uint8_t currentCmd = 0;
uint8_t payload[32];
uint8_t payloadIndex = 0;
uint8_t expectedLength = 0;

// --------------------
// Example navigation variables
// --------------------
uint32_t totalStepsTaken = 0;
uint16_t desiredBearing100 = 0;
uint16_t actualBearing100 = 0;

// Waypoint packet fields
uint32_t pathTotalDistance = 0;
uint32_t combinedStepsDesired = 0;
uint32_t oppositeDistance = 0;
uint8_t bearingAlignment = 0;
uint16_t reflectorBearing100 = 0;
uint8_t sensorPrime = 0;
uint16_t waypointDesiredBearing100 = 0;
uint16_t waypointActualBearing100 = 0;
uint8_t trackDir = 0;

// --------------------
// Helpers
// --------------------
uint16_t readUInt16LE(const uint8_t* data) {
  return (uint16_t)data[0] | ((uint16_t)data[1] << 8);
}

uint32_t readUInt32LE(const uint8_t* data) {
  return (uint32_t)data[0] |
         ((uint32_t)data[1] << 8) |
         ((uint32_t)data[2] << 16) |
         ((uint32_t)data[3] << 24);
}

void sendHeaderAndCmd(uint8_t cmd) {
  Serial3.write(UART_HEADER);
  Serial3.write(cmd);
}

void sendUInt16Packet(uint8_t cmd, uint16_t value) {
  Serial3.write(UART_HEADER);
  Serial3.write(cmd);
  Serial3.write((uint8_t)(value & 0xFF));
  Serial3.write((uint8_t)((value >> 8) & 0xFF));
}

void sendUInt32Packet(uint8_t cmd, uint32_t value) {
  Serial3.write(UART_HEADER);
  Serial3.write(cmd);
  Serial3.write((uint8_t)(value & 0xFF));
  Serial3.write((uint8_t)((value >> 8) & 0xFF));
  Serial3.write((uint8_t)((value >> 16) & 0xFF));
  Serial3.write((uint8_t)((value >> 24) & 0xFF));
}

// --------------------
// TX functions
// --------------------
void sendNavAck() {
  sendHeaderAndCmd(RSP_NAV_ACK);
}

void sendTotalSteps(uint32_t steps) {
  sendUInt32Packet(RSP_TOTAL_STEPS, steps);
}

void sendDesiredBearing(uint16_t bearing100) {
  sendUInt16Packet(RSP_DESIRED_BEARING, bearing100);
}

void sendObstacleScanRequest() {
  sendHeaderAndCmd(RSP_OBS_SCAN_REQUEST);
}

// --------------------
// RX packet handlers
// --------------------
void handleWaypointPacket(const uint8_t* data, uint8_t len) {
  if (len < 23) return;

  pathTotalDistance        = readUInt32LE(&data[0]);
  combinedStepsDesired     = readUInt32LE(&data[4]);
  oppositeDistance         = readUInt32LE(&data[8]);
  bearingAlignment         = data[12];
  reflectorBearing100      = readUInt16LE(&data[13]);
  sensorPrime              = data[15];
  waypointDesiredBearing100= readUInt16LE(&data[16]);
  waypointActualBearing100 = readUInt16LE(&data[18]);
  trackDir                 = data[20];

  RUN = 1;

  Serial.print("Waypoint received. PathTotalDistance=");
  Serial.println(pathTotalDistance);
  Serial.print("CombinedStepsDesired=");
  Serial.println(combinedStepsDesired);
  Serial.print("OppositeDistance=");
  Serial.println(oppositeDistance);
  Serial.print("BearingAlignment=");
  Serial.println(bearingAlignment);
  Serial.print("ReflectorBearing100=");
  Serial.println(reflectorBearing100);
  Serial.print("SensorPrime=");
  Serial.println(sensorPrime);
  Serial.print("DesiredBearing100=");
  Serial.println(waypointDesiredBearing100);
  Serial.print("ActualBearing100=");
  Serial.println(waypointActualBearing100);
  Serial.print("TrackDir=");
  Serial.println(trackDir);

  sendNavAck();
}

void handlePacket(uint8_t cmd, const uint8_t* data, uint8_t len) {
  switch (cmd) {
    case CMD_STOP_MOTORS:
      Serial.println("CMD_STOP_MOTORS received");
      RUN = 0;
      // stopMotors();
      sendTotalSteps(totalStepsTaken);
      break;

    case CMD_MISSED_IR:
      Serial.println("CMD_MISSED_IR received");
      // missedIR = true;
      break;

    case CMD_ACTUAL_BEARING:
      if (len >= 2) {
        actualBearing100 = readUInt16LE(data);
        Serial.print("Actual bearing received x100 = ");
        Serial.println(actualBearing100);
      }
      break;

    case CMD_RIGHT_PIVOT:
      if (len >= 2) {
        uint16_t steps = readUInt16LE(data);
        Serial.print("Right pivot steps = ");
        Serial.println(steps);
        // pivotRight(steps);
      }
      break;

    case CMD_LEFT_PIVOT:
      if (len >= 2) {
        uint16_t steps = readUInt16LE(data);
        Serial.print("Left pivot steps = ");
        Serial.println(steps);
        // pivotLeft(steps);
      }
      break;

    case CMD_WAYPOINT_DATA:
      handleWaypointPacket(data, len);
      break;

    case CMD_OBS_SCAN_COMPLETE:
      Serial.println("Obstacle scan complete received");
      // resume normal tracking
      break;

    default:
      Serial.print("Unknown command: ");
      Serial.println(cmd, HEX);
      break;
  }
}

uint8_t getExpectedLength(uint8_t cmd) {
  switch (cmd) {
    case CMD_STOP_MOTORS:       return 0;
    case CMD_MISSED_IR:         return 0;
    case CMD_ACTUAL_BEARING:    return 2;
    case CMD_RIGHT_PIVOT:       return 2;
    case CMD_LEFT_PIVOT:        return 2;
    case CMD_WAYPOINT_DATA:     return 23;
    case CMD_OBS_SCAN_COMPLETE: return 0;
    default:                    return 0xFF;
  }
}

void processIncomingByte(uint8_t b) {
  switch (state) {
    case WAIT_HEADER:
      if (b == UART_HEADER) {
        state = WAIT_CMD;
      }
      break;

    case WAIT_CMD:
      currentCmd = b;
      expectedLength = getExpectedLength(currentCmd);

      if (expectedLength == 0xFF) {
        state = WAIT_HEADER;
      } else if (expectedLength == 0) {
        handlePacket(currentCmd, nullptr, 0);
        state = WAIT_HEADER;
      } else {
        payloadIndex = 0;
        state = WAIT_PAYLOAD;
      }
      break;

    case WAIT_PAYLOAD:
      payload[payloadIndex++] = b;

      if (payloadIndex >= expectedLength) {
        handlePacket(currentCmd, payload, expectedLength);
        state = WAIT_HEADER;
      }
      break;
  }
}

void readIncomingUart() {
  while (Serial3.available() > 0) {
    uint8_t b = (uint8_t)Serial3.read();
    processIncomingByte(b);
  }
}

// --------------------
// Setup / Loop
// --------------------
void setup() {
  Serial.begin(115200);
  Serial3.begin(115200);
  Serial.println("Combined Arduino UART sketch ready");
}

void loop() {
  // Always read incoming data
  readIncomingUart();

  if (RUN == 0) {
    // Idle / alignment / pre-navigation logic
    // Example: if a waypoint is received, RUN becomes 1 in handleWaypointPacket()
  } 
  else if (RUN == 1) {
    // Forward navigation logic
    // Update total steps
    totalStepsTaken = 123456; // replace with real step calculation

    // Example periodic reporting
    // sendTotalSteps(totalStepsTaken);

    // Example: if desired bearing changes
    // sendDesiredBearing(desiredBearing100);
  }

  // Add a small delay if needed
  // delay(1);
}

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.

Author Avatar
PRO
Canada
Twitter YouTube GitHub
#28  

Yes - that’s a good step back, and I agree with your instinct:

  • Keep the UART scripts focused on UART
  • Keep the global values in a separate setup script
  • Avoid extra timing logic unless it’s truly needed

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_RX script

This script should only:

  • send Waypoint Data once
  • optionally send Right Pivot + Steps once
  • optionally send Left Pivot + Steps once
  • send Stop / Missed IR / Actual Bearing only when triggered
  • read Arduino responses in the same loop

2) UART_INIT script

This separate script should only:

  • define and/or set the global variables used by the UART script

That is the cleaner design.


Important issue in your current ARC script

The line:

var currentActualBearing = getVar("$actualBearing",-1);
if(currentActualBearing != lastActualBearing && currentActualBearing != -1){
    sendActualBearing(currentActualBearing);
    lastActualBearing = currentActualBearing;
}

is fine conceptually, but if $actualBearing is being updated frequently elsewhere, it can still cause rapid UART traffic.

So to keep things simple, I recommend:

  • do not auto-send actual bearing continuously
  • send it only when an explicit trigger flag is set

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.

// UART_INIT - run once or whenever you want to reset values

setVar("$rightPivotSteps", 2500);
setVar("$leftPivotSteps", 2500);

setVar("$pathTotalDistance", 120000);
setVar("$combinedStepsDesired", 84000);
setVar("$oppositeDistance", 24000);

// 0 = DA, 1 = OA
setVar("$bearingAlignment", 0);

setVar("$reflectorBearing", 97.00);
setVar("$sensorPrime", 4);
setVar("$desiredBearing", 79.35);
setVar("$actualBearing", 79.00);

// 0 = FD, 1 = RD
setVar("$trackDir", 0);

// Trigger flags
setVar("$sendWaypointNow", 0);
setVar("$sendRightPivotNow", 0);
setVar("$sendLeftPivotNow", 0);
setVar("$sendActualBearingNow", 0);
setVar("$sendStopNow", 0);
setVar("$sendMissedIRNow", 0);
setVar("$sendObsScanCompleteNow", 0);

print("UART globals initialized");

2) Simplified ARC UART script

This version removes continuous bearing auto-send and uses explicit trigger variables instead.

var UART_INDEX = 0;
var UART_HEADER = 0xA5;

// ARC -> Arduino
var CMD_STOP_MOTORS = 0x01;
var CMD_MISSED_IR = 0x02;
var CMD_ACTUAL_BEARING = 0x03;
var CMD_RIGHT_PIVOT = 0x04;
var CMD_LEFT_PIVOT = 0x05;
var CMD_WAYPOINT_DATA = 0x06;
var CMD_OBS_SCAN_COMPLETE = 0x07;

// Arduino -> ARC
var RSP_NAV_ACK = 0x10;
var RSP_TOTAL_STEPS = 0x11;
var RSP_DESIRED_BEARING = 0x12;
var RSP_OBS_SCAN_REQUEST = 0x13;

function u16ToBytes(value) {
    value = value & 0xFFFF;
    return [
        value & 0xFF,
        (value >> 8) & 0xFF
    ];
}

function u32ToBytes(value) {
    value = value >>> 0;
    return [
        value & 0xFF,
        (value >> 8) & 0xFF,
        (value >> 16) & 0xFF,
        (value >> 24) & 0xFF
    ];
}

function sendPacket(cmd, payload) {
    var packet = [UART_HEADER, cmd].concat(payload);
    UART.hardwareUartWrite(UART_INDEX, packet);
}

function sendStopMotors() {
    sendPacket(CMD_STOP_MOTORS, []);
}

function sendMissedIR() {
    sendPacket(CMD_MISSED_IR, []);
}

function sendActualBearing() {
    var bearing = parseFloat(getVar("$actualBearing", -1));
    if (isNaN(bearing) || bearing < 0) {
        print("Skipping actual bearing: invalid value");
        return;
    }

    var bearing100 = Math.round(bearing * 100);
    sendPacket(CMD_ACTUAL_BEARING, u16ToBytes(bearing100));
    print("Sent ACTUAL BEARING: " + bearing);
}

function sendRightPivot() {
    var steps = parseInt(getVar("$rightPivotSteps", 0));
    if (isNaN(steps) || steps < 0) {
        print("Skipping right pivot: invalid steps");
        return;
    }

    sendPacket(CMD_RIGHT_PIVOT, u16ToBytes(steps));
    print("Sent RIGHT PIVOT: " + steps);
}

function sendLeftPivot() {
    var steps = parseInt(getVar("$leftPivotSteps", 0));
    if (isNaN(steps) || steps < 0) {
        print("Skipping left pivot: invalid steps");
        return;
    }

    sendPacket(CMD_LEFT_PIVOT, u16ToBytes(steps));
    print("Sent LEFT PIVOT: " + steps);
}

function sendWaypointData() {
    var pathTotalDistance = parseInt(getVar("$pathTotalDistance", 0));
    var combinedStepsDesired = parseInt(getVar("$combinedStepsDesired", 0));
    var oppositeDistance = parseInt(getVar("$oppositeDistance", 0));
    var bearingAlignment = parseInt(getVar("$bearingAlignment", 0)) & 0xFF;
    var reflectorBearing = parseFloat(getVar("$reflectorBearing", 0));
    var sensorPrime = parseInt(getVar("$sensorPrime", 0)) & 0xFF;
    var desiredBearing = parseFloat(getVar("$desiredBearing", 0));
    var actualBearing = parseFloat(getVar("$actualBearing", 0));
    var trackDir = parseInt(getVar("$trackDir", 0)) & 0xFF;

    if (
        isNaN(pathTotalDistance) ||
        isNaN(combinedStepsDesired) ||
        isNaN(oppositeDistance) ||
        isNaN(reflectorBearing) ||
        isNaN(sensorPrime) ||
        isNaN(desiredBearing) ||
        isNaN(actualBearing)
    ) {
        print("Skipping waypoint: invalid global variable(s)");
        return;
    }

    var payload = [];
    payload = payload.concat(u32ToBytes(pathTotalDistance));
    payload = payload.concat(u32ToBytes(combinedStepsDesired));
    payload = payload.concat(u32ToBytes(oppositeDistance));
    payload.push(bearingAlignment);
    payload = payload.concat(u16ToBytes(Math.round(reflectorBearing * 100)));
    payload.push(sensorPrime);
    payload = payload.concat(u16ToBytes(Math.round(desiredBearing * 100)));
    payload = payload.concat(u16ToBytes(Math.round(actualBearing * 100)));
    payload.push(trackDir);

    sendPacket(CMD_WAYPOINT_DATA, payload);
    print("Sent WAYPOINT DATA");
}

function readIncomingPacket() {
    while (UART.hardwareUartAvailable(UART_INDEX) >= 2) {
        var header = UART.hardwareUartRead(UART_INDEX, 1)[0];
        if (header != UART_HEADER) {
            continue;
        }

        var cmd = UART.hardwareUartRead(UART_INDEX, 1)[0];

        if (cmd == RSP_NAV_ACK) {
            print("RX: NAV ACK");
            continue;
        }

        if (cmd == RSP_TOTAL_STEPS) {
            while (UART.hardwareUartAvailable(UART_INDEX) < 4) {
                sleep(1);
            }
            var data = UART.hardwareUartRead(UART_INDEX, 4);
            var steps = (data[0]) | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
            print("RX: Total Steps Taken = " + steps);
            continue;
        }

        if (cmd == RSP_DESIRED_BEARING) {
            while (UART.hardwareUartAvailable(UART_INDEX) < 2) {
                sleep(1);
            }
            var data2 = UART.hardwareUartRead(UART_INDEX, 2);
            var bearing100 = data2[0] | (data2[1] << 8);
            print("RX: Desired Bearing = " + (bearing100 / 100.0));
            continue;
        }

        if (cmd == RSP_OBS_SCAN_REQUEST) {
            print("RX: Obstacle Scan Request");
            setVar("$sendObsScanCompleteNow", 1);
            continue;
        }

        print("RX: Unknown command = " + cmd);
    }
}

while (true) {
    // TX triggers
    if (getVar("$sendStopNow", 0) == 1) {
        sendStopMotors();
        setVar("$sendStopNow", 0);
    }

    if (getVar("$sendMissedIRNow", 0) == 1) {
        sendMissedIR();
        setVar("$sendMissedIRNow", 0);
    }

    if (getVar("$sendActualBearingNow", 0) == 1) {
        sendActualBearing();
        setVar("$sendActualBearingNow", 0);
    }

    if (getVar("$sendRightPivotNow", 0) == 1) {
        sendRightPivot();
        setVar("$sendRightPivotNow", 0);
    }

    if (getVar("$sendLeftPivotNow", 0) == 1) {
        sendLeftPivot();
        setVar("$sendLeftPivotNow", 0);
    }

    if (getVar("$sendWaypointNow", 0) == 1) {
        sendWaypointData();
        setVar("$sendWaypointNow", 0);
    }

    if (getVar("$sendObsScanCompleteNow", 0) == 1) {
        sendPacket(CMD_OBS_SCAN_COMPLETE, []);
        setVar("$sendObsScanCompleteNow", 0);
        print("Sent OBS SCAN COMPLETE");
    }

    // RX processing
    readIncomingPacket();

    sleep(20);
}

3) Why this should be more stable

This version avoids:

  • automatic bearing spamming
  • repeated sending from a constantly changing variable
  • parseFloat() being used on every loop for a transmit decision
  • mixed responsibilities in the same code path

Instead, it only sends when you deliberately set a trigger flag to 1.


4) How to use it

Send waypoint once

setVar("$sendWaypointNow", 1);

Send right pivot once

setVar("$rightPivotSteps", 2500);
setVar("$sendRightPivotNow", 1);

Send left pivot once

setVar("$leftPivotSteps", 2500);
setVar("$sendLeftPivotNow", 1);

Send actual bearing once

setVar("$actualBearing", 79.35);
setVar("$sendActualBearingNow", 1);

5) Arduino sketch alignment

Your Arduino sketch should also remain simple:

  • read header
  • read command
  • read exact payload length
  • act on it
  • send responses when needed

Your packet lengths should be:

  • STOP = 0
  • MISSED IR = 0
  • ACTUAL BEARING = 2
  • RIGHT PIVOT = 2
  • LEFT PIVOT = 2
  • WAYPOINT DATA = 21 bytes
  • OBS SCAN COMPLETE = 0

6) One question before I refine the Arduino code again

For WAYPOINT DATA, do you want:

  1. desiredBearing and actualBearing included in the packet as you currently have, or
  2. do you want Arduino to compute/store those separately and remove them from the waypoint packet?

If you want, I can next give you:

  • a cleaned Arduino sketch v2
  • and a minimal test version of both scripts for one command at a time

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.
#29  

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

Author Avatar
PRO
Canada
Twitter YouTube GitHub
#30  

Absolutely,Jack - here is a cleaned Arduino sketch v2 structured around your RUN variable exactly as you described.

It is designed to work with your Simplified ARC UART Script and assumes:

  • WAYPOINT DATA is received only in RUN = 0
  • RIGHT PIVOT and LEFT PIVOT can also be received in RUN = 0
  • once waypoint data is accepted, Arduino sends NAV ACK
  • after NAV ACK, Arduino sets RUN = 1
  • in RUN = 1, forward navigation logic runs and UART status handling remains active

Arduino UART Sketch v2

#include <Arduino.h>

// --------------------------------------------------
// UART PROTOCOL
// --------------------------------------------------
static const uint8_t UART_HEADER = 0xA5;

// ARC -> Arduino
static const uint8_t CMD_STOP_MOTORS       = 0x01;
static const uint8_t CMD_MISSED_IR         = 0x02;
static const uint8_t CMD_ACTUAL_BEARING    = 0x03;
static const uint8_t CMD_RIGHT_PIVOT       = 0x04;
static const uint8_t CMD_LEFT_PIVOT        = 0x05;
static const uint8_t CMD_WAYPOINT_DATA     = 0x06;
static const uint8_t CMD_OBS_SCAN_COMPLETE = 0x07;

// Arduino -> ARC
static const uint8_t RSP_NAV_ACK           = 0x10;
static const uint8_t RSP_TOTAL_STEPS       = 0x11;
static const uint8_t RSP_DESIRED_BEARING   = 0x12;
static const uint8_t RSP_OBS_SCAN_REQUEST  = 0x13;

// --------------------------------------------------
// RUN STATE
// --------------------------------------------------
int RUN = 0;   // 0 = setup/alignment, 1 = forward navigation

// --------------------------------------------------
// PARSER STATE
// --------------------------------------------------
enum ParserState {
  WAIT_HEADER,
  WAIT_CMD,
  WAIT_PAYLOAD
};

ParserState state = WAIT_HEADER;
uint8_t currentCmd = 0;
uint8_t payload[32];
uint8_t payloadIndex = 0;
uint8_t expectedLength = 0;

// --------------------------------------------------
// NAV / WAYPOINT VARIABLES
// --------------------------------------------------
uint32_t totalStepsTaken = 0;
uint16_t desiredBearing100 = 0;
uint16_t actualBearing100 = 0;

uint32_t pathTotalDistance = 0;
uint32_t combinedStepsDesired = 0;
uint32_t oppositeDistance = 0;
uint8_t  bearingAlignment = 0;
uint16_t reflectorBearing100 = 0;
uint8_t  sensorPrime = 0;
uint16_t waypointDesiredBearing100 = 0;
uint16_t waypointActualBearing100 = 0;
uint8_t  trackDir = 0;

// --------------------------------------------------
// BYTE HELPERS
// --------------------------------------------------
uint16_t readUInt16LE(const uint8_t* data) {
  return (uint16_t)data[0] | ((uint16_t)data[1] << 8);
}

uint32_t readUInt32LE(const uint8_t* data) {
  return (uint32_t)data[0]
       | ((uint32_t)data[1] << 8)
       | ((uint32_t)data[2] << 16)
       | ((uint32_t)data[3] << 24);
}

void writeUInt16LE(uint16_t value) {
  Serial3.write((uint8_t)(value & 0xFF));
  Serial3.write((uint8_t)((value >> 8) & 0xFF));
}

void writeUInt32LE(uint32_t value) {
  Serial3.write((uint8_t)(value & 0xFF));
  Serial3.write((uint8_t)((value >> 8) & 0xFF));
  Serial3.write((uint8_t)((value >> 16) & 0xFF));
  Serial3.write((uint8_t)((value >> 24) & 0xFF));
}

void sendHeaderAndCmd(uint8_t cmd) {
  Serial3.write(UART_HEADER);
  Serial3.write(cmd);
}

void sendUInt16Packet(uint8_t cmd, uint16_t value) {
  sendHeaderAndCmd(cmd);
  writeUInt16LE(value);
}

void sendUInt32Packet(uint8_t cmd, uint32_t value) {
  sendHeaderAndCmd(cmd);
  writeUInt32LE(value);
}

// --------------------------------------------------
// TX TO ARC
// --------------------------------------------------
void sendNavAck() {
  sendHeaderAndCmd(RSP_NAV_ACK);
  Serial.println("TX:NAV ACK");
}

void sendTotalSteps(uint32_t steps) {
  sendUInt32Packet(RSP_TOTAL_STEPS, steps);
  Serial.print("TX:Total Steps = ");
  Serial.println(steps);
}

void sendDesiredBearing(uint16_t bearing100) {
  sendUInt16Packet(RSP_DESIRED_BEARING, bearing100);
  Serial.print("TX:Desired Bearing x100 = ");
  Serial.println(bearing100);
}

void sendObstacleScanRequest() {
  sendHeaderAndCmd(RSP_OBS_SCAN_REQUEST);
  Serial.println("TX:Obstacle Scan Request");
}

// --------------------------------------------------
// MOTOR / NAVIGATION STUBS
// Replace these with your real stepper logic
// --------------------------------------------------
void stopMotors() {
  Serial.println("Action: stopMotors()");
  // add stepper stop code here
}

void pivotRight(uint16_t steps) {
  Serial.print("Action: pivotRight(");
  Serial.print(steps);
  Serial.println(")");
  // add stepper pivot-right code here
}

void pivotLeft(uint16_t steps) {
  Serial.print("Action: pivotLeft(");
  Serial.print(steps);
  Serial.println(")");
  // add stepper pivot-left code here
}

void beginForwardNavigation() {
  Serial.println("Action: beginForwardNavigation()");
  RUN = 1;
}

// --------------------------------------------------
// WAYPOINT HANDLER
// Payload = 21 bytes:
// 0-3   pathTotalDistance      uint32
// 4-7   combinedStepsDesired   uint32
// 8-11  oppositeDistance       uint32
// 12    bearingAlignment       uint8
// 13-14 reflectorBearing100    uint16
// 15    sensorPrime            uint8
// 16-17 desiredBearing100      uint16
// 18-19 actualBearing100       uint16
// 20    trackDir               uint8
// --------------------------------------------------
void handleWaypointPacket(const uint8_t* data, uint8_t len) {
  if (len != 21) {
    Serial.print("Waypoint length mismatch: ");
    Serial.println(len);
    return;
  }

  pathTotalDistance        = readUInt32LE(&data[0]);
  combinedStepsDesired     = readUInt32LE(&data[4]);
  oppositeDistance          = readUInt32LE(&data[8]);
  bearingAlignment          = data[12];
  reflectorBearing100       = readUInt16LE(&data[13]);
  sensorPrime               = data[15];
  waypointDesiredBearing100 = readUInt16LE(&data[16]);
  waypointActualBearing100  = readUInt16LE(&data[18]);
  trackDir                  = data[20];

  Serial.println("RX: WAYPOINT DATA received");
  Serial.print("pathTotalDistance = ");        Serial.println(pathTotalDistance);
  Serial.print("combinedStepsDesired = ");     Serial.println(combinedStepsDesired);
  Serial.print("oppositeDistance = ");         Serial.println(oppositeDistance);
  Serial.print("bearingAlignment = ");         Serial.println(bearingAlignment);
  Serial.print("reflectorBearing100 = ");      Serial.println(reflectorBearing100);
  Serial.print("sensorPrime = ");              Serial.println(sensorPrime);
  Serial.print("desiredBearing100 = ");        Serial.println(waypointDesiredBearing100);
  Serial.print("actualBearing100 = ");         Serial.println(waypointActualBearing100);
  Serial.print("trackDir = ");                 Serial.println(trackDir);

  // Store received bearing values for RUN=1 use
  desiredBearing100 = waypointDesiredBearing100;
  actualBearing100   = waypointActualBearing100;

  // Confirm waypoint reception before switching to RUN=1
  sendNavAck();

  // Start forward motion after ACK
  beginForwardNavigation();
}

// --------------------------------------------------
// PACKET DISPATCH
// --------------------------------------------------
void handlePacket(uint8_t cmd, const uint8_t* data, uint8_t len) {
  switch (cmd) {
    case CMD_STOP_MOTORS:
      Serial.println("RX: STOP MOTORS");
      stopMotors();
      RUN = 0;
      sendTotalSteps(totalStepsTaken);
      break;

    case CMD_MISSED_IR:
      Serial.println("RX: MISSED IR");
      // put recovery logic here
      break;

    case CMD_ACTUAL_BEARING:
      if (len == 2) {
        actualBearing100 = readUInt16LE(data);
        Serial.print("RX: Actual Bearing x100 = ");
        Serial.println(actualBearing100);
      }
      break;

    case CMD_RIGHT_PIVOT:
      if (len == 2) {
        uint16_t steps = readUInt16LE(data);
        Serial.print("RX: Right Pivot steps = ");
        Serial.println(steps);
        pivotRight(steps);
      }
      break;

    case CMD_LEFT_PIVOT:
      if (len == 2) {
        uint16_t steps = readUInt16LE(data);
        Serial.print("RX: Left Pivot steps = ");
        Serial.println(steps);
        pivotLeft(steps);
      }
      break;

    case CMD_WAYPOINT_DATA:
      if (RUN == 0) {
        handleWaypointPacket(data, len);
      } else {
        Serial.println("RX: WAYPOINT ignored because RUN != 0");
      }
      break;

    case CMD_OBS_SCAN_COMPLETE:
      Serial.println("RX: Obstacle Scan Complete");
      // resume normal forward tracking behavior here
      break;

    default:
      Serial.print("RX: Unknown command 0x");
      Serial.println(cmd, HEX);
      break;
  }
}

uint8_t getExpectedLength(uint8_t cmd) {
  switch (cmd) {
    case CMD_STOP_MOTORS:       return 0;
    case CMD_MISSED_IR:         return 0;
    case CMD_ACTUAL_BEARING:    return 2;
    case CMD_RIGHT_PIVOT:       return 2;
    case CMD_LEFT_PIVOT:        return 2;
    case CMD_WAYPOINT_DATA:     return 21;
    case CMD_OBS_SCAN_COMPLETE: return 0;
    default:                    return 0xFF;
  }
}

void processIncomingByte(uint8_t b) {
  switch (state) {
    case WAIT_HEADER:
      if (b == UART_HEADER) {
        state = WAIT_CMD;
      }
      break;

    case WAIT_CMD:
      currentCmd = b;
      expectedLength = getExpectedLength(currentCmd);

      if (expectedLength == 0xFF) {
        state = WAIT_HEADER;
      } else if (expectedLength == 0) {
        handlePacket(currentCmd, nullptr, 0);
        state = WAIT_HEADER;
      } else {
        payloadIndex = 0;
        state = WAIT_PAYLOAD;
      }
      break;

    case WAIT_PAYLOAD:
      payload[payloadIndex++] = b;
      if (payloadIndex >= expectedLength) {
        handlePacket(currentCmd, payload, expectedLength);
        state = WAIT_HEADER;
      }
      break;
  }
}

void readIncomingUart() {
  while (Serial3.available() > 0) {
    uint8_t b = (uint8_t)Serial3.read();
    processIncomingByte(b);
  }
}

// --------------------------------------------------
// SETUP / LOOP
// --------------------------------------------------
void setup() {
  Serial.begin(115200);
  Serial3.begin(115200);

  Serial.println("Arduino UART sketch v2 ready");
}

void loop() {
  // Always listen for incoming UART bytes
  readIncomingUart();

  if (RUN == 0) {
    // Alignment / pivot / waypoint setup phase
    // Keep this light. Waypoint handling occurs in UART RX.
  } 
  else if (RUN == 1) {
    // Forward navigation phase
    // Put your step counting, PWM correction, and bearing logic here

    // Example placeholder:
    // totalStepsTaken = ...

    // Example periodic status reporting if needed:
    // sendTotalSteps(totalStepsTaken);
    // sendDesiredBearing(desiredBearing100);
  }

  // Small yield if your loop gets too busy
  // delay(1);
}

Notes on this version

What this sketch does

  • parses binary UART packets
  • accepts WAYPOINT DATA only in RUN = 0
  • accepts RIGHT PIVOT and LEFT PIVOT in RUN = 0
  • sends NAV ACK after waypoint is stored
  • switches to RUN = 1
  • leaves RUN = 1 available for your forward motion logic
  • keeps UART parsing always active

What you still need to add

You’ll still replace these stubs with your real code:

  • stopMotors()
  • pivotRight()
  • pivotLeft()
  • forward motion / PWM correction logic in RUN = 1

Important 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:

  1. rewrite your Simplified ARC UART script one final time so it matches this Arduino v2 sketch exactly, or
  2. add your actual stepper motor logic placeholders into this Arduino sketch so it looks closer to your real project.

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.
#31  

Hi @Athena, Please rewrite the Simplified ARC UART script one final time so it matches this Arduino v2 sketch exactly. Thank you. Jack

Author Avatar
PRO
Canada
Twitter YouTube GitHub
#32  

Absolutely,Jack. Below is a final simplified ARC UART JavaScript script that matches the Arduino v2 sketch exactly:

  • Header: 0xA5
  • ARC Arduino commands:
    • 0x01 Stop Motors
    • 0x02 Missed IR
    • 0x03 Actual Bearing
    • 0x04 Right Pivot
    • 0x05 Left Pivot
    • 0x06 Waypoint Data
    • 0x07 Obstacle Scan Complete
  • Arduino ARC responses:
    • 0x10 NAV ACK
    • 0x11 Total Steps Taken
    • 0x12 Desired Bearing Update
    • 0x13 Obstacle Scan Request

This version is intentionally kept simple and trigger-based, so ARC only sends packets when you set a variable to 1.


ARC UART Script v2

var UART_INDEX = 0;
var UART_HEADER = 0xA5;

// ARC -> Arduino
var CMD_STOP_MOTORS = 0x01;
var CMD_MISSED_IR = 0x02;
var CMD_ACTUAL_BEARING = 0x03;
var CMD_RIGHT_PIVOT = 0x04;
var CMD_LEFT_PIVOT = 0x05;
var CMD_WAYPOINT_DATA = 0x06;
var CMD_OBS_SCAN_COMPLETE = 0x07;

// Arduino -> ARC
var RSP_NAV_ACK = 0x10;
var RSP_TOTAL_STEPS = 0x11;
var RSP_DESIRED_BEARING = 0x12;
var RSP_OBS_SCAN_REQUEST = 0x13;

// ----------------------------
// Helpers
// ----------------------------
function safeInt(v, def) {
    var n = parseInt(v);
    if (isNaN(n)) return def;
    return n;
}

function safeFloat(v, def) {
    var n = parseFloat(v);
    if (isNaN(n)) return def;
    return n;
}

function u16ToBytes(value) {
    value = value & 0xFFFF;
    return [
        value & 0xFF,
        (value >> 8) & 0xFF
    ];
}

function u32ToBytes(value) {
    value = value >>> 0;
    return [
        value & 0xFF,
        (value >> 8) & 0xFF,
        (value >> 16) & 0xFF,
        (value >> 24) & 0xFF
    ];
}

function sendPacket(cmd, payload) {
    var packet = [UART_HEADER, cmd].concat(payload);
    UART.hardwareUartWrite(UART_INDEX, packet);
}

// ----------------------------
// TX functions
// ----------------------------
function sendStopMotors() {
    sendPacket(CMD_STOP_MOTORS, []);
    print("TX: STOP");
}

function sendMissedIR() {
    sendPacket(CMD_MISSED_IR, []);
    print("TX: MISSED IR");
}

function sendActualBearing() {
    var bearing = safeFloat(getVar("$actualBearing", -1), -1);
    if (bearing < 0) return;

    var bearing100 = Math.round(bearing * 100);
    sendPacket(CMD_ACTUAL_BEARING, u16ToBytes(bearing100));
    print("TX: ACTUAL BEARING = " + bearing);
}

function sendRightPivot() {
    var steps = safeInt(getVar("$rightPivotSteps", 0), 0);
    sendPacket(CMD_RIGHT_PIVOT, u16ToBytes(steps));
    print("TX: RIGHT PIVOT = " + steps);
}

function sendLeftPivot() {
    var steps = safeInt(getVar("$leftPivotSteps", 0), 0);
    sendPacket(CMD_LEFT_PIVOT, u16ToBytes(steps));
    print("TX: LEFT PIVOT = " + steps);
}

function sendWaypointData() {
    var pathTotalDistance = safeInt(getVar("$pathTotalDistance", 0), 0);
    var combinedStepsDesired = safeInt(getVar("$combinedStepsDesired", 0), 0);
    var oppositeDistance = safeInt(getVar("$oppositeDistance", 0), 0);
    var bearingAlignment = safeInt(getVar("$bearingAlignment", 0), 0) & 0xFF;
    var reflectorBearing = safeFloat(getVar("$reflectorBearing", 0), 0);
    var sensorPrime = safeInt(getVar("$sensorPrime", 0), 0) & 0xFF;
    var desiredBearing = safeFloat(getVar("$desiredBearing", 0), 0);
    var actualBearing = safeFloat(getVar("$actualBearing", 0), 0);
    var trackDir = safeInt(getVar("$trackDir", 0), 0) & 0xFF;

    var payload = [];
    payload = payload.concat(u32ToBytes(pathTotalDistance));
    payload = payload.concat(u32ToBytes(combinedStepsDesired));
    payload = payload.concat(u32ToBytes(oppositeDistance));
    payload.push(bearingAlignment);
    payload = payload.concat(u16ToBytes(Math.round(reflectorBearing * 100)));
    payload.push(sensorPrime);
    payload = payload.concat(u16ToBytes(Math.round(desiredBearing * 100)));
    payload = payload.concat(u16ToBytes(Math.round(actualBearing * 100)));
    payload.push(trackDir);

    sendPacket(CMD_WAYPOINT_DATA, payload);
    print("TX: WAYPOINT DATA sent");
}

// ----------------------------
// RX processing
// ----------------------------
function readIncomingPacket() {
    while (UART.hardwareUartAvailable(UART_INDEX) >= 2) {
        var header = UART.hardwareUartRead(UART_INDEX, 1)[0];
        if (header != UART_HEADER) {
            continue;
        }

        var cmd = UART.hardwareUartRead(UART_INDEX, 1)[0];

        if (cmd == RSP_NAV_ACK) {
            print("RX: NAV ACK");
            setVar("$run", 1);
            continue;
        }

        if (cmd == RSP_TOTAL_STEPS) {
            while (UART.hardwareUartAvailable(UART_INDEX) < 4) {
                sleep(1);
            }
            var data = UART.hardwareUartRead(UART_INDEX, 4);
            var steps = (data[0]) | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
            print("RX: TOTAL STEPS = " + steps);
            continue;
        }

        if (cmd == RSP_DESIRED_BEARING) {
            while (UART.hardwareUartAvailable(UART_INDEX) < 2) {
                sleep(1);
            }
            var d = UART.hardwareUartRead(UART_INDEX, 2);
            var bearing100 = d[0] | (d[1] << 8);
            print("RX: DESIRED BEARING = " + (bearing100 / 100.0));
            continue;
        }

        if (cmd == RSP_OBS_SCAN_REQUEST) {
            print("RX: OBSTACLE SCAN REQUEST");
            setVar("$obsScan", 1);
            continue;
        }

        print("RX: Unknown command = " + cmd);
    }
}

// ----------------------------
// Main loop
// ----------------------------
while (true) {

    // Trigger-based TX only
    if (getVar("$sendStopNow", 0) == 1) {
        sendStopMotors();
        setVar("$sendStopNow", 0);
    }

    if (getVar("$sendMissedIRNow", 0) == 1) {
        sendMissedIR();
        setVar("$sendMissedIRNow", 0);
    }

    if (getVar("$sendActualBearingNow", 0) == 1) {
        sendActualBearing();
        setVar("$sendActualBearingNow", 0);
    }

    if (getVar("$sendRightPivotNow", 0) == 1) {
        sendRightPivot();
        setVar("$sendRightPivotNow", 0);
    }

    if (getVar("$sendLeftPivotNow", 0) == 1) {
        sendLeftPivot();
        setVar("$sendLeftPivotNow", 0);
    }

    if (getVar("$sendWaypointNow", 0) == 1) {
        sendWaypointData();
        setVar("$sendWaypointNow", 0);
    }

    if (getVar("$sendObsScanCompleteNow", 0) == 1) {
        sendPacket(CMD_OBS_SCAN_COMPLETE, []);
        setVar("$sendObsScanCompleteNow", 0);
        print("TX: OBS SCAN COMPLETE");
    }

    // Always read incoming UART
    readIncomingPacket();

    sleep(20);
}

How to use it

Set the trigger variables from another ARC script or control:

Send waypoint once

setVar("$sendWaypointNow", 1);

Send right pivot once

setVar("$rightPivotSteps", 2500);
setVar("$sendRightPivotNow", 1);

Send left pivot once

setVar("$leftPivotSteps", 2500);
setVar("$sendLeftPivotNow", 1);

Send actual bearing once

setVar("$actualBearing", 79.35);
setVar("$sendActualBearingNow", 1);

Send stop

setVar("$sendStopNow", 1);

One important note

This script assumes:

  • $actualBearing is already being set elsewhere
  • $desiredBearing, $reflectorBearing, $pathTotalDistance, etc. are already set elsewhere
  • the Arduino is using the exact same packet format
  • Serial3 on the Arduino is wired correctly to the EZ-B UART

If 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 DATA before 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.
 
📚 This thread has been preserved as a helpful resource!

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.