Auto Position (Gait) icon Auto Position (Gait) Create servo frames and actions to animate gaits and gestures with automatic motion planning, software ramping, movement panel, import/export Try it →
Asked

Here Are 5 Subject Line Options, 4-5 Words Each:1.…

I have a question about adding an action to an existing script I have that is written in JavaScript for ARC.

I currently have two different ARC JavaScript scripts that monitor ADC ports on a single EZB (hardware connected to the EZB; ARC sends commands and the EZB reads the ADC values).

Summary of the current scripts:

  • Script A watches ADC port 0 (a micro switch inside the robot that detects when the robot’s fake power pack is inserted or removed). When the switch detects the power pack is pulled, the script triggers a sequence of ARC control commands (using ControlCommand, Digital, Servo, etc.), then pauses until the switch is released (power pack inserted) and runs the insert sequence. Practically, this simulates a visual robot power down while the EZB/robot remains powered. The script uses ADC.waitForHigher(0, 200) to detect the pack being pulled and ADC.waitForLower(0, 120) to detect insertion; it behaves like a toggle based on thresholds (start above 200, end below 160).
  • Script B watches ADC ports 1-7 (momentary buttons) and maps each port to an action (animations, sounds, script starts). This script is written as a single ADC watcher that polls multiple ADC ports and invokes functions stored in an actions[] array. It works very well and reduces ADC read load by consolidating reads.

My goal:

  • I want to merge the functionality of Script A (the micro switch toggle on ADC 0) into Script B so that a single ADC watcher script handles ADC 0..7 and reduces ADC read traffic. The toggle behavior for ADC 0 must be preserved (i.e., it should continue to watch for the pull/insert threshold behavior rather than acting like a momentary button).
  • Alternatively, would it be better to keep Script A as the dedicated ADC 0 toggle watcher and have Script B (the multi-button watcher) call or be toggled by Script A? I’m not sure which design is better for performance or reliability in ARC.

Notes and clarifications:

  • The second script already has an actions[] array and an entry actions[0] = function() (currently a placeholder). I understand that actions[0] could contain the toggle/power-pack logic, but I’m not sure of the best way to preserve the current toggle behavior (with waits and thresholds) inside the single ADC poll/debounce loop.
  • Both scripts are JavaScript Robot Skills running inside an ARC project. Script A uses ADC.waitForHigher/Lower blocking calls and loops, while Script B uses a polled ADC.get approach with debounce logic. I want to know if these two read methods can be combined in one script without breaking the toggle semantics.
  • I will attach both scripts below for reference and would appreciate suggestions on the best approach to handle this mixed setup: merge into one ADC watcher (with special-case handling for ADC 0 toggle) or keep the power-pack toggle as a separate watcher and have the multi-button skill trigger/coordinate with it.

I'll attach both scripts and hope for help. Thanks in advance for suggestions on the best way to handle this mixed bag.

Here is the first script that watches ADC port "0":

/*This script waits till Power Pack is pulled. Once pulled the pullPowerPack()function
will be called and run commands to make robot go dark and bend over. 
Then this script will stop and wait till Power Pack is inserted. Then it will call the 
insertPowerPack() function. The robot will power back up and stand up. 
This script will then wait again untill power pack is pulled again and power down.
This will continue to loop with each powerpack pull or insert or untll robot is powered down. 
*/

while (true) {
print("Waiting for power pack to be pulled...")
// Wait for ADC value to indicate the power pack is pulled
ADC.waitForHigher(0, 200)
// Perform actions after the power pack is pulled
pullPowerPack();

print("Waiting for power pack to be inserted...")
// Wait for ADC value to indicate the power pack is inserted
ADC.waitForLower(0, 120);
// Perform actions after the power pack is inserted
insertPowerPack();
}

