The Arexx TL-500 is a well-priced temperature and humidity logging system.
But with only twelve direct requests for a Linux USB drivers, Arexx recently decided to abandon their plans creating a Linux driver for this device (I think there is a much higher demand, but only 12 people directly asked for Linux support). Since my TL-500 is connected to a Linux server and I don’t want to have Windows running in a virtual machine all the time, I decided to write a Linux driver for the TL-500 myself.
Let’s connect the TL-500 to a Linux box and take a look at the device and it’s endpoints:
[...]
Bus 003 Device 002: ID 0451:3211 Texas Instruments, Inc.
Device Descriptor:
bLength 18
bDescriptorType 1
bcdUSB 1.10
bDeviceClass 255 Vendor Specific Class
bDeviceSubClass 0
bDeviceProtocol 0
bMaxPacketSize0 8
idVendor 0x0451 Texas Instruments, Inc.
idProduct 0x3211
bcdDevice 1.00
iManufacturer 0
iProduct 0
iSerial 0
bNumConfigurations 1
Configuration Descriptor:
bLength 9
bDescriptorType 2
wTotalLength 32
bNumInterfaces 1
bConfigurationValue 1
iConfiguration 0
bmAttributes 0x80
(Bus Powered)
MaxPower 100mA
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 0
bAlternateSetting 0
bNumEndpoints 2
bInterfaceClass 255 Vendor Specific Class
bInterfaceSubClass 0
bInterfaceProtocol 0
iInterface 0
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x01 EP 1 OUT
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0040 1x 64 bytes
bInterval 0
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x81 EP 1 IN
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0040 1x 64 bytes
bInterval 0
Device Status: 0x0000
(Bus Powered)
Aside from the control endpoint there are two bulk endpoints. (If you are not that familiar with USB, you perhaps want to read some pages from chapter 13 “USB Drivers” from the book Linux Device Drivers, which is available from O’Reilly and also freely available as pdf under creative commons license.) The endpoint with address 0×01 transfers data from the computer to the device (OUT endpoint – this direction is called “down”) and the endpoint with address 0×81 transfers data from the device to the computer (IN endpoint – this direction is called “up”).
But how do we get the logging data from the device? Since Arexx neither answered my emails nor responded to the forum posts, I used the Windows USB Sniffer SnoopyPro for unscrambling the communication.
If you have installed SnoopyPro and started, first open “View -> USB Devices“, then select “File -> Unpack drivers” and after that “File -> Install Service” from the menu of the USB Devices window. Right-click on the appropriate device (the prior lsusb told us the ID 0451:3211) and select “Install and Restart“.
An internal window “USBLog1″ should open, counting the recorded packets. If you have recorded enough, stop the recording and take a look at the output:
After a few times repeating this process the following pattern emerges:
- The computer sends data starting with 0×04.
- After that he sends data starting with 0×03. The TL-500 responds with data that starts with 0×0000 or with data that actually contains logging data. This repeats ad infinitum.
- Rarely the loop hangs and the computer resends again data starting with 0×03.
Fortunately with the library libusb nowadays writing a USB device driver is no big problem. (If you don’t want to use libusb but instead get involved with the kernel programming, take a look at the tutorial Writing a Linux kernel driver for an unknown USB device from Matthias Vallentin.) But let’s start with libusb and their example file under LGPL and implement what we have discovered so far:
#include <sys/types.h>
#include <unistd.h>
#include <libusb.h>
uint16_t VENDOR = 1105; /* =0x0451 */
uint16_t PRODUCT = 12817; /* =0x3211 */
libusb_device_handle** find_tl500(libusb_device **devs) {
libusb_device *dev;
libusb_device_handle **handle;
int i = 0;
printf("Trying to find Arexx logging system.\n");
while ((dev = devs[i++]) != NULL) {
struct libusb_device_descriptor desc;
int r = libusb_get_device_descriptor(dev, &desc);
if (r < 0) {
fprintf(stderr, "failed to get device descriptor");
return NULL;
}
printf("%04x:%04x (bus %d, device %d)\n",
desc.idVendor, desc.idProduct,
libusb_get_bus_number(dev), libusb_get_device_address(dev));
if (desc.idVendor == VENDOR && desc.idProduct == PRODUCT) {
printf("Found Arexx TL-500.\n");
int usb_open = libusb_open(dev, handle);
if (usb_open==0) {
printf("libusb_open successful.\n");
return handle;
}
fprintf(stderr, "libusb_open failed. Error code %d.\n", usb_open);
return handle;
}
}
return NULL;
}
int main() {
libusb_device **devs;
int r;
ssize_t cnt;
r = libusb_init(NULL);
if (r < 0)
return r;
cnt = libusb_get_device_list(NULL, &devs);
if (cnt < 0)
return (int) cnt;
libusb_device_handle **handle = find_tl500(devs);
if (handle==NULL) {
fprintf(stderr, "No logging system found.\n");
libusb_free_device_list(devs, 1);
libusb_exit(NULL);
return -1;
}
unsigned char ENDPOINT_DOWN = 0x1;
unsigned char ENDPOINT_UP = 0x81;
int actual_length;
unsigned char dataUp[64];
unsigned char dataDown[64];
for(int j=0;j<64;j++) { dataDown[j] = 0; }
dataDown[0] = 4;
libusb_bulk_transfer(handle[0], ENDPOINT_DOWN, dataDown, sizeof(dataDown), &actual_length, 0);
dataDown[0] = 3;
for(int i=0; i<10000; i++) {
r = libusb_bulk_transfer(handle[0], ENDPOINT_DOWN, dataDown,
sizeof(dataDown), &actual_length, 0);
r = libusb_bulk_transfer(handle[0], ENDPOINT_UP, dataUp,
sizeof(dataUp), &actual_length, 1000);
if (r == 0 && actual_length == sizeof(dataUp)) {
if (dataUp[0]!=0 || dataUp[1]!=0) {
printf("Data: ");
for(int j=0;j<64;j++) {
printf("%02x ",dataUp[j]);
}
printf("\n");
}
sleep(1);
} else {
printf("Something went wrong (r == %i, actual_length == %i , sizeof(data) == %lu ).\n",
r, actual_length, sizeof(dataUp));
}
}
libusb_free_device_list(devs, 1);
libusb_exit(NULL);
return 0;
}
If you want to test it, download this file with the code above and compile it (make sure you have the libusb development files installed – e.g. under Ubuntu the package libusb-dev):
g++ -otl500 tl500.o -lusb-1.0
If you run the executable tl500, it should find the TL-500 and will request data from it:
1d6b:0002 (bus 1, device 1)
1d6b:0002 (bus 2, device 1)
1d6b:0001 (bus 3, device 1)
1d6b:0001 (bus 4, device 1)
0451:3211 (bus 4, device 3)
Found Arexx TL-500.
Data: 00 0a 07 48 05 b4 04 00 00 00 0b 00 ff 01 00 00 00 01 09 02 19 00 01 01 00 80 32 09 04 00 00 01 ff 00 00 00 07 05 01 02 40 00 00 36 21 00 00 f1 b5 ad 76 af 7d c1 f2 47 6a 7f bf ed 31 2e 3f 09
Data: 00 0a cc 22 0a e7 1e 00 00 00 07 00 ff 01 00 00 00 01 09 02 19 00 01 01 00 80 32 09 04 00 00 01 ff 00 00 00 07 05 01 02 40 00 00 36 21 00 00 f1 b5 ad 76 af 7d c1 f2 47 6a 7f bf ed 31 2e 3f 09
Data: 00 0a 06 48 17 fe 2b 00 00 00 14 00 ff 01 00 00 00 01 09 02 19 00 01 01 00 80 32 09 04 00 00 01 ff 00 00 00 07 05 01 02 40 00 00 36 21 00 00 f1 b5 ad 76 af 7d c1 f2 47 6a 7f bf ed 31 2e 3f 09
Data: 00 0a db 45 05 67 3b 00 00 00 0c 0a 72 22 0b 07 3b 00 00 00 0a 00 01 01 00 80 32 09 04 00 00 01 ff 00 00 00 07 05 01 02 40 00 00 36 21 00 00 f1 b5 ad 76 af 7d c1 f2 47 6a 7f bf ed 31 2e 3f 09
Data: 00 0a cc 22 0a e7 50 00 00 00 07 00 ff 22 0b 07 3b 00 00 00 0a 00 01 01 00 80 32 09 04 00 00 01 ff 00 00 00 07 05 01 02 40 00 00 36 21 00 00 f1 b5 ad 76 af 7d c1 f2 47 6a 7f bf ed 31 2e 3f 09
How to decipher these data rows to meaningful logging data will be a blog post on its own.



Pingback: Linux USB driver for the Arexx TL-500 – Part II « Algorithm-Forge