No description
  • Python 92.3%
  • C 5.8%
  • CMake 1.9%
Find a file
2026-07-28 01:45:23 +02:00
docs Initial import: chipbarn — HTTP flash/monitor service for Pico and ESP32 boards 2026-07-02 21:16:03 +02:00
firmware Initial import: chipbarn — HTTP flash/monitor service for Pico and ESP32 boards 2026-07-02 21:16:03 +02:00
service fix: resolve esptool from active venv 2026-07-28 01:45:23 +02:00
tests fix: resolve esptool from active venv 2026-07-28 01:45:23 +02:00
.gitignore Initial import: chipbarn — HTTP flash/monitor service for Pico and ESP32 boards 2026-07-02 21:16:03 +02:00
chipbarn.service Initial import: chipbarn — HTTP flash/monitor service for Pico and ESP32 boards 2026-07-02 21:16:03 +02:00
LICENSE Initial import: chipbarn — HTTP flash/monitor service for Pico and ESP32 boards 2026-07-02 21:16:03 +02:00
pyproject.toml Initial import: chipbarn — HTTP flash/monitor service for Pico and ESP32 boards 2026-07-02 21:16:03 +02:00
README.md Initial import: chipbarn — HTTP flash/monitor service for Pico and ESP32 boards 2026-07-02 21:16:03 +02:00
requirements.txt Initial import: chipbarn — HTTP flash/monitor service for Pico and ESP32 boards 2026-07-02 21:16:03 +02:00
test_api.py Initial import: chipbarn — HTTP flash/monitor service for Pico and ESP32 boards 2026-07-02 21:16:03 +02:00

chipbarn

Chipbarn — RPi4 + Pico (RP2040 / RP2350) + ESP32 (any chip)

HTTP service on port 8080 lets remote machines flash firmware and stream console output.

Picos are wired to fixed RPi4 GPIO pins (see service/config.py) — any Pico SDK target works (original Pico, Pico W, Pico 2, Pico 2 W). ESP32 boards of any variant (S3, S2, C3, C6, H2, P4, original ESP32) are auto-discovered — plug in as many as you like; each is addressed by its USB serial number and the chip is auto-detected at flash time. GET /devices lists everything currently available.

Base URL: http://<your-host>:8080 — replace <your-host> with your Pi's hostname or IP (e.g. chipbarn.local or 192.168.x.x).


Service

Install dependencies

cd ~/chipbarn
pip install -r requirements.txt      # fastapi, uvicorn[standard], pyserial, esptool

On Debian/RPi OS (PEP 668) install into a venv first — python3 -m venv .venv && . .venv/bin/activate — or pass --break-system-packages. ESP-IDF users already have esptool in their environment and can omit it.

Start (foreground)

python3 -m uvicorn service.main:app --host 0.0.0.0 --port 8080

systemd (auto-start on boot)

First edit chipbarn.service and set User/Group to your username (defaults to a REPLACE_ME placeholder). The python3 in ExecStart must have the dependencies above installed, or point it at a venv. Then:

sudo ln -s "$PWD/chipbarn.service" /etc/systemd/system/   # run from the repo root
sudo systemctl daemon-reload && sudo systemctl enable --now chipbarn
sudo journalctl -u chipbarn -f   # logs

Security: the service has no authentication and binds to 0.0.0.0:8080 — anyone who can reach the host can flash firmware and read device consoles. Keep it behind a VPN/firewall or a reverse proxy with auth before exposing it.

Configuration

No edits are needed to run — the service resolves openocd/esptool from PATH automatically and auto-detects each ESP32's chip family at flash time. These environment variables are available for advanced setups (all optional):

Variable Default Purpose
OPENOCD openocd on PATH OpenOCD binary (Pico SWD flash + monitor)
ESPTOOL esptool on PATH esptool binary (ESP32 flash); IDF users already have it
MAX_FIRMWARE_BYTES 10737418240 (10 GiB) Max accepted firmware upload size; overruns return HTTP 413
OPENOCD_FLASH_TIMEOUT 120 Wall-clock cap (seconds) on one Pico flash operation

Pico SWD pin mapping, per-Pico chip selection (rp2040 vs rp2350), and the supported USB-serial bridge list live in service/config.py.

Interactive API docs

http://<your-host>:8080/docs


Discover devices

curl http://<your-host>:8080/devices

Returns { picos: [{id, chip, swdio, swclk}, ...], esp32: [{id, port}, ...] }. Call this first to learn the <id> of each connected ESP32. The Pico chip field reports rp2040 or rp2350 so callers know which SDK to build against. ESP32 chip detection happens lazily at flash time, so it is not listed here.


Flash Firmware

Pico — upload .elf

Works for any Pico SDK target — original Pico / Pico W (RP2040) or Pico 2 / Pico 2 W (RP2350). The chip is selected per-board in service/config.py.

curl -X POST http://<your-host>:8080/flash/pico1 -F "firmware=@build/blink.elf"
curl -X POST http://<your-host>:8080/flash/pico2 -F "firmware=@build/blink.elf"

Returns JSON with OpenOCD output when done.

ESP32 — upload build tarball (any chip)

After idf.py build, from the project root:

tar -czf fw.tar.gz -C build \
    flasher_args.json \
    <project>.bin \
    bootloader/bootloader.bin \
    partition_table/partition-table.bin

The chip family is read from flasher_args.json (IDF writes it during build), so the same flow works for ESP32, S2, S3, C3, C6, H2, P4 without any parameters.

Flash a specific board by its USB serial (<id> from /devices):

