Skip to content
Ahmed Dabak edited this page Jul 25, 2026 · 4 revisions

Arms

Introduction

Arms drives the two optional arm servos of the Otto Humanoid build. It is a facade over the ArmServos module.

Arms::raiseBoth();
Arms::waveRight(3);
Arms::lowerBoth();

Arms are off by default, because the classic biped does not have them. While they are off, every Arms call is a silent no-op -- so the same sketch runs on both builds without #ifdefs.

Enabling arms

Either take the preset:

OttoFlow::start(Preset::Humanoid);   // arms on pins 6 (left) and 7 (right)

or enable them explicitly, which is also how you move them to different pins:

OttoConfig cfg = Preset::Humanoid;
cfg.arms.leftPin  = 10;
cfg.arms.rightPin = 7;
OttoFlow::start(cfg);

Check at runtime whether this build has working arms:

if (Arms::isActive()) Arms::waveRight();

Center, up and down

The three whole-body arm poses:

Arms::center();           // rest: both arms to 90 degrees
Arms::up();               // both arms straight up   (left 180, right 0)
Arms::down();             // both arms at the sides  (left 0, right 180)

Arms::home() is an alias for center(), matching Legs::center().

The left and right servos face opposite directions, so the same physical pose needs mirrored angles -- that is why up() sends 180 to one arm and 0 to the other. center() is the exception: 90 mirrors to itself, which is also what makes it the right pose for mounting the horns.

Raising and lowering

Arms::raiseBoth();        // both arms up   (left 160, right 20)
Arms::lowerBoth();        // both arms down (left 20, right 160)
Arms::raiseLeft();        // left arm only
Arms::raiseRight();

raiseBoth() and lowerBoth() are the partial versions of up() and down() -- same mirroring, but you choose how far. The angle you pass is the left servo's; the right one gets 180 - degrees.

Each takes an explicit angle when the defaults do not suit your build:

Arms::raiseBoth(120);
Arms::raiseLeft(90);
Arms::lowerBoth(0);

Note raiseLeft() and raiseRight() take raw servo angles -- one arm, no mirroring, so they mean opposite physical directions. raiseRight(20) is the right arm's "up". Use ArmServos::positionBothDegrees() if you ever want the same raw angle on both.

Angles are 0 to 180 degrees and values above 180 are clamped. The movement is immediate -- the servo travels at its own speed and the call returns at once. To hold a pose, simply do not command a new angle.

Waving

Arms::waveRight();        // two waves
Arms::waveLeft(4);

A wave raises the arm high, drops it part way, repeats that for the number of times you asked, and finally lowers the arm. Both sides are mirrored, so waveRight() and waveLeft() look identical on the robot -- in raw servo angles that is 160 / 120 / 20 on the left and 20 / 60 / 160 on the right. It blocks for about 600 ms per wave.

A greeting that reads well:

Mouth::show(Icon::Happy);
Voice::play(Sound::Hello);
Arms::waveRight(2);

Relaxing and holding

Arms::relax();            // detach: stop driving the servos
Arms::hold();             // drive them again
bool active = Arms::isActive();

A detached servo consumes no current and makes no noise, and you can move the arm by hand -- useful while you check clearances or judge a mounting angle. A held servo resists being moved and hums quietly.

Motion::disable() relaxes the arms along with the legs. Motion::enable() re-attaches them, but only if arms are enabled in the configuration.

Note Arms::hold() on a build with arms.enabled = false does nothing, and isActive() stays false. That is the check to use in the serial console or a sketch that must know whether the hardware really exists.

Mounting and angles

What "up" means depends entirely on how you pressed the servo horns onto the splines. OttoFlow assumes the standard Humanoid build, where the two servos face opposite ways -- so every both-arm call mirrors, and up() means 180 on the left, 0 on the right.

If up() lowers both arms, both horns are on 180 degrees out. If it raises one arm and lowers the other, one horn is. Either way:

  • Pull the horn off, rotate it 180 degrees, and press it back on -- the mechanical fix, and the better one.
  • Or swap the calls in your sketch: use down() where you mean up, and raiseBoth(20) / lowerBoth(160).

Before mounting the horns, centre the servos so you can align them with the arm at rest:

Arms::center();

Going lower: the ArmServos module

ArmServos::attach();
ArmServos::detach();
bool attached = ArmServos::isAttached();

ArmServos::positionLeftDegrees(140);
ArmServos::positionRightDegrees(40);
ArmServos::positionBothDegrees(90);          // same angle to both
ArmServos::positionBothMirroredDegrees(140); // left 140, right 40

ArmServos::center();  ArmServos::up();  ArmServos::down();

ArmServos::waveRight(2);
ArmServos::waveLeft(2);

The module is driven by the standard Arduino Servo library, independently of OttoDIYLib -- which is why arm angles are plain servo angles with no trim applied. If you need arm calibration, add a constant offset in your own code.

Clone this wiki locally