Asked
Resolved Resolved by DJ Sures!

Intermittent EZ-B Controllers Disconnect Issue

I'm experiencing an intermittent issue with ARC where two out of my three EZ-B controllers lose their connection.

  • I'm using direct USB connections from the computer's USB port through USB-to-serial converter cables connected to the EZ-B's camera ports.
  • At startup, ARC connects to the two EZ-Bs using an initialization script. The EZ-B controllers connect properly to ARC, and commands can be sent without issue.
  • Randomly, connections 0 and 2 will disconnect from the computer. This could happen to either connection or both, but at different times. It seems they don't disconnect simultaneously.
  • The disconnections can occur within a few minutes of a successful ARC startup and initial connection to the EZ-Bs or after ARC has been running for an extended period.
  • It doesn't seem to matter if data is actively being sent through the serial connection or not. Disconnects occur whether ARC is idle or executing commands from scripts.
  • The EZ-B controllers do not lock up. I can simply click the reconnect button in the Connection Control within the Connection Skill, and the connection is reestablished between ARC and the EZ-B.

I'm seeking advice on what might be causing these disconnects and how to resolve the issue.

Could Windows 11 be interfering with the COM ports or doing something in the operating system that would cause these disconnects? Might electromagnetic fields (EMF) from nearby power cables induce such disconnections in the serial converter cables?

Thanks in advance for any suggestions.



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)
Serial Terminal by Synthiam
PC and EZB serial terminals for ARC: connect, monitor and send text/ASCII to serial devices. Supports COM, flow control, baud, DTR/RTS, hex
Diagnose EZ-B Wi-Fi connection issues with verbose EZ-B communication logging, ping tests, log export and diagnostic reports for Synthiam support.

ARC Pro

Upgrade to ARC Pro

Unleash your creativity with the power of easy robot programming using Synthiam ARC Pro

#49  

UPDATE 2/21/26: Progress finally made! YA! But now what? 

OK. I've been working on this disconnection issue between the direct connected computer and the EZB's now since last October. That's over 4 months now. that I've worked on this problem. Worked over 4 months, almost every day and have just now finally found an honest lead as to what the problem is. I did Sanity checks along the way and I have to admit I had a few ruff patches.;)

First, I need to say that the reason it's taken so long to get to this point is because of a few things. First, every time I make a change to a hopeful fix I'd need to wait anywhere from 2  to 10 hours to see if it worked. I's need to run the robot with all it's operating motors and other devices running from scripts that would trigger animations one after the other (with a minute or two in between). The end result would be always be a disconnect from ARC Index 0.

