Asked

Help With EZ-Script Coding

I'm working on a project with my EZ-B v4, and I need some assistance with coding the GetDigital command. My aim is to use pin D22 as an input pin to receive a high/low digital signal from an R/C receiver. The receiver uses a PWM-to-digital output device on one of its channels. This setup will allow me to switch my robot's drive wheels between ARC (automatic mode) and R/C control (manual mode).

The script below is intended to monitor the state of pin D22 and provide verbal feedback. When in Automatic Mode, ARC will control the wheels and all sensors. In manual Mode, a standard radio control transmitter will handle the wheel operations. I need this functionality implemented in both EZ-Script and JavaScript.

Here's my current EZ-Script code:

$X = GetDigital(D22)
if ($X = 0)
  $X = "PASS"
  EzbSay("in  manual Mode")
elseif ($X = 1)
  $X = "FAIL"
  EzbSay("in Automatic Mode")
endif

Could you review it and help me ensure that it performs correctly? Additionally, guidance on translating this into JavaScript for the ARC environment would be greatly appreciated. Thank you!


Related Hardware EZ-B v4
Related Controls Read Digital Set Digital

ARC Pro

Upgrade to ARC Pro

Become a Synthiam ARC Pro subscriber to unleash the power of easy and powerful robot programming

#73  

Athena, Im still doing some testing, but could you put back the LED for manual mode on D21 of your Robust EZB_D22 script? It always throws me off when it not on.

Author Avatar
PRO
Canada
#74  

Perfect - I added D21 LED control to the robust EZB_D22 monitor so the LED turns ON when the robot is in manual mode and OFF when in Automatic mode. The script uses the existing ezbIndex and only writes D21 when the mode actually changes (to avoid repeated writes). It also sets the LED after reconnect settle/read so it matches the announced mode.

Paste this into your Script skill (JavaScript tab), save and Start it. If your Script skill has a different title, update skillTitle. If you want the LED behavior reversed (ON for Automatic), tell me and I’ll flip it.

Updated script (JavaScript)

/*
 EZB_D22 Reliable Monitor with Manual-mode LED on D21
 - Place in a Script skill (JavaScript tab) and Start it.
 - Uses Digital.get(d22) for mode input.
 - Sets D21 HIGH when in  manual mode, LOW when in Automatic mode.
 - Sets global $Mode ("Automatic" / "Manual").
 - Debounce and settle on reconnect to avoid false announcements.
*/

var skillTitle = "EZB_D22_ConnMerge";   // change if your Script skill name is different
var ezbIndex = 0;                       // change if using another EZB index
var pollMs = 120;                       // main loop poll (ms)
var debounceMs = 300;                   // stable time to confirm change (ms)
var settleOnConnectMs = 900;            // wait after EZB reconnect before reading (ms)
var runFlagVar = "$EZB_D22_Running";    // global marker to prevent duplicates

// LED pin for  manual mode indicator (will be set HIGH for Manual)
var manualLedPin = d21; // use constant d21 (no quotes)

// Helper: prefer EZB TTS, fallback to PC
function announce(text) {
  try {
    if (EZB.isConnected(ezbIndex)) Audio.sayEZB(text, ezbIndex);
    else Audio.say(text);
  } catch (e) {
    print("announce error:" + e);
  }
}

// Map boolean to mode label
function modeFromBoolean(b) {
  return b ? "Automatic" : "Manual";
}

// Debounced read of d22. Returns true/false or null if unstable
function readDebouncedD22() {
  try {
    var a = Digital.get(d22); // constant d22 (no quotes)
    if (a === null || a === undefined) return null;
    var start = (new Date()).getTime();
    while ((new Date()).getTime() - start < debounceMs) {
      var b = Digital.get(d22);
      if (b !== a) { // unstable
        return null;
      }
      sleep(20);
    }
    return !!a;
  } catch (e) {
    print("readDebouncedD22 error:" + e);
    return null;
  }
}

// Set  manual LED: true = On (Manual), false = Off (Automatic)
function setManualLed(state) {
  try {
    // Only write when EZB is connected and state is boolean
    if (!EZB.isConnected(ezbIndex)) {
      print("setManualLed: EZB not connected, cannot set D21");
      return;
    }
    Digital.set(manualLedPin, state, ezbIndex);
    print("D21 set to " + (state ? "ON (Manual)" : "OFF (Automatic)"));
  } catch (e) {
    print("setManualLed error:" + e);
  }
}

