]> mj.ucw.cz Git - arexx.git/commitdiff
The first experimental version
authorMartin Mares <mj@ucw.cz>
Mon, 26 Dec 2011 20:34:53 +0000 (21:34 +0100)
committerMartin Mares <mj@ucw.cz>
Mon, 26 Dec 2011 20:34:53 +0000 (21:34 +0100)
Makefile [new file with mode: 0644]
arexx.c [new file with mode: 0644]

diff --git a/Makefile b/Makefile
new file mode 100644 (file)
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 (file)
index 0000000..04825ce
--- /dev/null
+++ b/arexx.c
@@ -0,0 +1,290 @@
+/*
+ *     Linux Interfece for Arexx Data Loggers
+ *
+ *     (c) 2011 Martin Mares <mj@ucw.cz>
+ */
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <math.h>
+#include <libusb-1.0/libusb.h>
+
+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<devn; i++) {
+               struct libusb_device_descriptor desc;
+               libusb_device *dev = devlist[i];
+               if (!libusb_get_device_descriptor(dev, &desc)) {
+                       if (desc.idVendor == 0x0451 && desc.idProduct == 0x3211) {
+                               printf("Arexx USB receiver found at usb%d.%d\n", libusb_get_bus_number(dev), libusb_get_device_address(dev));
+                               int err;
+                               if (err = libusb_open(dev, &devh))
+                                       die("libusb_open() failed: error %d\n", err);
+                               if (err = libusb_claim_interface(devh, 0))
+                                       die("libusb_claim_interface() failed: error %d\n", err);
+                               libusb_free_device_list(devlist, 1);
+                               return;
+                       }
+               }
+       }
+
+       die("No Arexx USB receiver found");
+}
+
+static void dump_packet(byte *pkt)
+{
+       for (int i=0; i<64; i++) {
+               if (!(i % 16))
+                       printf("\t%02x:", i);
+               printf(" %02x", pkt[i]);
+               if (i % 16 == 15)
+                       printf("\n");
+       }
+}
+
+static int send_and_receive(byte *req, byte *reply)
+{
+       int err, transferred;
+       if (err = libusb_bulk_transfer(devh, 0x01, req, 64, &transferred, 200)) {
+               fprintf(stderr, "Transmit error: %d\n", err);
+               return 0;
+       }
+       if (debug_packets) {
+               printf(">> 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 "<deviceinfo>". 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;
+}