Duo Desire (a small IoT project)

My wife was watching Shark Tank a few weeks ago and we were both chatting about a thing we saw (which wasn’t funded). It was kind of a clever idea, and I’ll sidestep the entire discussion of whether or not it is a “good” product or not, I just wanted to see what it would take to build something kind of like it (this is loosely inspired by LoveSync, but the LoveSync does have some interesting features around time and also their hardware is much prettier than what I’m using).
The Rebutton (ignore the dirty valet box)
As a quick aside: this method of asynchronous communication in which parties are only notified after reaching consensus seems like an interesting UX pattern. I was very interested to see almost the same pattern pop up (in almost the exact opposite context of LoveSync) in the form of the Bail App, which I believe is still in development (if it’s even feasible).

Quick Overview

So, to the meat of the project: this project is 2 IoT buttons hooked up to an Azure IoT Hub which trigger Azure functions and make use of Azure Table Storage for data persistence. When consensus is reached, a notification is sent via Twilio to all interested parties. Why Azure you ask? Easy: the buttons that were in stock only had the Azure model available, so that made the decision pretty easy. Amazon’s infamous IoT button is apparently no longer for sale to individuals (or if it is it is so well hidden I gave up on looking). Developing this on Azure worked pretty well, although there were some weird issues when I attempted to interact with the IoT Hub service using C#, so I ended up doing everything in Javascript (I know, IoT in JS, I never thought I’d see the day).

Bill of Materials

Building It

To start, configure the Rebutton to communicate with your IoT Hub. The docs do a pretty good job of describing this. Ensure that the buttons are communicating with the hub. This command will let you see events as they come from the event hub: az iot hub monitor-events -n <yourapp> --properties anno sys --timeout 0 In the Azure Function Apps blade, you’ll need to create the following functions

Event Hub Function

The first of the three functions needed is the function which will process event hub messages. At a high level all this is doing is being invoked any time an event occurs and then just writing the event data to the Azure Table Storage table defined in the configuration. You’ll need to configure it something like:
The Azure IoT Event Hub function
The code for the function is here. As I alluded to above, I was having some weird issues with the C# bindings in Visual Studio, so I ended up just writing everything in Javascript directly in browser. Not the greatest development environment…

Timer Function

The timer is the next function. It runs on whatever cron schedule is defined (I currently have it running every 10 minutes), and it just looks for consensus in the table for both configured devices. Consensus in this case is defined as one or more single-click events from each device. If any non-single-click events are discovered, then it is assumed that consensus is impossible, and the messages will not go out. It would be an interesting exercise to extend the current logic and use the other button events (double-click, long-press, super-long-press) for other functions. Upon discovering consensus, the timer function writes to an Azure Queue and writes a “FINISH” record to the table storage to prevent the next run of the function from also triggering a message.
The timer configuration
The code for it is here.

Queue Trigger Function

The final function needed is the queue trigger which is responsible for actually sending the messages to Twilio. The code is incredibly simple as Azure has a pre-built integration with Twilio which abstracts away the vast majority of the code that would otherwise need to be written.
The Queue Trigger configuration
At this point, everything should be wired up, so pressing both buttons should trigger the Twilio message.

Worth It?

Assuming usage remains fairly constant, Azure forecasts that I’ll spend ~$0.30 per month, plus ~$50 for the buttons. I think it’s worth it for the fun I had building this, plus if the current application of the buttons gets stale, I’m sure I can repurpose them for something else. Interestingly, these buttons are built on the Arduino platform, so conceivably I could do a whole lot of customization by changing the firmware around (which might happen eventually).

In Closing

This was a fun project which didn’t take very long to put together and entertained me. It was a timely project for Valentine’s Day. If you end up building something like it or have any feedback on this article, please reach out, I’d love to hear!

Making EF work with DotNet SPA 2.0.0 prerelease

The Microsoft.DotNet.Web.Spa.ProjectTemplates::2.0.0-rc2-final SPA template currently has a little issue with making EF work properly. If you encounter this issue you’ll notice that running Update-Database in the Package Manager Console results in ng serve being run, which prevents the migrations from being applied.

