This release is a pre-release and may not be stable for production use.
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.
What is atech?
A pip-installable kit that turns a plain-language idea into working
firmware on an Atech ESP32-S3 board — no electronics knowledge, pin maps, or
C++/PlatformIO setup required. The compiler is bundled, so one pip install
both builds and flashes, fully offline.
The intended flow — a person describes what they want; their coding agent writes a tiny spec plus a small behaviour snippet; the SDK deterministically compiles and flashes it. The split is deliberate:
- The agent understands intent and writes the behaviour (a few lines of C++), using each module's published API — which it discovers, never guesses.
- The SDK does everything mechanical: pin allocation, driver assembly,
main.cppgeneration, compiling, flashing — identical every time, no LLM in that path.
You: "make a lamp shine green on port 3"
→ agent: list-modules → reads neopixel API → writes project.yaml
→ atech validate → atech build → atech upload → green lamp 🟢
You can also drive it yourself: write the project.yaml (or the Python
Project builder) and run the CLI.
What you can do with it
| Discover hardware | atech list-boards, atech list-modules, get_module(id).usage |
| Describe a project | a small project.yaml (board + modules on ports + behaviour) |
| Validate / build / flash | atech validate → atech build → atech upload |
| Talk to the board | atech monitor / board.events(), atech send / board.send() |
| Diagnose & recover | atech check (why didn't it work?), atech free-port |
In the catalog today: 2 boards (8port, 14port) and 9 modules — button,
rotary_encoder, pir, aht20 (temp/humidity), neopixel, st7735_tft
(colour display), speaker, dc_motor, stepper_motor. Run
atech list-modules for the live list. How the board communicates is in
COMMUNICATION.md.
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()
)
Robot arm: check the power supply and the arm first
The Robot Arm module passes the board's high-voltage rail (labelled 12 V) straight to the servos. That rail is fed by a power module (the power jack module, or the USB-C PD power module) and carries whatever that module is given. Nothing regulates it, and the standard USB-C module does not feed it.
- SO-101 arms come in a 12 V and a 5 V version.
- The supply must match the arm: a 12 V supply for the 12 V arm, a 5 V supply for the 5 V arm.
- A 12 V supply on a 5 V arm damages the servos.
- Never plug more than 12 V into the jack.
- Check the voltage printed on the supply, and which arm you have, before you connect anything.
- Both versions have been run this way: power jack module and Robot Arm module, nothing outside the Atech stack.
atech validate, atech build and atech upload print this as a CAUTION
whenever a project contains robot_arm.
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.
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})
How the board talks to your computer — the event/action model, the exact wire
format, and the USB-Serial path — is in
COMMUNICATION.md.
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.
Metadata
Release files for atech 1.0.0a10
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| atech-1.0.0a10.tar.gz | 95.2 kB | Without provenance | Details for atech-1.0.0a10.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| atech-1.0.0a10-py3-none-any.whl | Python 3 | none | any | Details for atech-1.0.0a10-py3-none-any.whl |
Total release size: 224.6 kB
Release files / atech-1.0.0a10.tar.gz
| Download URL | atech-1.0.0a10.tar.gz |
|---|---|
| Size | 95.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
75b507f2398ffa62f0045952cb3a8ea9e7f0b76c322e8b8cfb7ac132af669658
|
|
BLAKE2b-256 checksum How to use checksums |
db7021e3baa7ac543eb17f40e21619140a3284a30166861538fb8baa403e79fe
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.19 {"installer":{"name":"uv","version":"0.11.19","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"CachyOS Linux","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / atech-1.0.0a10-py3-none-any.whl
| Download URL | atech-1.0.0a10-py3-none-any.whl |
|---|---|
| Size | 129.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
5c5342f03676afcae4285dae36adfd35dadf8d1ce8fcbc8cb80882e1791b0d04
|
|
BLAKE2b-256 checksum How to use checksums |
825323d7480c8ad91057d41a72fcb6baa21ef49246640efdb6ae25ebe81f4a9d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.19 {"installer":{"name":"uv","version":"0.11.19","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"CachyOS Linux","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Security for atech 1.0.0a10
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.