ESP32 firmware for the ai-tiny-bot companion robot. Talks to tiny-bot-cloud-agent.
cd ../tiny-bot-cloud-agent
make build-all
./bin/seed -device-id tinypal-01 -pairing-code ABCD-1234
./bin/tiny-bot-cloud-agentWhat does seed do?
It pre-registers a device in the cloud SQLite DB (device_id + pairing_code) — an “entry pass” for this ESP32.
It does not bring the board online by itself. Full flow:
- Server runs
seed→ device row exists - Board boots → Wi‑Fi →
POST /provision(same ID/code) → token - Connects
ws://…/ws→ ready to talk
Skip seed and you get device not found / HTTP 401; OLED stays on Reconnecting....
TB_DEVICE_ID / TB_PAIRING_CODE in config.h must match seed. On a remote host, run seed (or make seed-remote) on the machine that runs cloud-agent.
cp include/config.h.example include/config.h
vi include/config.h # Wi-Fi, WS_HOST, TB_DEVICE_ID, TB_PAIRING_CODEinclude/config.h holds private Wi‑Fi / host settings and is gitignored. Only config.h.example is committed.
Install the PlatformIO IDE extension (CLI is usually at ~/.platformio/penv/bin/pio).
If pio is not on your PATH:
# Option A: add to PATH (~/.zshrc)
export PATH="$HOME/.platformio/penv/bin:$PATH"
# Option B: use the Makefile (resolves that path for you)
make audiomake audio # mic + amp test firmware
make main # build + flash full firmware
make monitor # serial monitorWith the cloud ready, OLED shows a face and bottom status Speak anytime. Just talk (hands-free energy VAD; no BOOT button to start):
- Idle: blink / look around
- Speak → listening face +
Listening... - ~1.1s silence → thinking +
Thinking...→ happy +Speaking - During thinking/speaking: speak again or press BOOT to barge-in (serial:
[main] barge-in,[cloud] >> interrupt) - After TTS (or barge-in end), back to idle
Transcript and reply text go to the serial port, not the OLED.
Tune in config.h: TB_VAD_*, TB_VAD_BARGE_*, TB_SPK_GAIN_Q8. Loudness/noise: flash and listen on device (compile-checked only in CI/dev without board).
tiny-bot-firmware/
├── platformio.ini
├── src/
│ ├── main.cpp # state machine + record/play
│ ├── oled_face.h / oled_face.cpp # OLED eye animations
│ ├── cloud_client.h / .cpp # HTTP provision + WebSocket
│ ├── step1_blink.cpp
│ ├── step2_oled.cpp
│ └── step3_audio.cpp
├── include/
│ └── config.h.example
└── docs/
Follow the step-by-step build guide:
| Stage | File | Purpose |
|---|---|---|
| Step 1 | src/step1_blink.cpp |
LED blink |
| Step 2 | src/step2_oled.cpp |
OLED |
| Step 3 | src/step3_audio.cpp |
Audio |
| Final | src/main.cpp |
Full AI chat |
| Field | Required | Example | Notes |
|---|---|---|---|
WIFI_SSID / WIFI_PASSWORD |
yes | "MyHomeWiFi" |
Wi‑Fi |
HTTP_BASE |
yes | "http://192.168.1.100:5678" |
Cloud HTTP base |
WS_HOST / WS_PORT / WS_PATH |
yes | "192.168.1.100" / 5678 / "/ws" |
WebSocket |
TB_DEVICE_ID |
yes | "tinypal-01" |
Must match seed |
TB_PAIRING_CODE |
yes | "ABCD-1234" |
Cleared from NVS after first provision |
TB_VAD_SPEECH_THRESHOLD |
18 |
Speech peak threshold (0–100) | |
TB_VAD_SILENCE_MS |
1100 |
Silence to end utterance | |
TB_VAD_MIN_SPEECH_MS |
400 |
Min speech before silence can end | |
TB_VAD_REARM_DELAY_MS |
250 |
Cool-down before listening again | |
TB_VAD_BARGE_THRESHOLD |
40 |
Barge-in peak threshold while speaking | |
TB_VAD_BARGE_CHUNKS |
3 |
Consecutive loud chunks before barge-in (~96ms) | |
TB_SPK_GAIN_Q8 |
220 |
Digital speaker gain (Q8; 256=1.0); raise if GAIN=6dB is quiet |
- Human guide: ../tiny-bot-cloud-agent/docs/human-docs/02-firmware-integration.md
- Machine-readable: ../tiny-bot-cloud-agent/docs/firmware-integration/
- Barge-in: energy + BOOT during thinking/speaking; raise
TB_VAD_BARGE_THRESHOLDif speaker loopback false-triggers (no AEC) - VAD: on-device peak energy; noisy rooms may need a higher
TB_VAD_SPEECH_THRESHOLD - Audio UX on device: digital gain + idle I2S clock stop are implemented; verify loudness/hiss on hardware
- OLED: face + status bar only; chat text on serial
- Multi-device: NVS key is fixed
tinybot/token(fine for one device) - WS auth fail: clears NVS token and re-provisions on next boot
pio device monitor
wscat -c ws://192.168.1.100:5678/ws
pio run --target erase # or esptool.py erase_flash — force re-provision