A bunch of friends/colleagues offered me a raspberry pi 3. It may become my VPN gateway, or my firewall, or the brain of my CCTV, or maybe the center of an alarm…. Maybe a spotify player…
Anyway, I have installed raspbian and I’m now playing with it.
Yesterday evening, as I was about to go to bed, I’ve had a very bad idea… I’ve linked together my rpi and my Oregon Weather Station. 3 hours later, I was still geeking…
As usual in the blog I will explain what I did, what did work, and what did not.
Attaching the devices
I’ve plugged the device, ok! Now what does the system tells me about it:
dmesg tells me is simply
Finding the device
lsusb gives me the list of the usb devices on my rpi:
The first one correspond to my weather station but it belongs to root:
The first thing to do is to allow access to my usb device so I won’t need to run any program as root.
By default the
pi user belongs to a bunch of groups. One of those is called
It is the one I will use for my experiment.
Get information about my Device
I will note the vendor ID and the product ID. Funny stuff is that it presents itself as a WMRS200 and the model I have is a RMS300, but never mind.
Let’s create the udev rule file using the previous informations about the idVendor and the idProduct and create a special file
This will make the code more easy as I will be able to hard code the name, and leave the boring task of finding the device aside.
Once done, I can restart udev with
sudo /etc/init.d/udev restart or reload and trigger the rules with
IF something goes wrong, you can check the logs by turning the log level to info, reload the rules and look into the syslog file
# udevadm control -l info # udevadm control -R # # grep -i udev /var/log/syslog #
# ls -lrt /dev/weather-station lrwxrwxrwx 1 root root 15 Aug 29 21:32 /dev/weather-station -> bus/usb/001/007 # ls -lrt /dev/bus/usb/001/007 crw-rw-r-- 1 root plugdev 189, 6 Aug 29 21:32 /dev/bus/usb/001/007
So far so good…
Accessing the data
Linux has a low level library “libusb” that make the development of modules easy: libusb-1.0.
On my rpi, I can install the development version with a simple
sudo apt-get install libusb-1.0-0-dev.
Using GO: The
A binding for the libusb is available through the gousb
There is also a lsusb version that is available as an example.
Let’s grab it with a simple
go get -v github.com/kylelemons/gousb/lsusb
and test it
# ~GOPATH/bin/lsusb 001.004 0fde:ca01 WMRS200 weather station (Oregon Scientific) Protocol: (Defined at Interface level) Config 01: -------------- Interface 00 Setup 00 Human Interface Device (No Subclass) None Endpoint 1 IN interrupt - unsynchronized data [8 0] -------------- 001.003 0424:ec00 SMSC9512/9514 Fast Ethernet Adapter (Standard Microsystems Corp.) Protocol: Vendor Specific Class Config 01: -------------- Interface 00 Setup 00 Vendor Specific Class Endpoint 1 IN bulk - unsynchronized data [512 0] Endpoint 2 OUT bulk - unsynchronized data [512 0] Endpoint 3 IN interrupt - unsynchronized data [16 0] -------------- 001.002 0424:9514 SMC9514 Hub (Standard Microsystems Corp.) Protocol: Hub (Unused) TT per port Config 01: -------------- Interface 00 Setup 00 Hub (Unused) Single TT Endpoint 1 IN interrupt - unsynchronized data [1 0] Interface 00 Setup 01 Hub (Unused) TT per port Endpoint 1 IN interrupt - unsynchronized data [1 0] -------------- 001.001 1d6b:0002 2.0 root hub (Linux Foundation) Protocol: Hub (Unused) Single TT Config 01: -------------- Interface 00 Setup 00 Hub (Unused) Full speed (or root) hub Endpoint 1 IN interrupt - unsynchronized data [4 0] --------------
I want to read the raw data from the device. The gousb package comes along with an example named “rawread”. I’m using it:
After digging into the documentation and forums about the libusb, it looks like the device is locked by a generic kernel driver. So I need to detach it first.
The API call used to detach a kernel driver is
libusb_detach_kernel_driver. Sadly it has not be bound to the golang’s library.
Indeed Joseph Poirier maintain an active fork from the gousb library and he does implement the call.
It’s a private method that is called implicitly by another call, so no need to modify the code from rawread to use it.
I’ve switched to his version:
Nothing more because the code ends by
Cool… Now let’s add some code to read from the endpoint (which is an interface and that implements a Read method as described here)
And run the code:
OK! Here are the data, now what I need to figure out, is how to interpret them!
Decoding the Protocol
Internet is a great tool: I’ve found a description of the protocol here
I’ve read that it was mandatory to send a heartbeat sequence every 30 seconds. I will implement the heartbeat later. For now I will send it initially to request data from the station:
And then read the stream back. Every data payload is separate from the others by a 0xffff sequence.
Testing the sequence initialization request
What did² I do wrong?
Easy, I didn’t RTFM… Actually, I didn’t read the specification of the USB.
As described here the USB is a host-controlled bus which means that:
- Nothing on the bus happens without the host first initiating it.
- Devices cannot initiate a transaction.
- The USB is a Polled Bus
- The Host polls each device, requesting data or sending data
The possibles transaction are:
- IN : Device to Host
- OUT: Host to Device
On top of that, a device may handle 1 to N configuration which handles 1 to N endpoints which may be considered IN or OUT.
My weather station has only one endpoint which is IN. Therefore I will not be able to send information to the station from the host. What I will be able to send is a IN token to get data on the bus.
Note I also see that the endpoint is an interrupt
To be continued…
This blog post is quiet long, and I haven’t finished my research yet. Indeed I think that there is enough information for the post to go live. I will post a part II as soon as I will have time to continue my experiments with the USB device and the rpi.