@Athena,
I'm using an EZBv4 on a rover as the overall controller. The EZBv4 hosts ARC scripts (Robot Skills written in ARC JavaScript) that manage two UART peripherals connected to external microcontrollers:
- Teensy (controls one servo + IR sensor) - connected to EZB UART2 (Teensy UART2)
- Mega (controls four stepper motors) - connected to EZB UART0 (Mega UART0)
Both manager scripts below (Teensy manager = TEENSY_UART_MGRv2, Mega manager = MEGA_UART_MGRv2) are ARC JavaScript Robot Skills running on the EZB. They use the EZB hardware UART APIs (UART.initHardwareUart, UART.hardwareUartWrite, UART.hardwareUartAvailable, UART.hardwareUartRead, etc.) to communicate with the microcontrollers.
For the next revision (v3), can you implement the following?
- A matching Mega firmware update (C++ / Arduino) that implements the HELLO / READY / STATUS? / ACK / NAK startup/handshake and response flow to match the ARC manager expectations.
- A matching Teensy firmware script (C++ / Teensyduino) that implements the same startup/handshake flow.
- A single shared ARC helper (JavaScript) that both UART manager Robot Skills can use so the ARC UART manager scripts look almost identical and share the same initialization/handshake conventions.
Also, please provide a step-by-step explanation of each manager’s logic flow. Specifically, for each manager describe:
- What commands and messages need to be sent from the ARC JavaScript architecture (the UART manager Robot Skills / scripts in the ARC project).
- What needs to be handled in the C++ firmware on the Teensy and Mega to trigger the functions of each manager.
Thank you very much,
Jack
TEENSY_UART_MGRv2
var UART_INDEX = 2; // Teensy UART2
var UART_BAUD = 115200;
var UART_HEADER = 0xA5; // not used in this packet style, kept only if you later unify protocols
var running = true;
var teensyReady = false;
var lastHelloMs = 0;
var lastStatusMs = 0;
var txOpen = false;
function checksum(payload) {
var sum = 0;
for (var i = 0; i < payload.length; i++) {
sum = (sum ^ payload.charCodeAt(i)) & 0xFF;
}
return sum;
}
function toHex2(value) {
var hex = "0123456789ABCDEF";
return hex.charAt((value >> 4) & 0x0F) + hex.charAt(value & 0x0F);
}
function buildPacket(payload) {
var chk = checksum(payload);
return "$" + payload + "*" + toHex2(chk) + "\n";
}
function sendPacket(payload) {
var packet = buildPacket(payload);
print("TX: " + packet.trim());
UART.hardwareUartWriteString(UART_INDEX, packet);
}
function requestHello() {
sendPacket("HELLO");
lastHelloMs = (new Date()).getTime();
}
function requestStatus() {
sendPacket("STATUS?");
lastStatusMs = (new Date()).getTime();
}
function sendStart() {
sendPacket("START");
}
function sendStop() {
sendPacket("STOP");
}
function handlePayload(payload) {
var parts = payload.split(",");
var head = parts[0];
if (head == "READY") {
teensyReady = true;
print("Teensy ready: " + parts[1]);
return;
}
if (head == "ACK") {
print("ACK: " + parts[1]);
return;
}
if (head == "NAK") {
print("NAK: " + parts[1]);
return;
}
if (head == "STATUS") {
print("STATUS: " + payload);
return;
}
if (head == "BEARING") {
print("BEARING: " + parts[1]);
return;
}
if (head == "COUNT") {
print("COUNT: " + parts[1]);
return;
}
if (head == "STOP") {
print("Teensy reported STOP");
return;
}
print("Unknown payload: " + payload);
}
function readIncoming() {
var available = UART.hardwareUartAvailable(UART_INDEX);
if (available <= 0) return;
var data = UART.hardwareUartReadStringAvailable(UART_INDEX);
if (data == null || data == "") return;
var lines = data.split("\n");
for (var i = 0; i < lines.length; i++) {
var line = lines[i].trim();
if (line == "") continue;
print("RX: " + line);
if (line.charAt(0) != "$") continue;
var star = line.indexOf("*");
if (star < 0) continue;
var payload = line.substring(1, star);
handlePayload(payload);
}
}
function startupHandshake() {
teensyReady = false;
requestHello();
sleep(200);
requestStatus();
sleep(200);
sendStart();
}
function ensureConnection() {
if (!UART.isHardwareUartInitialized) {
// harmless placeholder if not available in your ARC build
}
}
print("Teensy UART2 Script starting...");
UART.initHardwareUart(UART_INDEX, UART_BAUD);
sleep(500);
startupHandshake();
while (running) {
readIncoming();
var now = (new Date()).getTime();
if (!teensyReady && (now - lastHelloMs) > 2000) {
print("Teensy not ready yet - retrying HELLO");
requestHello();
}
if (teensyReady && (now - lastStatusMs) > 5000) {
requestStatus();
}
sleep(50);
}
MEGA_UART_MGRv2
var UART_INDEX = 0; // Mega UART0
var UART_BAUD = 115200;
var UART_HEADER = 0xA5;
var running = true;
var megaReady = false;
var lastHelloMs = 0;
var lastStatusMs = 0;
// -------------------------
// Command IDs
// -------------------------
var CMD_STOP_MOTORS = 0x01;
var CMD_ACTUAL_BEARING = 0x03;
var CMD_RIGHT_PIVOT = 0x04;
var CMD_LEFT_PIVOT = 0x05;
var CMD_WAYPOINT_DATA = 0x06;
// -------------------------
// Response IDs
// -------------------------
var RSP_NAV_ACK = 0x10;
var RSP_TOTAL_STEPS = 0x11;
var RSP_DESIRED_BEARING = 0x12;
var RSP_OBS_SCAN_REQUEST = 0x13;
var RSP_READY = 0x14;
var RSP_STATUS = 0x15;
var RSP_ACK = 0x16;
var RSP_NAK = 0x17;
// -------------------------
// RX parser
// -------------------------
var rxState = 0;
var rxCmd = 0;
var rxLen = 0;
var rxIndex = 0;
var rxPayload = [];
var rxChecksum = 0;
// -------------------------
// Helpers
// -------------------------
function safeInt(v, def) {
var n = parseInt(v);
if (isNaN(n)) return def;
return n;
}
function clamp(v, min, max) {
if (v < min) return min;
if (v > max) return max;
return v;
}
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 bytesToU16LE(b0, b1) {
return (b0 & 0xFF) | ((b1 & 0xFF) << 8);
}
function bytesToU32LE(b0, b1, b2, b3) {
return (b0 & 0xFF) |
((b1 & 0xFF) << 8) |
((b2 & 0xFF) << 16) |
((b3 & 0xFF) << 24);
}
function checksumXor(cmd, len, payload) {
var c = (cmd ^ len) & 0xFF;
for (var i = 0; i < payload.length; i++) {
c ^= (payload[i] & 0xFF);
}
return c & 0xFF;
}
function sendPacket(cmd, payload) {
var len = payload.length & 0xFF;
var checksum = checksumXor(cmd, len, payload);
UART.hardwareUartWrite(UART_INDEX, [UART_HEADER, cmd, len]);
if (payload.length > 0) {
UART.hardwareUartWrite(UART_INDEX, payload);
}
UART.hardwareUartWrite(UART_INDEX, [checksum]);
}
function requestHello() {
// If your Mega firmware supports it, add a HELLO/READY handshake there too.
// For now this is a logical startup marker in ARC.
megaReady = false;
lastHelloMs = (new Date()).getTime();
print("TX: HELLO requested");
}
function requestStatus() {
// If your Mega firmware supports STATUS? add a command ID for it.
// For now this is a placeholder to match the Teensy manager style.
lastStatusMs = (new Date()).getTime();
print("TX: STATUS requested");
}
function sendStopMotors() {
sendPacket(CMD_STOP_MOTORS, []);
print("TX: STOP MOTORS");
}
function sendActualBearing() {
var bearing100 = safeInt(getVar("$actualBearing100", 9000), 9000);
bearing100 = clamp(bearing100, 0, 18000);
sendPacket(CMD_ACTUAL_BEARING, u16ToBytes(bearing100));
print("TX: ACTUAL BEARING x100 = " + bearing100);
}
function sendWaypointData() {
var pathTotalDistance = safeInt(getVar("$pathTotalDistance", 0), 0) >>> 0;
var combinedStepsDesired = safeInt(getVar("$combinedStepsDesired", 0), 0) >>> 0;
var oppositeDistance = safeInt(getVar("$oppositeDistance", 0), 0) >>> 0;
var bearingAlignment = safeInt(getVar("$bearingAlignment", 0), 0) & 0xFF;
var trackDir = safeInt(getVar("$trackDir", 0), 0);
trackDir = (trackDir === 0) ? 0 : 1;
var desiredBearing100 = clamp(safeInt(getVar("$desiredBearing100", 9000), 9000), 0, 18000);
var actualBearing100 = clamp(safeInt(getVar("$actualBearing100", 9000), 9000), 0, 18000);
var scanIntervalSteps = safeInt(getVar("$scanIntervalSteps", 3056), 3056) >>> 0;
setVar("$desiredBearing100", desiredBearing100);
setVar("$actualBearing100", actualBearing100);
var payload = [];
payload = payload.concat(u32ToBytes(pathTotalDistance));
payload = payload.concat(u32ToBytes(combinedStepsDesired));
payload = payload.concat(u32ToBytes(oppositeDistance));
payload.push(bearingAlignment);
payload = payload.concat(u16ToBytes(desiredBearing100));
payload = payload.concat(u16ToBytes(actualBearing100));
payload.push(trackDir);
payload = payload.concat(u32ToBytes(scanIntervalSteps));
if (payload.length !== 22) {
print("Waypoint payload length error: " + payload.length);
return;
}
sendPacket(CMD_WAYPOINT_DATA, payload);
print("TX: WAYPOINT DATA");
}
function sendPivot(cmd, name) {
var steps = safeInt(getVar("$pivotSteps", 0), 0) >>> 0;
var payload = u16ToBytes(steps);
sendPacket(cmd, payload);
print("TX: " + name + " steps=" + steps);
}
function sendRightPivot() {
sendPivot(CMD_RIGHT_PIVOT, "RIGHT PIVOT");
}
function sendLeftPivot() {
sendPivot(CMD_LEFT_PIVOT, "LEFT PIVOT");
}
function requestObstacleScan() {
if (getVar("$obsScanActive", 0) == 1) {
print("RX: OBSTACLE SCAN REQUEST ignored (scan already active)");
return;
}
setVar("$obsScanRequested", 1);
setVar("$obsScanActive", 1);
print("RX: OBSTACLE SCAN REQUEST -> $obsScanRequested=1, $obsScanActive=1");
}
function handleRxPacket(cmd, payload) {
if (cmd == RSP_READY) {
megaReady = true;
print("RX: MEGA READY");
return;
}
if (cmd == RSP_ACK) {
print("RX: ACK");
return;
}
if (cmd == RSP_NAK) {
print("RX: NAK");
return;
}
if (cmd == RSP_NAV_ACK) {
print("RX: NAV ACK");
setVar("$navAckReceived", 1);
return;
}
if (cmd == RSP_TOTAL_STEPS) {
if (payload.length == 4) {
var steps = bytesToU32LE(payload[0], payload[1], payload[2], payload[3]);
setVar("$totalStepsTaken", steps);
setVar("$legComplete", 1);
print("RX: TOTAL STEPS = " + steps);
}
return;
}
if (cmd == RSP_DESIRED_BEARING) {
if (payload.length == 2) {
var bearing100 = bytesToU16LE(payload[0], payload[1]);
setVar("$desiredBearing100", bearing100);
print("RX: DESIRED BEARING x100 = " + bearing100);
}
return;
}
if (cmd == RSP_OBS_SCAN_REQUEST) {
requestObstacleScan();
return;
}
print("RX: Unknown cmd=" + cmd + " len=" + payload.length);
}
function processIncomingBytes() {
var available = UART.hardwareUartAvailable(UART_INDEX);
while (available > 0) {
var b = UART.hardwareUartRead(UART_INDEX, 1)[0] & 0xFF;
available--;
if (rxState == 0) {
if (b == UART_HEADER) rxState = 1;
continue;
}
if (rxState == 1) {
rxCmd = b;
rxChecksum = rxCmd;
rxState = 2;
continue;
}
if (rxState == 2) {
rxLen = b;
rxChecksum ^= rxLen;
rxPayload = [];
rxIndex = 0;
rxState = (rxLen == 0) ? 4 : 3;
continue;
}
if (rxState == 3) {
rxPayload.push(b);
rxChecksum ^= b;
rxIndex++;
if (rxIndex >= rxLen) rxState = 4;
continue;
}
if (rxState == 4) {
var cs = b & 0xFF;
if (cs == (rxChecksum & 0xFF)) {
handleRxPacket(rxCmd, rxPayload);
} else {
print("RX checksum error. cmd=" + rxCmd + " expected=" + (rxChecksum & 0xFF) + " got=" + cs);
}
rxState = 0;
rxCmd = 0;
rxLen = 0;
rxIndex = 0;
rxPayload = [];
rxChecksum = 0;
continue;
}
}
}
print("Mega UART Manager started");
UART.initHardwareUart(UART_INDEX, UART_BAUD);
requestHello();
sleep(200);
requestStatus();
while (true) {
if (getVar("$sendWaypointNow", 0) == 1 && getVar("$waypointSent", 0) == 0) {
sendWaypointData();
setVar("$sendWaypointNow", 0);
setVar("$waypointSent", 1);
}
if (getVar("$sendStopNow", 0) == 1 && getVar("$stopSent", 0) == 0) {
sendStopMotors();
setVar("$sendStopNow", 0);
setVar("$stopSent", 1);
}
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);
}
processIncomingBytes();
sleep(20);
}