After many, many different changes of hardware and software adjustments (some of them made things better. I'll go into more detail on that below) I decided to finally start disconnecting the digital and ADC pins on the offending EZB connected to ARC index 0. This is where I finally got my first clue and uninterrupted connection of the USB to Serial connection between the computer and EZB.

I finally got lucky and picked the right thing to start with.in this process. I started by pulling all 8 of the ADC cables plugged into all of the ADC ports on this EZB (on index 0 and leaving all other cables connected). I powered up the robot and started it's ARC project and made sure the script polling these ports were turned off. I don't need these scripts or ADC ports for him to simply stand and run personality animations.. After running the robot and it's ARC animation scripts for two straight days I got absolutely no disconnects!

So, as I mentioned above, I am using all 8 ADC ports on this EZB. They monitor button pushes on the robot. Each of the 8 ADC ports has a separate script looping in ARC that watches for a voltage change when the button is pressed. Then when the button is pressed and it's switch closes, ARC will see the voltage changes from near zero to the suppled power and run it's script. In addition each cable attached to a ADC port has a Pull Down resistor added near the EZB to keep the voltage low new zero until the button is pushed and voltage is supplied. Resistor problem????

OK, now what? LOL.:( I'm not sure why using all ADC ports on one EZB at the same time is causing the connection between the EZB and computer to disconnect after a random number of long hours of use. My two other EZB's have their ADC ports in use but admittedly I'm only using a couple of these ADC ports on each of the other units.

My instinct tells me this may be a heat issue? Maybe over time with ARC polling these ADC pins is causing the EZB to heat up and causing my problem? I do feel that the outer case of the EZB in question is a bit warm to the touch.

OR,,, could I somehow be flooding the EZB with the ADC polling process from all 8 ADC ports? If so why would it take hours to show up? I am using the JavaScript command ADC.waitFor* (event-based polling), not the GetADC command  In each script watching these ADC ports. Should I try to move some of the ADC cables to a second EZB to relive the ADC polling load on this one EZB and maybe lessen the heat build up or polling process? Or should I find a better script that will do a more efferent job polling the ports? Here is one of the scripts I use to watch just one ADC port:

while (true) {
print("Waiting for Torso Turn Right toggle button (Alpha 202) to be pushed...")
// Wait for ADC value to indicate Torso Turn Right toggle button (Alpha 202) to be pushed
//to rotate torso half way to the right,
ADC.waitForHigher(4, 170);
var torsoPosition = getVar("$torsoPosition")
sleep(200)
if (torsoPosition == 0) { //0 means Torso is centered. 
rotateTorsoRight()
}else if (torsoPosition == 1) {//1 means Torso is turned right. 
centerTorso()
}
}

function rotateTorsoRight(){
//Torso Rotate Right momentary toggle button (Alpha 202)was pushed. Will rotate torso to the right.
ControlCommand("Personality Generator", "PauseOn");
ControlCommand("Leg Section", "ScriptStart", "Waist 1/2 Right");
setVar( "$torsoPosition", 1); //Sets Global Variable so next button push will rotate torso back to center.
sleep(6000); //Holds shript here until ADC reading returns to zero before switching to other function
ControlCommand("Soundboard 2", "Track_7"); //Say "Please Stand Back"
print("Rotate Torso Right");
}

function centerTorso(){
//Torso Rotate Right momentary toggle button (Alpha 202)was pushed a second time. Will rotate torso to the center position
ControlCommand("Leg Section", "ScriptStart", "Waist Center");
sleep(6000);  //Holds shript here until ADC reading returns to zero before switching to other function
ControlCommand("Personality Generator", "Pauseoff");
ControlCommand("Soundboard v4", "Track_23"); //Say "Green and GO"
print("Rotate Torso To Center");
setVar( "$torsoPosition", 0); //Sets Global Variable so next button push will Rotate Torso Right
}

One last thought on new scripting to solve this; Again, I have 8 different ADC polling scripts running. Would it be better to only have one script watching all 8 ports? The script would have to see the voltage change when the button is pushed and do a different event for individual each button.

Here's is an abridged list of what I've done to date to try to solve this issue.  Over the months I've literally rewritten many scripts, followed all the advice given here concerning possible electrical and device problems. So, I've actually:

*Taken apart the robot and rewired a lot of the major electrical circuitry. I did find a few wiring mistakes that could have caused electronic noise and ground loops. After doing this rewire I found that the problem of losing connections on the other indexes 1 and 2 totally stopped. However the disconnect issue was then confined to just index 0. Even so, that connection 0 seemed more stable.

*Rerouted USB to Serial converter cables far away from all power cables.

*Replaced every device and component that may have been causing the issues. These include new wiring and connections (like placing EZB's on their own dedicated power circuit going  straight to the Power supply), new EZB, new Serial to USB converter cables, even tried a new computer running ARC and directly connected to the three EZB's. This new computer had it's own separate power feed.

*Ran the EZB Stress test @DJ wrote for me. I ran it for many days with no issue or disconnects. Once I started using my regular project the disconnects continued.

*I also tried a combination of the three EZB's running with a mix of direct connection to the computer and over WIFI. An odd result happened with this test. I had index 0 and 2 directly connected with a new USB to Serial converter cable. Then I had Index 1 connected to it's EZB over WIFI.. Index 0 and 2 stayed stable and connected but oddly index1 would disconnect (instead of the usual index 0).

So I see @Athena hasn't chimed in on any of this. Has it been turned off for this thread? How would I get it's opinion? Of course I always love hearing from humans. Any ideas? You all are always apperceived..

Author Avatar
PRO
Canada
#50  

Nice work - that’s a meaningful lead. You’ve proven the ADC wiring/polling is tied to the disconnect symptoms. Below I’ll explain why this likely happens, give a safe prioritized plan you can run next, and include a ready-to-use JavaScript "single ADC watcher" script (with debounce and adjustable polling) to replace your eight separate scripts.

Short diagnosis summary

  • Each ADC read is a command between ARC and the EZ-B. Eight independent scripts repeatedly calling ADC.waitFor* (or ADC.get) produces a lot of command traffic. Over time that many rapid requests can overload the USB/serial or TCP command pipeline and cause intermittent socket/USB disconnects.
  • ADC.waitFor* without a frequency parameter can poll the EZ-B as fast as possible, multiplying the load.
  • Heat could be a factor (you said the EZ-B gets warm), but the more likely root cause is too much polling/command traffic (and/or wiring induced noise causing lots of transient ADC readings).
  • Pull-down resistors are good practice - but long runs, shielding, and floating conductors can create noise spikes that also increase polling responses and command traffic.

Recommended approach (priority order)

  1. Stop the many independent ADC scripts.
    • Put the project into a clean state: remove or disable the 8 ADC watcher scripts, and leave only one unified ADC watcher script (below).
  2. Replace ADC.waitFor* loops with a single script that polls all ADC ports at a controlled rate (e.g. 50-200 ms) and uses debounce/hysteresis.
    • This reduces the number of separate commands and lets you tune the poll rate to reduce bus load and noise sensitivity.
  3. Add debounce/hysteresis in software so short spikes don’t generate events (e.g. require stable reading > threshold for N ms).
  4. Re-run the minimal, empty project test with only:
    • Connection control
    • EZB Info (to see voltage/temp)
    • This new single ADC watcher script
    • Run for the same length of time you used before.
  5. If still disconnecting: move some ADC inputs to another EZ-B (as you suggested) to see if splitting the load fixes it.
  6. Hardware & wiring checks (parallel or if step 5 still fails):
    • Make pull-down resistor values reasonable (10k is typical for button pull-downs). Very small resistances draw unnecessary current; very large can be noisy.
    • Use short, twisted-pair wires; keep ADC and signal runs away from motor power, motor drivers, and PWM wires.
    • Add ferrite chokes on USB / serial cables or on the ADC harness.
    • If noise still persists, use an external microcontroller (Arduino/ESP) or an I2C GPIO expander (MCP23017) to handle the buttons and send events to ARC less often (offload ADC tasks).
  7. If you need absolute electrical isolation, use a USB isolator or opto-isolated serial interface for the TTL lines - but test the software/polling fixes first.

Why a single script helps

  • One script issues a small, predictable number of ADC reads in a loop rather than multiple scripts each issuing rapid blocking waits. This drastically reduces command overhead and improves stability.
  • Centralized debounce prevents repeated false triggers created by noise.

Single ADC watcher JavaScript (drop-in)

  • Adjust ports, thresholds, debounce times and action commands to match your robot.
  • Save as a JavaScript script in your project (Project Add Script JavaScript). Open the skill kebab menu (three lines) to edit or move settings.
  • This example polls 8 ADC ports every 100 ms and requires a button reading to remain above threshold for 150 ms to register.
// 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 = 100;                  // 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(){ ControlCommand("Leg Section","ScriptStart","Waist 1/2 Right"); ControlCommand("Soundboard 2","Track_7"); };
actions[1] = function(){ ControlCommand("Leg Section","ScriptStart","Waist Center"); ControlCommand("Soundboard v4","Track_23"); };
// Add similar for other ports as needed...

// Internal state
var lastState = [];         // boolean pressed/unpressed
var stableStart = [];       // timestamp ms when current state started
for(var i=0;i<adcPorts.length;i++){ lastState[i] = false; stableStart[i] = 0; }

// 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<adcPorts.length;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);
}

Notes about the script

  • ADC.get(port) uses the port constant rule: The ADC.get expects a port constant (ADC0..ADC7) in scripting docs, but ARC JavaScript examples commonly use numeric indexes. If your project requires explicit ADC constants, change adcPorts to [ADC0, ADC1, ...]. If you see errors, change to use ADC.get(ADC0) etc. Try the numeric approach first since your previous scripts used ADC.waitForHigher(4,170) which suggests a numeric index.
  • If you use ADC.waitForHigher in one central script, provide the frequencyMs parameter so it doesn’t spin: ADC.waitForHigher(port, value, 200) - but I prefer ADC.get + sleep because it is easier to add debounce/hysteresis and control traffic.

Other practical tips

  • Lower poll rate if possible. Buttons don’t need 10 ms updates - 50-200 ms is typically fine and reduces command traffic.
  • Use hysteresis: separate press threshold and release threshold (e.g. press >170, release <150) to avoid toggling near a noisy midpoint.
  • Confirm pull-down resistor value and mounting: 10k is common. Put them physically near the button or near the EZ-B? Putting them near the EZ-B is fine, but avoid long floating conductors.
  • Short wiring and twisted pair for signal + ground is best. Keep ADC runs away from motor driver wiring.
  • If the EZ-B case gets warm, ensure airflow and consider spacing from heat sources. But first try reducing polling load - that often cures the disconnect.

If the single script resolves the disconnect

  • You can keep the single script and disable the other 7 loops permanently.
  • Optionally split ADC input set across EZ-Bs if your robot is physically distributed.

If the single script does NOT resolve the disconnect

  • Re-run the EZB Stress Test with only the connection+EZB Info+this single ADC watcher (and let it run).
  • Collect logs (EZB Info temp/voltage), and run continuous ping/netstat during disconnect as previously suggested. Paste those logs here and I’ll help analyze.

Questions for you / what I need to continue helping

  1. What are the resistor values for your pull-downs? (10k, 100k, etc.)
  2. Approximate wire length from each button to the EZ-B? Any long runs near motors or motor controllers?
  3. Are you using ADC.get( ) or ADC.waitForHigher( ) now in your scripts? Your example used ADC.waitForHigher(4,170) - are the scripts using no frequency parameter?
  4. Would you like me to give a version of the script using ADC.waitForHigher() with a frequencyMs parameter instead of ADC.get()?
  5. After you paste the result from the single script run (e.g. "stable for 24 hours?") we can decide whether to split ADC lines between EZ-Bs or offload to an Arduino/I2C expander.

If you want I can also:

  • Provide an alternate script which batches fewer firmware commands (one call per loop), or
  • Show how to configure the script inside its skill (kebab menu) and where to place it in the Project (Project Add Script JavaScript). Also show how to enable/disable the script with the kebab menu.

Next step suggestion

  1. Replace the 8 scripts with this single watcher script (edit thresholds and actions).
  2. Run the minimal project (Connection, EZB Info, ADC watcher) for the same time window that previously caused disconnections (24 hours if that’s your test period).
  3. Report results (connected / disconnected + EZB temp/voltage logs + approximate time if disconnected).

Great investigative work so far - you’ve isolated ADC as a clear variable. Try the single script and post the results + the resistor/wire details and we’ll iterate.


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  

UPDATE 02/24/26

Hello all and @Athena

I've looked over the multicable ADC port polling script that @Athena provided. As is, without any additions or changes from me it ran in ARC without error. I then added the commands to each of the 8 actions that happen when each of the 8 buttons and switches that are being watched by the 8 ADC ports on EZB index 0 are pushed. After these additions the were made I tested each button and the they tripped the expected actions immediately and as expected.

So, even though I have this script running that is designed to reduce the overhead of constant ADC polling of 8 different ADC checking scripts I still had the usual Com disconnect of the EZB on ARC index 0. This disconnect happened only one hour into the test run. As usual, the other two EZB's stayed connected. 

This last disconnect was after I reconnected all 8 of the ADC ports to the cables running to the buttons and started the central polling script (oh, just to be clear, I do not have the old single ADC polling scripts running). i am running another test session on the robot today to see if I still get another disconnect between the computer and the one EZB on index 0 or the one yesterday was an anomaly.

I do have several buttons that as of yet have no animations assigned so I simply coded a voice response of "Not, Active" to the action associated to those buttons from one of ARC's soundboards. I did this just to make sure their buttons were responding in this script. I'm open to any suggested improvements. Here's my running script:

// 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 = 100;                  // 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 Anamation (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
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 - 
 sleep(6000);
 ControlCommand("Soundboard 2","Track_26"); // Say "Not"
  sleep(600);
  ControlCommand("Soundboard v4","Track_20"); // Say "Active"
}

actions[4] = function(){      //Chest Button: Alpha 202 - 
 sleep(6000);
 ControlCommand("Soundboard 2","Track_26"); // Say "Not"
  sleep(600);
  ControlCommand("Soundboard v4","Track_20"); // Say "Active"
}

actions[5] = function(){      //Chest Button: Bata 202 - Speak Time, Date and Enviromental Report
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 + 
sleep(6000); 
 ControlCommand("Soundboard 2","Track_26"); // Say "Not"
  sleep(600);
  ControlCommand("Soundboard v4","Track_20"); // Say "Active"
}

actions[7] = function(){      //Chest Button: Vector Release - Run Soil Sampler & Speak Report
  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
}

// 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);
}

