radeye-b20
A small Python driver for the Thermo Scientific RadEye B20 handheld radiation survey meter.
The RadEye B20 streams live readings over 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, so it's honest about what it can and can't do (see Limitations).
from 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 cps
Install
pip install radeye-b20
Or from source:
git clone https://github.com/navyasharmabits/radeye-b20
cd radeye-b20
pip install -e .
Requires Python 3.8+ and pyserial. Works on Linux, macOS, and Windows (the
meter appears as a normal USB serial port).
Command line
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)
radeye stream --port /dev/ttyUSB0 --scale-cps 0.01
Library
from radeye_b20 import RadEye, find_port
print(find_port()) # '/dev/ttyUSB0' or None
with RadEye(port="auto", scale_cps=0.01, scale_cpm=0.1) as meter:
r = meter.read() # one reading, or None on a skipped frame
if r:
print(r.value, r.unit, r.timestamp, r.checksum)
Each Reading carries the scaled value, the unit ("cps", "cpm", or
"code:<n>" for anything unverified), a unix timestamp taken at decode time,
the raw integer digits, the unit code, the trailing checksum token, and
the cleaned raw frame text.
How it works
The meter sends fixed ASCII frames at 9600 baud, 7 data bits, even parity, 1
stop bit (7E1), each framed by STX … ETX CR LF. A decoded frame looks like:
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 / by-id symlink, and whose occasional
zero-length-read glitches it tolerates without dropping the connection.
Limitations
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/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
scale_cps/scale_cpmaccordingly.
⚠️ Not for safety-critical use
This is a hobbyist/research convenience for logging and visualisation. 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.
Contributing
If you have a RadEye B20 and can capture frames in a mode this doesn't handle (especially dose-rate / µSv·h⁻¹ modes), a sample of raw frames alongside the displayed value is exactly what's needed to extend it. Open an issue or PR.
License
MIT © Navya Sharma
Metadata
Release files for radeye-b20 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| radeye_b20-0.1.0.tar.gz | 12.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| radeye_b20-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 24.1 kB
Release files / radeye_b20-0.1.0.tar.gz
| Download URL | radeye_b20-0.1.0.tar.gz |
|---|---|
| Size | 12.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
93881ce1b616605f03e3dd82dc9c5bda3631b20188c89499f00d05bd24b264e3
|
|
BLAKE2b-256 checksum How to use checksums |
de2467cb2a2189de1a5da2a7be51060d5c2c6d7ca732b3bb99d816050349edbc
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 23, 2026.
Transparency logRelease files / radeye_b20-0.1.0-py3-none-any.whl
| Download URL | radeye_b20-0.1.0-py3-none-any.whl |
|---|---|
| Size | 12.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
f0dda6d098ee12aaead8b546e9e75356d2b2b52792c8610f818831cf99cadf52
|
|
BLAKE2b-256 checksum How to use checksums |
82f156fe8d01fac75620e1e0f65bfb03b6a0d02dd7b9912f8f704d9030c0e63b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 23, 2026.
Transparency log