I’d been looking for a reason to use another Raspberry Pi for some time. For years I’ve had an old Oregon Scientific weather station that had 4 really nice 433mhz weather sensors, but I no longer used the weather station base. I wanted to setup a raspberry pi that would somehow retrieve the temperature readings these old sensors transmitted, and have a script that would push them to an influxdb container for graphing.
First, I went cheap. I bought a 433 ASK/OOK GPIO interfaced board from amazon for 7 bucks. Of course this required another $25 in jumper wiring and connectors, and once I had it hooked up I immediately found that it couldn’t demodulate whatever protocol these sensors used. It was primarily used for very very cheap temperature sensors, or short range signal communication between two embedded boards.
A while later, a friend of mine pointed me at an RTL-SDR USB dongle, and for $30 you can get a pretty nice kit which has antennas, the dongle, and a few mounts. I purchased it and immediately plugged it into my Macbook and installed gqrx.
This resulted in instant gratification: a very cool graphical interface with a waterfall of the radio spectrum. I quickly forgot about the temperature sensors as I realized I could visually poke around anywhere in the spectrum. I could browse anything, from ATC traffic control frequencies, to DPS/FD VHF, Weather forecasts, and even listen to stereo FM radio. Even better, a VERY robust software package, rtl_433, is available that decodes the 433 temperature sensors, from demodulating the transmissions all the way through decoding the proprietary packets and displaying them neatly or formatting them in a useful json blob. I’ll get back to doing that at some point.
Enter the Raspberry Pi
I quickly realized that the computers and other junk on my desk weren’t helping my signal reception very well. I also didn’t like having the antennas everywhere. I found that there’s a part of the rtl-sdr package called rtl_tcp which is perfect: It simply provides the raw data output from the RTL-SDR USB dongle over a TCP connection. GQRX already supports this as well. You can put your antenna and dongle far away, preferably somewhere with the best reception, hook it up to a cheap raspberry pi, and still listen to everything on your desktop! I was excited to try this with a raspberry pi. I immediately imagined zip-tieing a Raspberry Pi Zero W to the antenna mast and sticking it in a closet on the 2nd floor.
RTL-SDR is already an upstream package for Raspbian, so it’s as simple as sudo apt-get install rtl-sdr on your raspberry pi and off you go.
Not so fast! (literally)
I connected the dongle to an rpizerow I already had, installed RTL-SDR, and ran rtl_tcp. I pointed gqrx to the IP of the raspberry pi (following an excellent guide such as: http://www.hagensieker.com/blog/page/?post_id=70&title=make-a-raspberry-pi-3-rtl-sdr-server ) and I was in business quickly.
Except, it wasn’t so usable. I couldn’t use the full bandwidth of the RTL-SDR dongle. The lower the samples-per-second, the less of the spectrum you can see at once. Even at one of the lowest bandwidths (0.92Msps) there was a few seconds lag between changing the frequency and seeing it in GQRX. This was quite annoying, as GQRX doesn’t realize this, and so you get a few seconds of LOUD static if you’re changing modulation at the same time. And it’s no fun to jump around frequencies.
And there wasn’t really any way to go above 1.15M samples per second. When I had it directly connected to my laptop’s USB, GQRX was able to do 3.2M samples per second, and I could see a huge range of the radio spectrum, jumping around instantly. But here, I had to limit the sample rate, reducing the range I could see at once and the quality of the signal. Also, it wasn’t that stable. I saw a lot of these messages from rtl_tcp:
ll+, now 3 ll-, now 0 ll+, now 1 ll+, now 2 ll+, now 3 ll-, now 0 ll+, now 1 ll+, now 2 ll+, now 3 ll-, now 0 ll+, now 1 ll+, now 2 ll+, now 3 ll-, now 0 ll+, now 1 ll+, now 2
Sometimes this number grew and grew into the hundreds. A quick google showed this meant the rtl_tcp program wasn’t able to send the data it had, and had to allocate more buffers. This post outlines the problem, and explains that the data is stored in an ever growing linked list of data blobs read from the USB.
I immediately switched to a Raspberry Pi 3B+ and a wired ethernet connection. I figured the Ethernet connection and 4 cores would solve it. But it did not, even with a CPU usage of ~17% there was still significant lag above 1.92Msps. This indicated that the threads of the program were not being scheduled and likely blocking on something. I next experimented by increasing the MTU — which has the effect of decreasing the number of ACKs and TCP packets coming back to the Raspberry pi. This actually helped reduce lag and the “ll+ spiral”, somewhat. But I couldn’t increase the bandwidth to what I had with direct USB, and the raspberry pi wasn’t overloaded in network utilization or CPU utilization.
I decided to check out the source and found a few potential issues:
- Each data chunk read via the USB was dropped into a newly allocated buffer
- Every buffer only lived until it was transmitted over the TCP socket
- All buffers were inserted and removed from a linked list.
- There was a single lock between the thread reading data from the USB and transmitting data over the TCP socket. Therefore, in some cases, the TCP thread could prevent the USB thread from executing and vice-versa.
- Both threads could block/sleep and wait on a signal if there was lock contention.
The net result was a large amount of malloc/free heap overhead, as well as the bookkeeping required for the linked list element insertion and removal, and even worse, threads would halt or go to sleep if the other thread had a lock. This meant that when the TCP worker and USB-reader worker got “in phase” they could continually step on each other. Also most importantly, the design of this code was to simply grow the linked list of data blobs if it wasn’t able to push the data through from the USB to the network. This meant that every “hiccup” put the data stream further behind “real-time”, increasing the “lag” I felt when changing frequencies. This is the reason the author in the blog about raspberry pi as a rtl_tcp host remarks: “Note that tuning is a bit slow. But it works.”
I’m sure this code performs quite well on a multi-core Intel Core i5 or other mainstream desktop CPU. But the raspberry pi needs more finesse. Having a raspberry pi in a closet with a well-placed antenna seemed like such a compelling use case for rtl_tcp. And given the issues in the code, I felt there was a chance to improve it.
To be continued in part 2.