So, as of now, even though I'm thrilled I have a better and less resource eating script to poll many different ADC  ports I still seem to have the same disconnect problem. I now to look into the different tips and suggestions made by @Athena above. These are the suggestions copied from above:

If you use ADC.waitForHigher in one central script, provide the frequencyMs parameter so it doesn’t spin: ADC.waitForHigher(port, value, 200) - but I prefer ADC.get + sleep because it is easier to add debounce/hysteresis and control traffic.
Other practical tips

Lower poll rate if possible. Buttons don’t need 10 ms updates - 50-200 ms is typically fine and reduces command traffic.
Use hysteresis: separate press threshold and release threshold (e.g. press >170, release <150) to avoid toggling near a noisy midpoint.
Confirm pull-down resistor value and mounting: 10k is common. Put them physically near the button or near the EZ-B? Putting them near the EZ-B is fine, but avoid long floating conductors.
Short wiring and twisted pair for signal + ground is best. Keep ADC runs away from motor driver wiring.
If the EZ-B case gets warm, ensure airflow and consider spacing from heat sources. But first try reducing polling load - that often cures the disconnect.

Again, I'm open to suggestions, criticism or ideas. Thanks!

Author Avatar
PRO
Canada
#52  

Great update - you’ve narrowed this down a lot. You’ve proven the ADC inputs are tied to the symptom (excellent investigative work). Since the single-watcher script still saw a disconnect after about an hour, let’s focus on the high-probability causes and give you safe, repeatable tests and scripts to run next.

