The 20A PSU ceiling was hardcoded and clamped user entries, which breaks benches with a different supply. Now the sweep I limit field IS the max input current allowed for the test environment: used verbatim as the supply CC limit and as the step-rejection / measured-backstop ceiling. Manual supply-current clamp removed; manual CC/CP load gate now checks against the I limit field value. PSU_MAX_CURRENT_A is reduced to INPUT_LIMIT_DEFAULT_A (initial field value only). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
445 lines
17 KiB
Markdown
445 lines
17 KiB
Markdown
# MPPT Testbench
|
|
|
|
Unified tool for testing and tuning MPPT (Maximum Power Point Tracking) converters. Combines three bench instruments with direct STM32 firmware access for closed-loop parameter optimization.
|
|
|
|
| Instrument | Role | Interface |
|
|
|---|---|---|
|
|
| **ITECH IT6537D** | DC power supply (solar panel simulator, 80V/120A/6kW) | USB-TMC / SCPI via PyVISA |
|
|
| **Prodigit 3366G** | DC electronic load (600V/420A/6kW) | RS-232 (115200 8N1 RTS/CTS) |
|
|
| **HIOKI 3193-10** | Power analyzer (efficiency measurement) | GPIB via UsbGpib / PyVISA |
|
|
| **STM32G474** | Converter firmware (LVSolarBuck64) | Serial 460800 baud (debug protocol) |
|
|
|
|
## Wiring
|
|
|
|
```
|
|
+------------------+
|
|
IT6500D --(+/-)--> | MPPT Tracker | --(+/-)--> Prodigit 3366G
|
|
| (DUT) |
|
|
HIOKI Ch5 --(sense)-- | Input Output | --(sense)-- HIOKI Ch6
|
|
+------------------+
|
|
|
|
|
STM32 debug
|
|
serial (COM4)
|
|
|
|
HIOKI EFF1 = P6 / P5 x 100% (output power / input power)
|
|
```
|
|
|
|
## Installation
|
|
|
|
Requires Python 3.12+, NI-VISA runtime, and [uv](https://docs.astral.sh/uv/).
|
|
|
|
```bash
|
|
git clone --recurse-submodules https://git.b4l.co.th/B4L/mppt-testbench.git
|
|
cd mppt-testbench
|
|
uv sync
|
|
```
|
|
|
|
The debug console (inside `code64/`) has its own environment:
|
|
|
|
```bash
|
|
cd code64
|
|
uv sync
|
|
```
|
|
|
|
## Step-by-Step Guide
|
|
|
|
### 1. Connect and verify instruments
|
|
|
|
```bash
|
|
# Check all bench instruments respond
|
|
uv run bench identify
|
|
|
|
# Check STM32 responds (reads params + telemetry)
|
|
uv run bench stm32-read --stm32-port COM4
|
|
```
|
|
|
|
### 2. Configure instruments for MPPT testing
|
|
|
|
```bash
|
|
uv run bench setup
|
|
```
|
|
|
|
This sets wiring mode (1P2W), DC coupling, auto-ranging, efficiency formula (EFF1 = P6/P5), and display layout on the HIOKI.
|
|
|
|
### 3. Basic measurements
|
|
|
|
```bash
|
|
# Single reading from all instruments
|
|
uv run bench measure
|
|
|
|
# Continuous text monitoring with CSV export
|
|
uv run bench monitor --interval 1.0 --output data.csv
|
|
```
|
|
|
|
### 4. Launch the GUI
|
|
|
|
```bash
|
|
uv run bench-gui
|
|
```
|
|
|
|
The GUI provides:
|
|
- Real-time readouts from all three instruments
|
|
- Supply voltage/current control with ON/OFF indicators
|
|
- Load mode (CC/CR/CV/CP) and setpoint control
|
|
- HIOKI channel range selectors + degauss buttons
|
|
- Meter format selector (scientific/normal)
|
|
- 2D sweep panel with time estimate
|
|
- Live-updating power, efficiency, voltage, and current plots plus STM32
|
|
Vfly and vfly_correction/vfly_ofs_applied plots (selectable) — all graphs
|
|
show the last 20 seconds
|
|
- STM32 telemetry panel ("Link STM32", COM4 @ 460800 8-O-1): every field of
|
|
the 100 Hz broadcast (V/I/P + net efficiency, temps, Vfly group, control
|
|
mode, HRTIM compare registers, status flags, fault registers, param echo),
|
|
with Ping and Clear Flags buttons; auto-reconnects on serial errors
|
|
- Data logging: instrument rows (with the latest STM32 snapshot merged in as
|
|
`stm_*` columns) to the chosen CSV, plus the full-rate 100 Hz telemetry
|
|
stream to `<name>_telem.csv` alongside. Logging is ON by default: every
|
|
launch auto-starts a log at `logs/data_<timestamp>.csv` (relative to the
|
|
working directory); use Stop Log / Start Log to switch to a custom path
|
|
- Console log panel (STM32 fault flags are reported here as they latch)
|
|
- Input-current guard: the sweep "I limit" field is the maximum input
|
|
current allowed for the test environment (set it to whatever the PSU on
|
|
the bench can source; defaults to 20 A, no hardcoded ceiling). It is
|
|
programmed as the supply CC limit, and sweep steps whose estimated input
|
|
draw `I_in = P_out / (0.90 * V_in)` exceeds it are rejected (skipped and
|
|
reported per voltage), regardless of the requested step range; a measured
|
|
backstop additionally drops any point where the supply actually exceeded
|
|
the limit and backs the load off. Manual CC/CP load setpoints are checked
|
|
against the same limit using live Vin/Vout readings.
|
|
- Load range pinning: a mid-sweep auto-range transition on the Prodigit
|
|
momentarily unloads the converter, so at sweep start the CC range is
|
|
pinned to Range II for the whole run (auto-ranging restored after, with
|
|
the load off). The reachable maximum of the selected range is verified
|
|
empirically -- the sweep max is programmed with the load off and read
|
|
back; if the readback comes back clamped, steps above it are rejected
|
|
instead of silently clamped, and a sweep that fits nothing aborts up
|
|
front.
|
|
- Thermal sweep guard (needs the STM32 link): before every sweep step the
|
|
heatsink/board temperatures are checked against the firmware trip limits
|
|
(60 C / 80 C). At 57 C / 77 C the sweep pauses, holds the load at 1 A
|
|
(CC; ~1 A worth of W in CP), and waits until both temps drop 5 C below
|
|
the pause thresholds, then resumes at the same step -- no points are lost.
|
|
If the STM32 link is down the sweep still runs, with a console warning
|
|
that the guard is inactive.
|
|
|
|
### 5. Run efficiency sweeps
|
|
|
|
#### Voltage sweep (1D)
|
|
|
|
```bash
|
|
uv run bench sweep \
|
|
--v-start 30 --v-stop 100 --v-step 5 \
|
|
--current-limit 20 \
|
|
--load-mode CP --load-value 200 \
|
|
--settle 2.0 -o voltage_sweep.csv
|
|
```
|
|
|
|
#### Load sweep at fixed voltage (1D)
|
|
|
|
```bash
|
|
uv run bench sweep-load \
|
|
--voltage 60 --current-limit 20 \
|
|
--i-start 0.5 --i-stop 15 --i-step 0.5 \
|
|
--settle 2.0 -o load_sweep.csv
|
|
```
|
|
|
|
#### 2D voltage x load sweep (efficiency map)
|
|
|
|
```bash
|
|
# Constant Power mode
|
|
uv run bench sweep-vi \
|
|
--v-start 60 --v-stop 100 --v-step 5 \
|
|
--l-start 50 --l-stop 500 --l-step 50 \
|
|
--load-mode CP --current-limit 20 \
|
|
--settle 2.0 -o map_cp.csv
|
|
|
|
# Constant Current mode
|
|
uv run bench sweep-vi \
|
|
--v-start 35 --v-stop 100 --v-step 5 \
|
|
--l-start 0.5 --l-stop 15 --l-step 0.5 \
|
|
--load-mode CC --current-limit 20 \
|
|
--settle 2.0 -o map_cc.csv
|
|
```
|
|
|
|
### 6. Analyze sweep results
|
|
|
|
```bash
|
|
# Generate efficiency overlay, heatmap, and power loss plots (no instruments needed)
|
|
uv run bench plot-sweep map_cp.csv
|
|
|
|
# Save plots without displaying
|
|
uv run bench plot-sweep map_cp.csv --no-show -o plots/
|
|
```
|
|
|
|
Produces three PNG files:
|
|
- `*_efficiency.png` -- efficiency vs load, one line per voltage, best point marked
|
|
- `*_heatmap.png` -- 2D efficiency surface (voltage x load)
|
|
- `*_loss.png` -- power loss vs load, all voltages overlaid
|
|
|
|
#### Efficiency vs Vin vs current from any logged CSV
|
|
|
|
`bench-plot` auto-detects all three CSV formats the tooling produces -- sweep
|
|
CSVs, GUI data logs (`data_*.csv`), and full-rate telemetry logs
|
|
(`*_telem.csv`) -- and draws an operating-point map (x = Vin, y = current,
|
|
color = efficiency) plus efficiency-vs-current curves grouped by Vin bin:
|
|
|
|
```bash
|
|
uv run bench-plot # no args -> file-picker dialog
|
|
uv run bench-plot data_20260703_140000.csv
|
|
uv run bench-plot run1_telem.csv run2_telem.csv --vin-bin 2 --save eff.png
|
|
|
|
# options: --current iout|iin, --source auto|hioki|instr|stm (data logs),
|
|
# --min-pout W (default 5), --vin-bin V (default 1), --save PNG
|
|
```
|
|
|
|
Also reachable via the GUI's "Plot Eff..." button (Logging section, opens the
|
|
same dialog preselecting the last log) and `plot_eff.bat` one level up
|
|
(double-click for the dialog, or drag && drop CSV files onto it).
|
|
|
|
For GUI data logs the efficiency source defaults to `auto`: HIOKI EFF1 if the
|
|
meter was connected, else supply/load power ratio, else the board's own
|
|
`stm_eff_net_pct`. Telemetry logs always use the board's net efficiency
|
|
(`(P_out - P_sys) / P_in`, iout_slow). Points below `--min-pout` (default
|
|
5 W, same as the GUI display gate) are dropped.
|
|
|
|
### 7. Tune converter parameters
|
|
|
|
The tuning commands combine the testbench instruments (ground truth efficiency from HIOKI) with direct STM32 parameter writes to find optimal settings.
|
|
|
|
#### Read current STM32 state
|
|
|
|
```bash
|
|
uv run bench stm32-read
|
|
```
|
|
|
|
#### Write a single parameter
|
|
|
|
```bash
|
|
uv run bench stm32-write --param dt_10_20A --value 20
|
|
```
|
|
|
|
#### Sweep a parameter to find the optimum
|
|
|
|
Sweeps a parameter from start to stop, measuring HIOKI efficiency + STM32 telemetry at each step. Plots the result.
|
|
|
|
```bash
|
|
# Optimize deadtime for the 10-20A bracket at 300W
|
|
uv run bench tune-param \
|
|
--param dt_10_20A --start 14 --stop 40 --step 1 \
|
|
--voltage 60 --current-limit 20 \
|
|
--load-mode CP --load-value 300 \
|
|
--settle 3.0 -o dt_tune.csv
|
|
|
|
# Tune Vfly proportional gain
|
|
uv run bench tune-param \
|
|
--param vfly_kp --start -2 --stop 2 --step 0.1 \
|
|
--voltage 60 --current-limit 20 \
|
|
--load-mode CP --load-value 200 \
|
|
--settle 3.0
|
|
```
|
|
|
|
#### Optimize the global dead-time
|
|
|
|
The firmware uses a single global dead-time (`dt_normal`). `tune-deadtime` sweeps
|
|
it from `--dt-start` to `--dt-stop`, optionally at several `--load-values`, and
|
|
picks the highest-efficiency value.
|
|
|
|
```bash
|
|
# Sweep at a single load and report the best value
|
|
uv run bench tune-deadtime \
|
|
--voltage 60 --current-limit 20 --load-mode CP \
|
|
--dt-start 14 --dt-stop 50 --dt-step 1 \
|
|
-o deadtime_results.csv
|
|
|
|
# Sweep across several loads, then apply the best dt_normal to the STM32
|
|
uv run bench tune-deadtime \
|
|
--voltage 60 --current-limit 20 --load-mode CP \
|
|
--load-values 100,300,500 \
|
|
--apply
|
|
```
|
|
|
|
### 8. Shade / irradiance profile simulation
|
|
|
|
Simulate cloud passing or partial shading with a CSV-driven sequence:
|
|
|
|
```bash
|
|
uv run bench shade-profile \
|
|
--profile samples/cloud_pass.csv \
|
|
--settle 2.0 -o shade_results.csv
|
|
```
|
|
|
|
Profile CSV format: `time,voltage,current_limit,load_mode,load_value`
|
|
|
|
### 9. Real-time debug console (TUI)
|
|
|
|
For live monitoring and parameter tuning via the Textual terminal UI:
|
|
|
|
```bash
|
|
cd code64
|
|
uv run debug-console COM4
|
|
```
|
|
|
|
Keybindings: `p` ping, `f` toggle EMA filter, `s` shutoff, `x` reset, `t` 50% duty test,
|
|
`c` relay on, `d` relay off, `h` hold converter, `g` toggle precharge, `q` quit.
|
|
Parameter writes are auto-retried until ACKed (the MCU's RX is EMI-lossy while switching).
|
|
|
|
### 10. Direct instrument control
|
|
|
|
```bash
|
|
# Supply
|
|
uv run bench supply set --voltage 48 --current 10
|
|
uv run bench supply on
|
|
uv run bench supply off
|
|
|
|
# Load
|
|
uv run bench load set --mode CP --value 200
|
|
uv run bench load on
|
|
uv run bench load off
|
|
|
|
# Emergency shutdown (load first, then supply)
|
|
uv run bench safe-off
|
|
```
|
|
|
|
## CLI Reference
|
|
|
|
```
|
|
uv run bench [-h] [--supply-address ADDR] [--load-port PORT] [--load-baud BAUD]
|
|
[--meter-address ADDR] [--timeout MS]
|
|
[--stm32-port PORT] [--stm32-baud BAUD]
|
|
{command}
|
|
```
|
|
|
|
| Command | Description |
|
|
|---|---|
|
|
| `identify` | Show identity and status of all instruments |
|
|
| `setup` | Configure all instruments for MPPT testing |
|
|
| `measure` | Single measurement from all three instruments |
|
|
| `monitor` | Continuous text monitoring with optional CSV |
|
|
| `live` | Real-time 4-panel matplotlib graph |
|
|
| `sweep` | Voltage sweep with efficiency recording |
|
|
| `sweep-load` | Load current sweep at fixed voltage |
|
|
| `sweep-vi` | 2D voltage x load sweep (efficiency map) |
|
|
| `efficiency` | Averaged efficiency at a fixed operating point |
|
|
| `shade-profile` | Run shade/irradiance profile from CSV |
|
|
| `plot-sweep` | Generate analysis plots from sweep CSV (offline) |
|
|
| `stm32-read` | Read all STM32 parameters and telemetry |
|
|
| `stm32-write` | Write a parameter to the STM32 |
|
|
| `tune-param` | Sweep an STM32 parameter while measuring efficiency |
|
|
| `tune-deadtime` | Auto-optimize deadtime for each current bracket |
|
|
| `supply` | Direct IT6500D control (on/off/set) |
|
|
| `load` | Direct Prodigit 3366G control (on/off/set) |
|
|
| `safe-off` | Emergency shutdown (load first, then supply) |
|
|
|
|
### Global Options
|
|
|
|
| Option | Default | Description |
|
|
|---|---|---|
|
|
| `--supply-address` | auto-detect | IT6500D VISA address |
|
|
| `--load-port` | `COM11` | Prodigit 3366G serial port |
|
|
| `--load-baud` | `115200` | Prodigit 3366G baud rate |
|
|
| `--meter-address` | auto-detect | HIOKI 3193-10 VISA address |
|
|
| `--timeout` | `5000` | VISA timeout in milliseconds |
|
|
| `--stm32-port` | `COM4` | STM32 debug serial port |
|
|
| `--stm32-baud` | `460800` | STM32 debug baud rate |
|
|
|
|
## Tunable STM32 Parameters
|
|
|
|
Names, IDs, types and ranges mirror the firmware (`code64/debug_console/protocol.py`).
|
|
|
|
| Parameter | Type | Range | Description |
|
|
|---|---|---|---|
|
|
| `VREF` | uint16 | 2340-3500 | ADC reference voltage |
|
|
| `vfly_kp` | float | -10 to 10 | Vfly proportional gain (mode 1, duty asymmetry) |
|
|
| `vfly_ki` | float | -10 to 10 | Vfly integral gain (mode 1) |
|
|
| `vfly_kp_phase` | float | -10 to 10 | Vfly P gain (mode 2, master-phase offset) |
|
|
| `vfly_phase_clamp` | uint16 | 0-10000 | Clamp on the master-phase offset (mode 2) |
|
|
| `vfly_clamp` | uint16 | 0-10000 | Vfly integrator clamp (mode 1) |
|
|
| `vfly_loop_trig` | uint16 | 1-10000 | Vfly loop counter trigger |
|
|
| `vfly_active` | uint8 | 0-3 | Vfly mode: 0 off, 1 duty-asym PI, 2 phase P, 3 manual both |
|
|
| `test_corr` | int16 | -3000 to 3000 | Manual duty-asymmetry correction (mode 3) |
|
|
| `phase_ofs` | int16 | -3000 to 3000 | Master-phase offset: manual (mode 3), readback (mode 2) |
|
|
| `cc_target` | float | 0-60000 | CC target (mA) |
|
|
| `cc_gain` | float | -1 to 1 | CC proportional gain |
|
|
| `cc_min_step` / `cc_max_step` | float | -1000-0 / 0-1000 | CC step clamps |
|
|
| `cc_loop_trig` | uint16 | 1-10000 | CC loop counter trigger |
|
|
| `cc_active` | int32 | 0-1 | CC loop enable |
|
|
| `mppt_step` | float | 1-200 | MPPT P&O step size |
|
|
| `mppt_duty_min` / `mppt_duty_max` | float | 0-6800 | MPPT duty search bounds (CMP ticks) |
|
|
| `mppt_loop_trig` | uint16 | 1-50000 | MPPT loop counter trigger |
|
|
| `mppt_active` | int32 | 0-1 | MPPT loop enable |
|
|
| `cv_threshold` / `cv_hysteresis` | float | 20000-30000 / 0-5000 | CV corner (mV) |
|
|
| `cc_threshold` / `cc_hysteresis` | float | 0-55000 / 0-10000 | CC/Iout limit (mA) |
|
|
| `dt_normal` | uint16 | 14-200 | Single global dead-time (dt register ticks) |
|
|
| `override_duty` | uint16 | 716-6442 | Manual fixed-duty base (CMP ticks, D=10..90%) |
|
|
| `manual_duty_en` | uint8 | 0-1 | Enter manual fixed-duty mode |
|
|
| `precharge_kp` / `precharge_ki` | float | 0-100 / 0-10 | Closed-loop precharge PI gains |
|
|
| `precharge_reg_en` | uint8 | 0-1 | Enable closed-loop precharge |
|
|
| `dither_en` | uint8 | 0-1 | Enable duty dithering (de-stack-band avoidance) |
|
|
| `dither_band_lo` / `dither_band_hi` | uint16 | 716-6442 | Forbidden duty band edges (CMP ticks) |
|
|
| `dither_anear` / `dither_afar` | uint16 | 716-6442 | Out-of-band dither anchors |
|
|
| `dither_dzero` | uint16 | 716-6442 | \|e\| fold center (D=0.5) |
|
|
| `adc4_trig_phase` | uint16 | 3-14313 | HRTIM master CMP3: iout_slow sample instant |
|
|
| `iin_zero_sum` | uint16 | 0-32760 | IIN software zero offset (sum-of-8 counts) |
|
|
|
|
## CSV Output Format
|
|
|
|
Sweep CSV files contain:
|
|
|
|
| Column | Description |
|
|
|---|---|
|
|
| `voltage_set` | Supply voltage setpoint (V) |
|
|
| `current_limit` | Supply current limit (A) |
|
|
| `load_setpoint` | Load setpoint value (A for CC, W for CP) |
|
|
| `supply_V/I/P` | Supply measured voltage, current, power |
|
|
| `load_V/I/P` | Load measured voltage, current, power |
|
|
| `input_power` | HIOKI P5 -- power into MPPT tracker (W) |
|
|
| `output_power` | HIOKI P6 -- power out of MPPT tracker (W) |
|
|
| `efficiency` | HIOKI EFF1 -- P6/P5 x 100 (%) |
|
|
|
|
Tuning CSV files additionally contain `param_name`, `param_value`, and STM32 telemetry columns (`stm_vin`, `stm_vout`, `stm_iin`, `stm_iout`, `stm_eff`, `stm_vfly`, `stm_etemp`).
|
|
|
|
GUI data-log CSVs contain the instrument columns plus the latest STM32
|
|
broadcast snapshot per row (`stm_counter` … `stm_age_s`; flag/fault registers
|
|
as hex). While logging, the full-rate 100 Hz telemetry stream is additionally
|
|
written to `<name>_telem.csv` with every broadcast field (one row per fresh
|
|
publish, `pc_time`/`t_mono` timestamps, computed `p_in_W`/`p_out_W`).
|
|
|
|
Note: STM32-derived `power_out_W`/`efficiency` now use `iout_slow` (the
|
|
PWM-synchronous ADC4 output current) instead of the fast protection-path
|
|
`iout` — tuner numbers shift slightly vs. old logs.
|
|
|
|
## Project Structure
|
|
|
|
```
|
|
mppt-testbench/
|
|
+-- IT6500D/ git submodule -- DC power supply driver
|
|
+-- PRODIGIT-3366G/ git submodule -- electronic load driver
|
|
+-- HIOKI-3193-10/ git submodule -- power analyzer driver
|
|
+-- testbench/
|
|
| +-- __init__.py exports MPPTTestbench
|
|
| +-- bench.py orchestrator (sweeps, measurement, auto-range wait)
|
|
| +-- cli.py unified CLI entry point
|
|
| +-- gui.py tkinter GUI with live plots
|
|
| +-- gui_workers.py background instrument I/O thread
|
|
| +-- stm32_link.py STM32 debug protocol: 114B broadcast RX + CRC-framed TX commands (8-O-1)
|
|
| +-- plot_eff.py efficiency vs Vin vs current plots from any logged CSV
|
|
| +-- tuner.py automated tuning routines (param sweep, deadtime opt)
|
|
+-- code64/
|
|
| +-- Core/ STM32G474 firmware (C)
|
|
| +-- Drivers/ HAL drivers
|
|
| +-- debug_console/ Textual TUI for live debugging
|
|
| +-- pyproject.toml uv-compatible package config
|
|
+-- samples/ shade profile CSV examples
|
|
+-- pyproject.toml package config, entry points: bench, bench-gui, bench-plot
|
|
```
|
|
|
|
## Dependencies
|
|
|
|
- Python >= 3.12
|
|
- [PyVISA](https://pyvisa.readthedocs.io/) + [pyvisa-py](https://pyvisa.readthedocs.io/projects/pyvisa-py/)
|
|
- [pyserial](https://pyserial.readthedocs.io/)
|
|
- [matplotlib](https://matplotlib.org/)
|
|
- [numpy](https://numpy.org/)
|
|
- NI-VISA runtime (for GPIB/USB-TMC communication)
|
|
- [Textual](https://textual.textualize.io/) (debug console only, in code64/)
|