brymen-dmm: Add support for Brymen BM857
This patch might also work for a number of other Brymen models -- 859(a), 867, 869--- including their respective rebadges from Greenlee, Extech, and Amprobe. Signed-off-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
This commit is contained in:
parent
20cbc7854d
commit
601fb67cc4
|
@ -24,6 +24,7 @@ noinst_LTLIBRARIES = libsigrok_hw_brymen_dmm.la
|
|||
|
||||
libsigrok_hw_brymen_dmm_la_SOURCES = \
|
||||
api.c \
|
||||
parser.c \
|
||||
protocol.c \
|
||||
protocol.h
|
||||
|
||||
|
|
|
@ -18,42 +18,31 @@
|
|||
*/
|
||||
|
||||
#include <glib.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include "libsigrok.h"
|
||||
#include "libsigrok-internal.h"
|
||||
#include "protocol.h"
|
||||
|
||||
static const int hwopts[] = {
|
||||
SR_CONF_CONN,
|
||||
SR_CONF_SERIALCOMM,
|
||||
0,
|
||||
};
|
||||
|
||||
static const int hwcaps[] = {
|
||||
SR_CONF_MULTIMETER,
|
||||
SR_CONF_LIMIT_SAMPLES,
|
||||
SR_CONF_CONTINUOUS,
|
||||
SR_CONF_LIMIT_MSEC,
|
||||
0,
|
||||
};
|
||||
|
||||
SR_PRIV struct sr_dev_driver brymen_dmm_driver_info;
|
||||
static struct sr_dev_driver *di = &brymen_dmm_driver_info;
|
||||
|
||||
/* Properly close and free all devices. */
|
||||
static int clear_instances(void)
|
||||
{
|
||||
struct sr_dev_inst *sdi;
|
||||
struct drv_context *drvc;
|
||||
struct dev_context *devc;
|
||||
GSList *l;
|
||||
|
||||
if (!(drvc = di->priv))
|
||||
return SR_OK;
|
||||
|
||||
for (l = drvc->instances; l; l = l->next) {
|
||||
if (!(sdi = l->data))
|
||||
continue;
|
||||
if (!(devc = sdi->priv))
|
||||
continue;
|
||||
|
||||
/* TODO */
|
||||
|
||||
sr_dev_inst_free(sdi);
|
||||
}
|
||||
|
||||
g_slist_free(drvc->instances);
|
||||
drvc->instances = NULL;
|
||||
|
||||
return SR_OK;
|
||||
}
|
||||
|
||||
static int hw_init(void)
|
||||
static int hw_init(struct sr_context *sr_ctx)
|
||||
{
|
||||
struct drv_context *drvc;
|
||||
|
||||
|
@ -62,17 +51,107 @@ static int hw_init(void)
|
|||
return SR_ERR_MALLOC;
|
||||
}
|
||||
|
||||
/* TODO */
|
||||
|
||||
drvc->sr_ctx = sr_ctx;
|
||||
di->priv = drvc;
|
||||
|
||||
return SR_OK;
|
||||
}
|
||||
|
||||
static void free_instance(void *inst)
|
||||
{
|
||||
struct sr_dev_inst *sdi;
|
||||
struct dev_context *devc;
|
||||
if (!(sdi = inst))
|
||||
return;
|
||||
if (!(devc = sdi->priv))
|
||||
return;
|
||||
sr_serial_dev_inst_free(devc->serial);
|
||||
sr_dev_inst_free(sdi);
|
||||
}
|
||||
|
||||
/* Properly close and free all devices. */
|
||||
static int clear_instances(void)
|
||||
{
|
||||
struct drv_context *drvc;
|
||||
|
||||
if (!(drvc = di->priv))
|
||||
return SR_OK;
|
||||
|
||||
g_slist_free_full(drvc->instances, free_instance);
|
||||
drvc->instances = NULL;
|
||||
|
||||
return SR_OK;
|
||||
}
|
||||
|
||||
static GSList *brymen_scan(const char *conn, const char *serialcomm)
|
||||
{
|
||||
struct sr_dev_inst *sdi;
|
||||
struct dev_context *devc;
|
||||
struct drv_context *drvc;
|
||||
struct sr_probe *probe;
|
||||
struct sr_serial_dev_inst *serial;
|
||||
GSList *devices;
|
||||
int ret;
|
||||
|
||||
if (!(serial = sr_serial_dev_inst_new(conn, serialcomm)))
|
||||
return NULL;
|
||||
|
||||
if (serial_open(serial, SERIAL_RDWR|SERIAL_NONBLOCK) != SR_OK)
|
||||
return NULL;
|
||||
|
||||
sr_info("Probing port %s.", conn);
|
||||
|
||||
devices = NULL;
|
||||
|
||||
/* Request reading */
|
||||
if (brymen_packet_request(serial) == -1) {
|
||||
sr_err("Unable to send command. code: %d.", errno);
|
||||
goto scan_cleanup;
|
||||
}
|
||||
|
||||
uint8_t buf[128];
|
||||
size_t len = 128;
|
||||
|
||||
ret = brymen_stream_detect(serial, buf, &len, brymen_packet_length,
|
||||
brymen_packet_is_valid, 1000, 9600);
|
||||
if (ret != SR_OK)
|
||||
goto scan_cleanup;
|
||||
|
||||
sr_info("Found device on port %s.", conn);
|
||||
|
||||
if (!(sdi = sr_dev_inst_new(0, SR_ST_INACTIVE, "Brymen", "BM85x", "")))
|
||||
goto scan_cleanup;
|
||||
|
||||
if (!(devc = g_try_malloc0(sizeof(struct dev_context)))) {
|
||||
sr_err("Device context malloc failed.");
|
||||
goto scan_cleanup;
|
||||
}
|
||||
|
||||
devc->serial = serial;
|
||||
drvc = di->priv;
|
||||
sdi->priv = devc;
|
||||
sdi->driver = di;
|
||||
|
||||
if (!(probe = sr_probe_new(0, SR_PROBE_ANALOG, TRUE, "P1")))
|
||||
goto scan_cleanup;
|
||||
|
||||
sdi->probes = g_slist_append(sdi->probes, probe);
|
||||
drvc->instances = g_slist_append(drvc->instances, sdi);
|
||||
devices = g_slist_append(devices, sdi);
|
||||
|
||||
|
||||
scan_cleanup:
|
||||
serial_close(serial);
|
||||
|
||||
return devices;
|
||||
}
|
||||
|
||||
static GSList *hw_scan(GSList *options)
|
||||
{
|
||||
struct drv_context *drvc;
|
||||
GSList *devices;
|
||||
struct sr_config *src;
|
||||
GSList *devices, *l;
|
||||
const char *conn, *serialcomm;
|
||||
|
||||
(void)options;
|
||||
|
||||
|
@ -80,7 +159,29 @@ static GSList *hw_scan(GSList *options)
|
|||
drvc = di->priv;
|
||||
drvc->instances = NULL;
|
||||
|
||||
/* TODO */
|
||||
conn = serialcomm = NULL;
|
||||
for (l = options; l; l = l->next) {
|
||||
src = l->data;
|
||||
switch (src->key) {
|
||||
case SR_CONF_CONN:
|
||||
conn = src->value;
|
||||
break;
|
||||
case SR_CONF_SERIALCOMM:
|
||||
serialcomm = src->value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!conn) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (serialcomm) {
|
||||
/* Use the provided comm specs. */
|
||||
devices = brymen_scan(conn, serialcomm);
|
||||
} else {
|
||||
/* But 9600 8N1 should work all of the time */
|
||||
devices = brymen_scan(conn, "9600/8n1/dtr=1/rts=1");
|
||||
}
|
||||
|
||||
return devices;
|
||||
}
|
||||
|
@ -96,14 +197,34 @@ static GSList *hw_dev_list(void)
|
|||
|
||||
static int hw_dev_open(struct sr_dev_inst *sdi)
|
||||
{
|
||||
/* TODO */
|
||||
struct dev_context *devc;
|
||||
|
||||
if (!(devc = sdi->priv)) {
|
||||
sr_err("sdi->priv was NULL.");
|
||||
return SR_ERR_BUG;
|
||||
}
|
||||
|
||||
if (serial_open(devc->serial, SERIAL_RDWR | SERIAL_NONBLOCK) != SR_OK)
|
||||
return SR_ERR;
|
||||
|
||||
sdi->status = SR_ST_ACTIVE;
|
||||
|
||||
return SR_OK;
|
||||
}
|
||||
|
||||
static int hw_dev_close(struct sr_dev_inst *sdi)
|
||||
{
|
||||
/* TODO */
|
||||
struct dev_context *devc;
|
||||
|
||||
if (!(devc = sdi->priv)) {
|
||||
sr_err("sdi->priv was NULL.");
|
||||
return SR_ERR_BUG;
|
||||
}
|
||||
|
||||
if (devc->serial && devc->serial->fd != -1) {
|
||||
serial_close(devc->serial);
|
||||
sdi->status = SR_ST_INACTIVE;
|
||||
}
|
||||
|
||||
return SR_OK;
|
||||
}
|
||||
|
@ -112,27 +233,33 @@ static int hw_cleanup(void)
|
|||
{
|
||||
clear_instances();
|
||||
|
||||
/* TODO */
|
||||
|
||||
return SR_OK;
|
||||
}
|
||||
|
||||
static int hw_info_get(int info_id, const void **data,
|
||||
static int config_list(int key, const void **data,
|
||||
const struct sr_dev_inst *sdi)
|
||||
{
|
||||
switch (info_id) {
|
||||
/* TODO */
|
||||
(void)sdi;
|
||||
|
||||
switch (key) {
|
||||
case SR_CONF_SCAN_OPTIONS:
|
||||
*data = hwopts;
|
||||
break;
|
||||
case SR_CONF_DEVICE_OPTIONS:
|
||||
*data = hwcaps;
|
||||
break;
|
||||
default:
|
||||
sr_err("Unknown info_id: %d.", info_id);
|
||||
sr_err("Unknown config key: %d.", key);
|
||||
return SR_ERR_ARG;
|
||||
}
|
||||
|
||||
return SR_OK;
|
||||
}
|
||||
|
||||
static int hw_dev_config_set(const struct sr_dev_inst *sdi, int hwcap,
|
||||
const void *value)
|
||||
static int hw_dev_config_set(int id, const void *value,
|
||||
const struct sr_dev_inst *sdi)
|
||||
{
|
||||
struct dev_context *devc;
|
||||
int ret;
|
||||
|
||||
if (sdi->status != SR_ST_ACTIVE) {
|
||||
|
@ -140,11 +267,21 @@ static int hw_dev_config_set(const struct sr_dev_inst *sdi, int hwcap,
|
|||
return SR_ERR;
|
||||
}
|
||||
|
||||
if (!(devc = sdi->priv)) {
|
||||
sr_err("sdi->priv was NULL.");
|
||||
return SR_ERR_BUG;
|
||||
}
|
||||
|
||||
ret = SR_OK;
|
||||
switch (hwcap) {
|
||||
/* TODO */
|
||||
switch (id) {
|
||||
case SR_CONF_LIMIT_SAMPLES:
|
||||
devc->limit_samples = *(const uint64_t*)value;
|
||||
break;
|
||||
case SR_CONF_LIMIT_MSEC:
|
||||
devc->limit_msec = *(const uint64_t*)value;
|
||||
break;
|
||||
default:
|
||||
sr_err("Unknown hardware capability: %d.", hwcap);
|
||||
sr_err("Unknown hardware capability: %d.", id);
|
||||
ret = SR_ERR_ARG;
|
||||
}
|
||||
|
||||
|
@ -154,29 +291,73 @@ static int hw_dev_config_set(const struct sr_dev_inst *sdi, int hwcap,
|
|||
static int hw_dev_acquisition_start(const struct sr_dev_inst *sdi,
|
||||
void *cb_data)
|
||||
{
|
||||
/* TODO */
|
||||
struct sr_datafeed_packet packet;
|
||||
struct sr_datafeed_header header;
|
||||
struct dev_context *devc;
|
||||
|
||||
if (!(devc = sdi->priv)) {
|
||||
sr_err("sdi->priv was NULL.");
|
||||
return SR_ERR_BUG;
|
||||
}
|
||||
|
||||
sr_dbg("Starting acquisition.");
|
||||
|
||||
devc->cb_data = cb_data;
|
||||
|
||||
/*
|
||||
* Reset the number of samples to take. If we've already collected our
|
||||
* quota, but we start a new session, and don't reset this, we'll just
|
||||
* quit without acquiring any new samples.
|
||||
*/
|
||||
devc->num_samples = 0;
|
||||
devc->starttime = g_get_monotonic_time();
|
||||
|
||||
/* Send header packet to the session bus. */
|
||||
sr_dbg("Sending SR_DF_HEADER.");
|
||||
packet.type = SR_DF_HEADER;
|
||||
packet.payload = &header;
|
||||
header.feed_version = 1;
|
||||
gettimeofday(&header.starttime, NULL);
|
||||
sr_session_send(devc->cb_data, &packet);
|
||||
|
||||
/* Poll every 50ms, or whenever some data comes in. */
|
||||
sr_source_add(devc->serial->fd, G_IO_IN, 50,
|
||||
brymen_dmm_receive_data, (void *)sdi);
|
||||
|
||||
return SR_OK;
|
||||
}
|
||||
|
||||
static int hw_dev_acquisition_stop(const struct sr_dev_inst *sdi,
|
||||
void *cb_data)
|
||||
static int hw_dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
|
||||
{
|
||||
(void)cb_data;
|
||||
struct sr_datafeed_packet packet;
|
||||
struct dev_context *devc;
|
||||
|
||||
if (sdi->status != SR_ST_ACTIVE) {
|
||||
sr_err("Device inactive, can't stop acquisition.");
|
||||
return SR_ERR;
|
||||
}
|
||||
|
||||
/* TODO */
|
||||
if (!(devc = sdi->priv)) {
|
||||
sr_err("sdi->priv was NULL.");
|
||||
return SR_ERR_BUG;
|
||||
}
|
||||
|
||||
sr_dbg("Stopping acquisition.");
|
||||
|
||||
sr_source_remove(devc->serial->fd);
|
||||
hw_dev_close((struct sr_dev_inst *)sdi);
|
||||
|
||||
/* Send end packet to the session bus. */
|
||||
sr_dbg("Sending SR_DF_END.");
|
||||
packet.type = SR_DF_END;
|
||||
sr_session_send(cb_data, &packet);
|
||||
|
||||
return SR_OK;
|
||||
}
|
||||
|
||||
SR_PRIV struct sr_dev_driver brymen_dmm_driver_info = {
|
||||
.name = "brymen-dmm",
|
||||
.longname = "brymen-dmm",
|
||||
.longname = "Brymen BM850 series",
|
||||
.api_version = 1,
|
||||
.init = hw_init,
|
||||
.cleanup = hw_cleanup,
|
||||
|
@ -185,8 +366,8 @@ SR_PRIV struct sr_dev_driver brymen_dmm_driver_info = {
|
|||
.dev_clear = clear_instances,
|
||||
.dev_open = hw_dev_open,
|
||||
.dev_close = hw_dev_close,
|
||||
.info_get = hw_info_get,
|
||||
.dev_config_set = hw_dev_config_set,
|
||||
.config_list = config_list,
|
||||
.config_set = hw_dev_config_set,
|
||||
.dev_acquisition_start = hw_dev_acquisition_start,
|
||||
.dev_acquisition_stop = hw_dev_acquisition_stop,
|
||||
.priv = NULL,
|
||||
|
|
|
@ -0,0 +1,279 @@
|
|||
/*
|
||||
* This file is part of the sigrok project.
|
||||
*
|
||||
* Copyright (C) 2012 Alexandru Gagniuc <mr.nuke.me@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/>.
|
||||
*/
|
||||
|
||||
#include "protocol.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
/*
|
||||
* Flags passed from the DMM.
|
||||
*/
|
||||
struct brymen_flags {
|
||||
gboolean low_batt;
|
||||
gboolean decibel, duty_cycle, hertz, amp, beep, ohm, fahrenheit;
|
||||
gboolean celsius, capacitance, diode, volt, dc, ac;
|
||||
};
|
||||
|
||||
struct bm850_command {
|
||||
uint8_t dle;
|
||||
uint8_t stx;
|
||||
uint8_t cmd;
|
||||
uint8_t arg[2];
|
||||
uint8_t checksum;
|
||||
uint8_t dle2;
|
||||
uint8_t etx;
|
||||
};
|
||||
|
||||
struct brymen_header {
|
||||
uint8_t dle;
|
||||
uint8_t stx;
|
||||
uint8_t cmd;
|
||||
uint8_t len;
|
||||
};
|
||||
|
||||
struct brymen_tail {
|
||||
uint8_t checksum;
|
||||
uint8_t dle;
|
||||
uint8_t etx;
|
||||
};
|
||||
|
||||
/*
|
||||
* We only have one command because we only support the BM-857. However, the
|
||||
* driver is easily extensible to support more models, as the protocols are very
|
||||
* similar.
|
||||
*/
|
||||
enum {
|
||||
BM_CMD_REQUEST_READING = 0x00,
|
||||
};
|
||||
|
||||
|
||||
static int bm_send_command(uint8_t command, uint8_t arg1, uint8_t arg2,
|
||||
struct sr_serial_dev_inst *serial)
|
||||
{
|
||||
struct bm850_command cmdout = {
|
||||
.dle = 0x10, .stx = 0x02,
|
||||
.cmd = command,
|
||||
.arg = {arg1, arg2},
|
||||
.checksum = arg1^arg2, .dle2 = 0x10, .etx = 0x03};
|
||||
int written;
|
||||
|
||||
/* TODO: How do we compute the checksum? Hardware seems to ignore it */
|
||||
|
||||
/* Request reading */
|
||||
written = serial_write(serial, &cmdout, sizeof(cmdout));
|
||||
if(written != sizeof(cmdout))
|
||||
return SR_ERR;
|
||||
|
||||
return SR_OK;
|
||||
}
|
||||
|
||||
SR_PRIV int brymen_packet_request(struct sr_serial_dev_inst *serial)
|
||||
{
|
||||
return bm_send_command(BM_CMD_REQUEST_READING, 0, 0, serial);
|
||||
}
|
||||
|
||||
SR_PRIV int brymen_packet_length(const uint8_t *buf, int *len)
|
||||
{
|
||||
struct brymen_header *hdr;
|
||||
const int brymen_max_packet_len = 22;
|
||||
int packet_len;
|
||||
const size_t buflen = *len;
|
||||
|
||||
hdr = (void*)buf;
|
||||
|
||||
/* Did we receive a complete header yet? */
|
||||
if (buflen < sizeof(*hdr) )
|
||||
return PACKET_NEED_MORE_DATA;
|
||||
|
||||
if (hdr->dle != 0x10 || hdr->stx != 0x02)
|
||||
return PACKET_INVALID_HEADER;
|
||||
|
||||
/* Our packet includes the header, the payload, and the tail */
|
||||
packet_len = sizeof(*hdr) + hdr->len + sizeof(struct brymen_tail);
|
||||
|
||||
/* In case we pick up an invalid header, limit our search */
|
||||
if (packet_len > brymen_max_packet_len) {
|
||||
sr_spew("Header specifies an invalid payload length: %i.",
|
||||
hdr->len);
|
||||
return PACKET_INVALID_HEADER;
|
||||
}
|
||||
|
||||
*len = packet_len;
|
||||
sr_spew("Expecting a %d-byte packet.", *len);
|
||||
return PACKET_HEADER_OK;
|
||||
}
|
||||
|
||||
SR_PRIV gboolean brymen_packet_is_valid(const uint8_t *buf)
|
||||
{
|
||||
struct brymen_header *hdr;
|
||||
struct brymen_tail *tail;
|
||||
int i;
|
||||
uint8_t chksum = 0;
|
||||
const uint8_t *payload = buf + sizeof(struct brymen_header);
|
||||
|
||||
hdr = (void*)buf;
|
||||
tail = (void*)(payload + hdr->len);
|
||||
|
||||
for (i = 0; i< hdr->len; i++)
|
||||
chksum ^= payload[i];
|
||||
|
||||
if (tail->checksum != chksum) {
|
||||
sr_dbg("Packet has invalid checksum 0x%.2x. Expected 0x%.2x",
|
||||
chksum, tail->checksum);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static int parse_value(const char *strbuf, const int len, float *floatval)
|
||||
{
|
||||
int s, d;
|
||||
char str[32];
|
||||
|
||||
if (strstr(strbuf, "OL")) {
|
||||
sr_dbg("Overlimit.");
|
||||
*floatval = INFINITY;
|
||||
return SR_OK;
|
||||
}
|
||||
|
||||
memset(str, 0, sizeof(str));
|
||||
/* Spaces may interfere with strtod parsing the exponent. Strip them */
|
||||
for (s = 0, d = 0; s < len; s++)
|
||||
if (strbuf[s] != ' ')
|
||||
str[d++] = strbuf[s];
|
||||
/* YES, it's that simple !*/
|
||||
*floatval = strtod(str, NULL);
|
||||
|
||||
return SR_OK;
|
||||
}
|
||||
static void parse_flags(const uint8_t *buf, struct brymen_flags *info)
|
||||
{
|
||||
const uint8_t * bfunc = buf + sizeof(struct brymen_header);
|
||||
|
||||
info->low_batt = (bfunc[3] & (1 << 7)) != 0;
|
||||
|
||||
info->decibel = (bfunc[1] & (1 << 5)) != 0;
|
||||
info->duty_cycle = (bfunc[1] & (1 << 3)) != 0;
|
||||
info->hertz = (bfunc[1] & (1 << 2)) != 0;
|
||||
info->amp = (bfunc[1] & (1 << 1)) != 0;
|
||||
info->beep = (bfunc[1] & (1 << 0)) != 0;
|
||||
|
||||
info->ohm = (bfunc[0] & (1 << 7)) != 0;
|
||||
info->fahrenheit = (bfunc[0] & (1 << 6)) != 0;
|
||||
info->celsius = (bfunc[0] & (1 << 5)) != 0;
|
||||
info->diode = (bfunc[0] & (1 << 4)) != 0;
|
||||
info->capacitance = (bfunc[0] & (1 << 3)) != 0;
|
||||
info->volt = (bfunc[0] & (1 << 2)) != 0;
|
||||
info->dc = (bfunc[0] & (1 << 1)) != 0;
|
||||
info->ac = (bfunc[0] & (1 << 0)) != 0;
|
||||
}
|
||||
|
||||
SR_PRIV int sr_brymen_parse(const uint8_t *buf, float *floatval,
|
||||
struct sr_datafeed_analog *analog, void *info)
|
||||
{
|
||||
struct brymen_flags flags;
|
||||
struct brymen_header *hdr = (void*) buf;
|
||||
const uint8_t *bfunc = buf + sizeof(struct brymen_header);
|
||||
int asciilen;
|
||||
|
||||
(void)info;
|
||||
analog->mqflags = 0;
|
||||
|
||||
/* Give some debug info about the package */
|
||||
asciilen = hdr->len - 4;
|
||||
sr_dbg("DMM flags: %.2x %.2x %.2x %.2x",
|
||||
bfunc[3], bfunc[2], bfunc[1], bfunc[0]);
|
||||
/* Value is an ASCII string */
|
||||
sr_dbg("DMM packet: \"%.*s\"", asciilen, bfunc + 4);
|
||||
|
||||
parse_flags(buf, &flags);
|
||||
parse_value((const char*)(bfunc + 4), asciilen, floatval);
|
||||
|
||||
if (flags.volt) {
|
||||
analog->mq = SR_MQ_VOLTAGE;
|
||||
analog->unit = SR_UNIT_VOLT;
|
||||
}
|
||||
if (flags.amp) {
|
||||
analog->mq = SR_MQ_CURRENT;
|
||||
analog->unit = SR_UNIT_AMPERE;
|
||||
}
|
||||
if (flags.ohm) {
|
||||
if (flags.beep)
|
||||
analog->mq = SR_MQ_CONTINUITY;
|
||||
else
|
||||
analog->mq = SR_MQ_RESISTANCE;
|
||||
analog->unit = SR_UNIT_OHM;
|
||||
}
|
||||
if (flags.hertz) {
|
||||
analog->mq = SR_MQ_FREQUENCY;
|
||||
analog->unit = SR_UNIT_HERTZ;
|
||||
}
|
||||
if (flags.duty_cycle) {
|
||||
analog->mq = SR_MQ_DUTY_CYCLE;
|
||||
analog->unit = SR_UNIT_PERCENTAGE;
|
||||
}
|
||||
if (flags.capacitance) {
|
||||
analog->mq = SR_MQ_CAPACITANCE;
|
||||
analog->unit = SR_UNIT_FARAD;
|
||||
}
|
||||
if (flags.fahrenheit) {
|
||||
analog->mq = SR_MQ_TEMPERATURE;
|
||||
analog->unit = SR_UNIT_FAHRENHEIT;
|
||||
}
|
||||
if (flags.celsius) {
|
||||
analog->mq = SR_MQ_TEMPERATURE;
|
||||
analog->unit = SR_UNIT_CELSIUS;
|
||||
}
|
||||
if (flags.capacitance) {
|
||||
analog->mq = SR_MQ_CAPACITANCE;
|
||||
analog->unit = SR_UNIT_FARAD;
|
||||
}
|
||||
/*
|
||||
* The high-end brymen models have a configurable reference impedance.
|
||||
* When the reference impedance is changed, the DMM sends one packet
|
||||
* with the value of the new reference impedance. Both decibel and ohm
|
||||
* flags are set in this case, so we must be careful to correctly
|
||||
* identify the value as ohm, not dBmW
|
||||
*/
|
||||
if (flags.decibel && !flags.ohm) {
|
||||
analog->mq = SR_MQ_POWER;
|
||||
analog->unit = SR_UNIT_DECIBEL_MW;
|
||||
/*
|
||||
* For some reason, dBm measurements are sent by the multimeter
|
||||
* with a value three orders of magnitude smaller than the
|
||||
* displayed value.
|
||||
* */
|
||||
*floatval *= 1000;
|
||||
}
|
||||
|
||||
if (flags.diode)
|
||||
analog->mqflags |= SR_MQFLAG_DIODE;
|
||||
/* We can have both AC+DC in a single measurement */
|
||||
if (flags.ac)
|
||||
analog->mqflags |= SR_MQFLAG_AC;
|
||||
if (flags.dc)
|
||||
analog->mqflags |= SR_MQFLAG_DC;
|
||||
|
||||
if (flags.low_batt)
|
||||
sr_info("Low battery!");
|
||||
|
||||
return SR_OK;
|
||||
}
|
|
@ -17,16 +17,109 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <glib.h>
|
||||
#include "libsigrok.h"
|
||||
#include "libsigrok-internal.h"
|
||||
#include "protocol.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/* --- parser.c --- */
|
||||
SR_PRIV int sr_brymen_parse(const uint8_t *buf, float *floatval,
|
||||
struct sr_datafeed_analog *analog, void *info);
|
||||
|
||||
|
||||
static void handle_packet(const uint8_t *buf, struct sr_dev_inst *sdi)
|
||||
{
|
||||
float floatval;
|
||||
struct dev_context *devc;
|
||||
struct sr_datafeed_packet packet;
|
||||
struct sr_datafeed_analog analog;
|
||||
|
||||
devc = sdi->priv;
|
||||
|
||||
analog.num_samples = 1;
|
||||
analog.mq = -1;
|
||||
|
||||
sr_brymen_parse(buf, &floatval, &analog, NULL);
|
||||
analog.data = &floatval;
|
||||
|
||||
analog.probes = sdi->probes;
|
||||
|
||||
if (analog.mq != -1) {
|
||||
/* Got a measurement. */
|
||||
packet.type = SR_DF_ANALOG;
|
||||
packet.payload = &analog;
|
||||
sr_session_send(devc->cb_data, &packet);
|
||||
devc->num_samples++;
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_new_data(struct sr_dev_inst *sdi)
|
||||
{
|
||||
struct dev_context *devc;
|
||||
int len, status, offset = 0;
|
||||
|
||||
devc = sdi->priv;
|
||||
|
||||
/* Try to get as much data as the buffer can hold. */
|
||||
len = DMM_BUFSIZE - devc->buflen;
|
||||
len = serial_read(devc->serial, devc->buf + devc->buflen, len);
|
||||
if (len < 1) {
|
||||
sr_err("Serial port read error: %d.", len);
|
||||
return;
|
||||
}
|
||||
devc->buflen += len;
|
||||
status = PACKET_INVALID_HEADER;
|
||||
|
||||
/* Now look for packets in that data. */
|
||||
while (status != PACKET_NEED_MORE_DATA) {
|
||||
/* We don't have a header, look for one */
|
||||
if (devc->next_packet_len == 0) {
|
||||
len = devc->buflen - offset;
|
||||
status = brymen_packet_length(devc->buf + offset, &len);
|
||||
if (status == PACKET_HEADER_OK) {
|
||||
/* We know how large the packet will be */
|
||||
devc->next_packet_len = len;
|
||||
} else if (status == PACKET_NEED_MORE_DATA) {
|
||||
/* We did not yet receive the complete header */
|
||||
devc->next_packet_len = 0;
|
||||
break;
|
||||
} else {
|
||||
/* Invalid header. Move on */
|
||||
devc->next_packet_len = 0;
|
||||
offset++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/* We know how the packet size, but did we receive all of it? */
|
||||
if (devc->buflen - offset < devc->next_packet_len)
|
||||
break;
|
||||
|
||||
/* We should have a full packet here, so we can check it */
|
||||
if (brymen_packet_is_valid(devc->buf + offset)) {
|
||||
handle_packet(devc->buf + offset, sdi);
|
||||
offset += devc->next_packet_len;
|
||||
} else {
|
||||
offset++;
|
||||
}
|
||||
/* We are done with this packet. Look for a new one. */
|
||||
devc->next_packet_len = 0;
|
||||
}
|
||||
|
||||
/* If we have any data left, move it to the beginning of our buffer. */
|
||||
memmove(devc->buf, devc->buf + offset, devc->buflen - offset);
|
||||
devc->buflen -= offset;
|
||||
}
|
||||
|
||||
SR_PRIV int brymen_dmm_receive_data(int fd, int revents, void *cb_data)
|
||||
{
|
||||
const struct sr_dev_inst *sdi;
|
||||
struct sr_dev_inst *sdi;
|
||||
struct dev_context *devc;
|
||||
int ret;
|
||||
int64_t time;
|
||||
|
||||
(void)fd;
|
||||
|
||||
if (!(sdi = cb_data))
|
||||
return TRUE;
|
||||
|
@ -35,8 +128,134 @@ SR_PRIV int brymen_dmm_receive_data(int fd, int revents, void *cb_data)
|
|||
return TRUE;
|
||||
|
||||
if (revents == G_IO_IN) {
|
||||
/* TODO */
|
||||
/* Serial data arrived. */
|
||||
handle_new_data(sdi);
|
||||
} else {
|
||||
/* Timeout, send another packet request. */
|
||||
ret = brymen_packet_request(devc->serial);
|
||||
if (ret < 0) {
|
||||
sr_err("Failed to request packet: %d.", ret);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
if (devc->limit_samples && devc->num_samples >= devc->limit_samples) {
|
||||
sr_info("Requested number of samples reached, stopping.");
|
||||
sdi->driver->dev_acquisition_stop(sdi, cb_data);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (devc->limit_msec) {
|
||||
time = (g_get_monotonic_time() - devc->starttime) / 1000;
|
||||
if (time > (int64_t)devc->limit_msec) {
|
||||
sr_info("Requested time limit reached, stopping.");
|
||||
sdi->driver->dev_acquisition_stop(sdi, cb_data);
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to find a valid packet in a serial data stream.
|
||||
*
|
||||
* @param serial Previously initialized serial port structure.
|
||||
* @param buf Buffer containing the bytes to write.
|
||||
* @param buflen Size of the buffer.
|
||||
* @param packet_size Callback that assesses the size of the incoming packet/
|
||||
* @param is_valid Callback that assesses whether the packet is valid or not.
|
||||
* @param timeout_ms The timeout after which, if no packet is detected, to
|
||||
* abort scanning.
|
||||
* @param baudrate The baudrate of the serial port. This parameter is not
|
||||
* critical, but it helps fine tune the serial port polling
|
||||
* delay.
|
||||
*
|
||||
* @return SR_OK if a valid packet is found within the given timeout,
|
||||
* SR_ERR upon failure.
|
||||
*/
|
||||
SR_PRIV int brymen_stream_detect(struct sr_serial_dev_inst *serial,
|
||||
uint8_t *buf, size_t *buflen,
|
||||
packet_length_t get_packet_size,
|
||||
packet_valid_t is_valid,
|
||||
uint64_t timeout_ms, int baudrate)
|
||||
{
|
||||
int64_t start, time, byte_delay_us;
|
||||
size_t ibuf, i, maxlen;
|
||||
int status, len, packet_len, stream_len;
|
||||
|
||||
maxlen = *buflen;
|
||||
|
||||
sr_dbg("Detecting packets on FD %d (timeout = %" PRIu64
|
||||
"ms, baudrate = %d).", serial->fd, timeout_ms, baudrate);
|
||||
|
||||
/* Assume 8n1 transmission. That is 10 bits for every byte. */
|
||||
byte_delay_us = 10 * (1000000 / baudrate);
|
||||
start = g_get_monotonic_time();
|
||||
|
||||
packet_len = i = ibuf = len = 0;
|
||||
while (ibuf < maxlen) {
|
||||
len = serial_read(serial, &buf[ibuf], maxlen - ibuf);
|
||||
if (len > 0) {
|
||||
ibuf += len;
|
||||
sr_spew("Read %d bytes", len);
|
||||
}
|
||||
|
||||
time = g_get_monotonic_time() - start;
|
||||
time /= 1000;
|
||||
|
||||
stream_len = ibuf - i;
|
||||
if (stream_len > 0 && packet_len == 0) {
|
||||
/* How large of a packet are we expecting? */
|
||||
packet_len = stream_len;
|
||||
status = get_packet_size(&buf[i], &packet_len);
|
||||
switch(status) {
|
||||
case PACKET_HEADER_OK:
|
||||
/* We know how much data we need to wait for */
|
||||
break;
|
||||
case PACKET_NEED_MORE_DATA:
|
||||
/* We did not receive the full header */
|
||||
packet_len = 0;
|
||||
break;
|
||||
case PACKET_INVALID_HEADER:
|
||||
default:
|
||||
/*
|
||||
* We had enough data, but here was an error in
|
||||
* parsing the header. Restart parsing from the
|
||||
* next byte
|
||||
*/
|
||||
packet_len = 0;
|
||||
i++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( (stream_len >= packet_len) && (packet_len != 0) ) {
|
||||
/* We have at least a packet's worth of data. */
|
||||
if (is_valid(&buf[i])) {
|
||||
sr_spew("Found valid %d-byte packet after "
|
||||
"%" PRIu64 "ms.", packet_len, time);
|
||||
*buflen = ibuf;
|
||||
return SR_OK;
|
||||
} else {
|
||||
sr_spew("Got %d bytes, but not a valid "
|
||||
"packet.", packet_len);
|
||||
|
||||
}
|
||||
/* Not a valid packet. Continue searching. */
|
||||
i++;
|
||||
packet_len = 0;
|
||||
}
|
||||
if (time >= (int64_t)timeout_ms) {
|
||||
/* Timeout */
|
||||
sr_dbg("Detection timed out after %dms.", time);
|
||||
break;
|
||||
}
|
||||
g_usleep(byte_delay_us);
|
||||
}
|
||||
|
||||
*buflen = ibuf;
|
||||
sr_err("Didn't find a valid packet (read %d bytes).", ibuf);
|
||||
|
||||
return SR_ERR;
|
||||
}
|
||||
|
|
|
@ -33,6 +33,15 @@
|
|||
#define sr_warn(s, args...) sr_warn(DRIVER_LOG_DOMAIN s, ## args)
|
||||
#define sr_err(s, args...) sr_err(DRIVER_LOG_DOMAIN s, ## args)
|
||||
|
||||
|
||||
#define DMM_BUFSIZE 256
|
||||
|
||||
enum packet_len_status {
|
||||
PACKET_HEADER_OK,
|
||||
PACKET_NEED_MORE_DATA,
|
||||
PACKET_INVALID_HEADER,
|
||||
};
|
||||
|
||||
/** Private, per-device-instance driver context. */
|
||||
struct dev_context {
|
||||
/** The current sampling limit (in number of samples). */
|
||||
|
@ -46,8 +55,36 @@ struct dev_context {
|
|||
|
||||
/** The current number of already received samples. */
|
||||
uint64_t num_samples;
|
||||
|
||||
/** Start time of acquisition session */
|
||||
int64_t starttime;
|
||||
|
||||
struct sr_serial_dev_inst *serial;
|
||||
|
||||
uint8_t buf[DMM_BUFSIZE];
|
||||
int bufoffset;
|
||||
int buflen;
|
||||
int next_packet_len;
|
||||
};
|
||||
|
||||
SR_PRIV int brymen_dmm_receive_data(int fd, int revents, void *cb_data);
|
||||
/**
|
||||
* Callback that assesses the size and status of the incoming packet
|
||||
*
|
||||
* @return PACKET_HEADER_OK - This is a proper packet header.
|
||||
* PACKET_NEED_MORE_DATA The buffer does not contain the entire header
|
||||
* PACKET_INVALID_HEADER Not a valid start of packet.
|
||||
*/
|
||||
typedef int (*packet_length_t)(const uint8_t *buf, int *len);
|
||||
|
||||
SR_PRIV int brymen_dmm_receive_data(int fd, int revents, void *cb_data);
|
||||
SR_PRIV int brymen_packet_request(struct sr_serial_dev_inst *serial);
|
||||
|
||||
SR_PRIV int brymen_packet_length(const uint8_t *buf, int *len);
|
||||
SR_PRIV gboolean brymen_packet_is_valid(const uint8_t *buf);
|
||||
|
||||
SR_PRIV int brymen_stream_detect(struct sr_serial_dev_inst *serial,
|
||||
uint8_t *buf, size_t *buflen,
|
||||
packet_length_t get_packet_size,
|
||||
packet_valid_t is_valid,
|
||||
uint64_t timeout_ms, int baudrate);
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue