Skip to content
TODO — first-pass draft, verify against the ESProgrammer repo and live workflow before linking from guides.

MCU programming

Last updated:

Every ESci device ships with an STM32L0-family MCU on board, and every assembly guide reaches a step where firmware has to be flashed onto that MCU. The canonical tool for this is ESProgrammer — a desktop app that handles the toolchain, picks the right binary from the API, flashes the board, and registers the flash event back to the cloud. This page is the reference for how — guides should link here rather than re-document the workflow.

The canonical source for setup, releases, and internals is the ESProgrammer repo. When that diverges from what’s documented here, the repo wins.

  • Target board: any ESci device. All run the same STM32L0 family MCU.
  • Programmer: the ESProgrammer device — ESci’s custom USB-to-Serial flasher (not to be confused with the ESProgrammer app, which runs on the workstation).
  • Cable: TagConnect, between ESProgrammer device and the target’s programming pads.

Install once per workstation, following the repo’s Getting started. Summary:

Terminal window
git clone https://github.com/EnsembleScientific/esprogrammer
cd esprogrammer
brew install uv
uv venv -p 3.13
source .venv/bin/activate
uv pip install -e .
esprogrammer

On first launch, open Preferences and enter your ESci username and password — the app uses this to sync the device catalog and pre-built firmware binaries from the API.

Standard assembly-line flash:

  1. Plug the ESProgrammer device into the workstation. Switch ON.
  2. Launch the app (esprogrammer if you set the alias, otherwise python -m esprogrammer).
  3. Click Sync in the app to pull the latest device list and binaries (only needed when firmware has changed).
  4. Connect the target to the ESProgrammer via TagConnect.
  5. Press the ESProgrammer device’s reset button.
  6. In the app, select the device (by part number or firmware name) and click Flash.
  7. The app flashes, verifies, and registers the flash event with the API — the device’s firmware version is now tracked in admin.

For repetitive flashing of the same firmware (e.g., batch production), use CLI mode:

Terminal window
python -m esprogrammer <firmware-filename> -p <port>

The -p argument is optional — the tool auto-detects the port if only one ESProgrammer is connected.

Every device’s firmware target lives in admin under firmware_filename. The app uses that field to pick the right binary. Examples:

DeviceFirmware filename
Spruce NodeNodeSpruce
HydroPulse Direct V3HydroPulseDirectV3
IR Temperature SensorSensorIRTemp
Drip Line Pressure SensorSensorDriplinePressure

If a device has no firmware filename in admin, it isn’t ESci-managed firmware (e.g., third-party integrations like the LI-COR ET sensor) — the flash step doesn’t apply.

  • App doesn’t see any port: confirm the ESProgrammer device’s switch is in the ON position and the USB cable is good. Some hubs swallow the device — try plugging directly into the workstation.
  • Flash fails immediately: the target wasn’t in reset mode when you clicked Flash. Press the ESProgrammer reset button first, then Flash within a few seconds.
  • App says the firmware isn’t synced: hit Sync in Preferences. If sync fails, check your credentials and the API URL.
  • Device boots but reports the wrong firmware: the wrong binary was flashed. Confirm the device’s firmware_filename in admin matches what the app shows, and re-flash.
  • Firmware development (compiling from source, working with the Arduino toolchain directly): see ESFirmware/README. The ESProgrammer app uses arduino-cli internally and can be set up via python -m esprogrammer -c, but for hands-on development the Arduino IDE path in that README is more direct.
  • Recovery / inspection (bricked board, custom hex inspection): STM32CubeProgrammer connects directly to the MCU over serial without the app abstraction. Out of scope for production assembly; reach for it only when something has gone wrong upstream.