Documents the new layers introduced over the past several commits.
- docs/19_frame_io_and_alm_helpers.md (new): full reference for the
FrameIO and AlmTester helpers — three access levels (high/mid/low),
full API tables, fixture wiring, cookbook patterns, and §7
describing the four-phase SETUP/PROCEDURE/ASSERT/TEARDOWN test
pattern with the three template flavors plus a §7.4 link to the
PSU+LIN template.
- docs/14_power_supply.md: rewritten and expanded.
§3 cross-platform port resolution (Windows / WSL1 / WSL2 +
usbipd-win / Linux native compatibility table)
§4 auto-detection via idn_substr
§5 session-managed power: contract for tests, must-not list,
what changed in the existing tests
§6 the settle-then-validate pattern: two-delays table (PSU
bench-dependent vs ECU firmware-dependent), copy-paste
example, tuning guidance for ECU_VALIDATION_TIME_S
§6 PSU settling characterization (-m psu_settling)
§7 library API reference table + safe_off_on_close
§9 troubleshooting expanded with WSL2 usbipd-win + dialout
- docs/18_test_catalog.md: voltage-tolerance section refreshed for
the settle-then-validate shape, new "Hardware – PSU settling
(opt-in)" category, new §8 "Hardware-test infrastructure"
documenting conftest.py, frame_io.py, alm_helpers.py,
psu_helpers.py, and both templates.
- docs/05_architecture_overview.md: components list split into
framework core / hardware test layer / artifacts. Mermaid diagram
gained a Hardware-test helpers subgraph showing FrameIO,
AlmTester, rgb_to_pwm, and the templates. Data/control flow
summary describes the session-managed PSU and the helper layer.
- docs/15_report_properties_cheatsheet.md: PSU section split into
per-test (function-scoped rp) and module-scoped (testsuite
property) blocks; added psu_resolved_port, psu_resolved_idn,
psu_settled_s, validation_time_s.
- docs/README.md: links to the new doc 19.
- README.md, TESTING_FRAMEWORK_GUIDE.md: project-structure trees
expanded to show the full current layout — every file and
directory under tests/hardware/ (conftest, helpers, templates,
tests), tests/unit/, config/, docs/, scripts/, and vendor/.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
487 lines
19 KiB
Markdown
487 lines
19 KiB
Markdown
# Power Supply (Owon) — control, configuration, tests, and quick demo
|
|
|
|
This guide covers driving the Owon bench power supply via SCPI over a
|
|
serial link, plus the cross-platform port resolver and the safety
|
|
guarantees the controller class provides.
|
|
|
|
> **MUM users**: the Melexis Universal Master has its own power output
|
|
> on `power_out0` and the MUM adapter calls `power_up()` /
|
|
> `power_down()` in `connect()` / `disconnect()` automatically. The
|
|
> Owon PSU is **not required** for the standard MUM flow — leave
|
|
> `power_supply.enabled: false`. The Owon remains useful for
|
|
> over/under-voltage scenarios, separate-rail tests, or when running
|
|
> with the deprecated BabyLIN adapter (which has no built-in power).
|
|
|
|
| Artifact | Path |
|
|
|---|---|
|
|
| Controller library | [`ecu_framework/power/owon_psu.py`](../ecu_framework/power/owon_psu.py) |
|
|
| Hardware test | [`tests/hardware/test_owon_psu.py`](../tests/hardware/test_owon_psu.py) |
|
|
| Quick demo script | [`vendor/Owon/owon_psu_quick_demo.py`](../vendor/Owon/owon_psu_quick_demo.py) |
|
|
| Central config | [`config/test_config.yaml`](../config/test_config.yaml) → `power_supply` |
|
|
| Per-machine override | `config/owon_psu.yaml` or env `OWON_PSU_CONFIG` |
|
|
|
|
---
|
|
|
|
## 1. Install dependencies
|
|
|
|
```powershell
|
|
pip install -r .\requirements.txt
|
|
```
|
|
|
|
`pyserial` is the only non-stdlib dep used by the controller.
|
|
|
|
---
|
|
|
|
## 2. Configure
|
|
|
|
Settings can live centrally in `config/test_config.yaml` or be peeled
|
|
out into a machine-specific `config/owon_psu.yaml` (or any path set
|
|
via `OWON_PSU_CONFIG`). The loader merges the per-machine file into
|
|
the central `power_supply` section.
|
|
|
|
```yaml
|
|
power_supply:
|
|
enabled: true
|
|
port: COM7 # see §3 for cross-platform behaviour
|
|
baudrate: 115200
|
|
timeout: 1.0
|
|
eol: "\n" # or "\r\n" if your device requires CRLF
|
|
parity: N # N|E|O
|
|
stopbits: 1 # 1|1.5|2
|
|
xonxoff: false
|
|
rtscts: false
|
|
dsrdtr: false
|
|
idn_substr: OWON # optional — see §4 (auto-detection)
|
|
do_set: false
|
|
set_voltage: 5.0
|
|
set_current: 0.1
|
|
```
|
|
|
|
### Field reference
|
|
|
|
| Field | Default | Meaning |
|
|
|---|---|---|
|
|
| `enabled` | `false` | Master gate. Tests/utilities skip when `false`. |
|
|
| `port` | `null` | Bench port name. See §3 — works for `COM7` *or* `/dev/ttyUSB0` and translates between them. |
|
|
| `baudrate` | `115200` | Serial bit rate. |
|
|
| `timeout` | `1.0` | Read timeout in seconds. |
|
|
| `eol` | `"\n"` | Line terminator appended to every command and expected on every response. |
|
|
| `parity` | `"N"` | One of `N`, `E`, `O`. Translated to `pyserial` constants by `SerialParams.from_config()`. |
|
|
| `stopbits` | `1` | One of `1`, `1.5`, `2`. |
|
|
| `xonxoff` / `rtscts` / `dsrdtr` | `false` | Flow control flags. |
|
|
| `idn_substr` | `null` | Optional substring (case-insensitive) the device's `*IDN?` must contain to be accepted. Used as the filter when scanning ports for auto-detection. |
|
|
| `do_set` | `false` | If `true`, the hardware test runs the set/measure cycle (sets V/I, enables output briefly, measures, disables). |
|
|
| `set_voltage` / `set_current` | `5.0` / `0.1` | Setpoints used when `do_set: true`. |
|
|
|
|
---
|
|
|
|
## 3. Cross-platform port resolution
|
|
|
|
A bench config typically names the port the way Windows sees it
|
|
(`COM7`). The resolver lets the **same config** work on Windows,
|
|
Linux, and WSL by trying multiple candidates in priority order.
|
|
|
|
### What the resolver does
|
|
|
|
`resolve_port(configured, *, idn_substr, params)` walks four phases
|
|
and returns the first port whose `*IDN?` response is non-empty
|
|
(filtered by `idn_substr` if given):
|
|
|
|
| Phase | What's tried | Use case |
|
|
|---|---|---|
|
|
| 1 | `configured` verbatim | Windows native — `COM7` opens directly. |
|
|
| 2 | Cross-platform translation | `COM7` ↔ `/dev/ttyS6` on WSL1; `/dev/ttyS6` ↔ `COM7` on Windows. |
|
|
| 3 | Linux USB-serial paths | `/dev/ttyUSB*` and `/dev/ttyACM*` — covers WSL2 with `usbipd-win` plus generic Linux USB adapters. Linux/WSL only. |
|
|
| 4 | Full `scan_ports()` | Last resort — probes every serial port `pyserial` reports. |
|
|
|
|
Linux device files that don't exist on disk are skipped without an
|
|
open attempt, so the resolver is fast even on machines with many
|
|
phantom `ttyS*` entries.
|
|
|
|
### What works on each platform with `port: COM7`
|
|
|
|
| Host | What happens |
|
|
|---|---|
|
|
| **Windows native** | Phase 1 hits `COM7` directly. |
|
|
| **WSL1** | Phase 1 fails on `COM7`, Phase 2 finds `/dev/ttyS6` (the COM7 mapping). |
|
|
| **WSL2 + `usbipd-win`** | Phase 1+2 fail, Phase 3 finds the attached adapter at `/dev/ttyUSB0`. |
|
|
| **Linux native (USB adapter)** | Phases 1+2 fail, Phase 3 finds `/dev/ttyUSB0`. |
|
|
|
|
The resolved port is recorded in the JUnit testsuite properties as
|
|
`psu_resolved_port` (and the IDN as `psu_resolved_idn`), so report
|
|
viewers can see which path was used.
|
|
|
|
### Translation helpers
|
|
|
|
Useful as building blocks if you need to do the mapping yourself:
|
|
|
|
```python
|
|
from ecu_framework.power import (
|
|
windows_com_to_linux, linux_serial_to_windows,
|
|
candidate_ports, resolve_port,
|
|
)
|
|
|
|
windows_com_to_linux("COM7") # → "/dev/ttyS6"
|
|
windows_com_to_linux("com10") # → "/dev/ttyS9"
|
|
linux_serial_to_windows("/dev/ttyS6") # → "COM7"
|
|
|
|
# What resolve_port will try, in order, for port="COM7" on Linux:
|
|
candidate_ports("COM7")
|
|
# → ['COM7', '/dev/ttyS6', '/dev/ttyUSB0', '/dev/ttyUSB1', '/dev/ttyACM0', ...]
|
|
```
|
|
|
|
---
|
|
|
|
## 4. Auto-detection
|
|
|
|
Leave `port` empty and set `idn_substr` to let the resolver scan:
|
|
|
|
```yaml
|
|
power_supply:
|
|
enabled: true
|
|
port: # ← empty
|
|
idn_substr: OWON # ← required so we don't grab a different SCPI device
|
|
...
|
|
```
|
|
|
|
With no `port`, Phase 1 and Phase 2 short-circuit; Phase 3 (Linux USB
|
|
paths) and Phase 4 (full scan) do the work. The first port whose IDN
|
|
contains `OWON` (case-insensitive) wins.
|
|
|
|
> **Tip:** without `idn_substr`, *any* device that responds to `*IDN?`
|
|
> on any port is accepted — fine when the PSU is the only SCPI thing
|
|
> attached, risky otherwise. Always set `idn_substr` if your bench has
|
|
> other SCPI hardware.
|
|
|
|
---
|
|
|
|
## 5. Session-managed power (the bench powers the ECU through the PSU)
|
|
|
|
On benches where the **Owon PSU powers the ECU** (the MUM only carries
|
|
LIN traffic), the PSU output must stay on for the *entire* test
|
|
session — not just the duration of an individual PSU test. Otherwise
|
|
every test that runs after a closed PSU connection would brown out
|
|
the ECU and fail.
|
|
|
|
The hardware-suite conftest
|
|
([`tests/hardware/conftest.py`](../tests/hardware/conftest.py))
|
|
implements this with three session-scoped fixtures:
|
|
|
|
| Fixture | Scope | Role |
|
|
|---|---|---|
|
|
| `_psu_or_none` | session | Tolerant: opens the PSU once, parks at `set_voltage` / `set_current`, enables output. Yields the live `OwonPSU` or `None` if unreachable. Closes (with `output 0`) at session end. |
|
|
| `_psu_powers_bench` | session, **autouse** | Realizes `_psu_or_none`. Every hardware test triggers PSU power-up at session start, even tests that don't request `psu` by name. |
|
|
| `psu` | session | Public fixture for tests that read measurements or perturb voltage. Skips cleanly when the PSU isn't available. |
|
|
|
|
### What this means for tests
|
|
|
|
Tests **should**:
|
|
|
|
- Request `psu` if they need to read measurements or change the supply voltage.
|
|
- Always restore nominal voltage in their `finally` block — the session fixture won't restore it between tests.
|
|
|
|
Tests **must not**:
|
|
|
|
- Call `psu.set_output(False)` — this kills ECU power for every later test in the same session.
|
|
- Call `psu.close()` — the session fixture owns the lifecycle.
|
|
|
|
### What changed in the existing tests
|
|
|
|
- **`tests/hardware/test_owon_psu.py`** is now read-only: it queries `*IDN?`, `output?`, and the parsed measurement helpers, but doesn't toggle the output. The previous toggle-and-restore cycle has been deleted because it would brown out the bench mid-session.
|
|
- **`tests/hardware/_test_case_template_psu_lin.py`** drops its local `psu` fixture and uses the conftest's. Its autouse `_park_at_nominal` only restores voltage between tests — it never toggles output.
|
|
|
|
---
|
|
|
|
## 6. Run the hardware test
|
|
|
|
Skips cleanly unless `power_supply.enabled` is true, a port can be
|
|
resolved, and the device responds to `*IDN?`.
|
|
|
|
```powershell
|
|
pytest -k test_owon_psu_idn_and_optional_set -m hardware -q
|
|
```
|
|
|
|
What it does:
|
|
|
|
1. Resolves a working port via `resolve_port(...)` (cross-platform,
|
|
IDN-verified).
|
|
2. Queries `*IDN?` and the initial `output?` state.
|
|
3. If `do_set` is true: sets V/I, enables output, waits, measures,
|
|
disables output. The measure/disable pair lives in an inner
|
|
`try`/`finally` so the disable runs even if measurement raises.
|
|
4. Records IDN, before/after output state, setpoints, and parsed
|
|
measurements as report properties.
|
|
5. The fixture's `safe_off_on_close=True` is a backstop — it will
|
|
send `output 0` once more when the port closes.
|
|
|
|
The test follows the four-phase
|
|
[SETUP / PROCEDURE / ASSERT / TEARDOWN pattern from the template](19_frame_io_and_alm_helpers.md#72-the-four-phase-test-pattern)
|
|
because it mutates real bench state.
|
|
|
|
### The settle-then-validate pattern (recommended for any voltage-changing test)
|
|
|
|
Voltage changes go through two delays — and confusing them is the
|
|
single most common source of flaky tests:
|
|
|
|
| Delay | Source | Bench-dependent? |
|
|
|---|---|---|
|
|
| **PSU settling** | Owon needs time to slew its output to the new setpoint | **Yes** — depends on PSU model, load, cable drop. Different up-step / down-step times in practice. |
|
|
| **ECU validation** | Firmware samples its supply rail, debounces, and republishes status on its 10 ms LIN cycle | No (firmware-dependent, but constant for a given build) |
|
|
|
|
The shared helper [`tests/hardware/psu_helpers.py`](../tests/hardware/psu_helpers.py)
|
|
exposes `apply_voltage_and_settle()` which separates the two cleanly:
|
|
|
|
```python
|
|
from psu_helpers import apply_voltage_and_settle
|
|
|
|
result = apply_voltage_and_settle(
|
|
psu, OVERVOLTAGE_V,
|
|
validation_time=ECU_VALIDATION_TIME_S, # firmware budget
|
|
)
|
|
# By here:
|
|
# - PSU output is measurably at OVERVOLTAGE_V (within ±0.10 V)
|
|
# - validation_time has elapsed since the rail settled
|
|
# So a single status read is unambiguous:
|
|
status = fio.read_signal("ALM_Status", "ALMVoltageStatus")
|
|
assert status == VOLTAGE_STATUS_OVER
|
|
```
|
|
|
|
What `apply_voltage_and_settle` does internally:
|
|
|
|
1. `psu.set_voltage(1, target_v)` — issue the setpoint.
|
|
2. Polls `measure_voltage_v()` every 50 ms until the rail is within
|
|
±100 mV of target (or raises `AssertionError` on timeout).
|
|
3. `time.sleep(validation_time)` — hold the steady rail.
|
|
4. Returns `{settled_s, validation_s, final_v, trace}` for reporting.
|
|
|
|
The poll-the-meter approach means the function works on any bench
|
|
without re-tuning sleeps. Up-step and down-step are handled
|
|
identically — each waits as long as that *specific* transition takes.
|
|
|
|
To pick `ECU_VALIDATION_TIME_S`, run the characterization in §6.1
|
|
to learn your PSU's slew time, then add a margin for the firmware's
|
|
detection-and-debounce window. Default `1.0 s` is conservative for
|
|
most automotive ECUs. Tests that change voltage many times should
|
|
use the smallest validation time their firmware tolerates.
|
|
|
|
### Characterizing PSU settling time
|
|
|
|
Voltage-tolerance tests need to wait long enough after a setpoint
|
|
change for the PSU's output to actually reach the new voltage. The
|
|
right wait depends on the PSU model and the load. To extract real
|
|
numbers, run the dedicated characterization test:
|
|
|
|
```powershell
|
|
pytest -m psu_settling -s
|
|
```
|
|
|
|
`tests/hardware/test_psu_voltage_settling.py` walks four
|
|
transitions (`13 V↔18 V`, `13 V↔7 V`), polls `measure_voltage_v()`
|
|
every 50 ms until the rail is within ±100 mV of target, and records
|
|
`settling_time_s` plus a downsampled voltage trace per case. The
|
|
test is marked `psu_settling` + `slow` so it doesn't run on every
|
|
`-m hardware` invocation — it's meant for periodic re-tuning, not
|
|
every CI run.
|
|
|
|
Use the recorded settling times to size constants like
|
|
`VOLTAGE_DETECT_TIMEOUT` in `test_overvolt.py`: the timeout has to
|
|
exceed *both* the PSU's settling time *and* the ECU's detection
|
|
delay, so add a margin to the larger of the two.
|
|
|
|
### Writing a PSU+LIN test (over/undervoltage etc.)
|
|
|
|
For tests that *combine* PSU control with LIN observation — e.g.
|
|
overvoltage / undervoltage tolerance — there's a dedicated
|
|
copy-paste-ready template at
|
|
[`tests/hardware/_test_case_template_psu_lin.py`](../tests/hardware/_test_case_template_psu_lin.py).
|
|
It contains:
|
|
|
|
- The three module-scoped fixtures (`fio`, `alm`, `psu`) wired with
|
|
cross-platform port resolution and `safe_off_on_close=True`.
|
|
- An autouse `_park_at_nominal` fixture that parks the PSU at
|
|
`NOMINAL_VOLTAGE` and the LED OFF before AND after every test, so
|
|
failures don't leak supply state between tests.
|
|
- A `wait_for_voltage_status(fio, target, …)` helper that polls
|
|
`ALM_Status.ALMVoltageStatus` until it matches.
|
|
- Three flavors:
|
|
| Flavor | Demonstrates |
|
|
|---|---|
|
|
| A | Overvoltage detection — drive PSU above OV threshold, expect `ALMVoltageStatus = 0x02`, restore. |
|
|
| B | Undervoltage detection — symmetric for UV (`0x01`). |
|
|
| C | Parametrized voltage sweep walking `(V, expected_status)` tuples. |
|
|
|
|
Tune the four constants at the top of the file
|
|
(`NOMINAL_VOLTAGE`, `OVERVOLTAGE_V`, `UNDERVOLTAGE_V`,
|
|
`SET_CURRENT_A`) to your ECU's datasheet before running on real
|
|
hardware. The defaults are conservative automotive ranges.
|
|
|
|
---
|
|
|
|
## 7. Library API
|
|
|
|
```python
|
|
from ecu_framework.power import (
|
|
SerialParams, OwonPSU, resolve_port,
|
|
scan_ports, auto_detect, try_idn_on_port,
|
|
)
|
|
```
|
|
|
|
### `SerialParams`
|
|
|
|
Plain dataclass for serial-port settings. Build directly, or from the
|
|
project's PSU config:
|
|
|
|
```python
|
|
params = SerialParams(baudrate=115200, timeout=1.0)
|
|
# or
|
|
params = SerialParams.from_config(config.power_supply) # translates 'N'/'1' → pyserial constants
|
|
```
|
|
|
|
### `OwonPSU`
|
|
|
|
Context-managed controller. Two construction paths:
|
|
|
|
```python
|
|
# Manual:
|
|
psu = OwonPSU(port="COM4", params=params, eol="\n")
|
|
|
|
# From central config (recommended):
|
|
psu = OwonPSU.from_config(config.power_supply)
|
|
```
|
|
|
|
Then either use as a context manager or call `open()` / `close()` by
|
|
hand. Both forms send `output 0` before closing the port if
|
|
`safe_off_on_close=True` (the default).
|
|
|
|
```python
|
|
with OwonPSU.from_config(cfg) as psu:
|
|
print(psu.idn()) # *IDN?
|
|
psu.set_voltage(1, 5.0) # SOUR:VOLT 5.000
|
|
psu.set_current(1, 0.1) # SOUR:CURR 0.100
|
|
psu.set_output(True) # output 1
|
|
v = psu.measure_voltage_v() # MEAS:VOLT? → float
|
|
i = psu.measure_current_a() # MEAS:CURR? → float
|
|
is_on = psu.output_is_on() # output? → True/False/None
|
|
# safe_off_on_close=True turned the output OFF before the port closed
|
|
```
|
|
|
|
#### Method reference
|
|
|
|
| Method | SCPI sent | Returns |
|
|
|---|---|---|
|
|
| `idn()` | `*IDN?` | `str` |
|
|
| `set_voltage(channel, volts)` | `SOUR:VOLT <V>` | `None`. `channel` is currently ignored — placeholder for multi-channel firmware. |
|
|
| `set_current(channel, amps)` | `SOUR:CURR <A>` | `None` |
|
|
| `set_output(on)` | `output 1`/`output 0` | `None`. Note: dialect uses *lowercase* `output`, not `OUTP ON`. |
|
|
| `output_status()` | `output?` | Raw `str` (`'ON'`/`'OFF'`/`'1'`/`'0'`). |
|
|
| `output_is_on()` | `output?` | `bool` (or `None` if unparseable). |
|
|
| `measure_voltage()` | `MEAS:VOLT?` | Raw `str`. |
|
|
| `measure_voltage_v()` | `MEAS:VOLT?` | `float` (V) or `None`. |
|
|
| `measure_current()` | `MEAS:CURR?` | Raw `str`. |
|
|
| `measure_current_a()` | `MEAS:CURR?` | `float` (A) or `None`. |
|
|
| `query(s)` | `s` | Single-line `str` response (with newline stripped). |
|
|
| `write(s)` | `s` | `None`. No response read. |
|
|
|
|
#### Safety: `safe_off_on_close`
|
|
|
|
`OwonPSU(safe_off_on_close=True)` (the default) sends `output 0`
|
|
before the serial port closes. This protects against leaving the
|
|
bench powered on after an aborted test, an exception in user code, or
|
|
a forgotten manual close. Errors during the safe-off attempt are
|
|
swallowed so the close itself always completes.
|
|
|
|
Pass `safe_off_on_close=False` only when you specifically need the
|
|
output to stay enabled across context-manager boundaries. The
|
|
discovery helper `try_idn_on_port` opts out by default since it
|
|
shouldn't drive the bench in either direction.
|
|
|
|
### Discovery helpers
|
|
|
|
```python
|
|
# Probe one port, return its IDN (or "" on failure):
|
|
try_idn_on_port("COM7", params)
|
|
|
|
# Scan every serial port; returns [(port, idn), ...] for responders:
|
|
scan_ports(params)
|
|
|
|
# Pick the first responder matching idn_substr (or first responder if no substring):
|
|
auto_detect(params, idn_substr="OWON")
|
|
|
|
# Cross-platform resolver (recommended): tries the configured port,
|
|
# its translation, USB-serial paths, then a full scan. Returns
|
|
# (port, idn) or None.
|
|
resolve_port("COM7", idn_substr="OWON", params=params)
|
|
```
|
|
|
|
---
|
|
|
|
## 8. Quick demo script
|
|
|
|
The quick demo reads `OWON_PSU_CONFIG` or `config/owon_psu.yaml` and
|
|
performs a short sequence using the same library.
|
|
|
|
```powershell
|
|
python .\vendor\Owon\owon_psu_quick_demo.py
|
|
```
|
|
|
|
It also scans ports with `*IDN?` via `scan_ports()` to help confirm
|
|
which port the device is on before you commit it to the YAML.
|
|
|
|
---
|
|
|
|
## 9. Troubleshooting
|
|
|
|
### Empty `*IDN?` / timeouts
|
|
|
|
- Verify the port and exclusivity — no other program may hold it open.
|
|
- Try `eol: "\r\n"` if your firmware revision expects CRLF.
|
|
- Adjust `parity` and `stopbits` per your device manual.
|
|
- Power-cycle the PSU and re-attempt — some firmware revisions need
|
|
a fresh boot before they accept SCPI.
|
|
|
|
### `Could not find a working PSU port`
|
|
|
|
The fixture skips with this message when `resolve_port` returns
|
|
`None`. Things to check, in order:
|
|
|
|
1. Is the device powered and connected?
|
|
2. Does another process (Putty, Owon's own tool, an old test session)
|
|
still hold the port?
|
|
3. Does your user have permission to open the device file? On
|
|
Debian-style systems: `sudo usermod -aG dialout $USER` and re-login.
|
|
4. **WSL2 specifically**: USB-serial adapters need
|
|
[`usbipd-win`](https://learn.microsoft.com/en-us/windows/wsl/connect-usb)
|
|
to bind the device into the Linux side. Once attached they appear
|
|
at `/dev/ttyUSB0` and the resolver's Phase 3 picks them up
|
|
automatically.
|
|
5. **WSL1**: COMx → /dev/ttySn mapping is automatic. If `/dev/ttyS6`
|
|
doesn't exist for `COM7`, the bench probably has Windows COM port
|
|
numbering you weren't expecting — list with
|
|
`ls /dev/ttyS*` and try `linux_serial_to_windows()` to confirm.
|
|
|
|
### Windows COM > 9
|
|
|
|
Most Python tooling (including `pyserial`) accepts `COM10` directly.
|
|
If a third-party tool needs the long form, use `\\.\COM10`. The
|
|
translator in this repo accepts any positive integer.
|
|
|
|
### Flow control
|
|
|
|
Keep `xonxoff`, `rtscts`, `dsrdtr` set to `false` unless your specific
|
|
PSU model requires otherwise — the Owon family used in this project
|
|
doesn't.
|
|
|
|
---
|
|
|
|
## 10. Related files
|
|
|
|
| File | Purpose |
|
|
|---|---|
|
|
| `ecu_framework/power/owon_psu.py` | Controller library (`SerialParams`, `OwonPSU`, resolver helpers). |
|
|
| `tests/hardware/test_owon_psu.py` | Hardware test wired to central config. |
|
|
| `vendor/Owon/owon_psu_quick_demo.py` | Quick demo runner. |
|
|
| `config/owon_psu.example.yaml` | Example per-machine YAML. |
|
|
| `tests/hardware/_test_case_template.py` | Copyable starting point for new hardware tests. |
|
|
| [`docs/19_frame_io_and_alm_helpers.md`](19_frame_io_and_alm_helpers.md) | The four-phase test pattern and the FrameIO / AlmTester helpers. |
|
|
| [`docs/15_report_properties_cheatsheet.md`](15_report_properties_cheatsheet.md) | Standard `rp(...)` keys including the PSU ones (`psu_idn`, `psu_resolved_port`, …). |
|