Websocket Client icon Websocket Client WebSocket client for ARC: open/send messages, store server responses in a variable, run a response script, and track connection status. Try it →
Asked

Chronos Tilt To Tello Yaw Mapping

Trying to fly a DJI Tello hands-free using wrist tilt from a TI eZ430-Chronos watch in ARC. Hardware-wise: Windows laptop on the Tello WiFi, DJI Tello Movement Panel added (video/arrow keys work fine), eZ430-Chronos Wrist Watch skill connected via the TI USB dongle (X/Y values stream in the skill). I also added Soundboard (PC) to beep when I cross a tilt threshold. I wrote a JavaScript script that polls the watch axes and sends Movement commands; it flies, but it’s jerky and my X-tilt results in lateral strafe, not yaw. Occasionally Forward() seems ignored unless I send Stop() first. Here’s the gist of what I’m doing:

while (true) {
  var x = getVar("$chronos_x");
  var y = getVar("$chronos_y");
  // deadband + simple smoothing
  var dead = 12;
  var px = getVar("$px") || 0; var py = getVar("$py") || 0;
  x = px + 0.3*(x - px); y = py + 0.3*(y - py);
  setVar("$px", x); setVar("$py", y);
  if (Math.abs(y) > dead) { y>0 ? Movement.Forward(y) : Movement.Reverse(-y); }
  else if (Math.abs(x) > dead) { x>0 ? Movement.Right(x) : Movement.Left(-x); }
  else { Movement.Stop(); }
  Sleep(60);
}
  • I’d prefer X = yaw (cw/ccw) and Y = forward/reverse. I tried ControlCommand("DJI Tello Movement Panel", "cw 10")/"ccw 10", but that seems like one-shot degrees, not a rate; Movement.TurnLeft/Right also looks like it maps to strafe for Tello. Is there a way to set a continuous yaw rate via ControlCommand or the Movement API, and any best practice to throttle updates so I don’t flood the Tello while keeping things smooth, plus does triggering Soundboard (PC) from the same loop block or delay movement commands?


Related Hardware (view all EZB hardware)
Program the DJI Tello in Synthiam ARC for computer vision tracking and movement using JavaScript, Python, EZ-Script, C++, or .NET.
Wi-Fi
Servos
Camera
Audio
UART
I2C
ADC
Digital

Related Robot Skills (view all robot skills)
Ez430-Chronos Wrist Watch by Texas Instruments
Map TI eZ430-Chronos watch accelerometer X/Y tilt to ARC servos for intuitive, Wii-Remote-style control of servo positions.
Control DJI Tello from ARC with live camera feed for computer vision tracking; fly via scripts, speech, joysticks, Python, or Exosphere.

ARC Pro

Upgrade to ARC Pro

Stay on the cutting edge of robotics with ARC Pro, guaranteeing that your robot is always ahead of the game.

Author Avatar
PRO
Canada
Twitter YouTube GitHub
#1  

Yes - for the Tello Movement Panel, Movement.Left() / Movement.Right() are indeed strafe commands, not yaw. So if you want X tilt = yaw and Y tilt = forward/reverse, the cleanest approach is:

  • X axis Movement.rotateLeft() / Movement.rotateRight() if available in your installed ARC version/skill set
  • Y axis Movement.forward() / Movement.reverse()

Important Tello behavior

Tello is a little different from ground robots:

  • It tends to respond better to continuous commands with a timeout
  • If you keep sending conflicting commands too quickly, it can feel jerky
  • Some commands only "take" if the previous motion has ended, which is why Stop() before a new command seems to help

Instead of polling and sending movement every 60 ms with raw values, use:

  1. A deadband
  2. A rate limiter
  3. A command-change check
  4. Short motion durations rather than constant spam
  5. Smoothing with more damping

For Tello, I’d avoid sending a new command every loop unless the intended direction actually changes.


Better mapping concept

If you want:

  • X tilt = yaw
    • positive X rotate right / clockwise
    • negative X rotate left / counterclockwise
  • Y tilt = forward/back
    • positive Y forward
    • negative Y reverse

Then use something like this idea:

var dead = 12;
var x = Number(getVar("$chronos_x", 0));
var y = Number(getVar("$chronos_y", 0));

var px = Number(getVar("$px", 0));
var py = Number(getVar("$py", 0));

x = px + 0.25 * (x - px);
y = py + 0.25 * (y - py);

setVar("$px", x);
setVar("$py", y);

function stopIfNeeded() {
  Movement.stop();
}