Summary of likely causes (prioritized)

  • Software/command overload: lots of rapid ARCEZB commands or many simultaneous heavy ControlCommand() calls can overwhelm the USB/TCP command pipeline over time.
  • Power sag / motor/current events: your action handlers turn on motors/relays/LED banks which can create voltage dips and reset/disconnect the EZ-B or the USB-serial adapter.
  • Electrical noise & wiring: long/unshielded ADC cable runs and nearby motor power lines cause noise spikes that trigger ADC events and increase command traffic.
  • USB power management / adapter driver: Windows 11 USB selective suspend or poor USB-serial adapter drivers (Prolific, CH340 vs FTDI) can drop COM ports under load or idle.

Concrete next steps (what to try now - ordered)

  1. Reduce watcher work and offload heavy actions (important)

    • Change your watcher so it only detects presses and posts a small, timestamped event (global variable).
    • Run a separate Action Runner script that consumes those events and executes heavy ControlCommand() sequences one at a time.
    • Reason: watcher stays lightweight and predictable; heavy commands are serialized so they don’t flood the command pipeline all at once.
  2. Lower poll rate + add hysteresis

    • Set pollMs to 200-300 ms.
    • Use separate pressThreshold and releaseThreshold (e.g. press >=170, release <=150).
    • This minimizes command traffic and avoids flapping.
  3. Monitor EZ-B voltage & temperature while testing

    • Keep the Connection Control / EZ-B Info visible.
    • Watch for voltage dips when actions run (especially the sequence that powers motors/soil sampler).
  4. Disable Windows USB power management

    • Device Manager Universal Serial Bus controllers for each "USB Root Hub" and "Generic USB Hub" Properties Power Management uncheck "Allow the computer to turn off this device to save power".
    • In Windows Power Options, disable USB selective suspend.
  5. Check / improve USB-serial adapter & cabling

    • Use a short, high-quality adapter (FTDI-based adapters are more reliable than cheap CH340/Prolific clones).
    • Keep the USB cable short and route away from motor power.
    • Add ferrite beads on cables if you can.
  6. Wiring & pull-down checks

    • Typical pull-down 10k. Very large values are noisy; very small draw current.
    • Use twisted-pair (signal with ground) and keep ADC runs away from motors and driver wiring.
    • Terminate/cap unused conductors; do not leave long floating wires.
  7. If problem persists, split ADC inputs

    • Move some ADC ports to a second EZ-B to reduce per-device polling load.
    • Or offload button reading to a small Arduino/ESP and send higher-level events to ARC.
  8. Use USB isolator / separate power rails (if needed)

    • If grounding noise or ground loops are suspected, test with a USB isolator or separate power distribution and see if it stabilizes things.

