The repository is now only the HPCS 6500 driver: a Rust library (serial transport, every protocol command, block parsing, USBPcap captures, a simulated instrument) and the hpcs6500 CLI with the flags of the former hpcs6500.py, plus dump/capture for reverse engineering. The lamp station, lamp export, TM-30, label printer and LED calibration move to B4L/lamp-testbench. Golden tests pin the Rust output to the Python driver's: parsed values bit-exact, report text, CSV and number formatting. The Python driver is at tag python-final.
6.6 KiB
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, which pulls this repository in as a submodule (the same layout as 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
cargo install --git https://git.b4l.co.th/B4L/HPCS6500-py # 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 ....
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.
hpcs6500 --simulate # built-in sample
hpcs6500 --simulate captures/run.pcap --continuous # replay a capture
USB captures (reverse engineering)
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
[dependencies]
hpcs6500 = { git = "https://git.b4l.co.th/B4L/HPCS6500-py", default-features = false }
default-features = false leaves out the command line tool's dependencies.
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: command bytes, block layouts, field offsets and the measurement sequences. It was reverse-engineered from USB captures of the vendor software.
Development
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.