To fix the issue, we need to slightly change Program.cs and Startup.cs as follows:

    public class Program
    {
        public static void Main(string[] args)
        {
            // This needs to have all of the code originally in BuildWebHost
            WebHost.CreateDefaultBuilder(args)
                .UseStartup<Startup>().Build().Run();
        }

        // This is only invoked by EF
        public static IWebHost BuildWebHost(string[] args) =>
            WebHost.CreateDefaultBuilder(args)
            .ConfigureAppConfiguration((ctx, cfg) =>
            {
                cfg.SetBasePath(Directory.GetCurrentDirectory())
                  .AddJsonFile("appSettings.json", true) // require the appsettings file!
                  .AddEnvironmentVariables();
            })
                .UseStartup<Startup>().UseSetting("DesignTime", "true").Build();
    }

In Startup.cs look for app.UseSpa inside of Configure:

            app.UseSpa(spa =>
            {
                // To learn more about options for serving an Angular SPA from ASP.NET Core,
                // see https://go.microsoft.com/fwlink/?linkid=864501

                spa.Options.SourcePath = "ClientApp";
                // Get the "DesignTime" configuration param set above
                bool.TryParse(Configuration["DesignTime"], out var designTime);
                // Only launch the server if we are in development mode AND we are not in design time (which is EF)
                if (env.IsDevelopment() && !designTime)
                {
                    spa.UseAngularCliServer(npmScript: "start");
                }
            });

With those changes Entity Framework starts working perfectly again. Hope this saves people a few hours.

PucciMon – Temperature and Humidity monitoring on a Raspberry Pi with 2-way SMS

Over the last weekend I decided it would be fun to brush off one of my old Raspberry Pis and play with some of the hardware that I bought for it a while back for a project I was going to do and subsequently abandoned. Overall what I wanted to accomplish was pretty simple: have a little C-based application which can read the AM2302 sensor which was attached to the Pi and use the Twilio REST interface via C in order to send messages.

I ended up going a little beyond my original goals since I discovered how easy Twilio makes it to respond to inbound SMS. I had a partially developed Flask application (here) which I could easily extend to have an additional method which Twilio could invoke.

Before going any further, the Python/Flask code is here and the C code for the Pi is here. I haven’t written a single line of C code in about 4 years, so I’m sure it’s stylistically not the greatest code out there, but it is functional for my purposes.

Wiring up the Pi

The first step in this project is to wire up the AM2302 sensor to the Raspberry Pi. Note for this project I’m using the Model B Pi 1 (I purchased it way back in 2013, and it’s been repurposed a few times now). The AM2302 can be directly connected to the GPIO pins on the Pi. For wiring the positive lead (the black wire on the sensor I have), should go to pin 1. The negative lead (the grey wire) goes to pin 6. The white one goes to pin 7 (GPIO4 on the Pi).

The AM2302 should look similar to this: AM2302 picture

After wiring up the RPi, it should look like this (ignore the case I have on mine): Wired Raspberry Pi

Now that everything is wired up, the hardware portion of this project should be done.

Twilio Account Setup

Before we go any further, you’ll need a Twilio account in order to proceed. The next step will require us to know the From number, Twilio Account SID and Auth Token. If you head over to Twilio, the onboarding experience is pretty painless.

Development Environment Setup

If you haven’t already booted up your Pi, do so now and make sure you have a good network connection. I’m assuming you’ve prepared the Pi with the default Raspbian Debian-derived Linux installation that most people use.