Lightweight event architecture - two scripts Below are two ready-to-use JavaScript scripts: 1) Watcher (lightweight, with hysteresis + debounce) and 2) Action Runner (serializes heavy actions). Put both in your project as JavaScript scripts (Project Add Script JavaScript). Edit thresholds and actions to match your project.

  1. ADC Watcher - only posts an event (press), uses hysteresis and longer poll interval
// ADC Watcher (lightweight): sets a single global event variable on button press
var adcPorts = [0,1,2,3,4,5,6,7];    // ADC port indices
var pressThreshold = 170;            // value considered pressed
var releaseThreshold = 150;          // value considered released (hysteresis)
var pollMs = 250;                    // poll interval in ms (increase to reduce load)
var debounceMs = 200;                // must be stable for this many ms
var ezbIndex = 0;                    // which EZB to read

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

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

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

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

while(true){
  try{
    for(var i=0;i<adcPorts.length;i++){
      var port = adcPorts[i];
      var val = ADC.get(port, ezbIndex);
      // hysteresis
      var pressed = lastState[i] ? (val >= releaseThreshold) : (val >= pressThreshold);

      if(pressed !== lastState[i]){
        if(stableStart[i] === 0){
          stableStart[i] = nowMs();
        } else {
          if(nowMs() - stableStart[i] >= debounceMs){
            // confirmed state change
            lastState[i] = pressed;
            stableStart[i] = 0;
            var t = (new Date()).toLocaleString();
            print(t + " ADC" + port + " -> " + (pressed ? "PRESSED" : "RELEASED") + " val:" + val);
            if(pressed){
              // Post lightweight event: "$LastButtonEvent" = "port|timestamp"
              setVar("$LastButtonEvent", port + "|" + nowMs());
            }
          }
        }
      } else {
        // state unchanged
        stableStart[i] = 0;
      }
    }
  }catch(e){
    print("ADC Watcher:error reading ADC:" + e);
  }
  sleep(pollMs);
}
  1. Action Runner - polls the event variable and runs heavy actions sequentially
// Action Runner: consumes $LastButtonEvent events and runs actions sequentially
// Edit the actions[] functions to match your robot. Keep them single-threaded.

var pollMs = 500; // how often to check for events
var ezbIndex = 0;

// Map port index -> action function
var actions = [];
actions[0] = function(){
  // Example: small, non-blocking confirmation, then return
  ControlCommand("Soundboard 2","Track_26");
  sleep(600);
  ControlCommand("Soundboard v4","Track_20");
};
actions[1] = function(){
  sleep(6250);
  ControlCommand("Shutdown PC","ScriptStart");
};
// ... add the rest like your existing functions. Keep them as-is but try to avoid
// having many device toggles run all at once. If an action toggles motors/relays,
// ensure it monitors voltage or takes power limits into account.
actions[2] = function(){
  var personality = getVar("$Personality","0");
  sleep(200);
  if(personality == 0){
    ControlCommand("Personality Generator","PauseOn");
    sleep(4000);
    ControlCommand("Soundboard v4","Track_24");
    setVar("$Personality","1");
  } else {
    ControlCommand("Personality Generator","Pauseoff");
    sleep(4000);
    ControlCommand("Soundboard v4","Track_23");
    setVar("$Personality","0");
  }
};
// Add actions[3]...actions[7] like above. For actions that enable motors/relays,
// consider adding "sleep(200)" before turning on and check EZB voltage if possible.

var lastProcessedTime = 0;

print("Action Runner: start (polling event var every " + pollMs + " ms)");
while(true){
  try{
    var event = getVar("$LastButtonEvent","");
    if(event !== ""){
      // parse "port|timestamp"
      var parts = event.split("|");
      if(parts.length == 2){
        var port = parseInt(parts[0]);
        var t = parseInt(parts[1]);
        if(t > lastProcessedTime){
          // process
          print("Action Runner: processing port " + port + " @ " + new Date(t).toLocaleString());
          try{
            if(typeof actions[port] === "function"){
              actions[port]();
            } else {
              print("Action Runner: no action for port " + port);
            }
          }catch(ae){
            print("Action Runner: action error: " + ae);
          }
          lastProcessedTime = t;
          setVar("$LastProcessedTime", lastProcessedTime.toString());
        }
      }
    }
  }catch(e){
    print("Action Runner:error: " + e);
  }
  sleep(pollMs);
}

