Skip to content

Troubleshooting

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

Troubleshooting

Symptoms grouped by the part that shows them. Most first-build problems are wiring, servo horns, or configuration -- not code.

Nothing happens at all

No startup chirp, no face, nothing.

  • Confirm the upload actually succeeded, and that the Serial Monitor baud rate matches your sketch (9600 in every example here).
  • Check power. A Nano on USB alone often cannot drive the servos; use the battery pack for anything that moves.
  • Many Nano clones need the "old bootloader" option, or board_upload.speed = 57600. See Installation.

The chirp plays but the sketch seems dead. You probably did work in setup() that belongs in loop(), or forgot to call Console::poll() / AppLink::poll() every iteration.

Movement

Otto curves to one side when walking straight. The legs need trims. Run through Calibration -- center, then trim, then trim save.

Servos buzz but do not hold position, or Otto collapses. Under-powered. Servos draw far more current than USB provides; run from batteries.

A servo moves the wrong way, or "90 degrees" looks off by a lot. The horn is mounted a whole spline off. Detach it, centre the servo with center (or Legs::positionAllDegrees(90,90,90,90)), reseat the horn straight, then trim the small remainder.

Movement calls do nothing. The legs are disabled. Legs::enable(), or legs on in the console. Remember that Motion::disable() -- common in bench sketches -- switches them off.

The sketch freezes during a move. It is not frozen; movement calls block until the motion finishes. A Legs::walkForward(4, 1000) ties up the robot for about four seconds. See Movement, and the non-blocking engine on the Roadmap.

Arms do nothing. Arms are off unless enabled: Preset::Humanoid or config.arms.enabled = true. Check Arms::isActive(). If they move the wrong way, the horn is reversed -- see Arms.

The matrix

Icons appear rotated or upside down. A mounting question, not a fault. Change config.matrix.orientation (1 top, 2 bottom, 3 left, 4 right).

The matrix is blank. Check the three data pins against the default wiring: DIN A3, CS A2, CLK A1. If it is dim rather than blank, raise config.matrix.brightness.

Text scrolls off instantly or garbles. Keep messages short and uppercase; text lives in RAM. A very low scroll-speed value scrolls too fast to read -- raise it.

The distance sensor

Always reads out of range (999). No echo is coming back. Check trigger on pin 8 and echo on pin 9, and that nothing is closer than about 3 cm (below that the echo returns too soon). Use watch dist in the console and wave your hand.

Readings jump around wildly. Enable or strengthen the median filter: config.ultrasonic.medianFilter = true and a higher samples. Soft or angled surfaces scatter the echo and genuinely cannot be measured well.

Readings are slow / the loop lags. Each ping waits up to 25 ms for a timeout, and the filter adds settling delays. Lower samples, or point Otto where something is usually in range. See Eyes.

Sensors

Touch fires many times per touch, or never once. Use wasTapped(), not isTouched(), for once-per-touch events. If your board is the toggle type, set config.touch.toggleMode = true. See Touch.

The microphone reads constant nonsense. Check the gain potentiometer on the sensor board, and find your room's idle level with watch mic before choosing a threshold. An unconnected pin reads as noise.

Light readings are inverted. Your divider is wired the other way; swap the LDR and fixed resistor, or invert the value. Note the default pin is A7, while official single-sensor examples use A0.

tilt / accel say the MPU-6050 is not connected. It needs enabling (config.mpu6050.enabled = true) and wiring to A4 (SDA) and A5 (SCL). If your board pulls AD0 high, set config.mpu6050.i2cAddress = 0x69. Mpu6050::isConnected() confirms the bus link.

Sound

No sound at all. The buzzer may be muted -- Voice::unmute(), or config.buzzer.muted = false. Bench sketches often mute it. Also confirm the buzzer is on pin 13.

Two beeps sound like one. Add a trailing silence: Voice::playToneHz(hz, ms, 60). See Voice.

A bendTonesHz sweep hangs. The step direction and the start/end frequencies disagree -- a step above 1.0 must sweep upward, below 1.0 downward.

Bluetooth and the app

The module never connects. Check the crossover: module TXD to Nano pin 11, module RXD to pin 12. Confirm config.bluetooth.enabled = true and the baud rate (usually 9600). Many modules need 3.3 V on RX -- see Bluetooth and the Otto App.

The app connects but Otto ignores commands. Make sure AppLink::poll() runs every loop, and that only one consumer -- AppLink or Console -- is reading the stream, never both.

The app connects but Otto will not move. Movements respect Legs::disable(). A disabled robot acknowledges commands without moving.

Build errors

heart, smile, or similar "does not name a type". An OttoDIYLib mouth macro leaked into your sketch. You included <OttoFlowDriver.h> (or <Otto.h>) and used one of those words as a name. Rename your variable, or keep the driver include out of files that do not need it. See The Driver.

Otto does not name a type when using the driver. Include <OttoFlowDriver.h> after <OttoFlow.h>, and call OttoFlow::driver() only after OttoFlow::start().

Linker cannot find OttoDIYLib or Servo. Both are dependencies OttoFlow declares; make sure your lib_deps (PlatformIO) or installed libraries (Arduino IDE) include them. See Installation.


If none of this fits, open an issue with your board, your OttoConfig, and what you see versus what you expected.

Clone this wiki locally