Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 4 additions & 5 deletions .github/workflows/espfoc_flow.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,8 @@ jobs:
idf.py build
popd

# Temporarily disabled while tuner protocol / axis lifecycle catches up with Studio.
python_tests:
if: false
name: TunerStudio host tests
name: espFoC Tool host tests
runs-on: ubuntu-latest
steps:
- name: Checkout
Expand All @@ -94,14 +92,15 @@ jobs:
sudo apt-get update
sudo apt-get install -y libegl1 libxkbcommon0 libdbus-1-3
python -m pip install --upgrade pip
pip install -r tools/espfoc_studio/requirements.txt
pip install -r tools/espfoc_tool/requirements.txt

- name: Run host test suite
env:
QT_QPA_PLATFORM: offscreen
PYTHONPATH: ${{ github.workspace }}/tools
ESPFOC_TOOL_NO_GL: "1"
run: |
python -m pytest tools/espfoc_studio/tests/ -v --tb=short
python -m pytest tools/espfoc_tool/tests/ -v --tb=short

unit_tests:
name: Unit tests (build and run on QEMU)
Expand Down
2 changes: 1 addition & 1 deletion Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ menu "espFoC Settings"
depends on ESP_FOC_TUNER_ENABLE
default ESP_FOC_BRIDGE_NONE
help
Physical bus for Tuner Studio or custom host tools.
Physical bus for espFoC Tool / espfocctl or custom host tools.

config ESP_FOC_BRIDGE_NONE
bool "None (weak callbacks in application)"
Expand Down
59 changes: 33 additions & 26 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ position loops live in the application's regulation callback; the
library stays focused and the hot path runs without floating-point
math. Gains can be synthesised at build time, retuned live from the
firmware API, persisted to NVS, or dialled in interactively through
the bundled TunerStudio GUI.
the bundled **espFoC Tool** desktop GUI.