curl -X POST http://<your-host>:8080/flash/esp32/5970047764 -F "firmware=@fw.tar.gz"

Or the legacy no-id endpoint (targets the first detected ESP32):

curl -X POST http://<your-host>:8080/flash/esp32 -F "firmware=@fw.tar.gz"

A merged single binary (.bin) works too — the chip is auto-detected via esptool chip_id:

curl -X POST http://<your-host>:8080/flash/esp32/5970047764 -F "firmware=@merged.bin"

To skip auto-detection (e.g. for a stripped binary that doesn't enter the bootloader cleanly), pass ?chip=:

curl -X POST "http://<your-host>:8080/flash/esp32/5970047764?chip=esp32c3" -F "firmware=@merged.bin"

Successful responses include "chip": "<family>" so you can confirm what was flashed.

If the named ESP32 isn't plugged in the service returns 503 with the list of currently connected boards — plug it in and retry.


Monitor (SSE — use -N to disable curl buffering)

curl -N http://<your-host>:8080/monitor/pico1   # Pico #1 semihosting via SWD
curl -N http://<your-host>:8080/monitor/pico2   # Pico #2 semihosting via SWD
curl -N http://<your-host>:8080/monitor/esp32/5970047764   # specific ESP32 by USB serial
curl -N http://<your-host>:8080/monitor/esp32             # legacy: first detected ESP32

Optional timeout: curl -N "http://<your-host>:8080/monitor/pico1?timeout=60"
Default timeout: 300 s. Ctrl-C to stop early.


Status

curl http://<your-host>:8080/status

Hardware

Supported boards

Family Chips Connection
Pico SDK RP2040 (Pico, Pico W), RP2350 (Pico 2, Pico 2 W) SWD via RPi4 GPIO
ESP-IDF ESP32, ESP32-S2, ESP32-S3, ESP32-C3, ESP32-C6, ESP32-H2, ESP32-P4 USB-UART (auto-discovered)

ESP8266 is not supported — modern ESP-IDF dropped it, and its ROM bootloader reset sequence differs.

Pico SWD Wiring

3-pad connector, bottom edge, USB facing up: GND / SD / SC (left to right). Same pad layout on the original Pico, Pico W, Pico 2, and Pico 2 W.

Signal Pico pad Pico #1 RPi4 GPIO Pico #2 RPi4 GPIO
SWDIO SD (middle) GPIO 24 — pin 18 GPIO 22 — pin 15
SWDCLK SC (right) GPIO 25 — pin 22 GPIO 23 — pin 16
GND GND (left) any GND any GND

3.3 V both sides, no level shifter. No UART wires needed — console goes over SWD via semihosting.

ESP32

Discovery is automatic — the service scans /sys/class/tty/ for known USB-to-UART bridges (CH340/CH343, CP210x, FTDI, and Espressif's native USB-CDC on VID 0x303a — see ESP_USB_BRIDGES in service/config.py) and addresses each board by its USB serial number. No udev rule is required; plug boards into any USB port. User must be in the dialout group (no sudo).

To find the serial number of a board without the service:

udevadm info -q property -n /dev/ttyACM0 | grep ID_USB_SERIAL_SHORT

Adding a new Pico

One line in service/config.py — the chip drives the OpenOCD target config:

PicoDevice("pico3", chip="rp2040", swdio=18, swclk=17, gdb_port=3335, tcl_port=6668, telnet_port=4446),

Restart the service. /flash/pico3 and /monitor/pico3 are now available.

Adding a new ESP-style USB-UART bridge

One line in service/config.py — use pid=None to match every product from a vendor:

ESP_USB_BRIDGES: list[tuple[str, str | None]] = [
    ...,
    ("abcd", None),  # imaginary vendor; any PID
]

Firmware Examples

Source lives in firmware/:

Directory Target Notes
firmware/pico_blink/ RP2350 (Pico 2 / Pico 2 W) Blinks LED at 250 ms, prints blink #N via semihosting
firmware/esp32s3_hello/ ESP32-S3 Logs chip info on boot, blinks GPIO2 at 1 Hz, prints blink #N

The same flash flow works for any Pico SDK or ESP-IDF target — only the build differs. For a different chip, change set-target (ESP-IDF) or the SDK / link flags (Pico SDK) and rebuild; the chipbarn side needs no configuration change.

Pico SDK — build notes

  • RP2350 (Pico 2 W): LED is wired through the CYW43 WiFi chip, not a direct GPIO. PICO_DEFAULT_LED_PIN is undefined. Link pico_cyw43_arch_none; drive LED via cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 1).
  • RP2040 (original Pico): LED is on a direct GPIO, PICO_DEFAULT_LED_PIN is defined; no CYW43 link needed.
  • RP2040 (Pico W): same CYW43 LED path as Pico 2 W.
  • Enable semihosting in CMakeLists.txt: pico_enable_stdio_semihosting(blink 1) / pico_enable_stdio_uart(blink 0).
  • Build with a clean PATH — ESP-IDF prepends xtensa cross-assemblers that break pioasm/picotool host builds. Strip the environment: env -i HOME=$HOME PATH=/usr/local/bin:/usr/bin:/bin cmake ...

ESP-IDF — build notes

  • ESP-IDF v6.0.x (install via the official ESP-IDF installer).
  • Activate: source $IDF_PATH/export.sh (or your IDF activation script).
  • First build: idf.py set-target <chip> where <chip> is esp32, esp32s2, esp32s3, esp32c3, esp32c6, esp32h2, or esp32p4.
  • Build: idf.py build
  • The service flashes via the tarball described above — no need to flash from the RPi4 directly.