Install on your ESP32
Flash MicroPython 1.29, connect the ESP32 to your Mac/PC over USB, and run this command. It downloads release files, asks for your agent name, API key, and Wi-Fi credentials, and deploys everything to the board:
# on your Mac/PC
curl -fsSL https://raw.githubusercontent.com/ctrlpi/esp32-gpio-api/main/install.sh | bashPre-compiled bytecode, zero extra dependencies
app.mpy is pre-compiled into bytecode for fast boot times and reduced RAM usage. boot.py reserves Wi-Fi memory, and main.py loads app.mpy, so the server starts automatically.
Wi-Fi credentials live in wifi.txt on the board (install.sh writes it from a prompt):
SSID = "YourNetwork" PASSWORD = "YourPassword"
What it does
- Outputs: write
0/1/"on"/"off"/"toggle", pulse for a duration with automatic revert, clamp with a per-pinmax, invert relay logic withreversed - Inputs: pull-up/down/none, name aliases, and
watchedpins that fire a webhook on every state change - Sensors: named readings from a MicroPython script in the agent's
scripts/folder, returned as JSON - Scripts: list, read, write and run the files in
scripts/over REST. Writes are refused with the defaultapi_keyor while awrite.lockfile exists - Persistence: pin config and last output values survive restarts and reboots (
init: "last") - Identity: every agent reports its name, IP, platform, OS, and CPU temperature; unauthenticated
/hellomakes LAN discovery trivial - Logging: request log with configurable retention (
log_days0 to 7), readable over the API
The REST API
All routes require the Api-Key header except /hello:
| Method | Path | Description |
|---|---|---|
GET | /hello | Discovery & health check, returns the agent's name, no auth |
GET | /gpio/read | Read all configured pins |
GET | /gpio/read/{name_or_gpio} | Read one pin by GPIO number or name alias |
POST | /gpio/write/{name_or_gpio} | Write a value, supports "on"/"off"/"toggle" and timed duration pulses |
POST | /gpio/config/{name_or_gpio} | Configure a pin: type, name, init, pullup, max, reversed, watched |
GET | /gpio/watched | List watched pins and the active webhook URL |
GET | /gpio/scan | Scan all usable GPIO pins, includes physical level, platform, CPU temp |
POST | /sensor/config/{name} | Configure a named sensor: a script (.py) to run from the agent's scripts/ folder |
GET | /sensor/read | Read all sensors (or /sensor/read/{name} for one) as JSON |
GET | /script/list | List the files in the agent's scripts/ folder, and whether writes are allowed |
GET | /script/read/{name} | Read a script's text |
POST | /script/write/{name} | Create, replace or delete a script ({"content": "..."} or {"remove": true}). Refused with 403 while api_key is the default or a write.lock file exists |
POST | /script/run/{name} | Run a script in the agent and get {name, exit_code, output}: the value of read(*args) or result |
GET | /config/read | Agent identity + full config |
POST | /config/update | Update config fields (auto-backup first) |
POST | /config/load | Replace the whole config ({} resets) |
POST | /restart | Restart the device - resets microcontroller into clean state |
POST | /upgrade | Upgrade in place - restarts into upgrader.mpy, which downloads and safely installs the latest release, then starts the agent |
GET | /logs | Last 30 log lines as JSON (in-memory, cleared on reboot) |
install.sh, or Pico Bay). /upgrade works from then on.Try it
# configure GPIO 18 as an output named "led" curl -X POST http://<esp32-ip>:8314/gpio/config/18 \ -H "Api-Key: your-secret-key" -H "Content-Type: application/json" \ -d '{"type": "output", "name": "led"}' # pulse it for 1.5 seconds, reverts automatically curl -X POST http://<esp32-ip>:8314/gpio/write/led \ -H "Api-Key: your-secret-key" -H "Content-Type: application/json" \ -d '{"value": 1, "duration": 1.5}'
Webhooks
Mark any pin watched and the agent POSTs its state to your configured
webhook URL on every change, that's how
homebridge-ctrlpi gets real-time input events. While setting up,
point webhook_url at ctrlPi's public test endpoint and
watch your events live while wiring.
https:// webhook_url is refused with a 400 instead of failing later (which also rules out the ctrlpi.com test endpoint; point them at an http:// listener on your own network). The ESP32-S3 has the headroom and takes https:// normally.In the family
Everything that drives a Pi or Pico agent drives an ESP32 agent unchanged: homebridge-ctrlpi, mcp-bridge, ctrlpi-cli, and ctrlpi-lab. Watch live webhooks at ctrlpi.com.
To test endpoints, watch webhook events and tail logs in the browser, open ctrlpi-lab, and to find and drive agents from a terminal, use ctrlpi-cli.
To get an agent onto a board over USB, use pico-bay. It can install MicroPython, manage the board's files and run code on a Pico and an ESP32.