function pullPowerPack(){
var conn_0 = EZB.isConnected(0);
if (conn_0 == true) { //Only proceed if EZB 0 is connected. Till trigger script if not. 
ControlCommand("Personality Generator", "PauseOn");
sleep(100);
ControlCommand("Script Manager", "ScriptStart", "Mechalical Background Sounds - Stop");
ControlCommand("Auto Both Arms", "AutoPositionAction", "Both Ars Out");
sleep(100);
ControlCommand("Leg Section", "ScriptStart", "Hip Down Full");
sleep(100);
ControlCommand("Head Section", "ScriptStart", "Bubble Full Down");
sleep(500)
Digital.set(D8, true); //KIll Brain Lights
controlCommand("ADC Watcher", "ScriptStop");
ControlCommand("Weather Loop", "ScriptStop");

} else {
print("Reconnect EZB Connection 0 or restart Robot")
 halt() //EZB 0 is not connected. Till trigger script if not connected.
 
 }

// Wait for the   Auto Position status to change to a 1
while (getVar("$Both_ARS_Extended")== 0) {
   sleep(100);
}
ControlCommand("Auto Both Arms", "AutoPositionAction", "Dead_Down");
sleep(200);
Servo.release(D1, 2) //Release RT Up and Down Wrist Servo
Servo.release(D0, 2) //Release RT Side to Side Servo
Servo.release(D4, 2) //Release Lft Up and Down Wrist Servo
Servo.release(D5, 2) //Release Lft Side to Side Servo

}

function insertPowerPack() {
ControlCommand("Script Manager", "ScriptStart", "Mechalical Background Sounds - Loop");
ControlCommand("Leg Section", "ScriptStart", "Up with Hips");
sleep(500);
ControlCommand("Head Section", "ScriptStart", "Bubble Up and Down");
sleep(500);
Digital.set(D0, true) //Turn on Ear sensor Motors
Digital.set(D8,false) //Turn on Brain lights (Kill Brain Lights)
Digital.set(D10,true) //Turn on Fast Brain lights
Digital.set(D2,true) //Turn on scanner lights
PWM.set(D9,50); //Turn on Crown Motor
sleep(4000);
Digital.set(D2,false) //Turn off scanner lights
sleep(500);
Digital.set(D4,true) //Turn on Box lights
ControlCommand("Head Section", "ScriptStart", "Radar 1/4 Sweep");
ControlCommand("Auto Both Arms", "AutoPositionAction", "Both Ars In");
sleep(5000);
while (getVar("$Lft_Elbow_Adjusting")== 1) {
   sleep(100);
}

//Turn off and return to standby
//--------------------------------------------------
ControlCommand("Head Section", "ScriptStart", "Bubble Up and Down");
Digital.set(D0,false); //Turn Off Ear sensor motors.
Digital.set(D10, false); //Turn off fast brain lights
Digital.set(D4,false) //Turn off Box lights
PWM.set(D9, 0); //Turn off crown motor
controlCommand("ADC Watcher", "ScriptStart");
ControlCommand("Weather Loop", "ScriptStart");
ControlCommand("Personality Generator", "PauseOff");
}

Here's the second script I want to keep and add to it what the first script does. That is unless it's better to keep the first script and just let this second one toggle it. helped me write this one.

// Single ADC Watcher with debounce and actions
// Configure these:
var adcPorts = [0,1,2,3,4,5,6,7]; // ADC ports used (A0..A7 are ADC0..ADC7)
var threshold = 170;               // value above this is considered "pressed"
var pollMs = 600;                  // how often to poll (milliseconds)
var debounceMs = 150;              // how long value must be stable to accept
var ezbIndex = 0;                  // which EZB index this script is monitoring

// Map port index -> action function name or control commands
// Replace with your specific actions. Format: actions[port] = function() { ... }
var actions = [];
actions[0] = function(){ //Power Pack Pull Animation (Micro Switch is inside robot torso behind Power Pack)
 sleep(3000);
 ControlCommand("Soundboard 2","Track_26"); // Say "Not"
  sleep(600);
  ControlCommand("Soundboard v4","Track_20"); // Say "Active"
};

actions[1] = function(){ //Chest Button: Gamma 202 - Power Down Robot Compleatly
ControlCommand("Personality Generator", "PauseOn");  //Keeps personality from activating
sleep(6250);
ControlCommand("Shutdown PC", "ScriptStart");
}

