Open-source Python SDK for Atech motherboards: catalog, codegen, build, flash, and runtime control.
Project description
atech
The open-source way to write firmware for Atech boards — designed for humans + coding agents working together.
You have an Atech motherboard and you want it to do something. With this SDK installed, you can ask Claude Code (or any coding agent that can run Python) — "make my board light up red when the button is pressed" — and the agent has everything it needs: a catalog of available modules, the public API of each driver, deterministic codegen, a real compiler, and an upload command. No cloud account, no AI prompts in the SDK, no proprietary glue.
Install
pip install atech
# or
uv add atech
PlatformIO is pulled in as a dep, so a single install builds and flashes.
What it gives a coding agent
A coding agent working in a user's repo gets four things:
-
A discoverable catalog of bundled modules with rich metadata.
atech list-modules atech list-modules --category sensor python -c "from atech import get_module; print(get_module('button').usage)"
Each module's
usage:field is a short C++ snippet showing the public API — no need for the agent to grep through driver headers. -
A small declarative project format (
project.yaml) that the agent writes or edits directly:name: my_thing board: 8port modules: - {id: button, instance: btn, port: 3} - {id: neopixel, instance: led, port: 4} code: loop: | if (btn.wasPressed()) { led.setAll(255, 0, 0); led.show(); }
-
Mechanical, deterministic codegen.
atech buildturns the YAML into a vanilla PlatformIO project, compiles it, and produces afirmware.bin. No LLM in this path — the agent owns the behavior, the SDK owns the plumbing. -
An upload + monitor flow that doesn't require the user to know PlatformIO internals:
atech upload/atech monitor.
60-second example
atech new thermo --board 8port
cd thermo
# Tell Claude (or write yourself):
# "edit project.yaml to add a button on port 3 and a neopixel on port 4,
# and in code.loop, cycle the neopixel through six colours on each press"
atech validate .
atech build .
atech upload .
Or, fully programmatic:
from atech import Project
(
Project(board="8port", name="thermo")
.add("button", port=3, instance="btn")
.add("neopixel", port=4, instance="led")
.set_loop("""
if (btn.wasPressed()) {
static uint8_t hue = 0;
hue += 60;
led.setAll(hue, 0, 255 - hue);
led.show();
}
""")
.build()
.upload()
)
Bringing your own module
If the bundled catalog doesn't cover what the user has, drop a folder
next to project.yaml:
my_project/
├── project.yaml # add `modules_path: ./my_modules`
└── my_modules/
└── lidar_v2/
├── module.yaml # id, templates, usage, lib_deps
├── lidar_v2.h
└── lidar_v2.cpp
Same shape as the SDK's
src/atech/catalog/data/modules/ tree.
The SDK merges your modules with the bundled catalog at build time; you
never have to fork. See examples/custom_module/
for a working example.
Examples
examples/blink_button/— minimal user behavior hook: a button cycles an LED grid through colours.examples/speaker_music/— write your own music: note-by-note melodies, RTTTL ringtone strings, and chords on thespeakermodule.examples/custom_module/— a user-authored module (heartbeat) placed in a sibling folder, then used by the project.
All build end-to-end with atech build ..
The full Python API
import atech
# Discovery — what the agent should call first
atech.list_boards()
atech.list_modules(category="sensor")
atech.get_board("8port")
atech.get_module("button").usage # C++ snippet showing the public API
# Project assembly
p = atech.Project(board="8port", name="weather")
p.add("button", port=3, instance="btn")
p.add("dc_motor", ports=(1, 2)) # double-width = adjacent pair
p.set_setup("Serial.println(\"booting\");")
p.set_loop("if (btn.wasPressed()) { Serial.println(\"hi\"); }")
p.use_modules_from("./my_modules") # custom catalog directory
p.validate() # -> list[PlacementIssue]
p.describe() # human/agent summary + per-module usage
p.generate(out_dir="./build") # -> Path to PlatformIO tree
p.build() # -> BuildResult
p.upload(port="/dev/ttyACM0") # -> UploadResult
p.to_yaml(); atech.Project.from_yaml(text); atech.Project.load(path)
# Runtime — after flashing
with atech.Board.connect() as board:
for ev in board.events():
print(ev.key, ev.value)
board.send("led", {"r": 255, "g": 0, "b": 0})
CLI
atech list-boards
atech list-modules [--category X]
atech new <name> --board <id> # scaffolds project.yaml + README
atech validate [project-dir]
atech build [project-dir]
atech upload [project-dir] [--port X]
atech ports # candidate USB devices
atech monitor [--port X] [--type T,T] [--key K,K] [--json]
atech send <key> <value> [--port X]
How a coding agent should drive it
CLAUDE.md — drop a copy into your project root and your
coding agent will follow it. It covers: how to discover modules, how to
write the code: block, what not to do (invent modules, skip
validation, hand-edit generated files, use delay() in the loop).
Architecture (short version)
┌────────────────────────────────────────────────────────────────────┐
│ atech.Project ← agent assembles a hardware spec │
├────────────────────────────────────────────────────────────────────┤
│ atech.placement ← validates adjacency / reserved / overlap │
│ atech.catalog ← bundled + user-authored modules │
│ atech.codegen ← deterministic template stitching │
│ atech.build / upload ← `pio run` wrappers │
├────────────────────────────────────────────────────────────────────┤
│ atech.runtime.Board ← serial client for the flashed board │
└────────────────────────────────────────────────────────────────────┘
│ │
PlatformIO pyserial
│ │
ESP32 flash ESP32 USB-CDC
The closed Atech hosted product imports this SDK and layers AI module selection, OTA, sharing, and signing on top. The open SDK is everything needed to build firmware locally; nothing in this package phones home.
Status
v1.0.0a1 — alpha. Two boards (8port, 14port) and nine bundled
modules: button, rotary_encoder, pir (motion), aht20
(temp/humidity), neopixel, st7735_tft (colour display), speaker
(I2S audio), dc_motor, and stepper_motor. More ship through the
catalog as they're ported in — run atech list-modules for the live list.
The runtime serial API is stable and preserves the v0.1.x surface under
atech.runtime.
See PRD.md and IMPLEMENTATION_PLAN.md
for the longer story.
License
MIT.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file atech-1.0.0a1.tar.gz.
File metadata
- Download URL: atech-1.0.0a1.tar.gz
- Upload date:
- Size: 74.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
406aef4f8663b8e02f8f5ce23b9da8b2a8b156ba1b7b578e28bc5ca3db9f62cf
|
|
| MD5 |
953afc7322e4ae049a329217f0898d51
|
|
| BLAKE2b-256 |
ca698ba884a74634743e1022149e30972bfacf1eea91d11d2455217797210b70
|
File details
Details for the file atech-1.0.0a1-py3-none-any.whl.
File metadata
- Download URL: atech-1.0.0a1-py3-none-any.whl
- Upload date:
- Size: 79.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
45e5339f54f626f8a288075b322d144a972c5ecb183217446b27a78094f48aa7
|
|
| MD5 |
f87fa598c6f79764700babb0633a4274
|
|
| BLAKE2b-256 |
60afb0c01593092ca79d34902f299fda2c9a5ddbf03967e75a3f74aa7bbcb1eb
|