# robot-face
Expressive robot eyes for a 128×64 monochrome OLED (SSD1306).
**[▶ Live demo](https://robot-face-92b63d.gitlab.io/)** — the same face in the browser:
switch emotions, states and idle activities.

- **16 emotions picked by emoji**: an LLM starts a sentence with 😒, and the robot looks
irritated.
- **Voice-robot states**: idle, listening, thinking, speaking (a mouth on top of the
emotion), error.
- **7 idle activities** with a small prop and moving eyes, so the OLED does not burn in:
reading, writing, tea, humming, daydreaming, sleeping, the potty with a newspaper.
- **Tiny and predictable**: a frame is 1024 bytes in SSD1306 page format. `face_t` is at
most 512 bytes and owned by the caller. No `malloc`, no global state, no dependencies
besides ESP-IDF.
## Usage
```c
#include "robot_face.h"
#include "robot_face_ssd1306.h"
static face_t face;
static uint8_t fb[FACE_FB_SIZE];
static face_ssd1306_t oled; /* zero-initialized */
face_ssd1306_config_t cfg = { .addr = 0x3C, .scl_hz = 400000 };
face_ssd1306_init(&oled, bus, &cfg); /* bus: your i2c_master_bus_handle_t */
face_init(&face, esp_random());
face_set_emotion(&face, FACE_EMO_HAPPY); /* or face_emotion_from_emoji("😊", &e) */
face_set_activity(&face, FACE_ACT_READ); /* shown while idle */
for (;;) { /* every 1/30 s */
face_tick(&face, 1.0f / 30);
face_render(&face, fb);
face_ssd1306_draw(&oled, fb);
}
```
The adapter takes a ready I²C bus, so the OLED can share it with other devices. A full
frame takes ≈24 ms at 400 kHz and ≈31 ms at 300 kHz.
Full documentation, the API table, the JavaScript twin and an example project:
[gitlab.com/daveroboclub/robot-face](https://gitlab.com/daveroboclub/robot-face).
## License
MIT © 2026 MaksV
idf.py add-dependency "davemaks/robot_face^0.2.3"