Categories: Intermediate
Objectives
- Learn about the AWS IOT service by developing a Thing that performs some useful function. Make it a real-world example.
- Integrate other
newerAWS services like Kinesis Firehose and Machine Learning.
- Learn about the BeagleBone Black and do something interesting with it.
- Win the contest.
- ????????
- PROFIT!
What is a Thing?
From
Wikipedia
:
"Things," in the IoT sense, can refer to a wide variety of devices such as heart monitoring implants, biochip transponders on farm animals, electric clams in coastal waters, automobiles with built-in sensors, DNA analysis devices for environmental/food/pathogen monitoring or field operation devices that assist firefighters in search and rescue operations. These devices collect useful data with the help of various existing technologies and then autonomously flow the data between other devices. Current market examples include smart thermostat systems and washer/dryers that use Wi-Fi for remote monitoring.
So a "
Thing
" is some type of electronic device that can communicate its state to other devices. By keeping a "Thing" simple, we can minimize the cost and create many of the "Things". This allows for distributed sensing with centralized and elastic computation.
About Our Thing
Our Thing is a little more complex than other things. It consists of:
- a single board computer running Linux,
- an
rtlsdr
software defined radio device, - a GPS sensor,
- and a wireless ethernet adapter.
Single Board Computer
I needed to select a hardware platform. The contest was announced shortly after the 2015 re:Invent conference and many of the IOT kits were sold out. I realized I was going to have to use whatever I could get my hands on.
My Thing Idea requires the use of a USB RTLSDR device and a GPS sensor, so we need something with one or more USB ports and a digital inputs for a sensor component. We need to keep the cost of the single board computer or microprocessor low to minimize overall Thing cost.
Intel Edison Development Kit
During the 2014 Amazon Web Services re:Invent conference, Intel and SparkFun hosted a HackDay before the main conference began. Participants were given a free Intel Edison development kit as well as the Grove SeeedStudio sensor kit and we sent sensor data from the devices to Amazon Kinesis, DynamoDB, and SQS. I participated in this event and still use the Edison board and sensors.
The Intel Edison development boards are very powerful and have built in Wi-Fi with a u.Fl connector. They are an ideal platform for this idea but when I checked to acquire more, they were either sold out or a bit more expensive than other platforms at $70-100.
I decided I might use the one I obtained at the re:Invent HackDay if the lower cost boards could not keep up with the demands of the application.
BeagleBone Black
I happened to be in a RadioShack some time ago and saw a BeagleBone Black (BBB) at a reasonable price so I picked one up. It sat on my desk in the box for months before I got around to looking at it but I still hadn’t done anything with it.
After submitting an idea to the AWS IOT Mega contest, I began to consider the BBB as a low-cost platform for the project. I knew the BBB could run Debian and could enable solutions to a lot of my other challenges. I can also pick up a BBB for about $55.
RTLSDR Device
From
rtl-sdr.com
:
RTL-SDR is a very cheap software defined radio that uses a DVB-T TV tuner dongle based on the RTL2832U chipset. With the combined efforts of Antti Palosaari, Eric Fry and Osmocom it was found that the signal I/Q data could be accessed directly, which allowed the DVB-T TV tuner to be converted into a wideband software defined radio via a new software driver.
Essentially, this means that a cheap $20 TV tuner USB dongle with the RTL2832U chip can be used as a computer based radio scanner. This sort of scanner capability would have cost hundreds or even thousands of dollars just a few years ago. The RTL-SDR is also often referred to as RTL2832U, DVB-T SDR, RTL dongle or the “$20 Software Defined Radio”.
I bought several rtlsdr devices from Nooelec, gave a couple to friends, and have been using them ever since.
Price: $22.95 on Amazon
GPS Sensor
https://www.adafruit.com/images/970×728/746-08.jpg
I didn’t order this until mid January and wasn’t able to integrate it. But assume that eventually we would have real-time GPS updates. FOr now, we will hardcode the coordinates.
Price: $39.95
Networking Adapter
The BeagleBone Black and Green come with an RJ45 Ethernet adapter. The Intel Edison has a built in 802.11n wireless Ethernet adapter.
Flashing the BeagleBone
We need to flash the BeagleBone Black/Green with the latest Debian image. You can find the images at
http://beagleboard.org/latest-images
. For the Edison, you must install
Ubilinux Debian
.
For the BeagleBone, I initially selected the Debian 7.9 image but this image does not contain the correct modules for my spare USB wireless ethernet adapters (r8188eu). Everything else works, so you
can
use that version if you have different networking components.
Instead, I grabbed the Debian 8.2 image.
Booting the BeagleBone
Attach the USB Serial cable to your computer and the BeagleBone. I was using Windows so I opened Device Manager to identify the COM ports that existed, then used PuTTY to connect to COM8 at 115200 8/N/1.
Here is the first boot output:
U-Boot SPL 2013.04-dirty (Jul 10 2013 - 14:02:53)
musb-hdrc: ConfigData=0xde (UTMI-8, dyn FIFOs, HB-ISO Rx, HB-ISO Tx, SoftConn)
musb-hdrc: MHDRC RTL version 2.0
musb-hdrc: setup fifo_mode 4
musb-hdrc: 28/31 max ep, 16384/16384 memory
USB Peripheral mode controller at 47401000 using PIO, IRQ 0
musb-hdrc: ConfigData=0xde (UTMI-8, dyn FIFOs, HB-ISO Rx, HB-ISO Tx, SoftConn)
musb-hdrc: MHDRC RTL version 2.0
musb-hdrc: setup fifo_mode 4
musb-hdrc: 28/31 max ep, 16384/16384 memory
USB Host mode controller at 47401800 using PIO, IRQ 0
OMAP SD/MMC: 0
mmc_send_cmd : timeout: No status update
reading u-boot.img
reading u-boot.img
U-Boot 2013.04-dirty (Jul 10 2013 - 14:02:53)
I2C: ready
DRAM: 512 MiB
WARNING: Caches not enabled
NAND: No NAND device found!!!
0 MiB
MMC: OMAP SD/MMC: 0, OMAP SD/MMC: 1
*** Warning - readenv() failed, using default environment
musb-hdrc: ConfigData=0xde (UTMI-8, dyn FIFOs, HB-ISO Rx, HB-ISO Tx, SoftConn)
musb-hdrc: MHDRC RTL version 2.0
musb-hdrc: setup fifo_mode 4
musb-hdrc: 28/31 max ep, 16384/16384 memory
USB Peripheral mode controller at 47401000 using PIO, IRQ 0
musb-hdrc: ConfigData=0xde (UTMI-8, dyn FIFOs, HB-ISO Rx, HB-ISO Tx, SoftConn)
musb-hdrc: MHDRC RTL version 2.0
musb-hdrc: setup fifo_mode 4
musb-hdrc: 28/31 max ep, 16384/16384 memory
USB Host mode controller at 47401800 using PIO, IRQ 0
Net: not set. Validating first E-fuse MAC
cpsw, usb_ether
Hit any key to stop autoboot: 0
gpio: pin 53 (gpio 53) value is 1
mmc0 is current device
micro SD card found
mmc0 is current device
gpio: pin 54 (gpio 54) value is 1
SD/MMC found on device 0
reading uEnv.txt
1179 bytes read in 5 ms (229.5 KiB/s)
Loaded environment from uEnv.txt
Importing environment from mmc ...
Running uenvcmd ...
1758 bytes read in 40 ms (42 KiB/s)
debug: [/boot/vmlinuz-3.16.0-4-armmp] ...
3182192 bytes read in 573 ms (5.3 MiB/s)
debug: [/boot/initrd.img-3.16.0-4-armmp] ...
12416491 bytes read in 2104 ms (5.6 MiB/s)
debug: [/boot/dtbs/3.16.0-4-armmp/am335x-boneblack.dtb] ...
** File not found /boot/dtbs/3.16.0-4-armmp/am335x-boneblack.dtb **
debug: [console=tty0 console=ttyO0,115200n8 root=/dev/mmcblk0p2 rootfstype=ext4 rootwait coherent_pool=1M quiet init=/lib/systemd/systemd cape_universal=enable] ...
debug: [bootz 0x82000000 0x88080000:bd75eb 0x88000000] ...
ERROR: Did not find a cmdline Flattened Device Tree
Could not find a valid device tree
gpio: pin 55 (gpio 55) value is 1
** File not found /boot/uImage **
U-Boot# ls /boot
** No device specified **
U-Boot#
U-Boot SPL 2013.04-dirty (Jul 10 2013 - 14:02:53)
musb-hdrc: ConfigData=0xde (UTMI-8, dyn FIFOs, HB-ISO Rx, HB-ISO Tx, SoftConn)
musb-hdrc: MHDRC RTL version 2.0
musb-hdrc: setup fifo_mode 4
musb-hdrc: 28/31 max ep, 16384/16384 memory
USB Peripheral mode controller at 47401000 using PIO, IRQ 0
musb-hdrc: ConfigData=0xde (UTMI-8, dyn FIFOs, HB-ISO Rx, HB-ISO Tx, SoftConn)
null
musb-hdrc: MHDRC RTL version 2.0
musb-hdrc: setup fifo_mode 4
musb-hdrc: 28/31 max ep, 16384/16384 memory
USB Host mode controller at 47401800 using PIO, IRQ 0
OMAP SD/MMC: 0
mmc_send_cmd : timeout: No status update
reading u-boot.img
reading u-boot.img
U-Boot 2013.04-dirty (Jul 10 2013 - 14:02:53)
I2C: ready
DRAM: 512 MiB
WARNING: Caches not enabled
NAND: No NAND device found!!!
0 MiB
MMC: OMAP SD/MMC: 0, OMAP SD/MMC: 1
*** Warning - readenv() failed, using default environment
musb-hdrc: ConfigData=0xde (UTMI-8, dyn FIFOs, HB-ISO Rx, HB-ISO Tx, SoftConn)
musb-hdrc: MHDRC RTL version 2.0
musb-hdrc: setup fifo_mode 4
musb-hdrc: 28/31 max ep, 16384/16384 memory
USB Peripheral mode controller at 47401000 using PIO, IRQ 0
musb-hdrc: ConfigData=0xde (UTMI-8, dyn FIFOs, HB-ISO Rx, HB-ISO Tx, SoftConn)
musb-hdrc: MHDRC RTL version 2.0
musb-hdrc: setup fifo_mode 4
musb-hdrc: 28/31 max ep, 16384/16384 memory
USB Host mode controller at 47401800 using PIO, IRQ 0
Net: not set. Validating first E-fuse MAC
cpsw, usb_ether
Hit any key to stop autoboot: 0
gpio: pin 53 (gpio 53) value is 1
mmc0 is current device
micro SD card found
mmc0 is current device
gpio: pin 54 (gpio 54) value is 1
SD/MMC found on device 0
reading uEnv.txt
1179 bytes read in 5 ms (229.5 KiB/s)
Loaded environment from uEnv.txt
Importing environment from mmc ...
Running uenvcmd ...
1705 bytes read in 31 ms (53.7 KiB/s)
debug: [/boot/vmlinuz-4.1.12-ti-r29] ...
8266760 bytes read in 1393 ms (5.7 MiB/s)
debug: [/boot/initrd.img-4.1.12-ti-r29] ...
3968710 bytes read in 685 ms (5.5 MiB/s)
debug: [/boot/dtbs/4.1.12-ti-r29/am335x-boneblack.dtb] ...
59295 bytes read in 62 ms (933.6 KiB/s)
debug: [console=tty0 console=ttyO0,115200n8 root=/dev/mmcblk0p2 rootfstype=ext4 rootwait coherent_pool=1M quiet cape_universal=enable] ...
debug: [bootz 0x82000000 0x88080000:3c8ec6 0x88000000] ...
## Flattened Device Tree blob at 88000000
Booting using the fdt blob at 0x88000000
Using Device Tree in place at 88000000, end 8801179e
Starting kernel ...
[ 3.705799] wkup_m3_ipc 44e11324.wkup_m3_ipc: could not get rproc handle
[ 4.131402] omap-sham 53100000.sham: initialization failed.
[ 4.159546] cpu cpu0: cpu0 clock notifier not ready, retry
[ 4.320355] bone_capemgr bone_capemgr: slot #0: No cape found
[ 4.380326] bone_capemgr bone_capemgr: slot #1: No cape found
[ 4.443452] bone_capemgr bone_capemgr: slot #2: No cape found
[ 4.500336] bone_capemgr bone_capemgr: slot #3: No cape found
Loading, please wait...
fsck from util-linux 2.25.2
fsck: error 2 (No such file or directory) while executing fsck.ext4 for /dev/mmcblk0p2
fsck exited with status code 8
[ 13.209642] systemd-fsck[154]: rootfs: clean, 66324/102752 files, 292446/410368 blocks
[FAILED] Failed to start dnsmasq - A lightweight DHCP and caching DNS server.
See 'systemctl status dnsmasq.service' for details.
[DEPEND] Dependency failed for Host and Network Name Lookups.
[ OK ] Started Login Service.
Starting Hostname Service...
Starting WPA supplicant...
[ OK ] Reached target Remote File Systems (Pre).
[ OK ] Reached target Remote File Systems.
Starting LSB: Load kernel modules needed to enable cpufreq scaling...
Starting Trigger Flushing of Journal to Persistent Storage...
[ OK ] Stopped OpenBSD Secure Shell server.
[ OK ] Reached target Network.
Starting /etc/rc.local Compatibility...
[ OK ] Reached target Network is Online.
Starting LSB: Apache2 web server...
[ OK ] Started /etc/rc.local Compatibility.
[FAILED] Failed to start Hostname Service.
See 'systemctl status systemd-hostnamed.service' for details.
[ OK ] Started WPA supplicant.
[ OK ] Started Trigger Flushing of Journal to Persistent Storage.
[ OK ] Started LSB: Load kernel modules needed to enable cpufreq scaling.
Starting LSB: set CPUFreq kernel parameters...
Starting Permit User Sessions...
[ OK ] Started Permit User Sessions.
Starting Getty on tty1...
[ OK ] Started Getty on tty1.
Starting Serial Getty on ttyS0...
[ OK ] Started Serial Getty on ttyS0.
[ OK ] Started LSB: set CPUFreq kernel parameters.
[ 20.399433] remoteproc1: failed to load am335x-pru0-fw
[ 20.442914] remoteproc1: request_firmware failed: -2
[ 20.684389] pru-rproc 4a334000.pru0: rproc_boot failed
[ 20.993665] remoteproc1: failed to load am335x-pru1-fw
[ 21.005254] remoteproc1: request_firmware failed: -2
[ 21.180708] pru-rproc 4a338000.pru1: rproc_boot failed
[ OK ] Started LSB: Apache2 web server.
[ OK ] Reached target Sound Card.
Debian GNU/Linux 8 beaglebone ttyS0
BeagleBoard.org Debian Image 2015-11-12
Support/FAQ: http://elinux.org/Beagleboard:BeagleBoneBlack_Debian
default username:password is [debian:temppwd]
beaglebone login:
Let’s login:
beaglebone login: debian
Password:
Linux beaglebone 4.1.12-ti-r29 #1 SMP PREEMPT Mon Nov 9 22:46:19 UTC 2015 armv7l
The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.
Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
debian@beaglebone:~$ uname -a
Linux beaglebone 4.1.12-ti-r29 #1 SMP PREEMPT Mon Nov 9 22:46:19 UTC 2015 armv7l GNU/Linux
debian@beaglebone:~$ cat /etc/debian_version
8.2
debian@beaglebone:~$
Review Wired Networking Interfaces:
debian@beaglebone:~$ ifconfig
eth0 Link encap:Ethernet HWaddr 90:59:af:5f:00:38
UP BROADCAST MULTICAST DYNAMIC MTU:1500 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
Interrupt:170
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
inet6 addr: ::1/128 Scope:Host
UP LOOPBACK RUNNING MTU:65536 Metric:1
RX packets:1120 errors:0 dropped:0 overruns:0 frame:0
TX packets:1120 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:86880 (84.8 KiB) TX bytes:86880 (84.8 KiB)
usb0 Link encap:Ethernet HWaddr 90:59:af:5f:00:31
inet addr:192.168.7.2 Bcast:192.168.7.3 Mask:255.255.255.252
inet6 addr: fe80::9259:afff:fe5f:31/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:38 errors:0 dropped:0 overruns:0 frame:0
TX packets:37 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:6533 (6.3 KiB) TX bytes:8014 (7.8 KiB)
Review Wireless Networking Interfaces:
debian@beaglebone:~$ iwconfig
wlan0 unassociated Nickname:""
Mode:Auto Frequency=2.412 GHz Access Point: Not-Associated
Sensitivity:0/0
Retry:off RTS thr:off Fragment thr:off
Power Management:off
Link Quality:0 Signal level:0 Noise level:0
Rx invalid nwid:0 Rx invalid crypt:0 Rx invalid frag:0
Tx excessive retries:0 Invalid misc:0 Missed beacon:0
lo no wireless extensions.
eth0 no wireless extensions.
usb0 no wireless extensions.
can0 no wireless extensions.
can1 no wireless extensions.
Routing:
debian@beaglebone:~$ route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
192.168.7.0 0.0.0.0 255.255.255.252 U 0 0 0 usb0
Without a default route, we won’t be able to communicate with anything other than 192.168.7.1 given our netmask of 255.255.255.252 (/30). Let’s add a default route.
debian@beaglebone:~$ sudo route add default gw 192.168.7.1
debian@beaglebone:~$ ping -c 3 192.168.7.1
ping: icmp open socket: Operation not permitted
debian@beaglebone:~$ sudo ping -c3 192.168.7.1
PING 192.168.7.1 (192.168.7.1) 56(84) bytes of data.
64 bytes from 192.168.7.1: icmp_seq=1 ttl=128 time=0.419 ms
64 bytes from 192.168.7.1: icmp_seq=2 ttl=128 time=0.444 ms
64 bytes from 192.168.7.1: icmp_seq=3 ttl=128 time=0.462 ms
--- 192.168.7.1 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 1998ms
rtt min/avg/max/mdev = 0.419/0.441/0.462/0.030 ms
debian@beaglebone:~$ sudo ping -c 3 8.8.8.8
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=48 time=28.6 ms
64 bytes from 8.8.8.8: icmp_seq=2 ttl=48 time=26.3 ms
64 bytes from 8.8.8.8: icmp_seq=3 ttl=48 time=26.9 ms
--- 8.8.8.8 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2003ms
rtt min/avg/max/mdev = 26.361/27.302/28.609/0.972 ms
Excellent. We have established connectivity to our Windows host via the usb0 interface and in Windows, we have shared our main network interface with Internet connectivity to the usb0 interface. So we can ping Google’s DNS at 8.8.8.8 and get a response.
debian@beaglebone:~$ host www.google.com
Host www.google.com not found: 2(SERVFAIL)
debian@beaglebone:~$ cat /etc/resolv.conf
# Generated by Connection Manager
nameserver 127.0.0.1
nameserver ::1
DNS doesn’t work. There is a dnsmasq package installed but it is not configured. For now, we’ll just use Google DNS.
root@beaglebone:~# echo nameserver 8.8.8.8 > /etc/resolv.conf
root@beaglebone:~# host www.google.com
www.google.com has address 216.58.216.132
www.google.com has IPv6 address 2607:f8b0:400a:804::1013
Update Packages
root@beaglebone:~# apt-get update
Get:1 http://security.debian.org jessie/updates InRelease [63.1 kB]
Ign http://ftp.us.debian.org jessie InRelease
Get:2 http://repos.rcn-ee.com jessie InRelease [4,347 B]
Get:3 http://ftp.us.debian.org jessie-updates InRelease [136 kB]
Get:4 http://ftp.us.debian.org jessie Release.gpg [2,373 B]
Get:5 http://ftp.us.debian.org jessie Release [148 kB]
Get:6 http://security.debian.org jessie/updates/main armhf Packages [224 kB]
Get:7 http://repos.rcn-ee.com jessie/main armhf Packages [286 kB]
Get:8 http://security.debian.org jessie/updates/contrib armhf Packages [997 B]
Get:9 http://security.debian.org jessie/updates/non-free armhf Packages [20 B]
Get:10 http://ftp.us.debian.org jessie-updates/main armhf Packages [3,589 B]
Get:11 http://ftp.us.debian.org jessie-updates/contrib armhf Packages [20 B]
Get:12 http://ftp.us.debian.org jessie-updates/non-free armhf Packages [20 B]
Get:13 http://ftp.us.debian.org jessie/main armhf Packages [8,836 kB]
Get:14 http://ftp.us.debian.org jessie/contrib armhf Packages [44.7 kB]
Get:15 http://ftp.us.debian.org jessie/non-free armhf Packages [74.6 kB]
Fetched 9,824 kB in 26s (371 kB/s)
Reading package lists... Done
root@beaglebone:~# apt-get upgrade
Reading package lists... Done
Building dependency tree
Reading state information... Done
Calculating upgrade... Done
The following packages will be upgraded:
bind9-host c9-core-installer dpkg dpkg-dev git git-core git-man gyp
initramfs-tools libbind9-90 libdns-export100 libdns100 libdpkg-perl
libgdk-pixbuf2.0-0 libgdk-pixbuf2.0-common libirs-export91 libisc-export95
libisc95 libisccc90 libisccfg-export90 libisccfg90 liblwres90 libpng12-0
libssl-dev libssl-doc libssl1.0.0 libsystemd0 libudev1 libxml2
linux-libc-dev nodejs nodejs-dev nodejs-legacy openssl pastebinit systemd
systemd-sysv udev
38 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.
Need to get 22.5 MB of archives.
After this operation, 137 kB of additional disk space will be used.
Do you want to continue? [Y/n]
Get:1 http://security.debian.org/ jessie/updates/main dpkg armhf 1.17.26 [2,898 kB]
root@beaglebone:~# apt-get upgrade
Reading package lists... Done
Building dependency tree
Reading state information... Done
Calculating upgrade... Done
The following packages will be upgraded:
bind9-host c9-core-installer dpkg dpkg-dev git git-core git-man gyp
initramfs-tools libbind9-90 libdns-export100 libdns100 libdpkg-perl
libgdk-pixbuf2.0-0 libgdk-pixbuf2.0-common libirs-export91 libisc-export95
libisc95 libisccc90 libisccfg-export90 libisccfg90 liblwres90 libpng12-0
libssl-dev libssl-doc libssl1.0.0 libsystemd0 libudev1 libxml2
linux-libc-dev nodejs nodejs-dev nodejs-legacy openssl pastebinit systemd
systemd-sysv udev
38 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.
Need to get 22.5 MB of archives.
After this operation, 137 kB of additional disk space will be used.
Do you want to continue? [Y/n]
Get:1 http://security.debian.org/ jessie/updates/main dpkg armhf 1.17.26 [2,898 kB]
[....]
Processing triggers for initramfs-tools (0.120-1rcnee2~bpo80+20151210+1) ...
update-initramfs: Generating /boot/initrd.img-4.1.12-ti-r29
Processing triggers for libc-bin (2.19-18+deb8u1) ...
root@beaglebone:~#
Install rtl-sdr tools package
root@beaglebone:~# apt-get install rtl-sdr
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following extra packages will be installed:
librtlsdr0
The following NEW packages will be installed:
librtlsdr0 rtl-sdr
0 upgraded, 2 newly installed, 0 to remove and 0 not upgraded.
Need to get 77.0 kB of archives.
After this operation, 292 kB of additional disk space will be used.
Do you want to continue? [Y/n]
Get:1 http://ftp.us.debian.org/debian/ jessie/main librtlsdr0 armhf 0.5.3-3 [24.5 kB]
Get:2 http://ftp.us.debian.org/debian/ jessie/main rtl-sdr armhf 0.5.3-3 [52.5 kB]
Fetched 77.0 kB in 0s (118 kB/s)
Selecting previously unselected package librtlsdr0:armhf.
(Reading database ... 33055 files and directories currently installed.)
Preparing to unpack .../librtlsdr0_0.5.3-3_armhf.deb ...
Unpacking librtlsdr0:armhf (0.5.3-3) ...
Selecting previously unselected package rtl-sdr.
Preparing to unpack .../rtl-sdr_0.5.3-3_armhf.deb ...
Unpacking rtl-sdr (0.5.3-3) ...
Processing triggers for man-db (2.7.0.2-5) ...
Setting up librtlsdr0:armhf (0.5.3-3) ...
Setting up rtl-sdr (0.5.3-3) ...
Processing triggers for libc-bin (2.19-18+deb8u1) ...
Now test it:
root@beaglebone:~# rtl_test
Found 1 device(s):
0: Realtek, RTL2838UHIDIR, SN: 00000001
Using device 0: Generic RTL2832U OEM
Kernel driver is active, or device is claimed by second instance of librtlsdr.
In the first case, please either detach or blacklist the kernel module
(dvb_usb_rtl28xxu), or enable automatic detaching at compile time.
usb_claim_interface error -6
Failed to open rtlsdr device #0.
Hrm, it doesn’t work. I’ve noticed some unusual behavior with the BeagleBone Black when changing USB devices where it does not register bus changes. When changing USB devices, reboot if the USB device is not recognized.
debian@beaglebone:~$ rtl_test
Found 1 device(s):
0: Realtek, RTL2838UHIDIR, SN: 00000001
Using device 0: Generic RTL2832U OEM
Found Rafael Micro R820T tuner
Supported gain values (29): 0.0 0.9 1.4 2.7 3.7 7.7 8.7 12.5 14.4 15.7 16.6 19.7 20.7 22.9 25.4 28.0 29.7 32.8 33.8 36.4 37.2 38.6 40.2 42.1 43.4 43.9 44.5 48.0 49.6
Sampling at 2048000 S/s.
Info: This tool will continuously read from the device, and report if
samples get lost. If you observe no further output, everything is fine.
It lost a few samples but it’s functional. Let’s move on.
rtl-power-fftw
There is another project on github named
rtl-power-fftw
.
This software obtained power spectrum data from RTL devices but uses the FFTW library to do FFT. The FFT performance "
makes mincemeat of the routines used in rtl_power, even on simple processors such as raspberryPi
". This is targeted at more demanding environments like radio astronomy and we’ll use it here. We have a bit more CPU power than a raspberryPi, so I’m very hopeful.
debian@beaglebone:~$ sudo -i
root@beaglebone:~# route add default gw 192.168.7.1
root@beaglebone:~# echo nameserver 8.8.8.8 > /etc/resolv.conf
root@beaglebone:~# git clone https://github.com/AD-Vega/rtl-power-fftw.git
Cloning into 'rtl-power-fftw'...
remote: Counting objects: 519, done.
remote: Total 519 (delta 0), reused 0 (delta 0), pack-reused 519
Receiving objects: 100% (519/519), 170.99 KiB | 173.00 KiB/s, done.
Resolving deltas: 100% (336/336), done.
Checking connectivity... done.
root@beaglebone:~# cd rtl-power-fftw
root@beaglebone:~/rtl-power-fftw# mkdir build
root@beaglebone:~/rtl-power-fftw# cd build
root@beaglebone:~/rtl-power-fftw/build# cmake ..
-bash: cmake: command not found
root@beaglebone:~/rtl-power-fftw/build# apt-cache search cmake
cmake - cross-platform, open-source make system
cmake-curses-gui - curses based user interface for CMake (ccmake)
cmake-data - CMake data files (modules, templates and documentation)
cmake-dbg - debugging symbols for CMake
cmake-doc - extended documentation in various formats for CMake
cmake-qt-gui - Qt4 based user interface for CMake (cmake-gui)
icmake - Intelligent C-like MAKEr, or the ICce MAKE utility.
icmake-doc - Documentation files for icmake
libmarc-file-marcmaker-perl - work with MARCMaker/MARCBreaker records
extra-cmake-modules - Extra modules and scripts for CMake
libdbusmenu-qt5-dev - Qt implementation of the DBusMenu protocol (development)
root@beaglebone:~/rtl-power-fftw/build# apt-get install cmake
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following extra packages will be installed:
cmake-data libarchive13 libjsoncpp0
Suggested packages:
codeblocks eclipse ninja-build lrzip
The following NEW packages will be installed:
cmake cmake-data libarchive13 libjsoncpp0
0 upgraded, 4 newly installed, 0 to remove and 0 not upgraded.
Need to get 3,925 kB of archives.
After this operation, 15.6 MB of additional disk space will be used.
Do you want to continue? [Y/n]
Get:1 http://ftp.us.debian.org/debian/ jessie/main libarchive13 armhf 3.1.2-11 [238 kB]
Get:2 http://repos.rcn-ee.com/debian/ jessie/main cmake-data all 3.4.1-2~bpo80+20151227+1 [1,120 kB]
Get:3 http://ftp.us.debian.org/debian/ jessie/main libjsoncpp0 armhf 0.6.0~rc2-3.1 [62.6 kB]
Get:4 http://repos.rcn-ee.com/debian/ jessie/main cmake armhf 3.4.1-2~bpo80+20151227+1 [2,504 kB]
Fetched 3,925 kB in 4s (941 kB/s)
Selecting previously unselected package cmake-data.
(Reading database ... 33081 files and directories currently installed.)
Preparing to unpack .../cmake-data_3.4.1-2~bpo80+20151227+1_all.deb ...
Unpacking cmake-data (3.4.1-2~bpo80+20151227+1) ...
Selecting previously unselected package libarchive13:armhf.
Preparing to unpack .../libarchive13_3.1.2-11_armhf.deb ...
Unpacking libarchive13:armhf (3.1.2-11) ...
Selecting previously unselected package libjsoncpp0.
Preparing to unpack .../libjsoncpp0_0.6.0~rc2-3.1_armhf.deb ...
Unpacking libjsoncpp0 (0.6.0~rc2-3.1) ...
Selecting previously unselected package cmake.
Preparing to unpack .../cmake_3.4.1-2~bpo80+20151227+1_armhf.deb ...
Unpacking cmake (3.4.1-2~bpo80+20151227+1) ...
Processing triggers for man-db (2.7.0.2-5) ...
Setting up cmake-data (3.4.1-2~bpo80+20151227+1) ...
Setting up libarchive13:armhf (3.1.2-11) ...
Setting up libjsoncpp0 (0.6.0~rc2-3.1) ...
Setting up cmake (3.4.1-2~bpo80+20151227+1) ...
Processing triggers for libc-bin (2.19-18+deb8u1) ...
root@beaglebone:~/rtl-power-fftw/build# cmake ..
-- The C compiler identification is GNU 4.9.2
-- The CXX compiler identification is GNU 4.9.2
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Found PkgConfig: /usr/bin/pkg-config (found version "0.28")
-- Checking for module 'tclap'
-- Package 'tclap' not found
CMake Error at /usr/share/cmake-3.4/Modules/FindPkgConfig.cmake:360 (message):
A required package was not found
Call Stack (most recent call first):
/usr/share/cmake-3.4/Modules/FindPkgConfig.cmake:522 (_pkg_check_modules_internal)
CMakeLists.txt:5 (pkg_check_modules)
-- Configuring incomplete, errors occurred!
See also "/root/rtl-power-fftw/build/CMakeFiles/CMakeOutput.log".
root@beaglebone:~/rtl-power-fftw/build# apt-cache search tclap
libtclap-dev - Templatized command-line argument parser for C++
root@beaglebone:~/rtl-power-fftw/build# apt-get install libtclap-dev -y
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following NEW packages will be installed:
libtclap-dev
0 upgraded, 1 newly installed, 0 to remove and 0 not upgraded.
Need to get 724 kB of archives.
After this operation, 4,358 kB of additional disk space will be used.
Get:1 http://ftp.us.debian.org/debian/ jessie/main libtclap-dev armhf 1.2.1-1 [724 kB]
Fetched 724 kB in 7s (103 kB/s)
Selecting previously unselected package libtclap-dev.
(Reading database ... 35117 files and directories currently installed.)
Preparing to unpack .../libtclap-dev_1.2.1-1_armhf.deb ...
Unpacking libtclap-dev (1.2.1-1) ...
Setting up libtclap-dev (1.2.1-1) ...
root@beaglebone:~/rtl-power-fftw/build# cmake ..
-- Checking for module 'tclap'
-- Found tclap, version 1.2.1
-- Checking for module 'librtlsdr'
-- Package 'librtlsdr' not found
CMake Error at /usr/share/cmake-3.4/Modules/FindPkgConfig.cmake:360 (message):
A required package was not found
Call Stack (most recent call first):
/usr/share/cmake-3.4/Modules/FindPkgConfig.cmake:522 (_pkg_check_modules_internal)
CMakeLists.txt:6 (pkg_check_modules)
-- Configuring incomplete, errors occurred!
See also "/root/rtl-power-fftw/build/CMakeFiles/CMakeOutput.log".
root@beaglebone:~/rtl-power-fftw/build# apt-cache search tclap
libtclap-dev - Templatized command-line argument parser for C++
root@beaglebone:~/rtl-power-fftw/build# apt-get install libtclap-dev -y
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following NEW packages will be installed:
libtclap-dev
0 upgraded, 1 newly installed, 0 to remove and 0 not upgraded.
Need to get 724 kB of archives.
After this operation, 4,358 kB of additional disk space will be used.
Get:1 http://ftp.us.debian.org/debian/ jessie/main libtclap-dev armhf 1.2.1-1 [724 kB]
Fetched 724 kB in 7s (103 kB/s)
Selecting previously unselected package libtclap-dev.
(Reading database ... 35117 files and directories currently installed.)
Preparing to unpack .../libtclap-dev_1.2.1-1_armhf.deb ...
Unpacking libtclap-dev (1.2.1-1) ...
Setting up libtclap-dev (1.2.1-1) ...
root@beaglebone:~/rtl-power-fftw/build# cmake ..
-- Checking for module 'tclap'
-- Found tclap, version 1.2.1
-- Checking for module 'librtlsdr'
-- Package 'librtlsdr' not found
CMake Error at /usr/share/cmake-3.4/Modules/FindPkgConfig.cmake:360 (message):
A required package was not found
Call Stack (most recent call first):
/usr/share/cmake-3.4/Modules/FindPkgConfig.cmake:522 (_pkg_check_modules_internal)
CMakeLists.txt:6 (pkg_check_modules)
-- Configuring incomplete, errors occurred!
See also "/root/rtl-power-fftw/build/CMakeFiles/CMakeOutput.log".
root@beaglebone:~/rtl-power-fftw/build# apt-cache search librtlsdr
librtlsdr-dev - Software defined radio receiver for Realtek RTL2832U (development files)
librtlsdr0 - Software defined radio receiver for Realtek RTL2832U (library)
root@beaglebone:~/rtl-power-fftw/build# apt-get install librtlsdr-dev
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following NEW packages will be installed:
librtlsdr-dev
0 upgraded, 1 newly installed, 0 to remove and 0 not upgraded.
Need to get 26.7 kB of archives.
After this operation, 116 kB of additional disk space will be used.
Get:1 http://ftp.us.debian.org/debian/ jessie/main librtlsdr-dev armhf 0.5.3-3 [26.7 kB]
Fetched 26.7 kB in 0s (52.3 kB/s)
Selecting previously unselected package librtlsdr-dev.
(Reading database ... 35813 files and directories currently installed.)
Preparing to unpack .../librtlsdr-dev_0.5.3-3_armhf.deb ...
Unpacking librtlsdr-dev (0.5.3-3) ...
Setting up librtlsdr-dev (0.5.3-3) ...
root@beaglebone:~/rtl-power-fftw/build# cmake ..
-- Checking for module 'librtlsdr'
-- Found librtlsdr, version 0.5.3
-- Checking for module 'fftw3f'
-- Package 'fftw3f' not found
CMake Error at /usr/share/cmake-3.4/Modules/FindPkgConfig.cmake:360 (message):
A required package was not found
Call Stack (most recent call first):
/usr/share/cmake-3.4/Modules/FindPkgConfig.cmake:522 (_pkg_check_modules_internal)
CMakeLists.txt:7 (pkg_check_modules)
-- Configuring incomplete, errors occurred!
See also "/root/rtl-power-fftw/build/CMakeFiles/CMakeOutput.log".
root@beaglebone:~/rtl-power-fftw/build# apt-cache search fftw3f
root@beaglebone:~/rtl-power-fftw/build# apt-cache search fftw
cl-fftw3 - Common Lisp package for using the FFTW3 library
fftw-dev - library for computing Fast Fourier Transforms
fftw-docs - documentation for fftw
fftw2 - library for computing Fast Fourier Transforms
sfftw-dev - library for computing Fast Fourier Transforms
sfftw2 - library for computing Fast Fourier Transforms
libfftw3-3 - Library for computing Fast Fourier Transforms
libfftw3-bin - Library for computing Fast Fourier Transforms - Tools
libfftw3-dbg - Library for computing Fast Fourier Transforms - debug symbols
libfftw3-dev - Library for computing Fast Fourier Transforms - development
libfftw3-doc - Documentation for fftw version 3
libfftw3-double3 - Library for computing Fast Fourier Transforms - Double precision
libfftw3-mpi-dev - MPI Library for computing Fast Fourier Transforms - development
libfftw3-mpi3 - MPI Library for computing Fast Fourier Transforms
libfftw3-single3 - Library for computing Fast Fourier Transforms - Single precision
mffm-fftw-dev - A C++ wrapper for the fftw.org C library (version 3)
mffm-fftw1 - A C++ wrapper for the fftw.org C library (version 3)
python-fftw - Python bindings to the FFTW3 C-library for Fourier transforms
root-plugin-math-fftw3 - FFTw plugin for ROOT
ruby-fftw3 - Ruby interface to the FFTW Ver.3 library
ruby-fftw3-dbg - Ruby FFT library using FFTW Ver.3
yorick-yeti-fftw - FFT plugin for the Yorick language
root@beaglebone:~/rtl-power-fftw/build# apt-get install libfftw3-dev
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following extra packages will be installed:
libfftw3-bin libfftw3-double3 libfftw3-single3
Suggested packages:
libfftw3-doc
The following NEW packages will be installed:
libfftw3-bin libfftw3-dev libfftw3-double3 libfftw3-single3
0 upgraded, 4 newly installed, 0 to remove and 0 not upgraded.
Need to get 2,381 kB of archives.
After this operation, 6,543 kB of additional disk space will be used.
Do you want to continue? [Y/n]
Get:1 http://ftp.us.debian.org/debian/ jessie/main libfftw3-double3 armhf 3.3.4-2 [442 kB]
Get:2 http://ftp.us.debian.org/debian/ jessie/main libfftw3-single3 armhf 3.3.4-2 [727 kB]
Get:3 http://ftp.us.debian.org/debian/ jessie/main libfftw3-bin armhf 3.3.4-2 [41.7 kB]
Get:4 http://ftp.us.debian.org/debian/ jessie/main libfftw3-dev armhf 3.3.4-2 [1,171 kB]
Fetched 2,381 kB in 7s (308 kB/s)
Selecting previously unselected package libfftw3-double3:armhf.
(Reading database ... 35822 files and directories currently installed.)
Preparing to unpack .../libfftw3-double3_3.3.4-2_armhf.deb ...
Unpacking libfftw3-double3:armhf (3.3.4-2) ...
Selecting previously unselected package libfftw3-single3:armhf.
Preparing to unpack .../libfftw3-single3_3.3.4-2_armhf.deb ...
Unpacking libfftw3-single3:armhf (3.3.4-2) ...
Selecting previously unselected package libfftw3-bin.
Preparing to unpack .../libfftw3-bin_3.3.4-2_armhf.deb ...
Unpacking libfftw3-bin (3.3.4-2) ...
Selecting previously unselected package libfftw3-dev:armhf.
Preparing to unpack .../libfftw3-dev_3.3.4-2_armhf.deb ...
Unpacking libfftw3-dev:armhf (3.3.4-2) ...
Processing triggers for man-db (2.7.0.2-5) ...
Setting up libfftw3-double3:armhf (3.3.4-2) ...
Setting up libfftw3-single3:armhf (3.3.4-2) ...
Setting up libfftw3-bin (3.3.4-2) ...
Setting up libfftw3-dev:armhf (3.3.4-2) ...
Processing triggers for libc-bin (2.19-18+deb8u1) ...
root@beaglebone:~/rtl-power-fftw/build# apt-cache search librtlsdr
librtlsdr-dev - Software defined radio receiver for Realtek RTL2832U (development files)
librtlsdr0 - Software defined radio receiver for Realtek RTL2832U (library)
root@beaglebone:~/rtl-power-fftw/build# apt-get install librtlsdr-dev
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following NEW packages will be installed:
librtlsdr-dev
0 upgraded, 1 newly installed, 0 to remove and 0 not upgraded.
Need to get 26.7 kB of archives.
After this operation, 116 kB of additional disk space will be used.
Get:1 http://ftp.us.debian.org/debian/ jessie/main librtlsdr-dev armhf 0.5.3-3 [26.7 kB]
Fetched 26.7 kB in 0s (52.3 kB/s)
Selecting previously unselected package librtlsdr-dev.
(Reading database ... 35813 files and directories currently installed.)
Preparing to unpack .../librtlsdr-dev_0.5.3-3_armhf.deb ...
Unpacking librtlsdr-dev (0.5.3-3) ...
Setting up librtlsdr-dev (0.5.3-3) ...
root@beaglebone:~/rtl-power-fftw/build# cmake ..
-- Checking for module 'librtlsdr'
-- Found librtlsdr, version 0.5.3
-- Checking for module 'fftw3f'
-- Package 'fftw3f' not found
CMake Error at /usr/share/cmake-3.4/Modules/FindPkgConfig.cmake:360 (message):
A required package was not found
Call Stack (most recent call first):
/usr/share/cmake-3.4/Modules/FindPkgConfig.cmake:522 (_pkg_check_modules_internal)
CMakeLists.txt:7 (pkg_check_modules)
-- Configuring incomplete, errors occurred!
See also "/root/rtl-power-fftw/build/CMakeFiles/CMakeOutput.log".
root@beaglebone:~/rtl-power-fftw/build# apt-cache search fftw3f
root@beaglebone:~/rtl-power-fftw/build# apt-cache search fftw
cl-fftw3 - Common Lisp package for using the FFTW3 library
fftw-dev - library for computing Fast Fourier Transforms
fftw-docs - documentation for fftw
fftw2 - library for computing Fast Fourier Transforms
sfftw-dev - library for computing Fast Fourier Transforms
sfftw2 - library for computing Fast Fourier Transforms
libfftw3-3 - Library for computing Fast Fourier Transforms
libfftw3-bin - Library for computing Fast Fourier Transforms - Tools
libfftw3-dbg - Library for computing Fast Fourier Transforms - debug symbols
libfftw3-dev - Library for computing Fast Fourier Transforms - development
libfftw3-doc - Documentation for fftw version 3
libfftw3-double3 - Library for computing Fast Fourier Transforms - Double precision
libfftw3-mpi-dev - MPI Library for computing Fast Fourier Transforms - development
libfftw3-mpi3 - MPI Library for computing Fast Fourier Transforms
libfftw3-single3 - Library for computing Fast Fourier Transforms - Single precision
mffm-fftw-dev - A C++ wrapper for the fftw.org C library (version 3)
mffm-fftw1 - A C++ wrapper for the fftw.org C library (version 3)
python-fftw - Python bindings to the FFTW3 C-library for Fourier transforms
root-plugin-math-fftw3 - FFTw plugin for ROOT
ruby-fftw3 - Ruby interface to the FFTW Ver.3 library
ruby-fftw3-dbg - Ruby FFT library using FFTW Ver.3
yorick-yeti-fftw - FFT plugin for the Yorick language
root@beaglebone:~/rtl-power-fftw/build# apt-get install libfftw3-dev
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following extra packages will be installed:
libfftw3-bin libfftw3-double3 libfftw3-single3
Suggested packages:
libfftw3-doc
The following NEW packages will be installed:
libfftw3-bin libfftw3-dev libfftw3-double3 libfftw3-single3
0 upgraded, 4 newly installed, 0 to remove and 0 not upgraded.
Need to get 2,381 kB of archives.
After this operation, 6,543 kB of additional disk space will be used.
Do you want to continue? [Y/n]
Get:1 http://ftp.us.debian.org/debian/ jessie/main libfftw3-double3 armhf 3.3.4-2 [442 kB]
Get:2 http://ftp.us.debian.org/debian/ jessie/main libfftw3-single3 armhf 3.3.4-2 [727 kB]
Get:3 http://ftp.us.debian.org/debian/ jessie/main libfftw3-bin armhf 3.3.4-2 [41.7 kB]
Get:4 http://ftp.us.debian.org/debian/ jessie/main libfftw3-dev armhf 3.3.4-2 [1,171 kB]
Fetched 2,381 kB in 7s (308 kB/s)
Selecting previously unselected package libfftw3-double3:armhf.
(Reading database ... 35822 files and directories currently installed.)
Preparing to unpack .../libfftw3-double3_3.3.4-2_armhf.deb ...
Unpacking libfftw3-double3:armhf (3.3.4-2) ...
Selecting previously unselected package libfftw3-single3:armhf.
Preparing to unpack .../libfftw3-single3_3.3.4-2_armhf.deb ...
Unpacking libfftw3-single3:armhf (3.3.4-2) ...
Selecting previously unselected package libfftw3-bin.
Preparing to unpack .../libfftw3-bin_3.3.4-2_armhf.deb ...
Unpacking libfftw3-bin (3.3.4-2) ...
Selecting previously unselected package libfftw3-dev:armhf.
Preparing to unpack .../libfftw3-dev_3.3.4-2_armhf.deb ...
Unpacking libfftw3-dev:armhf (3.3.4-2) ...
Processing triggers for man-db (2.7.0.2-5) ...
Setting up libfftw3-double3:armhf (3.3.4-2) ...
Setting up libfftw3-single3:armhf (3.3.4-2) ...
Setting up libfftw3-bin (3.3.4-2) ...
Setting up libfftw3-dev:armhf (3.3.4-2) ...
Processing triggers for libc-bin (2.19-18+deb8u1) ...
root@beaglebone:~/rtl-power-fftw/build# cmake ..
-- Checking for module 'fftw3f'
-- Found fftw3f, version 3.3.4
-- Check size of long long int
-- Check size of long long int - done
-- Configuring done
-- Generating done
-- Build files have been written to: /root/rtl-power-fftw/build
root@beaglebone:~/rtl-power-fftw/build# make
Scanning dependencies of target rtl_power_fftw
[ 25%] Building CXX object CMakeFiles/rtl_power_fftw.dir/src/rtl_power_fftw.cxx.o
[ 50%] Building CXX object CMakeFiles/rtl_power_fftw.dir/src/datastore.cxx.o
[ 75%] Building CXX object CMakeFiles/rtl_power_fftw.dir/src/params.cxx.o
[100%] Linking CXX executable rtl_power_fftw
[100%] Built target rtl_power_fftw
root@beaglebone:~/rtl-power-fftw/build# ls
CMakeCache.txt CMakeFiles cmake_install.cmake Makefile rtl_power_fftw
root@beaglebone:~/rtl-power-fftw/build# make install
[100%] Built target rtl_power_fftw
Install the project...
-- Install configuration: "RelWithDebInfo"
-- Installing: /usr/local/bin/rtl_power_fftw
-- Installing: /usr/local/share/man/man1/rtl_power_fftw.1
We have successfully compiled the rtl_power_fftw binary. Let’s see the options:
null
root@beaglebone:~/rtl-power-fftw/build# ./rtl_power_fftw -h
USAGE:
./rtl_power_fftw [-B ] [-b ] [-c] [-d
] [-f ] [-g <1/10th of dB>]
[-n ] [-o ] [-p ] [-r
] [-s ] [-T] [-t ] [-w
] [--buffers ] [--] [--version] [-h]
Where:
-B , --baseline
Subtract baseline, read baseline data from file or stdin.
-b , --bins
Number of bins in FFT spectrum (must be even number)
-c, --continue
Repeat the same measurement endlessly.
-d , --device
RTL-SDR device index.
-f , --freq
Center frequency of the receiver or frequency range to scan.
-g <1/10th of dB>, --gain <1/10th of dB>
Receiver gain.
-n , --repeats
Number of scans for averaging (incompatible with -t).
-o , --overlap
Define lower boundary for overlap when frequency hopping (otherwise
meaningless).
-p , --ppm
Set custom ppm error in RTL-SDR device.
-r , --rate
Sample rate of the receiver.
-s , --buffer-size
Size of read buffers (leave it unless you know what you are doing).
-T, --strict-time
End measurement when the time set with --time option is up, regardless
of gathered samples.
-t , --time
Integration time (incompatible with -n).
-w , --window
Use window function, from file or stdin.
--buffers
Number of read buffers (don't touch unless running out of memory).
--, --ignore_rest
Ignores the rest of the labeled arguments following this flag.
--version
Displays version information and exits.
-h, --help
Displays usage information and exits.
Obtain power spectrum from RTL device using FFTW library.
Ok, let’s execute it by tuning to 145MHz with a 2MHz sample size, splitting the spectral data into 80 FFT bins (2MHz/80 bins == 25KHz/bin). This will give us 25KHz samples across 2MHz of spectrum.
beaglebone:~/rtl-power-fftw/build# ./rtl_power_fftw -r 2000000 -f 145M -t 1 -b 80
Found Rafael Micro R820T tuner
Available gains (in 1/10th of dB): 0, 9, 14, 27, 37, 77, 87, 125, 144, 157, 166, 197, 207, 229, 254, 280, 297, 328, 338, 364, 372, 386, 402, 421, 434, 439, 445, 480, 496
Selected nearest available gain: 372 (37.2 dB)
Exact sample rate is: 2000000.052982 Hz
Actual sample rate: 2000000 Hz
Number of bins: 80
Total number of (complex) samples to collect: 2000000
Buffer length: 1638400
Number of averaged spectra: 25000Estimated time of measurements: 1 seconds
Tuning to 145000000 Hz (try 1)
Device tuned to: 145000000 Hz
Acquisition started at 2016-01-10 22:05:51 UTC
Acquisition done at 2016-01-10 22:05:54 UTC
Actual number of (complex) samples collected: 2000000
Actual number of device readouts: 3
Number of successful readouts: 3
Actual number of averaged spectra: 25000
Effective integration time: 1 seconds
# rtl-power-fftw output
# Acquisition start: 2016-01-10 22:05:51 UTC
# Acquisition end: 2016-01-10 22:05:54 UTC
#
# frequency [Hz] power spectral density [dB/Hz]
1.44e+08 -42.9543
1.44025e+08 -49.5468
1.4405e+08 -48.998
1.44075e+08 -48.3581
1.441e+08 -47.5084
1.44125e+08 -46.9782
1.4415e+08 -46.3243
1.44175e+08 -46.1228
1.442e+08 -45.8917
1.44225e+08 -45.4923
1.4425e+08 -45.2527
1.44275e+08 -45.0918
1.443e+08 -45.0514
1.44325e+08 -45.0783
1.4435e+08 -45.0151
1.44375e+08 -45.0236
1.444e+08 -45.3503
1.44425e+08 -45.4552
1.4445e+08 -45.5265
1.44475e+08 -45.7308
1.445e+08 -45.9445
1.44525e+08 -46.1858
1.4455e+08 -46.3954
1.44575e+08 -46.1295
1.446e+08 -46.7449
1.44625e+08 -46.8207
1.4465e+08 -46.9043
1.44675e+08 -47.007
1.447e+08 -46.3486
1.44725e+08 -46.5357
1.4475e+08 -46.8672
1.44775e+08 -47.0745
1.448e+08 -47.1703
1.44825e+08 -46.9858
1.4485e+08 -46.9039
1.44875e+08 -46.8757
1.449e+08 -47.1295
1.44925e+08 -47.4566
1.4495e+08 -47.5535
1.44975e+08 -47.6782
1.45e+08 -47.8053
1.45025e+08 -47.9362
1.4505e+08 -48.069
1.45075e+08 -48.2265
1.451e+08 -48.3089
1.45125e+08 -48.6833
1.4515e+08 -48.8835
1.45175e+08 -48.6015
1.452e+08 -49.0843
1.45225e+08 -49.4431
1.4525e+08 -48.527
1.45275e+08 -49.8551
1.453e+08 -49.4284
1.45325e+08 -49.9214
1.4535e+08 -50.2484
1.45375e+08 -49.4057
1.454e+08 -50.2895
1.45425e+08 -50.1491
1.4545e+08 -49.7122
1.45475e+08 -50.0939
1.455e+08 -50.057
1.45525e+08 -50.0113
1.4555e+08 -49.9071
1.45575e+08 -49.69
1.456e+08 -49.5441
1.45625e+08 -49.9736
1.4565e+08 -49.8292
1.45675e+08 -50.1862
1.457e+08 -51.0741
1.45725e+08 -51.0371
1.4575e+08 -51.5102
1.45775e+08 -51.7468
1.458e+08 -50.5511
1.45825e+08 -52.3874
1.4585e+08 -52.1027
1.45875e+08 -52.8657
1.459e+08 -52.9722
1.45925e+08 -52.2689
1.4595e+08 -52.0936
1.45975e+08 -50.0316
Buffer queue histogram: 0 0 0 1 1 1
Eureka!
The informational header information is output to STDERR so we can redirect that to /dev/null or parse it separately from the main output. The main output has a few comments with leading # symbols, then key value pairs, one per line, with the FFT frequency bin in scientific notation and the power level in dB.
AWS IoT Service
To use the Thing with the AWS IoT service, we’ll obviously need an AWS account. After creating an AWS account, secure it by enabling Multi-Factor Authentication, then login to the AWS console and navigate to the AWS IoT service.
Now click the
Create Resource
button
Then click the
Create a Thing
button.
Name your thing "sdr1", leave the attributes empty, and click the
Create
button.
You have now created a
Thing
in AWS IoT. Click the
View thing
button.
You can see properties about the
Thing
in the right pane. We have the
Name
, the
REST API endpoint
, the
MQTT topic
, the
Last update
,
Attributes
, and
Linked certificates
.
http://docs.aws.amazon.com/iot/latest/developerguide/verify-pub-sub.html
None of these supported SDKs really match what we’re doing here. Let’s select the
Embedded C
SDK and see what happens
Ok, it wants to create a certificate and policy for us. Click the
Generate certificate and policy
button.
Download the public key, private key, and certificate. This is our only chance to download the keys since Amazon.
Note: You must left-click the links. Right-clicking and choosing Save As will not work.
After clicking each link, I had the following files:
- 81921dd06a-certificate.pem.crt
- 81921dd06a-private.pem.key
- 81921dd06a-public.pem.key
Click the
Confirm & start connecting
button.
Note the AWS_IOT_MQTT_HOST definition that is our custom endpoint. There is also a reference here to a root-CA.crt file that we don’t have yet.
In the
AWS IoT Developer Guide
, they tell us where to download the root-CA.crt:
Click the
Return to Thing Detail
button.
Thing State
If you click the
Update shadow
link on the Thing detail pane, you will see this:
Notice the JSON document with two hash keys named
reported
and
desired
, each with a value that is a HASH. Understand also that you can edit the black area and click the
Update shadow
button to save the shadow state.
It’s time to take a step back and think about how this works. The
reported
hash is what our Thing will report to AWS. The
desired
hash will contain key-value pairs set by other applications, or by us manually through the AWS console, to tell the Thing what state we want it to assume.
You have to remember that the Things may have disconnected operation and so this all happens asynchronously. We can tell a Thing about a desired state, but it is up to the Thing to listen for these state changes, implement them, and report back to Amazon that it has assumed that state. Until then, it’s going to chug along with its existing state.
How do we leverage this with our Thing?
For the SDR Thing, we can use this to change the frequency of the tuner, select different sample rates, sample time periods, FFT bin sizes, and so on. Essentially, we can expose all of the flags of the rtl_power_fftw binary through the shadow state.
More than that, we can make the Thing do other tsks such as capturing radio signals instead of just sampling the power levels.
How does the Thing learn about Shadow State changes?
This brings us to the Message Queue Telemetry Transport (MQTT) protocol. You can find more information about the protocol, AWS IoT compatibility, and release notes in the
Amazon IoT Developer Guide
. But at a conceptual level, understand that it is a message queue with various topics that we can subscribe to in order to receive messages, and that we can publish new messages as well.
Amazon has
defined several topics
that allow us to do things like detecting changes in the state, whether published messages have been accepted or rejected, retrieving the current state, and much more.
So, in a nutshell, the Thing will need to:
- subscribe to topics to learn about state changes,
- get the current state,
- change its state to a desired state, if applicable,
- begin reporting its state continuously
- dynamically change its state on receipt of a new desired state via callbacks
We will need to write a script or program that performs these tasks, including parsing the rtl_power_fftw output to coerce it into JSON.
Updating the Thing Shadow State
We have created the Thing object in AWS IoT, we have downloaded certificates and keys, and we know a thing or two about MQTT. Let’s see if we can just update this shadow state once.
Get the keys and certificates onto the Thing
If you’re using Linux, just scp the files to the BeagleBone. On Windows, I used the MobaXterm cygwin bash shell to scp the files.
[2016-01-10 16:30.35] /drives/c/Users/brg/Downloads
[..............] ➤ scp 81921dd06a-* debian@192.168.7.2:~
Debian GNU/Linux 8
BeagleBoard.org Debian Image 2015-11-12
Support/FAQ: http://elinux.org/Beagleboard:BeagleBoneBlack_Debian
default username:password is [debian:temppwd]
81921dd06a-certificate.pem.crt 100% 1224 1.2KB/s 00:00
81921dd06a-private.pem.key 100% 1679 1.6KB/s 00:00
81921dd06a-public.pem.key 100% 451 0.4KB/s 00:00
Now become root, create a /root/
keys
directory, and move the files from /home/debian. Also we need to download the root-CA.crt.
debian@beaglebone:~$ sudo -i
root@beaglebone:~# mkdir keys
root@beaglebone:~# ls
keys rtl-power-fftw
root@beaglebone:~# cd keys/
root@beaglebone:~/keys# ls
root@beaglebone:~/keys# mv /home/debian/81921dd06a-* .
root@beaglebone:~/keys# wget -O root-CA.crt https://www.symantec.com/content/en/us/enterprise/verisign/roots/VeriSign-Class%203-Public-Primary-Certification-Authority-G5.pem
root@beaglebone:~/keys# chown root.root *
root@beaglebone:~/keys# ls -al
total 24
drwxr-xr-x 2 root root 4096 Jan 11 00:36 .
drwx------ 7 root root 4096 Jan 11 00:33 ..
-rwx------ 1 root root 1224 Jan 11 00:32 81921dd06a-certificate.pem.crt
-rwx------ 1 root root 1679 Jan 11 00:32 81921dd06a-private.pem.key
-rwx------ 1 root root 451 Jan 11 00:32 81921dd06a-public.pem.key
-rw-r--r-- 1 root root 1758 Jun 20 2014 root-CA.crt
Install Mosquitto Tools
Mosquitto is a MQTT version 3.1/3.1.1 compatible message broker and by installing it or the clients package, we can talk to AWS IoT.
root@beaglebone:~/keys# apt-cache search mosquitto
libmosquitto-dev - MQTT version 3.1 client library, development files
libmosquitto1 - MQTT version 3.1 client library
libmosquittopp-dev - MQTT version 3.1 client C++ library, development files
libmosquittopp1 - MQTT version 3.1 client C++ library
mosquitto - MQTT version 3.1/3.1.1 compatible message broker
mosquitto-clients - Mosquitto command line MQTT clients
mosquitto-dbg - debugging symbols for mosquitto binaries
python-mosquitto - MQTT version 3.1 Python client library
python3-mosquitto - MQTT version 3.1 Python 3 client library
root@beaglebone:~/keys# apt-get install mosquitto-clients
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following extra packages will be installed:
libmosquitto1
The following NEW packages will be installed:
libmosquitto1 mosquitto-clients
0 upgraded, 2 newly installed, 0 to remove and 0 not upgraded.
Need to get 76.1 kB of archives.
After this operation, 200 kB of additional disk space will be used.
Do you want to continue? [Y/n]
Get:1 http://ftp.us.debian.org/debian/ jessie/main libmosquitto1 armhf 1.3.4-2 [36.9 kB]
Get:2 http://ftp.us.debian.org/debian/ jessie/main mosquitto-clients armhf 1.3.4-2 [39.2 kB]
Fetched 76.1 kB in 0s (85.3 kB/s)
Selecting previously unselected package libmosquitto1.
(Reading database ... 35883 files and directories currently installed.)
Preparing to unpack .../libmosquitto1_1.3.4-2_armhf.deb ...
Unpacking libmosquitto1 (1.3.4-2) ...
Selecting previously unselected package mosquitto-clients.
Preparing to unpack .../mosquitto-clients_1.3.4-2_armhf.deb ...
Unpacking mosquitto-clients (1.3.4-2) ...
Processing triggers for man-db (2.7.0.2-5) ...
Setting up libmosquitto1 (1.3.4-2) ...
Setting up mosquitto-clients (1.3.4-2) ...
Processing triggers for libc-bin (2.19-18+deb8u1) ...
Now we have
mosquitto_pub
and
mosquitto_sub
binaries in our path.
Publish State
Let’s try publishing a shadow state:
root@beaglebone:~/keys# mosquitto_pub --cert 81921dd06a-certificate.pem.crt --key 81921dd06a-private.pem.key --cafile root-CA.crt -h A1PE707UQJBVJT.iot.us-east-1.amazonaws.com -p 8883 -q 1 -d -t '$aws/things/sdr1/shadow/update' -i sdr1 -m '{ "state":{"reported": { "hello": "world" } } }'
Client sdr1 sending CONNECT
OpenSSL Error: error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed
Error: Protocol error
Hrm, ok, let’s try it again with the mosquitto_pub –insecure option:
root@beaglebone:~/keys# mosquitto_pub --cert 81921dd06a-certificate.pem.crt --key 81921dd06a-private.pem.key --cafile root-CA.crt -h A1PE707UQJBVJT.iot.us-east-1.amazonaws.com -p 8883 -q 1 -d -t '$aws/things/sdr1/shadow/update' -i sdr1 -m '{ "state":{"reported": { "hello": "world" } } }' --insecure
Client sdr1 sending CONNECT
Client sdr1 received CONNACK
Client sdr1 sending PUBLISH (d0, q1, r0, m1, '$aws/things/sdr1/shadow/update', ... (47 bytes))
Client sdr1 received PUBACK (Mid: 1)
Client sdr1 sending DISCONNECT
That looks promising. Let’s check the AWS IoT console.
It worked. We successfully reported the state and can see that AWS IoT also records metadata that includes a UNIX epoch timestamp.
We’re going to need to subscribe to topics to change the Thing state when users or applications request it, so we need some way to subscribe to topics, parse the messages received from it, execute the rtl_power_fftw binary and parse the output into JSON, and report state back. We could use mosquitto_pub and mosquitto_sub with bash but that seemed hairy to me.
Rumors of Perl’s death have been great^H^H^H^H^Hsomewhat exaggerated. I still reach for it from time to time because I can leverage a huge library of code with the Comprehensive Perl Archive Network (CPAN). A quick search of
http://search.cpan.org/recent
for "MQTT SSL" revealed a
Net::MQTT::Simple::SSL module
. It looks like it will support the TLS requirements of AWS IoT MQTT, so let’s install it.
root@beaglebone:~# apt-get install -y dh-make-perl
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following extra packages will be installed:
apt-file libalgorithm-c3-perl libapt-pkg-perl libarray-unique-perl
libauthen-sasl-perl libclass-accessor-chained-perl libclass-accessor-perl
libclass-c3-perl libclass-c3-xs-perl libconfig-file-perl
libdata-optlist-perl libdata-section-perl libemail-address-perl
libemail-date-format-perl libencode-locale-perl libenv-sanctify-perl
libexporter-lite-perl libfile-chdir-perl libfile-listing-perl
libfile-which-perl libfont-afm-perl libhtml-form-perl libhtml-format-perl
libhtml-parser-perl libhtml-tagset-perl libhtml-tree-perl
libhttp-cookies-perl libhttp-daemon-perl libhttp-date-perl
libhttp-message-perl libhttp-negotiate-perl libhttp-server-simple-perl
libio-html-perl libio-socket-ssl-perl libio-string-perl libio-stringy-perl
liblist-moreutils-perl liblwp-mediatypes-perl liblwp-protocol-https-perl
libmailtools-perl libmodule-depends-perl libmro-compat-perl libnet-http-perl
libnet-smtp-ssl-perl libnet-ssleay-perl libparams-util-perl
libparse-debcontrol-perl libparse-debianchangelog-perl
libregexp-assemble-perl libsoftware-license-perl libsub-exporter-perl
libsub-install-perl libsub-name-perl libtext-template-perl
libtie-ixhash-perl liburi-perl libwww-mechanize-perl libwww-perl
libwww-robotrules-perl libxdelta2 libyaml-libyaml-perl libyaml-perl pbzip2
pristine-tar xdelta
Suggested packages:
libdigest-hmac-perl libgssapi-perl libdata-dump-perl libcrypt-ssleay-perl
libhtml-template-perl libxml-simple-perl libauthen-ntlm-perl
libyaml-shell-perl
The following NEW packages will be installed:
apt-file dh-make-perl libalgorithm-c3-perl libapt-pkg-perl
libarray-unique-perl libauthen-sasl-perl libclass-accessor-chained-perl
libclass-accessor-perl libclass-c3-perl libclass-c3-xs-perl
libconfig-file-perl libdata-optlist-perl libdata-section-perl
libemail-address-perl libemail-date-format-perl libencode-locale-perl
libenv-sanctify-perl libexporter-lite-perl libfile-chdir-perl
libfile-listing-perl libfile-which-perl libfont-afm-perl libhtml-form-perl
libhtml-format-perl libhtml-parser-perl libhtml-tagset-perl
libhtml-tree-perl libhttp-cookies-perl libhttp-daemon-perl libhttp-date-perl
libhttp-message-perl libhttp-negotiate-perl libhttp-server-simple-perl
libio-html-perl libio-socket-ssl-perl libio-string-perl libio-stringy-perl
liblist-moreutils-perl liblwp-mediatypes-perl liblwp-protocol-https-perl
libmailtools-perl libmodule-depends-perl libmro-compat-perl libnet-http-perl
libnet-smtp-ssl-perl libnet-ssleay-perl libparams-util-perl
libparse-debcontrol-perl libparse-debianchangelog-perl
libregexp-assemble-perl libsoftware-license-perl libsub-exporter-perl
libsub-install-perl libsub-name-perl libtext-template-perl
libtie-ixhash-perl liburi-perl libwww-mechanize-perl libwww-perl
libwww-robotrules-perl libxdelta2 libyaml-libyaml-perl libyaml-perl pbzip2
pristine-tar xdelta
0 upgraded, 66 newly installed, 0 to remove and 0 not upgraded.
[....]
Eww, that installed a lot of things we aren’t going to need later. But let’s worry about that later and run
cpan2deb
to build a Debian package of the module:
root@beaglebone:~# cpan2deb Net::MQTT::Simple::SSL
== dh-make-perl 0.84 ==
CPAN.pm requires configuration, but most of it can be done automatically.
If you answer 'no' below, you will enter an interactive dialog for each
configuration option instead.
Would you like to configure as much as possible automatically? [yes]
Autoconfiguration complete.
commit: wrote '/root/.cpan/CPAN/MyConfig.pm'
You can re-run configuration any time with 'o conf init' in the CPAN shell
Fetching with LWP:
http://www.cpan.org/authors/01mailrc.txt.gz
Reading '/root/.cpan/sources/authors/01mailrc.txt.gz'
............................................................................DONE
Fetching with LWP:
http://www.cpan.org/modules/02packages.details.txt.gz
Reading '/root/.cpan/sources/modules/02packages.details.txt.gz'
Database was generated on Mon, 11 Jan 2016 00:41:02 GMT
..............
New CPAN.pm version (v2.10) available.
[Currently running version is v2.05]
You might want to try
install CPAN
reload cpan
to both upgrade CPAN.pm and run the new version without leaving
the current session.
..............................................................DONE
Fetching with LWP:
http://www.cpan.org/modules/03modlist.data.gz
Reading '/root/.cpan/sources/modules/03modlist.data.gz'
DONE
Writing /root/.cpan/Metadata
Fetching with LWP:
http://www.cpan.org/authors/id/J/JU/JUERD/Net-MQTT-Simple-1.21.tar.gz
CPAN: Digest::SHA loaded ok (v5.88)
Fetching with LWP:
http://www.cpan.org/authors/id/J/JU/JUERD/CHECKSUMS
Checksum for /root/.cpan/sources/authors/id/J/JU/JUERD/Net-MQTT-Simple-1.21.tar.gz ok
Uncompressed /root/.cpan/sources/authors/id/J/JU/JUERD/Net-MQTT-Simple-1.21.tar.gz successfully
Using Tar:/bin/tar xvf "Net-MQTT-Simple-1.21.tar":
Couldn't untar Net-MQTT-Simple-1.21.tar: 'Cannot allocate memory'
CPAN: File::Temp loaded ok (v0.2304)
Failed to move /root/.cpan/build/JUERD-8eu3nP to /root/Net-MQTT-Simple-1.21: Cannot allocate memory at /usr/share/perl5/DhMakePerl/Command/make.pm line 360.
Hrm, out of memory issues. We’re low on disk space so let’s try clearing the apt cache and running it again:
root@beaglebone:~# df -h
Filesystem Size Used Avail Use% Mounted on
udev 10M 0 10M 0% /dev
tmpfs 98M 8.3M 90M 9% /run
/dev/mmcblk0p2 1.6G 1.3G 204M 86% /
tmpfs 245M 0 245M 0% /dev/shm
tmpfs 5.0M 4.0K 5.0M 1% /run/lock
tmpfs 245M 0 245M 0% /sys/fs/cgroup
root@beaglebone:~# apt-get clean
root@beaglebone:~# df -h
Filesystem Size Used Avail Use% Mounted on
udev 10M 0 10M 0% /dev
tmpfs 98M 8.3M 90M 9% /run
/dev/mmcblk0p2 1.6G 1.2G 244M 84% /
tmpfs 245M 0 245M 0% /dev/shm
tmpfs 5.0M 4.0K 5.0M 1% /run/lock
tmpfs 245M 0 245M 0% /sys/fs/cgroup
root@beaglebone:~# cpan2deb Net::MQTT::Simple
== dh-make-perl 0.84 ==
Reading '/root/.cpan/Metadata'
Database was generated on Mon, 11 Jan 2016 00:41:02 GMT
CPAN: Digest::SHA loaded ok (v5.88)
Checksum for /root/.cpan/sources/authors/id/J/JU/JUERD/Net-MQTT-Simple-1.21.tar.gz ok
Net-MQTT-Simple-1.21/
Net-MQTT-Simple-1.21/META.json
Net-MQTT-Simple-1.21/Changes
Net-MQTT-Simple-1.21/lib/
Net-MQTT-Simple-1.21/lib/Net/
Net-MQTT-Simple-1.21/lib/Net/MQTT/
Net-MQTT-Simple-1.21/lib/Net/MQTT/Simple/
Net-MQTT-Simple-1.21/lib/Net/MQTT/Simple/SSL.pm
Net-MQTT-Simple-1.21/lib/Net/MQTT/Simple.pm
Net-MQTT-Simple-1.21/README
Net-MQTT-Simple-1.21/bin/
Net-MQTT-Simple-1.21/bin/mqtt-simple
Net-MQTT-Simple-1.21/t/
Net-MQTT-Simple-1.21/t/use.t
Net-MQTT-Simple-1.21/t/regex.t
Net-MQTT-Simple-1.21/META.yml
Net-MQTT-Simple-1.21/MANIFEST
Net-MQTT-Simple-1.21/Makefile.PL
CPAN: File::Temp loaded ok (v0.2304)
Using META.json
Found: Net-MQTT-Simple 1.21 (libnet-mqtt-simple-perl arch=all)
cat: /etc/mailname: No such file or directory
Switched to a new branch 'master'
No APT contents can be loaded.
Please install 'apt-file' package (at least version 2.5.0) and
run 'apt-file update' as root.
Dependencies not updated.
Using maintainer: root
Found docs:
cat: /etc/mailname: No such file or directory
Using rules: /usr/share/dh-make-perl/rules.dh.tiny
cat: /etc/mailname: No such file or directory
pristine-tar: committed libnet-mqtt-simple-perl_1.21.orig.tar.gz.delta to branch pristine-tar
make: Entering directory '/root/Net-MQTT-Simple-1.21'
dh clean
dh_testdir
dh_auto_clean
dh_clean
make: Leaving directory '/root/Net-MQTT-Simple-1.21'
make: Entering directory '/root/Net-MQTT-Simple-1.21'
dh build
dh_testdir
dh_auto_configure
Checking if your kit is complete...
Looks good
Generating a Unix-style Makefile
Writing Makefile for Net::MQTT::Simple
Writing MYMETA.yml and MYMETA.json
dh_auto_build
make[1]: Entering directory '/root/Net-MQTT-Simple-1.21'
cp lib/Net/MQTT/Simple/SSL.pm blib/lib/Net/MQTT/Simple/SSL.pm
cp lib/Net/MQTT/Simple.pm blib/lib/Net/MQTT/Simple.pm
cp bin/mqtt-simple blib/script/mqtt-simple
/usr/bin/perl -MExtUtils::MY -e 'MY->fixin(shift)' -- blib/script/mqtt-simple
Manifying blib/man1/mqtt-simple.1p
Manifying blib/man3/Net::MQTT::Simple.3pm
Manifying blib/man3/Net::MQTT::Simple::SSL.3pm
make[1]: Leaving directory '/root/Net-MQTT-Simple-1.21'
dh_auto_test
make[1]: Entering directory '/root/Net-MQTT-Simple-1.21'
PERL_DL_NONLAZY=1 /usr/bin/perl "-MExtUtils::Command::MM" "-MTest::Harness" "-e" "undef *Test::Harness::Switches; test_harness(0, 'blib/lib', 'blib/arch')" t/*.t
t/regex.t .. ok
t/use.t .... ok
All tests successful.
Files=2, Tests=561, 3 wallclock secs ( 0.95 usr 0.10 sys + 2.30 cusr 0.12 csys = 3.47 CPU)
Result: PASS
make[1]: Leaving directory '/root/Net-MQTT-Simple-1.21'
make: Leaving directory '/root/Net-MQTT-Simple-1.21'
make: Entering directory '/root/Net-MQTT-Simple-1.21'
dh binary
dh_testroot
dh_prep
dh_auto_install
make[1]: Entering directory '/root/Net-MQTT-Simple-1.21'
Installing /root/Net-MQTT-Simple-1.21/debian/libnet-mqtt-simple-perl/usr/share/perl5/Net/MQTT/Simple.pm
Installing /root/Net-MQTT-Simple-1.21/debian/libnet-mqtt-simple-perl/usr/share/perl5/Net/MQTT/Simple/SSL.pm
Installing /root/Net-MQTT-Simple-1.21/debian/libnet-mqtt-simple-perl/usr/share/man/man1/mqtt-simple.1p
Installing /root/Net-MQTT-Simple-1.21/debian/libnet-mqtt-simple-perl/usr/share/man/man3/Net::MQTT::Simple::SSL.3pm
Installing /root/Net-MQTT-Simple-1.21/debian/libnet-mqtt-simple-perl/usr/share/man/man3/Net::MQTT::Simple.3pm
Installing /root/Net-MQTT-Simple-1.21/debian/libnet-mqtt-simple-perl/usr/bin/mqtt-simple
make[1]: Leaving directory '/root/Net-MQTT-Simple-1.21'
dh_installdocs
dh_installchangelogs
dh_installman
dh_perl
dh_link
dh_compress
dh_fixperms
dh_installdeb
dh_gencontrol
dpkg-gencontrol: warning: File::FcntlLock not available; using flock which is not NFS-safe
dh_md5sums
dh_builddeb
dpkg-deb: building package `libnet-mqtt-simple-perl' in `../libnet-mqtt-simple-perl_1.21-1_all.deb'.
make: Leaving directory '/root/Net-MQTT-Simple-1.21'
--- Done
Reading package lists... Done
Building dependency tree
Reading state information... Done
Ok, that worked.
Note: The Debian 8.2 BeagleBone image is 2GiB but the microSD card is 4GiB. So there’s another 2GiB on the card available if we wanted to partition, format, and mount it.
Now install the module:
root@beaglebone:~# ls -al *.deb
-rw-r--r-- 1 root root 24968 Jan 11 01:09 libnet-mqtt-simple-perl_1.21-1_all.deb
root@beaglebone:~# dpkg -i libnet-mqtt-simple-perl_1.21-1_all.deb
Selecting previously unselected package libnet-mqtt-simple-perl.
(Reading database ... 37154 files and directories currently installed.)
Preparing to unpack libnet-mqtt-simple-perl_1.21-1_all.deb ...
Unpacking libnet-mqtt-simple-perl (1.21-1) ...
Setting up libnet-mqtt-simple-perl (1.21-1) ...
Processing triggers for man-db (2.7.0.2-5) ...
Let’s write a simple program to use this module:
root@beaglebone:~# apt-get install -y emacs-nox libjson-perl
[....]
I fooled around with this and came up with:
#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper qw( Dumper );
use Date::Parse qw( str2time );
use JSON qw( to_json from_json );
use Net::MQTT::Simple::SSL ();
local $| = 1;
## Provisioning
my $THING_NAME = 'sdr1';
my $MQTT_HOST = 'A1PE707UQJBVJT.iot.us-east-1.amazonaws.com';
my $MQTT_CRYPTO = { SSL_ca_file => 'keys/root-CA.crt',
SSL_cert_file => 'keys/81921dd06a-certificate.pem.crt',
SSL_key_file => 'keys/81921dd06a-private.pem.key', };
## MQTT object
my $mqtt = Net::MQTT::Simple::SSL->new( $MQTT_HOST, $MQTT_CRYPTO );
## Topic Subscriptions
sub dump_topic
{
# print Dumper( \@_ );
my ($topic, $msg) = @_;
print "[$topic] $msg\n";
my $rec = from_json( $msg );
print Dumper( $rec );
}
my $topic_prefix = "\$aws/things/$THING_NAME/shadow";
my @topics = (
'/get/accepted',
'/get/rejected',
'/update/accepted',
'/update/rejected',
'/update/delta',
# '/delete/accepted',
# '/delete/rejected',
);
foreach my $topic ( @topics )
{
print "Subscribing to $topic_prefix$topic...";
$mqtt->subscribe( $topic_prefix . $topic, \&dump_topic );
print "ok\n";
}
## Call the MQTT tick() function until it returns true so that we have
## established our connection before proceeding.
print "Connecting to MQTT server...";
while ( !$mqtt->tick(1) )
{
print '.';
sleep 1;
}
print "ok\n";
## Sleep for 5 seconds to work around a bug in the AWS MQTT server
print "Letting AWS warm up...";
for ( 1..5 )
{
print '.';
sleep 1;
}
print "ok\n";
## Get the current shadow state
print "Fetching current shadow state...";
$mqtt->publish( $topic_prefix . "/get", '{}' );
print "ok\n";
## Main Loop
print "Entering main loop\n";
$mqtt->run();
exit 0;
Running it, we can see the following output:
root@beaglebone:~# perl iot_hello.pl
Subscribing to $aws/things/sdr1/shadow/get/accepted...ok
Subscribing to $aws/things/sdr1/shadow/get/rejected...ok
Subscribing to $aws/things/sdr1/shadow/update/accepted...ok
Subscribing to $aws/things/sdr1/shadow/update/rejected...ok
Subscribing to $aws/things/sdr1/shadow/update/delta...ok
Connecting to MQTT server...........................................................ok
Letting AWS warm up........ok
Fetching current shadow state...ok
Entering main loop
[$aws/things/sdr1/shadow/get/accepted] {"state":{"reported":{"hello":"world"}},"metadata":{"reported":{"hello":{"timestamp":1452473237}}},"version":1,"timestamp":1452479767}
$VAR1 = {
'metadata' => {
'reported' => {
'hello' => {
'timestamp' => 1452473237
}
}
},
'version' => 1,
'state' => {
'reported' => {
'hello' => 'world'
}
},
'timestamp' => 1452479767
};
And there you go, we’ve retrieved the Thing shadow state by registering callback functions to the subscription topics and publishing an empty JSON document to the /get topic. Our callback received the shadow state JSON document and coerced it into a Perl hash reference, which I dumped to stdout.
So if we leave this running, what happens now if we update the desired state in the AWS IoT console?
I used the
Update shadow
editor to add a "hello" : "cloud" entry to the "reported" and saved. Returning to the
Detail
, we can see the
Shadow status
is
Out of sync
and the
Shadow state
has our entry along with a new
delta
hash.
Over in our BeagleBone application, the callbacks fired for the /update/accepted and /update/delta topics:
[$aws/things/sdr1/shadow/update/accepted] {"state":{"reported":{"hello":"world"},"desired":{"hello":"cloud"}},"metadata":{"reported":{"hello":{"timestamp":1452480233}},"desired":{"hello":{"timestamp":1452480233}}},"version":2,"timestamp":1452480233}
$VAR1 = {
'state' => {
'reported' => {
'hello' => 'world'
},
'desired' => {
'hello' => 'cloud'
}
},
'timestamp' => 1452480233,
'metadata' => {
'reported' => {
'hello' => {
'timestamp' => 1452480233
}
},
'desired' => {
'hello' => {
'timestamp' => 1452480233
}
}
},
'version' => 2
};
[$aws/things/sdr1/shadow/update/delta] {"version":2,"timestamp":1452480233,"state":{"hello":"cloud"},"metadata":{"hello":{"timestamp":1452480233}}}
$VAR1 = {
'version' => 2,
'metadata' => {
'hello' => {
'timestamp' => 1452480233
}
},
'state' => {
'hello' => 'cloud'
},
'timestamp' => 1452480233
};
By now, you should be beginning to see how this will work. If we simply create different logic for the various topic callbacks, we can adjust to the desired state and communicate that feedback to AWS IoT to complete the loop.
But how do we get the shadow state back "in sync"?
We must consume the keypairs in the "delta" messages, update the "reported" state, and set the values of each key in "delta" to null after the Thing has assumed it. Let’s add that logic.
#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper qw( Dumper );
use Date::Parse qw( str2time );
use JSON qw( to_json from_json );
use Net::MQTT::Simple::SSL ();
local $| = 1;
## Provisioning
my $THING_NAME = 'sdr1';
my $MQTT_HOST = 'A1PE707UQJBVJT.iot.us-east-1.amazonaws.com';
my $MQTT_CRYPTO = { SSL_ca_file => 'keys/root-CA.crt',
SSL_cert_file => 'keys/81921dd06a-certificate.pem.crt',
SSL_key_file => 'keys/81921dd06a-private.pem.key', };
## MQTT object
my $mqtt = Net::MQTT::Simple::SSL->new( $MQTT_HOST, $MQTT_CRYPTO );
my $shadow = {};
## Topic Subscriptions
my $topic_prefix = "\$aws/things/$THING_NAME/shadow";
sub dump_msg
{
# print Dumper( \@_ );
my ($topic, $msg) = @_;
print "[$topic] $msg\n";
my $rec = from_json( $msg );
print Dumper( $rec );
}
sub update_state
{
my ($topic, $msg) = @_;
print "[$topic] $msg\n";
my $rec = from_json( $msg );
print Dumper( $rec );
for ( qw( version timestamp metadata ) ) {
delete $rec->{$_};
}
if ( exists $rec->{state}{delta} )
{
foreach my $key ( keys %{$rec->{state}{delta}} )
{
$rec->{state}{reported}{$key} = $rec->{state}{delta}{$key};
$rec->{state}{desired}{$key} = undef;
}
delete $rec->{state}{delta};
}
foreach my $key ( keys %{$rec->{state}{desired}} )
{
if ( exists $shadow->{state}{desired} &&
exists $shadow->{state}{desired}{$key} &&
!defined $shadow->{state}{desired}{$key} )
{
delete $rec->{state}{desired}{$key};
}
}
$shadow = $rec;
$mqtt->publish( $topic_prefix . "/update", to_json( $shadow ) );
}
$mqtt->subscribe( $topic_prefix . '/get/accepted', \&update_state );
$mqtt->subscribe( $topic_prefix . '/get/rejected', \&dump_msg );
$mqtt->subscribe( $topic_prefix . '/update/delta', \&update_state );
$mqtt->subscribe( $topic_prefix . '/update/accepted', \&dump_msg );
$mqtt->subscribe( $topic_prefix . '/update/rejected', \&dump_msg );
## Call the MQTT tick() function until it returns true so that we have
## established our connection before proceeding.
print "Connecting to MQTT server...";
while ( !$mqtt->tick(1) )
{
print '.';
sleep 1;
}
print "ok\n";
## Sleep for 5 seconds to work around a bug (is it a feature?)
print "Warming up...";
for ( 1..5 )
{
print '.';
sleep 1;
}
print "ok\n";
## Get the current shadow state
print "Fetching current shadow state...";
$mqtt->publish( $topic_prefix . "/get", '{}' );
print "ok\n";
## Main Loop
print "Entering main loop\n";
$mqtt->run();
exit 0;
Ok, now running it:
root@beaglebone:~# perl -c iot_hello.pl
iot_hello.pl syntax OK
root@beaglebone:~# perl iot_hello.pl
Connecting to MQTT server......................ok
Warming up........ok
Fetching current shadow state...ok
Entering main loop
[$aws/things/sdr1/shadow/get/accepted] {"state":{"desired":{"hello":"cloud"},"reported":{"hello":"world"},"delta":{"hello":"cloud"}},"metadata":{"desired":{"hello":{"timestamp":1452480233}},"reported":{"hello":{"timestamp":1452480233}}},"version":2,"timestamp":1452482261}
$VAR1 = {
'timestamp' => 1452482261,
'metadata' => {
'reported' => {
'hello' => {
'timestamp' => 1452480233
}
},
'desired' => {
'hello' => {
'timestamp' => 1452480233
}
}
},
'state' => {
'desired' => {
'hello' => 'cloud'
},
'delta' => {
'hello' => 'cloud'
},
'reported' => {
'hello' => 'world'
}
},
'version' => 2
};
[$aws/things/sdr1/shadow/update/accepted] {"state":{"desired":{"hello":null},"reported":{"hello":"cloud"}},"metadata":{"desired":{"hello":{"timestamp":1452482261}},"reported":{"hello":{"timestamp":1452482261}}},"version":3,"timestamp":1452482261}
$VAR1 = {
'version' => 3,
'state' => {
'reported' => {
'hello' => 'cloud'
},
'desired' => {
'hello' => undef
}
},
'metadata' => {
'reported' => {
'hello' => {
'timestamp' => 1452482261
}
},
'desired' => {
'hello' => {
'timestamp' => 1452482261
}
}
},
'timestamp' => 1452482261
};
What just happened there? Well, we fetched the state on startup, invoked the new callback which updated our shadow state record and published an update, merging the delta into the reported and deleting the desired state. This program could use some more verbose output. But if we look at the AWS IoT console now, we can see that the Thing Shadow state is "in sync" and the "desired" and "delta" hashes have disappeared.
Integrating rtl_power_fftw
Let’s keep this simple for now. We need to be able to review and change the following options of rtl_power_fftw through the shadow state:
- freq = center frequency, in decimal hertz, with optional suffix, or range separated by colon
- rate = sample rate/second
- time = integration time, in seconds
- bins = FFT bins, must be an even number
- enabled = on/off switch, integer value 0 or 1
The script should subscribe to the IoT topics to get its configuration, then enter an endless loop executing the rtl_power_fftw program, capturing the output, parsing it, checking for messages, and looping. The rtl_power_fftw execution can be enabled or disabled by the configuration state.
The shadow state reported hash gains a config key whose value is a hash with our configuration options. To change this, updated the shadow state desired hash and the script will need to merge that delta hash.
#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper qw( Dumper );
use Date::Parse qw( str2time );
use JSON qw( to_json from_json );
use Net::MQTT::Simple::SSL ();
local $| = 1;
## Provisioning
my $THING_NAME = 'sdr1';
my $MQTT_HOST = 'A1PE707UQJBVJT.iot.us-east-1.amazonaws.com';
my $MQTT_CRYPTO = { SSL_ca_file => '/root/keys/root-CA.crt',
SSL_cert_file => '/root/keys/81921dd06a-certificate.pem.crt',
SSL_key_file => '/root/keys/81921dd06a-private.pem.key', };
my $RTL_POWER_FFTW = '/usr/local/bin/rtl_power_fftw';
## MQTT object
my $mqtt = Net::MQTT::Simple::SSL->new( $MQTT_HOST, $MQTT_CRYPTO );
my $shadow = {};
my $initialized = 0;
## Topic Subscriptions
my $topic_prefix = "\$aws/things/$THING_NAME/shadow";
sub dump_msg
{
# print Dumper( \@_ );
my ($topic, $msg) = @_;
print "Message Topic: $topic\n";
my $rec = from_json( $msg );
print Dumper( $rec ) if $topic =~ /rejected/;
if ( !$initialized )
{
if ( $topic eq "$topic_prefix/update/accepted" )
{
$initialized = 1;
}
else
{
print "Topic $topic != $topic_prefix/update/accepted\n";
sleep 1;
}
}
}
sub update_state
{
my ($topic, $msg) = @_;
#print "[$topic] $msg\n";
print "Message Topic: $topic\n";
my $rec = from_json( $msg );
for ( qw( version timestamp metadata ) ) {
delete $rec->{$_};
}
print Dumper( $rec );
## Is there a delta?
if ( $topic =~ /delta/ )
{
merge_delta( $rec );
## And delete the delta hash
delete $rec->{state};
}
else
{
foreach my $key ( keys %{$rec->{state}{desired}} )
{
## Set value to undef to delete it from the shadow state in AWS IoT
if ( defined $rec->{state}{desired}{$key} )
{
$rec->{state}{desired}{$key} = undef;
}
## it's already undef, so delete it
else
{
delete $rec->{state}{desired}{$key};
}
}
## Delete the config hashref if it's empty
if ( exists $rec->{state}{desired}{config} &&
!defined $rec->{state}{desired}{config} )
{
delete $rec->{state}{desired}{config};
}
if ( keys %{$rec->{state}{desired}} == 0 )
{
delete $rec->{state}{desired};
}
## Merge the delta if it exists
if ( exists $rec->{state}{delta} )
{
merge_delta( { state => $rec->{state}{delta} } );
delete $rec->{state}{delta};
}
$shadow = $rec
}
## update our shadow state global
# print Dumper( $shadow );
## and publish it back to AWS IoT
publish_state( $shadow );
}
sub publish_state
{
my $hashref = shift;
delete $hashref->{metadata} if exists $hashref->{metadata};
# print "UPDATE\n", Dumper( $hashref );
$mqtt->publish( $topic_prefix . "/update", to_json( $hashref ) );
}
sub merge_delta
{
my $rec = shift;
print "DELTA:\n", Dumper( $rec->{state} );
foreach my $key ( keys %{$rec->{state}} )
{
if ( $key eq 'config' && ref( $rec->{state}{$key} ) eq 'HASH' )
{
foreach my $subkey ( keys %{$rec->{state}{$key}} )
{
print "Setting $key.$subkey = ", $rec->{state}{$key}{$subkey}, "\n";
$shadow->{state}{reported}{$key}{$subkey} = $rec->{state}{$key}{$subkey};
}
}
else
{
if ( defined $rec->{state}{$key} )
{
$shadow->{state}{reported}{$key} = $rec->{state}{$key};
}
else
{
delete $shadow->{state}{reported}{$key};
}
}
}
}
## Subscribe to topics of interest
$mqtt->subscribe( $topic_prefix . '/get/accepted', \&update_state );
$mqtt->subscribe( $topic_prefix . '/get/rejected', \&dump_msg );
$mqtt->subscribe( $topic_prefix . '/update/delta', \&update_state );
$mqtt->subscribe( $topic_prefix . '/update/accepted', \&dump_msg );
$mqtt->subscribe( $topic_prefix . '/update/rejected', \&dump_msg );
## Call the MQTT tick() function until it returns true so that we have
## established our connection before proceeding.
print "Connecting to MQTT server...";
while ( !$mqtt->tick(1) )
{
print '.';
sleep 1;
}
print "done.\n";
## Sleep for 5 seconds to work around a bug (is it a feature?)
print "Warming up...";
for ( 1..5 )
{
print '.';
sleep 1;
}
print "done.\n";
## Get the current shadow state
print "Fetching current shadow state...";
$mqtt->publish( $topic_prefix . "/get", '{}' );
print "done.\n";
## Wait for the /get/accepted response
print "Waiting for shadow state and update...";
while ( keys %$shadow == 0 )
{
$mqtt->tick();
print '.';
sleep 1;
}
print "done.\n";
print "Waiting for initialization to complete...";
while ( !$initialized )
{
$mqtt->tick();
print '.';
sleep 1;
}
print "done.\n";
## Set some reasonable defaults if no config is present (newly provisioned?)
if ( !exists $shadow->{state}{reported}{config} )
{
print "Setting initial configuration\n";
$shadow->{state}{reported}{config} = { freq => '145.4M',
rate => '2800000',
time => '30',
bins => '112',
enabled => 0 };
$shadow->{state}{reported}{powers} = {};
publish_state( $shadow );
}
## Main Loop
print "Entering main loop\n";
#$mqtt->run();
my $failures;
my $failure_threshold = 5;
while ( 1 )
{
$mqtt->tick(2);
## Run rtl_power_fftw using our config options, if set
if ( !exists $shadow->{state} ||
! exists $shadow->{state}{reported} ||
! exists $shadow->{state}{reported}{config} ||
$shadow->{state}{reported}{config}{enabled} != 1 )
{
print "Disabled by configuration.\n";
print Dumper( $shadow->{state} );
sleep 1;
next;
}
my $cfg = $shadow->{state}{reported}{config};
my $cmd = sprintf( "%s -f %s -r %d -T -t %d -b %d",
$RTL_POWER_FFTW,
$cfg->{freq},
$cfg->{rate},
$cfg->{time},
$cfg->{bins} );
## Execute the program but realize this is going to block
## everything, even the callbacks.
## TODO: Execute this asynchronously. Something like POE would work well here.
print "Running cmdline: $cmd\n";
my @lines;
my $rc;
my $timeout = $cfg->{time} * 6;
eval {
local $SIG{ALRM} = sub { die "alarm\n" }; # NB: \n required
alarm $timeout;
chomp( @lines = `$cmd 2>&1` );
$rc = $?;
alarm 0;
};
if ($@) {
die unless $@ eq "alarm\n"; # propagate unexpected errors
## timed out
print "ERROR: rtl_power_fftw did not return a response in $timeout. Likely USB Bus error.\n";
## Try to kill it
`pkill rtl_power_fftw`;
if ( $failures++ >= $failure_threshold )
{
print "Consecutive failures: $failures\n";
print "Rebooting!\n";
`/sbin/reboot`;
exit(1);
}
sleep 2;
next;
}
if ( $rc != 0 )
{
print "WARNING: rtl_power_fftw command execution failed. Taint input variables?\n";
print "CMDLINE: $cmd\n";
if ( $failures++ >= $failure_threshold )
{
print "Consecutive failures: $failures\n";
print "Rebooting!\n";
`/sbin/reboot`;
exit(1);
}
sleep 30;
next;
}
## A success resets the failure count
$failures = 0;
my $times = {};
my $powers = {};
my @p = ();
foreach my $line ( @lines )
{
chomp( $line );
if ( substr( $line, 0, 1 ) eq '#' )
{
if ( $line =~ /^# Acquisition (start|end): (.+)$/ )
{
$times->{$1} = str2time( $2 );
}
}
elsif ( $line =~ /^([\d.e+]+) ([\-\d.]+)$/ )
{
my $freq = $1 + 0;
my $power = $2 + 0;
$powers->{$freq} = $power;
push @p, $power;
}
else
{
print "DEBUG: $line\n";
}
}
## Check for messages
# $mqtt->tick(2);
## Update the reported state and publish
if ( index( $cfg->{freq}, ":" ) == -1 )
{
$shadow->{state}{reported}{low} = freq_to_num( $cfg->{freq} ) - ( $cfg->{rate} / 2 );
$shadow->{state}{reported}{high} = $shadow->{state}{reported}{low} + $cfg->{rate};
}
else
{
( $shadow->{state}{reported}{low},
$shadow->{state}{reported}{high} ) = map { freq_to_num($_) } split( /:/, $cfg->{freq} );
my $rate_diff = ( $shadow->{state}{reported}{high} - $shadow->{state}{reported}{low} ) % $cfg->{rate};
if ( $rate_diff != 0 )
{
$shadow->{state}{reported}{high} += ( $cfg->{rate} - $rate_diff );
}
}
#$shadow->{state}{reported}{powers} = $powers;
$shadow->{state}{reported}{powers} = \@p;
$shadow->{state}{reported}{times} = $times;
publish_state( $shadow );
## Check for messages
$mqtt->tick(2);
}
exit 0;
sub freq_to_num
{
my $freq = shift;
if ( $freq =~ /^([\-\d.]+)([kM])$/i )
{
my $num = $1;
$num *= 1_000 if lc($2) eq 'k';
$num *= 1_000_000 if lc($2) eq 'm';
return $num;
}
if ( $freq < 1_000_000 )
{
$freq *= 1_000_000;
return $freq;
}
return $freq;
}
<br/>
Problems with USB
The rtlsdr device locked up periodically so I wrapped it in an alarm and reboot the beaglebone if it happens several times in a row.
Create an AWS IoT Rule
In the AWS IoT console, click the Thing to bring up the detail pane, then click the Create a rule button. You will see the following wizard:
When you select the action
Send the message to a real-time data stream that stores to a bucket (Kinesis Firehose), a Stream name
select box appears. Click the
Create a new resource link
, complete the wizard, add the action, and click the
Create button
.
Kinesis Firehose will ask us whether we want to store the data in Amazon S3 or Amazon RedShift. We will store it in S3 for now and call this Kinesis stream "sdr". We will store it in a S3 bucket named sdr-iot without a S3 prefix. Click
Next
.
Now we must define some properties defining how Kinesis Firehose will deliver our data to the S3 bucket. I have set the Buffer size to the minimum of 1MB and the Buffer interval to the minimum of 60 seconds. I enabled Gzip compression without any encryption.
In the IAM Role dropdown, select Create/Update existing IAM Role->Firehose delivery IAM role. The following wizard will appear and you can accept the defaults, then click the Allow button.
Click Next when returned to the Configuration wizard, then finally click the
Create Delivery Stream
button.
Returning to the
Create a rule
wizard, the Kinesis Firehose delivery stream will be populated. A role drop down will appear. click the Create a new role link to quickly create one, then click the Add Action. Finally, the
Create
button will become active so click it.
Wait for files in S3
Running the iot_hello.pl program on the BeagleBone Black and monitoring the AWS IoT dashboard for our thing, we see the state update every 15 seconds or so. Looking at the S3 console in our Kinesis Firehose target bucket, gzipped files will begin appearing.
We’re now logging everything to an S3 bucket hashed on date elements. Multiple things can be registered with AWS IoT and the shadow state changes can be collectively fed to this Kinesis Firehouse.
If you look inside one of these files, you’ll find JSON record after JSON record, all mashed together. There are no newlines separating the records so you have to split on "}{" to iterate over the records in a parser.
Now that we have some data, let’s try to visualize it. At first, I wrote a program using the perl Imager module but the results were marginal and I could see it was going to take some time to improve that.
If I could just transform the JSON into a known CSV format, I could use the existing heatmap.py to generate charts.
I wrote a script to convert the JSON records dumped by Kinesis Firehose into the CSV format produced by rtl_power so I could use heatmap.py to visualize it.
If you are trying to process this data and refresh something with a short period, using a 60 second split on the files
with Firehose
makes sense but it’s a tradeoff. Over time, you’ll eventually have to deal with a LOT of files.
After some reflection, I realized I could trigger a lambda function on shadow state updates to transform and append the latest data points to a file.
from __future__ import print_function
<br/>
import json
import datetime
import boto3
<br/>
# print('Loading function')
<br/>
def lambda_handler(event, context):
#print("Received event: " + json.dumps(event, indent=2))
# for i in event['state']['reported']['powers']:
## heatmap.py croaks if we have different numbers of data points
if len(event['state']['reported']['powers']) != event['state']['reported']['config']['bins'] * 2:
print( "The number of data points does not match the config: ", len(event['state']['reported']['powers']), " != ", event['state']['reported']['config']['bins'] * 2 )
print( json.dumps(event, indent=2))
return
## Build up our rtl_power compatible output in CSV format
rec = []
rec.append(datetime.datetime.fromtimestamp(event['state']['reported']['times']['start']).strftime('%Y-%m-%d'))
rec.append(datetime.datetime.fromtimestamp(event['state']['reported']['times']['start']).strftime('%H:%M:%S'))
rec.append(str(event['state']['reported']['low']))
rec.append(str(event['state']['reported']['high']))
rec.append(str((int(event['state']['reported']['config']['rate']) / event['state']['reported']['config']['bins'])))
rec.append("5000")
rec.append(", ".join(map(str, event['state']['reported']['powers'])))
csvstr=", ".join(rec)
## If we're testing, don't post this to Kinesis Firehose. Just dump to stdout.
if 'test' in event['state']['reported']['config']:
if event['state']['reported']['config']['test'] == 1:
print( "Testing Lambda Function: ", csvstr)
return
## Put the record into Kinesis Firehose
client = boto3.client('firehose')
response = client.put_record(
DeliveryStreamName='sdr-rtl-power-json2csv',
Record={
'Data': csvstr + "\n"
}
)
return
#return event['key1'] # Echo back the first key value
#raise Exception('Something went wrong')
Merging multiple things into a single visualization
Retrospective
So we have learned how to load a common Linux distribution on the BeagleBone Black and Intel Edison, install the mosquitto tools for command-line interface to the MQTT endpoint provided by AWS IoT. We also wrote a Perl program that can interface with MQTT over TLS and experimented with using the Shadow State to pass a large number of synthetic sensor values (FFT bins) as well as remotely control the Thing.
A lesson learned is that there is a 8KiB limit to the Thing Shadow State document so it is not a good channel for large amounts of data. JSON exacerbates this as it is a space inefficient format. However, this is a secure channel so we can use this to pass temporary credentials so the Thing can send larger data streams to Amazon S3, Firehose directly, Kinesis proper, or any other service that makes sense.
I have reached the limits of what can be documented in the Hackster.io editor but I have created a new project named CloudSDR that will cover development of a public service leveraging what we have learned here to allow anybody with an RTL-SDR device to connect it as a Thing and leverage the swarm of devices and cloud services from Amazon to do interesting things limited only by the imagination.
I may have omitted some details and I could write a book on this topic. If you have any questions, please leave a comment and I’ll try to answer them.
Comments are not currently available for this post.