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 Script EZ-Script Console

ARC Pro

Upgrade to ARC Pro

Harnessing the power of ARC Pro, your robot can be more than just a simple automated machine.

Author Avatar
PRO
Synthiam
#49  

Oh cool little unit. Make sure you have a common gnd between everything.

Author Avatar
PRO
Canada
#50  

Nice - thanks for the detailed hardware info. Yes, that Pololu multiplexer is a clean solution and will avoid the direct "two outputs fighting" problem if wired and used correctly. A few short confirmations and then a ready-to-use JavaScript snippet you can paste into your monitor script (and suggestions for where to put it).

Key points / confirmations

  • Yes - you must have a common ground between the EZB, the Pololu multiplexer (which is powered by the RC receiver), and the HB25 (motor controller). Without common ground the PWM signals won’t be referenced correctly. (DJ_Sures was right to call this out.)
  • The Pololu multiplexer physically isolates the EZB outputs from the receiver outputs when it switches, which is safer than directly tying outputs together. Still, it’s good practice to have the EZB release its servos (Servo.release) when in manual so the EZB is not actively commanding the output and for an extra safety layer.
  • Because you’re using the pololu SEL/Y wired to the RC channel and also Yconnected to D22 on the EZB, D22 will reliably tell ARC which mode is selected (High = one mode, Low = the other depending on wiring). Use that digital reading as your mode input.

Why Servo.release() is useful here

  • If you release the EZB servos when the multiplexer switches to the RC receiver you:
    • Ensure the EZB is not driving PWM while the RC is active (extra safety).
    • Reduce chances of accidental conflicting drive if the multiplexer has any transient overlap when switching.
  • When returning to Automatic, run your HB25 Init neutral pulses and resume EZB servo outputs.

Recommended JavaScript monitor to detect D22 and act on changes

  • Put this code in a Script skill JavaScript tab (not the EZScript tab).
  • It watches D22, on change to manual it releases d12/d13, sets a $Mode variable, and announces. On change back to Automatic it runs HB25 Init (ScriptStartWait) then sets $Mode back and announces.
  • Adjust script names and ports to your exact names if different.

Paste-ready JavaScript (Script skill JavaScript tab)

// D22 Mode Monitor (JavaScript)
// Place in a Script skill JavaScript tab. Name skill whatever you like.
// Adjust these values to match your ports/skill names:
var ezbIndex = 0;
var inputPin = d22;            // D22 input from Pololu/RC SEL
var leftServoPort = d12;       // EZB servo outputs used for HB25 (left)
var rightServoPort = d13;      // EZB servo outputs used for HB25 (right)
var hb25InitScriptName = "HB25 Init";         // exact Script skill name
var hb25MonitorScriptName = "HB25_D22_Monitor"; // if you want to restart monitor
var pollIntervalMs = 150;      // how often to poll D22

function printTS(message){
  print((new Date()).toLocaleTimeString() + " - " + message);
}

// Helper to speak on the EZB speaker (non-blocking)
function sayEZB(text){
  try { Audio.sayEZB(text, ezbIndex); } catch(e) { print("Audio.sayEZB failed: " + e); }
}

// Get initial state
var prevState = Digital.get(inputPin) ? 1 : 0;
printTS("D22 monitor starting. Initial state: " + prevState);
setVar("$Mode", prevState ? "Automatic" : "Manual");

while (true) {
  try {
    var state = Digital.get(inputPin) ? 1 : 0;
    if (state !== prevState) {
      // Rising edge or falling edge detected
      if (state === 0) {
        // Example: state==0 ->  manual (adjust logic if your wiring is inverted)
        printTS("D22 ->  manual detected. Releasing servos and stopping movement.");
        // Release servos so EZ-B doesn't actively drive PWM
        Servo.release(leftServoPort);
        Servo.release(rightServoPort);
        // Stop automatic movement if running
        try { Movement.stop(); } catch(e) { print("Movement.stop failed: " + e); }
        setVar("$Mode", "Manual");
        sayEZB("Manual mode enabled");
      } else {
        // state==1 -> Automatic (adjust if inverted)
        printTS("D22 -> Automatic detected. Running HB25 Init and resuming automatic mode.");
        // Ensure HB25 init runs before trying to drive motors
        try {
          controlCommand(hb25InitScriptName, "ScriptStartWait");
          // optional small settle
          sleep(250);
        } catch(e) {
          print("HB25 Init start/wait failed: " + e);
        }
        // Optionally restart your monitor or other auto scripts
        try {
          controlCommand(hb25MonitorScriptName, "ScriptStart");
        } catch(e) {
          // ignore if you didn't create a separate monitor
        }
        setVar("$Mode", "Automatic");
        sayEZB("Automatic mode enabled");
      }
      prevState = state;
    }
    sleep(pollIntervalMs);
  } catch(err) {
    printTS("Exception in D22 monitor loop: " + err);
    sleep(1000);
  }
}

