USB device power-cycle reliability test
Both USB ports on the AT1032S have switched power and their own current measurement. A firmware reliability run that would otherwise need a relay board and an inline USB meter becomes a loop: cut the port, restore it, and confirm the device came back and is drawing what it should. Leave it running overnight and it will find the unit that stops re-enumerating after four hundred cycles.
Switch, wait, measure, log. One row per cycle, so a failure at 03:00 is on the record.
Wiring​
| AT1032S | Device under test |
|---|---|
USB 0 | The USB device, plugged straight into the port |
The test sequence​
- NodeJS
- Python
import { AT1000 } from '@ikalogic/at1000';
import { appendFileSync, writeFileSync } from 'node:fs';
// ---- The run --------------------------------------------------------------
const USB_PORT = 0;
const CYCLES = 500;
const OFF_MS = 1000; // how long the device stays unpowered
const BOOT_MS = 3000; // time the device needs to come back and settle
const CURRENT_MIN_A = 0.020; // below this, the device did not start
const CURRENT_MAX_A = 0.450; // above this, it came up wrong
const STOP_AFTER_FAILURES = 5; // consecutive failures that end the run
const CSV_PATH = 'usb-power-cycle.csv';
// ---------------------------------------------------------------------------
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
const devices = await AT1000.findDevices();
const tester = await AT1000.open(devices[0]);
await tester.reset();
const port = tester.power.usb(USB_PORT);
writeFileSync(CSV_PATH, 'cycle,timestamp,voltage_v,current_a,verdict\n');
let failures = 0;
let consecutive = 0;
for (let cycle = 1; cycle <= CYCLES; cycle++) {
await port.disable();
await sleep(OFF_MS);
await port.enable();
await sleep(BOOT_MS);
const volts = await port.read_voltage();
const amps = await port.read_current();
let verdict;
if (amps < CURRENT_MIN_A) {
verdict = 'did-not-start';
} else if (amps > CURRENT_MAX_A) {
verdict = 'overcurrent';
} else {
verdict = 'ok';
}
if (verdict === 'ok') {
consecutive = 0;
} else {
failures++;
consecutive++;
}
const timestamp = new Date().toISOString();
appendFileSync(CSV_PATH,
`${cycle},${timestamp},${volts.toFixed(3)},${amps.toFixed(3)},${verdict}\n`);
if (verdict !== 'ok' || cycle % 25 === 0) {
console.log(`cycle ${cycle}: ${volts.toFixed(2)} V, ` +
`${(amps * 1000).toFixed(0)} mA, ${verdict}`);
}
if (consecutive >= STOP_AFTER_FAILURES) {
console.log(`\nStopping: ${consecutive} failures in a row at cycle ${cycle}.`);
break;
}
}
await port.enable(); // leave the device powered
console.log(`\n${failures} failing cycles. Log written to ${CSV_PATH}`);
from ikalogic_at1000 import AT1000
from datetime import datetime, timezone
import csv
import time
# ---- The run -------------------------------------------------------------------
USB_PORT = 0
CYCLES = 500
OFF_S = 1.0 # how long the device stays unpowered
BOOT_S = 3.0 # time the device needs to come back and settle
CURRENT_MIN_A = 0.020 # below this, the device did not start
CURRENT_MAX_A = 0.450 # above this, it came up wrong
STOP_AFTER_FAILURES = 5 # consecutive failures that end the run
CSV_PATH = "usb-power-cycle.csv"
# ---------------------------------------------------------------------------------
devices = AT1000.find_devices()
tester = AT1000.open(devices[0])
tester.reset()
port = tester.power.usb(USB_PORT)
failures = 0
consecutive = 0
with open(CSV_PATH, "w", newline="") as handle:
writer = csv.writer(handle)
writer.writerow(["cycle", "timestamp", "voltage_v", "current_a", "verdict"])
for cycle in range(1, CYCLES + 1):
port.disable()
time.sleep(OFF_S)
port.enable()
time.sleep(BOOT_S)
volts = port.read_voltage()
amps = port.read_current()
if amps < CURRENT_MIN_A:
verdict = "did-not-start"
elif amps > CURRENT_MAX_A:
verdict = "overcurrent"
else:
verdict = "ok"
if verdict == "ok":
consecutive = 0
else:
failures += 1
consecutive += 1
timestamp = datetime.now(timezone.utc).isoformat()
writer.writerow([cycle, timestamp, f"{volts:.3f}", f"{amps:.3f}", verdict])
handle.flush()
if verdict != "ok" or cycle % 25 == 0:
print(f"cycle {cycle}: {volts:.2f} V, {amps * 1000:.0f} mA, {verdict}")
if consecutive >= STOP_AFTER_FAILURES:
print(f"\nStopping: {consecutive} failures in a row at cycle {cycle}.")
break
port.enable() # leave the device powered
print(f"\n{failures} failing cycles. Log written to {CSV_PATH}")
tester.close()
Adapting it to your device​
CURRENT_MIN_AandCURRENT_MAX_Aare the window a healthy device sits in once it has come up. Run a handful of cycles first and read the log to pick them.BOOT_MSmust cover enumeration and whatever your device does at startup.STOP_AFTER_FAILURESkeeps an overnight run from spending hours on a device that has already died.- The second USB port works the same way, so two devices can be cycled in one run.