// Prevent duplicate instances started (by Connection script)
if (getVar(runFlagVar, "0") === "1") {
  print(skillTitle + " already running. Exiting duplicate start.");
} else {
  setVar(runFlagVar, "1");
  print(skillTitle + " starting. EZB.isConnected=" + EZB.isConnected(ezbIndex));

  var lastState = null;
  var wasConnected = EZB.isConnected(ezbIndex);

  // On first start, try to read D22 right away (if connected)
  if (wasConnected) {
    sleep(200);
    var s0 = readDebouncedD22();
    if (s0 !== null) {
      lastState = s0;
      setVar("$Mode", modeFromBoolean(s0));
      setManualLed(!s0); // note: manualLed ON when mode is  manual (D22 false -> Manual)
      announce("Initial mode " + modeFromBoolean(s0) + " (D22=" + (s0 ? 1 : 0) + ").");
      print("Initial read D22=" + (s0 ? 1 : 0));
    } else {
      print("Initial D22 read unstable/null.");
    }
  }

  // Main monitor loop
  while (true) {
    try {
      var connected = EZB.isConnected(ezbIndex);

      // Handle reconnect: when connection goes disconnected -> connected
      if (!wasConnected && connected) {
        print("EZB reconnected - waiting " + settleOnConnectMs + "ms to settle.");
        sleep(settleOnConnectMs);
        var state = readDebouncedD22();
        if (state !== null) {
          if (state !== lastState) {
            lastState = state;
            setVar("$Mode", modeFromBoolean(state));
            setManualLed(!state); //  manual when state is false (D22=0)
            announce("Mode change " + modeFromBoolean(state) + " (D22=" + (state ? 1 : 0) + ").");
            print("Announced after reconnect D22=" + (state ? 1 : 0));
          } else {
            // Re-announce current mode once after reconnect and ensure LED matches
            setVar("$Mode", modeFromBoolean(state));
            setManualLed(!state);
            announce("Mode " + modeFromBoolean(state) + " (D22=" + (state ? 1 : 0) + ").");
            print("Reconnected; re-announced D22=" + (state ? 1 : 0));
          }
        } else {
          print("After reconnect: D22 unstable/null - no announcement.");
        }
      }

      // Regular polling if connected
      if (connected) {
        var s = readDebouncedD22();
        if (s !== null && lastState === null) {
          // first known read
          lastState = s;
          setVar("$Mode", modeFromBoolean(s));
          setManualLed(!s);
          announce("Initial mode " + modeFromBoolean(s) + " (D22=" + (s ? 1 : 0) + ").");
          print("First stable read D22=" + (s ? 1 : 0));
        } else if (s !== null && s !== lastState) {
          // stable change detected
          lastState = s;
          setVar("$Mode", modeFromBoolean(s));
          setManualLed(!s);
          announce("Mode change " + modeFromBoolean(s) + " (D22=" + (s ? 1 : 0) + ").");
          print("Mode changed D22=" + (s ? 1 : 0));
        }
      } else {
        // Optionally you could set LED off when disconnected,
        // but we cannot write to EZB when it's disconnected.
        //print("EZB not connected.");
      }

      wasConnected = connected;
    } catch (loopErr) {
      print("Monitor loop exception:" + loopErr);
    }
    sleep(pollMs);
  }
}

Quick testing tips

  • Manual LED test: in a quick Script skill or the Script Console run:
Digital.set(d21, true);   // turn LED on
Digital.set(d21, false);  // turn LED off
  • Confirm D21 wiring uses an appropriate resistor and common ground. If you used this LED before it should be fine.
  • If your LED is active-low (wired to Vcc through LED and port sinks to ground) tell me and I’ll invert the writes.

Note about the LED logic

  • The script assumes D22 = HIGH (true) means Automatic, LOW (false / 0) means manual (per your earlier description). The LED is turned ON for manual (i.e., when D22 is false). If you prefer the LED ON for Automatic instead, I’ll flip the setManualLed(!state) calls to setManualLed(state).

Next

  • Would you like the LED to blink briefly when mode changes (visual feedback) instead of just steady ON/OFF?
  • Do you want me to add a small file log (timestamped) whenever the mode changes so you can audit reconnects later?

Tell me which LED polarity or blink preference you want, and I’ll provide that tweak.


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

Yes, I would like the LED on D21 to blink briefly when mode changes, and add the log file as you suggested.

Author Avatar
PRO
Canada
#76  