Targets: ESP32, **ESP32-C6**, ESP32-S3, ESP32-P4 (ESP-IDF v5+). The
reference bring-up target is **ESP32-C6** (fixed-point hot path sized for
Expand Down Expand Up @@ -83,27 +83,30 @@ Inverter and rotor drivers are pluggable:

---

## Tuning
## Tuning (espFoC Tool)

![TunerStudio demo](doc/images/tuner_studio.gif)
![espFoC Tool](doc/images/espfoc_tool_logo.svg)

espFoC ships with **TunerStudio**, a PySide6 + pyqtgraph desktop app
that speaks the runtime tuner protocol over UART or USB-CDC. In a
single window you get:
**espFoC Tool** is a PySide6 + pyqtgraph control app over UART/USB-CDC.
It opens without a board connected (USB auto-scan) and exposes four views:

- live axis state and gain readout with in-place editing;
- one-click rotor alignment with auto-detected natural direction;
- align / run / stop lifecycle and store / erase calibration to NVS;
- predicted step response, Bode, pole-zero and root-locus plots;
- firmware scope stream with per-channel colour, toggle and cursor;
- SVPWM hexagon with the three phase projections and the resultant
voltage vector rotating as the motor is driven.
| View | Purpose |
|------|---------|
| **Config** | Live gains, editor vs device diff, write dirty fields, NVS RMW store/erase |
| **Current** | Motor R/L/bw + MPZ plots (step, Bode, pole-zero, root locus) |
| **Control** | id/iq targets, align, E-stop, SVPWM hexagon |
| **States** | Named scope channels ([axis_tuning](examples/axis_tuning) wire map) |

### Launch TunerStudio
OpenGL plot rendering is enabled by default; set `ESPFOC_TOOL_NO_GL=1` to disable.
Full workflow: [`doc/TUNING.md`](doc/TUNING.md).

### Launch espFoC Tool

```bash
pip install -r tools/espfoc_studio/requirements.txt
PYTHONPATH=tools python3 -m espfoc_studio.gui --port /dev/ttyACM0
pip install -r tools/espfoc_tool/requirements.txt
PYTHONPATH=tools python3 -m espfoc_tool.gui
# optional fixed port:
PYTHONPATH=tools python3 -m espfoc_tool.gui --port /dev/ttyACM0
```

### Talk to a real target
Expand All @@ -130,14 +133,19 @@ For your own firmware, enable a transport bridge in `menuconfig`
Then:

```bash
PYTHONPATH=tools python3 -m espfoc_studio.gui --port /dev/ttyACM0
PYTHONPATH=tools python3 -m espfoc_tool.gui --port /dev/ttyACM0
```

### Scripted tuning
### Scripted control

**espfocctl** drives align, run, stop, E-stop, gain writes, id/iq targets,
and NVS store/erase from scripts:

A companion CLI (`tunerctl`) drives align, run, stop, gain writes,
target id/iq, store, and erase from scripts. Details in
[`doc/TUNING.md`](doc/TUNING.md).
```bash
PYTHONPATH=tools python3 -m espfoc_tool.cli.espfocctl --port /dev/ttyACM0 -i
# one-shot E-stop:
PYTHONPATH=tools python3 -m espfoc_tool.cli.espfocctl --port /dev/ttyACM0 estop
```

---

Expand Down Expand Up @@ -219,20 +227,19 @@ loop contains no floating-point operations.
```
espFoC/
├── doc/
│ ├── images/ # architecture, TunerStudio screenshot, demo gif
│ └── TUNING.md # deep dive: autogen, runtime API, protocol, CLI
│ ├── images/ # architecture, espFoC Tool logo, demo gifs
│ └── TUNING.md # espFoC Tool + espfocctl workflow and scope map
├── examples/ # axis_tuning / unit_test_runner / test_drivers
├── include/espFoC/ # public API
├── scripts/
│ └── motors/*.json # motor profiles consumed by the autotuner
├── scripts/ # gen_iq31_sin_lut.py, build_samples.sh
├── source/
│ ├── calibration/ # NVS calibration format and axis helpers
│ ├── drivers/ # inverters, encoders, shunts, tuner bridges
│ ├── gui_link/ # binary link codec, scope, tuner reactor
│ ├── motor_control/ # axis core (FOC ISR + slow loop), MPZ, Q16 helpers
│ └── osal/ # OS abstraction (tasks, critical sections, esp_timer)
├── test/ # Unity unit tests (run via examples/unit_test_runner)
└── tools/espfoc_studio # PySide6 + pyqtgraph GUI, CLI, host protocol
└── tools/espfoc_tool # PySide6 GUI (espFoC Tool), espfocctl, host protocol
```

---
Expand Down
15 changes: 15 additions & 0 deletions changelog.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,21 @@ This file is used to generate GitHub releases. All versions from 2.0.0 onward ar

## Unreleased

### Added

- **espFoC Tool** (`tools/espfoc_tool`) — PySide6 control GUI replacing TunerStudio: Config / Current / Control / States views, USB auto-scan, offline navigation, OpenGL plots by default.
- **`espfocctl`** — CLI rename from `tunerctl`; adds `estop` (id/iq zero + stop).
- **`doc/TUNING.md`** — host tuning reference (views, scope map, CLI, troubleshooting).

### Removed

- **TunerStudio** host app (`tools/espfoc_studio`), generic Scope tab, in-GUI demo/loopback mode, Generate App / Hardware tabs (never shipped in 3.x tree).

### Changed

- Host package **`espfoc_studio` → `espfoc_tool`**; launch with `python -m espfoc_tool.gui`.
- CI **`python_tests`** job runs `tools/espfoc_tool/tests` (offscreen Qt, no GL).

### Removed

- **Tuner override mode** (`CMD_OVERRIDE_ON/OFF`, `TUNER_OVERRIDE` state bit, `ESP_FOC_TUNER_ALWAYS_OVERRIDE_VOLTAGE_MODE`). Host writes `target_i_d` / `target_i_q` directly while the axis is `RUNNING`.
Expand Down
179 changes: 179 additions & 0 deletions doc/TUNING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
# espFoC Tool — tuning and host protocol

This document describes how to control an espFoC axis from the host using
**espFoC Tool** (GUI) or **espfocctl** (CLI). Both use the same binary link
layer and tuner protocol implemented in firmware (`esp_foc_link`, `esp_foc_tuner`).

---

## Prerequisites

```bash
pip install -r tools/espfoc_tool/requirements.txt
export PYTHONPATH=tools # or prefix every command with PYTHONPATH=tools
```

Reference firmware: [`examples/axis_tuning`](../examples/axis_tuning). It advertises
firmware type **`TSGX`** so auto-scan can recognise the board.

Enable in your own project:

- `CONFIG_ESP_FOC_TUNER_ENABLE=y`
- `CONFIG_ESP_FOC_BRIDGE_UART` or `CONFIG_ESP_FOC_BRIDGE_USBCDC`
- `CONFIG_ESP_FOC_SCOPE=y` (for Dashboard scope plots)

---

## Quick start (GUI)

```bash
python3 -m espfoc_tool.gui
# optional fixed port (skips USB scan):
python3 -m espfoc_tool.gui --port /dev/ttyACM0 --baud 921600
```

1. Wait for **CONNECTED** in the status bar (or plug the board — scan runs every 2 s).
2. Open **Dashboard** → **Run alignment** (rotor direction + encoder zero).
3. Enable **Manual setpoints**, set **iq** / **id** (nudge buttons optional).
4. Open **Tune** → edit Kp/Ki/lim/filter → **Write** (RAM) → **Patch** (flash).
5. **E-STOP** (Dashboard → Actions) or disable manual setpoints → axis stops.

The GUI works **offline**: both views are navigable without a board; device
actions stay disabled until connected.

---

## Views

### Tune

| Area | Purpose |
|------|---------|
| Left | Live gains, manual editor, **Apply gains** / **Apply filter**, serial log |
| Center | Device vs pending diff; flash badge (stored / empty) |
| Right | Motor **R**, **L**, **bandwidth**; MPZ step/Bode/pole-zero/root locus |

**Flash actions** (center column):

| Button | Action |
|--------|--------|
| **Read** | Load Kp, Ki, lim, filter cutoff from device RAM into the editor |
| **Write** | Push only fields that differ from live RAM |
| **Patch** | Write dirty fields, then **store calibration** to NVS (firmware RMW) |

**Apply gains** under the MPZ plots writes synthesized Kp/Ki/lim from the motor model.
Pole pairs can be changed in the plot toolbar; persist with **Patch**.

NVS **store** only writes tuning fields that changed relative to the blob
(`esp_foc_calibration_axis_tuner_store` on device). Align data in NVS is not
edited from the tool in this release.

### Dashboard

| Area | Purpose |
|------|---------|
| Left — Motion | Manual setpoints, id/iq spinboxes + nudge |
| Left — Actions | **Run alignment**, **E-STOP**, **Autoset** (SVM/scope reset) |
| Right — top | SVPWM hexagon (pu) beside three-phase waveforms (scope ch 10–12) |
| Right — bottom | Rolling plots for all scope channels (`axis_tuning` map) |

**E-STOP** sequence: id/iq → 0, `stop` axis (also calls `inverter.disable` via
`park_inverter_safe`).

Scope streaming starts automatically on connect.

---

## Scope channel map (`axis_tuning`)

| Ch | Signal |
|----|--------|
| 0 | id target |
| 1 | id measured |
| 2 | iq target |
| 3 | iq measured |
| 4 | ud |
| 5 | uq |
| 6 | θ_meas mech |
| 7 | θ_est mech |
| 8 | ω_est mech |
| 9 | PLL error |
| 10 | iu |
| 11 | iv |
| 12 | iα |
| 13 | FOC hot-path µs |

Other examples may use different maps; only `axis_tuning` is the contract for espFoC Tool.

---

## espfocctl (CLI)

Interactive:

```bash
python3 -m espfoc_tool.cli.espfocctl --port /dev/ttyACM0 -i
```

One-shot:

```bash
python3 -m espfoc_tool.cli.espfocctl --port /dev/ttyACM0 align
python3 -m espfoc_tool.cli.espfocctl --port /dev/ttyACM0 estop
```

| Command | Description |
|---------|-------------|
| `connect` / `disconnect` | Link session |
| `status` | Heartbeat + axis state flags |
| `read` | Kp, Ki, Kd, Kff, ILim, Vmax |
| `write --kp … --ki …` | Write gains |
| `align` | Rotor alignment |
| `run` / `stop` | Start / stop FOC loop |
| `set-target id\|iq VALUE` | Current references (A) |
| `store` / `erase` | NVS calibration |
| `cutoff [--set HZ]` | Current LPF |
| `scope-start` / `scope-stop` | Scope stream |
| `firmware-type` | FourCC (expect `TSGX` on axis_tuning) |
| `estop` | id/iq=0 + stop |

---

## Environment variables

| Variable | Effect |
|----------|--------|
| `ESPFOC_TOOL_NO_GL=1` | Disable OpenGL plot rendering |
| `ESPFOC_TOOL_SCOPE_CSV=1` | Decode legacy CSV scope (if firmware built with legacy CSV) |
| `QT_QPA_PLATFORM=offscreen` | Headless CI / smoke tests |

---

## Host tests

```bash
QT_QPA_PLATFORM=offscreen ESPFOC_TOOL_NO_GL=1 PYTHONPATH=tools \
python3 -m pytest tools/espfoc_tool/tests/ -v
```

`FakeTunerLoopback` is for unit tests only — not exposed in the GUI.

---

## Troubleshooting

| Symptom | Check |
|---------|--------|
| Stuck on SCANNING | Cable, driver, correct port; firmware must expose bridge + `TSGX` |
| NO LINK after connect | Heartbeat / baud (default 921600); another app holding the port |
| Align fails | Motor wiring, pole pairs, sensor; see serial log on **Tune** |
| Scope flat | `CONFIG_ESP_FOC_SCOPE`, scope started, PWM loop running |
| Plots slow on VM | `ESPFOC_TOOL_NO_GL=1` or smaller window |

---

## FITL builds

Firmware built with `CONFIG_ESP_FOC_FITL` simulates plant + sensors in software.
espFoC Tool treats it like a normal target (`TSGX`); use **axis_tuning** without
FITL when validating real hardware.
20 changes: 20 additions & 0 deletions doc/images/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Documentation images

| File | Description |
|------|-------------|
| `architecture.svg` | espFoC component architecture (README) |
| `espfoc_tool_logo.svg` | espFoC Tool icon / README header |
| `espfoc_tool_logo.png` | Raster variant for window icon |
| `espfoc_demo.gif` | Hardware demo (motor running) |
| `espfoc_tool.gif` | *(optional)* espFoC Tool screen capture for README |

## Recording `espfoc_tool.gif`

When the UI is stable:

1. Run `axis_tuning` on hardware and connect espFoC Tool.
2. Capture 1280×720, ~15 s: connect → Tune → Dashboard (iq step, scope).
3. Export as GIF (e.g. `ffmpeg` or Peek) and save as `espfoc_tool.gif`.
4. Link from the root `README.md` tuning section.

Until then the SVG logo is used as the README visual.
Binary file added doc/images/espfoc_tool_logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
13 changes: 13 additions & 0 deletions doc/images/espfoc_tool_logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion examples/axis_tuning/sdkconfig.defaults
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# axis_tuning — reference firmware with runtime tuner always enabled.
# Host drives align → run → tune → store → stop via tunerctl / Studio.
# Host drives align → run → tune → store → stop via espfocctl / espFoC Tool.

CONFIG_ESP_TASK_WDT_EN=n

Expand Down
2 changes: 1 addition & 1 deletion include/espFoC/drivers/gui_link/esp_foc_bridge_uart.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
* @brief UART bridge for the espFoC tuner / scope link.
*
* Implements the weak callbacks declared by esp_foc_tuner.h so any
* espFoC build can talk to the host TunerStudio over a regular UART:
* espFoC build can talk to the host espFoC Tool over a regular UART:
*
* - esp_foc_tuner_init_bus_callback() -> driver install + RX task
* - esp_foc_tuner_recv_callback() -> shim around uart_read_bytes
Expand Down
Loading
Loading