Where to put it and startup

  • Add this Script skill (Project Add robot skill Script), paste JS into JavaScript tab, save.
  • Start it manually to test, or start it automatically from the Connection skill (kebab menu Settings Connection Established Scripts).
  • If you add a Connection Established script line use EZScript style (these run in the Connection setting area). Example (no trailing spaces): controlCommand("D22 Mode Monitor","ScriptStart") controlCommand("HB25 Init","ScriptStartWait") sleep(500) controlCommand("HB25_D22_Monitor","ScriptStart")

Notes on EZScript vs JavaScript

  • I provided JavaScript per our standard recommendations. I cannot author EZScript directly (I’ll help convert with guidance, but I won’t produce EZScript code), so run the JavaScript in the Script skill JavaScript tab.
  • If you need an EZScript snippet for the Connection Connection Established area, that small area accepts lines like the three ControlCommand() lines shown above (those are EZScript style calls).

Wiring checklist (quick)

  • Common ground between:
    • Pololu multiplexer/RC receiver ground (VS negative)
    • HB25 ground (motor controller ground)
    • EZB ground
  • Do NOT tie the receiver +V to the EZB +V unless you intentionally want to share power and the voltages match and are safe. Your described approach of powering the Pololu from the receiver and the HB25 separately and only sharing ground is correct.
  • Verify SEL RC CH5 Y D22 wiring logic so you know whether D22=1 means manual or Automatic and invert the JS logic above if needed.

Extra suggestions

  • Keep the Servo.release() calls in your monitor script as shown - they’re a low-risk safety step and can prevent EZB from trying to actively drive the motor lines while RC is in control.
  • On restore back to Automatic, use HB25 Init (ScriptStartWait) so you get neutral pulses before the EZB begins driving servos again.
  • If you want, I can merge this D22 logic into your existing EZB_Reconnect script (so reconnect + mode switching + logging are combined) and provide a single script that also timestamps events into a log file. Tell me the exact script skill names you are using and the preferred log path.

Anything else you want me to add or change (e.g., invert the D22 logic, add file logging, integrate into reconnect script)?


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

oh cool, I will try this new D22 Mode Monitor script. Also like the idea of merging the scripts you suggested and will try that as another option for me.

I also went thru all scripts and corrected any syntax errors on my part. Also made sure these scripts were in the right script skill (javascript or ezscript)  this made a major difference with way less disconnect issues before.

Author Avatar
PRO
Canada
#52  

Nice work - glad fixing the editor locations and syntax cleaned up a lot of the problems. Good sign.

Below I’ve provided a merged JavaScript Script skill that combines:

  • EZ-B reconnect monitoring (auto reconnect via ARC Connection skill),
  • D22 mode monitoring (Manual vs Automatic), and
  • Timestamped logging to a file, plus safe actions on mode changes (Servo.release, HB25 Init, Movement.stop, EZB announcements).

Before you paste/run

  • Put this code in a Script skill JavaScript tab (not the EZ-Script tab).
  • Update the top variables to match your exact skill names, servo ports, and log path.
  • Ensure a common ground between EZ-B, Pololu multiplexer/RC receiver, and HB25.
  • Decide which D22 polarity means manual vs Automatic and set manualOnHigh below or invert the logic as noted.

Merged Script (JavaScript)

// Merged EZB Reconnect + D22 Mode Monitor (JavaScript)
// Place in a Script skill JavaScript tab. Use print() for console output.
// Edit the variables below to match your project names/ports/paths.

var ezbIndex = 0;                        // EZB board index
var inputPin = d22;                      // D22 input monitoring selection
var leftServoPort = d12;                 // EZB servo port for left motor
var rightServoPort = d13;                // EZB servo port for right motor

// Script skill names (update to match EXACT names)
var hb25InitScriptName = "HB25 Init";            // HB25 initialization script (neutral pulses)
var hb25MonitorScriptName = "HB25_D22_Monitor";  // other monitor script you might have

// Logging
var logFile = "C:\\Users\\Public\\EZB_MergedMonitor.log"; // change to a writable path

// Reconnect settings
var checkIntervalMs = 3000;
var connectAttemptWaitMs = 4000;
var maxConnectAttempts = 6;
var backoffMs = 15000;

// Mode logic: set to true if D22=HIGH means Manual, false if D22=HIGH means Automatic
var manualOnHigh = false; // change if your wiring semantics are inverted

// Helpers
function ts(){ return (new Date()).toLocaleString(); }
function log(s){
  var line = ts() + " - " + s;
  print(line);
  try { File.appendStringLine(logFile, line); }
  catch(e) { print("Log write failed: " + e); }
}
function printTS(s){ print(ts() + " - " + s); }
function sayEZB(text){
  try { Audio.sayEZB(text, ezbIndex); }
  catch(e) { print("Audio.sayEZB failed: " + e); }
}