Why splitting helps

  • Watcher issues minimal commands to ARC/EZB (ADC.get and one setVar). Action Runner serializes heavy work so you don’t issue many heavy ControlCommand() calls at the same instant - this reduces bursts that may overwhelm the comm layer and also prevents simultaneous motor starts that cause current spikes.

Other checks & experiments

  • Reproduce with motors disabled: Run the full watcher + action runner but comment out motor/relay actions in actions[7] (or any action that turns on motors). If disconnects stop, you’ve found a power/current issue.
  • Run with some ADC inputs moved to a different EZ-B and leave this EZ-B with only 4 ADCs connected. If stable, load was part of the problem.
  • Change pollMs from 250 500 1000 ms and see if stability improves. This is a low-effort test.
  • Replace USB-serial adapter temporarily with a different model (FTDI recommended) and try again.
  • Use Connection Diagnostic skill (Project Add Skill General Connection Diagnostic) and run it while reproducing the disconnect. When it fails, press Copy Log and paste it here.

Windows & driver actions (do these)

  • Disable USB selective suspend and uncheck "Allow the computer to turn off this device to save power" for USB hubs (Device Manager).
  • Make sure your USB-serial drivers are up to date (FTDI, Prolific official driver).
  • If using a USB hub, try direct PC USB port (and try a different port).

Log collection I need from you if the problem continues

  • ARC status log from Connection Diagnostic (use Copy Log right after a disconnect).
  • Timestamp of disconnect and any EZ-B voltage/temp visible in Connection Control just before the disconnect.
  • USB-serial adapter make/model (FTDI, CH340, PL2303, etc.), cable length from adapter to EZ-B, and length of ADC wire runs (approx).
  • Pull-down resistor value (10k?).

Next recommended experiment (short/medium term)

  1. Install watcher + action-runner scripts above (only these scripts plus Connection Control in a clean project).
  2. Set pollMs=250 (watcher), action runner pollMs=500.
  3. Temporarily disable the heavy motor-relay actions in your actions[7] (comment them out or replace them with a short sound) and run 8-24 hours.
    • If no disconnects: the motor/power draw is the likely cause - introduce power distribution changes (separate battery for motors, add caps, or ramp motor startup).
    • If still disconnects: run with ADCs disconnected - if stable, it’s ADC/wiring noise; if still disconnects, suspect USB-adapter/Windows/firmware.

If you want, I’ll:

  • Provide a variant that logs EZB temperature and input voltage periodically into the project log (so you can see voltage dips correlated with disconnect).
  • Provide a short checklist for wiring changes (ferrite, twisted pair, ground star).

Questions for you now

  1. What value are your pull-down resistors (ohms)?
  2. Approximate cable lengths (button EZB) and are ADC cables routed near motor/mains wiring?
  3. Which USB-serial adapter type (brand/model)? Are drivers up to date?
  4. Do disconnects always happen soon after a specific button action runs (e.g. the soil sampler/motor sequence), or does it appear unrelated to running a heavy action?

You’ve done the hard part - isolating ADC as a variable. Try the two-script pattern (watcher + action runner), reduce polling, and run with motor actions disabled for a long test. Paste logs or the Connection Diagnostic copy if a disconnect happens and I’ll analyze it with you.


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.
Author Avatar
PRO
Synthiam
#53  

Dude, youre polling everywhere. You have adc robot skill that are polling. You have scripts polling.

it’s normally fine to be that inefficient with a project. ARC and ezb is designed for that.

Id be looking back at

  1. usb uart driver
  2. power saving
  3. wiring (use twisted pair with gnd)
  4. power (brownout)
  5. adc short, overload, or back feed voltage or ac voltage
  6. ezb shouldn’t be getting hot so sownthing is drawing a ton of current or feeding a ton of current on a port

the inefficient polling would just slow stuff down. It wouldn’t cause a disconnect.

but if you’re going down the path of polling, at least to optimize your project, get rid of your adc robot skills because that will slow down the ui and protocol.

lastly, adc.waitfor() does accept a speed ms parameter which will read at a specified rate.

Author Avatar
PRO
Synthiam
#54  

Oh and one more thing on the topic of the ADC robot skills. if you must keep them, just get rid of the adc.read's and adc.waitfor and use the variable that the adc robot skill creates. The adc robot skill creates a variable for this purpose, so you don't have to double poll.

Author Avatar
PRO
Canada
#55  

Thanks - excellent troubleshooting and great detail. You’ve proven ADC activity is tied to the problem, which is a huge step. Below I’ll summarize the highest-probability causes, then give a concise, prioritized set of tests and configuration steps you can run next (including how to stop "double polling" with ADC robot skills), plus a compact lightweight watcher pattern and the exact logs/data to collect if the issue persists.

Short diagnosis (what’s most likely)

  • USB / USB-UART driver, Windows USB power management, or COM-layer instability under load.
  • Power brownout / voltage dip when motors/relays start (your actions enable motors/relays). That can drop the EZ-B or the USB-serial adapter and look like a disconnect.
  • Electrical noise on ADC lines (long/unshielded runs, no twisted pair, or wiring near motors) causing lots of spurious ADC readings and extra command traffic.
  • "Double polling": ADC Robot Skill(s) and your scripts both polling the same ports (unnecessary traffic). DJ’s point: the ADC robot skill already creates variables you can read - use those instead of double polling.

