Hardware · 2026
radeye-b20 — a Python driver for an undocumented survey meter
A pip-installable driver for the Thermo Scientific RadEye B20 radiation survey meter, reverse-engineered from the wire and published on PyPI under MIT.
The Thermo Scientific RadEye B20 is a handheld radiation survey meter. It streams live readings out of its infrared read-head, but the serial protocol is undocumented and — as far as I can tell — no public library speaks it. This one does.
It was reverse-engineered by watching the wire and checking decoded values against the meter's own display, then packaged as a small, dependency-light Python library so anyone with the same instrument can log and plot from it without repeating the work.
pip install radeye-b20from radeye_b20 import RadEye
with RadEye() as meter: # auto-detects the IR-USB cable
for reading in meter.stream():
print(reading.value, reading.unit) # e.g. 3.51 cpsThere's a command line too, for when you just want a CSV:
radeye ports # list serial ports, flag the likely RadEye
radeye stream # live readings until Ctrl+C
radeye stream --csv # timestamp,value,unit (pipe to a file)How the protocol works
The meter sends fixed ASCII frames at 9600 baud, 7 data bits, even parity, 1 stop bit, each framed by STX … ETX CR LF. A decoded frame looks like this:
351 5 0 0 04 FH41B2 0 42
| | | └─ checksum (algorithm unknown — exposed, not verified)
| | └───────── device / probe tag
| └────────────────────── unit code: 5 = cps, 3 = cpm
└────────────────────────── value digits (decimal point implied by the unit)There's no decimal point on the wire, so the integer is scaled per unit (cps × 0.01, cpm × 0.1). The cable is a Prolific PL2303, which the driver auto-detects by USB vendor ID, and whose occasional zero-length-read glitches it tolerates without dropping the connection.
Each reading carries the scaled value, the unit, a timestamp taken at decode time, the raw digits, the unit code, the trailing checksum token, and the cleaned raw frame — so nothing the meter said is hidden from you.
Honest limits
I'd rather you know these up front than discover them:
- Reverse-engineered, not from a spec. Verified against one meter's display.
- Only cps and cpm are trusted. Other unit and range modes are surfaced as
code:<n>and passed through unscaled — the meter has modes I couldn't test, and I won't pretend the scaling for those is right. - The checksum is not verified. The algorithm is unknown; the field is exposed so you can experiment, but frames aren't validated against it.
- Scaling may be meter-dependent. If readings are off by a constant factor, check against the display and set the scale factors accordingly.
Not for safety-critical use
This is a research and logging convenience. It is not calibrated, validated, or certified, and must not be relied on for radiation-safety decisions, dose assessment, or any situation where a wrong or missing reading could harm someone. For that, use the instrument's own display and your organisation's approved procedures.
Published
On PyPI as radeye-b20, source on GitHub, MIT licensed. Releases go out through PyPI Trusted Publishing from a GitHub Actions workflow, so no API token is stored anywhere. If you have a B20 and can capture frames in a mode this doesn't handle — dose-rate especially — raw frames alongside the displayed value are exactly what's needed to extend it.