actions[2] = function(){ //Chest Button: Gamma 101 - Personality Toggle On and Off
var personality = getVar("$Personality")
sleep(200)
if (personality == 0) { //0 means Personality is running. 
turnOffPersonality()
}else if (personality == 1) {//1 means Personality is Paused. 
turnOnPersonality()
}
}
function turnOffPersonality(){
//Personality toggle Momentary button (Gamma 101) was pushed. Will turn Persponality Off (PauseOn).
ControlCommand("Personality Generator", "PauseOn");
sleep(4000); //Holds shript here until ADC reading returns to zero before switching to other function
ControlCommand("Soundboard v4", "Track_24"); //Will do "Raspberries" sound
print("Personality is Off");
setVar( "$Personality", 1); //Sets Global Variable so next button push will turn Personalilty On
}
function turnOnPersonality(){
//Personality toggle Momentary button (Gamma 101) was pushed. Will turn Persponality On (PauseOff)
// Perform actions after Personality toggle button (Gamma 101) was pushed
ControlCommand("Personality Generator", "Pauseoff");
sleep(4000); //Holds shript here until ADC reading returns to zero before switching to other function
ControlCommand("Soundboard v4", "Track_23"); //Say "Green and GO"
print("Personality is On");
setVar( "$Personality", 0); //Sets Global Variable so next button push will turn Personalilty Off
}

actions[3] = function(){ //Chest Button: Vector Hold -
ControlCommand("Personality Generator", "PauseOn");  //Keeps personality from activating. 
setVar( "$Personality", 1); //Sets Global Variable so next button push will turn Personalilty off 
 sleep(6000);
 ControlCommand("Soundboard 2","Track_26"); // Say "Not"
  sleep(600);
  ControlCommand("Soundboard v4","Track_20"); // Say "Active"
ControlCommand("Personality Generator", "PauseOff");
setVar( "$Personality", 0); //Sets Global Variable so next button push will turn Personalilty on
}

actions[4] = function(){ //Chest Button: Alpha 202 - 
ControlCommand("Personality Generator", "PauseOn");  //Keeps personality from activating.
setVar( "$Personality", 1); //Sets Global Variable so next button push will turn Personalilty off
 sleep(6000);
 ControlCommand("Soundboard 2","Track_26"); // Say "Not"
  sleep(600);
  ControlCommand("Soundboard v4","Track_20"); // Say "Active"
  ControlCommand("Personality Generator", "PauseOff");
  setVar( "$Personality", 0); //Sets Global Variable so next button push will turn Personalilty Oon
}

actions[5] = function(){  //Chest Button: Bata 202 - Speak Time, Date and Enviromental Report
ControlCommand("Personality Generator", "PauseOn");  //Keeps personality from activating. Called script will unpause. 
setVar( "$Personality", 1); //Sets Global Variable so next button push will turn Personalilty off
sleep(6000); //Pause script to let robot speak confirmation of button push
ControlCommand("Time, Date & Temp", "ScriptStart", "Time,Date,Enviroment");
}

actions[6] = function() { //Chest Button: Detent + 
ControlCommand("Personality Generator", "PauseOn");  //Keeps personality from activating.
setVar( "$Personality", 1); //Sets Global Variable so next button push will turn Personalilty off
sleep(6000); 
 ControlCommand("Soundboard 2","Track_26"); // Say "Not"
  sleep(600);
  ControlCommand("Soundboard v4","Track_20"); // Say "Active"
  ControlCommand("Personality Generator", "PauseOff");
  setVar( "$Personality", 0); //Sets Global Variable so next button push will turn Personalilty on
}

actions[7] = function(){ //Chest Button: Vector Release - Run Soil Sampler & Speak Report
 ControlCommand("Personality Generator", "PauseOn");  //Keeps personality from activating.
 setVar( "$Personality", 1); //Sets Global Variable so next button push will turn Personalilty off
  Digital.set(D0, true) //Turn on Ear Motors
  Digital.set(D23, true, 1) //Trun on Soil Samper Relay
  Digital.set(D2, true) //Turn on Scanner chect lights
  Digital.set(D10, true) //Turn on fast brain lights
  sleep(2000)
  Digital.set(D23, false, 1) //Turn off Soil Sampler Relay
  ControlCommand("Leg Section", "ScriptStart", "Hip Center")
  sleep(0250)
  ControlCommand("Leg Section", "ScriptStart", "Hip Down Full")
  sleep(6000)
  ControlCommand("Leg Section", "ScriptStart", "Up with Hips")
  sleep(3500)
  ControlCommand("Head Section", "ScriptStart", "Bubble Up and Down")
  sleep(0250)
  Digital.set(D2, false) //Turn off Scanner chect lights
  sleep(10000)
  Digital.set(D0, false) //Turn off Ear motors
  Digital.set(D10, false) //Turn off fast brain lights
  ControlCommand("Personality Generator", "PauseOff");
  setVar( "$Personality", 0); //Sets Global Variable so next button push will turn Personalilty on
}
// Add similar actions for other ADC ports if needed...