High-priority steps to run now (ordered)

  1. Stop double polling (quick check, often fixes things)

    • Open your project and locate any "ADC / Analog Input" robot skills you added (Project -> Add robot skill if you need to add/remove).
    • If you need those ADC robot skills for UI or their variables, DO NOT run their internal polling and your scripts polling simultaneously. Two options:
      1. Disable the ADC robot skill’s polling (kebab menu -> Settings -> uncheck Enable / set polling rate to 0) AND let your single watcher do polling; OR
      2. Let the ADC robot skill do the polling and remove ADC.get / ADC.waitFor calls from your scripts - instead read the variable the skill creates. This is the most efficient: the skill polls once and updates a global variable you can read cheaply.
    • How to read the ADC Skill variable (general): open the ADC skill Settings (kebab menu). Note the variable name it creates (likely something like $ADC0 or a name you set). In your JS use getVar("$YourADCVar", "0") to read that value. This prevents any duplicate reads.
  2. Split detection from action (serialize heavy work)

    • Keep the ADC watcher minimal and lightweight (detect and set a small global event variable).
    • Run a separate Action Runner that reads that single event variable and executes heavy ControlCommand() sequences one at a time. This prevents bursts of heavy commands at event time.
    • I gave a watcher + action-runner pattern earlier - please switch to that pattern now if you haven’t already. It serializes heavy motor/relay startup and reduces protocol bursts.
  3. Reduce polling rate + add hysteresis

    • pollMs = 200-300 ms is fine for buttons. Lower traffic, less chance of saturating the comms.
    • Use separate press/release thresholds (press >=170, release <=150) to avoid toggling around noise.
  4. Test with motors/off (quick triage)

    • Temporarily disable motor/relay actions in your actions[7] and any action that turns on motors/relays.
    • Run the project for your usual test length ( the time it previously took to disconnect). If disconnect stops power or current transients are a likely cause. If still disconnects driver/USB wiring or ADC wiring/noise.
  5. USB / driver / Windows checks (do these now)

    • Try a different USB port on the PC (avoid hubs). Use a direct port.
    • Try a different USB-UART adapter (FTDI chipset is recommended). If you’re using a CH340/Prolific adapter, swap to FTDI temporarily.
    • Disable USB selective suspend and Device power management:
      • Power Options -> Edit Plan Settings -> Change advanced power settings -> USB settings -> USB selective suspend setting -> Disabled.
      • Device Manager -> Universal Serial Bus controllers -> Right-click each USB Root Hub -> Properties -> Power Management: uncheck "Allow the computer to turn off this device to save power."
    • Update USB-serial drivers to official latest from vendor (FTDI, CH340, Prolific). If you’re using older Prolific clones, they can be flaky.
  6. Power / brownout checks

    • Check the EZ-B input voltage and battery under load. If possible, run the robot while logging EZ-B voltage (EZB Info robot skill or Connection Diagnostic can show voltage).
    • Add a decoupling capacitor near motor driver power, or ensure motors have a separate power feed and proper common ground. Brownouts will manifest as disconnects or resets.
    • If suspecting power: run with motors powered off and if stable, that confirms it’s a power event.
  7. Wiring / noise controls

    • Use twisted pair (Signal + GND) for each ADC run. Keep ADC wiring away from motor & PWM wiring. Keep runs short.
    • Pull-down resistor values: use ~10k for pushbutton pull-downs. Very large values (100k) increase noise; very small draws more current. Tell me your resistor value.
    • Add ferrite beads/chokes on the USB cable and the ADC harness if noise persists. Tie unused conductors to ground at one end.
  8. If all else fails

    • Move some ADC inputs to another EZ-B (you already considered this). If the problem follows load (index 0) -> split the load.
    • Use an external microcontroller / I2C GPIO expander (MCP23017 or small Arduino/ESP) as a button aggregator and send events to ARC less frequently (offload ADC polling away from EZ-B). Or consider a USB isolator if ground/noise isolation is needed.

How to disable ADC robot skill polling and use its variable (step-by-step)

  1. Project -> locate the ADC / Analog Input robot skill in your project.
  2. Open the robot skill window and click the kebab menu (three lines, top-right).
  3. Choose Settings (or Configure). In that settings panel you can:
    • disable the skill (uncheck Enable) OR reduce its Polling Rate OR find the Global Variable names assigned to each ADC input.
  4. Note the variable names the skill creates (they start with $). Use getVar("$YourADCvar", "0") in your script instead of calling ADC.get or ADC.waitFor*.

Small lightweight watcher pattern (single event setter) - use only one poller

  • Watcher: only reads ADC (or reads the ADC skill variable) and sets a short event var like $LastButtonEvent = "port|timestamp". No heavy ControlCommand calls inside this watcher.
  • Action Runner: reads $LastButtonEvent and runs heavy commands sequentially.

Example (very small) watcher - reads skill variable instead of raw ADC (replace $ADC0 names with the actual variable names the ADC skill created)

// Lightweight watcher (reads ADC skill variables)
// poll every 250ms, post event var on press
var pollMs = 250;
var pressThreshold = 170;
var releaseThreshold = 150;
var debounceMs = 150;
var adcVars = ["$ADC0","$ADC1","$ADC2","$ADC3","$ADC4","$ADC5","$ADC6","$ADC7"]; // change to your actual ADC skill variables
var lastState = [];
var stableStart = [];

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