In order to make full use of everything, you’ll need to get two sets of code: the C based application which handles querying the AM2302 and a second Python/Flask based application which handles the Twilio webhooks.

  1. SSH to your Pi
  2. cd to wherever you want the code to live
  3. git clone https://github.com/lbearl/PucciMon.git to get the C code
  4. git clone https://github.com/lbearl/puccithe.dog.git to get the python code
  5. Set the following environment variables:
    a. export TWIL_WEATHER_NUMBER=<Your From Number in E.164 format>
    b. export TWIL_WEATHER_AUTH=<Your Twilio Auth key>
    c. export TWIL_WEATHER_SID=<Your Twilio SID>
    d. export TWIL_WEATHER_TONUMS=<Comma separated list of recipients in E.164 format>
  6. cd into PucciMon and run make (of course you have to have all of the build tools installed)
  7. sudo mkdir -p /opt/lbearl/ in order to make directory for the file.
  8. Execute sudo -E ./bin/c_sms -f and make sure that the AM2302 was read and that you receive an SMS.
    a. Test run output of application
  9. Execute cat /opt/lbearl/temp.txt and verify that it matches the output of the test run.
  10. In order to make things slightly easier to execute, without always requiring sudo, I’ve found that setting UID works well:
sudo mkdir -p /opt/lbearl/bin
sudo cp ./bin/c_sms /opt/lbearl/bin/c_sms
chown root /opt/lbearl/bin/c_sms
chmod u+s /opt/lbearl/bin/c_sms

At this point the sensor is working properly and can be read, the remainder of the setup is getting the flask application up and running. I’ve opted to do this using WSGI on Apache. The relevant configuration is as follows:

<VirtualHost *:443>
        ServerName puccithe.dog
        ServerAlias www.puccithe.dog

        ServerAdmin [email protected]

        WSGIDaemonProcess puccidog user=www-data group=www-data threads=5
        WSGIScriptAlias / /var/www/puccithe.dog/puccithedog.wsgi

        <Directory /var/www/puccithe.dog>
                WSGIProcessGroup puccidog
                WSGIApplicationGroup %{GLOBAL}
                Order deny,allow
        </Directory>
        ErrorLog ${APACHE_LOG_DIR}/error.log
        CustomLog ${APACHE_LOG_DIR}/access.log combined

        # I use CloudFlare's certificates so that I don't have to muck about with anything else
        SSLEngine on
        SSLCertificateFile /etc/ssl/certs/puccithedog-cf.pem
        SSLCertificateKeyFile /etc/ssl/private/puccithedog-cf.key
</VirtualHost>

The WSGI file is:

import sys
activate_this = '/home/pi/puccidog/puccidog/env/Scripts/activate_this.py'
execfile(activate_this, dict(__file__=activate_this))
sys.path.insert(0, '/home/pi/puccidog/puccidog')

import logging, sys
logging.basicConfig(stream=sys.stderr)

from puccidog import app as application

It’s been a couple of years since I set that up (the Flask application was actually originally used for a different side project, and I just repurposed it to process the webhooks). If you look around on the Flask website, they have some good documentation around how to make this work.

CloudFlare Dynamic DNS

As mentioned earlier, I’m using CloudFlare to handle all of the DNS and SSL around this. CloudFlare has a little known feature where they actually are able to use recent versions of ddclient in order to update your A records. I used to use Namecheap’s Dynamic DNS, and the CloudFlare one is just as simple to set up. The one big gotcha is that the version of ddclient that ships with Raspbian by default isn’t new enough to support CloudFlare. In order to fix it, we have to install a brand new version. Jens Segers wrote up a great summary on how to do it. The short version is that we need to download ddclient from SourceForge and then overwrite our local copy. The configuration file layout changed, so we need to create an /etc/ddclient directory and move (or create a new) /etc/ddclient/ddclient.conf in there. The configuration should look something like:

################
# ddclient.conf
################
use=web, web=dyndns
ssl=yes
protocol=cloudflare,
server=www.cloudflare.com,
zone=puccithe.dog,
login=<CloudFlare Email Address>
password=<CloudFlare API Key>
puccithe.dog,

Testing

Now if you send an SMS to your Twilio “From” number, you should get a response back a few seconds later which has the most recent temperature data. Additionally, if you scheduled everything in cron, then any time a measurement comes in above 86F/30C, you’ll get an SMS. You can also pass -f to the c_sms binary any time in order to force it to send SMS messages.

Why?

Why did I build this? Mostly because it was fun, and also partly because sometimes I forget to turn on the AC and don’t want my dog to have to suffer. I hope you enjoyed reading this as much as I enjoyed building it.