SR_CONF_ENABLED | SR_CONF_GET | SR_CONF_SET,
};
+static const uint32_t devopts_cg_di[] = {
+ SR_CONF_ENABLED | SR_CONF_GET,
+};
+
static const uint32_t devopts_cg_ai[] = {
SR_CONF_VOLTAGE | SR_CONF_GET,
};
static const struct devantech_eth008_model models[] = {
- { 18, "ETH002", 2, 0, 1, 0, },
- { 19, "ETH008", 8, 0, 1, 0, },
- { 20, "ETH484", 16, 0, 2, 0x00f0, },
- { 21, "ETH8020", 20, 0, 3, 0, },
+ { 18, "ETH002", 2, 0, 0, 0, 1, 0, },
+ { 19, "ETH008", 8, 0, 0, 0, 1, 0, },
+ { 20, "ETH484", 16, 8, 4, 0, 2, 0x00f0, },
+ { 21, "ETH8020", 20, 8, 8, 0, 3, 0, },
};
static const struct devantech_eth008_model *find_model(uint8_t code)
gboolean has_serno_cmd;
char snr_txt[16];
struct channel_group_context *cgc;
- size_t ch_idx, nr, do_idx;
+ size_t ch_idx, nr, do_idx, di_idx, ai_idx;
struct sr_channel_group *cg;
char cg_name[24];
int ret;
ch_idx = 0;
devc->mask_do = (1UL << devc->model->ch_count_do) - 1;
- devc->mask_do &= ~model->mask_do_missing;
+ devc->mask_do &= ~devc->model->mask_do_missing;
for (do_idx = 0; do_idx < devc->model->ch_count_do; do_idx++) {
nr = do_idx + 1;
- if (model->mask_do_missing & (1UL << do_idx))
+ if (devc->model->mask_do_missing & (1UL << do_idx))
continue;
snprintf(cg_name, sizeof(cg_name), "DO%zu", nr);
cgc = g_malloc0(sizeof(*cgc));
(void)cg;
ch_idx++;
}
+ for (di_idx = 0; di_idx < devc->model->ch_count_di; di_idx++) {
+ nr = di_idx + 1;
+ snprintf(cg_name, sizeof(cg_name), "DI%zu", nr);
+ cgc = g_malloc0(sizeof(*cgc));
+ cg = sr_channel_group_new(sdi, cg_name, cgc);
+ cgc->index = di_idx;
+ cgc->number = nr;
+ cgc->ch_type = DV_CH_DIGITAL_INPUT;
+ (void)cg;
+ ch_idx++;
+ }
+ for (ai_idx = 0; ai_idx < devc->model->ch_count_ai; ai_idx++) {
+ nr = ai_idx + 1;
+ snprintf(cg_name, sizeof(cg_name), "AI%zu", nr);
+ cgc = g_malloc0(sizeof(*cgc));
+ cg = sr_channel_group_new(sdi, cg_name, cgc);
+ cgc->index = ai_idx;
+ cgc->number = nr;
+ cgc->ch_type = DV_CH_ANALOG_INPUT;
+ (void)cg;
+ ch_idx++;
+ }
if (1) {
/* Create an analog channel for the supply voltage. */
snprintf(cg_name, sizeof(cg_name), "Vsupply");
*data = g_variant_new_boolean(on);
return SR_OK;
}
+ if (cgc->ch_type == DV_CH_DIGITAL_INPUT) {
+ ret = devantech_eth008_query_di(sdi, cg, &on);
+ if (ret != SR_OK)
+ return ret;
+ *data = g_variant_new_boolean(on);
+ return SR_OK;
+ }
return SR_ERR_NA;
case SR_CONF_VOLTAGE:
+ if (cgc->ch_type == DV_CH_ANALOG_INPUT) {
+ ret = devantech_eth008_query_ai(sdi, cg, &vin);
+ if (ret != SR_OK)
+ return ret;
+ *data = g_variant_new_uint32(vin);
+ return SR_OK;
+ }
if (cgc->ch_type == DV_CH_SUPPLY_VOLTAGE) {
ret = devantech_eth008_query_supply(sdi, cg, &vin);
if (ret != SR_OK)
*data = std_gvar_array_u32(ARRAY_AND_SIZE(devopts_cg_do));
return SR_OK;
}
+ if (cgc->ch_type == DV_CH_DIGITAL_INPUT) {
+ *data = std_gvar_array_u32(ARRAY_AND_SIZE(devopts_cg_di));
+ return SR_OK;
+ }
+ if (cgc->ch_type == DV_CH_ANALOG_INPUT) {
+ *data = std_gvar_array_u32(ARRAY_AND_SIZE(devopts_cg_ai));
+ return SR_OK;
+ }
if (cgc->ch_type == DV_CH_SUPPLY_VOLTAGE) {
*data = std_gvar_array_u32(ARRAY_AND_SIZE(devopts_cg_ai));
return SR_OK;
* See http://www.robot-electronics.co.uk/files/eth008b.pdf for device
* capabilities and a protocol discussion.
* See https://github.com/devantech/devantech_eth_python for Python
- * source code which is maintained by the vendor.
+ * source code which is maintained by the vendor. The untested parts
+ * of this sigrok driver are based on version 0.1.2 of this Python
+ * code which is MIT licensed (corresponds to commit 0c0080b88e29),
+ * and example code in ZIP archives provided on the shop's products'
+ * pages.
*
* The device provides several means of communication: HTTP requests
* (as well as an interactive web form). Raw TCP communication with
* TODO
* - Add support for other models. Currently exclusively supports the
* ETH008-B model which was used during implementation of the driver.
+ * (Descriptions for more models were added, their operation is yet
+ * to get verified.) Getting relay state involves variable length
+ * responses, bits appear to be in little endian presentation.
+ * - Add support for absent relay output channels (ETH484 lacks R5..R8).
+ * - Add support for digital inputs. ETH484 has command 0x25 which gets
+ * two bytes, the second byte carries eight digital input bits.
+ * ETH1610 has 16 inputs, evaluates both bytes. Is data format u16be?
+ * ETH8020 support code is inconsistent, implements two accessors
+ * which either retrieve two or three bytes, while callers access the
+ * fourth byte of these responses? Cannot have worked, seems untested.
+ * - Add support for analog inputs. ETH484 has command 0x32 which takes
+ * a channel number, and gets two bytes which carry a u16be value(?).
+ * So does ETH8020. Channel count differs across models.
+ * - Are there other models of interest? ETH1610 product page reads
+ * as if the card had 10 relays (strict output), and 16 inputs which
+ * could either be used in analog mode, or simply get interpreted as
+ * digital input?
* - Add support for password protection?
* - See command 0x79 to "login" (beware of the differing return value
* compared to other commands), command 0x7a to check if passwords
CMD_DIGITAL_INACTIVE = 0x21,
CMD_DIGITAL_SET_OUTPUTS = 0x23,
CMD_DIGITAL_GET_OUTPUTS = 0x24,
+ CMD_DIGITAL_GET_INPUTS = 0x25,
+ CMD_ANALOG_GET_INPUT = 0x32,
CMD_ASCII_TEXT_COMMAND = 0x3a,
CMD_GET_SERIAL_NUMBER = 0x77,
CMD_GET_SUPPLY_VOLTS = 0x78,
if (!devc)
return SR_ERR_ARG;
+ /* Unconditionally get the state of digital output. */
rx_size = devc->model->width_do;
if (rx_size > sizeof(rsp))
return SR_ERR_NA;
have &= devc->mask_do;
devc->curr_do = have;
+ /*
+ * Get the state of digital inputs when the model supports them.
+ * Firmware of other models happens to not respond to unknown
+ * requests. Responses seem to have identical size across all
+ * models. Payload is assumed to be u16 be formatted. Must be
+ * verified when other models are seen.
+ *
+ * Caching the state of analog inputs is condidered undesirable.
+ */
+ if (devc->model->ch_count_di) {
+ rx_size = sizeof(uint16_t);
+ if (rx_size > sizeof(rsp))
+ return SR_ERR_NA;
+
+ wrptr = req;
+ write_u8_inc(&wrptr, CMD_DIGITAL_GET_INPUTS);
+ ret = send_then_recv(serial, req, wrptr - req, rsp, rx_size);
+ if (ret != SR_OK)
+ return ret;
+ rdptr = rsp;
+
+ switch (rx_size) {
+ case 2:
+ have = read_u16be_inc(&rdptr);
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+ have &= (1UL << devc->model->ch_count_di) - 1;
+ devc->curr_di = have;
+ }
+
return SR_OK;
}
return SR_OK;
}
+SR_PRIV int devantech_eth008_query_di(const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg, gboolean *on)
+{
+ struct dev_context *devc;
+ struct channel_group_context *cgc;
+ uint32_t have;
+ int ret;
+
+ /* Unconditionally update the internal cache. */
+ ret = devantech_eth008_cache_state(sdi);
+ if (ret != SR_OK)
+ return ret;
+
+ /*
+ * Only reject unexpected requeusts after the update. Get the
+ * individual channel's state from the cache of all channels.
+ */
+ devc = sdi->priv;
+ if (!devc)
+ return SR_ERR_ARG;
+ if (!cg)
+ return SR_ERR_ARG;
+ cgc = cg->priv;
+ if (!cgc)
+ return SR_ERR_BUG;
+ if (cgc->index >= devc->model->ch_count_di)
+ return SR_ERR_ARG;
+ have = devc->curr_di;
+ have >>= cgc->index;
+ have &= 1 << 0;
+ if (on)
+ *on = have ? TRUE : FALSE;
+
+ return SR_OK;
+}
+
+SR_PRIV int devantech_eth008_query_ai(const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg, uint16_t *adc_value)
+{
+ struct sr_serial_dev_inst *serial;
+ struct dev_context *devc;
+ struct channel_group_context *cgc;
+ uint8_t req[2], *wrptr;
+ uint8_t rsp[2];
+ const uint8_t *rdptr;
+ uint32_t have;
+ int ret;
+
+ serial = sdi->conn;
+ if (!serial)
+ return SR_ERR_ARG;
+ devc = sdi->priv;
+ if (!devc)
+ return SR_ERR_ARG;
+ if (!cg)
+ return SR_ERR_ARG;
+ cgc = cg->priv;
+ if (!cgc)
+ return SR_ERR_ARG;
+ if (cgc->index >= devc->model->ch_count_ai)
+ return SR_ERR_ARG;
+
+ wrptr = req;
+ write_u8_inc(&wrptr, CMD_ANALOG_GET_INPUT);
+ write_u8_inc(&wrptr, cgc->number & 0xff);
+ ret = send_then_recv(serial, req, wrptr - req, rsp, sizeof(rsp));
+ if (ret != SR_OK)
+ return ret;
+ rdptr = rsp;
+
+ /*
+ * TODO The u16 BE format is a guess. Needs verification.
+ * As is the unit-less nature of that value.
+ */
+ have = read_u16be_inc(&rdptr);
+ if (adc_value)
+ *adc_value = have;
+
+ return SR_OK;
+}
+
SR_PRIV int devantech_eth008_query_supply(const struct sr_dev_inst *sdi,
const struct sr_channel_group *cg, uint16_t *millivolts)
{
* same time. Some models have gaps in their relay channel numbers
* (ETH484 misses R5..R8).
*
- * Some models have digital inputs and analog inputs. These features
- * currently are not supported in this implementation.
+ * ETH484 also has 8 digital inputs, and 4 analog inputs. Features
+ * beyond relay output are untested in this implementation.
+ *
+ * Vendor's support code for ETH8020 suggests that it has 8 digital
+ * inputs and 8 analog inputs. But that digital input supporting code
+ * could never have worked, probably wasn't tested.
+ *
+ * Digital inputs and analog inputs appear to share I/O pins. Users can
+ * read these pins either in terms of an ADC value, or can interpret
+ * them as raw digital input. While not all models with digital inputs
+ * seem to provide all of them in analog form. DI and AI channel counts
+ * may differ depending on the model.
*/
struct devantech_eth008_model {
uint8_t code;
const char *name;
size_t ch_count_do;
+ size_t ch_count_di;
+ size_t ch_count_ai;
uint8_t min_serno_fw;
size_t width_do;
uint32_t mask_do_missing;
enum devantech_eth008_channel_type {
DV_CH_DIGITAL_OUTPUT,
+ DV_CH_DIGITAL_INPUT,
+ DV_CH_ANALOG_INPUT,
DV_CH_SUPPLY_VOLTAGE,
};
const struct devantech_eth008_model *model;
uint32_t mask_do;
uint32_t curr_do;
+ uint32_t curr_di;
};
SR_PRIV int devantech_eth008_get_model(struct sr_serial_dev_inst *serial,
const struct sr_channel_group *cg, gboolean *on);
SR_PRIV int devantech_eth008_setup_do(const struct sr_dev_inst *sdi,
const struct sr_channel_group *cg, gboolean on);
+SR_PRIV int devantech_eth008_query_di(const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg, gboolean *on);
+SR_PRIV int devantech_eth008_query_ai(const struct sr_dev_inst *sdi,
+ const struct sr_channel_group *cg, uint16_t *adc_value);
SR_PRIV int devantech_eth008_query_supply(const struct sr_dev_inst *sdi,
const struct sr_channel_group *cg, uint16_t *millivolts);