Asked
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.

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
Recommended
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:
Why I would not fully merge them
Your current Script A is event-driven by threshold crossing:
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:
In other words, it becomes more complex than two scripts, not less.
My practical recommendation
Option 1 - Best overall
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:
pulledwhenvalue > 200insertedwhenvalue < 120pressedwhenvalue >= thresholdBut 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:
This keeps the long-running loop isolated.
Example idea:
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 hysteresisthresholdmodel, which is fine for buttons but not enough for the power-pack toggleifstatementsPauseoffshould bePauseOffcontrolCommand()vsControlCommand()consistencyEZB.isConnected(0)is correct, but make sure the rest of the code is syntactically valid JavaScriptIf you want to combine them anyway
Use this structure:
waitingForPull/waitingForInsert> 200< 120Pseudo-structure:
But note: if
pullPowerPack()orinsertPowerPack()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:
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.
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?
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:
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:
PauseOffcapitalization correctedControlCommand()used consistently0removed from pollingI also preserved your action structure and left the button behaviors intact.
A couple important notes
1. I left your
actions[0]placeholder out of useSince Script A now owns ADC 0, Script B should not react to it.
2. Your
ControlCommand("Shutdown PC", "ScriptStart");line may need reviewIf
"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?
ADC.waitForHigher()/ADC.waitForLower()are simple and reliable for this useIf you want a polling version
I can convert it so it:
statevariable like"inserted"/"pulled"That would match the style of Script B more closely.
My recommendation
For your robot, I’d do this:
If you want, I can next do one of these:
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.