Hi guys,
I have written a dmm-serial module for the Meterman 38XR Multimeter and
have attached the patch below. I have also created preliminary wiki
pages which will be updated in due time.
This is my first time contributing to this project so if you could tell
me how to proceed it would be much appreciated.
Regards,
--
Peter Skarpetis
/data/src/electronics/sigrok/libsigrok/src/dmm/diff --git a/Makefile.am
b/Makefile.am
index 71d560d2..1f51684f 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -175,6 +175,7 @@ libsigrok_la_SOURCES += \
src/dmm/fs9922.c \
src/dmm/m2110.c \
src/dmm/metex14.c \
+ src/dmm/mm38xr.c \
src/dmm/ms2115b.c \
src/dmm/ms8250d.c \
src/dmm/rs9lcd.c \
diff --git a/src/dmm/mm38xr.c b/src/dmm/mm38xr.c
new file mode 100644
index 00000000..d1c3bcbb
--- /dev/null
+++ b/src/dmm/mm38xr.c
@@ -0,0 +1,463 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2020 Peter Skarpetis <pet...@skarpetis.com>
+ *
+ * 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/>.
+ */
+
+/*
+ * Meterman 38XR protocol parser
+ *
+ * Communication parameters: Unidirectional, 9600/8n1
+ *
+ * The user guide can be downloaded from:
+ * https://assets.tequipment.net/assets/1/26/Documents/38XR_Manual.pdf
+ *
+ * Protocol is described in a PDF available at:
+ *
https://www.elfadistrelec.fi/Web/Downloads/od/es/fj38XR-Serial-Output-Codes.pdf
+ *
+ * There is also a disussion about the protocol at the NI forum:
+ *
https://forums.ni.com/t5/Digital-Multimeters-DMMs-and/Meterman-DMM/td-p/179597?profile.language=en
+ *
+ * EEVBlog discussion thread about the meter
+ * https://www.eevblog.com/forum/chat/meterman-38xr/
+ *
+ */
+
+/**
+ * @file
+ *
+ * Meterman 38XR ASCII protocol parser.
+ *
+ */
+
+#include <config.h>
+#include <string.h>
+#include <math.h>
+#include <glib.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "mm38xr"
+
+#define METERMAN_DIGITS_OVERLOAD 0xB0DD
+#define METERMAN_DIGITS_BAD_INPUT_JACK 0xBAAB
+#define METERMAN_BARGRAPH_NO_SEGMENTS = 0x2a
+
+enum {
+ FUNCTION_CODE_UNUSED = 0x01,
+ FUNCTION_CODE_TEMPERATURE_FARENHEIGHT = 0x02,
+ FUNCTION_CODE_CURRENT_4_20_MAMPS = 0x03, // 4-20 mA
+ FUNCTION_CODE_DIODE_TEST = 0x04,
+ FUNCTION_CODE_INDUCTANCE_HENRIES = 0x05,
+ FUNCTION_CODE_TEMPERATURE_CELSIUS = 0x06,
+ FUNCTION_CODE_CURRENT_UAMPS = 0x07, // uA
+ FUNCTION_CODE_RESISTANCE_OHMS = 0x08,
+ FUNCTION_CODE_INDUCTANCE_MHENRIES = 0x09, // mH
+ FUNCTION_CODE_CURRENT_10_AMPS = 0x0a,
+ FUNCTION_CODE_CAPACITANCE = 0x0b,
+ FUNCTION_CODE_VOLTS_DC = 0x0c,
+ FUNCTION_CODE_LOGIC = 0x0d,
+ FUNCTION_CODE_CURRENT_MAMPS = 0x0e, // mA
+ FUNCTION_CODE_FREQUENCY_HZ = 0x0f, // and duty cycle
+ FUNCTION_CODE_VOLTS_AC = 0x10 // and dBm
+};
+
+enum {
+ MEASUREMENT_MODE_VOLTS = 0, // needed so we can index into the digits
and exponent arrays below
+ MEASUREMENT_MODE_RESISTANCE_OHMS,
+ MEASUREMENT_MODE_CURRENT_UAMPS, // uA
+ MEASUREMENT_MODE_CURRENT_MAMPS, // mA
+ MEASUREMENT_MODE_CURRENT_AMPS,
+ MEASUREMENT_MODE_CAPACITANCE,
+ MEASUREMENT_MODE_DIODE_TEST,
+ MEASUREMENT_MODE_TEMPERATURE_CELSIUS,
+ MEASUREMENT_MODE_TEMPERATURE_FARENHEIGHT,
+ MEASUREMENT_MODE_FREQUENCY_HZ,
+ MEASUREMENT_MODE_INDUCTANCE_HENRIES,
+ MEASUREMENT_MODE_INDUCTANCE_MHENRIES, // mH
+ MEASUREMENT_MODE_DBM,
+ MEASUREMENT_MODE_DUTY_CYCLE,
+ MEASUREMENT_MODE_CONTINUITY,
+
+ MEASUREMENT_MODE_UNDEFINED
+};
+
+enum {
+ ACDC_MODE_NONE = 1000,
+ ACDC_MODE_DC,
+ ACDC_MODE_AC,
+ ACDC_MODE_AC_AND_DC
+};
+
+typedef struct METERMANINFO {
+ uint32_t functioncode; // columns 0 1,
+ uint32_t reading; // columns 2,3,4,5, LCD digits
+ uint32_t bargraphsegments; // columns 6, 7, maximum of 40 segments,
0x2A means no bargraph
+ uint32_t rangecode; // column 8
+ uint32_t ampsfunction; // column 9
+ uint32_t peakstatus; // column 10
+ uint32_t rflag_h; // column 11
+ uint32_t rflag_l; // column 12
+
+ // calculated values
+ uint32_t measurement_mode;
+ uint32_t acdc; // DC, AC or AC+DC
+} METERMANINFO;
+
+static const int decimal_digits[][7] = {
+ { 1, 3, 2, 1, 0, 0, 0 }, /* V */
+ { 2, 3, 4, 2, 3, 1, 0 }, /* Ohms */
+ { 2, 1, 0, 0, 0, 0, 0 }, /* uA */
+ { 3, 2, 1, 0, 0, 0, 0 }, /* mA */
+ { 3, 0, 0, 0, 0, 0, 0 }, /* A */
+ { 2, 1, 3, 2, 1, 0, 0 }, /* F */
+ { 0, 3, 0, 0, 0, 0, 0 }, /* Diode test */
+ { 0, 0, 0, 0, 0, 0, 0 }, /* °C */
+ { 0, 0, 0, 0, 0, 0, 0 }, /* °F */
+ { 2, 1, 3, 2, 1, 3, 2 }, /* Hz */
+ { 0, 0, 0, 3, 2, 0, 0 }, /* H */
+ { 3, 2, 1, 0, 0, 0, 0 }, /* mH */
+ { 2, 2, 2, 2, 2, 2, 2 }, /* dBm */
+ { 2, 2, 2, 2, 2, 2, 2 }, /* Duty cycle */
+ { 0, 0, 0, 0, 0, 1, 0 } /* Continuity */
+};
+
+static const int units_exponents[][7] = {
+ { -3, 0, 0, 0, 0, 0, 0 }, /* V */
+ { 6, 6, 6, 3, 3, 0, 0 }, /* Ohms */
+ { -6, -6, 0, 0, 0, 0, 0 }, /* uA */
+ { -3, -3, -3, 0, 0, 0, 0 }, /* mA */
+ { 0, 0, 0, 0, 0, 0, 0 }, /* A */
+ { -9, -9, -6, -6, -6, 0, 0 }, /* F */
+ { 0, 0, 0, 0, 0, 0, 0 }, /* Diode test */
+ { 0, 0, 0, 0, 0, 0, 0 }, /* °C */
+ { 0, 0, 0, 0, 0, 0, 0 }, /* °F */
+ { 0, 0, 3, 3, 3, 6, 6 }, /* Hz */
+ { 0, 0, 0, 0, 0, 0, 0 }, /* H */
+ { -3, -3, -3, 0, 0, 0, 0 }, /* mH */
+ { 0, 0, 0, 0, 0, 0, 0 }, /* dBm */
+ { 0, 0, 0, 0, 0, 0, 0 }, /* Duty cycle */
+ { 0, 0, 0, 0, 0, 0, 0 } /* Continuity */
+};
+
+
+/* Assumes caller has already checked data fall within 0..9 and A..F */
+static uint32_t meterman_38xr_hexnibble_to_uint(uint8_t v)
+{
+ return (v <= '9') ? v - '0' : v - 'A' + 10;
+}
+
+static uint32_t meterman_38xr_function_code(const uint8_t *buf)
+{
+ uint32_t v;
+
+ v = meterman_38xr_hexnibble_to_uint(buf[0]) << 4 |
meterman_38xr_hexnibble_to_uint(buf[1]);
+ return v;
+}
+
+static uint32_t meterman_38xr_barsegments(const uint8_t *buf)
+{
+ uint32_t v;
+
+ v = meterman_38xr_hexnibble_to_uint(buf[6]) << 4 |
meterman_38xr_hexnibble_to_uint(buf[7]);
+ return v;
+}
+
+static int32_t meterman_38xr_reading(const uint8_t *buf)
+{
+ int32_t v;
+ if (buf[2] > 'A') { // overload
+ v = meterman_38xr_hexnibble_to_uint(buf[2]) << 12 |
+ meterman_38xr_hexnibble_to_uint(buf[3]) << 8 |
+ meterman_38xr_hexnibble_to_uint(buf[4]) << 4 |
+ meterman_38xr_hexnibble_to_uint(buf[5]);
+ return v;
+ }
+ else {
+ v = meterman_38xr_hexnibble_to_uint(buf[2]) * 1000 +
+ meterman_38xr_hexnibble_to_uint(buf[3]) * 100 +
+ meterman_38xr_hexnibble_to_uint(buf[4]) * 10 +
+ meterman_38xr_hexnibble_to_uint(buf[5]);
+
+ }
+ return v;
+}
+
+static gboolean meterman_38xr_is_negative(METERMANINFO *mi) {
+ if (mi->rflag_l == 0x01) {
+ return TRUE;
+ }
+ if (mi->measurement_mode == MEASUREMENT_MODE_DBM && mi->rflag_l ==
0x05) {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static int currentACDC(METERMANINFO *mi) {
+ if (mi->ampsfunction == 0x01) {
+ return ACDC_MODE_AC;
+ } else if (mi->ampsfunction == 0x02) {
+ return ACDC_MODE_AC_AND_DC;
+ } else {
+ return ACDC_MODE_DC;
+ }
+}
+static int meterman_38xr_decode(const uint8_t *buf, METERMANINFO *mi)
+{
+ if (!meterman_38xr_packet_valid(buf))
+ return SR_ERR;
+
+ mi->functioncode = meterman_38xr_function_code(buf);
+ if (mi->functioncode < 2 || mi->functioncode > 0x10) {
+ return SR_ERR;
+ }
+ mi->reading = meterman_38xr_reading(buf);
+ mi->bargraphsegments = meterman_38xr_barsegments(buf);
+ mi->rangecode = meterman_38xr_hexnibble_to_uint(buf[8]);
+ if (mi->rangecode > 6) {
+ return SR_ERR;
+ }
+ mi->ampsfunction = meterman_38xr_hexnibble_to_uint(buf[9]);
+ mi->peakstatus = meterman_38xr_hexnibble_to_uint(buf[10]);
+ mi->rflag_h = meterman_38xr_hexnibble_to_uint(buf[11]);
+ mi->rflag_l = meterman_38xr_hexnibble_to_uint(buf[12]);
+
+ mi->acdc = ACDC_MODE_NONE;
+ switch (mi->functioncode) {
+ case FUNCTION_CODE_TEMPERATURE_FARENHEIGHT:
+ mi->measurement_mode = MEASUREMENT_MODE_TEMPERATURE_FARENHEIGHT;
+ break;
+
+ case FUNCTION_CODE_CURRENT_4_20_MAMPS:
+ mi->measurement_mode = MEASUREMENT_MODE_CURRENT_MAMPS;
+ mi->acdc = currentACDC(mi);
+ break;
+
+ case FUNCTION_CODE_DIODE_TEST:
+ mi->measurement_mode = MEASUREMENT_MODE_DIODE_TEST;
+ mi->acdc = ACDC_MODE_DC;
+ break;
+
+ case FUNCTION_CODE_INDUCTANCE_HENRIES:
+ mi->measurement_mode = MEASUREMENT_MODE_INDUCTANCE_HENRIES;
+ break;
+
+ case FUNCTION_CODE_TEMPERATURE_CELSIUS:
+ mi->measurement_mode = MEASUREMENT_MODE_TEMPERATURE_CELSIUS;
+ break;
+
+ case FUNCTION_CODE_CURRENT_UAMPS:
+ mi->measurement_mode = MEASUREMENT_MODE_CURRENT_UAMPS;
+ mi->acdc = currentACDC(mi);
+ break;
+
+ case FUNCTION_CODE_RESISTANCE_OHMS:
+ mi->measurement_mode = (mi->rflag_l == 0x08) ?
MEASUREMENT_MODE_CONTINUITY : MEASUREMENT_MODE_RESISTANCE_OHMS;
+ break;
+
+ case FUNCTION_CODE_INDUCTANCE_MHENRIES:
+ mi->measurement_mode = MEASUREMENT_MODE_INDUCTANCE_MHENRIES;
+ break;
+
+ case FUNCTION_CODE_CURRENT_10_AMPS:
+ mi->measurement_mode = MEASUREMENT_MODE_CURRENT_AMPS;
+ mi->acdc = currentACDC(mi);
+ break;
+
+ case FUNCTION_CODE_CAPACITANCE:
+ mi->measurement_mode = MEASUREMENT_MODE_CAPACITANCE;
+ break;
+
+ case FUNCTION_CODE_VOLTS_DC:
+ mi->measurement_mode = MEASUREMENT_MODE_VOLTS;
+ mi->acdc = (mi->rflag_l == 0x02) ? ACDC_MODE_AC_AND_DC :
ACDC_MODE_DC;
+ break;
+
+ case FUNCTION_CODE_CURRENT_MAMPS:
+ mi->measurement_mode = MEASUREMENT_MODE_CURRENT_MAMPS;
+ mi->acdc = currentACDC(mi);
+ break;
+
+ case FUNCTION_CODE_FREQUENCY_HZ:
+ mi->measurement_mode = (mi->rflag_h == 0x0B) ?
MEASUREMENT_MODE_DUTY_CYCLE : MEASUREMENT_MODE_FREQUENCY_HZ;
+ break;
+
+ case FUNCTION_CODE_VOLTS_AC:
+ mi->measurement_mode = (mi->rflag_l == 0x04 || mi->rflag_l == 0x05)
? MEASUREMENT_MODE_DBM : MEASUREMENT_MODE_VOLTS;
+ mi->acdc = ACDC_MODE_AC;
+ break;
+
+ default:
+ mi->measurement_mode = MEASUREMENT_MODE_UNDEFINED;
+ return SR_ERR;
+
+ }
+ return SR_OK;
+}
+
+SR_PRIV gboolean meterman_38xr_packet_valid(const uint8_t *buf)
+{
+ int i;
+ uint32_t fcode;
+
+ if ((buf[13] != '\r') || (buf[14] != '\n')) {
+ return FALSE;
+ }
+ // Check for all hex digits
+ for (i = 0; i < 13; i++) {
+ if (buf[0] < '0') {
+ return FALSE;
+ }
+ else if (buf[0] > '9' && buf[0] < 'A') {
+ return FALSE;
+ }
+ else if (buf[0] > 'F') {
+ return FALSE;
+ }
+ }
+ fcode = meterman_38xr_function_code(buf);
+ if (fcode < 0x01 || fcode > 0x10) {
+ return FALSE;
+ }
+ return TRUE;
+}
+
+SR_PRIV int meterman_38xr_parse(const uint8_t *buf, float *floatval,
+ struct sr_datafeed_analog *analog, void *info)
+{
+ int exponent;
+ int digits;
+ METERMANINFO mi;
+
+ (void)info;
+
+ if (meterman_38xr_decode(buf, &mi) != SR_OK)
+ return SR_ERR;
+
+ if (mi.measurement_mode != MEASUREMENT_MODE_CONTINUITY) {
+ if (mi.reading == METERMAN_DIGITS_OVERLOAD || mi.reading ==
METERMAN_DIGITS_BAD_INPUT_JACK) {
+ sr_spew("Over limit.");
+ *floatval = INFINITY; // overload
+ return SR_OK;
+ }
+ }
+ switch (mi.measurement_mode) {
+ case MEASUREMENT_MODE_VOLTS:
+ analog->meaning->mq = SR_MQ_VOLTAGE;
+ analog->meaning->unit = SR_UNIT_VOLT;
+ break;
+ case MEASUREMENT_MODE_RESISTANCE_OHMS:
+ analog->meaning->mq = SR_MQ_RESISTANCE;
+ analog->meaning->unit = SR_UNIT_OHM;
+ break;
+ case MEASUREMENT_MODE_CURRENT_UAMPS:
+ case MEASUREMENT_MODE_CURRENT_MAMPS:
+ case MEASUREMENT_MODE_CURRENT_AMPS:
+ analog->meaning->mq = SR_MQ_CURRENT;
+ analog->meaning->unit = SR_UNIT_AMPERE;
+ break;
+ case MEASUREMENT_MODE_CAPACITANCE:
+ analog->meaning->mq = SR_MQ_CAPACITANCE;
+ analog->meaning->unit = SR_UNIT_FARAD;
+ break;
+ case MEASUREMENT_MODE_DIODE_TEST:
+ analog->meaning->mq = SR_MQ_VOLTAGE;
+ analog->meaning->unit = SR_UNIT_VOLT;
+ analog->meaning->mqflags |= SR_MQFLAG_DIODE;
+ break;
+ case MEASUREMENT_MODE_TEMPERATURE_CELSIUS:
+ analog->meaning->mq = SR_MQ_TEMPERATURE;
+ analog->meaning->unit = SR_UNIT_CELSIUS;
+ break;
+ case MEASUREMENT_MODE_TEMPERATURE_FARENHEIGHT:
+ analog->meaning->mq = SR_MQ_TEMPERATURE;
+ analog->meaning->unit = SR_UNIT_FAHRENHEIT;
+ break;
+ case MEASUREMENT_MODE_FREQUENCY_HZ:
+ analog->meaning->mq = SR_MQ_FREQUENCY;
+ analog->meaning->unit = SR_UNIT_HERTZ;
+ break;
+ case MEASUREMENT_MODE_INDUCTANCE_HENRIES:
+ analog->meaning->mq = SR_MQ_SERIES_INDUCTANCE;
+ analog->meaning->unit = SR_UNIT_HENRY;
+ break;
+ case MEASUREMENT_MODE_INDUCTANCE_MHENRIES:
+ analog->meaning->mq = SR_MQ_SERIES_INDUCTANCE;
+ analog->meaning->unit = SR_UNIT_HENRY;
+ break;
+ case MEASUREMENT_MODE_DBM:
+ analog->meaning->mq = SR_MQ_VOLTAGE;
+ analog->meaning->unit = SR_UNIT_DECIBEL_MW;
+ analog->meaning->mqflags |= SR_MQFLAG_AC;
+ break;
+ case MEASUREMENT_MODE_DUTY_CYCLE:
+ analog->meaning->mq = SR_MQ_DUTY_CYCLE;
+ analog->meaning->unit = SR_UNIT_PERCENTAGE;
+ break;
+ case MEASUREMENT_MODE_CONTINUITY:
+ analog->meaning->mq = SR_MQ_CONTINUITY;
+ analog->meaning->unit = SR_UNIT_BOOLEAN;
+ *floatval = (mi.reading == METERMAN_DIGITS_OVERLOAD) ? 0.0 :
1.0;
+ break;
+ default:
+ return SR_ERR;
+ }
+ switch (mi.acdc) {
+ case ACDC_MODE_DC:
+ analog->meaning->mqflags |= SR_MQFLAG_DC;
+ break;
+ case ACDC_MODE_AC:
+ analog->meaning->mqflags |= SR_MQFLAG_AC;
+ break;
+ case ACDC_MODE_AC_AND_DC:
+ analog->meaning->mqflags |= SR_MQFLAG_DC | SR_MQFLAG_AC;
+ break;
+ default:
+ break;
+ }
+ if (mi.peakstatus == 0x02 || mi.peakstatus == 0x0a) {
+ analog->meaning->mqflags |= SR_MQFLAG_MAX;
+ }
+ if (mi.peakstatus == 0x03 || mi.peakstatus == 0x0b) {
+ analog->meaning->mqflags |= SR_MQFLAG_MIN;
+ }
+ if (mi.rflag_h == 0x0a || mi.peakstatus == 0x0b) {
+ analog->meaning->mqflags |= SR_MQFLAG_AUTORANGE;
+ }
+ if (mi.measurement_mode != MEASUREMENT_MODE_CONTINUITY) {
+ digits = decimal_digits[mi.measurement_mode][mi.rangecode];
+ exponent = units_exponents[mi.measurement_mode][mi.rangecode];
+
+ *floatval = mi.reading;
+ if (meterman_38xr_is_negative(&mi)) {
+ *floatval *= -1.0f;
+ }
+ *floatval *= powf(10, -digits);
+ *floatval *= powf(10, exponent);
+ }
+ analog->encoding->digits = 4;
+ analog->spec->spec_digits = 4;
+
+ return SR_OK;
+}
+
+
+/* Local Variables: */
+/* mode: c */
+/* indent-tabs-mode: t */
+/* c-basic-offset: 8 */
+/* tab-width: 8 */
+/* End: */
diff --git a/src/hardware/serial-dmm/api.c b/src/hardware/serial-dmm/api.c
index 9e050af7..1d3781a4 100644
--- a/src/hardware/serial-dmm/api.c
+++ b/src/hardware/serial-dmm/api.c
@@ -637,6 +637,15 @@ SR_REGISTER_DEV_DRIVER_LIST(serial_dmm_drivers,
NULL
),
/* }}} */
+ /* meterman 38XR based meters {{{ */
+ DMM(
+ "meterman-38xr", mm38xr,
+ "Meterman", "38XR", "9600/8n1/rts=0/dtr=1",
+ METERMAN_38XR_PACKET_SIZE, 0, 0, NULL,
+ meterman_38xr_packet_valid, meterman_38xr_parse,
+ NULL
+ ),
+ /* }}} */
/* metex14 based meters {{{ */
DMM(
"mastech-mas345", metex14,
diff --git a/src/libsigrok-internal.h b/src/libsigrok-internal.h
index 44be53c6..8af7d832 100644
--- a/src/libsigrok-internal.h
+++ b/src/libsigrok-internal.h
@@ -2251,6 +2251,23 @@ SR_PRIV void sr_fs9721_10_temp_c(struct
sr_datafeed_analog *analog, void *info);
SR_PRIV void sr_fs9721_01_10_temp_f_c(struct sr_datafeed_analog
*analog, void *info);
SR_PRIV void sr_fs9721_max_c_min(struct sr_datafeed_analog *analog,
void *info);
+/*--- dmm/mm38xr.c
---------------------------------------------------------*/
+
+#define METERMAN_38XR_PACKET_SIZE 15
+
+struct mm38xr_info {
+ gboolean is_ac, is_dc, is_resistance, is_capacity, is_temperature;
+ gboolean is_diode, is_frequency, is_ampere, is_volt, is_farad;
+ gboolean is_hertz, is_ohm, is_celsius, is_fahrenheit;
+ gboolean is_nano, is_micro, is_milli, is_kilo, is_mega;
+ gboolean is_decibel;
+ gboolean is_min, is_max, is_avg;
+};
+
+SR_PRIV gboolean meterman_38xr_packet_valid(const uint8_t *buf);
+SR_PRIV int meterman_38xr_parse(const uint8_t *buf, float *floatval,
+ struct sr_datafeed_analog *analog, void
*info);
+
/*--- dmm/ms2115b.c
---------------------------------------------------------*/
#define MS2115B_PACKET_SIZE 9
_______________________________________________
sigrok-devel mailing list
sigrok-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/sigrok-devel