153 lines
6.6 KiB
Markdown
153 lines
6.6 KiB
Markdown
# HPCS 6500 — open-source driver
|
|
|
|
Rust driver and command line tool for the HPCS 6500 spectrophotometer /
|
|
integrating sphere. It talks to the instrument directly over its USB serial
|
|
port and replaces the vendor software for measurement automation and data
|
|
extraction.
|
|
|
|
This repository is only the driver. The lamp measurement station, the lamp
|
|
comparison export (TM-30, label printing) and the LED light calibration are
|
|
in the testbench repository,
|
|
[B4L/lamp-testbench](https://git.b4l.co.th/B4L/lamp-testbench), which pulls
|
|
this repository in as a submodule (the same layout as
|
|
[mppt-testbench](https://git.b4l.co.th/B4L/mppt-testbench)).
|
|
|
|
## What it reads
|
|
|
|
- **Photometric**: luminous flux (lm), efficacy (lm/W), CCT (K), Duv
|
|
- **Colour**: CRI Ra and R1-R15, SDCM, TLCI-2012, CIE xy / uv / u'v', CIE 1931 XYZ
|
|
- **Radiometric**: total, UV, blue, yellow, red, far-red and IR flux (mW)
|
|
- **Spectrum**: 350 points, 380-1050 nm
|
|
- **Electrical**: voltage, current, power, power factor, frequency
|
|
- **Harmonics**: 50 voltage and current harmonics, UThd, AThd, one cycle of both waveforms
|
|
- **Power supply**: the built-in AC (100-240 V, 50/60 Hz) and DC (1-60 V, 0-5 A) supply
|
|
|
|
## Install
|
|
|
|
```bash
|
|
cargo install --git https://git.b4l.co.th/B4L/HPCS6500 # puts `hpcs6500` on the PATH
|
|
# or, in a checkout:
|
|
cargo build --release # target/release/hpcs6500(.exe)
|
|
```
|
|
|
|
Windows needs no driver: the instrument is an STM32 virtual COM port
|
|
(VID `0483`, PID `5741`) and is found automatically. Linux: add yourself to
|
|
the `dialout` group.
|
|
|
|
## Command line
|
|
|
|
The flags are the ones of the former Python tool: `uv run hpcs6500.py ...`
|
|
is now `hpcs6500 ...`.
|
|
|
|
```bash
|
|
hpcs6500 # single reading (vendor single-test flow)
|
|
hpcs6500 --continuous # continuous readings, PSU on while measuring
|
|
hpcs6500 --continuous --count 10 --csv out.csv
|
|
hpcs6500 --quick --continuous # lumen + CCT only
|
|
hpcs6500 --spectrum --harmonics # full spectrum and harmonics
|
|
hpcs6500 --json # one JSON object per reading on stdout
|
|
hpcs6500 --passive --continuous # read only, while the vendor software drives the instrument
|
|
hpcs6500 --port COM42 # pick the port
|
|
hpcs6500 ports # list serial ports, the instrument is marked
|
|
|
|
# power supply
|
|
hpcs6500 --psu-status
|
|
hpcs6500 --mode ac --voltage 230 --frequency 50
|
|
hpcs6500 --mode dc --voltage 12 --current 1.0
|
|
hpcs6500 --psu-on / hpcs6500 --psu-off
|
|
hpcs6500 --integration 500 # ms, 0 = auto
|
|
```
|
|
|
|
Ctrl+C stops a continuous run cleanly: the supply is switched off and the
|
|
instrument reset. The text output and the CSV files are character for
|
|
character those of the Python tool, so scripts that read them keep working.
|
|
|
|
### Without the instrument
|
|
|
|
`--simulate` talks to a simulated instrument that answers every command of
|
|
the protocol: with a capture it replays the readings recorded in it,
|
|
without one it uses a built-in sample reading.
|
|
|
|
```bash
|
|
hpcs6500 --simulate # built-in sample
|
|
hpcs6500 --simulate captures/run.pcap --continuous # replay a capture
|
|
```
|
|
|
|
### USB captures (reverse engineering)
|
|
|
|
```bash
|
|
hpcs6500 --parse captures/run.pcap [--quick] [--spectrum] [--json] # readings in a capture
|
|
hpcs6500 dump captures/run.pcap # traffic timeline + <name>_TX.bin / _RX.bin
|
|
hpcs6500 capture # record the vendor software with USBPcap (admin, Windows)
|
|
```
|
|
|
|
`capture` starts `USBPcapCMD` on every root hub, waits for Ctrl+C, keeps the
|
|
largest file and dumps it. Captures go to `captures/` (not in git).
|
|
|
|
## Library
|
|
|
|
```toml
|
|
[dependencies]
|
|
hpcs6500 = { git = "https://git.b4l.co.th/B4L/HPCS6500", default-features = false }
|
|
```
|
|
|
|
`default-features = false` leaves out the command line tool's dependencies.
|
|
|
|
```rust
|
|
use hpcs6500::{Hpcs6500, Mode};
|
|
|
|
let port = hpcs6500::find_port().ok_or(hpcs6500::Error::NotFound)?;
|
|
let mut dev = Hpcs6500::open(&port)?;
|
|
dev.identify()?; // "HPCS6500"
|
|
dev.set_mode(Mode::Ac)?;
|
|
dev.set_ac_voltage(230.0)?;
|
|
dev.set_ac_frequency(50.0)?;
|
|
dev.psu_on()?;
|
|
dev.send_test_config(false)?; // the vendor's single-test configuration
|
|
let r = dev.take_single_reading()?; // trigger, poll, read both blocks, reset
|
|
dev.psu_off()?;
|
|
println!("{:.0} lm {:.0} K Ra {:.1}", r.flux_lm(), r.cct_k(), r.ra());
|
|
println!("{:?}", r.get("Phi_eb_mW")); // every field by its CSV name
|
|
let nm = r.spectrum_nm(); // with r.spectrum
|
|
```
|
|
|
|
| Item | |
|
|
|---|---|
|
|
| `Hpcs6500::open(port)` / `with_transport(t, name)` | the instrument over the serial port or any `Transport` |
|
|
| `identify`, `poll_state`, `wait_for_data` | |
|
|
| `take_single_reading`, `take_reading(psu)`, `read_current` | single test, one continuous reading, passive read |
|
|
| `start_measurement`, `stop_measurement`, `reset`, `send_test_config` | the raw sequence steps |
|
|
| `psu_on`, `psu_off`, `set_mode`, `set_ac_voltage`, `set_ac_frequency`, `set_dc_voltage`, `set_dc_current`, `read_psu_settings` | built-in supply |
|
|
| `set_integration_time(ms)` | 0 = auto |
|
|
| `read_measurement_block`, `read_electrical_block` | raw 3904 / 1584-byte blocks |
|
|
| `set_cancel_flag(flag)`, `pause(d)` | long waits end with `Error::Cancelled` when the flag goes up (Ctrl+C) |
|
|
| `Reading` | `values` by `FIELDS`, `get(name)`, `scalars()`, `spectrum`, `spectrum_nm()`, `electrical` with `harmonics`; serializes to the Python driver's JSON keys |
|
|
| `pcap::read_messages`, `pcap::readings`, `pcap::blocks` | USBPcap captures |
|
|
| `sim::Simulator` | simulated instrument (`sample()`, `from_pcap(path)`), logs every frame |
|
|
| `format::*` | the text report and the CSV export |
|
|
| `pyfmt::*` | Python-compatible number formatting (`repr`, `.6g`, `.2f`, `round`) |
|
|
|
|
Every method returns `Result`; errors say what was expected and what came
|
|
back (`no acknowledge for command 0x72 (got nothing)`).
|
|
|
|
## Protocol
|
|
|
|
The binary protocol is documented byte by byte in [PROTOCOL.md](PROTOCOL.md):
|
|
command bytes, block layouts, field offsets and the measurement sequences.
|
|
It was reverse-engineered from USB captures of the vendor software.
|
|
|
|
## Development
|
|
|
|
```bash
|
|
cargo test # protocol, simulator, golden tests
|
|
cargo clippy --all-targets -- -D warnings
|
|
```
|
|
|
|
`tests/golden/` holds the output of the former Python driver for real
|
|
readings (`tests/fixtures/`): the parsed values (bit-exact), the text report,
|
|
the CSV row and 2,000 number-formatting cases. The Rust driver reproduces all
|
|
of them exactly. Where `captures/` is present, the `--parse` reports of real
|
|
captures are compared too.
|
|
|
|
The Python driver this replaces is at tag `python-final`.
|