UT171A data reader on Linux

Multimeter with CP2110 based USB-UART bridge


I bought this DMM (digital multimeter)[1],[2] for my experiments. From the descriptions, I knew it was possible to attach it to a (Windows) PC via USB. As there were many sites [3], [4], [5] describing alternative programs to read data from Unitrend multimeters I was positive that there was something around to connect it to my Linux box also.

Of course, you could not read this article, if that had proven true. But step by step, which may help you to connect your DMM to Linux, too.


DMMs used to be connected to the PC via a serial port (COM) or a USB to serial adapter. The latter ones are needed, when PCs do not have a COM port. When you plug in such a device into a USB port, "artificial" serial devices are created in the /dev folder of your machine. They show up as /dev/ACM* , /ddev/ttyS* ,or /dev/ttyusb*. These devices can be handled like a standard serial device, allowing to read and write to it like a terminal, with the UART protocol.

Not here. When you connect the USB plug of the UT171A you will find a new '/dev/hidraw*' in your device folder, in my case '/dev/hidraw3'.

Running dmesg on an OpenSuse Linux shows:

This Python project pycp2110 [6] got me onto the right track. With this library I was able to read descriptions from the device and to enable/disable UART. But still I couldn't read data.



With a colleague of mine we sniffed on the USB communication between the UT171A and the companion Windows software. I figured out, that to make the DMM to send data some command must be issued first, and that data sent from the device starts with 2 2-Byte-blocks 0x01ab,0x01cd.

But there was still a lot confusion about how to write configuration vs. command data and how to interpret the values coming from the device. It took some reading [7], [8] until I understood that all data exchange between the HID raw device and a program is organized in "reports". There are reserved Bytes which appear as the first Byte in a message and that configuration data goes into another channel than command data.
I reduced the library class from pycp2110 to the minimum of what was needed to create a device for interaction, enable UART, configure it with the parameters from our USB sniffing protocol, send the start command and read single reports from the UT171A (after turning the USB interface on, shown as a little "S" in the DMMs display).

Unitrend data protocol

Removing the initial 0x01 Byte from device response, collecting the data Bytes and splitting up into chunks starting with 0xabcd I got these patterns, e.g. for reading a resistance of 5100 Ohm:

ab cd 11 00 02 08 01 0a 03 13 3a a3 40 30 10 c9 39 a3 40 7e 03
ab cd 11 00 02 08 01 0a 03 d3 39 a3 40 30 10 cc 37 a3 40 3e 04 ...

For 230 VAC, 50Hz the pattern is longer:

ab cd 17 00 02 09 03 03 03 55 4e 68 43 20 01 58 b3 4c 3d 30 13 65 44 68 43 c5 04
ab cd 17 00 02 09 03 03 03 ea 4f 68 43 20 01 25 b5 4c 3d 30 13 36 65 68 43 1c 05 ...

None of the documentation I found about serial protocols, helped me with this. With lots of experiments I figured out the following parts of the protocol, which I use in the UT171A.format method to translate data from the device. This tool helped me to recognize the Little Endian float values [9].

meaning array slice, length examples (hex)
marker [0:2], 2 ABCD
data length [2], 1 11,17
version? [3:5], 2 0002
display elements, bitwise [5], 1 08, 88, 09, 0C, 0D
AC/DC? [6], 1 01, 03
meas. type [7], 1 0A, 03, 02, 17, 14 ...
meas. range [8], 1 01,.., 06
main value: 4 Byte Little Endian float [9:13], 4 13 3A A3 40, 55 4E 68 43
factor/range? [13:15], 2 3010
aux. value: 4 Byte LE float, [15:19], 4 C9 39 A3 40, 58 B3 4C 3D
checksum: 2 Byte LE integer = sum([2:17+2]) [19:21], 2 7E 03
factor/range? [19:21], 2 3013
3.rd value: 4 Byte LE float, [21:25], 4 65 44 68 43
checksum: 2 Byte LE integer = sum([2:23+2]) [25:27], 2 c5 04
The table shows patterns for 21 and 27 Bytes length, for measurements in DC and AC circuits.

The auxiliary (2nd) value in DC measurements is in the range of the main value, but was never the same. In AC measurements it becomes the frequency in kHz, while the third value now is the "copy" of the first value. I haven't figured out, what that means. I just observed that during changes in type or range of measurements the first value becomes 0 and the aux. value keeps some value of the original range until the range is adjusted, which takes about 2 cycles (with usually 4-5 cycles per second).

The table is not complete, please check the program for how some of the values were interpreted. Descriptions with ? are still unclear.

At the moment you can only read values from the device with the program, not control it, e.g. by setting measurement ranges or types. There is no GUI or real-time plot either.

Check out the code from github [10], you may have to install additional Python and System libraries, see the chapter Raspberry below.
The DMM USB must be plugged into the PC and the USB interface turned on. For other CP2110 driven devices you may be able to derive from UTHID class to apply their protocol and formatting.

Run it from command line with
python3 -m ut-cp2110.UT171A
For testing:
python3 -m ut-cp2110.testing.UTTest

Good luck and have fun!


Reading values works on Raspberries also. I upgraded my 2B to the latest version of Rasberry OS v 1.4 (2020-08-16) [11].

Then I installed these libs:

Add a udev-rule to provide the pi user with r/w access to the hidraw device. You find the file on gitub [12] and the original example here [13].

Known issues (on Raspberry):

  1. the test does not work
  2. read failures with checksum errors happen more frequently than on the big Linux box
  3. try different USBs on the Raspi, some seem to create a more stable hidraw device for connect and read, which also affects errors.


If you make some additions/improvements to the program, please let me know.


  1. UNI-T: UT171A Digital Multimeter
  2. detailed UNI-T UT171A review with many pictures
  3. Universal DMM logging program for Windows + logs (German)
  4. Python module to read UT61E
  5. Support for a number of different DMMs
  6. pycp2110 Python library for working with the CP2110
  7. SiLabs CP21110 interface spec (pdf)
  8. Accessing HID devices on raspberry in C
  9. IEEE 32Bit float converter (German)
  11. Download link to Raspberry OS
  12. udev rules for users on pi
  13. original example of a udev rules file for trezor