From c14df1a541821430c061c98e9a99bad1951115b6 Mon Sep 17 00:00:00 2001 From: Martin Mares Date: Mon, 26 Dec 2011 21:34:53 +0100 Subject: [PATCH] The first experimental version --- Makefile | 10 ++ arexx.c | 290 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 300 insertions(+) create mode 100644 Makefile create mode 100644 arexx.c diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..e136433 --- /dev/null +++ b/Makefile @@ -0,0 +1,10 @@ +CC=gcc +LD=gcc +CFLAGS=-O2 -Wall -W -Wno-parentheses -Wstrict-prototypes -Wmissing-prototypes -Wundef -Wredundant-decls -std=gnu99 +LDLIBS=-lusb-1.0 -lm + +all: arexx + +clean: + rm -f `find . -name "*~" -or -name "*.[oa]" -or -name "\#*\#" -or -name TAGS -or -name core -or -name .depend -or -name .#*` + rm -f arexx diff --git a/arexx.c b/arexx.c new file mode 100644 index 0000000..04825ce --- /dev/null +++ b/arexx.c @@ -0,0 +1,290 @@ +/* + * Linux Interfece for Arexx Data Loggers + * + * (c) 2011 Martin Mares + */ + +#include +#include +#include +#include +#include +#include +#include + +typedef unsigned char byte; +static libusb_context *usb_ctxt; +static libusb_device_handle *devh; + +static int debug_packets = 0; + +#define TIME_OFFSET 946681200 // Timestamp of 2000-01-01 00:00:00 + +static void die(char *msg, ...) +{ + va_list args; + va_start(args, msg); + vfprintf(stderr, msg, args); + fputc('\n', stderr); + va_end(args); + exit(1); +} + +static void find_device(void) +{ + libusb_device **devlist; + ssize_t devn = libusb_get_device_list(usb_ctxt, &devlist); + if (devn < 0) + die("Cannot enumerate USB devices: error %d\n", (int) devn); + + for (ssize_t i=0; i> xmit %d bytes\n", transferred); + dump_packet(req); + } + if (err = libusb_bulk_transfer(devh, 0x81, reply, 64, &transferred, 200)) { + fprintf(stderr, "Receive error: %d\n", err); + return 0; + } + if (debug_packets) + printf("<< recv %d bytes\n", transferred); + while (transferred < 64) + reply[transferred++] = 0xff; + if (debug_packets) + dump_packet(reply); + return 1; +} + +static unsigned int get_be16(byte *p) +{ + return p[1] | (p[0] << 8); +} + +static unsigned int get_le16(byte *p) +{ + return p[0] | (p[1] << 8); +} + +static unsigned int get_le32(byte *p) +{ + return get_le16(p) | (get_le16(p+2) << 16); +} + +static void put_le16(byte *p, unsigned int x) +{ + p[0] = x; + p[1] = x >> 8; +} + +static void put_le32(byte *p, unsigned int x) +{ + put_le16(p, x); + put_le16(p+2, x>>16); +} + +static void raw_point(int t, int id, int raw) +{ + /* + * The binary blob provided by Arexx contains an embedded XML fragment + * with descriptions of all known sensor types. If you want to see it, + * grep the blob for "". The meanings of the parameters are + * as follows: + * + * m1, m2 Device type matches if raw_sensor_id & m1 == m2 + * type Unit measured by the sensor (1=Celsius, 2=RH%, 3=CO2 ppm) + * dm User-visible sensor ID = raw_sensor_id & dm + * i 1 if the raw value is signed + * p[] Coefficients of transformation polynomial (x^0 first) + * vLo, vUp Upper and lower bound on the final value + * scale Scaling function: + * 0 = identity (default) + * 1 = 10^x + * 2 = exp(x) + * 3 = (x < 0) ? 0 : log10(x) + * 4 = (x < 0) ? 0 : log(x) + * + * The raw values are transformed this way: + * - sign-extend if signed + * - apply the transformation polynomial + * - apply the scaling function + * - drop if outside the interval [vLo,vUp] + * + * This function applies the necessary transform for sensors we've + * seen in the wild. + */ + + double z = raw; + double hi, lo; + char *unit; + int idhi = id & 0xf000; + + if (idhi == 0x1000) { + z = 0.02*z - 273.15; + lo = -200; + hi = 600; + unit = "C"; + } else if (idhi == 0x2000) { + if (raw >= 0x8000) + z -= 0x10000; + z /= 128; + lo = -60; + hi = 125; + unit = "C"; + } else if (idhi == 0x4000) { + if (!(id & 1)) { + z = z/100 - 39.6; + lo = -60; + hi = 125; + unit = "C"; + } else { + z = -2.8e-6*z*z + 0.0405*z - 4; + lo = 0; + hi = 100.1; + unit = "%RH"; + } + } else if (idhi == 0x6000) { + if (!(id & 1)) { + if (raw >= 0x8000) + z -= 0x10000; + z /= 128; + lo = -60; + hi = 125; + unit = "C"; + } else { + z = -3.8123e-11*z; + z = (z + 1.9184e-7) * z; + z = (z - 1.0998e-3) * z; + z += 6.56; + z = pow(10, z); + lo = 0; + hi = 1e6; + unit = "ppm"; + } + } else { + printf("### Unknown sensor ID 0x%04x\n", id); + return; + } + + if (z < lo || z > hi) { + printf("### Sensor %d: value %f out of range\n", id, z); + return; + } + + printf("\t-> %f %s\n", z, unit); +} + +static int parse_packet(byte *reply) +{ + if (reply[0]) { + printf("### Unknown packet type %02x\n", reply[0]); + return 0; + } + + int pos = 1; + int points = 0; + while (pos < 64) { + byte *p = reply + pos; + int len = p[0]; + if (!len || len == 0xff) + break; + if (len < 9 || len > 10) { + printf("### Unknown packet length %02x\n", len); + break; + } + if (pos + len > 64) { + printf("### Packet truncated\n"); + break; + } + int id = get_le16(p+1); + int raw = get_be16(p+3); + int t = get_le32(p+5); + printf("... %02x: id=%d raw=%d t=%d", len, id, raw, t); + if (len > 9) + printf(" q=%d", p[9]); + printf("\n"); + raw_point(t, id, raw); + pos += len; + points++; + } + + return points; +} + +static void set_clock(void) +{ + puts("### Syncing time"); + + byte req[64], reply[64]; + memset(req, 0, 64); + req[0] = 4; + time_t t = time(NULL); + put_le32(req+1, t-TIME_OFFSET); + send_and_receive(req, reply); + +#if 0 + req[0] = 3; + send_and_receive(req, reply); + parse_packet(reply); +#endif +} + +int main(void) +{ + int err; + if (err = libusb_init(&usb_ctxt)) + die("Cannot initialize libusb: error %d", err); + libusb_set_debug(usb_ctxt, 3); + + find_device(); + set_clock(); + + for (;;) { + byte req[64], reply[64]; + memset(req, 0, sizeof(req)); + req[0] = 3; + if (send_and_receive(req, reply)) { + if (parse_packet(reply)) + continue; + } + sleep(4); + } + + return 0; +} -- 2.39.2