RevEng the USB data

So I've found my SPL instrument (here) but unfortunately it does run only on Windows XP.

My plan was to use RaspberryPi and Linux to build the environmental monitoring station and I had to find a way to retrieve data from the AR844 SPL meter in realtime... But without manuals and open source drivers ... how to make it?

The only solution is to do some reverse engineering.
I had to find those bits in the protocol that I can use to compute the measure and do my own statistics.

The most professional solution would be to have an USB protocol analyzer, for example the excellent "Beagle USB 12 Protocol Analyzer" available for just around 400USD at Adafruit, but I don't have it (!) And even worst they don't ship to my country (how lucky am I?)

The solution came with an open source USB sniffer tool called USBSNOOP:

The tool works under Windows (great!!) and once you launch you can open the USB device list by pressing F2.

That is the list of your connected USB devices and you want to find the one you care about to install the correct sniffer hook. Now the question is: what vendor_id and product_id are used by the AR844 unit? Easy answer..

Connecting the AR844 to my linux laptop and using libusb you can actually browse all the details of the unit:

 #> lsusb -v 

And you will see something like this output:

Bus 001 Device 005: ID 1234:5678 Brain Actuated Technologies
Device Descriptor:
  bLength                18
  bDescriptorType         1
  bcdUSB               1.10
  bDeviceClass            0 (Defined at Interface level)
  bDeviceSubClass         0
  bDeviceProtocol         0
  bMaxPacketSize0         8
  idVendor           0x1234 Brain Actuated Technologies
  idProduct          0x5678
  bcdDevice            0.00
  iManufacturer           1 SM
  iProduct                2 SM
  iSerial                 0
  bNumConfigurations      1
  Configuration Descriptor:
    bLength                 9
    bDescriptorType         2
    wTotalLength           41
    bNumInterfaces          1
    bConfigurationValue     1
    iConfiguration          0
    bmAttributes         0x80
      (Bus Powered)
    MaxPower               50mA
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        0
      bAlternateSetting       0
      bNumEndpoints           2
      bInterfaceClass         3 Human Interface Device
      bInterfaceSubClass      0 No Subclass
      bInterfaceProtocol      0 None
      iInterface              0
        HID Device Descriptor:
          bLength                 9
          bDescriptorType        33
          bcdHID               1.10
          bCountryCode            0 Not supported
          bNumDescriptors         1
          bDescriptorType        34 Report
          wDescriptorLength      52
         Report Descriptors:
           ** UNAVAILABLE **
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x02  EP 2 OUT
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0008  1x 8 bytes
        bInterval               0
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x81  EP 1 IN
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0008  1x 8 bytes
        bInterval               0
Device Status:     0xb908
  (Bus Powered)

Hell this is a lot of informations ... but we need only few of them:

  • idVendor           0x1234 Brain Actuated Technologies
  • idProduct          0x5678
  • The unit has one input endpoint (dataflow going from AR844 to the computer) at add address 0x81, transfer is 8 bytes, interrupt
  • The unit has one output endpoint, address 0x02, 8bytes data, interrupt

So I did install the usbsniffer on the correct id vendor/product while I was connected to the VoiceLab. I wanted to understand what messaging was going on to retrieve the measure and for this purpose I did use Voicelab in manual mode, making one reading for each time I pressed the button.

By looking at the log I was able to understand the following:

  • every time I want to trigger a reading from the instrument I have to send a command on the output endpoint with the "b3 00 00 00 00 00 00" data. I'm sure there is more in those bits, but with all the test I did the word "b3" was always sent and so I use it as a constant value.
  • the AR844 does return a measure (8 bytes) and somehow tha last part seems not to change a lot..
  • here are some USBSniffer logs I've used to reverse the data format: USBLog1 USBLog3

The next step was to manually correlate all those logs with the data I saw from the unit.

Note that it was not just about the number itself, but also all the other important informations: the scale type (A or C) and the measure range set on the unit (30-130dB, 30-80dB etc etc)

I did a spreadsheet and started to stair at the results, here is a table for log n.3: AR844_test3

After a while I was able to retrieve the informations I needed: EUREKA!

It turns out the protocol is pretty simple: we just need the first 3 bytes.

The first two bytes are the one that give you the SPL measure value:

	SPL_measure = (Byte0 *256 + Byte1 ) /10


While the 3rd byte is the one containing informations about the curve type (A or C) and the measure range. This time it is a bitmask:

	bit0(LSB) : measure range bit0
	bit1      : measure range bit1
	bit2      : measure range bit2
	bit3      : NC
	bit4      : curve type, A=1, C=0
	bit5      : NC
	bit6      : sampling type, SLOW=1, FAST=0
	bit7      : ??

The measure range is specified by 3bits because the unit has 3 levels of sensitivity: 30-130, 30-80, 50-100 and 60-110. Ranges are expressed by these values of the last 3 bits of the 3rd byte:

  • 0 = range 30 to 180dB
  • 1 = range 30 to 80dB
  • 2 = range 50 to 100dB
  • 3 = range 60 to 110dB

The next step was to read the usb data from Linux ... libusb!