Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .cursor/rules/conventional-commits.mdc
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ Scopes: `firmware`, `http`, `settings`, `anim`, `servos`, `wifi`, `integrations`

`integrations` is anything under `packages/` (Cursor, Antigravity, Claude Code, later agent CLIs). Do not add a new scope per package.

`mods` is anything under `3d_models/mods/`. Name the mod in the summary. Do not add a new scope per mod.
`mods` is anything under `mods/`. Name the mod in the summary. Do not add a new scope per mod.

**Breaking here:** removed/renamed HTTP route or query param; NVS key rename that drops settings; pinout change; default servo range change that invalidates calibration; hook CLI flag or event rename. Use `!` on the type **and** a `BREAKING CHANGE:` footer.

Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ jobs:
${{ runner.os }}-pio-
- name: Install PlatformIO
run: pip install -U platformio
- name: Audio pack tests
run: python3 scripts/test_audio_pack.py
- name: Build firmware
run: pio run
- name: Build OLED expression demo
Expand Down
2 changes: 1 addition & 1 deletion 3d_models/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Printable parts and source CAD for the Tiny Engineer desk robot.

The full assembly — all components composed — lives in [`cad/TinyEngineer.f3d`](cad/TinyEngineer.f3d) (Autodesk Fusion). Open that file to adjust the model or adapt it to different hardware sizing (e.g. different servos). Parametric servo sizes and the Fusion add-in: [docs/3d/parametric-design.md](../docs/3d/parametric-design.md).

**Adding a new part** (parametric rules, timeline, `PRINT_LAYOUT`, Servo Configurator, export, optional mods): [docs/3d/adding-parts.md](../docs/3d/adding-parts.md). Optional / community mods: [`mods/`](mods/).
**Adding a new part** (parametric rules, timeline, `PRINT_LAYOUT`, Servo Configurator, export, optional mods): [docs/3d/adding-parts.md](../docs/3d/adding-parts.md). Optional / community mods: [`mods/`](../mods/).

## Printables

Expand Down
24 changes: 0 additions & 24 deletions 3d_models/mods/README.md

This file was deleted.

6 changes: 3 additions & 3 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ Tiny Engineer is an open-source ESP32-C3 Wi-Fi desk robot: 3D-printed mechanics,
| `src/`, `include/`, `lib/`, `data/` | Firmware (PlatformIO) |
| `packages/` | HTTP / hook CLIs (Cursor, Antigravity, Claude Code, …) — scope `integrations` |
| `3d_models/` | CAD and printables (CERN-OHL-S) |
| `mods/` | Optional mods; models under `mods/<name>/3d_models/` (CERN-OHL-S) |
| `hardware/` | KiCad boards (CERN-OHL-S) |
| `docs/` | Human docs; depth lives here |

