Skip to content

Bench Testing

Ahmed Dabak edited this page Jul 24, 2026 · 2 revisions

Bench Testing

Introduction

Hardware never comes up all at once. A servo is on the wrong pin, a sensor reads backwards, a horn is mounted a quarter-turn off. OttoFlow treats developing one feature at a time as a first-class workflow, not an afterthought -- so you can verify each part on the bench before the whole robot moves.

The problem

A half-built robot that runs its full loop() is dangerous to itself. It walks off the table while you are trying to read a distance value, or twitches its arms while you check whether the microphone works. You end up commenting code out and reflashing between every test.

OttoFlow's answer: keep the robot still and quiet by default, and switch on exactly the one part you are testing.

Silencing the robot

Two lines make Otto safe to hold and quiet to sit next to:

void setup() {
  OttoFlow::start();
  Motion::disable();   // detach every servo: legs and arms
  Voice::mute();       // no sounds
}

Now every movement and every sound call is a silent no-op. You can run your real loop() and watch the sensors without anything spinning or beeping.

The three switches

Call Affects Use when
Motion::disable() / enable() All servos, legs and arms "The robot must not move" -- the one call for safety
Legs::disable() / enable() The four leg and foot servos You want the legs off but the arms working
Arms::relax() / hold() The two arm servos You want the arms off but the legs working
Voice::mute() / unmute() The buzzer Testing near people, or at night

Motion is deliberately the broad one, so "don't move" is never a checklist you can get half right. Legs and Arms are there for when you genuinely want one group live and the other parked.

Bringing up one feature at a time

A reliable order to bring a fresh build to life:

  1. Power and boot. Upload the smallest sketch, confirm the hello chirp and a face on the matrix.
  2. Matrix. Mouth::show(...), check orientation, fix config.matrix.orientation if icons are rotated.
  3. Distance sensor. Eyes::distanceCm() in the console; wave your hand and watch it drop.
  4. Other sensors. Touch, microphone, light, tilt -- one at a time, with watch in the console.
  5. Servos, still detached. center in the console to send every leg servo to 90, then mount the horns straight.
  6. Trims. Nudge each servo until Otto stands square -- see Calibration.
  7. Movement. Only now, on the floor, Motion::enable() and a single Legs::walkForward(1).

The Serial Console is built for exactly steps 2 to 6 -- it exercises one part per typed command, with no reflashing.

A bench-testing sketch

This is the pattern the project's own bench sketch follows: start with a full configuration, keep movement off, and drive everything by hand through the console.

#include <OttoFlow.h>

void setup() {
  OttoConfig cfg = Preset::Humanoid;
  cfg.arms.leftPin    = 10;
  cfg.mpu6050.enabled = true;      // GY-521 on A4/A5

  OttoFlow::start(cfg);
  Motion::disable();               // nothing moves until you say so
  Console::begin(9600);            // drive each part from the Serial Monitor
}

void loop() {
  Console::poll();
}

Open the Serial Monitor at 9600 baud and type help. Enable a servo group only when you are ready: legs on, then center, then a single-servo command.

Warning Motion::disable() in setup() is undone the moment you type legs on or motion on in the console. That is intentional -- it is how you opt in to movement for one test -- but it means the robot can move after that command. Keep it on the floor.

Why disabled means silent

Every guard in OttoFlow turns a disabled call into a silent no-op -- never a compile error, never a runtime warning, never a queued action that fires later. This is a deliberate design choice, explained in Architecture, and it buys two things:

  • No #ifdefs. Your loop() can freely call Legs::walkForward(1) even while movement is disabled. The same source runs on the bench and on the floor.
  • One sketch, many builds. A sketch that waves its arms runs unchanged on a biped with no arms -- the arm calls simply do nothing.

A one-call bench mode -- OttoFlow::benchMode() -- that bundles all of this with a clear indicator on the matrix is on the Roadmap.

Clone this wiki locally