X-Git-Url: https://sigrok.org/gitweb/?a=blobdiff_plain;f=src%2Fhardware%2Funi-t-ut181a%2Fprotocol.h;h=e0406961c38aa8cb13ec38dacdb0ab9b879c0a0a;hb=8b172e78f6b9643cd62384df87459acb71f60d62;hp=d84fadc907375aed2975eae712dbdf0cc6e99bea;hpb=3094e9d8ca2b8cb7aad1c3bf41b47f0087e45ff8;p=libsigrok.git diff --git a/src/hardware/uni-t-ut181a/protocol.h b/src/hardware/uni-t-ut181a/protocol.h index d84fadc9..e0406961 100644 --- a/src/hardware/uni-t-ut181a/protocol.h +++ b/src/hardware/uni-t-ut181a/protocol.h @@ -1,7 +1,7 @@ /* * This file is part of the libsigrok project. * - * Copyright (C) 2019 Gerhard Sittig + * Copyright (C) 2019-2020 Gerhard Sittig * * 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 @@ -20,16 +20,419 @@ #ifndef LIBSIGROK_HARDWARE_UNI_T_UT181A_PROTOCOL_H #define LIBSIGROK_HARDWARE_UNI_T_UT181A_PROTOCOL_H -#include #include #include +#include + #include "libsigrok-internal.h" #define LOG_PREFIX "uni-t-ut181a" +/* + * Optional features. Tunables. + */ +#define UT181A_WITH_TIMESTAMP 0 +#define UT181A_WITH_SER_ECHO 0 + +/* + * The largest frame we expect to receive is chunked record data. Which + * can span up to 256 items which each occupy 9 bytes, plus some header + * before the items array. Be generous and prepare to receive several + * frames in a row, e.g. when synchronizing to the packet stream at the + * start of a session or after communication failure. + * + * The largest frame we expect to transmit is a "start record" command. + * Which contains 18 bytes of payload (plus 6 bytes of frame envelope). + */ +#define RECV_BUFF_SIZE 4096 +#define SEND_BUFF_SIZE 32 +#define SEND_TO_MS 100 + +/* + * The device can hold several recordings, their number is under the + * user's control and dynamic at runtime. It's assumed that there is an + * absolute upper bound of 20 recordings at any time. Names are under + * user control, too (auto-preset, then editable), and a maximum label + * length is assumed from the protocol description. + * + * Update 2020-03-17 + * Turns out that 20 is *not* the limit on the number of recordings. + * Nor do I believe that 20K or 10K is the limit. It may be the total + * of the number of recordings and their sample counts which may not + * exceed 10K, while saved measurements can be up to 20K? This is just + * a guess though, the "Operating Manual" does not specify a limit, + * nor does it discuss a dependency beyond mentioning the 10K/20K + * figures. + */ +#define MAX_REC_COUNT 20 +#define MAX_REC_NAMELEN 12 + +#define MAX_RANGE_INDEX 8 + +/* Literals look weird as numbers. LE format makes them readable on the wire. */ +#define FRAME_MAGIC 0xcdab /* Becomes the AB CD byte sequence. */ +#define REPLY_CODE_OK 0x4b4f /* Becomes the "OK" text. */ +#define REPLY_CODE_ERR 0x5245 /* Becomes the "ER" text. */ + +enum ut181a_channel_idx { + UT181A_CH_MAIN, + UT181A_CH_AUX1, + UT181A_CH_AUX2, + UT181A_CH_AUX3, + UT181A_CH_BAR, +#if UT181A_WITH_TIMESTAMP + UT181A_CH_TIME, +#endif +}; + +enum ut181_cmd_code { + CMD_CODE_INVALID = 0x00, + CMD_CODE_SET_MODE = 0x01, + CMD_CODE_SET_RANGE = 0x02, + CMD_CODE_SET_REFERENCE = 0x03, + CMD_CODE_SET_MIN_MAX = 0x04, + CMD_CODE_SET_MONITOR = 0x05, + CMD_CODE_SAVE_MEAS = 0x06, + CMD_CODE_GET_SAVED_MEAS = 0x07, + CMD_CODE_GET_SAVED_COUNT = 0x08, + CMD_CODE_DEL_SAVED_MEAS = 0x09, + CMD_CODE_START_REC = 0x0a, + CMD_CODE_STOP_REC = 0x0b, + CMD_CODE_GET_REC_INFO = 0x0c, + CMD_CODE_GET_REC_SAMPLES = 0x0d, + CMD_CODE_GET_RECS_COUNT = 0x0e, + CMD_CODE_BTN_PRESS = 0x12, +}; + +enum ut181_rsp_type { + RSP_TYPE_REPLY_CODE = 0x01, + RSP_TYPE_MEASUREMENT = 0x02, + RSP_TYPE_SAVE = 0x03, + RSP_TYPE_REC_INFO = 0x04, + RSP_TYPE_REC_DATA = 0x05, + RSP_TYPE_REPLY_DATA = 0x72, /* 'r' */ +}; + +/* + * TODO + * - See if there is a pattern to these number codes. + * - [3:0] == 2 relative mode (when available) + * - [7:4] == 3 peak mode aka max/min (when available) + * (but there is command 4 set max/min on/off too) + */ + +enum ut181a_mode_code { + /* V AC */ + MODE_V_AC = 0x1111, + MODE_V_AC_REL = 0x1112, + MODE_V_AC_Hz = 0x1121, + MODE_V_AC_PEAK = 0x1131, + MODE_V_AC_LOWPASS = 0x1141, + MODE_V_AC_LOWPASS_REL = 0x1142, + MODE_V_AC_dBV = 0x1151, + MODE_V_AC_dBV_REL = 0x1152, + MODE_V_AC_dBm = 0x1161, + MODE_V_AC_dBm_REL = 0x1162, + /* mV AC */ + MODE_mV_AC = 0x2111, + MODE_mV_AC_REL = 0x2112, + MODE_mV_AC_Hz = 0x2121, + MODE_mV_AC_PEAK = 0x2131, + MODE_mV_AC_ACDC = 0x2141, + MODE_mV_AC_ACDC_REL = 0x2142, + /* V DC */ + MODE_V_DC = 0x3111, + MODE_V_DC_REL = 0x3112, + MODE_V_DC_ACDC = 0x3121, + MODE_V_DC_ACDC_REL = 0x3122, + MODE_V_DC_PEAK = 0x3131, + /* mV DC */ + MODE_mV_DC = 0x4111, + MODE_mV_DC_REL = 0x4112, + MODE_mV_DC_PEAK = 0x4121, /* TODO Check number code, is it 0x4131? */ + /* temperature Celsius */ + MODE_TEMP_C_T1_and_T2 = 0x4211, + MODE_TEMP_C_T1_and_T2_REL = 0x4212, + MODE_TEMP_C_T2_and_T1 = 0x4221, + MODE_TEMP_C_T2_and_T1_REL = 0x4222, + MODE_TEMP_C_T1_minus_T2 = 0x4231, /* XXX exception, not PEAK */ + MODE_TEMP_C_T2_minus_T1 = 0x4241, + /* temperature Farenheit */ + MODE_TEMP_F_T1_and_T2 = 0x4311, + MODE_TEMP_F_T1_and_T2_REL = 0x4312, + MODE_TEMP_F_T2_and_T1 = 0x4321, + MODE_TEMP_F_T2_and_T1_REL = 0x4322, + MODE_TEMP_F_T1_minus_T2 = 0x4331, + MODE_TEMP_F_T2_minus_T1 = 0x4341, /* XXX exception, not PEAK */ + /* resistance, continuity, conductivity */ + MODE_RES = 0x5111, + MODE_RES_REL = 0x5112, + MODE_CONT_SHORT = 0x5211, + MODE_CONT_OPEN = 0x5212, + MODE_COND = 0x5311, + MODE_COND_REL = 0x5312, + /* diode, capacitance */ + MODE_DIODE = 0x6111, + MODE_DIODE_ALARM = 0x6112, /* XXX exception, not REL */ + MODE_CAP = 0x6211, + MODE_CAP_REL = 0x6212, + /* frequency, duty cycle, pulse width */ + MODE_FREQ = 0x7111, + MODE_FREQ_REL = 0x7112, + MODE_DUTY = 0x7211, + MODE_DUTY_REL = 0x7212, + MODE_PULSEWIDTH = 0x7311, + MODE_PULSEWIDTH_REL = 0x7312, + /* uA DC */ + MODE_uA_DC = 0x8111, + MODE_uA_DC_REL = 0x8112, + MODE_uA_DC_ACDC = 0x8121, + MODE_uA_DC_ACDC_REL = 0x8122, + MODE_uA_DC_PEAK = 0x8131, + /* uA AC */ + MODE_uA_AC = 0x8211, + MODE_uA_AC_REL = 0x8212, + MODE_uA_AC_Hz = 0x8221, + MODE_uA_AC_PEAK = 0x8231, + /* mA DC */ + MODE_mA_DC = 0x9111, + MODE_mA_DC_REL = 0x9112, + MODE_mA_DC_ACDC = 0x9121, + MODE_mA_DC_ACDC_REL = 0x9122, + MODE_mA_DC_ACDC_PEAK = 0x9131, + /* mA AC */ + MODE_mA_AC = 0x9211, + MODE_mA_AC_REL = 0x9212, + MODE_mA_AC_Hz = 0x9221, + MODE_mA_AC_PEAK = 0x9231, + /* A DC */ + MODE_A_DC = 0xa111, + MODE_A_DC_REL = 0xa112, + MODE_A_DC_ACDC = 0xa121, + MODE_A_DC_ACDC_REL = 0xa122, + MODE_A_DC_PEAK = 0xa131, + /* A AC */ + MODE_A_AC = 0xa211, + MODE_A_AC_REL = 0xa212, + MODE_A_AC_Hz = 0xa221, + MODE_A_AC_PEAK = 0xa231, +}; + +/* Maximum number of UT181A modes which map to one MQ item. */ +#define MODE_COUNT_PER_MQ_MQF 15 + +struct mqopt_item { + enum sr_mq mq; + enum sr_mqflag mqflags; + enum ut181a_mode_code modes[MODE_COUNT_PER_MQ_MQF]; +}; + +struct mq_scale_params { + int scale; + enum sr_mq mq; + enum sr_mqflag mqflags; + enum sr_unit unit; +}; + +struct value_params { + float value; + int digits; + gboolean ol_neg, ol_pos; +}; + +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; /* TODO double, for epoch timestamps */ +}; + +struct ut181a_info { + struct { + enum ut181_rsp_type rsp_type; + } rsp_head; + struct { + uint16_t code; + gboolean ok; + } reply_code; + struct { + uint32_t stamp; + time_t epoch; + } save_time; + struct { + uint8_t misc1, misc2, range; + uint16_t mode; + uint8_t is_type; + gboolean is_norm, is_rel, is_minmax, is_peak; + gboolean has_hold, has_aux1, has_aux2, has_bar; + gboolean is_rec, is_comp, is_auto_range; + gboolean has_lead_err, has_high_volt; + } meas_head; + union { + struct { + float main_value; + uint8_t main_prec; + char main_unit[8]; + float aux1_value; + uint8_t aux1_prec; + char aux1_unit[8]; + float aux2_value; + uint8_t aux2_prec; + char aux2_unit[8]; + float bar_value; + char bar_unit[8]; + } norm; + struct { + enum { + COMP_MODE_INNER = 0, + COMP_MODE_OUTER = 1, + COMP_MODE_BELOW = 2, + COMP_MODE_ABOVE = 3, + } mode; + gboolean fail; + int digits; + float limit_high; + float limit_low; + } comp; + struct { + float rel_value; + uint8_t rel_prec; + char rel_unit[8]; + float ref_value; + uint8_t ref_prec; + char ref_unit[8]; + float abs_value; + uint8_t abs_prec; + char abs_unit[8]; + float bar_value; + char bar_unit[8]; + } rel; + struct { + float curr_value; + uint8_t curr_prec; + float max_value; + uint8_t max_prec; + uint32_t max_stamp; + float avg_value; + uint8_t avg_prec; + uint32_t avg_stamp; + float min_value; + uint8_t min_prec; + uint32_t min_stamp; + char all_unit[8]; + } minmax; + struct { + float max_value; + uint8_t max_prec; + char max_unit[8]; + float min_value; + uint8_t min_prec; + char min_unit[8]; + } peak; + } meas_data; + struct { + size_t save_idx; + size_t save_count; + } save_info; + struct { + size_t rec_count; + size_t rec_idx; + gboolean auto_feed; + gboolean auto_next; + char name[12]; + char unit[8]; + uint16_t interval; + uint32_t duration; + uint32_t samples; + float max_value, avg_value, min_value; + uint8_t max_prec, avg_prec, min_prec; + uint32_t start_stamp; + } rec_info; + struct { + size_t rec_idx; + size_t samples_total; + size_t samples_curr; + uint8_t samples_chunk; + } rec_data; + struct { + enum ut181_cmd_code code; + uint16_t data; + } reply_data; +}; + +enum ut181a_data_source { + DATA_SOURCE_LIVE, + DATA_SOURCE_SAVE, + DATA_SOURCE_REC_FIRST, + DATA_SOURCE_MAX = DATA_SOURCE_REC_FIRST + MAX_REC_COUNT, +}; + struct dev_context { + struct sr_sw_limits limits; + enum ut181a_data_source data_source; + size_t data_source_count; + const char *data_source_names[DATA_SOURCE_MAX + 1]; + size_t record_count; + char record_names[MAX_REC_COUNT][MAX_REC_NAMELEN]; + gboolean is_monitoring; + gboolean is_recording; + + /* Reception of serial communication data. */ + uint8_t recv_buff[RECV_BUFF_SIZE]; + size_t recv_count; + + /* Meter's internal state tracking. */ + int disable_feed; + gboolean frame_started; + struct ut181a_info info; + + /* Management for request/response pairs. */ + struct wait_state { + gboolean want_code, got_code; + enum ut181_cmd_code want_data; gboolean got_data; + enum ut181_rsp_type want_rsp_type; + gboolean got_rsp_type; + gboolean want_measure, got_measure; + gboolean got_rec_count; + gboolean got_save_count; + gboolean got_sample_count; + size_t response_count; + gboolean code_ok; + size_t data_value; + } wait_state; + struct { + char unit_text[12]; + } last_data; }; -SR_PRIV int uni_t_ut181a_receive_data(int fd, int revents, void *cb_data); +SR_PRIV const struct mqopt_item *ut181a_get_mqitem_from_mode(uint16_t mode); +SR_PRIV uint16_t ut181a_get_mode_from_mq_flags(enum sr_mq mq, enum sr_mqflag mqflags); +SR_PRIV GVariant *ut181a_get_mq_flags_list_item(enum sr_mq mq, enum sr_mqflag mqflag); +SR_PRIV GVariant *ut181a_get_mq_flags_list(void); + +SR_PRIV int ut181a_send_cmd_monitor(struct sr_serial_dev_inst *serial, gboolean on); +SR_PRIV int ut181a_send_cmd_setmode(struct sr_serial_dev_inst *serial, uint16_t mode); +SR_PRIV int ut181a_send_cmd_setrange(struct sr_serial_dev_inst *serial, uint8_t range); +SR_PRIV int ut181a_send_cmd_get_save_count(struct sr_serial_dev_inst *serial); +SR_PRIV int ut181a_send_cmd_get_saved_value(struct sr_serial_dev_inst *serial, size_t idx); +SR_PRIV int ut181a_send_cmd_get_recs_count(struct sr_serial_dev_inst *serial); +SR_PRIV int ut181a_send_cmd_get_rec_info(struct sr_serial_dev_inst *serial, size_t idx); +SR_PRIV int ut181a_send_cmd_get_rec_samples(struct sr_serial_dev_inst *serial, size_t idx, size_t off); + +SR_PRIV int ut181a_configure_waitfor(struct dev_context *devc, + gboolean want_code, enum ut181_cmd_code want_data, + enum ut181_rsp_type want_rsp_type, + gboolean want_measure, gboolean want_rec_count, + gboolean want_save_count, gboolean want_sample_count); +SR_PRIV int ut181a_waitfor_response(const struct sr_dev_inst *sdi, int timeout_ms); + +SR_PRIV int ut181a_handle_events(int fd, int revents, void *cb_data); + +SR_PRIV GVariant *ut181a_get_ranges_list(void); +SR_PRIV const char *ut181a_get_range_from_packet_bytes(struct dev_context *devc); +SR_PRIV int ut181a_set_range_from_text(const struct sr_dev_inst *sdi, const char *text); #endif