// Convert boolean from Digital.get into 0/1
function readDigitalInt(pin){
  try { return Digital.get(pin) ? 1 : 0; }
  catch(e){ log("Digital.get failed: " + e); return -1; }
}

// Reconnect helper: request Connection skill to connect the specified index
function connectionConnectIndex(index){
  try {
    // Use Connection skill control commands. Adjust or add as needed.
    if (index === 0) controlCommand("Connection","Connect0");
    else if (index === 1) controlCommand("Connection","Connect1");
    else if (index === 2) controlCommand("Connection","Connect2");
    else if (index === 3) controlCommand("Connection","Connect3");
    else if (index === 4) controlCommand("Connection","Connect4");
    else controlCommand("Connection","ConnectAll");
  } catch(e) {
    log("connectionConnectIndex  ControlCommand failed: " + e);
  }
}

// Main merged loop logic
log("Merged monitor starting. Log: " + logFile);
var consecutiveConnectFailures = 0;

// Determine initial mode var
var d = readDigitalInt(inputPin);
var prevModeIsManual;
if (d === -1) {
  prevModeIsManual = false;
  log("Initial D22 read failed, assuming Automatic.");
} else {
  prevModeIsManual = manualOnHigh ? (d === 1) : (d === 0);
  setVar("$Mode", prevModeIsManual ? "Manual" : "Automatic");
  log("Initial D22 state: " + d + " => Mode: " + (prevModeIsManual ? "Manual" : "Automatic"));
}

while(true){
  try {
    // Reconnect handling
    if (!EZB.isConnected(ezbIndex)){
      log("EZB index " + ezbIndex + " not connected. Attempting to connect.");
      connectionConnectIndex(ezbIndex);
      sleep(connectAttemptWaitMs);

      if (EZB.isConnected(ezbIndex)){
        log("EZB reconnected. Running HB25 Init and resuming monitors.");
        try { controlCommand(hb25InitScriptName, "ScriptStartWait"); }
        catch(e) { log("HB25 Init ScriptStartWait failed: " + e); }
        try { controlCommand(hb25MonitorScriptName, "ScriptStart"); }
        catch(e) { /* optional */ }
        consecutiveConnectFailures = 0;
        // After reconnect, re-evaluate D22 initial state
        d = readDigitalInt(inputPin);
        if (d !== -1) {
          prevModeIsManual = manualOnHigh ? (d === 1) : (d === 0);
          setVar("$Mode", prevModeIsManual ? "Manual" : "Automatic");
          log("Post-reconnect D22 state: " + d + " => Mode: " + (prevModeIsManual ? "Manual" : "Automatic"));
        }
        // continue main loop
        sleep(1500);
        continue;
      } else {
        consecutiveConnectFailures += 1;
        log("Reconnect attempt failed (count=" + consecutiveConnectFailures + ").");
        if (consecutiveConnectFailures >= maxConnectAttempts){
          log("Max reconnect attempts reached. Backing off " + backoffMs + " ms.");
          sleep(backoffMs);
          consecutiveConnectFailures = 0;
        } else {
          sleep(2000);
        }
        continue;
      }
    } // end reconnect handling

    // Normal connected operation: poll D22 for mode changes
    var state = readDigitalInt(inputPin);
    if (state === -1){
      // read failed, skip this cycle
      sleep(500);
      continue;
    }
    var curModeIsManual = manualOnHigh ? (state === 1) : (state === 0);

    if (curModeIsManual !== prevModeIsManual){
      // Mode changed
      if (curModeIsManual){
        // -> Manual
        log("Mode change ->  manual detected (D22=" + state + "). Releasing servos and stopping automatic movement.");
        try { Servo.release(leftServoPort); } catch(e){ log("Servo.release left failed: " + e); }
        try { Servo.release(rightServoPort); } catch(e){ log("Servo.release right failed: " + e); }
        try { Movement.stop(); } catch(e){ log("Movement.stop failed: " + e); }
        setVar("$Mode", "Manual");
        sayEZB("Manual mode enabled");
      } else {
        // -> Automatic
        log("Mode change -> AUTOMATIC detected (D22=" + state + "). Running HB25 Init and resuming automatic control.");
        try { controlCommand(hb25InitScriptName, "ScriptStartWait"); } catch(e){ log("HB25 Init failed: " + e); }
        sleep(250);
        try { controlCommand(hb25MonitorScriptName, "ScriptStart"); } catch(e){ /* optional */ }
        setVar("$Mode", "Automatic");
        sayEZB("Automatic mode enabled");
      }
      prevModeIsManual = curModeIsManual;
    }

    // normal sleep between polls
    sleep(150);
  } catch(err){
    log("Exception in merged loop: " + err);
    sleep(1000);
  }
}

How to autostart this merged Script

  • Add these lines to Connection -> Settings -> Connection Established Scripts (that area uses EZ-Script-style ControlCommand lines). Make sure the skill name you paste matches exactly, including capitalization and spacing. Example: controlCommand("Merged EZB Monitor","ScriptStart") controlCommand("HB25 Init","ScriptStartWait") sleep(500) controlCommand("HB25_D22_Monitor","ScriptStart")

