Skip to main content

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.

The AT1032S switches power to its own USB port off and on for hundreds of cycles, measuring the steady-state current after each restart to confirm the device came back every time.

Switch, wait, measure, log. One row per cycle, so a failure at 03:00 is on the record.

Wiring​

AT1032SDevice under test
USB 0The USB device, plugged straight into the port

The test sequence​

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

Adapting it to your device​

  • CURRENT_MIN_A and CURRENT_MAX_A are 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_MS must cover enumeration and whatever your device does at startup.
  • STOP_AFTER_FAILURES keeps 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.

API used on this page​