serial-dmm: add support for the Brymen BM820s family
authorGerhard Sittig <gerhard.sittig@gmx.net>
Wed, 30 Sep 2020 07:59:32 +0000 (09:59 +0200)
committerGerhard Sittig <gerhard.sittig@gmx.net>
Thu, 22 Oct 2020 19:25:34 +0000 (21:25 +0200)
The BM820s series uses the same 10000 counts protocol as BM520s does,
but lacks the capability of recording measurements. Re-use the bm52x
DMM packet parser, but don't register the config get/set/list and
acquisition start callbacks.

It turns out that the packet request and packet validity check routines
need to be individual, since 0x82 is used instead of 0x52 as a magic
number in some places. Fortunately the complex payload parser is shared
among BM520s and BM820s series. This was tested with a BM829s meter.

src/dmm/bm52x.c
src/hardware/serial-dmm/api.c
src/libsigrok-internal.h

index 728b70a690308e6cabb662cbf35dbddf4df67a06..69c017194aa0ebc31983bb9f3d29092f1b663039 100644 (file)
  * http://brymen.com/product-html/PD02BM520s_protocolDL.html
  * http://brymen.com/product-html/images/DownloadList/ProtocolList/BM520-BM520s_List/BM520-BM520s-10000-count-professional-dual-display-mobile-logging-DMMs-protocol.zip
  *
+ * This parser was initially created for BM520s devices and tested with
+ * BM525s. The Brymen BM820s family of devices uses the same protocol,
+ * with just 0x82 instead of 0x52 in request packets and in the fixed
+ * fields of the responses. Which means that the packet parser can get
+ * shared among the BM520s and BM820s devices, but validity check needs
+ * to be individual, and the "wrong" packet request will end up without
+ * a response. Compared to BM520s the BM820s has dBm (in the protocol)
+ * and NCV (not seen in the protocol) and is non-logging (live only).
+ * BM820s support was tested with BM829s.
+ *
  * The parser implementation was tested with a Brymen BM525s meter. Some
  * of the responses differ from the vendor's documentation:
  * - Recording session total byte counts don't start after the byte count
@@ -45,7 +55,6 @@
  *   - AVG is not available in BM525s and BM521s.
  *   - LoZ, eliminating ghost voltages.
  *   - LPF, low pass filter.
- *   - dBm is a BM829s feature only, not available in BM525s.
  *   - low battery, emits sr_warn() but isn't seen in the feed.
  *   - @, 4-20mA loop, % (main display, left hand side), Hi/Lo. Some of
  *     these are in the vendor's documentation for the DMM packet but not
@@ -64,8 +73,6 @@
  *   the full byte stream is necessary on one hand since random access
  *   is not available, and useful on the other hand for consistency
  *   checks.
- * - The vendor's shipping box and user manual suggests a similarity of
- *   BM520s and BM820s meters. Can this DMM packet parser support both?
  */
 
 #include <config.h>
