/* * This file is part of the libsigrok project. * * Copyright (C) 2019 Vitaliy Vorobyov * * 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 2 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 . */ /* * MASTECH MS2115B protocol parser. * * Sends 9 bytes. * D0 D1 D2 D3 D4 D5 D6 D7 D8 D9 * * D0 = 0x55 - sync byte * * D1 - mode: * bits: * B7..B4 ?? * B3 - func * B2..B0: * 0 - A 600/1000 (func=0 AC, func=1 DC), signed * 1 - A 60 (func=0 AC, func=1 DC), signed * 2 - V (func=0 AC, func=1 DC), signed * 3 - diode/beep (func=0 buz, func=1 diode) * 4 - resistance * 5 - capacitance * 6 - hz * * D2 - range * * D3 - frq range * * D4 main value LSB * D5 main value MSB * * (secondary value, hz, min/max, rel) * D6 secondary value LSB * D7 secondary value MSB * * D8 - flags * bits: * B7..B1:?? * B0 - 0 - auto, 1 - manual * * resistance: * 55 04 00 00 9B 18 00 00 01 (0.L, manual) 600.0 Ohm (x 0.1) * 55 04 01 00 9B 18 00 00 01 (0.L, manual) 6.000 kOhm (x 0.001) * 55 04 02 00 9B 18 00 00 01 (0.L, manual) 60.00 kOhm (x 0.01) * 55 04 03 00 9B 18 00 00 01 (0.L, manual) 600.0 kOhm (x 0.1) * 55 04 04 00 9B 18 00 00 01 (0.L, manual) 6.000 MOhm (x 0.001) * 55 04 05 00 9B 18 00 00 00 (0.L, auto) 60.00 MOhm (x 0.01) * * capacitance: * 55 05 00 00 04 00 00 00 00 (4nF, auto) * 55 05 00 00 05 00 00 00 01 (5nF, manual) 6.000 nF (x 0.001) * 55 05 01 00 03 19 00 00 01 (0.L nF, manual) 60.00 nF (x 0.01) * 55 05 02 00 D4 03 00 00 01 (980.0 nF, manual) 600.0 nF (x 0.1) * 55 05 03 00 63 00 00 00 01 (0.099 uF, manual) 6.000 uF (x 0.001) * 55 05 04 00 40 18 00 00 01 (0.L uF, manual) * 55 05 04 00 F0 00 00 00 01 (2.40 uF, manual) 60.00 uF (x 0.01) * 55 05 05 00 17 00 00 00 01 (2.3 uF, manual) 600.0 uF (x 0.1) * 55 05 06 00 02 00 00 00 01 (0.002 mF, manual) 6.000 mF (x 0.001) * 55 05 07 00 2F 00 00 00 01 (0.47 mF, manual) 60.00 mF (x 0.01) * * voltage: * 55 02 00 00 00 00 00 00 01 (0.0mV, manual) 600.0 mV (x 0.1) * 55 02 01 00 00 00 00 00 00 (0.000V, auto) * 55 02 01 00 00 00 00 00 01 (0.000V, manual) 6.000 V (x 0.001) * 55 02 02 00 00 00 00 00 01 (0.00V, manual) 60.00 V (x 0.01) * 55 02 03 00 00 00 00 00 01 (0.0V, manual) 600.0 V (x 0.1) * 55 02 04 00 00 00 00 00 01 (0V, manual) 1000 V (x 1) * * - Communication parameters: Unidirectional, 1200/8n1 * - CP2102 USB to UART bridge controller */ #include #include #include #include #include #include #include "libsigrok-internal.h" #define LOG_PREFIX "ms2115b" static void handle_flags(struct sr_datafeed_analog *analog, float *floatval, const struct ms2115b_info *info) { /* Measurement modes */ if (info->is_volt) { analog->meaning->mq = SR_MQ_VOLTAGE; analog->meaning->unit = SR_UNIT_VOLT; } if (info->is_ampere) { analog->meaning->mq = SR_MQ_CURRENT; analog->meaning->unit = SR_UNIT_AMPERE; } if (info->is_ohm) { analog->meaning->mq = SR_MQ_RESISTANCE; analog->meaning->unit = SR_UNIT_OHM; } if (info->is_hz) { analog->meaning->mq = SR_MQ_FREQUENCY; analog->meaning->unit = SR_UNIT_HERTZ; } if (info->is_farad) { analog->meaning->mq = SR_MQ_CAPACITANCE; analog->meaning->unit = SR_UNIT_FARAD; } if (info->is_beep) { analog->meaning->mq = SR_MQ_CONTINUITY; analog->meaning->unit = SR_UNIT_BOOLEAN; *floatval = (*floatval == INFINITY) ? 0.0 : 1.0; } if (info->is_diode) { analog->meaning->mq = SR_MQ_VOLTAGE; analog->meaning->unit = SR_UNIT_VOLT; } if (info->is_duty_cycle) analog->meaning->mq = SR_MQ_DUTY_CYCLE; if (info->is_percent) analog->meaning->unit = SR_UNIT_PERCENTAGE; /* Measurement related flags */ if (info->is_ac) analog->meaning->mqflags |= SR_MQFLAG_AC; if (info->is_dc) analog->meaning->mqflags |= SR_MQFLAG_DC; if (info->is_auto) analog->meaning->mqflags |= SR_MQFLAG_AUTORANGE; if (info->is_diode) analog->meaning->mqflags |= SR_MQFLAG_DIODE | SR_MQFLAG_DC; } SR_PRIV gboolean sr_ms2115b_packet_valid(const uint8_t *buf) { sr_dbg("DMM packet: %02x %02x %02x %02x %02x %02x %02x %02x %02x", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], buf[8]); if (buf[0] == 0x55) return TRUE; return FALSE; } /* Mode values equal to received data */ enum { MODE_A600_1000 = 0, MODE_A60 = 1, MODE_V = 2, MODE_DIODE_BEEP = 3, MODE_OHM = 4, MODE_CAP = 5, MODE_HZ = 6, }; static const int res_exp[] = { -1, /* 600.0 Ohm (x 0.1) */ -3 + 3, /* 6.000 kOhm (x 0.001) */ -2 + 3, /* 60.00 kOhm (x 0.01) */ -1 + 3, /* 600.0 kOhm (x 0.1) */ -3 + 6, /* 6.000 MOhm (x 0.001) */ -2 + 6, /* 60.00 MOhm (x 0.01) */ }; static const int cap_exp[] = { -3 - 9, /* 6.000 nF (x 0.001) */ -2 - 9, /* 60.00 nF (x 0.01) */ -1 - 9, /* 600.0 nF (x 0.1) */ -3 - 6, /* 6.000 uF (x 0.001) */ -2 - 6, /* 60.00 uF (x 0.01) */ -1 - 6, /* 600.0 uF (x 0.1) */ -3 - 3, /* 6.000 mF (x 0.001) */ -2 - 3, /* 60.00 mF (x 0.01) */ }; static const int hz_exp[] = { -2, /* 60.00 Hz (x 0.01) */ -1, /* 600.0 Hz (x 0.1) */ -3 + 3, /* 6.000 kHz (x 0.001) */ -2 + 3, /* 60.00 kHz (x 0.01) */ -1 + 3, /* 600.0 kHz (x 0.1) */ -3 + 6, /* 6.000 MHz (x 0.001) */ -2 + 6, /* 60.00 MHz (x 0.01) */ }; static const int v_exp[] = { -1 - 3, /* 600.0 mV (x 0.1) */ -3, /* 6.000 V (x 0.001) */ -2, /* 60.00 V (x 0.01) */ -1, /* 600.0 V (x 0.1) */ 0, /* 1000 V (x 1) */ }; SR_PRIV const char *ms2115b_channel_formats[MS2115B_DISPLAY_COUNT] = { "main", "sub", }; static int ms2115b_parse(const uint8_t *buf, float *floatval, struct sr_datafeed_analog *analog, void *info) { int exponent = 0; float up_limit = 6000.0; gboolean sign = FALSE; uint32_t mode = (buf[1] & 7); gboolean func = (buf[1] & 8) ? TRUE : FALSE; uint32_t range = (buf[2] & 7); struct ms2115b_info *info_local = info; enum eev121gw_display display = info_local->ch_idx; memset(info_local, 0, sizeof(*info_local)); info_local->ch_idx = display; switch (display) { case MS2115B_DISPLAY_MAIN: switch (mode) { case MODE_A600_1000: exponent = -1; sign = TRUE; info_local->is_ampere = TRUE; if (func) info_local->is_dc = TRUE; else info_local->is_ac = TRUE; break; case MODE_A60: exponent = -2; sign = TRUE; info_local->is_ampere = TRUE; if (func) info_local->is_dc = TRUE; else info_local->is_ac = TRUE; break; case MODE_V: if (range >= ARRAY_SIZE(v_exp)) return SR_ERR; exponent = v_exp[range]; sign = TRUE; info_local->is_volt = TRUE; if (func) info_local->is_dc = TRUE; else info_local->is_ac = TRUE; break; case MODE_DIODE_BEEP: if (func) { exponent = -3; up_limit = 2500.0; info_local->is_diode = TRUE; } else { info_local->is_beep = TRUE; } break; case MODE_OHM: if (range >= ARRAY_SIZE(res_exp)) return SR_ERR; exponent = res_exp[range]; info_local->is_ohm = TRUE; break; case MODE_CAP: if (range >= ARRAY_SIZE(cap_exp)) return SR_ERR; exponent = cap_exp[range]; info_local->is_farad = TRUE; break; case MODE_HZ: range = (buf[3] & 7); if (range >= ARRAY_SIZE(hz_exp)) return SR_ERR; exponent = hz_exp[range]; info_local->is_hz = TRUE; break; default: return SR_ERR; } if (sign) { *floatval = RL16S(buf + 4); /* signed 16bit value */ } else { *floatval = RL16(buf + 4); /* unsigned 16bit value */ } info_local->is_auto = (buf[8] & 1) ? FALSE : TRUE; break; case MS2115B_DISPLAY_SUB: switch (mode) { case MODE_A600_1000: case MODE_A60: case MODE_V: if (func) /* DC */ return SR_ERR_NA; /* AC */ info_local->is_hz = TRUE; exponent = -2; break; case MODE_HZ: info_local->is_duty_cycle = TRUE; info_local->is_percent = TRUE; exponent = -1; break; default: return SR_ERR_NA; } *floatval = RL16(buf + 6); /* unsigned 16bit value */ break; default: return SR_ERR; } if (fabsf(*floatval) > up_limit) { sr_spew("Over limit."); *floatval = INFINITY; return SR_OK; } *floatval *= powf(10, exponent); handle_flags(analog, floatval, info_local); analog->encoding->digits = -exponent; analog->spec->spec_digits = -exponent; return SR_OK; } /** * Parse a protocol packet. * * @param buf Buffer containing the 9-byte protocol packet. Must not be NULL. * @param floatval Pointer to a float variable. That variable will contain the * result value upon parsing success. Must not be NULL. * @param analog Pointer to a struct sr_datafeed_analog. The struct will be * filled with data according to the protocol packet. * Must not be NULL. * @param info Pointer to a struct ms2115b_info. The struct will be filled * with data according to the protocol packet. Must not be NULL. * * @return SR_OK upon success, SR_ERR upon failure. Upon errors, the * 'analog' variable contents are undefined and should not be used. */ SR_PRIV int sr_ms2115b_parse(const uint8_t *buf, float *floatval, struct sr_datafeed_analog *analog, void *info) { int ret; int ch_idx; struct ms2115b_info *info_local = info; ch_idx = info_local->ch_idx; ret = ms2115b_parse(buf, floatval, analog, info); info_local->ch_idx = ch_idx + 1; return ret; }