print("Watcher:start. polling " + adcVars.length + " vars every " + pollMs + "ms");
while(true){
  try{
    for(var i=0;i<adcVars.length;i++){
      var raw = getVar(adcVars[i],"0");
      var value = parseInt(raw) || 0;
      var pressed = (value >= pressThreshold);
      if(pressed !== lastState[i]){
        if(stableStart[i] === 0 || stableStart[i] === undefined) stableStart[i] = nowMs();
        else {
          if(nowMs() - stableStart[i] >= debounceMs){
            lastState[i] = pressed;
            stableStart[i] = 0;
            if(pressed){
              setVar("$LastButtonEvent", i + "|" + nowMs());
              print("Watcher: posted event port " + i + " val:"+ value);
            } else {
              print("Watcher: released port " + i + " val:" + value);
            }
          }
        }
      } else {
        stableStart[i] = 0;
      }
    }
  }catch(e){
    print("Watcher:error:"+ e);
  }
  sleep(pollMs);
}

What to collect if a disconnect still happens

  • Immediately after a disconnect, run the Connection Diagnostic skill and press Copy Log (paste here). This is the single most useful log. (Project -> Add robot skill -> General -> Connection Diagnostic).
  • Note the timestamp of disconnect and what you saw in EZB Info (voltage & CPU temp) right before the disconnect.
  • Tell me: USB-UART adapter make/model (FTDI, CH340, Prolific, PL2303, etc.) and cable lengths: adapterEZ-B cable and button wire lengths to EZ-B.
  • Pull-down resistor values (ohms).
  • Whether disconnects occur shortly after a specific button action that runs motors/relays (and which one), or appear unrelated.

Quick checklist of immediate experiments (do these in order)

  1. Disable ADC robot skill polling or stop your separate ADC robot skills so only ONE poller is active (either skill or watcher script).
  2. Run watcher + action-runner pattern; set pollMs = 250-300 ms. Disable the heavy motor actions temporarily. Run 8-24 hours. Report results.
  3. If still disconnects: disable motors and run. If stable, address power (battery, caps, separate motor supply).
  4. Try different USB adapter (FTDI recommended) / change USB port / update drivers / disable USB selective suspend.
  5. If still fails: move half the ADCs to another EZ-B, run again.

Useful references

A few other small tips

  • Keep ADC pull-downs near the button or near the EZ-B, but do not leave long floating wires. Use twisted pair (signal + ground). Ground star to a single point.
  • If the EZ-B case is hot - that suggests a large current draw or a short. Don’t leave it running until that’s resolved.

Questions for you (so I can give the exact next script/diagnostic)

  1. Are you running any ADC robot skills right now in the project? If yes - list them or confirm you’ll disable their polling.
  2. Which USB-serial adapter/driver are you using? (FTDI / CH340 / Prolific / PL2303 / built-in USB)
  3. Pull-down resistor values (ohms) and approximate ADC wire lengths (short < 30cm, medium 30-100cm, long > 100cm).
  4. Do disconnects correlate with a particular action (e.g. the soil sampler motor sequence), or do they happen when the robot is idle?

If you want, I’ll:

  • give a ready Action Runner script you can paste into the project to serialize heavy actions, and
  • provide a short logging script that periodically writes EZ-B temp/voltage to the project log so we can correlate drops to a disconnect.

You’ve done the hard detective work so far - we’ll iterate quickly from here. Which of the items above would you like to try first and what info can you provide from the questions?


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

@DJ, thanks for all your attention and advice on my subject here. All the insight really helps me learn and understand how to get my project to run smoothly with the hardware in my robot.

Just to be clear; Yes, I have a lot of scripts and controls that poll ADC. However I do not have them polling or even active at the same time. I started out with 6 or seven individual scripts polling ADC ports mostly on one EZB (two more on a second EZB). I just asked Athena to write me one script that combines all these ADC watching scripts into one to optimize my project. I will get rid of the other ones running individually. Also, I keep all the ADC reading skills I have installed paused unless I need to see if a button is working. I do plan to remove them when my project and robot gets the bugs worked out.

In the end I hope to only have the one ADC watcher script that Athena helped me with running watching my 7 or 8 buttons and switches and triggering scripts when activated. Oh, and the only other ADC reading will happen when a script is triggered from time to time.

In the above mentioned scripts I do use the adc.waitfor() command with a speed ms parameter. Athena has been writing the single multiple ADC port watcher script using the ADC.get() command. I know the adc.waitfor() command is more resource friendly so I was thinking of asking Athena to provide a different script that uses that command instead of the ADC.Get(). Maybe I can just replace her use of the ADC.Get() command with the adc.waitfor() command (with a speed ms parameter). I just haven't looked closely to see how to do that or if I would understand how to do it without help.

On a positive note, keeping all the all the ADC polling scripts and skills paused and turned off and running only Athena's multi ADC port script my robot seems very stable now. After I had the one disconnect early yesterday I slowed down the polling in that script from ms to 350ms and didn't get a disconnect to EZB index 0 in ARC all day. It ran for a good 12 hours.:D Running the test again today. We'll see.