serial-dmm: add support for the Brymen BM820s family

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.
This commit is contained in:
Gerhard Sittig 2020-09-30 09:59:32 +02:00
parent 0931639a12
commit 6bee394dee
3 changed files with 52 additions and 12 deletions

View File

@ -27,6 +27,16 @@
* 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" :

View 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(

View 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);