Sunday, November 30, 2014

AcuRite Weather Station, Raspberry Pi and a USB interface; Part 1

This is a long drawn out story.  Sorry.  I'm going to go into more detail than I usually do and the project will span more than one blog entry.  That's because it is really complex and deals with stuff that most of us would rather not mess with.  We want to monitor and control our house, not do a dissertation.  But, in this case, you can't have enough background information or advice.  There are things that can happen that you need a minimum of background to overcome.  I hope to give you enough to repeat the project.

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/input0
I 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.


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>


  1. Thanks for the time and effort and writing this all!

  2. I'm looking to do this exact same thing. If you need any help and/or someone to test, I'd be happy to lend a hand.

    1. I've actually got this running, but I haven't finished writing it up. There's so darn many steps to getting everything going that It will take me some days to get all the posts up. Then, I'll probably have to edit them over a week or two for mistakes that turn up.

      Give me about a week to get the other parts up and you'll have some code to test. I'm not going to give up at that point though, I want to crack into the actual RF from the sensor and do that also. I'm a little tired of companies and their secret protocols that they won't tell anyone about.

    2. Hi Dave,

      I have been working on decoding RF for a few sensors. See for ideas on Acurite. I have tried putting the Acurite outside temperature sensor in my refrigerator and reading the sensor from a set up like Ray's. Unfortunately, I am not getting a lot of range. A better antenna might resolve that.

      I also have some Oregon Scientific weather instruments that I have been working with using the same Arduino and RF receiver to receive and decode their signal. Over at JeeLabs they had a post that got me started with that. The encoding for Oregon Scientific is more complicated.

      My plan is to decode both the Acurite and Oregon Scientific sensors with the same hardware. Plus create my own sensors where I need something very low power, like sensing whether or not my fence gate is closed.


    3. Thanks Chris. I'm going for a tactic similar to Ray's, but I'm going with one of those new software controlled radios (well, new to me anyway). I haven't started that yet, but it might turn out to be fun.

      I want to get this hack into the usb port done and implemented with the other house software first, then I have a couple of other unfinished software projects I'm going to finish....sigh.

  3. I also have one of those Acurite weather stations and a Raspberry Pi so thought I would use them together. I have ordered a 433mhz receiver and transmitter from e-Bay for ONLY $1.56 so plan to use that to receive the data directly instead of going the USB route. I have very limited experience in this area so I keep searching sites like this to find information on how to do what I plan on doing.

    1. Decoding the RF is a painful process. There's a ton of sites out there that talk about it though. Have fun getting it going.

  4. Hello,

    I have an Acurite 01036 (USB mode 3) that works fine under Windows (just to be sure I can receive data). I now try to connect this under Ubuntu 14. I am following your steps up to read the data throught the weather device (udev rule) and hexdump returns only four lines (always the same lines) :

    0000000 0112 0110 0000 0800 24c0 0003 0020 0200
    0000010 0100 0209 0022 0101 8000 0932 0004 0100
    0000020 0003 0000 2109 0111 0200 4922 0700 8105
    0000030 0803 0a00

    and hexdump stops. I am not sure this is what is expected (why the flux stops?).

    Could you describe what is expected ?

    I would be sure I am true before following your next explanation page.

    Thank you.

    1. I have a whole series of posts on exactly what happens when you do each step. When you do the hexdump above you will get the same thing each time, you have to actually interact with the USB device to get the data you want. Go on to the next post and work through them one at a time. One reader had some problems with Ubuntu, so take a look at the comments as well because he found the solution.

    2. Thank you so much, I go ahead.

  5. Wouh, I get this working (weatherstation.c) to read the data under Ubuntu 14 !! Thank you !
    Now I want to leave my computer because, like you with your rasberry PI, I want to implement this in my Qnap NAS (debian linux based) as I do not want to keep my computer ON all the time.

    Imagine, on your PI, you got this from dmesg command :

    [582219.414242] hid-generic 0003:24C0:0003.0012: input: USB HID v1.11 Device [Chaney Instrument] on usb-0000:00:1a.0-1/input0

    that is to say, without hidraw0 indication (this is what I get from my NAS). What would you do then, in this case ? How to manage it (I just need some ideas, not details) ?

    Thank you.

  6. That's what you get when you reboot with the station plugged into the machine; you also get it when you plug the device in and are watching dmesg. The code will disconnect the hidraw device and then hook itself in to the weather station. If you did it right, removing the station will let the hidraw device hook back into the port.

    1. Yes, I remember the explanation on your blog. But it is different on my NAS : if I plug a USB mouse, I have the same issue and there no hidraw rising up. I just see the major and the minor of the device. I think this not implemented on my NAS. This is why I ask an opinion without the hidraw feature : impossible to manage ? hidraw absolutly needed then I have to implement it on the NAS ?

    2. You probably don't need the hidraw driver running, but the only way to tell is to hook up the code, get it running and then unplug the console from the computer. Then plug it back in and see if it works. On the raspberry pi, I needed the hidraw driver running to keep the port initialize with the kernal, you may not.

      Just give it a try and see what happens.

    3. I installed libudev-dev in the NAS (kernel 3.4.6) because it is claimed for the libusb compilation even if udev cannot be used for it. I prepared the code under my NAS like i sucessfully did with my Ubuntu PC : libusb-1.0.19 is installed without error and usbexample1.c is compiled ... with a few warnings :
      usbexample1.c: In function ‘showit’:
      usbexample1.c:119:17: warning: format ‘%d’ expects argument of type ‘int’, but argument 4 has type ‘time_t’ [-Wformat]
      usbexample1.c:119:17: warning: format ‘%d’ expects argument of type ‘int’, but argument 6 has type ‘time_t’ [-Wformat]
      usbexample1.c:119:17: warning: format ‘%d’ expects argument of type ‘int’, but argument 8 has type ‘time_t’ [-Wformat]
      usbexample1.c:119:17: warning: format ‘%d’ expects argument of type ‘int’, but argument 10 has type ‘time_t’ [-Wformat]
      usbexample1.c:119:17: warning: format ‘%d’ expects argument of type ‘int’, but argument 12 has type ‘time_t’ [-Wformat]

      ldd a.out gave : => (0xf777a000) => /usr/local/lib/ (0xf7755000) => /lib/i386-linux-gnu/ (0xf75aa000) => /lib/i386-linux-gnu/ (0xf7599000) => /lib/i386-linux-gnu/ (0xf7590000) => /lib/i386-linux-gnu/ (0xf7575000)
      /lib/ (0xf777b000)

      I started a.out with the console plugged :

      ./a.out Starting ... libusbDebug = 0, noisy = 0
      This is not an error!! Checking linker, Success
      Couldn't init usblib, Other error

      I unplug the console, plug back, but a.out returns the same result. libusb does not start at all, may be a missing library that I cannot see, a bad version library ...

    4. If you want to get rid of the warnings, cast the variable to an int '(int)variable', and they will go away. The init call is weird. When I got that error it was most often a pipe error which means it couldn't open the device. You may have to print the error number which is called 'err' in the code to see what the number is.

      Also, type in 'dmesg' and take a look at the end of the output for errors related to USB. You'll see things in there that may help you chase down the problem. Remember also that you can turn on debugging output in libusb with a parameter.

      I don't have a ubuntu machine to try this on, so I may run out of suggestions.

  7. Thank you for your hints. The number error is -99. I am not an expert in C, but I see that usbexample1.c exits after trying to open libusb. If I understand, this code did not attempt to open the device yet.

    dmesg did not add any more information. Just to see what happen, I commented this exit(1) instruction :

    if (err < 0) {
    fprintf(stderr,"Couldn't init usblib, %d\n", err);
    // exit(1);

    A segmentation fault appeared that dmesg caught :

    a.out[11895]: segfault at 4 ip 00000000f76eabd8 sp 00000000ffdc61ac error 4 in[f76e8000+16000]

    Turning on the debbuging mode did not rise the verbosity in this case.

    What about this error number -99 and may you give me your ldd a.out for your Pi ? Thank you.

    1. My ldd output:

      ~/src/house$ ldd getweather
      /usr/lib/arm-linux-gnueabihf/ (0xb6f3e000) => /usr/local/lib/ (0xb6f23000) => /lib/arm-linux-gnueabihf/ (0xb6de9000) => /lib/arm-linux-gnueabihf/ (0xb6dd4000) => /lib/arm-linux-gnueabihf/ (0xb6dc5000) => /lib/arm-linux-gnueabihf/ (0xb6da6000)
      /lib/ (0xb6f4b000) => /lib/arm-linux-gnueabihf/ (0xb6d7e000)

      Libusb errors are negative numbers and the positive numbers are system error. Error -99 is 'other' which doesn't tell you much. The libusb errors are here:

    2. Indeed, i do not know what to do with the -99 error.

      A new firmware is released for my NAS and I just installed it. I tried the usbexample1.c code. Now there is a new version of libusb-1.0 included (I do not know exactly which version). The execution of a.out did progress with the included libusb-1.0 !!

      I just disabled libusb_strerror and libusb_set_auto_detach_kernel_driver functions and now I have :

      ./a.out Starting ... libusbDebug = 0, noisy = 0
      This is not an error!! Checking linker, 4
      1005:b155 (bus 2, device 2)
      24c0:0003 (bus 3, device 2)
      Found the weather station
      got the device descriptor back
      I was able to open it
      Released the device list
      Currently active configuration is 1
      Claimed Interface
      Setting the device 'idle' before starting reads
      libusb: 0.000000 warning [libusb_control_transfer] unrecognised status code 1
      Starting reads
      libusb: 2.003992 warning [libusb_control_transfer] unrecognised status code 1
      Read didn't work for report 1, 3
      libusb: 12.008004 warning [libusb_control_transfer] unrecognised status code 1
      Read didn't work for report 1, 3
      libusb: 22.012009 warning [libusb_control_transfer] unrecognised status code 1
      Read didn't work for report 1, 3
      libusb: 31.016017 warning [libusb_control_transfer] unrecognised status code 1
      Read didn't work for report 2, 3
      ^CShutting down ...
      Done with device, release and close it
      Released interface and restored kernal driver
      Closed Weatherstation device
      Closed USB interface

      I know you choose le 1.0.19 version but do you think I have a chance to adapt your code with my (unknown) version of libusb-1.0 ?

      Thank you.

    3. There's a call in libusb to get the version, but I haven't used it. You can take a look at that if you're interested in finding out which version you got. You also may be able to take a look at the source and find out, if it came with the source.

      As for your being able to adapt it to the version you have. I suspect you can get it to work. It looks like you only have one problem to deal with from the console output, and a little experimenting may show what is different.

  8. Thank you so much for the time and effort put into this, i was ready to toss my station as i was tired of keeping a windows machine around just for weather data.

    Great work!

    running on ArchLinuxARM/RaspberryPi 1 model B

    1. Taget, be sure to go through the entire series (including the RF portion), there's fixes and changes and stuff that you may be interested in.

      Have fun with it.

  9. Hello,

    Thought your readers may like to know that I have made a Node.js library to read values from AcuRite weather stations. Right now, it supports the 02064C - which I bought from Costco.

    AcuRead -


  10. Hello, I am new here. I work for a community college and we are trying to implement this process with our Acurite weather station. We actually have 9 weather stations which we are trying to have communicate with 9 raspberry pi s. I do not have a lot of computer experience as far as working in command lines.

    So far I am on Part 1 of your 8 part series and I was just able to run the command 'Dmesg | grep -i usb' and I was able to see some usb processes that had gone through. Some saying "Chaney Instrument" and what not. It looked very similar to what came up for you. Now on the next part where I have to add the phrase to the /boot/cmdline.txt . How do I add the phrase. Sorry, I do not have a lot of experience working in command lines. By the way, I am trying to do this on a raspberry pi running ubuntu if that helps. I am going to try and learn more about command lines but if anyone can help me it would be much apreciated.

  11. Part 1 ?? OK, you have a ways to go on this, but command lines aren't anything to be reluctant to dive into. This particular thing can mess you up pretty bad so first copy cmdline.txt to a backup file.

    You have to be root to do this, so:

    sudo cp cmdline.txt cmdline.orig

    Then you want to edit it and the easiest editor for you to use would be 'nano'.

    sudo nano cmdline.txt

    and follow the onscreen menu to get through the changes. There's about a million examples out there of doing this to change the cmdline.txt file, so just look at them for specific keystrokes and such.

    HOWEVER !! reread the posting above. In Italics I tell you not to do this particular step because it will cause trouble later. Read on through the posts and you'll see what happened to me.


  12. FYI, this problem has been solved. I suggest you check out weewx. Run great on the Pi and reads the weather console information. Please remember that you have to manually set the console to UBS mode 3 or 4, 4 works much better. No known way to set the USB mode in software, so if you loose both commercial power and battery power to your console, it will come back up in mode 2.

  13. We were in contact some time ago - KCOBLACK1. Just thought of you again and thinking of your house, weather, power consumption etc... ESP8266. 70% done with a project to keep track of our hot tub - tends to wonk-up on me now and then and 87deg instead of 102 - I'd like to know before I go trip'n out in the yard when it's 38deg. So a esp8266, a TMP36, a 5v usb and I done gots me a monitor (along with some bash code on my server).

    1. That's a truly practical use of home monitoring. Good Job.