From 4bbed566f4d5e1a4a3569c46ef7518d31f0d57c4 Mon Sep 17 00:00:00 2001 From: Konrad Tagowski Date: Tue, 20 Jun 2023 11:29:20 +0200 Subject: [PATCH 1/2] uni-t-ut8802e: Initial commit --- Makefile.am | 6 + configure.ac | 1 + src/hardware/uni-t-ut8802e/api.c | 219 +++++++++++++ src/hardware/uni-t-ut8802e/protocol.c | 430 ++++++++++++++++++++++++++ src/hardware/uni-t-ut8802e/protocol.h | 131 ++++++++ 5 files changed, 787 insertions(+) create mode 100644 src/hardware/uni-t-ut8802e/api.c create mode 100644 src/hardware/uni-t-ut8802e/protocol.c create mode 100644 src/hardware/uni-t-ut8802e/protocol.h diff --git a/Makefile.am b/Makefile.am index 280cf64d2..7fae1e081 100644 --- a/Makefile.am +++ b/Makefile.am @@ -680,6 +680,12 @@ src_libdrivers_la_SOURCES += \ src/hardware/uni-t-ut32x/protocol.c \ src/hardware/uni-t-ut32x/api.c endif +if HW_UNI_T_UT8802E +src_libdrivers_la_SOURCES += \ + src/hardware/uni-t-ut8802e/protocol.h \ + src/hardware/uni-t-ut8802e/protocol.c \ + src/hardware/uni-t-ut8802e/api.c +endif if HW_YOKOGAWA_DLM src_libdrivers_la_SOURCES += \ src/hardware/yokogawa-dlm/protocol.h \ diff --git a/configure.ac b/configure.ac index 3ba6c8c5d..fe5e572c6 100644 --- a/configure.ac +++ b/configure.ac @@ -364,6 +364,7 @@ SR_DRIVER([Tondaj SL-814], [tondaj-sl-814], [serial_comm]) SR_DRIVER([UNI-T DMM], [uni-t-dmm], [libusb]) SR_DRIVER([UNI-T UT181A], [uni-t-ut181a], [serial_comm]) SR_DRIVER([UNI-T UT32x], [uni-t-ut32x], [serial_comm]) +SR_DRIVER([UNI-T UT8802E], [uni-t-ut8802e], [serial_comm]) SR_DRIVER([Yokogawa DL/DLM], [yokogawa-dlm]) SR_DRIVER([ZEROPLUS Logic Cube], [zeroplus-logic-cube], [libusb]) SR_DRIVER([ZKETECH EBD-USB], [zketech-ebd-usb], [serial_comm]) diff --git a/src/hardware/uni-t-ut8802e/api.c b/src/hardware/uni-t-ut8802e/api.c new file mode 100644 index 000000000..e04b829ba --- /dev/null +++ b/src/hardware/uni-t-ut8802e/api.c @@ -0,0 +1,219 @@ +/* + * This file is part of the libsigrok project. + * + * Copyright (C) 2023 Konrad Tagowski + * + * 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 . + */ + +#include +#include "protocol.h" + +static const uint32_t scanopts[] = { + SR_CONF_CONN, +}; + +static const uint32_t drvopts[] = { + SR_CONF_MULTIMETER, +}; + +static const uint32_t devopts[] = { + SR_CONF_CONTINUOUS, + SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET, + SR_CONF_LIMIT_MSEC | SR_CONF_GET | SR_CONF_SET, + SR_CONF_DATA_SOURCE | SR_CONF_GET | SR_CONF_LIST, +}; + + +static const char *channel_names[] = { + "Main", +}; + +static const char *data_sources[] = { + "Live", +}; + +static struct sr_dev_driver uni_t_ut8802e_driver_info; + +static GSList *scan(struct sr_dev_driver *di, GSList *options) +{ + struct sr_dev_inst *sdi; + struct dev_context *devc; + struct sr_serial_dev_inst *serial; + struct sr_config *src; + const char *conn, *serialcomm; + char conn_id[64]; + int rc; + size_t idx; + GSList *l ,*devices; + + conn = NULL; + serialcomm = "9600/8n1"; + for (l = options; l; l = l->next) { + src = l->data; + if (src->key == SR_CONF_CONN) + conn = g_variant_get_string(src->data, NULL); + } + + if (!conn) + return NULL; + + devices = NULL; + serial = sr_serial_dev_inst_new(conn, serialcomm); + rc = serial_open(serial, SERIAL_RDWR); + snprintf(conn_id, sizeof(conn_id), "%s", serial->port); + if (rc != SR_OK) { + serial_close(serial); + sr_serial_dev_inst_free(serial); + return devices; + } + + sdi = g_malloc0(sizeof(*sdi)); + sdi->status = SR_ST_INACTIVE; + sdi->vendor = g_strdup("UNI-T"); + sdi->model = g_strdup("UT8802E"); + sdi->inst_type = SR_INST_SERIAL; + sdi->conn = serial; + sdi->connection_id = g_strdup(conn_id); + devc = g_malloc0(sizeof(*devc)); + sdi->priv = devc; + sr_sw_limits_init(&devc->limits); + for (idx = 0; idx < ARRAY_SIZE(channel_names); idx++) { + sr_channel_new(sdi, idx, SR_CHANNEL_ANALOG, TRUE, + channel_names[idx]); + } + devices = g_slist_append(devices, sdi); + + return std_scan_complete(di, devices); + +} + + +static int config_get(uint32_t key, GVariant **data, + const struct sr_dev_inst *sdi, const struct sr_channel_group *cg) +{ + struct dev_context *devc; + + (void)cg; + + devc = sdi->priv; + + switch (key) { + case SR_CONF_CONN: + *data = g_variant_new_string(sdi->connection_id); + break; + case SR_CONF_LIMIT_FRAMES: + case SR_CONF_LIMIT_SAMPLES: + case SR_CONF_LIMIT_MSEC: + if (!devc) + return SR_ERR_ARG; + return sr_sw_limits_config_get(&devc->limits, key, data); + case SR_CONF_DATA_SOURCE: + *data = g_variant_new_string(data_sources[0]); + break; + // TO DO ADD SR_CONF_MEASURED_QUANTITY and SR_CONF_RANGE + default: + return SR_ERR_NA; + } + + return SR_OK; +} + +static int config_set(uint32_t key, GVariant *data, + const struct sr_dev_inst *sdi, const struct sr_channel_group *cg) +{ + struct dev_context *devc; + + (void)cg; + + devc = sdi->priv; + + switch (key) { + case SR_CONF_LIMIT_FRAMES: + case SR_CONF_LIMIT_SAMPLES: + case SR_CONF_LIMIT_MSEC: + if (!devc) + return SR_ERR_ARG; + return sr_sw_limits_config_set(&devc->limits, key, data); + break; + default: + return SR_ERR_NA; + } + + return SR_OK; +} + +static int config_list(uint32_t key, GVariant **data, + const struct sr_dev_inst *sdi, const struct sr_channel_group *cg) +{ + switch (key) { + case SR_CONF_SCAN_OPTIONS: + case SR_CONF_DEVICE_OPTIONS: + return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts); + case SR_CONF_DATA_SOURCE: + *data = g_variant_new_strv(ARRAY_AND_SIZE(data_sources)); + break; + // TO DO: ADD MEASURED QUANTITY and RANGE + default: + return SR_ERR_NA; + } + + return SR_OK; +} + +static int dev_acquisition_start(const struct sr_dev_inst *sdi) +{ + struct dev_context *devc; + struct sr_serial_dev_inst *serial; + + devc = sdi->priv; + serial = sdi->conn; + serial_flush(serial); + + sr_sw_limits_acquisition_start(&devc->limits); + devc->packet_len = 0; + std_session_send_df_header(sdi); + + serial_source_add(sdi->session, serial, G_IO_IN, 10, + ut8802e_handle_events, (void *)sdi); + + return SR_OK; +} + +static int dev_acquisition_stop(struct sr_dev_inst *sdi) +{ + sdi->status = SR_ST_STOPPING; + /* Initiate stop here. Activity happens in ut8802e_handle_events(). */ + return SR_OK; +} + +static struct sr_dev_driver uni_t_ut8802e_driver_info = { + .name = "uni-t-ut8802e", + .longname = "UNI-T UT8802E", + .api_version = 1, + .init = std_init, + .cleanup = std_cleanup, + .scan = scan, + .dev_list = std_dev_list, + .dev_clear = std_dev_clear, + .config_get = config_get, + .config_set = config_set, + .config_list = config_list, + .dev_open = std_serial_dev_open, + .dev_close = std_serial_dev_close, + .dev_acquisition_start = dev_acquisition_start, + .dev_acquisition_stop = dev_acquisition_stop, + .context = NULL, +}; +SR_REGISTER_DEV_DRIVER(uni_t_ut8802e_driver_info); diff --git a/src/hardware/uni-t-ut8802e/protocol.c b/src/hardware/uni-t-ut8802e/protocol.c new file mode 100644 index 000000000..90ee06929 --- /dev/null +++ b/src/hardware/uni-t-ut8802e/protocol.c @@ -0,0 +1,430 @@ +/* + * This file is part of the libsigrok project. + * + * Copyright (C) 2023 Konrad Tagowski + * + * 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 . + */ + +#include +#include "math.h" + +#include "protocol.h" + +/* + * Developer notes on the UT8802E protocol: + * - Serial communication over HID CP2110 converter USB-UART. + * - UART frame format 8n1 at 9600 bps. + * - DMM packet starts with a magic marker, functionality, value, etc. + * - UT8802E only sends a packet with measurement. Example of frame: + * Frame | func | value | comma | settings | checksum | + * ac | 1b | 45 01 00 | 33 | 04 | 44 | + * - The frame to send a command to the multimeter is unknown. +*/ + +static const int8_t range_volt[] = {-3, 0}; +static const int8_t range_amp[] = {-6, -3, 0}; +static const int8_t range_ohm[] = {0, 3, 6}; +static const int8_t range_farad[] = {-9, -6, -3}; +static const int8_t range_hz[] = {0, 3, 6}; + +static int ut8802e_feedbuff_initialization(struct feed_buffer *buff) { + memset(buff, 0, sizeof(*buff)); + memset(&buff->packet, 0, sizeof(buff->packet)); + sr_analog_init(&buff->analog, &buff->encoding, + &buff->meaning, &buff->spec, 0); + buff->analog.meaning->mq = 0; + buff->analog.meaning->mqflags = 0; + buff->analog.meaning->unit = 0; + buff->analog.meaning->channels = 0; + buff->analog.encoding->unitsize = sizeof(buff->main_value); + buff->analog.encoding->digits = 4; + buff->analog.spec->spec_digits = 4; + buff->analog.num_samples = 1; + buff->analog.data = &buff->main_value; + buff->packet.type = SR_DF_ANALOG; + buff->packet.payload = &buff->analog; + buff->analog.encoding->is_float = TRUE; + return SR_OK; +} + +static int process_packet(struct sr_dev_inst *sdi, uint8_t *pkt, size_t len) { + struct dev_context *devc; + struct ut8802e_info *info; + const uint8_t *payload; + size_t checksum_dlen; + + uint8_t v8, got_magic_frame, got_checksum, want_checksum; + uint16_t clc_checksum; + uint32_t v32; + uint8_t got_length, settings; + float new_value; + + struct feed_buffer feedbuff; + + int ret; + + devc = sdi ? sdi->priv : NULL; + info = devc ? &devc->info : NULL; + + if (len < 1 * sizeof(uint8_t)) { + sr_spew("Wrong packet"); + return SR_ERR_DATA; + } + + got_magic_frame = R8(&pkt[0]); + + if (got_magic_frame != FRAME_MAGIC) { + sr_spew("Wrong frame packet"); + return SR_ERR_DATA; + } + + got_length = 7; + if (got_length != len - sizeof(uint8_t)) { + sr_spew("Wrong lenght of packet"); + return SR_ERR_DATA; + } + + payload = &pkt[sizeof(uint8_t)]; + + clc_checksum = 0; + checksum_dlen = got_length; + for (uint32_t i = 0; i < checksum_dlen; i++) { + clc_checksum = clc_checksum + pkt[i]; + } + want_checksum = clc_checksum; + + if (clc_checksum > 256 && want_checksum >= 128) + want_checksum = want_checksum - 128; + + got_checksum = R8(&pkt[checksum_dlen]); + sr_spew("Checksum: %d, Got: %d, ALL %d", want_checksum, got_checksum, clc_checksum); + if (want_checksum != got_checksum) + return SR_ERR_DATA; + + info->meas_head.mqflag = SR_MQFLAG_AC | SR_MQFLAG_DC; + info->meas_data.main_prec = 0; + info->meas_data.main_value = 0; + + v8 = R8(&payload[1]); // 99 + v32 = (v8 & 0x0F) + ((v8 & 0xF0) >> 4) * 10; + new_value = v32; + + v8 = R8(&payload[2]); // 99-- + v32 = ((v8 & 0x0F) + ((v8 & 0xF0) >> 4) * 10) * 100; + new_value = new_value + v32; + + v8 = R8(&payload[3]); // 2---- + v32 = (v8 & 0x0F) * 10000; + new_value = new_value + v32; + + v8 = R8(&payload[4]); + info->meas_data.comma_position = -(v8 - 0x30); + new_value = new_value * pow(10, info->meas_data.comma_position); + + settings = R8(&payload[5]); + if ((settings & (1 << 7))) + new_value = -new_value; + + v8 = R8(&payload[0]); + switch (v8) { + case MODE_V_AC_2V: + case MODE_V_AC_20V: + case MODE_V_AC_200V: + case MODE_V_AC_750V: + info->meas_head.mode = SR_MQ_VOLTAGE; + info->meas_head.mqflag = SR_MQFLAG_AC; + info->meas_data.main_unit = SR_UNIT_VOLT; + info->meas_data.main_prec = range_volt[1]; + break; + case MODE_V_DC_200mV: + info->meas_head.mode = SR_MQ_VOLTAGE; + info->meas_head.mqflag = SR_MQFLAG_DC; + info->meas_data.main_unit = SR_UNIT_VOLT; + info->meas_data.main_prec = range_volt[0]; + break; + case MODE_V_DC_2V: + case MODE_V_DC_20V: + case MODE_V_DC_200V: + case MODE_V_DC_1000V: + info->meas_head.mode = SR_MQ_VOLTAGE; + info->meas_head.mqflag = SR_MQFLAG_DC; + info->meas_data.main_unit = SR_UNIT_VOLT; + info->meas_data.main_prec = range_volt[1]; + break; + + case MODE_A_AC_2mA: + case MODE_A_AC_20mA: + case MODE_A_AC_200mA: + info->meas_head.mode = SR_MQ_CURRENT; + info->meas_head.mqflag = SR_MQFLAG_AC; + info->meas_data.main_unit = SR_UNIT_AMPERE; + info->meas_data.main_prec = range_amp[0]; + break; + case MODE_A_AC_20A: + info->meas_head.mode = SR_MQ_CURRENT; + info->meas_head.mqflag = SR_MQFLAG_AC; + info->meas_data.main_unit = SR_UNIT_AMPERE; + info->meas_data.main_prec = range_amp[1]; + break; + + case MODE_A_DC_2mA: + case MODE_A_DC_20mA: + case MODE_A_DC_200mA: + info->meas_head.mode = SR_MQ_CURRENT; + info->meas_head.mqflag = SR_MQFLAG_DC; + info->meas_data.main_unit = SR_UNIT_AMPERE; + info->meas_data.main_prec = range_amp[0]; + break; + case MODE_A_DC_20A: + info->meas_head.mode = SR_MQ_CURRENT; + info->meas_head.mqflag = SR_MQFLAG_DC; + info->meas_data.main_unit = SR_UNIT_AMPERE; + info->meas_data.main_prec = range_amp[1]; + break; + + case MODE_RES_200: + info->meas_head.mode = SR_MQ_RESISTANCE; + info->meas_data.main_unit = SR_UNIT_OHM; + info->meas_data.main_prec = range_ohm[0]; + break; + case MODE_RES_2k: + case MODE_RES_20k: + case MODE_RES_200k: + info->meas_head.mode = SR_MQ_RESISTANCE; + info->meas_data.main_unit = SR_UNIT_OHM; + info->meas_data.main_prec = range_ohm[1]; + break; + case MODE_RES_2M: + case MODE_RES_200M: + info->meas_head.mode = SR_MQ_RESISTANCE; + info->meas_data.main_unit = SR_UNIT_OHM; + info->meas_data.main_prec = range_ohm[2]; + break; + + case MODE_CIRCUIT_CONTINUITY: + info->meas_head.mode = SR_MQ_CONTINUITY; + info->meas_head.mqflag = SR_MQFLAG_AUTORANGE; + info->meas_data.main_unit = SR_UNIT_OHM; + break; + + case MODE_DIODE: + info->meas_head.mode = SR_MQ_VOLTAGE; + info->meas_head.mqflag = SR_MQFLAG_DIODE | SR_MQFLAG_DC; + info->meas_data.main_unit = SR_MQ_VOLTAGE; + break; + + case MODE_CAPACITANCE_nF: + info->meas_head.mode = SR_MQ_CAPACITANCE; + info->meas_data.main_unit = SR_UNIT_FARAD; + info->meas_data.main_prec = range_farad[0]; + break; + case MODE_CAPACITANCE_uF: + info->meas_head.mode = SR_MQ_CAPACITANCE; + info->meas_data.main_unit = SR_UNIT_FARAD; + info->meas_data.main_prec = range_farad[1]; + break; + case MODE_CAPACITANCE_mF: + info->meas_head.mode = SR_MQ_CAPACITANCE; + info->meas_data.main_unit = SR_UNIT_FARAD; + info->meas_data.main_prec = range_farad[2]; + break; + + case MODE_TRIODE_HFE: + info->meas_head.mode = SR_MQ_GAIN; + info->meas_data.main_unit = SR_UNIT_UNITLESS; + break; + + case MODE_THYRISTOR_SCR: + info->meas_head.mode = SR_MQ_VOLTAGE; + info->meas_data.main_unit = SR_UNIT_VOLT; + break; + + case MODE_FREQ_Hz: + info->meas_head.mode = SR_MQ_FREQUENCY; + info->meas_data.main_unit = SR_UNIT_HERTZ; + info->meas_data.main_prec = range_hz[0]; + break; + case MODE_FREQ_KHz: + info->meas_head.mode = SR_MQ_FREQUENCY; + info->meas_data.main_unit = SR_UNIT_HERTZ; + info->meas_data.main_prec = range_hz[1]; + break; + case MODE_FREQ_MHz: + info->meas_head.mode = SR_MQ_FREQUENCY; + info->meas_data.main_unit = SR_UNIT_HERTZ; + info->meas_data.main_prec = range_hz[2]; + break; + + case MODE_DUTY: + info->meas_head.mode = SR_MQ_DUTY_CYCLE; + info->meas_data.main_unit = SR_UNIT_PERCENTAGE; + break; + + default: + sr_spew("Unkwon functionality"); + return SR_ERR_DATA; + } + + ret = ut8802e_feedbuff_initialization(&feedbuff); + if (ret != SR_OK) + return SR_ERR_DATA; + + g_slist_free(feedbuff.analog.meaning->channels); + feedbuff.analog.meaning->channels = g_slist_append( + NULL, + g_slist_nth_data(sdi->channels, ut8802e_CH_MAIN)); + + feedbuff.analog.meaning->mqflags = info->meas_head.mqflag; + feedbuff.analog.meaning->mq = info->meas_head.mode; + feedbuff.analog.meaning->unit = info->meas_data.main_unit; + + info->meas_data.main_value = new_value * pow(10, info->meas_data.main_prec); + + feedbuff.analog.encoding->digits = -info->meas_data.main_prec + 4; + feedbuff.analog.spec->spec_digits = -info->meas_data.main_prec + 4; + + feedbuff.main_value = info->meas_data.main_value; + if (sdi->status != SR_ST_ACTIVE) + return SR_OK; + + ret = sr_session_send(sdi, &feedbuff.packet); + if (ret != SR_OK) + return SR_ERR_DATA; + + sr_sw_limits_update_samples_read(&devc->limits, 1); + if (sr_sw_limits_check(&devc->limits)) + sr_dev_acquisition_stop(sdi); + + if (feedbuff.analog.meaning) + g_slist_free(feedbuff.analog.meaning->channels); + + return SR_OK; +} + +static int process_buffer(struct sr_dev_inst *sdi) { + struct dev_context *devc; + uint8_t *pkt; + uint8_t v8; + size_t pkt_len, remain, idx; + int ret; + GString *spew; + + devc = sdi->priv; + pkt = &devc->packet[0]; + + do { + if (devc->packet_len < 1 * sizeof(uint8_t)) + return SR_OK; + + v8 = R8(&pkt[0]); + if (v8 != FRAME_MAGIC) + break; + + pkt_len = 8; + sr_spew("Got a expected packet lenght %zu, have %zu", + pkt_len, devc->packet_len); + + if (pkt_len > devc->packet_len) + return SR_OK; + + spew = sr_hexdump_new(pkt, pkt_len); + sr_spew("Packet to procceses, len %zu, bytes: %s", pkt_len, spew->str); + sr_hexdump_free(spew); + + ret = process_packet(sdi, pkt, pkt_len); + if (ret == SR_ERR_DATA) { + /* Verification failed */ + break; + } + + remain = devc->packet_len - pkt_len; + if (remain) + memmove(&pkt[0], &pkt[pkt_len], remain); + devc->packet_len -= pkt_len; + } while (1); + + if (devc->packet_len < 1 * sizeof(uint8_t)) + return SR_OK; + + for (idx = 1; idx < devc->packet_len; idx++) { + if (devc->packet_len - idx < sizeof(uint8_t)) { + pkt[0] = pkt[idx]; + devc->packet_len = 1; + return SR_OK; + } + + v8 = R8(&pkt[idx]); + if (v8 != FRAME_MAGIC) + continue; + + remain = devc->packet_len - idx; + if (remain) + memmove(&pkt[0], &pkt[idx], remain); + devc->packet_len -= idx; + break; + } + return SR_OK; +} + +static int ut8802e_receive_data(struct sr_dev_inst *sdi) { + struct dev_context *devc; + struct sr_serial_dev_inst *serial; + size_t len; + uint8_t *data; + + devc = sdi->priv; + serial = sdi->conn; + + if (devc->packet_len == sizeof(devc->packet)) { + (void)process_packet(sdi, &devc->packet[0], devc->packet_len); + devc->packet_len = 0; + } + + len = sizeof(devc->packet) - devc->packet_len; + data = &devc->packet[devc->packet_len]; + len = serial_read_nonblocking(serial, + data, len); + if (!len) + return 0; + + devc->packet_len += len; + process_buffer(sdi); + + return 0; +} + +SR_PRIV int ut8802e_handle_events(int fd, int revents, void *cb_data) { + struct sr_dev_inst *sdi; + struct sr_serial_dev_inst *serial; + + (void)fd; + + sdi = cb_data; + if (!sdi) + return TRUE; + serial = sdi->conn; + if (!serial) + return TRUE; + + if (revents & G_IO_IN) + (void)ut8802e_receive_data(sdi); + + if (sdi->status == SR_ST_STOPPING) { + serial_source_remove(sdi->session, serial); + std_session_send_df_end(sdi); + sdi->status = SR_ST_INACTIVE; + } + return TRUE; +} diff --git a/src/hardware/uni-t-ut8802e/protocol.h b/src/hardware/uni-t-ut8802e/protocol.h new file mode 100644 index 000000000..0c4f62c9d --- /dev/null +++ b/src/hardware/uni-t-ut8802e/protocol.h @@ -0,0 +1,131 @@ +/* + * This file is part of the libsigrok project. + * + * Copyright (C) 2023 Konrad Tagowski + * + * 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 . + */ + +#ifndef LIBSIGROK_HARDWARE_UNI_T_UT8802E_PROTOCOL_H +#define LIBSIGROK_HARDWARE_UNI_T_UT8802E_PROTOCOL_H + +#include +#include +#include +#include "libsigrok-internal.h" + +#define LOG_PREFIX "uni-t-ut8802e" + +#define PACKET_SIZE 32 + +#define FRAME_MAGIC 0xac /* AC */ + +enum ut8802e_mode_code { + /* V AC */ + MODE_V_AC_2V = 0x09, + MODE_V_AC_20V = 0x0A, + MODE_V_AC_200V = 0x0B, + MODE_V_AC_750V = 0x0C, + /* V DC */ + MODE_V_DC_200mV = 0x01, + MODE_V_DC_2V = 0x03, + MODE_V_DC_20V = 0x04, + MODE_V_DC_200V = 0x05, + MODE_V_DC_1000V = 0x06, + /* A AC */ + MODE_A_AC_2mA = 0x10, + MODE_A_AC_20mA = 0x13, + MODE_A_AC_200mA = 0x14, + MODE_A_AC_20A = 0x18, + /* A DC */ + MODE_A_DC_200uA = 0x0D, + MODE_A_DC_2mA = 0x0E, + MODE_A_DC_20mA = 0x11, + MODE_A_DC_200mA = 0x12, + MODE_A_DC_20A = 0x16, + /* Resistance */ + MODE_RES_200 = 0x19, + MODE_RES_2k = 0x1A, + MODE_RES_20k = 0x1B, + MODE_RES_200k = 0x1C, + MODE_RES_2M = 0x1D, + MODE_RES_200M = 0x1F, + /* Resistance */ + MODE_CIRCUIT_CONTINUITY = 0x24, + /* Diode */ + MODE_DIODE = 0x23, + /* Capacitance */ + MODE_CAPACITANCE_nF = 0x27, + MODE_CAPACITANCE_uF = 0x28, + MODE_CAPACITANCE_mF = 0x29, + /* Triode hFe */ + MODE_TRIODE_HFE = 0x25, + /* THYRISTOR SCR */ + MODE_THYRISTOR_SCR = 0x2A, + /* frequency, duty cycle, pulse width */ + MODE_FREQ_Hz = 0x2B, + MODE_FREQ_KHz = 0x2C, + MODE_FREQ_MHz = 0x2D, + MODE_DUTY = 0x22, +}; + +enum ut8802e_rsp_type { + RSP_TYPE_INFO = 0x00, + RSP_TYPE_MEASUREMENT = 0x02, + RSP_TYPE_REC_INFO = 0x04, +}; + +enum ut8802e_channel_idx { + ut8802e_CH_MAIN, +}; +struct ut8802e_info { + struct { + enum ut8802e_rsp_type rsp_type; + } rsp_head; + + struct { + uint8_t range; + uint16_t mode; + uint8_t is_type; + uint8_t mqflag; + } meas_head; + + struct { + uint16_t main_unit; + float main_value; + int8_t main_prec; + int8_t comma_position; + } meas_data; +}; + +struct feed_buffer { + struct sr_datafeed_packet packet; + struct sr_datafeed_analog analog; + struct sr_analog_encoding encoding; + struct sr_analog_meaning meaning; + struct sr_analog_spec spec; + int scale; + float main_value; +}; + +struct dev_context { + struct sr_sw_limits limits; + struct ut8802e_info info; + uint8_t packet[PACKET_SIZE]; + size_t packet_len; +}; + +SR_PRIV int ut8802e_handle_events(int fd, int revents, void *cb_data); + +#endif From eebc0e5721c3c252533ec8e1519a6b0f01567023 Mon Sep 17 00:00:00 2001 From: Konrad Tagowski Date: Wed, 21 Jun 2023 08:14:36 +0200 Subject: [PATCH 2/2] Fixed issue with checksum calculation --- src/hardware/uni-t-ut8802e/protocol.c | 28 +++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/hardware/uni-t-ut8802e/protocol.c b/src/hardware/uni-t-ut8802e/protocol.c index 90ee06929..89757e777 100644 --- a/src/hardware/uni-t-ut8802e/protocol.c +++ b/src/hardware/uni-t-ut8802e/protocol.c @@ -17,10 +17,11 @@ * along with this program. If not, see . */ +#include "protocol.h" + #include -#include "math.h" -#include "protocol.h" +#include "math.h" /* * Developer notes on the UT8802E protocol: @@ -29,9 +30,9 @@ * - DMM packet starts with a magic marker, functionality, value, etc. * - UT8802E only sends a packet with measurement. Example of frame: * Frame | func | value | comma | settings | checksum | - * ac | 1b | 45 01 00 | 33 | 04 | 44 | + * ac | 1b | 45 01 00 | 33 | 04 | 44 | * - The frame to send a command to the multimeter is unknown. -*/ + */ static const int8_t range_volt[] = {-3, 0}; static const int8_t range_amp[] = {-6, -3, 0}; @@ -66,7 +67,7 @@ static int process_packet(struct sr_dev_inst *sdi, uint8_t *pkt, size_t len) { size_t checksum_dlen; uint8_t v8, got_magic_frame, got_checksum, want_checksum; - uint16_t clc_checksum; + uint16_t sum_of_bytes; uint32_t v32; uint8_t got_length, settings; float new_value; @@ -98,18 +99,17 @@ static int process_packet(struct sr_dev_inst *sdi, uint8_t *pkt, size_t len) { payload = &pkt[sizeof(uint8_t)]; - clc_checksum = 0; + sum_of_bytes = 0; checksum_dlen = got_length; for (uint32_t i = 0; i < checksum_dlen; i++) { - clc_checksum = clc_checksum + pkt[i]; + sum_of_bytes = sum_of_bytes + pkt[i]; } - want_checksum = clc_checksum; - - if (clc_checksum > 256 && want_checksum >= 128) - want_checksum = want_checksum - 128; + want_checksum = sum_of_bytes; + want_checksum &= ~(1UL << 7); got_checksum = R8(&pkt[checksum_dlen]); - sr_spew("Checksum: %d, Got: %d, ALL %d", want_checksum, got_checksum, clc_checksum); + sr_spew("Checksum: %d, Got: %d, sum of bytes: %d", + want_checksum, got_checksum, sum_of_bytes); if (want_checksum != got_checksum) return SR_ERR_DATA; @@ -296,8 +296,8 @@ static int process_packet(struct sr_dev_inst *sdi, uint8_t *pkt, size_t len) { feedbuff.analog.spec->spec_digits = -info->meas_data.main_prec + 4; feedbuff.main_value = info->meas_data.main_value; - if (sdi->status != SR_ST_ACTIVE) - return SR_OK; + if (sdi->status != SR_ST_ACTIVE) + return SR_OK; ret = sr_session_send(sdi, &feedbuff.packet); if (ret != SR_OK)