Skip to content

Examples

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

Examples

Complete, ready-to-flash sketches. Copy any one into your project's main.cpp (PlatformIO) or .ino (Arduino IDE) and upload -- each is a whole program, setup() and loop() included.

Note Two things about the movement calls below: walking, turning, and gestures are blocking -- the call returns only when the motion finishes -- and the walking sketches genuinely drive the robot across the floor, so give it space and be ready to catch it. See Bench Testing for the safe bring-up workflow.

Obstacle avoider

The classic: Otto walks forward and turns away from anything in its path.

Needs: the standard biped kit (distance sensor + leg servos), and room to walk.

#include <OttoFlow.h>

const long AVOID_CM = 15;      // react when something is closer than this

void setup() {
  OttoFlow::start();           // classic Otto biped kit
  Mouth::show(Icon::Happy);
  Voice::play(Sound::Hello);
}

void loop() {
  if (Eyes::closerThanCm(AVOID_CM)) {
    // Something is in the way: stop, react, then turn until the path is clear.
    Mouth::show(Icon::Surprised);
    Voice::play(Sound::OhOoh);
    Legs::walkBackward(1);

    while (Eyes::closerThanCm(AVOID_CM)) {
      Legs::turnRight(1);
    }

    Mouth::show(Icon::Happy);
  } else {
    // Clear ahead: take a step.
    Legs::walkForward(1);
  }
}

How it works

  • Eyes::closerThanCm(15) is the whole obstacle test. The distance reading is median-filtered for you, so a single bad echo will not trigger a false turn.
  • Because moves block, the logic reads straight down the page: back off one step, keep turning right until the way ahead opens, then carry on walking.
  • Turning until clear, rather than a fixed amount, means Otto handles corners and walls it turns into -- not just one obstacle at a time.

Curious desk pet

A safe-on-a-desk companion: it never walks, but reacts to a hand, a tap, and a clap with faces, sounds, and a wave.

Needs: distance sensor, touch sensor (pin A0), and microphone (pin A6). Arms are optional -- the wave is a no-op without them.

#include <OttoFlow.h>

void setup() {
  OttoFlow::start(Preset::Humanoid);   // arms if present; harmless on a biped
  Mouth::show(Icon::Happy);
  Voice::play(Sound::Hello);
  Arms::waveRight(2);                   // silently skipped if this build has no arms
}

void loop() {
  // A tap on the head -> affection
  if (Touch::wasTapped()) {
    Mouth::show(Icon::Heart);
    Voice::play(Sound::Cuddly);
    Arms::waveRight(1);
    delay(800);
    return;
  }

  // A clap or loud noise -> surprise
  if (Ears::hearsSoundLouderThanPercent(65)) {
    Mouth::show(Icon::SmallSurprise);
    Voice::play(Sound::OhOoh);
    delay(600);
    return;
  }

  // Otherwise the resting face follows how close your hand is
  Mouth::show(Eyes::closerThanCm(10) ? Icon::Surprised : Icon::Happy);
}

How it works

  • Touch::wasTapped() fires once per tap because it detects the change, so a resting finger does not keep re-triggering. It works whether your sensor is momentary or toggle -- see Sensors.
  • The return after each reaction lets the heart or surprise face linger for its delay() before the resting face takes back over.
  • Arms::waveRight() on a biped build is a silent no-op, so the one sketch runs on both kits with no #ifdef.
  • The loop issues no leg commands, so the feet stay put -- safe on a desk.

Clap to dance

Clap to cue the next move in a playlist of gestures.

Needs: microphone (pin A6), and room to move -- gestures move the legs.

#include <OttoFlow.h>

// A little show; each clap advances to the next move.
const Gesture SHOW[] = {
  Gesture::Happy, Gesture::Love, Gesture::Victory,
  Gesture::Confused, Gesture::Magic, Gesture::SuperHappy
};
const uint8_t SHOW_COUNT = sizeof(SHOW) / sizeof(SHOW[0]);
uint8_t next = 0;

void setup() {
  OttoFlow::start();           // classic biped
  Mouth::show(Icon::Happy);
}

void loop() {
  if (Ears::hearsSoundLouderThanPercent(70)) {
    Gestures::play(SHOW[next]);          // blocks until the move finishes
    next = (next + 1) % SHOW_COUNT;      // wrap back to the start
  }
}

How it works

  • Each gesture is a whole performance -- movement, face, and sound -- and blocks until it ends, then leaves Otto back in a neutral standing pose.
  • Stepping through a fixed playlist keeps the show predictable and needs no random-seed setup.
  • Raise the 70 threshold if a noisy room keeps setting Otto off; lower it if it ignores your claps.

Do not drop me

Hold Otto in your hand and it reacts to being shaken, tilted, or turned upside down -- a good first check that the motion sensor works.

Needs: the MPU-6050 motion sensor wired to I2C (A4/A5) and enabled in the config.

#include <OttoFlow.h>

Icon mood = Icon::Happy;

void setup() {
  OttoConfig cfg;
  cfg.mpu6050.enabled = true;    // GY-521 board on I2C (A4 = SDA, A5 = SCL)
  OttoFlow::start(cfg);

  Motion::disable();             // held in hand: keep the servos off and quiet
  Mouth::show(mood);
}

void loop() {
  Icon next;
  if      (Balance::isShakenHarderThanG(0.8))  next = Icon::Surprised;
  else if (Balance::isUpsideDown())            next = Icon::Confused;
  else if (!Balance::isLevelWithinDegrees(25)) next = Icon::SmallSurprise;   // tilted
  else                                         next = Icon::Happy;           // level and calm

  if (next != mood) {            // act only when the mood actually changes
    mood = next;
    Mouth::show(mood);
    if (mood == Icon::Surprised) Voice::play(Sound::Surprise);
    if (mood == Icon::Confused)  Voice::play(Sound::OhOoh);
  }
}

How it works

  • The four conditions are tested most-urgent first, so a hard shake wins over a mere tilt.
  • The next != mood guard is the key idea: it turns a continuously-true condition (you are still holding it tilted) into a single reaction, so the sound plays once on the change instead of machine-gunning every loop.
  • Motion::disable() detaches the servos while you handle the robot -- no current draw, no buzzing, nothing to move by accident. The motion sensor is unaffected; Balance reads the MPU directly.

Where to go next

Clone this wiki locally