Absolutely - this is a good candidate for a clean v3 protocol with the same handshake pattern on both controllers.
Below I’ll give you:
I’m going to keep the protocol simple and consistent:
Proposed v3 Protocol
Text line format for startup / status
Use newline-terminated ASCII messages:
HELLOREADY,<device>,<version>STATUS?STATUS,<state>,<details>ACK,<message>NAK,<reason>Example flow
For the Mega, you can keep the same startup flow:
1) Shared ARC JavaScript Helper
This helper can be reused by both managers so they look almost identical.
Notes
2) Teensy Firmware v3 Example
This is a clean Teensyduino example implementing:
HELLOREADYSTATUS?STATUS,...STARTSTOPACKNAKWhat this Teensy firmware does
HELLOwithREADY,Teensy,v3STATUS?with current runtime infoSTART/STOPIf your Teensy really is on UART2, you’ll likely want to replace
SerialwithSerial2and set pin assignments accordingly. If that’s your actual wiring, tell me which Teensy model you’re using and I’ll rewrite it for the correct UART port.3) Mega Firmware v3 Example
This is the matching Arduino Mega version using
Serial1,Serial2, etc.If your Mega is physically connected to the EZB UART0, then the firmware side depends on which hardware serial port you wired to the EZB.
Here’s the same handshake style:
4) Step-by-Step Manager Logic Flow
A. Teensy Manager Logic Flow
ARC side
HELLO.READY,Teensy,v3.STATUS?.STATUS,....START.ACK,START.STATUS?.STOP.ACK,STOP.Teensy firmware side
HELLO, mark itself ready and replyREADY,Teensy,v3.STATUS?, send runtime state.START, enable servo/sensor loop and replyACK,START.STOP, disable runtime behavior and replyACK,STOP.NAK,UNKNOWN_CMD.B. Mega Manager Logic Flow
ARC side
HELLO.READY,Mega,v3.STATUS?.STATUS,....STARTandSTOPfor lifecycle control.Mega firmware side
HELLO, replyREADY,Mega,v3.STATUS?, report system state.START, enable motor logic.STOP, disable all motors.NAK,....5) What ARC Sends vs What Firmware Must Handle
ARC JavaScript should send
For both controllers:
HELLOSTATUS?STARTSTOPOptional:
Firmware should handle
For both controllers:
HELLOREADY,...STATUS?STATUS,...STARTACK,STARTSTOPACK,STOPNAK,UNKNOWN_CMD6) How to Make Both ARC Managers Almost Identical
A good pattern is:
Each manager only defines:
UART_INDEXdeviceNameThat way your manager scripts become mostly:
or
Everything else stays shared.
7) Important ARC Note
Your current Mega manager uses a binary packet protocol with headers, command IDs, payload lengths, and checksums. That is fine - but it is different from the Teensy text protocol you posted.
For v3, I strongly recommend choosing one protocol style for both devices:
Option A: Text-only protocol
Best for:
Option B: Binary protocol
Best for:
Best compromise
Use:
That gives you clean startup plus efficient runtime control.
8) Recommended Next Step
If you want, I can do the next pass and turn this into:
That would let you drop the code in with minimal changes.
Also, for code restructuring like this, the ARC AI Assistant built into ARC can be very helpful for rewriting and refactoring your script directly in the editor:
https://synthiam.com/Products/AI-Assistant
Quick question so I can make this exact:
Do you want v3 to be:
If you answer that, I can produce the exact v3 code set in the format you can paste straight into ARC and Arduino/Teensyduino.
@Athena. Thank you. Can you do your recommendations?
Recommended Next Step
If you want, I can do the next pass and turn this into:
That would let you drop the code in with minimal changes
Quick question so I can make this exact:
Do you want v3 to be:
ANSWER: Please do option #2. Text handshake + binary runtime payloads If you answer that, I can produce the exact v3 code set in the format you can paste straight into ARC and Arduino/Teensyduino.
Finally, to be clear on UART connections for the code rewrite:
UART used between
UART used between
Thank you. Jack
Excellent - option #2 is the right choice here. That gives you a clean text handshake for startup/debug and binary payloads for the actual runtime control, which is a great fit for ARC and the two microcontrollers.
Below is a complete v3 recommendation set:
v3 Protocol Design
High-level behavior
Startup / handshake
Use text lines:
HELLOREADY,<device>,v3STATUS?STATUS,...ACK,<cmd>NAK,<reason>Runtime control
Use binary framed packets with your existing Mega-style format:
0xA5headercmdlenpayload[len]checksumThis lets ARC quickly send structured data for motor/sensor control, while the firmware still has a very readable startup handshake.
1) Shared ARC JavaScript Helper
This helper is designed so your Teensy manager and Mega manager can share nearly all logic.
Shared framing rules
Shared helper code
2) Teensy 4.1 Firmware Sketch
This version uses:
That matches your wiring:
Teensy firmware
3) Mega Firmware Sketch
This is the matching Mega side.
You said the Mega is wired as:
That means the Mega should use Serial3.
Mega firmware
4) Message Table
Text handshake messages
HELLOREADY,<device>,v3STATUS?STATUS,...STARTACK,STARTSTOPACK,STOPNAK,<reason>Binary runtime packets
CMD_STOP_MOTORS0x01CMD_ACTUAL_BEARING0x03CMD_RIGHT_PIVOT0x04CMD_LEFT_PIVOT0x05CMD_WAYPOINT_DATA0x06Binary responses
RSP_READY0x14RSP_STATUS0x15RSP_ACK0x16RSP_NAK0x17RSP_TOTAL_STEPS0x11RSP_DESIRED_BEARING0x125) Step-by-Step Manager Logic
Teensy manager flow
HELLO.READY,Teensy,v3.STATUS?.STATUS,....ACKorNAK.STATUS?to confirm health.Mega manager flow
HELLO.READY,Mega,v3.STATUS?.STATUS,....ACKorNAK.6) What ARC Must Handle vs What Firmware Must Handle
ARC JavaScript should:
HELLOSTATUS?START/STOP$teensyReady,$megaReady,$navAckReceived, etc.Firmware should:
HELLOREADY,...STATUS?STARTandSTOPACK/NAK7) Important Wiring Note
You provided:
That is perfect for:
So the firmware examples above are aligned with your wiring.
8) Recommended ARC Project Structure
In ARC, I would suggest:
UART_CommonHelperTEENSY_UART_MGRv3MEGA_UART_MGRv3Each manager can call the same helper functions and only define:
This keeps both scripts almost identical, exactly as you wanted.
9) One small ARC tip
If you are editing these manager scripts in ARC, the built-in AI Assistant can help refactor the repeated logic into a clean shared helper file very quickly:
https://synthiam.com/Products/AI-Assistant
If you want, I can do the next pass and turn this into:
CMD_*andRSP_*IDsIf you’d like, I can write the exact v3 ARC manager scripts next so they’re ready to paste into ARC.
@Athena. Thank you. Can you do the next pass and turn this into the following?
CMD_*andRSP_*IDsCan you please write the exact v3 ARC manager scripts next so they’re ready to paste into ARC.
I have included my existing sketch that is running on the Mega firmware. I think it may be overly complicated, it does a poor job of smoothly correcting the motors. Could you please review it and make recommendations? Can you consider how to implement the existing commands with your new Mega firmware sketch?
Thank you. Jack
Existing Mega sketch
Ultima
Absolutely - and thank you for the existing Mega sketch. It helps a lot because I can see why the current behavior feels jerky: the sketch is heavily blocking, uses
goto, mixes transport parsing with motion logic, and ramps speed in a way that only updates when the error changes rather than continuously smoothing toward target speeds.Below is the v3 rewrite direction I recommend:
What I recommend changing
Problems in the current Mega sketch
Your current code has a few things that make smoothing difficult:
Blocking
whileloopsgoto Mainand nested state blocksUART protocol is ad hoc
A,R,x,Tare not self-describing enough for long-term growth.Speed correction is not truly continuous
diffCoursechanges.Motor control and packet parsing are tightly coupled
v3 architecture I recommend
ARC side
Mega side
Teensy side
1) Full ARC JavaScript implementation
I’m going to give you two scripts:
TEENSY_UART_MGRv3MEGA_UART_MGRv3They share the same helper pattern and are ready to paste into ARC.
Shared helper pattern
ARC does not have true file imports like a full desktop environment, so the easiest approach is to keep a shared section at the top of both scripts and only change:
UART_INDEXDEVICE_NAMETEENSY_UART_MGRv3
MEGA_UART_MGRv3
2) Complete packet parser for CMD_* and RSP_* IDs
Here is the recommended handling map for v3.
Commands ARC sends to firmware
0x01CMD_STOP_MOTORS0x03CMD_ACTUAL_BEARING0x04CMD_RIGHT_PIVOT0x05CMD_LEFT_PIVOT0x06CMD_WAYPOINT_DATAResponses firmware sends to ARC
0x10RSP_NAV_ACK0x11RSP_TOTAL_STEPS0x12RSP_DESIRED_BEARING0x13RSP_OBS_SCAN_REQUEST0x14RSP_READY0x15RSP_STATUS0x16RSP_ACK0x17RSP_NAK3) Teensy firmware parser for the 22-byte waypoint payload
Below is the payload layout I recommend for
CMD_WAYPOINT_DATA:22-byte payload layout
pathTotalDistancelittle-endianuint32combinedStepsDesiredlittle-endianuint32oppositeDistancelittle-endianuint32bearingAlignmentuint8desiredBearing100little-endianuint16actualBearing100little-endianuint16trackDiruint8scanIntervalStepslittle-endianuint32If you want, I can also write the exact Teensyduino decode code for this packet in the next message, but the parsing logic is:
4) Mega firmware implementation recommendations
What I recommend for the Mega motion loop
Your current motor logic should be converted to:
State variables
systemReadymotorsEnabledcurrentLeftSpeedcurrentRightSpeedtargetLeftSpeedtargetRightSpeedpathTotalDistancetotalStepsTakendesiredCourseactualCoursediffCourseLoop structure
runSpeed()on all four steppersThat removes the blocking loops.
Recommended smoothing strategy
Instead of:
Use:
currentLeftSpeed += constrain(targetLeftSpeed - currentLeftSpeed, -step, step)currentRightSpeed += constrain(targetRightSpeed - currentRightSpeed, -step, step)This creates smooth motor correction.
5) How to implement your existing commands with the new Mega firmware
Here is how your existing behaviors map into v3.
Existing behavior: path tracking / reflectance correction
Replace the current
while(run == 1)behavior with:CMD_WAYPOINT_DATAdesiredCoursediffCourseExisting behavior: pivot right
Map to:
CMD_RIGHT_PIVOTRSP_ACKand optionallyRSP_TOTAL_STEPSExisting behavior: pivot left
Map to:
CMD_LEFT_PIVOTExisting behavior: stop
Map to:
CMD_STOP_MOTORSmotorsEnabled = falseExisting behavior: actual bearing updates
Map to:
CMD_ACTUAL_BEARINGactualCourse6) Concrete recommendation for the new Mega firmware structure
I strongly recommend splitting the Mega sketch into these functions:
processIncoming()handleTextCommand()handleBinaryPacket()applyWaypointData()updateMotionTargets()rampMotorSpeeds()runMotors()stopMotors()This will make the firmware much easier to debug than the current monolithic
loop().7) ARC setup note
In ARC, add each manager as its own Robot Skill:
TEENSY_UART_MGRv3MEGA_UART_MGRv3Then configure them through the skill’s kebab menu:
8) Important follow-up
I can do the next step and give you the full Mega C++ firmware rewrite in the same v3 protocol style, including:
If you want, I can also convert your current sketch directly into:
If you’d like, I’ll do the Mega firmware rewrite first since that’s the part most in need of restructuring.
@Athena. Thank you very much for your help. I want to be very clear what the function of each microcontroller is responsible for. Looking at the code for Mega mgr and Teensy mgr, I'm not sure we share the same vision for this rover. I'm probably just misunderstanding,
Teensy firmware:
That's it. Nothing more. Simply produces an actual bearing and/or a STOP command.
The Mega does everything else. Mega firmware:
RECEIVES LEG INFO (from EZB firmware)
RUN:
The EZB starts and monitors the Teensy and Mega. EZB firmware:
Can you do the next step and give you the full Mega C++ firmware rewrite in the same v3 protocol style? including:
If you want, I can also convert your current sketch directly into:
If you’d like, I’ll do the Mega firmware rewrite first since that’s the part most in need of restructuring.
Thank you very much. Jack
Absolutely - that clarifies the architecture very well, and I agree with your division of responsibilities.
Summary of the roles
Teensy
Only does:
Mega
Does everything else:
EZB / ARC
Acts as the orchestrator:
That is a clean and very good split.
Next step: Mega v3 firmware rewrite
Below is a non-blocking Mega firmware rewrite in the same v3 protocol style, designed around your description:
I’m going to make one practical assumption:
AccelStepper1) Mega v3 firmware sketch
2) What this Mega rewrite changes
Exact binary packet decode
The Mega now cleanly decodes:
CMD_STOP_MOTORSCMD_ACTUAL_BEARINGCMD_RIGHT_PIVOTCMD_LEFT_PIVOTCMD_WAYPOINT_DATANon-blocking state machine
No more nested infinite loops or
goto Main.Smooth ramping
Speed changes are applied gradually in
rampTowardTargets().Pivot handling
Pivot commands are handled as a motion mode.
Step reporting
Total steps are reported back as binary response packets.
Desired bearing reporting
Desired bearing is reported periodically.
3) Recommendations for improving the motion quality
Your old sketch is trying to do three things at once:
That is why it feels unstable.
Better approach
Use this separation:
A. UART parser
Only handles:
B. Motion state
Tracks:
C. Control loop
Runs every loop iteration:
runSpeed()That gives you a much smoother robot.
4) How your existing commands map into this v3 firmware
Existing leg info from EZB
These variables become the 22-byte
CMD_WAYPOINT_DATApayload:$pathTotalDistance$combinedStepsDesired$oppositeDistance$bearingAlignment$desiredBearing$actualBearing$trackDirMega behavior
Teensy behavior
5) Important note about your old Mega sketch
One important thing: your current sketch appears to be mixing:
It also uses blocking while-loops that make the robot less responsive.
If you want smooth correction, the best next improvement is to make the target speed change continuously based on bearing error, rather than only when the error changes. That’s what the new
rampTowardTargets()flow is designed for.6) What I recommend next
The best next step is for me to do two things for you:
Write the Teensy v3 firmware
Refine the ARC manager scripts
If you want, I can do the Teensy v3 firmware next and make it match this Mega rewrite exactly.
Also, if you’d like to turn these scripts into ARC robot skills, remember the skill settings are accessed from the kebab menu on the top right of each skill window.
If you want, I can continue with:
Just say which one you want next.
@Athena. Thank you for MEGAv3 Firmware Sketch. Under the subtitle:
Mega behavior
Sends desired bearing updates back. This only needs to accomplish via UART to EZB 'Monitor script' whenever the desired bearing changes by 1 degree. EZB 'Monitor script' prints the actual bearing as it is received by the Teensy which declares a new actual bearing only when change is significant. The 'Monitor script' prints are for my evaluation of tracking accuracy. I chose 1 degree variance for desired bearing so as not to bog down UART with unnecessary tasks.
Can you please rewrite MEGAv3 Firmware Sketch?
I wish to be able to run the Teensy-v3 and the Mega-v3 together to confirm their mutual compatibility. Fine tuning this pair seems appropriate after test running them on the rover together.
I have attached my EZB 'LEG' script so that you can see how I have been using it. Please feel free to update/rewrite it so that I can verify data is being sent properly via UART. Also verifying that the Teensy-v3 and Mega-v3 work together. I had been printing the desired bearing/actual bearings from a previous UART script for 'Monitoring'. Can you write a 'Monitoring' script that prints the desired bearing and actual bearing from their global variables?
Your Statement: Also, if you’d like to turn these scripts into ARC robot skills, remember the skill settings are accessed from the kebab menu on the top right of each skill window. My Question: Is there a difference in the terms 'script' and 'ARC robot skill'? My understanding of kebab menu is that this is how a script is accessed and changed. 'Saving' it closes the script. Is this correct?
Thank you. Jack
EZB LEGv1