]> sigrok.org Git - libsigrok.git/blame - hardware/common/dmm/metex14.c
Centralise duplicated logging helper defines.
[libsigrok.git] / hardware / common / dmm / metex14.c
CommitLineData
ac913e5c 1/*
50985c20 2 * This file is part of the libsigrok project.
ac913e5c 3 *
71f1302b 4 * Copyright (C) 2012-2013 Uwe Hermann <uwe@hermann-uwe.de>
ac913e5c
UH
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21/*
22 * Metex 14-bytes ASCII protocol parser.
23 *
24 * This should work for various multimeters which use this kind of protocol,
25 * even though there is some variation in which modes each DMM supports.
26 *
27 * It does _not_ work for all Metex DMMs, some use a quite different protocol.
28 */
29
30#include <string.h>
31#include <ctype.h>
32#include <math.h>
33#include <glib.h>
34#include "libsigrok.h"
35#include "libsigrok-internal.h"
36
3544f848 37#define LOG_PREFIX "metex14"
ac913e5c
UH
38
39static int parse_value(const uint8_t *buf, float *result)
40{
1a807c13
UH
41 int i, is_ol, cnt;
42 char valstr[7 + 1];
43
44 /* Strip all spaces from bytes 2-8. */
45 memset(&valstr, 0, 7 + 1);
46 for (i = 0, cnt = 0; i < 7; i++) {
47 if (buf[2 + i] != ' ')
48 valstr[cnt++] = buf[2 + i];
ac913e5c
UH
49 }
50
51 /* Bytes 5-7: Over limit (various forms) */
52 is_ol = 0;
ee6cb5a4
UH
53 is_ol += (!strcasecmp((const char *)&valstr, ".OL")) ? 1 : 0;
54 is_ol += (!strcasecmp((const char *)&valstr, "O.L")) ? 1 : 0;
55 is_ol += (!strcasecmp((const char *)&valstr, "OL.")) ? 1 : 0;
56 is_ol += (!strcasecmp((const char *)&valstr, "OL")) ? 1 : 0;
3a8cad91
UH
57 is_ol += (!strcasecmp((const char *)&valstr, "-.OL")) ? 1 : 0;
58 is_ol += (!strcasecmp((const char *)&valstr, "-O.L")) ? 1 : 0;
59 is_ol += (!strcasecmp((const char *)&valstr, "-OL.")) ? 1 : 0;
60 is_ol += (!strcasecmp((const char *)&valstr, "-OL")) ? 1 : 0;
ac913e5c
UH
61 if (is_ol != 0) {
62 sr_spew("Over limit.");
63 *result = INFINITY;
64 return SR_OK;
65 }
66
1a807c13
UH
67 /* Bytes 2-8: Sign, value (up to 5 digits) and decimal point */
68 sscanf((const char *)&valstr, "%f", result);
ac913e5c 69
1a807c13 70 sr_spew("The display value is %f.", *result);
ac913e5c
UH
71
72 return SR_OK;
73}
74
75static void parse_flags(const char *buf, struct metex14_info *info)
76{
71f1302b
UH
77 int i, cnt;
78 char unit[4 + 1];
79 const char *u;
80
ac913e5c
UH
81 /* Bytes 0-1: Measurement mode */
82 /* Note: Protocol doesn't distinguish "resistance" from "beep" mode. */
83 info->is_ac = !strncmp(buf, "AC", 2);
84 info->is_dc = !strncmp(buf, "DC", 2);
85 info->is_resistance = !strncmp(buf, "OH", 2);
86 info->is_capacity = !strncmp(buf, "CA", 2);
87 info->is_temperature = !strncmp(buf, "TE", 2);
88 info->is_diode = !strncmp(buf, "DI", 2);
89 info->is_frequency = !strncmp(buf, "FR", 2);
9871215c
UH
90 info->is_gain = !strncmp(buf, "DB", 2);
91 info->is_hfe = !strncmp(buf, "HF", 2);
92
93 /*
94 * Note: "DB" shows the logarithmic ratio of input voltage to a
95 * pre-stored (user-changeable) value in the DMM.
96 */
ac913e5c
UH
97
98 if (info->is_dc || info->is_ac)
99 info->is_volt = TRUE;
100
1a807c13 101 /* Bytes 2-8: See parse_value(). */
ac913e5c 102
1a807c13 103 /* Strip all spaces from bytes 9-12. */
71f1302b
UH
104 memset(&unit, 0, 4 + 1);
105 for (i = 0, cnt = 0; i < 4; i++) {
106 if (buf[9 + i] != ' ')
107 unit[cnt++] = buf[9 + i];
108 }
1a807c13
UH
109
110 /* Bytes 9-12: Unit */
71f1302b 111 u = (const char *)&unit;
ee6cb5a4 112 if (!strcasecmp(u, "A"))
ac913e5c 113 info->is_ampere = TRUE;
ee6cb5a4 114 else if (!strcasecmp(u, "mA"))
ac913e5c 115 info->is_milli = info->is_ampere = TRUE;
ee6cb5a4 116 else if (!strcasecmp(u, "uA"))
2477fb95 117 info->is_micro = info->is_ampere = TRUE;
ee6cb5a4 118 else if (!strcasecmp(u, "V"))
ac913e5c 119 info->is_volt = TRUE;
ee6cb5a4 120 else if (!strcasecmp(u, "mV"))
ac913e5c 121 info->is_milli = info->is_volt = TRUE;
ee6cb5a4 122 else if (!strcasecmp(u, "Ohm"))
ac913e5c 123 info->is_ohm = TRUE;
ee6cb5a4 124 else if (!strcasecmp(u, "KOhm"))
ac913e5c 125 info->is_kilo = info->is_ohm = TRUE;
ee6cb5a4 126 else if (!strcasecmp(u, "MOhm"))
ac913e5c 127 info->is_mega = info->is_ohm = TRUE;
c02dc3e2
UH
128 else if (!strcasecmp(u, "pF"))
129 info->is_pico = info->is_farad = TRUE;
ee6cb5a4 130 else if (!strcasecmp(u, "nF"))
ac913e5c 131 info->is_nano = info->is_farad = TRUE;
ee6cb5a4 132 else if (!strcasecmp(u, "uF"))
ac913e5c 133 info->is_micro = info->is_farad = TRUE;
ee6cb5a4 134 else if (!strcasecmp(u, "KHz"))
ac913e5c 135 info->is_kilo = info->is_hertz = TRUE;
ee6cb5a4 136 else if (!strcasecmp(u, "C"))
ac913e5c 137 info->is_celsius = TRUE;
ee6cb5a4 138 else if (!strcasecmp(u, "DB"))
9871215c 139 info->is_decibel = TRUE;
ee6cb5a4 140 else if (!strcasecmp(u, ""))
9871215c 141 info->is_unitless = TRUE;
ac913e5c
UH
142
143 /* Byte 13: Always '\r' (carriage return, 0x0d, 13) */
144}
145
146static void handle_flags(struct sr_datafeed_analog *analog, float *floatval,
147 const struct metex14_info *info)
148{
149 /* Factors */
c02dc3e2
UH
150 if (info->is_pico)
151 *floatval /= 1000000000000ULL;
ac913e5c
UH
152 if (info->is_nano)
153 *floatval /= 1000000000;
154 if (info->is_micro)
155 *floatval /= 1000000;
156 if (info->is_milli)
157 *floatval /= 1000;
158 if (info->is_kilo)
159 *floatval *= 1000;
160 if (info->is_mega)
161 *floatval *= 1000000;
162
163 /* Measurement modes */
164 if (info->is_volt) {
165 analog->mq = SR_MQ_VOLTAGE;
166 analog->unit = SR_UNIT_VOLT;
167 }
168 if (info->is_ampere) {
169 analog->mq = SR_MQ_CURRENT;
170 analog->unit = SR_UNIT_AMPERE;
171 }
172 if (info->is_ohm) {
173 analog->mq = SR_MQ_RESISTANCE;
174 analog->unit = SR_UNIT_OHM;
175 }
176 if (info->is_hertz) {
177 analog->mq = SR_MQ_FREQUENCY;
178 analog->unit = SR_UNIT_HERTZ;
179 }
180 if (info->is_farad) {
181 analog->mq = SR_MQ_CAPACITANCE;
182 analog->unit = SR_UNIT_FARAD;
183 }
184 if (info->is_celsius) {
185 analog->mq = SR_MQ_TEMPERATURE;
186 analog->unit = SR_UNIT_CELSIUS;
187 }
188 if (info->is_diode) {
189 analog->mq = SR_MQ_VOLTAGE;
190 analog->unit = SR_UNIT_VOLT;
191 }
9871215c
UH
192 if (info->is_gain) {
193 analog->mq = SR_MQ_GAIN;
194 analog->unit = SR_UNIT_DECIBEL_VOLT;
195 }
196 if (info->is_hfe) {
197 analog->mq = SR_MQ_GAIN;
198 analog->unit = SR_UNIT_UNITLESS;
199 }
ac913e5c
UH
200
201 /* Measurement related flags */
202 if (info->is_ac)
203 analog->mqflags |= SR_MQFLAG_AC;
204 if (info->is_dc)
205 analog->mqflags |= SR_MQFLAG_DC;
a6ed50f4
UH
206 if (info->is_diode)
207 analog->mqflags |= SR_MQFLAG_DIODE;
ac913e5c
UH
208}
209
210static gboolean flags_valid(const struct metex14_info *info)
211{
212 int count;
213
214 /* Does the packet have more than one multiplier? */
215 count = 0;
c02dc3e2 216 count += (info->is_pico) ? 1 : 0;
ac913e5c
UH
217 count += (info->is_nano) ? 1 : 0;
218 count += (info->is_micro) ? 1 : 0;
219 count += (info->is_milli) ? 1 : 0;
220 count += (info->is_kilo) ? 1 : 0;
221 count += (info->is_mega) ? 1 : 0;
222 if (count > 1) {
223 sr_err("More than one multiplier detected in packet.");
224 return FALSE;
225 }
226
227 /* Does the packet "measure" more than one type of value? */
228 count = 0;
229 count += (info->is_ac) ? 1 : 0;
230 count += (info->is_dc) ? 1 : 0;
231 count += (info->is_resistance) ? 1 : 0;
232 count += (info->is_capacity) ? 1 : 0;
233 count += (info->is_temperature) ? 1 : 0;
234 count += (info->is_diode) ? 1 : 0;
235 count += (info->is_frequency) ? 1 : 0;
236 if (count > 1) {
237 sr_err("More than one measurement type detected in packet.");
238 return FALSE;
239 }
240
241 /* Both AC and DC set? */
242 if (info->is_ac && info->is_dc) {
243 sr_err("Both AC and DC flags detected in packet.");
244 return FALSE;
245 }
246
247 return TRUE;
248}
249
c4f2dfd0 250#ifdef HAVE_LIBSERIALPORT
e90cf076
UH
251SR_PRIV int sr_metex14_packet_request(struct sr_serial_dev_inst *serial)
252{
253 const uint8_t wbuf = 'D';
254
255 sr_spew("Requesting DMM packet.");
256
257 return (serial_write(serial, &wbuf, 1) == 1) ? SR_OK : SR_ERR;
258}
c4f2dfd0 259#endif
e90cf076 260
ac913e5c
UH
261SR_PRIV gboolean sr_metex14_packet_valid(const uint8_t *buf)
262{
263 struct metex14_info info;
264
265 memset(&info, 0x00, sizeof(struct metex14_info));
266 parse_flags((const char *)buf, &info);
267
268 if (!flags_valid(&info))
269 return FALSE;
270
271 if (buf[13] != '\r')
272 return FALSE;
273
274 return TRUE;
275}
276
277/**
278 * Parse a protocol packet.
279 *
1fbab466 280 * @param buf Buffer containing the protocol packet. Must not be NULL.
ac913e5c 281 * @param floatval Pointer to a float variable. That variable will be modified
1fbab466 282 * in-place depending on the protocol packet. Must not be NULL.
ac913e5c
UH
283 * @param analog Pointer to a struct sr_datafeed_analog. The struct will be
284 * filled with data according to the protocol packet.
1fbab466
UH
285 * Must not be NULL.
286 * @param info Pointer to a struct metex14_info. The struct will be filled
287 * with data according to the protocol packet. Must not be NULL.
ac913e5c
UH
288 *
289 * @return SR_OK upon success, SR_ERR upon failure. Upon errors, the
290 * 'analog' variable contents are undefined and should not be used.
291 */
292SR_PRIV int sr_metex14_parse(const uint8_t *buf, float *floatval,
1fbab466 293 struct sr_datafeed_analog *analog, void *info)
ac913e5c
UH
294{
295 int ret;
1fbab466
UH
296 struct metex14_info *info_local;
297
298 info_local = (struct metex14_info *)info;
ac913e5c 299
e82d7dbc
AG
300 /* Don't print byte 13. That one contains the carriage return. */
301 sr_dbg("DMM packet: \"%.13s\"", buf);
302
ac913e5c
UH
303 if ((ret = parse_value(buf, floatval)) != SR_OK) {
304 sr_err("Error parsing value: %d.", ret);
305 return ret;
306 }
307
1fbab466
UH
308 memset(info_local, 0x00, sizeof(struct metex14_info));
309 parse_flags((const char *)buf, info_local);
310 handle_flags(analog, floatval, info_local);
ac913e5c
UH
311
312 return SR_OK;
313}