]> sigrok.org Git - libsigrok.git/blobdiff - src/dmm/bm25x.c
Implement Brymen BM25x series as a serial DMM.
[libsigrok.git] / src / dmm / bm25x.c
diff --git a/src/dmm/bm25x.c b/src/dmm/bm25x.c
new file mode 100644 (file)
index 0000000..67c2d04
--- /dev/null
@@ -0,0 +1,214 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2014 Janne Huttunen <jahuttun@gmail.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/>.
+ */
+
+/**
+ * @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(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: 0x%02x.", 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_spew("Multiple decimal points found!");
+                       return -1;
+               }
+               p = i;
+       }
+
+       return p;
+}
+
+static float scale_value(float val, int point, int digits)
+{
+       int pos;
+
+       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;
+}