Enable the RS232 communication on a Digital Multimeter
Some Digital Multimeters use an IC which has RS232 ouput but that feature is not enabled in the hardware design. However, it is easy to add some hardware componemts to fully export the RS232 port, mostly with an opto and some resistors.
Last update: 2022-07-30
Table of Content
DMM with log function#
We usually do not see a DMM can send data to computer, but they actually has some models doing that. You can refer to the list from sigrok - an opensource signal analysis software, so you can find some popular names:
- Agilent DMM with RS232
- Brymen with RS232/ USB
- Fluke with RS232
- Metrawatt wth RS232/ USB
- UNI-T with RS232/ USB/ BLE
My DMM is Twintex TM287 (3 ⅚ digit True RMS multimeter, 6000 counts with Analog bar), which uses FS9922-DMM4 IC from Fortune Semiconductor Corp., This IC is also used in the DMM UNI-T UT61D. The DMM UNI-T UT61D comes with RS232 function, with a PC applicatiion to show and log mesurated values; however, Twintex doesn’t support it.
RS232 feature#
Checking the FS9922-DMM4 datasheet, there actually is pin for RS232 ouput named TXD
.
And the instruction to enable RS232 mode by long pressing the REL button.
Finally, it shows a schematic which connects RS232 TXD
to a LED for isolated optical communication method.
The ioslated optical method can be done by using an opto PS2701 as below:
And on the DB9 header, the current flows from pin 2 (RX - Receive Data) to pin 4 (DTR - Data Terminal Ready)
Modify the hardware#
Firsly, wire TXD pin and VDD pin from the IC. VDD wire can be connected to any VDD point.
Then, wire an opto PS2701 with 1K resister between VDD
and TXD
pins.
After that, wire DTR and RX pin from an USB-to-TTL converter:
Here is the USB-to-TTL converter:
Now, you can read RS232 messages in text mode:
Decode data packet#
The RS232 data packet is defined in 14-buyte messages.
The sign and digits are sent as ASCII characters. Other details, such as AC/DC, unit, mode can be decoded from the RS232 data bytes. Check the FS9922-DMM4 datasheet for message details.
You can find a simple python decoder and draw a graph like this:
Decode RS232 messages:
import serial
##########
def parse(data):
try:
# print(data.hex(' '), end=' >> ')
sign = 1 if data[0]==0x2b else -1
digit1 = data[1]-0x30
digit2 = data[2]-0x30
digit3 = data[3]-0x30
digit4 = data[4]-0x30
decimal = 1
match data[6]:
case 0x30:
decimal = 1
case 0x31:
decimal = 1000
case 0x32:
decimal = 100
case 0x34:
decimal = 10
if data[1] == 0x3f:
if sign == 1:
value = float('inf')
else:
value = float('-inf')
else:
value = sign * (digit1*1000 + digit2*100 + digit3*10 + digit4) / decimal
range = 'auto' if data[7] & (1 << 5) else 'man'
dc = 'DC' if data[7] & (1 << 4) else ''
ac = 'AC' if data[7] & (1 << 3) else ''
rel = 'REL' if data[7] & (1 << 2) else ''
hold = 'HOLD' if data[7] & (1 << 1) else ''
bpn = 'BPN' if data[7] & (1 << 0) else ''
u = 'u' if data[9] & (1 << 7) else ''
m = 'm' if data[9] & (1 << 6) else ''
k = 'k' if data[9] & (1 << 5) else ''
M = 'M' if data[9] & (1 << 4) else ''
diode = 'D' if data[9] & (1 << 2) else ''
duty = '%' if data[9] & (1 << 1) else ''
vol = 'V' if data[10] & (1 << 7) else ''
amp = 'A' if data[10] & (1 << 6) else ''
ohm = 'Ω' if data[10] & (1 << 5) else ''
hz = 'Hz' if data[10] & (1 << 3) else ''
far = 'F' if data[10] & (1 << 2) else ''
tC = '°C' if data[10] & (1 << 1) else ''
tF = '°F' if data[10] & (1 << 0) else ''
except:
value = float('inf')
else:
print(f'{range} {dc}{ac} {value:+} {u}{m}{k}{M}{vol}{amp}{ohm}{far}{hz}{duty}{tC}{tF}')
return value
##########
if __name__ == '__main__':
# open and read
with serial.Serial('COM5', 2400) as dmm:
while True:
try:
line = dmm.readline()
print(parse(line))
except KeyboardInterrupt:
break
Show data in a graph using Animation in the MatPlotLib package:
import io
import time
import matplotlib.pyplot as plt
import matplotlib as mpl
import matplotlib.animation
import serial
from fs9922_dmm4 import parse
PLOT_LIMIT = 60
y_min = -1
y_max = 1
fig, ax = plt.subplots(1, 1, figsize=(10,8))
ax.set_title("Digital Multimeter")
ax.set_xlabel("second")
ax.set_ylabel("value")
ax.set_ylim([y_min, y_max])
##########
def static_vars(**kwargs):
def decorate(func):
for k in kwargs:
setattr(func, k, kwargs[k])
return func
return decorate
##########
def get_data(line):
return parse(line)
##########
@static_vars(counter=0)
def animate(i, xs, ys, serial, limit=PLOT_LIMIT):
global y_min
global y_max
# grab the data
try:
# next second
animate.counter += 1
line = serial.readline()
data = get_data(line)
if data < y_min:
y_min = data - 0.5
if data > y_max:
y_max = data + 0.5
# Add x and y to lists
xs.append(animate.counter)
ys.append(data)
# Trim xs, ys
xs = xs[-limit:]
ys = ys[-limit:]
except ValueError:
print(f"W: {time.time()} :: EXCEPTION!")
else:
# Draw x and y lists
ax.clear()
ax.set_ylim([y_min, y_max])
ax.plot(xs, ys)
##########
if __name__ == '__main__':
# open and read
with serial.Serial('COM5', 2400) as dmm:
anim = mpl.animation.FuncAnimation(fig, animate, fargs=([None], [None], dmm))
plt.show()
plt.close()