Do you have a cheap Chinese multimeter using the FS9721 chipset and want to add an easy-to-use USB interface to it? Or, do you have an arduino project in mind where you might want to collect measurements from your multimeter? This project can help you do both of those things.
Any multimeter using the FS9721 chipset is compatible, but you may have to adapt the hardware design herein if your multimeter does not have a 3.5mm stereo jack labeled "RS232".
Known-compatible multimeters include:
- RECOMMENDED TekPower TP4000ZC
- Also available as the Digitek DT-4000ZC.
- Includes a 3.5mm RS232 port.
- Mastech V&A 18B
- Does have an external RS232 port, but it is not compatible with the hardware files you'll find herein. You will have to improvise.
- UNI-T UT60E
- Does have an external RS232 port, but it is not compatible with the hardware files you'll find herein. You will have to improvise.
- Baud Rate: 2400
When connected to your PC, the device will appear as an additional serial port (may require installation of a driver for the CH340G USB UART chip).
By default, the raw output from the Multimeter will be displayed as new multimeter data is received in a way identical to that of what the built-in serial port would output were you to connect it directly to your computer, but you can select a variety of output formats using the below commands.
Commands are sent in the format COMMAND=VALUE
followed by a single
newline character.
- Command
output
:- Value
raw
(default): Display raw bytes received from multimeter. - Value
value
: Display actual value scaled to base unit. - Value
displayed
: Display displayed value. Note that this is not scaled to the base unit. Use theunit
command below to determine magnitude. - Value
none
: Disable output.
- Value
- Command
units
:- Value
0
(default): Do not print unit. - Value
1
: Print unit. If the value is not scaled to the base unit, this may include an SI magnitude prefix ('u', 'n, 'm', 'k', 'M').
- Value
Note: Command output is not displayed until you press
For example; to display units in the format they are displayed on the screen, you could send the following two commands:
output=displayed
units=1
and you would, assuming you were measuring a voltage source, receive responses like:
1.244 mV
1.239 mV
But, if you wanted to output values scaled to their base unit so programs consuming these values wouldn't need to worry about the SI magnitude, you could instead send the following commands:
output=value
units=1
and you'd receive the following values:
0.001244 V
0.001239 V
You could even turn off unit display if you were not concerned about
identifying what mode the multimeter was in while the returned measurement
was taking place (note, though, that units=0
is the default state, so
you need not send that command if you have not already enabled unit display):
output=value
units=0
and then you'd receive very easy-to-parse values:
0.001244
0.001239
Every SPI transaction is a single 8-bit command ID, followed by a 32-bit response.
The following commands are implemented:
0x01
: Return multimeter measurement as IEEE 754 floating point value; this value is scaled to the appropriate base unit for the mode in which the multimeter is currently operating..0x02
: Return multimeter display as IEEE 754 floating point value. Note that this value is not scaled to handle displayed SI magnitudes!0x10
: Return current display flags as bits in this order:- 31:
- 30: Sign
- 29: Low Battery
- 28: Hold
- 27: Rel
- 26: Beep
- 25: Sign (?)
- 24: RS232
- 23: AUTO
- 22:
- 21:
- 20: M (mega)
- 19: k (kilo)
- 18: m (milli)
- 17: n (nano)
- 16: µ (micro)
- 15: C2C1 00 (?)
- 14: C2C1 01 (?)
- 13: C2C1 10 (?)
- 12: C2C1 11 (?)
- 11:
- 10: Diode
- 09: DC
- 08: AC
- 07:
- 06:
- 05: %
- 04: Hz
- 03: V
- 02: A
- 01: F
- 00: Ω
The device itself operates as an SPI slave, and it's rather difficult to keep up with fast SPI speeds with just a 12Mhz clock, so if you have unreliable results, try slowing down your SPI bus speed -- it's not like you're going to ever be able to get more than a few measurements/second anyway given that the multimeter itself only measures 2-3x second.
#include <SPI.h>
#define MM_PIN 9
void setup() {
digitalWrite(MM_PIN, HIGH);
pinMode(MM_PIN, OUTPUT);
pinMode(MISO, INPUT);
pinMode(MOSI, OUTPUT);
pinMode(SCK, OUTPUT);
SPI.begin();
SPI.beginTransaction(SPISettings(1000, MSBFIRST, SPI_MODE0));
Serial.begin(9600);
}
void loop() {
union {
float measurement;
unsigned char bytes[4];
} floatMeasurement;
digitalWrite(MM_PIN, LOW);
// Let's get the current measurement
SPI.transfer(0x01);
for(uint8_t i = 0; i < 4; i++) {
floatMeasurement.bytes[i] = SPI.transfer(i);
}
Serial.println(floatMeasurement.measurement, 7);
digitalWrite(MM_PIN, HIGH);
delay(500);
// Now let's print the current flags
digitalWrite(MM_PIN, LOW);
SPI.transfer(0x10);
for(uint8_t i = 0; i < 4; i++) {
Serial.println(SPI.transfer(i), BIN);
}
digitalWrite(MM_PIN, HIGH);
delay(3000);
}
Check out the hardware
directory to find Kicad schematics and layout. The
layout you'll find was designed for homemade pcb fabrication (note how
big the vias are and the presence of "transit" vias for working around the
lack of through-hole plating), but it could be sent to your favorite PCB
boardhouse for manufacture if desired.
The following major components are used (see the schematic for details):
- ATMEGA328PB Microcontroller in the QFP package. A normal ATMEGA328P would work fine, too, but you will have to update a handful of SPI-related register and interrupt names to remove the extra zero (the -PB version has two SPI peripherals). Although the ATMEGA328PB is a little more capable than the ATMEGA328P these days, they are just a little cheaper; both are around 2.5 USD.
- CH340G USB-to-UART IC: You can buy these from Aliexpress or wherever for less than 0.50 USD.
- 12Mhz Crystal (SMD HC-49)
- A MicroUSB connector.
- A standard through-hole 3.5mm stereo connector.
Note that the design relies upon the CH340G chip sharing the
microcontroller's clock; you will want to set the CKOUT=0
fuse
(generally titled "Clock output on PORTB0").
There are lots of things about this project that I'd change if I were to approach it again; those include:
- Making the primary serial interface I2C instead of SPI.
- I2C provides a lot more flexibility for letting a device take extra time to perform slow operations via "clock stretching". As this is currently designed, SPI is used, and the client device must be able to keep up with the clock speed set by the master or data will be lost.
- With I2C, you can save a wire or two!
- The downside is that we might have to expose a couple extra pins since the current header is used for both programming and the serial interface. If I2C is added, either two additional header pins will need to be added, or maybe a multiplexer chip allowing two header pins to be shared individually between two different microcontroller pins.
- Orient the microcontroller at a 45˚ angle. This should make routing a little less chaotic.
- Add reverse current protection! Not having such protection was a little bit of an embarassing oversight.
- Use either a MicroUSB breakout board or a MiniUSB connector instead of the existing MicroUSB connector. Soldering that tiny connector onto a homemade PCB is very difficult -- the pin pitch of those connectors is 0.65mm; just a smidge below what my current process is able to resolve (~0.75mm) without needing to use a sharp knife to separate traces.
- Perhaps place devices on both sides of the board to save space? It's not clear to me whether that would be worth it, but it could make the board a lot smaller.