]> sigrok.org Git - libsigrok.git/blob - hardware/common/dmm/es51922.c
Centralise duplicated logging helper defines.
[libsigrok.git] / hardware / common / dmm / es51922.c
1 /*
2  * This file is part of the libsigrok project.
3  *
4  * Copyright (C) 2012 Uwe Hermann <uwe@hermann-uwe.de>
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  * Cyrustek ES51922 protocol parser.
23  *
24  * Communication parameters: Unidirectional, 19230/7o1
25  */
26
27 #include <string.h>
28 #include <ctype.h>
29 #include <math.h>
30 #include <glib.h>
31 #include "libsigrok.h"
32 #include "libsigrok-internal.h"
33
34 #define LOG_PREFIX "es51922"
35
36 /* Factors for the respective measurement mode (0 means "invalid"). */
37 static const float factors[8][8] = {
38         {1e-4,  1e-3,  1e-2,  1e-1, 1e-5, 0,    0,    0},    /* V */
39         {1e-8,  1e-7,  0,     0,    0,    0,    0,    0},    /* uA */
40         {1e-6,  1e-5,  0,     0,    0,    0,    0,    0},    /* mA */
41         {1e-3,  0,     0,     0,    0,    0,    0,    0},    /* 22A */
42         {1e-4,  1e-3,  1e-2,  1e-1, 1,    0,    0,    0},    /* Manual A */
43         {1e-2,  1e-1,  1,     1e1,  1e2,  1e3,  1e4,  0},    /* Resistance */
44         {1e-2,  1e-1,  0,     1,    1e1,  1e2,  1e3,  1e4},  /* Frequency */
45         {1e-12, 1e-11, 1e-10, 1e-9, 1e-8, 1e-7, 1e-6, 1e-5}, /* Capacitance */
46 };
47
48 static int parse_value(const uint8_t *buf, float *result)
49 {
50         int sign, intval;
51         float floatval;
52
53         /*
54          * Bytes 1-5: Value (5 decimal digits)
55          *
56          * Over limit: "0L." on the display, "22580" as protocol "digits".
57          *             (chip max. value is 22000, so 22580 is out of range)
58          *
59          * Example: "OL.", auto-range mega-ohm mode
60          *          Hex:   36  32 32 35 38 30  33 31 30 30 32 30 0d 0a
61          *          ASCII:      2  2  5  8  0
62          */
63         if (!strncmp((const char *)&buf[1], "22580", 5)) {
64                 sr_spew("Over limit.");
65                 *result = INFINITY;
66                 return SR_OK;
67         } else if (!isdigit(buf[1]) || !isdigit(buf[2]) ||
68                    !isdigit(buf[3]) || !isdigit(buf[4]) || !isdigit(buf[5])) {
69                 sr_err("Value contained invalid digits: %02x %02x %02x %02x "
70                        "%02x (%c %c %c %c %c).",
71                         buf[1], buf[2], buf[3], buf[4], buf[5]);
72                 return SR_ERR;
73         }
74         intval = 0;
75         intval += (buf[1] - '0') * 10000;
76         intval += (buf[2] - '0') * 1000;
77         intval += (buf[3] - '0') * 100;
78         intval += (buf[4] - '0') * 10;
79         intval += (buf[5] - '0') * 1;
80
81         floatval = (float)intval;
82
83         /* Note: The decimal point position will be parsed later. */
84
85         /* Byte 7: Sign bit (and other stuff) */
86         sign = ((buf[7] & (1 << 2)) != 0) ? -1 : 1;
87
88         /* Apply sign. */
89         floatval *= sign;
90
91         sr_spew("The display value is %f.", floatval);
92
93         *result = floatval;
94
95         return SR_OK;
96 }
97
98 static int parse_range(uint8_t b, float *floatval,
99                        const struct es51922_info *info)
100 {
101         int idx, mode;
102
103         idx = b - '0';
104
105         if (!(idx >= 0 && idx <= 7)) {
106                 sr_dbg("Invalid range byte / index: 0x%02x / 0x%02x.", b, idx);
107                 return SR_ERR;
108         }
109
110         /* Parse range byte (depends on the measurement mode). */
111         if (info->is_voltage)
112                 mode = 0; /* V */
113         else if (info->is_current && info->is_micro)
114                 mode = 1; /* uA */
115         else if (info->is_current && info->is_milli)
116                 mode = 2; /* mA */
117         else if (info->is_current && !info->is_micro && !info->is_milli)
118                 mode = 3; /* 22A */
119         else if (info->is_current && !info->is_auto)
120                 mode = 4; /* Manual A */
121         else if (info->is_resistance)
122                 mode = 5; /* Resistance */
123         else if (info->is_frequency)
124                 mode = 6; /* Frequency */
125         else if (info->is_capacitance)
126                 mode = 7; /* Capacitance */
127         else {
128                 sr_dbg("Invalid mode, range byte was: 0x%02x.", b);
129                 return SR_ERR;
130         }
131
132         if (factors[mode][idx] == 0) {
133                 sr_dbg("Invalid factor for range byte: 0x%02x.", b);
134                 return SR_ERR;
135         }
136
137         /* Apply respective factor (mode-dependent) on the value. */
138         *floatval *= factors[mode][idx];
139         sr_dbg("Applying factor %f, new value is %f.",
140                factors[mode][idx], *floatval);
141
142         return SR_OK;
143 }
144
145 static void parse_flags(const uint8_t *buf, struct es51922_info *info)
146 {
147         /* Get is_judge and is_vbar early on, we'll need it. */
148         info->is_judge = (buf[7] & (1 << 3)) != 0;
149         info->is_vbar = (buf[11] & (1 << 2)) != 0;
150
151         /* Byte 6: Function */
152         switch (buf[6]) {
153         case 0x3b: /* V */
154                 info->is_voltage = TRUE;
155                 break;
156         case 0x3d: /* uA */
157                 info->is_auto = info->is_micro = info->is_current = TRUE;
158                 break;
159         case 0x3f: /* mA */
160                 info->is_auto = info->is_milli = info->is_current = TRUE;
161                 break;
162         case 0x30: /* 22A */
163                 info->is_current = TRUE;
164                 break;
165         case 0x39: /* Manual A */
166                 info->is_auto = FALSE; /* Manual mode */
167                 info->is_current = TRUE;
168                 break;
169         case 0x33: /* Resistance */
170                 info->is_resistance = TRUE;
171                 break;
172         case 0x35: /* Continuity */
173                 info->is_continuity = TRUE;
174                 break;
175         case 0x31: /* Diode */
176                 info->is_diode = TRUE;
177                 break;
178         case 0x32: /* Frequency / duty cycle */
179                 if (info->is_judge)
180                         info->is_frequency = TRUE;
181                 else
182                         info->is_duty_cycle = TRUE;
183                 break;
184         case 0x36: /* Capacitance */
185                 info->is_capacitance = TRUE;
186                 break;
187         case 0x34: /* Temperature */
188                 info->is_temperature = TRUE;
189                 if (info->is_judge)
190                         info->is_celsius = TRUE;
191                 else
192                         info->is_fahrenheit = TRUE;
193                 /* IMPORTANT: The digits always represent Celsius! */
194                 break;
195         case 0x3e: /* ADP */
196                 info->is_adp = TRUE;
197                 break;
198         default:
199                 sr_err("Invalid function byte: 0x%02x.", buf[6]);
200                 break;
201         }
202
203         /* Byte 7: Status */
204         /* Bits [6:4]: Always 0b011 */
205         info->is_judge = (buf[7] & (1 << 3)) != 0;
206         info->is_sign  = (buf[7] & (1 << 2)) != 0;
207         info->is_batt  = (buf[7] & (1 << 1)) != 0; /* Battery low */
208         info->is_ol    = (buf[7] & (1 << 0)) != 0; /* Input overflow */
209
210         /* Byte 8: Option 1 */
211         /* Bits [6:4]: Always 0b011 */
212         info->is_max   = (buf[8] & (1 << 3)) != 0;
213         info->is_min   = (buf[8] & (1 << 2)) != 0;
214         info->is_rel   = (buf[8] & (1 << 1)) != 0;
215         info->is_rmr   = (buf[8] & (1 << 0)) != 0;
216
217         /* Byte 9: Option 2 */
218         /* Bits [6:4]: Always 0b011 */
219         info->is_ul    = (buf[9] & (1 << 3)) != 0;
220         info->is_pmax  = (buf[9] & (1 << 2)) != 0; /* Max. peak value */
221         info->is_pmin  = (buf[9] & (1 << 1)) != 0; /* Min. peak value */
222         /* Bit 0: Always 0 */
223
224         /* Byte 10: Option 3 */
225         /* Bits [6:4]: Always 0b011 */
226         info->is_dc    = (buf[10] & (1 << 3)) != 0;
227         info->is_ac    = (buf[10] & (1 << 2)) != 0;
228         info->is_auto  = (buf[10] & (1 << 1)) != 0;
229         info->is_vahz  = (buf[10] & (1 << 0)) != 0;
230
231         /* Byte 11: Option 4 */
232         /* Bits [6:3]: Always 0b0110 */
233         info->is_vbar  = (buf[11] & (1 << 2)) != 0;
234         info->is_hold  = (buf[11] & (1 << 1)) != 0;
235         info->is_lpf   = (buf[11] & (1 << 0)) != 0; /* Low pass filter on */
236
237         /* Byte 12: Always '\r' (carriage return, 0x0d, 13) */
238
239         /* Byte 13: Always '\n' (newline, 0x0a, 10) */
240 }
241
242 static void handle_flags(struct sr_datafeed_analog *analog,
243                          float *floatval, const struct es51922_info *info)
244 {
245         /*
246          * Note: is_micro etc. are not used directly to multiply/divide
247          * floatval, this is handled via parse_range() and factors[][].
248          */
249
250         /* Measurement modes */
251         if (info->is_voltage) {
252                 analog->mq = SR_MQ_VOLTAGE;
253                 analog->unit = SR_UNIT_VOLT;
254         }
255         if (info->is_current) {
256                 analog->mq = SR_MQ_CURRENT;
257                 analog->unit = SR_UNIT_AMPERE;
258         }
259         if (info->is_resistance) {
260                 analog->mq = SR_MQ_RESISTANCE;
261                 analog->unit = SR_UNIT_OHM;
262         }
263         if (info->is_frequency) {
264                 analog->mq = SR_MQ_FREQUENCY;
265                 analog->unit = SR_UNIT_HERTZ;
266         }
267         if (info->is_capacitance) {
268                 analog->mq = SR_MQ_CAPACITANCE;
269                 analog->unit = SR_UNIT_FARAD;
270         }
271         if (info->is_temperature && info->is_celsius) {
272                 analog->mq = SR_MQ_TEMPERATURE;
273                 analog->unit = SR_UNIT_CELSIUS;
274         }
275         if (info->is_temperature && info->is_fahrenheit) {
276                 analog->mq = SR_MQ_TEMPERATURE;
277                 analog->unit = SR_UNIT_FAHRENHEIT;
278         }
279         if (info->is_continuity) {
280                 analog->mq = SR_MQ_CONTINUITY;
281                 analog->unit = SR_UNIT_BOOLEAN;
282                 *floatval = (*floatval < 0.0) ? 0.0 : 1.0;
283         }
284         if (info->is_diode) {
285                 analog->mq = SR_MQ_VOLTAGE;
286                 analog->unit = SR_UNIT_VOLT;
287         }
288         if (info->is_duty_cycle) {
289                 analog->mq = SR_MQ_DUTY_CYCLE;
290                 analog->unit = SR_UNIT_PERCENTAGE;
291         }
292
293         /* Measurement related flags */
294         if (info->is_ac)
295                 analog->mqflags |= SR_MQFLAG_AC;
296         if (info->is_dc)
297                 analog->mqflags |= SR_MQFLAG_DC;
298         if (info->is_auto)
299                 analog->mqflags |= SR_MQFLAG_AUTORANGE;
300         if (info->is_diode)
301                 analog->mqflags |= SR_MQFLAG_DIODE;
302         if (info->is_hold)
303                 /*
304                 * Note: HOLD only affects the number displayed on the LCD,
305                 * but not the value sent via the protocol! It also does not
306                 * affect the bargraph on the LCD.
307                 */
308                 analog->mqflags |= SR_MQFLAG_HOLD;
309         if (info->is_max)
310                 analog->mqflags |= SR_MQFLAG_MAX;
311         if (info->is_min)
312                 analog->mqflags |= SR_MQFLAG_MIN;
313         if (info->is_rel)
314                 analog->mqflags |= SR_MQFLAG_RELATIVE;
315
316         /* Other flags */
317         if (info->is_judge)
318                 sr_spew("Judge bit is set.");
319         if (info->is_batt)
320                 sr_spew("Battery is low.");
321         if (info->is_ol)
322                 sr_spew("Input overflow.");
323         if (info->is_pmax)
324                 sr_spew("pMAX active, LCD shows max. peak value.");
325         if (info->is_pmin)
326                 sr_spew("pMIN active, LCD shows min. peak value.");
327         if (info->is_vahz)
328                 sr_spew("VAHZ active.");
329         if (info->is_vbar)
330                 sr_spew("VBAR active.");
331         if (info->is_lpf)
332                 sr_spew("Low-pass filter feature is active.");
333 }
334
335 static gboolean flags_valid(const struct es51922_info *info)
336 {
337         int count;
338
339         /* Does the packet have more than one multiplier? */
340         count = 0;
341         count += (info->is_nano) ? 1 : 0;
342         count += (info->is_micro) ? 1 : 0;
343         count += (info->is_milli) ? 1 : 0;
344         /* Note: No 'kilo' or 'mega' bits per se in this protocol. */
345         if (count > 1) {
346                 sr_err("More than one multiplier detected in packet.");
347                 return FALSE;
348         }
349
350         /* Does the packet "measure" more than one type of value? */
351         count = 0;
352         count += (info->is_voltage) ? 1 : 0;
353         count += (info->is_current) ? 1 : 0;
354         count += (info->is_resistance) ? 1 : 0;
355         count += (info->is_frequency) ? 1 : 0;
356         count += (info->is_capacitance) ? 1 : 0;
357         count += (info->is_temperature) ? 1 : 0;
358         count += (info->is_continuity) ? 1 : 0;
359         count += (info->is_diode) ? 1 : 0;
360         count += (info->is_duty_cycle) ? 1 : 0;
361         if (count > 1) {
362                 sr_err("More than one measurement type detected in packet.");
363                 return FALSE;
364         }
365
366         /* Both AC and DC set? */
367         if (info->is_ac && info->is_dc) {
368                 sr_err("Both AC and DC flags detected in packet.");
369                 return FALSE;
370         }
371
372         return TRUE;
373 }
374
375 SR_PRIV gboolean sr_es51922_packet_valid(const uint8_t *buf)
376 {
377         struct es51922_info info;
378
379         memset(&info, 0x00, sizeof(struct es51922_info));
380         parse_flags(buf, &info);
381
382         if (!flags_valid(&info))
383                 return FALSE;
384
385         if (buf[12] != '\r' || buf[13] != '\n') {
386                 sr_spew("Packet doesn't end with \\r\\n.");
387                 return FALSE;
388         }
389
390         return TRUE;
391 }
392
393 /**
394  * Parse a protocol packet.
395  *
396  * @param buf Buffer containing the protocol packet. Must not be NULL.
397  * @param floatval Pointer to a float variable. That variable will contain the
398  *                 result value upon parsing success. Must not be NULL.
399  * @param analog Pointer to a struct sr_datafeed_analog. The struct will be
400  *               filled with data according to the protocol packet.
401  *               Must not be NULL.
402  * @param info Pointer to a struct es51922_info. The struct will be filled
403  *             with data according to the protocol packet. Must not be NULL.
404  *
405  * @return SR_OK upon success, SR_ERR upon failure. Upon errors, the
406  *         'analog' variable contents are undefined and should not be used.
407  */
408 SR_PRIV int sr_es51922_parse(const uint8_t *buf, float *floatval,
409                              struct sr_datafeed_analog *analog, void *info)
410 {
411         int ret;
412         struct es51922_info *info_local;
413
414         info_local = (struct es51922_info *)info;
415
416         if ((ret = parse_value(buf, floatval)) != SR_OK) {
417                 sr_err("Error parsing value: %d.", ret);
418                 return ret;
419         }
420
421         memset(info_local, 0x00, sizeof(struct es51922_info));
422         parse_flags(buf, info_local);
423         handle_flags(analog, floatval, info_local);
424
425         return parse_range(buf[0], floatval, info_local);
426 }