X-Git-Url: https://sigrok.org/gitweb/?a=blobdiff_plain;f=hardware%2Ftesto%2Fprotocol.c;h=50bcdb457ea080bb5e9358c4996aaa106c202bd3;hb=6dcb97230eac000a4033aef0d5ec1099e5f8143d;hp=eb6075da223cfed24404271c22da9cc4043c3bb3;hpb=0acdd79357633e90bb1a398e246ad96b39dceda9;p=libsigrok.git diff --git a/hardware/testo/protocol.c b/hardware/testo/protocol.c index eb6075da..50bcdb45 100644 --- a/hardware/testo/protocol.c +++ b/hardware/testo/protocol.c @@ -17,24 +17,250 @@ * along with this program. If not, see . */ +#include #include "protocol.h" -SR_PRIV int testo_receive_data(int fd, int revents, void *cb_data) +SR_PRIV int testo_set_serial_params(struct sr_usb_dev_inst *usb) +{ + int ret; + + if ((ret = libusb_control_transfer(usb->devhdl, 0x40, FTDI_SET_BAUDRATE, + FTDI_BAUDRATE_115200, FTDI_INDEX, NULL, 0, 10)) < 0) { + sr_err("Failed to set baudrate: %s", libusb_error_name(ret)); + return SR_ERR; + } + + if ((ret = libusb_control_transfer(usb->devhdl, 0x40, FTDI_SET_PARAMS, + FTDI_PARAMS_8N1, FTDI_INDEX, NULL, 0, 10)) < 0) { + sr_err("Failed to set comm parameters: %s", libusb_error_name(ret)); + return SR_ERR; + } + + if ((ret = libusb_control_transfer(usb->devhdl, 0x40, FTDI_SET_FLOWCTRL, + FTDI_FLOW_NONE, FTDI_INDEX, NULL, 0, 10)) < 0) { + sr_err("Failed to set flow control: %s", libusb_error_name(ret)); + return SR_ERR; + } + + if ((ret = libusb_control_transfer(usb->devhdl, 0x40, FTDI_SET_MODEMCTRL, + FTDI_MODEM_ALLHIGH, FTDI_INDEX, NULL, 0, 10)) < 0) { + sr_err("Failed to set modem control: %s", libusb_error_name(ret)); + return SR_ERR; + } + + return SR_OK; +} + +/* Due to the modular nature of the Testo hardware, you can't assume + * which measurements the device will supply. Fetch a single result + * set synchronously to see which measurements it has. */ +SR_PRIV int testo_probe_channels(struct sr_dev_inst *sdi) { - const struct sr_dev_inst *sdi; struct dev_context *devc; + struct sr_usb_dev_inst *usb; + struct sr_channel *ch; + int unit, packet_len, len, i; + unsigned char packet[MAX_REPLY_SIZE], buf[MAX_REPLY_SIZE]; + char *probe_name; - (void)fd; + devc = sdi->priv; + usb = sdi->conn; - if (!(sdi = cb_data)) - return TRUE; + if (sdi->driver->dev_open(sdi) != SR_OK) + return SR_ERR; + if (testo_set_serial_params(usb) != SR_OK) + return SR_ERR; + if (libusb_bulk_transfer(usb->devhdl, EP_OUT, devc->model->request, + devc->model->request_size, &devc->reply_size, 10) < 0) + return SR_ERR; + packet_len = 0; + while(TRUE) { + if (libusb_bulk_transfer(usb->devhdl, EP_IN, buf, MAX_REPLY_SIZE, + &len, 250) < 0) + return SR_ERR; + if (len == 2) + /* FTDI cruft */ + continue; + sr_dbg("got %d", len); + if (packet_len + len - 2 > MAX_REPLY_SIZE) + return SR_ERR; + memcpy(packet + packet_len, buf + 2, len - 2); + packet_len += len - 2; - if (!(devc = sdi->priv)) - return TRUE; + if (packet_len >= 7) { + if (!testo_check_packet(packet, packet_len)) + return SR_ERR; + if (packet_len >= 7 + packet[6] * 7 + 2) + /* Got a complete packet. */ + break; + } + } - if (revents == G_IO_IN) { - /* TODO */ + if (packet[6] > MAX_CHANNELS) { + sr_err("Device says it has %d channels!", packet[6]); + return SR_ERR; } + for (i = 0; i < packet[6]; i++) { + unit = packet[7 + i * 7 + 4]; + devc->channel_units[i] = unit; + switch (unit) { + case 1: + probe_name = "Temperature"; + break; + case 3: + probe_name = "Humidity"; + break; + case 5: + probe_name = "Windspeed"; + break; + case 24: + probe_name = "Pressure"; + break; + default: + sr_dbg("Unsupported measurement unit %d", unit); + return SR_ERR; + } + ch = sr_channel_new(i, SR_CHANNEL_ANALOG, TRUE, probe_name); + sdi->channels = g_slist_append(sdi->channels, ch); + } + devc->num_channels = packet[6]; + + sdi->driver->dev_close(sdi); + + return SR_OK; +} + +SR_PRIV int testo_request_packet(const struct sr_dev_inst *sdi) +{ + struct dev_context *devc; + struct sr_usb_dev_inst *usb; + int ret; + + devc = sdi->priv; + usb = sdi->conn; + + libusb_fill_bulk_transfer(devc->out_transfer, usb->devhdl, EP_OUT, + devc->model->request, devc->model->request_size, + receive_transfer, (void *)sdi, 100); + if ((ret = libusb_submit_transfer(devc->out_transfer) != 0)) { + sr_err("Failed to request packet: %s.", libusb_error_name(ret)); + sdi->driver->dev_acquisition_stop((struct sr_dev_inst *)sdi, + devc->cb_data); + return SR_ERR; + } + sr_dbg("Requested new packet."); + + return SR_OK; +} + +/* Check if the packet is well-formed. This matches packets for the + * Testo 175/177/400/650/950/435/635/735/445/645/945/946/545. */ +SR_PRIV gboolean testo_check_packet(unsigned char *buf, int len) +{ + int i; + unsigned char check[] = { 0x21, 0, 0, 0, 1 }; + + if (len < 5) + return FALSE; + + for (i = 0; i < 5; i++) { + if (buf[i] != check[i]) { + sr_dbg("Packet has invalid prefix."); + return FALSE; + } + } + + /* TODO: check FCS, algorithm corresponds to python crcmod crc-16-mcrf4xx */ + return TRUE; } + +static float binary32_le_to_float(unsigned char *buf) +{ + GFloatIEEE754 f; + + f.v_float = 0; + f.mpn.sign = (buf[3] & 0x80) ? 1 : 0; + f.mpn.biased_exponent = (buf[3] << 1) | (buf[2] >> 7); + f.mpn.mantissa = buf[0] | (buf[1] << 8) | ((buf[2] & 0x7f) << 16); + + return f.v_float; +} + +SR_PRIV void testo_receive_packet(const struct sr_dev_inst *sdi) +{ + struct dev_context *devc; + struct sr_datafeed_packet packet; + struct sr_datafeed_analog analog; + struct sr_channel *ch; + GString *dbg; + float value; + int i; + unsigned char *buf; + + devc = sdi->priv; + sr_dbg("Got %d-byte packet.", devc->reply_size); + + if (sr_log_loglevel_get() >= SR_LOG_SPEW) { + dbg = g_string_sized_new(128); + g_string_printf(dbg, "Packet:"); + for (i = 0; i < devc->reply_size; i++) + g_string_append_printf(dbg, " %.2x", devc->reply[i]); + sr_spew("%s", dbg->str); + g_string_free(dbg, TRUE); + } + + if (!testo_check_packet(devc->reply, devc->reply_size)) + return; + + packet.type = SR_DF_ANALOG; + packet.payload = &analog; + analog.num_samples = 1; + analog.mqflags = 0; + analog.data = &value; + /* Decode 7-byte values */ + for (i = 0; i < devc->reply[6]; i++) { + buf = devc->reply + 7 + i * 7; + value = binary32_le_to_float(buf); + sr_dbg("value: %f unit %d exp %d", value, buf[4], buf[6]); + switch (buf[4]) { + case 1: + analog.mq = SR_MQ_TEMPERATURE; + analog.unit = SR_UNIT_CELSIUS; + break; + case 3: + analog.mq = SR_MQ_RELATIVE_HUMIDITY; + analog.unit = SR_UNIT_HUMIDITY_293K; + break; + case 5: + analog.mq = SR_MQ_WIND_SPEED; + analog.unit = SR_UNIT_METER_SECOND; + break; + case 24: + analog.mq = SR_MQ_PRESSURE; + analog.unit = SR_UNIT_HECTOPASCAL; + break; + default: + sr_dbg("Unsupported measurement unit %d", buf[4]); + return; + } + + /* Match this measurement with its channel. */ + for (i = 0; i < devc->num_channels; i++) { + if (devc->channel_units[i] == buf[4]) + break; + } + if (i == devc->num_channels) { + /* Shouldn't happen. */ + sr_err("Some channel hotswapped in!"); + return; + } + ch = g_slist_nth_data(sdi->channels, i); + sr_dbg("channel %d name %s unit %d", i, ch->name, analog.unit); + analog.channels = g_slist_append(NULL, ch); + sr_session_send(sdi, &packet); + g_slist_free(analog.channels); + } +} +