From: Janne H. <jah...@gm...> - 2014-07-30 07:29:17
|
This patch implements support for the BC-20X serial cable for the Brymen BM25x series of multimeters. I have only tested this using the BUA-2303 USB-to-Serial adapter that is sold with the cable and I don't currently have means to produce all the supported measurement ranges from microamperes to hundreds of volts, so all of them have not been tested. However with the limited ranges of measurements I could test, the driver seems to work correctly and support most (if not all) of the available modes. Janne Huttunen (1): Implement Brymen BM25x series as a serial DMM. Makefile.am | 3 +- src/dmm/bm25x.c | 210 +++++++++++++++++++++++++++++++++++++ src/drivers.c | 2 + src/hardware/serial-dmm/api.c | 9 ++ src/hardware/serial-dmm/protocol.c | 1 + src/hardware/serial-dmm/protocol.h | 2 + src/libsigrok-internal.h | 11 ++ 7 files changed, 237 insertions(+), 1 deletion(-) create mode 100644 src/dmm/bm25x.c -- 2.0.1 |
From: Janne H. <jah...@gm...> - 2014-07-30 07:29:17
|
The Brymen BM25x series suports the BC-20X that is an opto-isolated serial cable. The link seems to be unidirectional i.e. when activated the DMM periodically sends updates to the host while the host cannot control the DMM in any way. The protocol is documented in "6000-count-digital-multimeters-r1.pdf" that is available from the manufacturer. Every 15 byte packet consists of a bitmap where the bits correspond to segments or symbols on the LCD display i.e. the DMM essentially sends the contents of its screen to the host in every update. This driver then decodes the measured quantity, unit and its value from the bitmap. --- Makefile.am | 3 +- src/dmm/bm25x.c | 210 +++++++++++++++++++++++++++++++++++++ src/drivers.c | 2 + src/hardware/serial-dmm/api.c | 9 ++ src/hardware/serial-dmm/protocol.c | 1 + src/hardware/serial-dmm/protocol.h | 2 + src/libsigrok-internal.h | 11 ++ 7 files changed, 237 insertions(+), 1 deletion(-) create mode 100644 src/dmm/bm25x.c diff --git a/Makefile.am b/Makefile.am index e81ead0..7e9781b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -98,7 +98,8 @@ libsigrok_la_SOURCES += \ src/dmm/fs9922.c \ src/dmm/m2110.c \ src/dmm/metex14.c \ - src/dmm/rs9lcd.c + src/dmm/rs9lcd.c \ + src/dmm/bm25x.c # Hardware drivers if HW_AGILENT_DMM diff --git a/src/dmm/bm25x.c b/src/dmm/bm25x.c new file mode 100644 index 0000000..cfc4ee0 --- /dev/null +++ b/src/dmm/bm25x.c @@ -0,0 +1,210 @@ +/* + * This file is part of the libsigrok project. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/** + * @file + * + * Brymen BM25x serial protocol parser. + */ + +#include <math.h> +#include "libsigrok.h" +#include "libsigrok-internal.h" + +#define LOG_PREFIX "brymen-bm25x" + +#define MAX_DIGITS 4 + +SR_PRIV gboolean sr_brymen_bm25x_packet_valid(const uint8_t *buf) +{ + int i; + + if (buf[0] != 2) + return FALSE; + + for (i = 1; i < BRYMEN_BM25X_PACKET_SIZE; i++) + if ((buf[i] >> 4) != i) + return FALSE; + + return TRUE; +} + +static int decode_digit(const int num, const uint8_t *buf) +{ + int val; + + val = (buf[3 + 2 * num] & 0xe) | ((buf[4 + 2 * num] << 4) & 0xf0); + + switch (val) { + case 0xbe: return 0; + case 0xa0: return 1; + case 0xda: return 2; + case 0xf8: return 3; + case 0xe4: return 4; + case 0x7c: return 5; + case 0x7e: return 6; + case 0xa8: return 7; + case 0xfe: return 8; + case 0xfc: return 9; + case 0x00: return ' '; + case 0x40: return '-'; + case 0x16: return 'L'; + case 0x1e: return 'C'; + case 0x4e: return 'F'; + case 0x5e: return 'E'; + case 0x62: return 'n'; + case 0x42: return 'r'; + default: + sr_dbg("unknown digit: %02x\n", val); + return -1; + } +} + +static int decode_point(const uint8_t *buf) +{ + int i, p = 0; + + for (i = 1; i < MAX_DIGITS; i++) { + if ((buf[11 - 2 * i] & 1) == 0) + continue; + if (p != 0) { + sr_dbg("error: multiple decimal points found!"); + return -1; + } + p = i; + } + + return p; +} + +static float scale_value(float val, int point, int digits) +{ + int pos = point ? point + digits - MAX_DIGITS : 0; + + switch (pos) { + case 0: return val; + case 1: return val * 1e-1; + case 2: return val * 1e-2; + case 3: return val * 1e-3; + } + + sr_dbg("invalid decimal point %d (%d digits)", point, digits); + + return NAN; +} + +static float decode_prefix(const uint8_t *buf) +{ + if (buf[11] & 2) return 1e+6; + if (buf[11] & 1) return 1e+3; + if (buf[13] & 1) return 1e-3; + if (buf[13] & 2) return 1e-6; + if (buf[12] & 1) return 1e-9; + + return 1.0f; +} + +static float decode_value(const uint8_t *buf) +{ + float val = 0.0f; + int i, digit; + + for (i = 0; i < MAX_DIGITS; i++) { + digit = decode_digit(i, buf); + if (i == 3 && (digit == 'C' || digit == 'F')) + break; + if (digit < 0 || digit > 9) + goto special; + val = 10.0 * val + digit; + } + + return scale_value(val, decode_point(buf), i); + +special: + if (decode_digit(1, buf) == 0 && decode_digit(2, buf) == 'L') + return INFINITY; + + return NAN; +} + +SR_PRIV int sr_brymen_bm25x_parse(const uint8_t *buf, float *floatval, + struct sr_datafeed_analog *analog, void *info) +{ + float val; + + (void)info; + + analog->mq = SR_MQ_GAIN; + analog->unit = SR_UNIT_UNITLESS; + analog->mqflags = 0; + + if (buf[1] & 8) + analog->mqflags |= SR_MQFLAG_AUTORANGE; + if (buf[1] & 4) + analog->mqflags |= SR_MQFLAG_DC; + if (buf[1] & 2) + analog->mqflags |= SR_MQFLAG_AC; + if (buf[1] & 1) + analog->mqflags |= SR_MQFLAG_RELATIVE; + if (buf[11] & 8) + analog->mqflags |= SR_MQFLAG_HOLD; + if (buf[13] & 8) + analog->mqflags |= SR_MQFLAG_MAX; + if (buf[14] & 8) + analog->mqflags |= SR_MQFLAG_MIN; + + if (buf[14] & 4) { + analog->mq = SR_MQ_VOLTAGE; + analog->unit = SR_UNIT_VOLT; + if ((analog->mqflags & (SR_MQFLAG_DC | SR_MQFLAG_AC)) == 0) + analog->mqflags |= SR_MQFLAG_DIODE; + } + if (buf[14] & 2) { + analog->mq = SR_MQ_CURRENT; + analog->unit = SR_UNIT_AMPERE; + } + if (buf[12] & 4) { + analog->mq = SR_MQ_RESISTANCE; + analog->unit = SR_UNIT_OHM; + } + if (buf[13] & 4) { + analog->mq = SR_MQ_CAPACITANCE; + analog->unit = SR_UNIT_FARAD; + } + if (buf[12] & 2) { + analog->mq = SR_MQ_FREQUENCY; + analog->unit = SR_UNIT_HERTZ; + } + + if (decode_digit(3, buf) == 'C') { + analog->mq = SR_MQ_TEMPERATURE; + analog->unit = SR_UNIT_CELSIUS; + } + if (decode_digit(3, buf) == 'F') { + analog->mq = SR_MQ_TEMPERATURE; + analog->unit = SR_UNIT_FAHRENHEIT; + } + + val = decode_value(buf) * decode_prefix(buf); + + if (buf[3] & 1) + val = -val; + + *floatval = val; + + return SR_OK; +} diff --git a/src/drivers.c b/src/drivers.c index f93214d..8aa9acf 100644 --- a/src/drivers.c +++ b/src/drivers.c @@ -149,6 +149,7 @@ extern SR_PRIV struct sr_dev_driver uni_t_ut61e_ser_driver_info; extern SR_PRIV struct sr_dev_driver iso_tech_idm103n_driver_info; extern SR_PRIV struct sr_dev_driver tenma_72_7745_ser_driver_info; extern SR_PRIV struct sr_dev_driver tenma_72_7750_ser_driver_info; +extern SR_PRIV struct sr_dev_driver brymen_bm25x_ser_driver_info; #endif #ifdef HAVE_HW_SYSCLK_LWLA extern SR_PRIV struct sr_dev_driver sysclk_lwla_driver_info; @@ -316,6 +317,7 @@ SR_PRIV struct sr_dev_driver *drivers_list[] = { &iso_tech_idm103n_driver_info, &tenma_72_7745_ser_driver_info, &tenma_72_7750_ser_driver_info, + &brymen_bm25x_ser_driver_info, #endif #ifdef HAVE_HW_SYSCLK_LWLA &sysclk_lwla_driver_info, diff --git a/src/hardware/serial-dmm/api.c b/src/hardware/serial-dmm/api.c index b96c7a0..d56a9d7 100644 --- a/src/hardware/serial-dmm/api.c +++ b/src/hardware/serial-dmm/api.c @@ -74,6 +74,7 @@ SR_PRIV struct sr_dev_driver uni_t_ut61e_ser_driver_info; SR_PRIV struct sr_dev_driver iso_tech_idm103n_driver_info; SR_PRIV struct sr_dev_driver tenma_72_7745_ser_driver_info; SR_PRIV struct sr_dev_driver tenma_72_7750_ser_driver_info; +SR_PRIV struct sr_dev_driver brymen_bm25x_ser_driver_info; SR_PRIV struct dmm_info dmms[] = { { @@ -318,6 +319,13 @@ SR_PRIV struct dmm_info dmms[] = { NULL, &tenma_72_7750_ser_driver_info, receive_data_TENMA_72_7750_SER, }, + { + "Brymen", "BM25x (BC20X cable)", "9600/8n1/rts=1/dtr=1", + 9600, BRYMEN_BM25X_PACKET_SIZE, 0, 0, NULL, + sr_brymen_bm25x_packet_valid, sr_brymen_bm25x_parse, + NULL, + &brymen_bm25x_ser_driver_info, receive_data_BRYMEN_BM25X_SER, + }, }; static int dev_clear(int dmm) @@ -630,3 +638,4 @@ DRV(uni_t_ut61e_ser, UNI_T_UT61E_SER, "uni-t-ut61e-ser", "UNI-T UT61E (UT-D02 ca DRV(iso_tech_idm103n, ISO_TECH_IDM103N, "iso-tech-idm103n", "ISO-TECH IDM103N") DRV(tenma_72_7745_ser, TENMA_72_7745_SER, "tenma-72-7745-ser", "Tenma 72-7745 (UT-D02 cable)") DRV(tenma_72_7750_ser, TENMA_72_7750_SER, "tenma-72-7750-ser", "Tenma 72-7750 (UT-D02 cable)") +DRV(brymen_bm25x_ser, BRYMEN_BM25X_SER, "brymen-bm25x-ser", "Brymen BM25x (BC20X cable)") diff --git a/src/hardware/serial-dmm/protocol.c b/src/hardware/serial-dmm/protocol.c index 0b2472f..c812793 100644 --- a/src/hardware/serial-dmm/protocol.c +++ b/src/hardware/serial-dmm/protocol.c @@ -223,3 +223,4 @@ RECEIVE_DATA(UNI_T_UT61E_SER, es519xx) RECEIVE_DATA(ISO_TECH_IDM103N, es519xx) RECEIVE_DATA(TENMA_72_7745_SER, fs9721) RECEIVE_DATA(TENMA_72_7750_SER, es519xx) +RECEIVE_DATA(BRYMEN_BM25X_SER, bm25x) diff --git a/src/hardware/serial-dmm/protocol.h b/src/hardware/serial-dmm/protocol.h index fb5a2a3..2468946 100644 --- a/src/hardware/serial-dmm/protocol.h +++ b/src/hardware/serial-dmm/protocol.h @@ -56,6 +56,7 @@ enum { ISO_TECH_IDM103N, TENMA_72_7745_SER, TENMA_72_7750_SER, + BRYMEN_BM25X_SER, }; struct dmm_info { @@ -155,5 +156,6 @@ SR_PRIV int receive_data_UNI_T_UT61E_SER(int fd, int revents, void *cb_data); SR_PRIV int receive_data_ISO_TECH_IDM103N(int fd, int revents, void *cb_data); SR_PRIV int receive_data_TENMA_72_7745_SER(int fd, int revents, void *cb_data); SR_PRIV int receive_data_TENMA_72_7750_SER(int fd, int revents, void *cb_data); +SR_PRIV int receive_data_BRYMEN_BM25X_SER(int fd, int revents, void *cb_data); #endif diff --git a/src/libsigrok-internal.h b/src/libsigrok-internal.h index 2a4384c..25d6383 100644 --- a/src/libsigrok-internal.h +++ b/src/libsigrok-internal.h @@ -740,4 +740,15 @@ SR_PRIV gboolean sr_rs9lcd_packet_valid(const uint8_t *buf); SR_PRIV int sr_rs9lcd_parse(const uint8_t *buf, float *floatval, struct sr_datafeed_analog *analog, void *info); +/*--- hardware/common/dmm/bm25x.c -----------------------------------------*/ + +#define BRYMEN_BM25X_PACKET_SIZE 15 + +/* Dummy info struct. The parser does not use it. */ +struct bm25x_info { int dummy; }; + +SR_PRIV gboolean sr_brymen_bm25x_packet_valid(const uint8_t *buf); +SR_PRIV int sr_brymen_bm25x_parse(const uint8_t *buf, float *floatval, + struct sr_datafeed_analog *analog, void *info); + #endif -- 2.0.1 |
From: Uwe H. <uw...@he...> - 2014-08-01 19:08:09
|
Hi, On Wed, Jul 30, 2014 at 10:28:58AM +0300, Janne Huttunen wrote: > The Brymen BM25x series suports the BC-20X that is an opto-isolated > serial cable. The link seems to be unidirectional i.e. when activated > the DMM periodically sends updates to the host while the host cannot > control the DMM in any way. Great stuff, merged with a few minor whitespace/typo fixes and such, thanks a lot! I also added your (missing) copyright line to bm25x.c :) One minor thing to check though, we only use the _SER and -ser suffixes in serial-dmm driver names when there are multiple cables/methods to get data from the DMM. This is the case for e.g. the UNI-T UT61D, which can be used with either a USB/HID based cable or with a simple RS232 cable (the drivers are thus named uni-t-ut61d and uni-t-ut61d-ser). If this distinction is not relevant for this Brymen DMM (because there's only one possible cable) we can/should drop the _SER/-ser etc. suffixes. Also, could you please add some info (and photos) about your DMM + cable in our sigrok.org wiki if possible? Ideally there should be a page for the specific DMM you have (which one is it btw?), something like this: http://sigrok.org/wiki/Brymen_BM857 That page should be linked from here (marking the DMM as supported as well): http://sigrok.org/wiki/Supported_hardware In addition we might make a "Brymen BMxxx series" page similar to these eventually (not a priority right now, though): http://sigrok.org/wiki/Rigol_DS2000_series Also, it would be great to have some info and photos of the BC-20X cable, listed here alongside the other Brymen cables: http://sigrok.org/wiki/Device_cables#Brymen_BC-85X.28-1.29 Please let me know which wiki username you prefer, and/or join us on the #sigrok IRC channel on FreeNode to get a wiki account, if you're interested in adding the above content and photos etc. Cheers, Uwe. -- http://hermann-uwe.de | http://randomprojects.org | http://sigrok.org |
From: Janne H. <jah...@gm...> - 2014-08-02 18:51:14
|
> One minor thing to check though, we only use the _SER and -ser suffixes > in serial-dmm driver names when there are multiple cables/methods to get > data from the DMM. This is the case for e.g. the UNI-T UT61D, which can > be used with either a USB/HID based cable or with a simple RS232 > cable (the drivers are thus named uni-t-ut61d and uni-t-ut61d-ser). > > If this distinction is not relevant for this Brymen DMM (because there's > only one possible cable) we can/should drop the _SER/-ser etc. suffixes. As far as I know, there is only the RS-232 cable. It is nowadays sold in package that includes also an USB-to-serial adapter (which I used for testing the driver), but that only provides one additional serial port. The DMM still appears attached into an RS-232 port and uses the same protocol. > Also, could you please add some info (and photos) about your DMM + cable in > our sigrok.org wiki if possible? Ideally there should be a page for the > specific DMM you have (which one is it btw?), something like this: I have the 'BM-257s', which I think is the top of the series. I won't be opening my DMM at this point, so I cannot provide any photos of the internals. I also cannot provide any photos of the externals right now either, but that may be possible at some point in the future (unless someone else provides them first). You probably can find photos of both the external and internals in the web and may even get a permission to use them if you ask. At least here seems to be some (with the cable and adapter included): http://www.eevblog.com/forum/testgear/brymen-bm-257-pictures-and-mini-review/ |
From: Uwe H. <uw...@he...> - 2014-08-03 16:18:48
|
Hi, On Sat, Aug 02, 2014 at 09:51:07PM +0300, Janne Huttunen wrote: > > One minor thing to check though, we only use the _SER and -ser suffixes > > in serial-dmm driver names when there are multiple cables/methods to get > > data from the DMM. This is the case for e.g. the UNI-T UT61D, which can > > be used with either a USB/HID based cable or with a simple RS232 > > cable (the drivers are thus named uni-t-ut61d and uni-t-ut61d-ser). > > > > If this distinction is not relevant for this Brymen DMM (because there's > > only one possible cable) we can/should drop the _SER/-ser etc. suffixes. > > As far as I know, there is only the RS-232 cable. It is nowadays sold > in package that includes also an USB-to-serial adapter (which I used > for testing the driver), but that only provides one additional serial port. > The DMM still appears attached into an RS-232 port and uses the same > protocol. OK, thanks! I'll drop the _SER prefixes then. > > Also, could you please add some info (and photos) about your DMM + cable in > > our sigrok.org wiki if possible? Ideally there should be a page for the > > specific DMM you have (which one is it btw?), something like this: > > I have the 'BM-257s', which I think is the top of the series. I won't be > opening my DMM at this point, so I cannot provide any photos of the > internals. I also cannot provide any photos of the externals right now > either, but that may be possible at some point in the future (unless > someone else provides them first). You probably can find photos of > both the external and internals in the web and may even get > a permission to use them if you ask. At least here seems to be > some (with the cable and adapter included): > > http://www.eevblog.com/forum/testgear/brymen-bm-257-pictures-and-mini-review/ We're generally only accepting self-made photos with an open-ish license (public domain or CC-BY-SA) in the wiki, so using photos from elsewhere is usually not possible. We can ask the author of the eevblog.com photos though, sure. Thanks for the pointer! If you want to make a wiki page for the BM-257s and/or if you can do external photos (without opening the DMM) now or later please do let us know anyway and we'll make a wiki account for you (or you can just send the photos to me via email if you prefer that, I can upload them). Cheers, Uwe. -- http://hermann-uwe.de | http://randomprojects.org | http://sigrok.org |
From: Bert V. <be...@bi...> - 2014-08-06 15:42:28
|
On 08/02/2014 08:51 PM, Janne Huttunen wrote: > I have the 'BM-257s', which I think is the top of the series. I won't be > opening my DMM at this point, so I cannot provide any photos of the > internals. I also cannot provide any photos of the externals right now > either, but that may be possible at some point in the future (unless > someone else provides them first). You probably can find photos of > both the external and internals in the web and may even get > a permission to use them if you ask. At least here seems to be > some (with the cable and adapter included): > > http://www.eevblog.com/forum/testgear/brymen-bm-257-pictures-and-mini-review/ So done, and the author agreed to make them available as public domain. Here's the page: http://sigrok.org/wiki/Brymen_BM257 -- Bert Vermeulen be...@bi... email/xmpp |