Tenma 72-7735 DMM: Serial interface with Python

The Tenma 72-7735 is a lower end DMM that comes with quite a few handy features including an opto-isolated serial output. This serial communication is over a cable with a male db-9 rs-232 serial connector on one end and a connector housing a photo-diode that fits into the multimeter on the other end. Here is a picture of the cable:

Multimeter serial cable
Multimeter serial cable
rs232 cable plugged into multimeter
rs232 cable plugged into multimeter

Software is provided by Tenma that offers a gui with that can do things like datalogging, generate graphs and show real time data. The problem is that this program only works on windows and my computer doesn’t have a serial port. Also it would be nice if it was possible to read this serial data using some kind of microcontroller based embedded system and do something cool with it.

First step was to take apart the photodiode housing:

Circuitry inside serial cable
Circuitry inside serial cable

The original idea was to cut off the db-9 connector on the other end of the cable and hook up the wires to the correct voltages to get serial data out. This turned out to be problematic due to that fact that rs232 voltages are bi-polar. See the image of the reverse engineered circuit below:

Reverse engineered circuit diagram
Reverse engineered circuit diagram

These wire were attached to the following rs232 pins:
DCD – yellow
RxD – orange
Txd – white
DTR – brown
RTS – green

This particular cable was missing several components from the internal PCB. It only seemed to have the parts needed to receive data, not send it. The TxD (white) wire is soldered to the PCB but doesn’t seem to be connected to anything. It turns out that it is possible to power very low current (roughly 10mA max) devices over rs-232, some older computer mice would have been powered this way. This is usually done using the RTS and DTR lines to generate +5V and then using the TxD line as the negative voltage. Not wanting to go messing about with negative voltages I tried to get the circuit to work with just ttl voltage levels but it didn’t work out. In the end I just de-soldered the photo-diode from the PCB and made a much simpler circuit that easily works with 5v logic levels. The quick and easy circuit looks like this:

Photodiode circuit diagram
Photodiode circuit diagram

This serial data is sent to a computer via a pl2303 based usb to serial converter.

Here is a picture of the new and improved circuit stuffed inside the housing:

new circuit inside housing
new circuit inside housing

Picture of the output from this circuit on an oscilloscope below. It looks a bit non-square but it didn’t seem to affect the data being read correctly:

Serial data output on a scope
Serial data output on a scope

All that is left now is to read the serial data and do cool things with it. Only problem is the serial protocol is fairly poorly documented. A protocol outline can be found here. Despite the documentation being pretty confusing, the protocol is extremely simple.The serial data sent out is the LCD table which tells us which parts of the LCD are on and which are not. This makes it slightly more difficult to decode the data on the other end than if it just sent values of voltage or current but it’s not that big of a deal. The data is sent out in 14 byte packets ( this means 14 bytes, short pause then another 14 bytes etc). The upper nibble (upper 4 bits) of each byte is the bytes location within the packet. EG the first byte in a packet will be 0001XXXX, the second one 0010XXXX etc. The XXXX’s or the lower nibble of each byte is what we are interested in. There is a table provided in the datasheet that outlines what these lower 4 bits mean.

Serial protocol for multimeter
Serial protocol for multimeter

This table is so unnecessarily confusing. What does COM 1-4 Mean? I still have no idea. The way to look at it is just ignore columns 1-4, just pay attention to the seg colummns (seg1 – seg14). There are 14 seg columns and 14 bytes in a packet. This is because each bytes lower nibble corresponds to the lcd sections in one of those columns. If for example the first byte in a packet was 00010111, the upper nibble is 0001 which means it refers to column seg1. The lower nibble is 0111, each bit of this nibble tells us if that particular symbol is currently showing on the LCD. 1 means on and 0 means off. So for example in this case 0111 means the following

0 – AC OFF
1 – Auto ON
1 – DC ON
1 – RS232 ON

This process repeats for the full 14 byte packet and by knowing which parts of the LCD are on, you can then work out what numbers are being displayed on the screen and so on. See below for a python script I wrote to do this (click to expand):

import serial
import time
import binascii

ser = serial.Serial(
	port='/dev/ttyUSB0',
	baudrate=2400,
	timeout = 0.1,
	stopbits=serial.STOPBITS_ONE,
	bytesize=serial.EIGHTBITS
)

ser.close()
ser.open()

ser.flush()

hex_vals = []
temp_str = ''
bin_vals = []
dmm_data = ''
lower_nibbles = []
for i in range (0, 14):       # make lists to store hex and binary values
	hex_vals.append('0')
	bin_vals.append(0)
	lower_nibbles.append(0)
	
	
def get_lower_nibbles(bin_vals):            # only keep the lower part of nibble
	for i in range(0, len(bin_vals)):
		temp_str = str(bin_vals[i])
		lower_nibbles[i] = str(temp_str[4:8])
	
	return(lower_nibbles)

a=[]
b=[]
c=[]
d=[]
digit = 0

for h in range(8):  # make a list for each digit to record which parts of seven segment are on or off
	a.append('0')
	b.append('0')
	c.append('0')
	d.append('0')
	