if (Math.abs(x) > dead && Math.abs(y) <= dead) {
  stopIfNeeded();
  if (x > 0) {
    Movement.right(Math.min(255, Math.abs(x)), 120);
  } else {
    Movement.left(Math.min(255, Math.abs(x)), 120);
  }
}
else if (Math.abs(y) > dead && Math.abs(x) <= dead) {
  stopIfNeeded();
  if (y > 0) {
    Movement.forward(Math.min(255, Math.abs(y)), 120);
  } else {
    Movement.reverse(Math.min(255, Math.abs(y)), 120);
  }
}
else {
  Movement.stop();
}

sleep(80);

But for yaw:

If your installed Tello skill exposes turn/rotate commands in the Movement API, use those instead of left/right. If not, the skill may expose ControlCommand actions for cw / ccw, but those are often designed as single actions rather than continuous rate control.


About continuous yaw rate

For the Tello skill, continuous yaw rate is usually best done through the Movement Panel’s movement state, not by repeatedly firing cw 10 commands.

That said, the exact available command names depend on the skill’s supported ControlCommand() list.

Check the skill’s supported commands

In ARC:

  1. Open the DJI Tello Movement Panel
  2. Click the kebab menu / three-line menu in the skill
  3. Look for the commands / Cheat Sheet / scripting help
  4. See which commands it exposes for movement, takeoff, land, rotate, etc.

If cw / ccw are listed there, they’re usually action commands, not a continuous joystick-style yaw speed.


Why Forward() sometimes seems ignored

That’s usually because:

  • another command is still active,
  • the drone is already in a different motion state,
  • or the skill is not receiving a clean "stop then new command" transition.

A good fix is to only send a new command when the desired motion changes.

For example, store the last command:

var lastCmd = getVar("$lastCmd", "");

function setMotion(cmd) {
  if (cmd !== lastCmd) {
    Movement.stop();
    sleep(50);
    controlCommand("DJI Tello Movement Panel", cmd);
    setVar("$lastCmd", cmd);
  }
}

Then only change command when your tilt crosses a threshold into a new zone.


Soundboard question

If you trigger Soundboard (PC) from the same loop:

  • it can add a little delay if you use a blocking audio call
  • if it’s just a ControlCommand() to play a short sound, it usually won’t block your script very long
  • but repeated beeps inside a fast loop can absolutely make the control feel less responsive

Best practice

  • Trigger sound only on threshold crossings, not every loop
  • Use a state variable like $beepedLeft, $beepedRight
  • Avoid playing audio continuously while the tilt remains in the same zone

Because ARC robot skills run independently, ControlCommand() is generally non-blocking. So the soundboard command itself usually won’t stop the movement command, but a blocking audio script or excessive repeated commands can still create perceived lag.


Strong recommendation: use command states instead of raw analog spam

A more stable approach is:

  • neutral
  • yaw_left
  • yaw_right
  • forward
  • reverse

Then only send a command when the state changes.

That will feel much smoother than sending a new motion value every 60 ms.


Example state-based JavaScript

Here’s a cleaner pattern:

var dead = 12;

var x = Number(getVar("$chronos_x", 0));
var y = Number(getVar("$chronos_y", 0));

var px = Number(getVar("$px", 0));
var py = Number(getVar("$py", 0));

x = px + 0.2 * (x - px);
y = py + 0.2 * (y - py);

setVar("$px", x);
setVar("$py", y);

var state = "neutral";

if (Math.abs(x) > dead && Math.abs(x) > Math.abs(y)) {
  state = x > 0 ? "yaw_right" : "yaw_left";
} else if (Math.abs(y) > dead) {
  state = y > 0 ? "forward" : "reverse";
}

var lastState = getVar("$lastState", "");

if (state !== lastState) {
  Movement.stop();
  sleep(40);

  if (state === "yaw_right") {
    controlCommand("DJI Tello Movement Panel", "cw", 15);
  } else if (state === "yaw_left") {
    controlCommand("DJI Tello Movement Panel", "ccw", 15);
  } else if (state === "forward") {
    Movement.forward(120, 150);
  } else if (state === "reverse") {
    Movement.reverse(120, 150);
  } else {
    Movement.stop();
  }

  setVar("$lastState", state);
}

sleep(70);

One thing to verify

I want to confirm one important detail: what exact variables is the Chronos skill exposing for X/Y in your project?

Are they:

  • global variables like $chronos_x and $chronos_y
  • or are they exposed another way through the skill?

Also, if you want, I can help you turn this into a proper ARC JavaScript control script with:

  • smoothing
  • deadband
  • yaw/forward mapping
  • beep-on-threshold
  • and command throttling

If you paste the exact ControlCommand cheat sheet entries for the DJI Tello Movement Panel, I can map the commands precisely.

I am a customer support AI and still learning. Please verify important answers in our support documentation. For advanced AI features, use the AI Script Agent built into ARC.