AWS Panoptes

An IoT Thing using the Amazon cloud that monitors and reports observed radio frequency spectral power and can be remotely controlled.

Categories: Intermediate

Two IOT Things - An Intel Edison and BeagleBone BlackBeagleBone Black ThingIntel Edison Thing

Objectives

  • Learn about the AWS IOT service by developing a Thing that performs some useful function.  Make it a real-world example.
  • Integrate other

    newer

    AWS 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.

None

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.

None

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.  

None

Price: $22.95 on Amazon

GPS Sensor

https://www.adafruit.com/images/970×728/746-08.jpg

Adafruit Ultimate GPS Breakout - 66 channel w/10 Hz updates - Version 3

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.

Select the AWS IoT Service

Now click the
Create Resource
button 

None

Then click the
Create a Thing
button.

None

Name your thing "sdr1", leave the attributes empty, and click the
Create
button.

None

You have now created a
Thing
in AWS IoT.  Click the
View thing
button.

None

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
.

None

http://docs.aws.amazon.com/iot/latest/developerguide/verify-pub-sub.html

None

None of these supported SDKs really match what we’re doing here.  Let’s select the
Embedded C
 SDK and see what happens

None

Ok, it wants to create a certificate and policy for us.  Click the
Generate certificate and policy
 button.

None

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.

None

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:

https://www.symantec.com/content/en/us/enterprise/verisign/roots/VeriSign-Class%203-Public-Primary-Certification-Authority-G5.pem

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:

None

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.

None

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?

None

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.

None

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:

None

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
.

None

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.  

None

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.

None

Click Next when returned to the Configuration wizard, then finally click the
Create Delivery Stream
 button.

None

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.

None

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.  

None

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')


Visualization of programmatically tuning the SDR Thing via AWS IoT and collecting data via Shadow State updatesNoneNone

Merging multiple things into a single visualization

None

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.