def lcd_get_num(lcd_array):                 ##  each digit corresponds to certain parts of seven segment display being on
	digit = 0
	if lcd_array == '1010000' : digit = 1
	if lcd_array =='1101101' : digit = 2
	if lcd_array == '1111001' : digit = 3
	if lcd_array == '0110011' : digit = 4
	if lcd_array == '1011011' : digit = 5
	if lcd_array == '1011111' : digit = 6
	if lcd_array == '1110000' : digit = 7
	if lcd_array == '1111111' : digit = 8
	if lcd_array == '1111011' : digit = 9
	if lcd_array == '1111110' : digit = 0
	
	return str(digit)
	
	
def decode_data(lower_nibbles):             #this function works out what numbers are being displated based on 
	for i in range(1, len(lower_nibbles)):  # which parts of each seven segment digit are turned on
		temp_reg = lower_nibbles[i]
	#	print temp_reg
	#	print len(a)
		if (i==1):         ## first digit
			#print temp_reg[3]
			a[1] = temp_reg[3]
			a[6] = temp_reg[2]
			a[5] = temp_reg[1]
		if (i==2):
			a[2] = temp_reg[3]
			a[7] = temp_reg[2]
			a[3] = temp_reg[1]
			a[4] = temp_reg[0]
			
		if (i==3):
			b[1] = temp_reg[3]
			b[6] = temp_reg[2]
			b[5] = temp_reg[1]
			
		if (i==4):
			b[2] = temp_reg[3]
			b[7] = temp_reg[2]
			b[3] = temp_reg[1]
			b[4] = temp_reg[0]
			
		if (i==5):
			c[1] = temp_reg[3]
			c[6] = temp_reg[2]
			c[5] = temp_reg[1]
		
		if (i==6):
			c[2] = temp_reg[3]
			c[7] = temp_reg[2]
			c[3] = temp_reg[1]
			c[4] = temp_reg[0]
		
		if (i==7):
			d[1] = temp_reg[3]
			d[6] = temp_reg[2]
			d[5] = temp_reg[1]
		
		if (i==8):
			d[2] = temp_reg[3]
			d[7] = temp_reg[2]
			d[3] = temp_reg[1]
			d[4] = temp_reg[0]
	a.pop(0)
	b.pop(0)
	c.pop(0)
	d.pop(0)
	achar = ''.join(a)
	bchar = ''.join(b)
	cchar = ''.join(c)
	dchar = ''.join(d)	
	a.append('0')	
	b.append('0')
	c.append('0')              # this terrible piece of code would probably make                          
	d.append('0')             # Guido van Rossum cry  
	
	d1 = lcd_get_num(achar)
	d2 = lcd_get_num(bchar)
	d3 = lcd_get_num(cchar)
	d4 = lcd_get_num(dchar)
	print d1+d2+d3+d4              #print numbers displayed on LCD screen
	
	return(achar, bchar, cchar, dchar)
		
			
		
dmm_data = ""

while True:
	dmm_data = ""
	ser.flush()
	while dmm_data == "":                                #wait for a packet
		dmm_data = str(binascii.hexlify(ser.read(14)))


	g = 0                                               # concert hex values into binary
	for i in range(0, len(dmm_data), 2):
		bin_vals[g] = str( bin(int(dmm_data[i:i+2], 16))[2:].zfill(8)) 
		g=g+1

	#print bin_vals

	get_lower_nibbles(bin_vals)                       # only interested in lower nibble of each byte
	#print lower_nibbles

	decode_data(lower_nibbles)                        # from this data work out what numbers are displayed on screen
	time.sleep(0.5)                               

Table outlining how to work out what digit is being displayed
Table outlining how to work out what digit is being displayed

The output from the Python script at the moment is just the numbers currently being displayed on the multimeter. This is a work in progress, data logging with a microcontroller is something to try out in the future.

Python script output
Python script output

14 thoughts on “Tenma 72-7735 DMM: Serial interface with Python

  1. It is much easier to find the Protek 608, a 4.5 digit meter with the same interface ($129 US). It would be the same assemble the segments problem except that it has a 16 segment “bar graph” which is display of 16 bit data, with no segment foolishment to decode. I took the path of least resistance and used MAX232 chips to convert data level and supply the optoisolator with needed supply voltages. Only 2ma of 5V used. The chips were $1 on Ebay.

  2. Take a look at this board (https://www.pjrc.com/teensy/). This is a Arduino-like board with some really nice features. Because of the size constraints, I’d look at the 2.0. (I like the 3.1 better, but it looks like you need as small as board as possible.) If you have some C skills and the board fits in your enclosure, think about wiring the photodiode right to the board. You could take care of the signal conversion on the Teensy. After you convert it to what you want, you can deliver the data out through the USB port (it emulates a standard USB/serial device) with the right code (documented). Oh… and powered through the USB cable.

    Good luck with the project. Great start.

  3. The COM1-4 is just an indicator of which packet you are decoding. I wouldn’t throw that away. I use it as a check to make certain that you are working on the packet that you think you are.

    1. The dump is literally what the multimeter is sending to the LCD. The LCD itself is multiplexed, arranged as a 4 by 14 matrix. Only one of the first four bits is active at any time, which selects which set of segments are lit up by the other bits. So you definitely need to pay attention to which of the COM bits is set, not just assume that the meter will always send them through in sequential order.

      1. Thanks for the info. Not sure its absolutely necessary to bother looking at the com lines. Using a serial timeout of 100ms and waiting for 14 bytes seems to work reliably

    1. Extremely useful info, thanks! That wiki page does a much better job of explaining how the protocol works compared to the datasheet that comes with the re branded tenma meter.

Leave a Reply

%d bloggers like this: