It all started when one of my readers asked me if I had looked into a weather station for the house. Well, I had, but there were two problems, the good ones cost a fortune, and the others don't have a lot of capabilities. I was going to buy one of the sensor sets sold by SparkFun. They have a really nice one for about $70 that would do the job pretty well. As luck would have it, I was in Costco drooling over the multi-terrabyte disks and noticed they had a Acu-Rite weather station for $80, display and all. It had a sensor head with windspeed, rainfall, temperature, humidity and wind direction. The console was an LCD color display with a USB output that promised I could plug it into a computer and send data out on the web.
Yes, I left with it.
Heck, I couldn't buy the sensors and assemble them any cheaper, plus I'd have to learn all about reading rain gauges and anemometers. This sounded like a great deal, and it was. There was only one thing missing, being able to get the data into some kind of database so I could play with it. Yes there was some pretty slick software that would upload the data to the AcuRite site, but you all know how I feel about relying on someone else for my data. Also, the software ONLY runs on a Windows PC. That means I have to keep a PC running all the time to get the data uploaded. What? This is the 21st century people, what's up with keeping a big old power hungry PC running 24/7?
I went looking for solutions and found a couple of interesting things. A site called Weather Display <link> that had some nice software and a driver for Linux, but the poor developer had recently lost the source to his work <link>, and he wanted $70 bucks for it. Another one, Valley Information Systems <link> who, it turns out, does the driver for the AcuRite software for the weather station. V.I.S. had a forum as well <link>, so I prowled it and found out that they didn't have a Linux driver, and basically weren't interested in doing one. I got this from a long rambling discussion with the author (you know me, I can't shut up) where he flat out told me it wasn't worth his time. I spent several more hours looking for leads on a Linux driver with no luck at all.
So, I didn't want to spend $70 on a system that the source was being rebuilt from scratch for, and I didn't want to run it on a PC since that would tie up my machine and hook me to a wire 24/7; I guess the only solution was to write something myself.
But, what to do? I could get a receiver and capture the RF sent from the 5 in 1 sensor I mounted on the roof:
(people tell me it looks like a rabbit setting up there)
I could also buy one of their 'bridge' devices. This is basically an RF receiver hooked to an ethernet plug through some kind of processor. This device was listed at $80, the same price as the weather station, and it wasn't even wireless.
It did meet the requirement of not having to have a PC plugged in and tethered to the display though. I guess it's AcuRite's way of overcoming an obvious shortcoming ... for a price.
Or, I could try and conquer the USB interface to it on my Raspberry Pi. Since I have years of experience dealing with RF in various forms, what do you think I did? Right, I decided to hack into the USB interface.
The only problem is that I know exactly zero about USB and how to interact with it ... sigh; here I go again.
A while later, I had learned that there were several ways to interface with USB on the Pi, something called pyusb written in python, but there was so little documentation on it that frankly, I couldn't make heads or tails out of it. There was libusb that had tons of documentation, but all of it was written in a language I couldn't understand --- USB jargonese. There was something called libusbx, but it looked exactly like libusb. There were a ton of examples, but nothing that showed how you could actually get data off the darn thing. If I wanted to list the devices, I could find an example almost instantly, but actually get some data, basically nothing.
Fine, first I'd play with the USB, and get a feel for what was going on. Maybe that would give me a few keywords that I could use in a better refined google search. I learned all about lsusb and several other tools that can enumerate the devices hooked to a machine. I looked at USB sniffers and how they worked. I found out that HID stood for Human Interface Device and it had a special driver that was dealt with differently than other USB devices. I learned all about how to modify udev rules to make a touchpad behave like a joystick; everything except how to get data from this weather station.
I decided to just start playing and see what happened. I plugged the weather station into the Pi and started looking around. The very first thing I found out was that a normal user on the Pi, like the user pi, can't read the device that is created when you plug the weather station in. You can tell if this is the problem; look for the device in /dev and check its permissions
crw------- 1 root root 249, 0 Dec 31 1969 /dev/hidraw0
See, what happens is that the device isn't recognized by udev (the process that watches the USB bus) and it assigned the hidraw driver (the most basic generic driver of this type) to the device. The hidraw driver defaulted like this can only be read and written to by root. The situation was that the device would identify itself as a HID device, but it wasn't recognized properly and udev would default to the hidraw device driver. OK, I started looking for how to use this device. It turns out there's quite a bit of information on the hidraw driver and how to use it. So I put together some code and gave it a try.
Nothing worked as advertised. Nothing. The weather station might have been assigned the hidraw driver, but it wouldn't act like one to Linux. That was a bust. I wrote some code to read the various descriptors from the device and noticed a couple of errors in its implementation that might have been causing the problem, but nothing that could solve the problem. All of this was running as root on the Pi since a normal user couldn't talk to the device. Yes, I could change the permissions on the device, but that would only last until it was unplugged. When the device was plugged back in, the device was created root-only all over again. I could also set the bit such that the code would run as root, but that could create a monster if I messed up in the code. I really didn't want to risk that. And, I was getting tired of dealing with the problems of a default device that wouldn't work, so I decided to conquer that item first.
When dealing with USB, the devices can come and go at will. When we unplug the device, the system notices it and dismantles the connection to it. Everything is restored when the device is plugged back in. Although, the device may come up with a different mount point which means the device name changes. For example on my Pi, since I don't have a keyboard or mouse, the weather station shows up as hidraw0. On some other machine it could be hidraw1, hidraw2, whatever the next number after the devices already plugged in happens to be. That's just the nature of USB. This stuff works by a set of rules that are in the secret directory /etc/udev/rules.d.
I'm not going to drive you nuts with an in-depth discussion of udev and how it works, but I will tell you how to fix the permission problems so the device can be read by a normal Linux user (not root).
First, you have to find out what the OS sees the device as. This is logged in the system log when you plug the device in and udev does its thing. So, plug the weather station into the Pi and do the command 'dmesg'. You're interested in a device from Chaney Instrument, so look in the dmesg output for something like the following:
[ 2.399920] usb 1-1.2: new low-speed USB device number 4 using dwc_otg [ 2.536425] usb 1-1.2: New USB device found, idVendor=24c0, idProduct=0003 [ 2.538128] usb 1-1.2: New USB device strings: Mfr=0, Product=2, SerialNumber=0 [ 2.541669] usb 1-1.2: Product: Chaney Instrument [ 3.127499] udevd[156]: starting version 175 [ 5.553287] bcm2708-i2s bcm2708-i2s.0: Failed to create debugfs directory [ 12.559829] hid-generic 0003:24C0:0003.0001: usb_submit_urb(ctrl) failed: -1 [ 12.561497] hid-generic 0003:24C0:0003.0001: timeout initializing reports [ 12.563452] input: Chaney Instrument as /devices/platform/bcm2708_usb/usb1/1-1/1-1.2/1-1.2:1.0/input/input0 [ 12.569076] hid-generic 0003:24C0:0003.0001: input,hidraw0: USB HID v1.11 Device [Chaney Instrument] on usb-bcm2708_usb-1.2/input0I used the command 'dmesg | grep -i usb' to capture this, and it means the HID USB driver grabbed the device because it is listed as type 3 (HID) in its descriptor.
Edit: A short time after I wrote this, I found a significant problem. It's all detailed in a later post (I'm working on it right now). Net, DON'T CHANGE THE COMMAND LINE. It won't destroy your machine or anything, but it will make the weather station USB interface fail in a really annoying fashion. Skip down to the description of the udev changes and go on from there
That needs to be fixed, but first a tiny bit about cmdline.txt. This is used to feed parameters into the Linux boot up process, for example many folk have a cmdline.txt file like this when they first boot their Pi:
dwc_otg.lpm_enable=0 console=ttyAMA0,115200 kgdboc=ttyAMA0,115200 console=tty1 root=/dev/mmcblk0p2 rootfstype=ext4 elevator=deadline rootwait
My Pi, before this change looks like this:
dwc_otg.lpm_enable=0 root=/dev/mmcblk0p2 rootfstype=ext4 elevator=deadline rootwait
I had to remove the console entries for other projects, so the command is smaller. To stop the HID driver binding to the weather station USB port from happening, add this phrase to /boot/cmdline.txt, but be careful, messing up this line may cause the Pi not to boot back up.
usbhid.quirks=0xXXXX:0xYYYY:0x04
Replace XXXX with the idVendor above and the YYYY with the idProduct above, the 0x04 is HID_QUIRK_IGNORE, leave that alone. Just put the phrase somewhere in the line, you'll see what I mean when you look at it. It seems the HID driver has allowances for 'quirks', things that just don't work the way you expect them to - like this device. After I made the changes, my /boot/cmdline.txt looks like this:
dwc_otg.lpm_enable=0 root=/dev/mmcblk0p2 rootfstype=ext4 elevator=deadline usbhid.quirks=0x24c0:0x0003:0x04 rootwait
Edit: This is where you want to pick up again. Yes, I could have just edited this out and gone on with life, but that isn't fair. We all make mistakes and admitting them and correcting for it is part of the fun.
Now you need to make a new usb device usable by someone other than root for the weather station, so go to the directory /etc/udev/rules.d (remember that from above) and create a file named 10-local.rules which contains the line:
SUBSYSTEM=="usb", ATTRS{idVendor}=="XXXX", ATTRS{idProduct}=="YYYY", SYMLINK="weather" MODE="666",GROUP="users"
The reason for the obscure file name is due to the way the file is read. The XXXX and YYYY are replaced with the idVendor and idProduct as described above. This will cause udev to make the device usable by a normal user. My /etc/udev/rules.d/10-local.rules looks like this:
SUBSYSTEM=="usb", ATTRS{idVendor}=="24c0", ATTRS{idProduct}=="0003", SYMLINK="weather" MODE="666",GROUP="users"
Yours will probably look exactly the same since the vendor and product number won't change. This rule will set the permission and create a symbolic link in the /dev/directory called 'weather' for the station. Be careful with the equal and double equal signs; one is a comparison and the other is an assignment. I fought this mistake for a hour before I noticed what I did wrong. After you reboot the machine to make it read the changes, take a look at /dev/weather:
lrwxrwxrwx 1 root root 15 Dec 31 1969 /dev/weather -> bus/usb/001/004
The permissions are right and the link is into the USB bus structure. To test that a normal user can get to the file, do 'hexdump < /dev/weather' while logged in as pi and make sure you get something on the screen. The reason for hexdump is to prevent control characters from getting to the screen and messing up settings. You're not really interested in what the data is, just that you can get it.
Once all these usb related changes are done you're all set with an entry in /dev called weather that can be read and written by a normal user. You can unplug the device and /dev/weather will disappear; plug it back in and /dev/weather will come back. This is the way it's supposed to work. I actually did a 'tail -f /var/log/messages' and watched as I plugged and unplugged the weather station. It had taken a lot of research and experimentation to get to this point, and I wanted to enjoy it. DO NOT DO THIS! I now have a plug on the back of the weather station console that is flaky. It's ok to unplug it, but don't be like me.
Edit: Note that the hidraw device will still be there, that's a good thing and is explained in a later post. The new device 'weather' is also there and the permissions are necessary.
So, when do we get to the part about actually reading the darn thing. Maybe in the next post, but there's much more to come. We have to get libusb sorted out; that's a story in itself and will probably bore you to tears. Then we have to discuss how to actually implement a user-space-usb-reader. That's part of the jargon that I picked up learning about this. Eventually, we'll actually decode the bit stream the weather station provides; yes, it's a dog gone bitstream.
I promise, I won't take months and months to get the rest of it up, but I don't want to make these too long.
Part 2 of this is now here <link>