--- /dev/null
+/*
+ * 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;
+}
SR_PRIV struct sr_dev_driver iso_tech_idm103n_driver_info;
SR_PRIV struct sr_dev_driver tenma_72_7745_ser_driver_info;
SR_PRIV struct sr_dev_driver tenma_72_7750_ser_driver_info;
+SR_PRIV struct sr_dev_driver brymen_bm25x_ser_driver_info;
SR_PRIV struct dmm_info dmms[] = {
{
NULL,
&tenma_72_7750_ser_driver_info, receive_data_TENMA_72_7750_SER,
},
+ {
+ "Brymen", "BM25x (BC20X cable)", "9600/8n1/rts=1/dtr=1",
+ 9600, BRYMEN_BM25X_PACKET_SIZE, 0, 0, NULL,
+ sr_brymen_bm25x_packet_valid, sr_brymen_bm25x_parse,
+ NULL,
+ &brymen_bm25x_ser_driver_info, receive_data_BRYMEN_BM25X_SER,
+ },
};
static int dev_clear(int dmm)
DRV(iso_tech_idm103n, ISO_TECH_IDM103N, "iso-tech-idm103n", "ISO-TECH IDM103N")
DRV(tenma_72_7745_ser, TENMA_72_7745_SER, "tenma-72-7745-ser", "Tenma 72-7745 (UT-D02 cable)")
DRV(tenma_72_7750_ser, TENMA_72_7750_SER, "tenma-72-7750-ser", "Tenma 72-7750 (UT-D02 cable)")
+DRV(brymen_bm25x_ser, BRYMEN_BM25X_SER, "brymen-bm25x-ser", "Brymen BM25x (BC20X cable)")