Monthly Archives: January 2010

Linux USB driver for the Arexx TL-500 – Part II

Last time we started communicating with the TL-500. This time we want to find out the meaning of the data rows from the IN endpoint.

Therefore we start the USB Sniffer SnoopyPro again and observe the data flow under Windows while the logger is working. We then export the USB log as XML and grep for “000a”. Now we can compare the raw data from the USB device with the recorded measurements. The first surprising thing is that in some cases SnoopyPro seems to miss some incoming data events (ca. 5% of the total events). (The other possibility is that the Windows software is interpolating data points when you export them – but I disbelieve this, since this would be rather stupid, wouldn’t it?)

By only using one sensor at a time one is able to distinguish the data for each sensor. I have the following sensors and the respective data has the following values as hexadecimal digits in the 5th-8th position:

8818: 72 22
8908: cc 22
17882: da 45
17882RH: db 45
18438: 06 48
18438RH: 07 48

After changing the order of the two halfs, the temperature sensors translate directly: 0x2272=8818, 0x22CC=8908, 0x45DA=17882 and 0x4806=18438. Also if the last bit of the first half is 0, we have a RH sensor, otherwise a temperature sensor. This would imply that all sensors have even numbers. RH sensors would have the same id incremented by 1. Is this always the case?

Just by looking at the raw data rows and exposing the sensors to different conditions, I suspect the 9-14 hexadecimal digits to contain the temperature and RH data.

Let’s look at a sample data set: tl500.csv. We load it into R and start investigating:

data <- read.table("tl500.csv", header=TRUE)

hex2int <- Vectorize(function(x) {
  v <- 0
  for (i in 1:nchar(x)) {
    w <- ifelse(substr(x,i,i)==c(as.character(0:9), letters[1:6]), 0:15, NA)
    w <- ifelse(!all(is.na(w)), w <- w[!is.na(w)], NA)
    v <- v + w*16**(nchar(x)-i)
  }
  return(v)
})

data$Sensor <- substr(data$Rawdata,5,8)
data$RawValue <- hex2int(substr(data$Rawdata,9,12))
plot(data$RawValue, data$Value)

There are obviously subgroups and in these groups a linear relationship exists between the 5th and 6th byte (9-12 hexadecimal digits) of the raw data and the measurements. Adding the 7th byte does not improve the linear correlation. (Weee! No floating point data!) Further investigation shows us that we can calculate the measurements from RH sensors by (0.6 + Rawdata * 0.03328) RH%, from temperature sensors TSN-TH70E by (-39.58 + Rawdata * 0.01) °C and from temperature sensors TL-3TSN by (Rawdata * 0.0078) °C. These coefficients are based on the the following linear regressions. With more data they will slightly change, but given the accuracy and that you need to calibrate your sensors either way, it’s a sufficient translation of uncalibrated sensor data.

rhdata <- data[data$Sensor %in% c("db45", "0748"),]
t1data <- data[data$Sensor %in% c("da45", "0648"),]
t2data <- data[data$Sensor %in% c("7222", "cc22"),]

lm(Value~RawValue,data=rhdata)
lm(Value~RawValue,data=t1data)
lm(Value~RawValue,data=t2data)

Let’s update the C program from the last time with the new results:

static int get_sensor(unsigned char data[64]) {
    return (data[3])*(256)+(data[2]);
}

static int get_value(unsigned char data[64]) {
    int value = 0;
    // We use a loop since it is not clear whether we want to use also data[6] someday.
    for (int i=4; i<=5; i++) {
        value += data[i]*pow(16,2*(5-i));
    }
    return value;
}

/**
 * We assume that all TSN-TH70E sensors have ids bigger than 10000.
 * If the id is then odd we have a humidity sensor.
 * Has anyone more information about this?
 */

static double get_measurement(unsigned char data[64]) {
    double value = get_value(data);
    int sensor = get_sensor(data);
    if (sensor < 10000) {
        return value * 0.0078;
    } else {
        if (sensor%2==0) {
           return -39.58 + value * 0.01;
        } else {
           return 0.6 + value * 0.03328;
        }
    }
}

/**
 * We assume that all TSN-TH70E sensors have ids bigger than 10000.
 * If the id is then odd we have a humidity sensor.
 * Has anyone more information about this?
 */

const char* get_unit(unsigned char data[64]) {
    int sensor = get_sensor(data);
    if (sensor > 10000 && sensor%2!=0) {
        return "%RH";
    } else {
        return "°C";
    }
}

If we now add the following line into the data reading loop we get a usefull result:

printf("From sensor %d we get a raw value %d. We guess this means %3.2f %s. n",
       get_sensor(dataUp), get_value(dataUp), get_measurement(dataUp), get_unit(dataUp));
Found Arexx TL-500.
Data: 00 0a 72 22 0c 17 05 00 00 00 15 00 ff 04 ed 1e 06 00 00 00 ff ff ff ff ff ff ff ff ff ff ff 0b ff af 05 00 00 09 07 48 04 ec c9 05 00 00 09 72 22 0b ff df 05 00 00 00 06 48 48 ed 31 2e 3f 09
From sensor 8818 we get a raw value 3095. We guess this means 24.14 °C.
Data: 00 0a 07 48 04 f5 17 00 00 00 06 00 ff 04 ed 1e 06 00 00 00 ff ff ff ff ff ff ff ff ff ff ff 0b ff af 05 00 00 09 07 48 04 ec c9 05 00 00 09 72 22 0b ff df 05 00 00 00 06 48 48 ed 31 2e 3f 09
From sensor 18439 we get a raw value 1269. We guess this means 42.83 %RH.
Data: 00 0a 72 22 0c 0f 39 00 00 00 0c 00 ff 04 ed 1e 06 00 00 00 ff ff ff ff ff ff ff ff ff ff ff 0b ff af 05 00 00 09 07 48 04 ec c9 05 00 00 09 72 22 0b ff df 05 00 00 00 06 48 48 ed 31 2e 3f 09
From sensor 8818 we get a raw value 3087. We guess this means 24.08 °C.
Data: 00 0a 06 48 18 dd 51 00 00 00 06 00 ff 04 ed 1e 06 00 00 00 ff ff ff ff ff ff ff ff ff ff ff 0b ff af 05 00 00 09 07 48 04 ec c9 05 00 00 09 72 22 0b ff df 05 00 00 00 06 48 48 ed 31 2e 3f 09
From sensor 18438 we get a raw value 6365. We guess this means 24.07 °C.

You can download the updated C file here. Now we can create our first applications for Linux.

P.S.: The connection quality is in the 21th and 22th hexadecimal digits and further blog posts will follow…