Great - I added the blink-on-change behavior and a timestamped log file to the robust EZB_D22 monitor. Paste the script into a Script robot skill (JavaScript tab), save and Start it. The script will:

  • Blink the D21 LED briefly whenever the mode changes.
  • Set D21 steady ON when in manual mode, OFF in Automatic mode (same behavior as before).
  • Append a timestamped line to a log file each time the mode changes, on reconnect announcements, and at script start.
  • Keep $Mode updated ("Automatic" / "Manual") so other scripts can read it.

Important: put this code in the Script skill JavaScript tab (not EZ-Script). Update file path or ezbIndex if needed. If Windows permissions block file writes, change the logFile path to a writable folder (see notes below).

Script - paste into your Script skill JavaScript tab

// EZB_D22 Monitor with D21 blink on mode change + log file
// Place in a Script skill (JavaScript tab) and Start it.
// Adjust ezbIndex, skillTitle, and logFile path if needed.

var skillTitle = "EZB_D22_ConnMerge"; // exact Script skill title
var ezbIndex = 0;                     // change if you use another EZB index
var pollMs = 120;                     // main loop interval (ms)
var debounceMs = 300;                 // stable time to confirm change (ms)
var settleOnConnectMs = 900;          // wait after EZB reconnect before reading (ms)
var runFlagVar = "$EZB_D22_Running";  // prevents duplicate instances

// LED pin for  manual mode indicator (HIGH when Manual)
var manualLedPin = d21;               // use constant d21 (no quotes)

// Logging
var logFile = "C:\\Users\\Public\\Documents\\ModeChanges.log"; // change if needed

// Blink configuration
var blinkCount = 2;
var blinkMs = 150;

// Helper: prefer EZB TTS, fallback to PC TTS
function announce(text){
  try {
    if (EZB.isConnected(ezbIndex)) Audio.sayEZB(text, ezbIndex);
    else Audio.say(text);
  } catch(e){
    print("announce error: " + e);
  }
}

// Log a timestamped message to the file and to the ARC log
function logEvent(text){
  try {
    var ts = (new Date()).toLocaleString();
    var line = ts + " - " + text;
    File.appendStringLine(logFile, line);
    print("LOG: " + line);
  } catch(e){
    print("Log write error: " + e + " (path: " + logFile + ")");
  }
}

// Map boolean D22 -> mode string
function modeFromBoolean(b){ return b ? "Automatic" : "Manual"; }

// Set the  manual LED (safe: checks EZB connected)
function setManualLed(val){
  try {
    if (EZB.isConnected(ezbIndex)){
      Digital.set(manualLedPin, val);
      print("D21 set to " + (val ? "ON" : "OFF"));
    } else {
      print("Cannot set D21 - EZB not connected");
    }
  } catch(e){
    print("setManualLed error: " + e);
  }
}

// Blink the LED count times, then set final state
function blinkManualLed(finalState){
  try {
    if (!EZB.isConnected(ezbIndex)){
      print("Cannot blink D21 - EZB not connected");
      return;
    }
    for (var i=0;i<blinkCount;i++){
      Digital.set(manualLedPin, !finalState); // flash opposite
      sleep(blinkMs);
      Digital.set(manualLedPin, finalState);
      sleep(blinkMs);
    }
    // ensure final steady state
    Digital.set(manualLedPin, finalState);
  } catch(e){
    print("blinkManualLed error: " + e);
  }
}

// Debounced read of d22. Returns true/false or null if unstable/unavailable
function readDebouncedD22(){
  try {
    var a = Digital.get(d22); // use constant d22
    if (a === null || a === undefined) return null;
    var start = (new Date()).getTime();
    while ((new Date()).getTime() - start < debounceMs){
      var a2 = Digital.get(d22);
      if (a2 === null || a2 === undefined) return null;
      if (a2 !== a) {
        // unstable
        return null;
      }
      sleep(10);
    }
    return !!a;
  } catch(e){
    print("readDebouncedD22 error: " + e);
    return null;
  }
}

