Tracking Neighborhood Power Usage From The Sky

Photo of tehCoops

Made by tehCoops / Drones / IoT

About the project

Ever wonder how the power company reads your meter without walking door-to-door? What if I told you that you can read your power meter, and your neighbors', to see how you compare?

Giant Board SBC Contest WinnerGiant Board SBC contest winner

Project info

Difficulty: Moderate

Platforms: Linux

Estimated time: 6 hours

License: GNU General Public License, version 3 or later (GPL3+)

Items used in this project

Hardware components

Giant Board Giant Board x 1
USB Software Defined Radio (SDR) USB Software Defined Radio (SDR) https://www.banggood.com/USB2_0-FM-DAB-DVB-T-RTL2832U-R820T2-RTL-SDR-SDR-Dongle-Stick-Digital-TV-Tuner-Receiver-with-Antenna-p-1353904.html x 1
Tiny OTG Adapter - USB Micro to USB Tiny OTG Adapter - USB Micro to USB amazon.com/gp/product/B00CXAC1ZW x 1
USB Li-Ion Power Bank with 2 x 5V Outputs a. 2.1A - 5000mAh USB Li-Ion Power Bank with 2 x 5V Outputs a. 2.1A - 5000mAh x 1
Micro SD Card Micro SD Card x 1

Software apps and online services

rtl-sdr rtl-sdr https://osmocom.org/projects/rtl-sdr/wiki/Rtl-sdr
rtlamr rtlamr https://github.com/bemasher/rtlamr/

Story

Backstory

A couple years ago I fell hard into the world of RF communications. It's easy to forget the invisible waves all around us that make the world go 'round, from cell networks to local wifi to FM radio to long-range IoT. After getting a amateur radio license (typically known as a HAM license) I purchased a USB SDR (software defined radio) and started to scrub the virtual dial up and down the spectrum to see what I could "hear" in those invisible waves. I heard FM radio, downloaded images from the space station, eavesdropped on chatter from around the world, and set up a node to receive airplane location information as part of the FlightAware network.