Important notes and follow-ups

  • I cannot generate EZ-Script for you (per policy) - I provide JavaScript. The Connection Established script area accepts simple EZ-Script ControlCommand() lines as shown above; those are tiny one-line commands only.
  • Confirm which D22 polarity corresponds to manual vs Automatic:
    • If D22 HIGH = Manual, set manualOnHigh = true. The default in the sample is manualOnHigh = false (D22 LOW => Manual).
  • Confirm exact skill names and preferred log path and I will personalize the script with those names and path (I'll update the code block for you).
  • If you’d like file rotation (keep logs to a maximum size or number), tell me the desired max size and I’ll add it.
  • If you want the reconnect-only behavior separate from mode-monitoring, I can split them back into two skills or keep merged (merged is convenient).

Anything you want changed now? If you post:

  • the exact names of your HB25 init script and any monitor script,
  • desired log path (or confirm C:\Users\Public\EZB_MergedMonitor.log), I’ll return a copy of the merged script with those values already set so you can paste/run immediately.

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.
#53   — Edited

Athena

I have tried both new scripts. They work but noticed a couple of things:

First, I dont see the LED on D21 I believe, ever lite up when in manual mode anymore.

Second, When I first connect, it use to announce which mode the D22 was in. Also, a couple of times, it would disconnect after I make a switch,the reconnect, and fail to announce which mode it was last in. but would announce it after I flip switch again (but no more disconnects).

Here is one error message I did get while switching between automatic/manal Mode: Manual Monday, December 22, 2025 9:23:15 PM - Mode change -> AUTOMATIC detected (D22=1). Running HB25 Init and resuming automatic control. Monday, December 22, 2025 9:23:18 PM - Mode change -> manual detected (D22=0). Releasing servos and stopping automatic movement. Monday, December 22, 2025 9:23:19 PM - EZB index 0 not connected. Attempting to connect. Done (00:00:12.8651506)

Author Avatar
PRO
Canada
#54  

Thanks - good troubleshooting notes. The behavior you described fits a few small issues we can fix in software, plus a hardware check I’ll remind you about.

Summary of what I changed and why

  • LED D21 never lit: your merged script never set D21. I added explicit Digital.set(d21, ...) so the LED reflects current mode.
  • Initial announce on connect: the merged script set $Mode but didn’t announce at startup/reconnect. I added an initial announce after a successful connection/read.
  • Missed announces after reconnect: Audio.sayEZB() can fail or be meaningless when the EZB is disconnected. I now only call Audio.sayEZB when EZB.isConnected(ezbIndex). If EZB is not connected we fall back to local PC TTS Audio.say().
  • Disconnects right after switching: this can be hardware transients, but adding a short debounce/settle before acting on a mode change reduces false triggers and transient behavior.
  • Robust logging + safer calls: improved try/catch around hardware actions and added log entries for D21 changes.

Please check wiring/ground (hardware reminder)

  • Confirm one common ground between: EZ-B, Pololu multiplexer/receiver ground, HB25 ground. DJ_Sures already recommended this - it’s essential. If switching still causes disconnects, that’s likely a hardware transient or ground/power issue.

Updated Script (paste into a Script skill JavaScript tab)

  • Put this code into the Script skill JavaScript tab (NOT the EZ-Script tab).
  • Edit the top variables to match your exact script names, logging path, and whether D22 HIGH corresponds to Manual.
// Merged EZB Reconnect + D22 Mode Monitor (updated)
// Paste this into a Script skill JavaScript tab (use print() for console)
// === EDIT THESE VALUES TO MATCH YOUR PROJECT ===
var ezbIndex = 0;               // EZB board index
var inputPin = d22;             // SEL input from Pololu (D22)
var ledPin = d21;               // LED indicator pin (D21)
var leftServoPort = d12;        // EZB servo port for left motor
var rightServoPort = d13;       // EZB servo port for right motor

var hb25InitScriptName = "HB25 Init";         // exact script skill name (HB25 neutral)
var hb25MonitorScriptName = "HB25_D22_Monitor"; // optional other monitor skill name

var logFile = "C:\\Users\\Public\\EZB_MergedMonitor.log"; // writable path
var manualOnHigh = false;       // set true if D22 HIGH == Manual, false if D22 LOW == Manual

// Timing & debounce
var pollIntervalMs = 150;
var debounceMs = 150;           // require stable read for this many ms
var reconnectCheckIntervalMs = 3000;
var connectAttemptWaitMs = 4000;
var maxConnectAttempts = 6;
var backoffMs = 15000;

// === Helpers ===
function ts() { return (new Date()).toLocaleString(); }
function log(s) {
  var line = ts() + " - " + s;
  print(line);
  try { File.appendStringLine(logFile, line); } catch(e) { print("Log write failed:" + e); }
}
function say(text) {
  // Prefer EZB TTS when connected, fallback to PC TTS
  try {
    if (EZB.isConnected(ezbIndex)) {
      Audio.sayEZB(text, ezbIndex);
    } else {
      Audio.say(text); // PC speaker TTS fallback
    }
  } catch(e) {
    log("say() failed: " + e);
  }
}
function readDigitalInt(pin) {
  try { return Digital.get(pin) ? 1 : 0; } catch(e) { log("Digital.get failed:" + e); return -1; }
}
function setLedManual(isManual) {
  try {
    // Choose LED behavior: true = LED on in Manual, false = LED on in Automatic.
    // If you prefer the opposite, invert the boolean here.
    var ledOn = isManual; 
    Digital.set(ledPin, ledOn);
    log("D21 set to " + (ledOn ? "ON" : "OFF"));
  } catch(e) {
    log("Digital.set(d21) failed: " + e);
  }
}
function connectionConnectIndex(index) {
  try {
    if (index == 0) controlCommand("Connection", "Connect0");
    else if (index == 1) controlCommand("Connection", "Connect1");
    else if (index == 2) controlCommand("Connection", "Connect2");
    else if (index == 3) controlCommand("Connection", "Connect3");
    else if (index == 4) controlCommand("Connection", "Connect4");
    else controlCommand("Connection", "ConnectAll");
  } catch(e){ log("connectionConnectIndex  ControlCommand failed:" + e); }
}

// === Start ===
log("Merged monitor starting. Log file: " + logFile);

// initialize LED off until we know state
try { Digital.set(ledPin, false); } catch(e){ /* ignore */ }

// read initial D22
var raw = readDigitalInt(inputPin);
var prevModeIsManual;
if (raw == -1) {
  prevModeIsManual = false;
  log("Initial D22 read failed, assuming Automatic.");
  setVar("$Mode", "Automatic");
} else {
  prevModeIsManual = manualOnHigh ? (raw == 1) : (raw == 0);
  setVar("$Mode", prevModeIsManual ? "Manual" : "Automatic");
  setLedManual(prevModeIsManual);
  // Announce initial mode if connected
  if (EZB.isConnected(ezbIndex)) {
    say("Initial mode: " + (prevModeIsManual ? "Manual" : "Automatic"));
  } else {
    log("EZB not connected at startup; initial announce skipped (will announce on reconnect).");
  }
}

// main loop
var consecutiveConnectFailures = 0;
while (true) {
  try {
    // Reconnect handling
    if (!EZB.isConnected(ezbIndex)) {
      log("EZB index " + ezbIndex + " not connected. Attempting to connect.");
      connectionConnectIndex(ezbIndex);
      sleep(connectAttemptWaitMs);
      if (EZB.isConnected(ezbIndex)) {
        log("EZB reconnected. Running HB25 Init and resuming monitors.");
        try { controlCommand(hb25InitScriptName, "ScriptStartWait"); } catch(e) { log("HB25 Init ScriptStartWait failed:" + e); }
        try { controlCommand(hb25MonitorScriptName, "ScriptStart"); } catch(e) { /* optional */ }
        consecutiveConnectFailures = 0;
        // after reconnect: read D22 and announce
        raw = readDigitalInt(inputPin);
        if (raw != -1) {
          prevModeIsManual = manualOnHigh ? (raw == 1) : (raw == 0);
          setVar("$Mode", prevModeIsManual ? "Manual" : "Automatic");
          setLedManual(prevModeIsManual);
          say("Reconnected. Mode is " + (prevModeIsManual ? "Manual" : "Automatic"));
        } else {
          log("D22 read failed after reconnect.");
        }
        sleep(1500);
        continue;
      } else {
        consecutiveConnectFailures += 1;
        log("Reconnect attempt failed (count=" + consecutiveConnectFailures + ").");
        if (consecutiveConnectFailures >= maxConnectAttempts) {
          log("Max reconnect attempts reached. Backing off " + backoffMs + " ms.");
          sleep(backoffMs);
          consecutiveConnectFailures = 0;
        } else {
          sleep(2000);
        }
        continue;
      }
    }

    // Normal connected operation: poll D22 with debounce
    var s1 = readDigitalInt(inputPin);
    if (s1 == -1) { sleep(200); continue; }

    // Debounce check: require stable value for debounceMs
    var stable = true;
    var t0 = Date.now();
    while (Date.now() - t0 < debounceMs) {
      var s2 = readDigitalInt(inputPin);
      if (s2 == -1 || s2 != s1) { stable = false; break; }
      sleep(10);
    }
    if (!stable) { sleep(pollIntervalMs); continue; }

    var curModeIsManual = manualOnHigh ? (s1 == 1) : (s1 == 0);
    if (curModeIsManual !== prevModeIsManual) {
      // Mode changed - give hardware a short settle (Pololu switching transient)
      sleep(200);

      if (curModeIsManual) {
        // -> Manual
        log("Mode change ->  manual detected (D22=" + s1 + "). Releasing servos and stopping automatic movement.");
        try { Servo.release(leftServoPort); } catch(e) { log("Servo.release left failed:" + e); }
        try { Servo.release(rightServoPort); } catch(e) { log("Servo.release right failed:" + e); }
        try { Movement.stop(); } catch(e) { log("Movement.stop failed:" + e); }
        setVar("$Mode", "Manual");
        setLedManual(true);
        say("Manual mode enabled");
      } else {
        // -> Automatic
        log("Mode change -> AUTOMATIC detected (D22=" + s1 + "). Running HB25 Init and resuming automatic control.");
        try { controlCommand(hb25InitScriptName, "ScriptStartWait"); } catch(e) { log("HB25 Init failed:" + e); }
        sleep(250);
        try { controlCommand(hb25MonitorScriptName, "ScriptStart"); } catch(e) { /* optional */ }
        setVar("$Mode", "Automatic");
        setLedManual(false);
        say("Automatic mode enabled");
      }
      prevModeIsManual = curModeIsManual;
    }

    sleep(pollIntervalMs);
  } catch (err) {
    log("Exception in merged loop: " + err);
    sleep(1000);
  }
}

What I changed (concise)

  • setLedManual() toggles D21 for visible LED feedback.
  • Debounce: require stable D22 reading before acting.
  • Announce initial mode on startup and after reconnect (only if EZB connected).
  • Safe/silent fallback to PC TTS when EZB not connected.
  • Short settle delay after a mode change to avoid transient effects.

Next steps / suggestions

  1. Confirm LED polarity: does D21 LED turn ON when you set it HIGH? If it’s active-low, tell me and I’ll invert the led logic for you.
  2. Try the updated script and flip the Pololu SEL switch a few times. Watch the log file at C:\Users\Public\EZB_MergedMonitor.log (or change path at top).
  3. If you still see disconnects right after switching, please tell me:
    • Does the disconnect always happen the instant you flip the Pololu switch or only sometimes?
    • Do you see any power dips or servo twitching? (That would indicate a hardware transient.)
  4. If disconnects persist, briefly test with the Pololu disconnected (force mode selection manually) just to confirm if the Pololu switching is the trigger - that helps isolate hardware vs software.

Want me to invert LED logic, change the LED-on-mode (Manual vs Automatic), or move the log file location? Also say if you want this script split back into two skills (Reconnect + Mode Monitor) instead of merged.


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

ok, so the program is somewhat working, I think I know why.

In the connection skill, I have this: (which is an EZBscript)

controlCommand("EZB Reconnect Monitor ", "ScriptStart") controlCommand("HB25 Init", "ScriptStartWait") sleep(500) #wait a bit, or remove if you don't need a delay controlCommand("EZB_D22_ConnMerge", "ScriptStart")

As you can see, on the first line is the EZB reconnector monitor script controlcommand. Is this script still needed? Isn't this script now part of the merged EZ Reconnect + D22 Monitor script above? Will this also explain why when I want to disconnect from EZB, the program immediately reconnects. So I have wait a couple seconds to click disconnect  again and then it stays disconnected.

Finally, the LED does in fact lite up in manual more as I want, but is not blinking, lets make it blink againc

Author Avatar
PRO
Canada
#56  

Yes - that explains the behavior and is an easy fix. Short answers first, then exact steps and a paste-ready JavaScript update.

Summary

  • Do you still need the old "EZB Reconnect Monitor" script? No - if you’re using the merged EZB Reconnect + D22 Mode Monitor script, the old reconnect monitor does the same job and will cause duplicated reconnect behavior if both are started. Remove or stop the older script from auto-starting.
  • Why you see an immediate reconnect after you click Disconnect: the other (old) monitor is still running (or being auto-started) and it detects the EZ-B is disconnected, then immediately attempts to reconnect. Removing the duplicate autostart will stop the immediate reconnect.
  • LED blinking: your LED was being set only once. I’ll give you a small change to make it blink while in manual mode and be steady/off in Automatic.

What to change now (step-by-step)

  1. Edit Connection -> Settings -> Connection Established Scripts (this area takes one-line ControlCommand() commands)

    • Remove (or comment out) the line that starts the old reconnect monitor: controlCommand("EZB Reconnect Monitor ","ScriptStart") (Note: remove trailing spaces from skill names - use exact skill name text as in the skill window)
    • Keep only the merged script autostart lines (example): controlCommand("Merged EZB Monitor","ScriptStart") controlCommand("HB25 Init","ScriptStartWait") sleep(500)
    • This ensures only your merged monitor handles reconnects and mode monitoring.
  2. If you want a way to temporarily disable automatic reconnects (so clicking Disconnect stays disconnected), add a global flag and the guard to the merged script. I recommend this so you can control reconnect behavior from a button or quick script.

Updated merged script (adds reconnect guard + blinking LED)

  • Put this into your Script skill JavaScript tab (NOT EZ-Script).
  • Edit the top variables for your exact skill names and file path.
  • This version:
    • Ignores reconnect attempts if $AllowAutoReconnect is "0".
    • Blinks D21 while in manual mode (steady off in Automatic).
    • Debounces D22 changes and announces on reconnect/start.

Paste-ready JavaScript (replace values at top as needed)

// Merged EZB Reconnect + D22 Mode Monitor (with reconnect guard + blinking D21)
// Paste into a Script skill JavaScript tab. Use print() for console output.

// === EDIT THESE VALUES FOR YOUR PROJECT ===
var ezbIndex = 0;                 // EZ-B index
var inputPin = d22;              // SEL input from Pololu (D22)
var ledPin = d21;                // LED indicator pin (D21)
var leftServoPort = d12;         // EZB servo port left
var rightServoPort = d13;        // EZB servo port right
var hb25InitScriptName = "HB25 Init";      // HB25 Init script name (exact)
var hb25MonitorScriptName = "HB25_D22_Monitor";// optional
var logFile = "C:\\Users\\Public\\EZB_MergedMonitor.log"; // writable path
var manualOnHigh = false;        // true if D22 HIGH == Manual, false if D22 LOW == Manual
// =========================================

// Timing & debounce
var pollIntervalMs = 150;
var debounceMs = 150;
var blinkIntervalMs = 500;      // LED blink interval when in Manual
var connectAttemptWaitMs = 4000;
var maxConnectAttempts = 6;
var backoffMs = 15000;

// Helpers
function ts(){ return (new Date()).toLocaleString(); }
function log(s){
  var line = ts() + " - " + s;
  print(line);
  try { File.appendStringLine(logFile, line); } catch(e) { print("Log write failed: " + e); }
}
function say(text){
  try {
    if (EZB.isConnected(ezbIndex)) Audio.sayEZB(text, ezbIndex);
    else Audio.say(text);
  } catch(e){ log("say() failed: " + e); }
}
function readDigitalInt(pin){
  try { return Digital.get(pin) ? 1 : 0; }
  catch(e){ log("Digital.get failed: " + e); return -1; }
}
function connectionConnectIndex(index){
  try {
    // Adjust if you use different Connection commands
    if(index == 0) controlCommand("Connection","Connect0");
    else if(index == 1) controlCommand("Connection","Connect1");
    else if(index == 2) controlCommand("Connection","Connect2");
    else if(index == 3) controlCommand("Connection","Connect3");
    else if(index == 4) controlCommand("Connection","Connect4");
    else controlCommand("Connection","ConnectAll");
  } catch(e){ log("connectionConnectIndex  ControlCommand failed: " + e); }
}

// Start
log("Merged monitor starting. Log file: " + logFile);

// Initialize LED OFF
try { Digital.set(ledPin, false); } catch(e){}

// Read initial D22
var raw = readDigitalInt(inputPin);
var prevModeIsManual;
if (raw == -1) {
  prevModeIsManual = false;
  setVar("$Mode", "Automatic");
  log("Initial D22 read failed, assuming Automatic.");
} else {
  prevModeIsManual = manualOnHigh ? (raw == 1) : (raw == 0);
  setVar("$Mode", prevModeIsManual ? "Manual" : "Automatic");
  log("Initial D22 state: " + raw + " => Mode: " + (prevModeIsManual ? "Manual" : "Automatic"));
  // Announce if connected
  if (EZB.isConnected(ezbIndex)) say("Initial mode: " + (prevModeIsManual ? "Manual" : "Automatic"));
}

var consecutiveConnectFailures = 0;
var lastBlinkTime = Date.now();
var ledState = false;

while(true){
  try {
    // Reconnect handling with guard
    if (!EZB.isConnected(ezbIndex)) {
      // Respect a global flag to allow/halt auto reconnect
      var allow = getVar("$AllowAutoReconnect", "1");
      if (allow !== "1") {
        log("Auto-reconnect disabled ($AllowAutoReconnect != 1). Skipping reconnect.");
        sleep(2000);
      } else {
        log("EZB index " + ezbIndex + " not connected. Attempting to connect.");
        connectionConnectIndex(ezbIndex);
        sleep(connectAttemptWaitMs);
        if (EZB.isConnected(ezbIndex)) {
          log("EZB reconnected. Running HB25 Init and resuming monitors.");
          try { controlCommand(hb25InitScriptName, "ScriptStartWait"); }
          catch(e){ log("HB25 Init ScriptStartWait failed: " + e); }
          try { controlCommand(hb25MonitorScriptName, "ScriptStart"); } catch(e){}
          consecutiveConnectFailures = 0;
          // post-reconnect read D22 and announce
          raw = readDigitalInt(inputPin);
          if (raw != -1) {
            prevModeIsManual = manualOnHigh ? (raw == 1) : (raw == 0);
            setVar("$Mode", prevModeIsManual ? "Manual" : "Automatic");
            log("Post-reconnect D22 state: " + raw + " => Mode: " + (prevModeIsManual ? "Manual" : "Automatic"));
            // Ensure LED state is set immediately
            try { Digital.set(ledPin, prevModeIsManual ? true : false); } catch(e){}
            say("Reconnected. Mode is " + (prevModeIsManual ? "Manual" : "Automatic"));
          } else {
            log("D22 read failed after reconnect.");
          }
          sleep(1500);
          continue;
        } else {
          consecutiveConnectFailures += 1;
          log("Reconnect attempt failed (count=" + consecutiveConnectFailures + ").");
          if (consecutiveConnectFailures >= maxConnectAttempts) {
            log("Max reconnect attempts reached. Backing off " + backoffMs + " ms.");
            sleep(backoffMs);
            consecutiveConnectFailures = 0;
          } else { sleep(2000); }
          continue;
        }
      } // end allow check
    } // end not connected block

    // Normal connected operation: poll D22 with debounce
    var s1 = readDigitalInt(inputPin);
    if (s1 == -1) { sleep(200); continue; }

    // Debounce - require stable read for debounceMs
    var stable = true;
    var t0 = Date.now();
    while (Date.now() - t0 < debounceMs) {
      if (readDigitalInt(inputPin) != s1) { stable = false; break; }
      sleep(10);
    }
    if (!stable) { sleep(pollIntervalMs); continue; }

    var curModeIsManual = manualOnHigh ? (s1 == 1) : (s1 == 0);
    if (curModeIsManual !== prevModeIsManual) {
      if (curModeIsManual) {
        // -> Manual
        log("Mode change ->  manual detected (D22=" + s1 + "). Releasing servos and stopping automatic movement.");
        try { Servo.release(leftServoPort); } catch(e){ log("Servo.release left failed: " + e); }
        try { Servo.release(rightServoPort); } catch(e){ log("Servo.release right failed: " + e); }
        try { Movement.stop(); } catch(e){ log("Movement.stop failed: " + e); }
        setVar("$Mode", "Manual");
        // LED will be blinking (handled below)
        say("Manual mode enabled");
      } else {
        // -> Automatic
        log("Mode change -> AUTOMATIC detected (D22=" + s1 + "). Running HB25 Init and resuming automatic control.");
        try { controlCommand(hb25InitScriptName, "ScriptStartWait"); } catch(e){ log("HB25 Init failed: " + e); }
        sleep(250);
        try { controlCommand(hb25MonitorScriptName, "ScriptStart"); } catch(e){}
        setVar("$Mode", "Automatic");
        // LED steady/off handled below
        say("Automatic mode enabled");
      }
      prevModeIsManual = curModeIsManual;
    }

    // LED behavior: blink when Manual, steady OFF when Automatic
    var now = Date.now();
    if (prevModeIsManual) {
      if (now - lastBlinkTime >= blinkIntervalMs) {
        ledState = !ledState;
        try { Digital.set(ledPin, ledState); } catch(e){ log("Digital.set LED failed: " + e); }
        lastBlinkTime = now;
      }
    } else {
      // ensure LED is OFF in Automatic (change to ON if you want it ON in Auto)
      if (ledState) {
        ledState = false;
        try { Digital.set(ledPin, false); } catch(e){ log("Digital.set LED failed: " + e); }
      }
    }

    sleep(pollIntervalMs);

  } catch(err) {
    log("Exception in merged loop: " + err);
    sleep(1000);
  }
}

How to use the $AllowAutoReconnect guard

  • To allow automatic reconnects (default) set: setVar("$AllowAutoReconnect","1")
  • To temporarily disable auto-reconnect (so a manual Disconnect stays disconnected): setVar("$AllowAutoReconnect","0") You can run these small setVar lines from any Script skill (JavaScript) or via a UI button.

Next steps and checks

  • Remove the old "EZB Reconnect Monitor" autostart line from Connection Established Scripts so you don’t have two monitors running.
  • Confirm D21 polarity: above code assumes HIGH = LED ON. You said LED lights in manual now - perfect. If your LED is active-low, tell me and I’ll invert the logic.
  • Test: flip Pololu SEL/ch5 several times and watch the log file. If you still see disconnects immediately after switching, try:
    • Verify common ground again between EZB, receiver/Pololu ground, and HB25 ground.
    • Power-check: watch for dropouts or buzzes during the switch (this would indicate a power transient).
  • If you want, I can paste a version that:
    • Uses LED steady ON in manual and steady OFF in Auto (instead of blink), or
    • Uses a LED blink pattern (e.g., long blink for Manual, short blink for Auto), or
    • Splits the merged script back into separate Reconnect and Mode Monitor skills.

Would you like me to:

  • Provide the single-line Connection Established Scripts to paste in (I can show exactly which lines to keep and which to remove)?
  • Invert the LED logic or change the blink pattern?
  • Provide a small test script you can run to toggle $AllowAutoReconnect manually?

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.