Expand Down Expand Up @@ -62,7 +63,7 @@ Format: `type(scope): summary` (imperative, lowercase type, no trailing period;

Scopes: `firmware`, `http`, `settings`, `anim`, `servos`, `wifi`, `integrations`, `cad`, `mods`, `pcb`, `docs`, `scripts`, `ci`.
`integrations` = anything under `packages/`. Do not add a new scope per package.
`mods` = anything under `3d_models/mods/`. Name the mod in the summary. Do not add a new scope per mod.
`mods` = anything under `mods/`. Name the mod in the summary. Do not add a new scope per mod.

Breaking rules, SemVer mapping, and examples: [CONTRIBUTING.md](CONTRIBUTING.md).

Expand All @@ -72,8 +73,7 @@ Breaking rules, SemVer mapping, and examples: [CONTRIBUTING.md](CONTRIBUTING.md)
- **Settings** — layer checklist in [docs/settings.md](docs/settings.md). Never log raw `access_token`.
- **Integrations** — add/extend package tests; prefer short timeouts and ignore network errors so a missing robot does not stall the agent.
- **CAD** — edit `.f3d` **and** export affected `3mf`. CERN-OHL-S. Do not swap `AiEmblem.3mf` as branding. New parts: [docs/3d/adding-parts.md](docs/3d/adding-parts.md).
- **Mods** — optional CAD under `3d_models/mods/<mod_name>/`. Commit `type(mods)` and name the mod in the summary. `feat(mods)` / `fix(mods)` do not version stock CAD. See [3d_models/mods/README.md](3d_models/mods/README.md).
- **Mods** — optional CAD under `3d_models/mods/<mod_name>/`. Commit `type(mods)` and name the mod in the summary. `feat(mods)` / `fix(mods)` do not version stock CAD. See [3d_models/mods/README.md](3d_models/mods/README.md).
- **Mods** — optional add-ons under `mods/<mod_name>/`. Models live in `3d_models/{cad,parts}/`. Commit `type(mods)` and name the mod in the summary. `feat(mods)` / `fix(mods)` do not version stock CAD. See [mods/README.md](mods/README.md).
- **PCB** — [docs/pcb.md](docs/pcb.md) checklist + KiCad review rules below. Run `python3 scripts/check_pcb.py` (KiCad 10). New board paths need `REUSE.toml`. CERN-OHL-S.
- **Motion** — poses −1..1 mapped to saved min/max; see [docs/robot-movement.md](docs/robot-movement.md). Do not widen NVS servo clamps without testing on a real robot.
- **Secrets** — no `.env`, tokens, or Wi-Fi passwords in logs or screenshots.
Expand Down
8 changes: 5 additions & 3 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ This is a desk robot: firmware on an ESP32-C3, 3D-printed mechanics, and HTTP cl
- Integrations: Cursor hooks, Antigravity CLI, Claude Code hooks, or any REST client ([docs/integration.md](docs/integration.md))
- Docs, wiring, BOM corrections
- CAD / printables (`3d_models/`)
- Optional mods (`mods/`)
- KiCad PCB boards (`hardware/boards/` — [docs/pcb.md](docs/pcb.md))
- Photos of a working build or a failure (brownout, binding, blink codes)

Expand All @@ -21,9 +22,10 @@ Opening a PR licenses your change under the license of the files you touch. No C
| --- | --- |
| Firmware, packages, scripts, docs | [MIT](LICENSE) — see [LICENSING.md](LICENSING.md) |
| `3d_models/cad/`, `3d_models/parts/` | [CERN-OHL-S-2.0](3d_models/LICENSE) |
| `mods/*/3d_models/cad/`, `mods/*/3d_models/parts/` | [CERN-OHL-S-2.0](3d_models/LICENSE) |
| `hardware/boards/` | [CERN-OHL-S-2.0](hardware/LICENSE) |

If you modify the hardware designs and distribute Products based on them, CERN-OHL-S-2.0 requires you to make the Complete Source available under the same license. Keep [3d_models/NOTICE](3d_models/NOTICE) and [hardware/NOTICE](hardware/NOTICE) Source Location accurate for the revision you ship.
If you modify the hardware designs and distribute Products based on them, CERN-OHL-S-2.0 requires you to make the Complete Source available under the same license. Keep [3d_models/NOTICE](3d_models/NOTICE) and [hardware/NOTICE](hardware/NOTICE) Source Location accurate for the revision you ship. For a mod product, the Source Location is that mod’s `3d_models` tree ([mods/README.md](mods/README.md)).

The **Tiny Engineer** name, logo, and [`3d_models/parts/sg90/3mf/AiEmblem.3mf`](3d_models/parts/sg90/3mf/AiEmblem.3mf) are **not** licensed. Factual “based on Tiny Engineer” is fine. Do not imply an official product. Details: [TRADEMARK.md](TRADEMARK.md).

Expand Down Expand Up @@ -61,7 +63,7 @@ python3 scripts/check_pcb.py

**CAD.** Edit [`3d_models/cad/TinyEngineer.f3d`](3d_models/cad/TinyEngineer.f3d) **and** export the affected [`3d_models/parts/{servo_id}/3mf/*.3mf`](3d_models/parts/). Keep CERN-OHL-S. Do not swap `AiEmblem.3mf` as a branding change. New parts: [docs/3d/adding-parts.md](docs/3d/adding-parts.md). Servo presets / add-in: [docs/3d/parametric-design.md](docs/3d/parametric-design.md).

**Mods.** Optional CAD under [`3d_models/mods/<mod_name>/`](3d_models/mods/README.md). Commit as `type(mods)` and name the mod in the summary. Do not add a scope per mod. `feat(mods)` / `fix(mods)` do not version the stock CAD revision.
**Mods.** Optional add-ons under [`mods/<mod_name>/`](mods/README.md). Models live in `3d_models/{cad,parts}/`; other files sit beside that folder. Commit as `type(mods)` and name the mod in the summary. Do not add a scope per mod. `feat(mods)` / `fix(mods)` do not version the stock CAD revision.

**PCB.** Follow the [PCB checklist](docs/pcb.md#checklist). Run `python3 scripts/check_pcb.py` before opening a PCB PR. Keep [`expected-nets.yml`](docs/pcb.md#expected-nets-yml) in sync. One board per `hardware/boards/<name>/`, KiCad 10, ERC and DRC reviewed, no generated Gerbers or other fab outputs. Keep CERN-OHL-S. New board paths need a matching `[[annotations]]` block in [REUSE.toml](REUSE.toml).

Expand Down Expand Up @@ -95,7 +97,7 @@ Scopes: `firmware`, `http`, `settings`, `anim`, `servos`, `wifi`, `integrations`

`integrations` is anything under `packages/` (Cursor, Antigravity, Claude Code, later agent CLIs). Do not add a new scope per package.

`mods` is anything under `3d_models/mods/`. Name the mod in the summary. Do not add a new scope per mod.
`mods` is anything under `mods/`. Name the mod in the summary. Do not add a new scope per mod.

**Breaking in this repo** means: removed or renamed HTTP route or query param; NVS key rename that drops existing settings; pinout change; default servo range change that invalidates calibration; hook CLI flag or event rename. Call it out with `!` on the type and a `BREAKING CHANGE:` footer.

Expand Down
5 changes: 4 additions & 1 deletion LICENSING.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,18 +24,21 @@ Mechanical CAD, printable manufacturing outputs, and PCB sources are licensed un
| --- | --- |
| `3d_models/cad/` | Source CAD (`.f3d`) |
| `3d_models/parts/` | Printable part exports (`.3mf`) |
| `mods/*/3d_models/cad/`, `mods/*/3d_models/parts/` | Mod CAD and manufacturing exports |
| `hardware/boards/` | KiCad PCB projects |

Commercial use of these hardware designs is allowed. If you modify and distribute Products based on these designs, the reciprocal provisions of CERN-OHL-S-2.0 require that you make the corresponding Complete Source available under the same license. See [3d_models/LICENSE](3d_models/LICENSE), [3d_models/NOTICE](3d_models/NOTICE), [hardware/LICENSE](hardware/LICENSE), and [hardware/NOTICE](hardware/NOTICE) for copyright, warranty disclaimer, and Source Location details.

Documentation in `3d_models/README.md` and `hardware/README.md` is software documentation and remains under the MIT License.
Documentation in `3d_models/README.md`, `mods/README.md`, and `hardware/README.md` is software documentation and remains under the MIT License. Other files beside a mod’s `3d_models/` folder are MIT as well.

### Source Location

Canonical mechanical design source:

`https://github.com/jamro/tiny-engineer/tree/main/3d_models`

Optional mod CAD and exports use that mod’s `3d_models` tree (for example `https://github.com/jamro/tiny-engineer/tree/main/mods/halloween/3d_models`).

Canonical PCB design source:

`https://github.com/jamro/tiny-engineer/tree/main/hardware`
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ Cursor projects can map agent events to poses via hooks — [docs/hooks.md](docs
| Parts / cart | [docs/shopping.md](docs/shopping.md) |
| Wiring / power | [docs/hardware/README.md](docs/hardware/README.md) |
| Printable parts | [3d_models/README.md](3d_models/README.md) |
| Optional mods | [mods/README.md](mods/README.md) |
| Assemble printed parts | [docs/3d/assembly.md](docs/3d/assembly.md) |
| Resize CAD for another servo | [docs/3d/parametric-design.md](docs/3d/parametric-design.md) |
| Servo axes / safe ranges | [docs/robot-movement.md](docs/robot-movement.md) |
Expand All @@ -126,6 +127,6 @@ Print it, wire it, change the CAD, swap animations, or hook up a different agent
## License

- **Software** (firmware, integrations, scripts, documentation) — [MIT](LICENSE)
- **Hardware designs** (CAD source and `.3mf` printables in [`3d_models/`](3d_models/); KiCad PCBs in [`hardware/`](hardware/)) — [CERN-OHL-S-2.0](3d_models/LICENSE)
- **Hardware designs** (CAD source and printables in [`3d_models/`](3d_models/) and under [`mods/*/3d_models/`](mods/); KiCad PCBs in [`hardware/`](hardware/)) — [CERN-OHL-S-2.0](3d_models/LICENSE)

See [LICENSING.md](LICENSING.md) for scope and effective date. The **Tiny Engineer** name and logo are not licensed — see [TRADEMARK.md](TRADEMARK.md).
10 changes: 10 additions & 0 deletions REUSE.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,16 @@ path = "3d_models/parts/**"
SPDX-FileCopyrightText = "2026 Krzysztof Jamroz"
SPDX-License-Identifier = "CERN-OHL-S-2.0"

[[annotations]]
path = "mods/**/3d_models/cad/**"
SPDX-FileCopyrightText = "2026 Krzysztof Jamroz"
SPDX-License-Identifier = "CERN-OHL-S-2.0"

[[annotations]]
path = "mods/**/3d_models/parts/**"
SPDX-FileCopyrightText = "2026 Krzysztof Jamroz"
SPDX-License-Identifier = "CERN-OHL-S-2.0"

[[annotations]]
path = "LICENSE"
SPDX-License-Identifier = "MIT"
Expand Down
2 changes: 2 additions & 0 deletions assets/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ WAV clips played by animations and the setup wizard. Format: **44100 Hz, mono, 1

`pio run` copies these files into `data/` via [`scripts/copy_assets.py`](../scripts/copy_assets.py). The next filesystem image overwrites `data/`, so edit the files here. Flash them with `pio run -t upload` or `pio run -t uploadfs`.

`custom_audio_mod` in [`platformio.ini`](../platformio.ini) is empty by default, so this folder is the whole image. Set it to a mod name to overlay `mods/<name>/assets/*.wav` on top of these files. Clips the mod omits stay the files below. See [docs/flash.md](../docs/flash.md).

| File | Duration | Transcript |
| --- | --- | --- |
| [`abort.wav`](abort.wav) | 2.5 s | Fine! I didn't want to finish that anyway! |
Expand Down
20 changes: 11 additions & 9 deletions docs/3d/adding-parts.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,22 +105,24 @@ After the CAD and exports land:

- **Docs sync** — new stock part → parts table in [`3d_models/README.md`](../../3d_models/README.md); color-group / aggregate change → [order-parts.md](order-parts.md); join-order change → [assembly.md](assembly.md).
- **Trademark** — do not replace or repurpose `AiEmblem` as branding. See [TRADEMARK.md](../../TRADEMARK.md).
- **License** — `cad/` and `parts/` (including under mods) are [CERN-OHL-S-2.0](../../3d_models/LICENSE). When distributing Products based on these designs, keep Source Location accurate per [`3d_models/NOTICE`](../../3d_models/NOTICE).
- **License** — stock `cad/` and `parts/`, and mod geometry under `mods/<mod_name>/3d_models/{cad,parts}/`, are [CERN-OHL-S-2.0](../../3d_models/LICENSE). When distributing Products based on these designs, keep Source Location accurate per [`3d_models/NOTICE`](../../3d_models/NOTICE). For a mod, the Source Location is that mod’s `3d_models` tree.

## Optional mods

Optional elements and community mods do **not** go in the main `cad/` / `parts/` trees as first-class stock parts unless they become core. Overview: [`3d_models/mods/README.md`](../../3d_models/mods/README.md).
Optional elements and community mods live under [`mods/`](../../mods/README.md). Promote one into stock `cad/` and `parts/` only when it becomes core.

Place them under:

```text
3d_models/mods/<mod_name>/
cad/ # Fusion source for the mod
parts/ # Exported meshes, same layout idea as 3d_models/parts/
README.md # optional but recommended: fit notes, which servo folders exported
mods/<mod_name>/
README.md # optional: what it is, non-model notes
3d_models/
cad/ # Fusion source for the mod
parts/ # Exported meshes, same layout idea as 3d_models/parts/
... # anything that is not a model
```

Mirror the top-level `3d_models` structure (`cad` + `parts`). Keep CERN-OHL-S licensing consistent with [`3d_models/LICENSE`](../../3d_models/LICENSE) when you distribute Products based on these designs. Each mod may include a short `README.md` describing what it fits and which `parts/{servo_id}/` folders were exported.
Mirror stock `3d_models` inside the mod (`cad` + `parts`). Keep CERN-OHL-S licensing consistent with [`3d_models/LICENSE`](../../3d_models/LICENSE) when you distribute Products based on these designs. Each mod may include a short `README.md` describing what it fits and which `parts/{servo_id}/` folders were exported.

Commit and PR title: `type(mods): summary` — name the mod in the summary. `feat(mods)` / `fix(mods)` do not version the stock CAD revision. Promoting a mod into stock `cad/` and `parts/` is `feat(cad)`. See [CONTRIBUTING.md](../../CONTRIBUTING.md).

Expand All @@ -142,15 +144,15 @@ Commit and PR title: `type(mods): summary` — name the mod in the summary. `fea
- [ ] Docs synced (README parts table / order-parts / assembly as needed)
- [ ] `AiEmblem` not repurposed as branding
- [ ] CERN-OHL-S respected; NOTICE Source Location accurate if distributing Products
- [ ] Optional/mod work under `3d_models/mods/<mod_name>/{cad,parts}/` (+ mod README)
- [ ] Optional/mod work under `mods/<mod_name>/3d_models/{cad,parts}/` (+ mod README)

## Related

| Topic | Doc |
| --- | --- |
| Servo params, add-in install, new servo preset | [parametric-design.md](parametric-design.md) |
| Print / part inventory | [`3d_models/README.md`](../../3d_models/README.md) |
| Optional mods folder | [`3d_models/mods/README.md`](../../3d_models/mods/README.md) |
| Optional mods folder | [`mods/README.md`](../../mods/README.md) |
| Order aggregated sets | [order-parts.md](order-parts.md) |
| Mechanical assembly | [assembly.md](assembly.md) |
| Trademark | [TRADEMARK.md](../../TRADEMARK.md) |
Expand Down
1 change: 1 addition & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ Pick a path. Depth lives in the linked pages.
| **Order printed parts** (no 3D printer) | [3d/order-parts.md](3d/order-parts.md) |
| **Adapt CAD to a different servo** | [3d/parametric-design.md](3d/parametric-design.md) |
| **Add a new CAD / printable part** | [3d/adding-parts.md](3d/adding-parts.md) |
| **Optional mods** | [../mods/README.md](../mods/README.md) |
| **Use with an agent** (robot already on Wi-Fi) | [integration.md](integration.md) · Cursor: [hooks.md](hooks.md) |

### Reference and contribute
Expand Down
2 changes: 2 additions & 0 deletions docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -486,6 +486,8 @@ curl -X POST "http://tiny-engineer.local/anim?name=none"

Eye detail below for `eyes_style=classic` (default). With `eyes_style=cover`, the same sequences drive full-half bars ([Cover eyes](#cover-eyes)). With `eyes_style=dots`, the same sequences drive 12×12 circles ([Dots eyes](#dots-eyes)). With `eyes_style=kaomoji`, faces follow [Kaomoji eyes](#kaomoji-eyes) instead of procedural blinks, glances, flicker, or X eyes. Servo/audio behavior is the same for every style.

Durations and quoted lines below are the stock clips in [`assets/`](../assets/). A filesystem built with `custom_audio_mod` can replace some of those WAVs. Phrase timing then follows that clip's `.cue` file on LittleFS (`/welcome.cue` and the same pattern for `attention`, `error`, `abort`, and `dead`). See [flash.md](flash.md).

| `name` | Behavior |
| --- | --- |
| `none` | Head/neck/body → mid; hands down (right `min`, left `max` — inverted scales). After all joints still for 2 s, PCA9685 PWM is full-off (servos limp; head may droop). Setup AP does not release PWM. |
Expand Down
Loading
Loading