Skip to content

EmberOS firmware

EmberOS is the robot half of Ember: a firmware image built from WireOS, which is itself a rebuild of Anki’s own robot software from the sources Digital Dream Labs released.

Installing it is optional. Ember’s server and brain work with a robot on stock WireOS; the firmware exists because some things — detectors that stay on without a client holding behaviour control, a thermal daemon, styleable eyes — cannot be done from a server at all.

The robot’s boot animation is generated rather than drawn: a glowing ember with rising sparks that settles into the name, written out as raw 184×96 RGB565 frames at 24 frames a second — the format the robot’s boot animation process memory-maps. The generator lives with the firmware files, so changing the animation is running a script, not editing an asset.

It points at your server, not at anyone else’s

Section titled “It points at your server, not at anyone else’s”

The robot’s cloud client finds its server by mDNS name rather than by a hard-coded address, and the certificate that server presents is embedded in the image and trusted, so a robot pairs without needing an override file dropped onto him first. The updater’s base URL, the fault-code screen and the rescue screen all point at your own server too. There are no third-party addresses left in the image, and the info screen says EmberOS.

Out of the box, a docked robot makes a small movement about every thirty seconds, forever. EmberOS stretches that curve so a settled robot moves about every thirty minutes instead. Eye contact and his reaction to movement are untouched — he still looks up when you walk past.

The same change is available from the server as the calm behaviour preset, applied over SSH with no rebuild, if you would rather not reflash.

The animation process reads an eye-style file from the robot and applies it after the usual interpolation: scale, offset, angle, corner radii, lid offset and bend, and spacing. It re-reads the file when its modification time changes, roughly once a second, so the dashboard’s eye builder updates live. The animation loader also reads a directory of the owner’s own clips.

On stock firmware, a detector a server asks for is refused unless the server holds behaviour control, and is dropped the moment control is released. EmberOS adds a file on the robot that switches detectors on independently of any client: faces, motion, markers, and the newer ones below. That is the mission applied to the robot — the detections flow, and whatever wants them subscribes.

It also exposes detections that never left the robot before — pets, laser points, illumination and the neural-net person and hand detections — through the robot’s external protocol, converted the same way motion already was. See Vision.

Taking behaviour control over the robot’s SDK used to switch his cliff sensors off. That is not a theory: it is how this robot drove off a desk. Later builds keep the reflex armed while a client holds control, and report it firing as an event so the server knows why an action stopped and whether he reversed off the edge.

Until such a build is installed, Ember’s social engine refuses every step that turns his wheels, whatever the settings say.

WireOS defaults the robot to a 1.27 GHz CPU ceiling against a stock 533 MHz, and this robot reached 87 °C there. Meanwhile the platform’s own thermal throttling is disabled at boot by the vendor’s init script.

EmberOS adds a small daemon that reads the wanted CPU level from a file on the robot, enforces a temperature guard at 70 and 80 °C, and re-asserts the ceiling over anything else that writes it. The server drives it from the dashboard’s Power page rather than reaching in over SSH every ten seconds.

Check the CPU profile after every firmware change. It is the single easiest thing to get wrong and the most expensive.

Behaviour presets are folders of replacement behaviour JSON. They are normally applied over SSH by the server, which keeps a copy of each original alongside so a preset can be undone; the shipped set is calm, quiet, quiet-night, sleepy, companion, playful, show-off, cat, wall-e and stock. Newer builds carry an on-robot installer so that a preset survives the slot swap a firmware update performs — otherwise an update lands you on a slot that has never had the preset applied, and the server has to notice and re-apply it.

The largest change, and the one with the most caveats. See Power and battery for what it does and why it did not work first time.

BuildWhat it added
3.0.1.2dThis server’s addresses and its link certificate baked in
3.0.1.4dEye styles, applied from a file on the robot and re-read about once a second, so the dashboard’s eye builder changes his face live
3.0.1.8dAlways-on detectors without behaviour control; pet, laser, illumination and salient-point events exposed to the SDK
3.0.1.9dThe thermal daemon: CPU levels and the temperature guard, on the robot
3.0.1.10dCalm power mode when docked
3.0.1.11dThe on-robot preset installer; cube app intents; cliff-stop kept armed under SDK control
3.0.1.12dThe image built at its used size, roughly halving the slot write
3.0.1.13dCliff handling in three modes with a reported stop event; a gateway reconnect when the engine restarts; the docked power save made switchable

Builds up to 3.0.1.11d have been installed on a robot. 3.0.1.12d and 3.0.1.13d are built and staged and have never run on hardware.

The robot has two system slots, A and B, plus recovery partitions that an update never writes. An update writes the slot you are not running from, marks it active, and reboots into it. The slot you were on is left exactly as it was.

That is the rollback: set the other slot active and reboot. It costs a command and a restart and gets you back to a known-good image, which is why testing a new build means installing it, not hand-copying binaries onto a running robot.

Images live in a firmware directory on the server. Nothing is committed to git — they are around 160 MB each.

The server exposes them two ways. One is a direct download the dashboard uses. The other is the robot’s own updater endpoint: when the robot checks for an update, the server answers with a newer build if it has one, and a 404 if it does not.

Installing is a one-shot: the server stops the robot’s update timer, writes a one-shot environment file naming the image, and starts the update service. Progress comes back from the robot’s own updater state files, so the dashboard can show a phase and a percentage. When the updater reports done, the robot is restarted.

Three things about that sequence were learned the hard way, and all three are now enforced in code:

  • Never restart the voice server while a robot is downloading. The download truncates and the updater aborts. The server counts downloads in flight and the supervisor and the deploy script both refuse to bounce it while any are running.
  • A rebuilt server binary loses the capability to bind port 443. Without it the supervisor crash-loops the server every twelve seconds. Always deploy through the script, which re-applies it.
  • Restarting the update service is not the same as starting it. The unit deletes the one-shot file when it stops, so a restart wipes the URL and the updater falls back to timer mode and does nothing.

The build is Yocto, unchanged from upstream, and takes about 42 minutes clean or five minutes cached. Two constraints:

  • Build as a non-root user, with a PTY. As root it fails creating a user; with no terminal it fails on an interactive container run. Wrapping the build in setsid script -qefc "..." satisfies both.
  • The signature handler is a no-op in this configuration, which means the build system never notices that a recipe’s input files changed. A recipe whose files you have edited only ships if that recipe is on the clean list. Add new recipes to it or the image quietly carries stale files.

One more trap worth knowing: the cloud client in a built image is compressed, so searching the image for its strings finds nothing. Decompress it before concluding a change did not make it in.

3.0.1.11d was installed and two of its changes were confirmed on the robot: the on-robot preset installer applied a preset, and cliff-stop stayed armed while the server held behaviour control. Two were not: the docked power saving did not engage, because passive status reads from the server counted as activity, and the gateway socket reconnect did not fire on the write path. 3.0.1.13d is meant to fix both, and has not been installed.

Everything since 3.0.1.11d is staged and has never run on hardware. The robot’s battery failed and no firmware has been installed since. Where this documentation describes newer behaviour, treat it as a description of the code rather than of an observed robot.

The recovery path has not been exercised either. An update writes the inactive slot and leaves the other one bootable, and no update has yet touched the recovery partition — but flashing an unsigned image on a robot nobody makes any more is not a reversible operation if it goes wrong.