// Internal state
var lastState = [];         // boolean pressed/unpressed
var stableStart = [];       // timestamp ms when current state started
for(var i=0;i
// helper
function nowMs(){ return (new Date()).getTime(); }

print("ADC Watcher: start. Polling " + adcPorts.length + " ports every " + pollMs + "ms");

while(true){
  try{
    for(var i=0;i      var port = adcPorts[i];
      // Read ADC once per loop
      var value = ADC.get(port, ezbIndex);
      var pressed = (value >= threshold);

      if(pressed !== lastState[i]){
        // state changed - start a debounce timer if not already started
        if(stableStart[i] === 0){
          stableStart[i] = nowMs();
        } else {
          // check if we've been stable long enough
          if(nowMs() - stableStart[i] >= debounceMs){
            // accept new state
            lastState[i] = pressed;
            stableStart[i] = 0;
            var t = (new Date()).toLocaleString();
            print(t + " ADC" + port + " state -> " + (pressed ? "PRESSED" : "RELEASED") + " val:" + value);
            if(pressed){
              // call action for this port if defined
              try{
                if(typeof actions[i] === "function"){
                  actions[i]();
                } else {
                  print("ADC Watcher: no action defined for port "+port);
                }
              }catch(ae){
                print("ADC Watcher: action error: " + ae);
              }
            }
          }
        }
      } else {
        // state unchanged - reset debounce timer
        stableStart[i] = 0;
      }
    }
  }catch(e){
    print("ADC Watcher: error reading ADC: " + e);
  }
  sleep(pollMs);
}


Related Hardware (view all EZB hardware)
EZ-B V4 by EZ-Robot
EZ-B v4/2 robot controller: dual Cortex ARM, Wi-Fi, audio/video, 24 servo/digital ports, I2C/UART, camera and Synthiam ARC control for custom robots
Wi-Fi / USB
Servos 24
Camera
Audio
UART 3
I2C
ADC 8
Digital 24

Related Robot Skills (view all robot skills)
Script by Synthiam
Multi-language ARC Script: build, run and debug Blockly, JavaScript, EZ-Script or Python with Intellisense, run/save/load and Roboscratch support
Read Digital by Synthiam
Read TTL digital input from an ARC I/O port; real-time red/green status for low (0V) or high (+3.3/5V), selectable board/port and read interval.

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
Canada
Twitter YouTube GitHub
#1  

You can combine them, but I would not merge the ADC.waitForHigher()/waitForLower() toggle logic into the same polling loop as a normal momentary-button handler unless you rework the toggle as its own state machine.

Best design for ARC

Keep ADC 0 (power pack toggle) as a separate dedicated watcher, and keep your ADC 1-7 polling script as the multi-button action router.

That gives you:

  • Cleaner logic
  • Less risk of breaking the pull/insert semantics
  • Better reliability since the power-pack sequence has long waits and multiple robot-state transitions
  • Easier debugging if the robot ever fails mid-sequence

Why I would not fully merge them

Your current Script A is event-driven by threshold crossing:

  • wait for higher than 200 = pack pulled
  • wait for lower than 120 = pack inserted

That is not the same as a standard "pressed/released" button.
It has a wide hysteresis band, which is exactly what makes it stable.

Your Script B is a poll/debounce dispatcher. That works well for buttons, but it is a different model. If you try to mix both behaviors in one loop, you’ll need:

  • separate state tracking for ADC 0
  • separate threshold enter/exit states
  • protection so the long power-down routine doesn’t block the rest of the buttons

In other words, it becomes more complex than two scripts, not less.


My practical recommendation

Option 1 - Best overall

  • Keep Script A for ADC 0 power-pack logic
  • Keep Script B for ADC 1-7 button actions
  • If desired, make Script B’s ADC 0 entry just call a shared function or trigger a separate script

This is usually the most maintainable setup in ARC.

Option 2 - Single script, but only if you really want one file

If you want one script, then treat ADC 0 as a special case state machine with hysteresis, and treat ADC 1-7 as momentary debounced buttons.

That means:

  • ADC 0 uses:
    • pulled when value > 200
    • inserted when value < 120
  • ADC 1-7 use:
    • pressed when value >= threshold
    • release/debounce logic

But again, this is more code and more chance for a stuck state.


A cleaner ARC approach

If you want Script B to coordinate with Script A, I’d suggest this pattern:

  • Script B handles all button polling
  • When ADC 0 is detected as "pulled," Script B starts a separate power-pack script
  • That power-pack script owns the full pull/insert cycle

This keeps the long-running loop isolated.

Example idea:

ControlCommand("Power Pack Script", "ScriptStart");

That way the multi-button watcher never gets blocked by the power-pack sequence.


Important issue in your current Script B

Your current ADC watcher has a couple structural issues:

  • actions[0] is currently just a placeholder and does not implement hysteresis
  • The script is using a single threshold model, which is fine for buttons but not enough for the power-pack toggle
  • The posted code looks like it may have copy/paste formatting issues:
    • several missing semicolons
    • some malformed if statements
    • Pauseoff should be PauseOff
    • controlCommand() vs ControlCommand() consistency
    • EZB.isConnected(0) is correct, but make sure the rest of the code is syntactically valid JavaScript

If you want to combine them anyway

Use this structure:

  • ADC 0
    • state: waitingForPull / waitingForInsert
    • thresholds:
      • pull: > 200
      • insert: < 120
  • ADC 1-7
    • debounce as you already do
    • action on press only

Pseudo-structure:

var powerState = "waitingForPull";

while (true) {
  var v0 = ADC.get(0, ezbIndex);

  if (powerState == "waitingForPull" && v0 > 200) {
    powerState = "waitingForInsert";
    pullPowerPack();
  } else if (powerState == "waitingForInsert" && v0 < 120) {
    powerState = "waitingForPull";
    insertPowerPack();
  }

  // then handle ADC 1-7 polling/debounce here
  sleep(pollMs);
}

But note: if pullPowerPack() or insertPowerPack() takes a long time, it will delay button handling unless you split the scripts.


My recommendation in one sentence

Keep the power-pack toggle as its own dedicated script, and let your multi-button ADC watcher stay focused on ADC 1-7. That is the most reliable ARC design.


If you want, I can do either of these next:

  1. Rewrite your Script B into a clean JavaScript ARC version
  2. Convert Script A into a polling/state-machine version
  3. Show you how to split this into two scripts that coordinate cleanly using ControlCommand()

If you want, paste the cleaned version of Script B and I’ll help refactor it into something ARC-friendly.


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

Ok, this all makes sense and helps. If the second script polling adc ports doesn't overload the pipeline then I'll keep both scripts and not merge the two.

So, can you help me with a few of the items that you mentioned?

In script B can you clean up the things you mentioned?:

copy/paste formatting issues: several missing semicolons some malformed if statements Pauseoff should be PauseOff ControlCommand() vs ControlCommand() consistency EZB.isConnected(0) is correct, but make sure the rest of the code is syntactically valid JavaScript.

Also, please disable and remove script B's polling of the "0" adc port. I'll have script A do that as you suggested.

You mentioned rewriting Script A into a polling/state-machine version. I thought it already was that. Maybe I'm confused with this definition. If so, could you do that if it means it follows your suggestion of two separate polling scripts?

Author Avatar
PRO
Canada
Twitter YouTube GitHub
#3  

Absolutely - keeping two separate scripts is a solid ARC design here.

About your question on Script A

Your current Script A is not really a polling/state-machine script. It uses:

  • ADC.waitForHigher()
  • ADC.waitForLower()

That means it blocks and waits for a value change instead of continuously polling in a loop.

A true polling/state-machine version would look more like:

  • read ADC 0 every loop
  • remember current state
  • detect transition from inserted pulled
  • run the pull sequence once
  • then wait for pulled inserted
  • run the insert sequence once

So yes, I can rewrite it that way if you want. But you do not need to change Script A unless you specifically want it to behave like Script B.

First: cleaned-up Script B

Below is a cleaned JavaScript version of your Script B with:

  • formatting fixed
  • semicolons added
  • PauseOff capitalization corrected
  • ControlCommand() used consistently
  • ADC 0 removed from polling
  • syntax cleaned up for ARC JavaScript

I also preserved your action structure and left the button behaviors intact.

// Single ADC Watcher with debounce and actions
// Polls ADC 1-7 only. ADC 0 is handled by Script A.

var adcPorts = [1, 2, 3, 4, 5, 6, 7]; // ADC ports used (ADC0-ADC7)
var threshold = 170; // value above this is considered "pressed"
var pollMs = 600; // how often to poll (milliseconds)
var debounceMs = 150; // how long value must be stable to accept
var ezbIndex = 0; // which EZB index this script is monitoring

// Map port index -> action function
var actions = [];

actions[0] = function () {
    // No ADC 0 here. Script A handles the power pack switch.
};

actions[1] = function () {
    // Chest Button: Gamma 202 - Power Down Robot Completely
    ControlCommand("Personality Generator", "PauseOn");
    sleep(6250);
    ControlCommand("Shutdown PC", "ScriptStart");
};

actions[2] = function () {
    // Chest Button: Gamma 101 - Personality Toggle On and Off
    var personality = getVar("$Personality");
    sleep(200);

    if (personality == 0) {
        // 0 means Personality is running.
        turnOffPersonality();
    } else if (personality == 1) {
        // 1 means Personality is Paused.
        turnOnPersonality();
    }
};

function turnOffPersonality() {
    // Personality toggle momentary button pushed. Will turn personality off.
    ControlCommand("Personality Generator", "PauseOn");
    sleep(4000);

    // Hold script here until ADC reading returns to zero before switching to other function
    ControlCommand("Soundboard v4", "Track_24"); // "Raspberries"
    print("Personality is Off");
    setVar("$Personality", 1);
}

function turnOnPersonality() {
    // Personality toggle momentary button pushed. Will turn personality on.
    ControlCommand("Personality Generator", "PauseOff");
    sleep(4000);

    ControlCommand("Soundboard v4", "Track_23"); // "Green and GO"
    print("Personality is On");
    setVar("$Personality", 0);
}

actions[3] = function () {
    // Chest Button: Vector Hold
    ControlCommand("Personality Generator", "PauseOn");
    setVar("$Personality", 1);
    sleep(6000);

    ControlCommand("Soundboard 2", "Track_26"); // Say "Not"
    sleep(600);
    ControlCommand("Soundboard v4", "Track_20"); // Say "Active"
    ControlCommand("Personality Generator", "PauseOff");
    setVar("$Personality", 0);
};

actions[4] = function () {
    // Chest Button: Alpha 202
    ControlCommand("Personality Generator", "PauseOn");
    setVar("$Personality", 1);
    sleep(6000);

    ControlCommand("Soundboard 2", "Track_26"); // Say "Not"
    sleep(600);
    ControlCommand("Soundboard v4", "Track_20"); // Say "Active"
    ControlCommand("Personality Generator", "PauseOff");
    setVar("$Personality", 0);
};

actions[5] = function () {
    // Chest Button: Bata 202 - Speak Time, Date and Environmental Report
    ControlCommand("Personality Generator", "PauseOn");
    setVar("$Personality", 1);
    sleep(6000);

    ControlCommand("Time,Date & Temp", "ScriptStart", "Time,Date,Enviroment");
};

actions[6] = function () {
    // Chest Button: Detent +
    ControlCommand("Personality Generator", "PauseOn");
    setVar("$Personality", 1);
    sleep(6000);

    ControlCommand("Soundboard 2", "Track_26"); // Say "Not"
    sleep(600);
    ControlCommand("Soundboard v4", "Track_20"); // Say "Active"
    ControlCommand("Personality Generator", "PauseOff");
    setVar("$Personality", 0);
};

actions[7] = function () {
    // Chest Button: Vector Release - Run Soil Sampler & Speak Report
    ControlCommand("Personality Generator", "PauseOn");
    setVar("$Personality", 1);

    Digital.set(D0, true);   // Turn on Ear Motors
    Digital.set(D23, true, 1); // Turn on Soil Sampler Relay
    Digital.set(D2, true);   // Turn on Scanner chest lights
    Digital.set(D10, true);  // Turn on fast brain lights
    sleep(2000);

    Digital.set(D23, false, 1); // Turn off Soil Sampler Relay
    ControlCommand("Leg Section", "ScriptStart", "Hip Center");
    sleep(250);
    ControlCommand("Leg Section", "ScriptStart", "Hip Down Full");
    sleep(6000);
    ControlCommand("Leg Section", "ScriptStart", "Up with Hips");
    sleep(3500);
    ControlCommand("Head Section", "ScriptStart", "Bubble Up and Down");
    sleep(250);

    Digital.set(D2, false); // Turn off Scanner chest lights
    sleep(10000);
    Digital.set(D0, false);  // Turn off Ear motors
    Digital.set(D10, false); // Turn off fast brain lights

    ControlCommand("Personality Generator", "PauseOff");
    setVar("$Personality", 0);
};

// Internal state
var lastState = [];
var stableStart = [];

// Initialize state arrays
for (var i = 0; i < adcPorts.length; i++) {
    lastState[i] = false;
    stableStart[i] = 0;
}

function nowMs() {
    return new Date().getTime();
}

print("ADC Watcher: start. Polling " + adcPorts.length + " ports every " + pollMs + " ms");

while (true) {
    try {
        for (var i = 0; i < adcPorts.length; i++) {
            var port = adcPorts[i];
            var value = ADC.get(port, ezbIndex);
            var pressed = (value >= threshold);

            if (pressed !== lastState[i]) {
                // state changed - start debounce timer
                if (stableStart[i] === 0) {
                    stableStart[i] = nowMs();
                } else {
                    // check if we've been stable long enough
                    if (nowMs() - stableStart[i] >= debounceMs) {
                        // accept new state
                        lastState[i] = pressed;
                        stableStart[i] = 0;

                        var t = new Date().toLocaleString();
                        print(t + " ADC" + port + " state -> " + (pressed ? "PRESSED" : "RELEASED") + " val: " + value);

                        if (pressed) {
                            try {
                                if (typeof actions[i] === "function") {
                                    actions[i]();
                                } else {
                                    print("ADC Watcher: no action defined for port " + port);
                                }
                            } catch (ae) {
                                print("ADC Watcher: action error: " + ae);
                            }
                        }
                    }
                }
            } else {
                // state unchanged - reset debounce timer
                stableStart[i] = 0;
            }
        }
    } catch (e) {
        print("ADC Watcher: error reading ADC: " + e);
    }

    sleep(pollMs);
}

A couple important notes

1. I left your actions[0] placeholder out of use

Since Script A now owns ADC 0, Script B should not react to it.

2. Your ControlCommand("Shutdown PC", "ScriptStart"); line may need review

If "Shutdown PC" is actually the name of a Script skill, that is fine.
If not, we may need to adjust that call.

3. There may still be a logic issue in your button debounce

Right now, your debounce logic only calls actions when a port becomes pressed, not when released. That is probably what you want for buttons, but I mention it because it’s intentional behavior.


About rewriting Script A

I can rewrite Script A into a true polling/state-machine version if you want, but I’d recommend leaving it as-is unless you specifically want a loop-based approach.

Why keep it as-is?

  • It already works as a dedicated toggle watcher
  • ADC.waitForHigher() / ADC.waitForLower() are simple and reliable for this use
  • Since it’s isolated in its own script, it won’t interfere with the button watcher

If you want a polling version

I can convert it so it:

  • continuously reads ADC 0
  • detects pull/insert transitions
  • uses a state variable like "inserted" / "pulled"
  • avoids blocking waits

That would match the style of Script B more closely.

My recommendation

For your robot, I’d do this:

  • Keep Script A as the dedicated ADC 0 toggle watcher
  • Keep Script B as the ADC 1-7 button watcher
  • Use the cleaned Script B above
  • Only rewrite Script A if you want a more consistent code style

If you want, I can next do one of these:

  1. Rewrite Script A into a clean polling/state-machine JavaScript version
  2. Check Script A for syntax cleanup too
  3. Help you make both scripts coordinate more safely using a shared global variable

If you’d like, I can do Script A next.


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.