@@ -102,7 +109,8 @@ struct brymen_bm52x_state {
 };
 
 enum bm52x_reqtype {
-       REQ_LIVE_READ,
+       REQ_LIVE_READ_520,
+       REQ_LIVE_READ_820,
        REQ_REC_HEAD,
        REQ_REC_NEXT,
        REQ_REC_CURR,
@@ -111,17 +119,19 @@ enum bm52x_reqtype {
 #ifdef HAVE_SERIAL_COMM
 static int bm52x_send_req(struct sr_serial_dev_inst *serial, enum bm52x_reqtype t)
 {
-       static const uint8_t req_live[] = { 0x00, 0x00, 0x52, 0x66, };
+       static const uint8_t req_live_520[] = { 0x00, 0x00, 0x52, 0x66, };
+       static const uint8_t req_live_820[] = { 0x00, 0x00, 0x82, 0x66, };
        static const uint8_t req_head[] = { 0x00, 0x00, 0x52, 0x88, };
        static const uint8_t req_next[] = { 0x00, 0x00, 0x52, 0x89, };
        static const uint8_t req_curr[] = { 0x00, 0x00, 0x52, 0x8a, };
        static const uint8_t *req_bytes[] = {
-               [REQ_LIVE_READ] = req_live,
+               [REQ_LIVE_READ_520] = req_live_520,
+               [REQ_LIVE_READ_820] = req_live_820,
                [REQ_REC_HEAD] = req_head,
                [REQ_REC_NEXT] = req_next,
                [REQ_REC_CURR] = req_curr,
        };
-       static const size_t req_len = ARRAY_SIZE(req_live);
+       static const size_t req_len = ARRAY_SIZE(req_live_520);
 
        const uint8_t *p;
        size_t l;
@@ -142,7 +152,12 @@ static int bm52x_send_req(struct sr_serial_dev_inst *serial, enum bm52x_reqtype
 
 SR_PRIV int sr_brymen_bm52x_packet_request(struct sr_serial_dev_inst *serial)
 {
-       return bm52x_send_req(serial, REQ_LIVE_READ);
+       return bm52x_send_req(serial, REQ_LIVE_READ_520);
+}
+
+SR_PRIV int sr_brymen_bm82x_packet_request(struct sr_serial_dev_inst *serial)
+{
+       return bm52x_send_req(serial, REQ_LIVE_READ_820);
 }
 #endif
 
@@ -167,6 +182,20 @@ SR_PRIV gboolean sr_brymen_bm52x_packet_valid(const uint8_t *buf)
        return TRUE;
 }
 
+SR_PRIV gboolean sr_brymen_bm82x_packet_valid(const uint8_t *buf)
+{
+       if (buf[16] != 0x82)
+               return FALSE;
+       if (buf[17] != 0x82)
+               return FALSE;
+       if (buf[18] != 0x82)
+               return FALSE;
+       if (buf[19] != 0x82)
+               return FALSE;
+
+       return TRUE;
+}
+
 /*
  * Data bytes in the DMM packet encode LCD segments in an unusual order
  * (bgcpafed) and in an unusual position (bit 4 being the decimal point
@@ -628,7 +657,7 @@ static int bm52x_rec_next_rsp(struct sr_serial_dev_inst *serial,
        int ret;
 
        /* Seed internal state when sending the HEAD request. */
-       if (req == REQ_REC_HEAD || req == REQ_LIVE_READ)
+       if (req == REQ_REC_HEAD || req == REQ_LIVE_READ_520)
                memset(&state->rsp, 0, sizeof(state->rsp));
 
        /* Move unprocessed content to the front. */
@@ -647,7 +676,7 @@ static int bm52x_rec_next_rsp(struct sr_serial_dev_inst *serial,
 
        /* Add another response chunk to the read buffer. */
        b = &state->rsp.buff[state->rsp.fill_pos];
-       l = req == REQ_LIVE_READ ? 24 : 32;
+       l = req == REQ_LIVE_READ_520 ? 24 : 32;
        if (sizeof(state->rsp.buff) - state->rsp.fill_pos < l)
                return SR_ERR_BUG;
        ret = bm52x_send_req(serial, req);
@@ -665,7 +694,7 @@ static int bm52x_rec_next_rsp(struct sr_serial_dev_inst *serial,
                GString *text;
                const char *req_text;
 
-               req_text = (req == REQ_LIVE_READ) ? "LIVE" :
+               req_text = (req == REQ_LIVE_READ_520) ? "LIVE" :
                        (req == REQ_REC_HEAD) ? "MEM HEAD" :
                        (req == REQ_REC_NEXT) ? "MEM NEXT" :
                        (req == REQ_REC_CURR) ? "MEM CURR" :
index 1b9a810b4a91378951540ec2eeb8e5168861400c..9e050af7706a598fdd00717b39dcd84d1c251b89 100644 (file)
@@ -127,7 +127,8 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
         * happen to provide them. (This is a compromise to do it here,
         * and not extend the DMM_CONN() et al set of macros.)
         */
-       if (dmm->packet_parse == sr_brymen_bm52x_parse) {
+       if (strcmp(dmm->di.name, "brymen-bm52x") == 0) {
+               /* Applicable to BM520s but not to BM820s. */
                dmm->dmm_state_init = brymen_bm52x_state_init;
                dmm->dmm_state_free = brymen_bm52x_state_free;
                dmm->config_get = brymen_bm52x_config_get;
@@ -379,6 +380,13 @@ SR_REGISTER_DEV_DRIVER_LIST(serial_dmm_drivers,
                sr_brymen_bm52x_packet_valid, sr_brymen_bm52x_parse,
                NULL
        ),
+       DMM_CONN(
+               "brymen-bm82x", brymen_bm52x, "Brymen", "BM82x",
+               "hid/bu86x", NULL, BRYMEN_BM52X_PACKET_SIZE, 4000, 500,
+               sr_brymen_bm82x_packet_request,
+               sr_brymen_bm82x_packet_valid, sr_brymen_bm52x_parse,
+               NULL
+       ),
        /* }}} */
        /* bm85x based meters {{{ */
        DMM_LEN(
index 6a85d694453edce239e5411fd6c8736622b2321f..9a672c7dbf97a5402a774d73dd06e5e0eca4071d 100644 (file)
@@ -2344,8 +2344,11 @@ struct brymen_bm52x_info { size_t ch_idx; };
 
 #ifdef HAVE_SERIAL_COMM
 SR_PRIV int sr_brymen_bm52x_packet_request(struct sr_serial_dev_inst *serial);
+SR_PRIV int sr_brymen_bm82x_packet_request(struct sr_serial_dev_inst *serial);
 #endif
 SR_PRIV gboolean sr_brymen_bm52x_packet_valid(const uint8_t *buf);
+SR_PRIV gboolean sr_brymen_bm82x_packet_valid(const uint8_t *buf);
+/* BM520s and BM820s protocols are similar, the parse routine is shared. */
 SR_PRIV int sr_brymen_bm52x_parse(const uint8_t *buf, float *floatval,
                struct sr_datafeed_analog *analog, void *info);