// Prevent duplicate runs started by Connection scripts
if (getVar(runFlagVar, "0") === "1"){
  print(skillTitle + " already running. Exiting duplicate start.");
} else {
  setVar(runFlagVar, "1");
  print(skillTitle + " starting. EZB.isConnected=" + EZB.isConnected(ezbIndex));
  logEvent(skillTitle + " script started");

  var lastState = null; // true = Automatic (D22 high), false =  manual (D22 low)
  var wasConnected = EZB.isConnected(ezbIndex);

  // On first start, try an initial read if connected
  if (wasConnected){
    sleep(200);
    var s0 = readDebouncedD22();
    if (s0 !== null){
      lastState = s0;
      setVar("$Mode", modeFromBoolean(s0));
      // LED steady:  manual when s0==false -> LED ON when  manual => finalLed = !s0
      var finalLed0 = !s0;
      setManualLed(finalLed0);
      announce("Initial mode " + modeFromBoolean(s0) + " (D22=" + (s0 ? 1 : 0) + ").");
      logEvent("Initial read: D22=" + (s0 ? 1 : 0) + " => " + modeFromBoolean(s0));
    } else {
      print("Initial D22 read unstable/null.");
      logEvent("Initial D22 read unstable/null.");
    }
  }

  // Main loop
  while (true){
    try {
      var connected = EZB.isConnected(ezbIndex);

      // Handle reconnect: disconnected -> connected
      if (!wasConnected && connected){
        print("EZB reconnected - waiting " + settleOnConnectMs + "ms to settle.");
        sleep(settleOnConnectMs);
        var state = readDebouncedD22();
        if (state !== null){
          var finalLed = !state; //  manual when state==false
          if (state !== lastState){
            lastState = state;
            setVar("$Mode", modeFromBoolean(state));
            // blink then set final
            blinkManualLed(finalLed);
            announce("Mode change " + modeFromBoolean(state) + " (D22=" + (state ? 1 : 0) + ").");
            logEvent("Reconnected - Mode change detected: D22=" + (state ? 1 : 0) + " => " + modeFromBoolean(state));
          } else {
            // re-announce current mode once and ensure LED matches
            setVar("$Mode", modeFromBoolean(state));
            setManualLed(finalLed);
            announce("Mode " + modeFromBoolean(state) + " (D22=" + (state ? 1 : 0) + ").");
            logEvent("Reconnected - Mode re-announced: D22=" + (state ? 1 : 0) + " => " + modeFromBoolean(state));
          }
        } else {
          print("After reconnect: D22 unstable/null - no announcement.");
          logEvent("After reconnect: D22 unstable/null");
        }
      }

      // Normal polling while connected
      if (connected){
        var s = readDebouncedD22();
        if (s !== null && lastState === null){
          // first stable read
          lastState = s;
          setVar("$Mode", modeFromBoolean(s));
          var finalLed = !s;
          setManualLed(finalLed);
          announce("Initial mode " + modeFromBoolean(s) + " (D22=" + (s ? 1 : 0) + ").");
          logEvent("First stable read: D22=" + (s ? 1 : 0) + " => " + modeFromBoolean(s));
        } else if (s !== null && s !== lastState){
          // stable mode change
          lastState = s;
          setVar("$Mode", modeFromBoolean(s));
          var finalLed = !s;
          blinkManualLed(finalLed);
          announce("Mode change " + modeFromBoolean(s) + " (D22=" + (s ? 1 : 0) + ").");
          logEvent("Mode changed: D22=" + (s ? 1 : 0) + " => " + modeFromBoolean(s));
        }
      } else {
        // EZB not connected: nothing to do
      }

      wasConnected = connected;
    } catch(loopErr){
      print("Monitor loop exception: " + loopErr);
      logEvent("Monitor loop exception: " + loopErr);
    }
    sleep(pollMs);
  } // end main loop
}

Notes and troubleshooting

  • Log file path: default is C:\Users\Public\Documents\ModeChanges.log. If you get permission errors, change logFile to a folder you know ARC can write to (for example a folder inside your user Documents, e.g. "C:\Users\YourName\Documents\ModeChanges.log"). If the script prints "Log write error", update the path and restart the script.
  • LED polarity: script assumes D21 HIGH = LED ON. If your LED is wired active-low (i.e., port sinks to ground to turn LED ON), tell me and I’ll invert finalLed logic (use finalLed = state instead of !state).
  • Auto-start: keep a single Connection Established script line: controlCommand("EZB_D22_ConnMerge","ScriptStart") Remove any old duplicate starts for other monitor scripts.
  • If you ever manually stop the script, clear the run flag before restarting: setVar("$EZB_D22_Running","0") Then restart the Script skill.

Follow-up

  • Confirm the blink behavior looks right (double short blink). If you want faster/slower or different blink count, tell me new blinkMs or blinkCount.
  • Confirm log file is created and being appended (I can show an example format). Would you like the script to also rotate logs daily or keep multiple log files?

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.