This got me to wondering what else I could listen to. After coming across a blog post about how to listen to gas, water, and power meters I set up a powerful linux server to log usage of power meters all around me to see how I compared to my neighbors and how accurate the monthly shaming letter from the power company was. You know the ones. They say, "you've used X% more electric than your neighbors this month. Try to use less." (I've tried and it doesn't work.) You know how many meters my whiz-bang new set up could hear? One. Uno. A single meter -- mine. :(

Not to be discouraged I started planning how to gather more data. On my daily commute to work I noticed antennas on the highest power poles in each community. The antennas connected to a box on the side of the pole and plugged into a standard 120V outlet. Peculiar, I thought. There's no power meter. That must mean they're owned by the power company and I bet that box has a cell modem in it. With my HAM radio knowledge I knew that the higher your receiving antenna is, the more you can hear, and that's why repeaters are on towers on the tops of hills. So that's how they're reading the meters in the area -- an antenna that's high enough to hear them in the area with a cell modem for the backhaul to HQ.

How could I replicate their configuration in a valley without access to a tower? How about a temporary tower? A drone is able to get great pictures of visible light (also RF) from the sky, why couldn't it also get great pictures of invisible light from the sky as well? I couldn't send a normal computer up on a drone but I could send a tiny computer. I could send the smallest computer I own, a GIANT Board! Yeah, I have Pi Zeros but they're bigger, use more power, and don't have the gorgeous lipo connector.

The Build

My first issue was how to swap between USB OTG mode so that I could edit files for testing with SSH but also be able to switch to USB host mode for when the SDR was connected to the GIANT Board. Since /boot is FAT16 and mounts when connected to another computer (or when the SD card is put into a reader) I added a script to /boot/uboot/backdoor.sh that is run every time the board boots. This way I don't lock myself out of the board without a way to get back in. The script checks to see if the gadget service is enabled and if it's not it will set it to enabled then run the logging script (~/log.sh). It will also power off the board once the logging script is done executing.

/boot/uboot/backdoor.sh:

  1. #!/usr/bin/env bash
  2.  
  3.  
  4. if ! systemctl is-enabled usbgadget-serial-eth-ms
  5. then
  6. sudo systemctl enable usbgadget-serial-eth-ms
  7. ~/log.sh
  8. echo "Powering off" > ~/log/status.log
  9. sync
  10. sudo systemctl poweroff
  11. fi
  12.  

This script is called via the debian user's cron:

  1. # Edit this file to introduce tasks to be run by cron.
  2. #
  3. # Each task to run has to be defined through a single line
  4. # indicating with different fields when the task will be run
  5. # and what command to run for the task
  6. #
  7. # To define the time you can provide concrete values for
  8. # minute (m), hour (h), day of month (dom), month (mon),
  9. # and day of week (dow) or use '*' in these fields (for 'any').#
  10. # Notice that tasks will be started based on the cron's system
  11. # daemon's notion of time and timezones.
  12. #
  13. # Output of the crontab jobs (including errors) is sent through
  14. # email to the user the crontab file belongs to (unless redirected).
  15. #
  16. # For example, you can run a backup of all your user accounts
  17. # at 5 a.m every week with:
  18. # 0 5 * * 1 tar -zcf /var/backups/home.tgz /home/
  19. #
  20. # For more information see the manual pages of crontab(5) and cron(8)
  21. #
  22. # m h dom mon dow command
  23. @reboot sleep 60; /boot/uboot/backdoor.sh

For this to work you also need to have the debian user execute sudo without prompting for a password:

  1. $ sudo sudoedit -f /etc/sudoers.d/admin
  2.  
  3. Change contents to:
  4. Defaults env_keep += "NODE_PATH"
  5. %admin ALL=(ALL:ALL) NOPASSWD: ALL

Next install the software needed to decode the radio signal and decode those results to match the format the meter is sending. There are two parts to this: rtl_tcp (part of the rtl_sdr package) and rtlamr. The former decodes the radio signal and the latter turns that output into meter values.

On Debian there's a prerequisite of  libusb-1.0-0-dev for rtl_tcp and golang for rtlamr.

Full build instructions for rtl_sdr can be found at https://osmocom.org/projects/rtl-sdr/wiki/Rtl-sdr

Install instructions for rtlamr can be found at https://github.com/bemasher/rtlamr

If you aren't a linux person, installation of rtlamr is as easy as:

  1. $ mkdir ~/go
  2. $ export GOPATH=~/go/bin
  3. $ go get github.com/bemasher/rtlamr

Once all this is done, we need the ~/log.sh script and a logging directory to make it easier to troubleshoot since this all runs headless.

  1. $ cd ~
  2. $ mkdir log
  3. $ cat << EOF
  4. #!/usr/bin/env bash export LD_LIBRARY_PATH=/usr/local/lib/ sudo rtl_tcp >>log/rtl_tcp.out 2>>log/rtl_tcp.err & sleep 10 while [ 1 ] do pgrep rtl_tcp || sudo rtl_tcp >>log/rtl_tcp.out 2>>log/rtl_tcp.err & sudo ~/go/bin/rtlamr -format=csv -msgtype=all -symbollength=8 >>meters.csv 2>>log/rtlamr.err sleep 10 done
  5. EOF
  6. chmod +x log.sh

Let me break down this script a little.

I had issues with library paths so the first step is to set the LD_LIBRARY_PATH variable so the librtlsdr.so.0 library can be found by rtl_tcp.

Next I start rtl_tcp (the "server" that takes the RF signal over USB and sends it to a client) using sudo since the USB device file it's readable by the debian user. This is started in the background using the "&" at the end of the line. Following this is a pause to make sure it starts correctly.

Next is a while loop that runs forever. Sometimes rtl_tcp or rtlamr crash due to a lack of resources. This loop restarts them if they die. In fact, the "pgrep rtl_tcp" is what restarts rtl_tcp if it's not running.

After I know rtl_tcp is running, I start rtlamr (the "client" to rtl_tcp) to decode the meter values, again using sudo however it's probably not needed in this case.

The specific command is "rtlamr -format=csv -msgtype=all -symbollength=8"

The arguments do the following:

-format=csv -- Outputs the data in CSV format.

-msgtype=all --Listens for all formats. Different types of meters use different formats and I want to log them all.

-symbollength=8 -- Sets the data rate to the lowest possible. The GIANT Board is small and it also has limited resources so I need to have it do less work. Setting this higher causes the client to receive more data than it can process and therefore crash. This took about a day to figure out!

You'll notice that both rtl_tcp and rtlamr log both standard out and standard error as well so I can look back through the logs in case there are issues.

Execution

Once you're ready to collect data, disable the gadget service with "sudo systemctl disable usbgadget-serial-eth-ms", then power off "sudo systemctl poweroff", and once the lights go out plug in a USB OTG cable with a power input and the SDR to the GIANT Board and you're good to go!

Remember that each time the GIANT Board boots in USB host mode it will enable the usbgadget service so you can use SSH/console/mass storage again. To "pull the pin" on data collection mode run "sudo systemctl disable usbgadget-serial-eth-ms" from an SSH or console session.

Results

After running in data collection mode, with or without it gaff taped to your drone, you can review the logged data by looking at meters.csv:

2020-07-06T12:56:59.379162225Z,0,0,4,0x0,0x2,53089,0xbad1

2020-07-06T12:57:06.749211493Z,0,0,4,0x0,0x2,53089,0xbad1

2020-07-06T12:57:14.23219881Z,0,0,4,0x0,0x2,53089,0xbad1

2020-07-06T12:57:34.051995201Z,0,0,4,0x0,0x2,20446,0xff46

2020-07-06T12:57:45.84081403Z,0,0,4,0x0,0x2,20446,0xff46

2020-07-06T12:57:57.570473933Z,0,0,4,0x0,0x2,20446,0xff46

2020-07-06T12:58:16.025402421Z,0,0,4,0x0,0x2,20446,0xff46

2020-07-06T13:03:03.183740962Z,0,0,4,0x0,0x3,31166,0x26b7

2020-07-06T13:03:03.921314913Z,0,0,4,0x0,0x3,43079,0x5c39

2020-07-06T13:10:46.03005609Z,0,0,4,0x0,0x2,44198,0xa15a

2020-07-06T13:29:53.37876801Z,0,0,8,0x0,0x1,44151,0x84e4

2020-07-06T13:31:47.054006158Z,0,0,4,0x0,0x2,34213,0xf49d

2020-07-06T13:32:28.614693671Z,0,0,4,0x0,0x1,7143,0xa591

2020-07-06T13:32:45.843889964Z,0,0,4,0x0,0x2,74282,0x1def

2020-07-06T13:33:29.27331816Z,0,0,163,6,0,14000,0,0

2020-07-06T13:34:31.265539039Z,0,0,4,0x0,0x1,79599,0x2593

2020-07-06T13:34:44.239376209Z,0,0,4,0x0,0x1,79599,0x2593

2020-07-06T13:35:38.337546357Z,0,0,5,0x0,0x1,1736805,0x59c3

2020-07-06T13:35:39.184454162Z,0,0,5,0x0,0x1,1736805,0x59c3

2020-07-06T13:36:13.539333772Z,0,0,4,0x0,0x0,710,0xc306

2020-07-06T13:36:16.936279235Z,0,0,4,0x0,0x0,710,0xc306

2020-07-06T13:36:21.587471138Z,0,0,4,0x0,0x0,710,0xc306

2020-07-06T13:36:32.725334748Z,0,0,4,0x0,0x2,29572,0x5714

2020-07-06T13:37:22.581992505Z,0,0,4,0x0,0x1,12474,0xa151

2020-07-06T13:37:31.760309871Z,0,0,4,0x0,0x1,12474,0xa151

2020-07-06T13:37:40.370119529Z,0,0,4,0x0,0x1,95623,0xfbe

2020-07-06T13:37:40.903781968Z,0,0,4,0x0,0x3,14392,0x16a2

2020-07-06T13:37:41.443526456Z,0,0,4,0x0,0x1,26953,0x7907

2020-07-06T13:37:42.078560212Z,0,0,4,0x0,0x1,95623,0xfbe

2020-07-06T13:37:43.287948407Z,0,0,4,0x0,0x1,71937,0x3601

2020-07-06T13:37:43.518602163Z,0,0,4,0x0,0x1,95623,0xfbe

2020-07-06T13:37:44.856290163Z,0,0,4,0x0,0x1,26953,0x7907

2020-07-06T13:37:47.2169927Z,0,0,4,0x0,0x1,51303,0xf4d1

2020-07-06T13:38:02.696892798Z,0,0,4,0x0,0x3,19309,0xc7be

2020-07-06T13:38:08.0449167Z,0,0,4,0x0,0x2,2488,0x3911

2020-07-06T13:38:09.529019432Z,0,0,4,0x0,0x0,74264,0x8233

2020-07-06T13:38:10.289069774Z,0,0,4,0x0,0x2,32193,0x73f4

2020-07-06T13:38:12.733227822Z,0,0,4,0x0,0x3,50666,0xb395

2020-07-06T13:38:15.529263237Z,0,0,4,0x0,0x3,19309,0xc7be

2020-07-06T13:38:16.780056993Z,0,0,4,0x0,0x2,32193,0x73f4

2020-07-06T13:38:17.052826457Z,0,0,4,0x0,0x2,9946,0x8dfb

2020-07-06T13:38:18.018740408Z,0,0,4,0x0,0x1,64248,0xe906

2020-07-06T13:38:18.498968213Z,0,0,4,0x0,0x2,32193,0x73f4

2020-07-06T13:38:19.804041383Z,0,0,4,0x0,0x1,53786,0xe68

2020-07-06T13:38:19.91806314Z,0,0,4,0x0,0x2,32193,0x73f4

The format is:

timestamp, ?, ? meter ID (redacted in the above example), TamperPhysical, TamperEnclosure, Consumption (i.e. current reading), ChecksumValue

Next Steps

I didn't have a Lipo battery to use so I'm waiting for one to be delivered before taping it all to the drone and sending it sky-high!

I'd also like to design a case to attach it to the drone more easily.

Code

log.sh

This is where the heavy lifting happens

Backdoor script

Located at /boot/uboot/backdoor.sh. Also mounted as BOOT when using gadget mode.

Credits

Leave your feedback...