A small Linux reader for the Cleware USB-Temp temperature sensor on firmware
version 5, where the classic
clewarecontrol tool reports a fixed
bogus value (about -229 °C).
cleware_temp reads the sensor over USB-HID and prints the temperature, either as
InfluxDB line protocol (the default, a drop-in for Telegraf's exec input) or as a
plain number.
$ cleware_temp
cleware_temp,sensor=usbtemp,serial=0018CE0 temperature=26.3125
$ cleware_temp --plain
26.3125Linux only. It talks to the sensor through the hidapi libusb backend, and the device-access setup (sudo or udev) is Linux-specific. It should build and run on any modern Linux distribution that has hidapi installed. It has not been tested on Windows or macOS.
Developed and tested on:
| Component | Tested with |
|---|---|
| OS | Debian 13 (trixie), x86_64 |
| Kernel | Linux 7.0.2 |
| hidapi | 0.14.0 (hidapi-libusb) |
| Compilers | gcc 14.2.0 (build host); CI: gcc 13.2.0 + clang 18.0, -Werror |
The Cleware USB-Temp (USB 0d50:0010, HID device type 0x10) exists in several
firmware revisions. clewarecontrol decodes the temperature like this:
value = (buf[2] << 5) + (buf[3] >> 3); // treats byte2 as 8 data bits
if (value & 0x1000) value -= 0x1000; // bit 12 = sign
temp = value * 0.0625;On firmware v5 that is wrong. Byte 2 bit 7 is a status flag, not data, and the
temperature is a 12-bit signed field. When byte 2 is 0x8d, bit 7 is set, so the
old code reads it as a large negative number and prints a constant near -229 °C.
The correct decode masks that bit and uses 12-bit two's-complement (the Cleware
manual documents the 0.0625 °C step):
v = ((buf[2] & 0x7f) << 5) | (buf[3] >> 3); // 12-bit value
if (v & 0x800) v -= 0x1000; // 12-bit signed
tempC = v * 0.0625;Example frame ae 51 8d 2b 00 00: ((0x8d & 0x7f)<<5) | (0x2b>>3) = (13<<5)|5
= 421, so 421 × 0.0625 = 26.3125 °C, which matches a reference thermometer.
| byte | meaning |
|---|---|
| 0 | bit 7 = valid flag, bits 6..0 = time high |
| 1 | time low |
| 2 | bit 7 = status flag, bits 6..0 = temperature high bits |
| 3 | bits 7..3 = temperature low bits (bits 2..0 unused) |
| 4, 5 | 0 |
Read sequence: send the 3-byte HID feature report {0x00, seq, 0x81}, then read
the 6-byte input report. cleware_temp takes the median of several valid frames
and applies a -30 to +90 °C sanity window.
Requires a C compiler and hidapi (the libusb backend).
Debian / Ubuntu:
sudo apt-get install -y gcc make pkg-config libhidapi-dev libhidapi-libusb0
makeThis produces the cleware_temp binary. Install it system-wide with:
sudo make install # -> /usr/local/bin/cleware_tempOr use the convenience installer (also sets up Telegraf sudo access with
--telegraf):
sudo ./install.sh --telegrafThe decode logic (cleware_decode.h) and the serial sanitizer
(cleware_serial.h) have hardware-free unit tests: known
frames checked against expected °C (including the v5 status-bit case that trips up
clewarecontrol), and serial inputs checked for injection-safe output.
make checkCI (GitHub Actions, see .github/workflows/ci.yml)
builds with gcc and clang using -Werror, runs the unit tests, checks the
CLI and the no-device exit code, and verifies make install / uninstall.
cleware_temp [options]
-p, --plain print just the temperature in C (e.g. 26.3125)
(default: InfluxDB line protocol)
-s, --serial HEX only use the device with this serial (e.g. 0018CE0)
-h, --help show help
-V, --version show version
Exit codes: 0 ok, 2 hid_init, 3 no device, 4 open failed, 5 no valid read
Find your sensor's serial:
lsusb -d 0d50:0010 -v 2>/dev/null | grep iSerialcleware_temp talks to the device through libusb, which needs access to the USB
device node (root by default). For a service such as Telegraf, pick one:
- sudo (simplest): install
examples/sudoers.d/telegraf-clewareto/etc/sudoers.d/telegraf-cleware(mode0440) and callsudo /usr/local/bin/cleware_temp. - udev (least privilege): install
examples/udev/99-cleware-usbtemp.rules, reload udev, add the service user to the group, and call the binary directly.
Use examples/telegraf/cleware_temp.conf
(copy into /etc/telegraf/telegraf.d/):
[[inputs.exec]]
commands = ["sudo /usr/local/bin/cleware_temp"]
timeout = "5s"
data_format = "influx"The measurement is cleware_temp, the field is temperature, and the tags are
sensor=usbtemp and serial=.... Telegraf adds the global host tag and the
timestamp. If your influxdb_v2 output uses a namepass allow-list, add
"cleware_temp" to it.
Verify the data arrived (Flux):
from(bucket: "sensors")
|> range(start: -5m)
|> filter(fn: (r) => r._measurement == "cleware_temp")
|> last()
examples/systemd/cleware-temp-influx.sh
shows how to POST a reading straight to the InfluxDB v2 write API from a cron job
or systemd timer.
| Symptom | Cause / fix |
|---|---|
no Cleware USB-Temp (0d50:0010) found |
sensor not plugged in, or you filtered by a serial that doesn't match (lsusb -d 0d50:0010) |
hid_open_path(...) failed (exit 4) |
permissions (see Device access), or a transient libusb/kernel race (the tool already retries a few times) |
no valid reading (exit 5) |
device returned only frames with the valid bit clear; check the cable/USB port |
reads about -229 °C with clewarecontrol |
that's the v5 bug this tool fixes; use cleware_temp |
This tool does one thing on purpose: read temperature from the USB-Temp on
firmware v5. It is an independent reimplementation based on the HID protocol, not
a fork of clewarecontrol, and it doesn't control switches, watchdogs, or LEDs.
The decode has been verified on a firmware-v5 device (type 0x10); other Cleware
temperature variants (USB-Temp2 0x11, Temp5 0x15, USB-Humidity) use different
frame layouts and are out of scope.
See SECURITY.md for the security model, how untrusted USB-device data is handled, and how to report a vulnerability.
This project was built with AI assistance. See ai-disclosure.md for exactly how, and ai-stance.md for the reasoning behind it.
MIT. See LICENSE.