A cheap PC interface for Brymen BM869s multimeters (hardware+software). Based on Attiny25.
This repo contains a DIY alternative to the commercially available PC interface BU-86x for Brymen BM869s and possibly other multimeters. The hardware is based around a Attiny25 and whatever USB to serial module you have in stock. The software is text mode and Linux only. Everything was made/written from scratch.
The content of this repo is licenced under AGPLv3+ (code) / CC BY-NC-SA 4.0 (schematic and pictures) and comes WITHOUT ANY WARRANTY!
- Thanks to Brymen for publishing a short specification that contains the mapping of the individual data bits to the LCD segments. Figuring this out would have been long and painful. I will not link directly to the pdf as the URL seems to change quite often, just search for "500000count-professional-dual-display-DMMs-protocol.pdf" or similar.
- Thanks to the folks over at the EEVblog forum who figured out the (undocumented) low level protocol stuff.
The interface with the BM869s is optical, as for most (all?) multimeters on the market. This is really important as it ensures insulation between the (potentially letal) voltages going inside the meter (e.g. if you are measuring mains voltage) and your computer (and your body)! The wavelength used for the BM869s is 940nm (=infrared). You will find plenty of suitable LED and phototransistors on your favourite electronics parts website. I had some ITR9606-F "Opto Interrupter" laying around that are working at the needed wavelength, so i destroyed one to get the bare LED and phototransistor.
You need some kind of "bracket" to hold the LED and phototransistor in place. If you don't mind a permanent connection, hot glue is probably the easiest way. I used a piece of wood that i carefully shaped to slide between the two plastic "slots" on the meter. Then i drilled to holes through the wood and mounted the LED and phototransistor with hot glue. It works... (I don't have access to a 3D printer.) If you look at the meter from the back the LED goes on the left side.
It's quite basic: You send a pulse (=LED ON) with a duration of 10ms (or more), then wait for the LED inside the meter to turn on and then send 160 light pulses while receiving data (160/8=20 bytes) from the meter. What you get is an encoded representation of the individual segments of the LCD, so some code is needed to "translate" this into human-readable text. I did this on the PC as there is more processing power available and - more important - it is easier to debug.
Everything should be in the schematic. Adjust resistance values depending on your LED and phototransistor. Make sure you programm the AVR before soldering (fuses and code)!
The firmware was written in assembly (yikes!) due to timing problems. Indeed the Attiny25 does not have a UART and making a (kinda) full-duplex one work in C is not easy, especially with a single 8 Bit timer available (the other timer was needed for data acquisition from the DMM) and ~8MHz system clock.
Build the firmware using avr-gcc -mmcu=attiny25 -o avr.elf main.S
and flash with your favourite tool. Don't forget setting the fuses!
The Attiny25 has 5 GPIO (6 if you can disable RESET and have a way to still program the chip). We need 2 of them for UART, 2 of them for the DMM-interface and the last one was used for a LED to provide at least some feedback to user. This means there is no way to connect a crystal to the AVR, but the internal 8 MHz oscillator is unsuitable for "fast" UART (115200).
The solution was to use the OSCCAL
register to slightly lower the 8MHz to about 7,3728MHz which would be perfect for "fast" UART. To avoid having to do this manually an autocalibration procedure was added to the firmware using the UART as a reference. Indeed if you continously send 0x55 you basically have a perfect square wave with fixed, known frequency. Once the correct OSCCAL
value (which will vary between several Attiny25 and is also a function of voltage, temperature, ...) is known it is stored inside EEPROM.
Please note that this calibration procedure is highly experimental and may fail for some parts depending on voltage, temperature, process variations during manufacturing of the AVR, ...
The datasheet of Attiny25 states on page 32:
To ensure stable operation of the MCU the calibration value should be changed in small. A variation in frequency of more than 2% from one cycle to the next can lead to unpredicatble behavior. Changes in OSCCAL should not exceed 0x20 for each calibration.
The current code is violating this as - assuming the initial calibration was done once - on startup the OSCCAL
value is read from EEPROM and directly written to OSCCAL
. To be in spec we would need to adjust OSCCAL
slowly (inside a loop or similar) until the correct value is reached. However for me writing OSCCAL
directly works fine too. YMMV.
The calibration procedure needs to be done once at the very first time the dongle is connected to a PC. The status LED (see below) will blink 3 times and then stop. At this point the actual DMM interface is not yet functional! You will need to provide a "calibration signal" via UART for like one or two seconds. To do this you can use these commands from Linux, assuming your USB to serial module is /dev/ttyUSB0
(adjust as needed). Leave it running for a few seconds and then stop with Ctrl+C.
stty -F /dev/ttyUSB0 raw 115200
cat /dev/zero | tr "\000" "\125" > /dev/ttyUSB0
Due to pin constraints there is only a single LED to provide some feedback. Here is some help to interpret the blinky stuff:
- 3 slow blinks and then nothing: Waiting for calibration signal via UART, see above.
- (during calibration) continous fast blink: Calibration failed. :(
- continous slow blink: Waiting for data from DMM, check if your "dongle" is correctly mounted and the DMM switched on.
- fast(-ish) flashes: Receiving data from DMM and transmitting via UART. This is the mode you want.
The software is written in C and for Linux only. With WSL you may somehow be able to make it run on Windows, but i can't and won't provide any support for this.
The main
-function parses some options, does some init-stuff for the serial port and soon fork()
s to split itself into 2 parts:
- An
uart_worker()
that manages the serial interface. - An
interface()
that crunches the received data.
Both parts are communicating using "messages" sent through unnamed pipes. The actual decoder that makes senses of the raw data from the AVR lives insides - you guessed it - decoder.c/h
. Please note that the decoder is somewhat messy, incomplete und mostly untested. Feedback welcome.
gcc -Wall -Wextra -o 869log main.c uart_worker.c intercom.c fifo.c interface.c decoder.c
No dependencies (except libc
of course).
usage: 869log [--port $serial_port] [--once|--twice] [--relative-time] [--output-file $file]
--port $serial_port tells the software to use $serial_port instead of /dev/ttyUSB0 by default
--once instructs the AVR to request 1 reading per second
--twice is the same but 2 readings per second
By default the tool shows as many readings as the meter can return.
--relative-time does not show current date/time but a clock starting at 00:00:00 once data is received
--output-file $file writes received and decoded data to $file in addition to showing it on the screen
I made a "dead bug" style construction on the back of an USB to serial adapter. Once the dongle was confirmed working i used hot glue to protect the electronics. As i said, i don't have access to a 3D printer to make a nice enclosure. The cable was salvaged from another old, dead project (recycle, reuse, ...).
- Further processing of the received data can and should be done with standard Linux tools. This software will never get a plotting function, a GUI and similar stuff.
- Due to multitasking, several buffers at different layers of Linux and other stuff the displayed time for each reading is not really accurate; however the readings once/twice a second are timed from inside the AVR, so the delay should always be the same to some extend.