Actually useful home automation
By: David Richard Holtz
Tags: DIY, Programming, IoT, Electronics, Quarantine

Moving on from the hardware we need to program the board to record changes to the lock.

Logic

In a perfect world, we’d check the lock constantly (twice a second) and read if the blot is opened or closed. Then based on the status of the bolts we’d communicate back to the server/cell phone/Bluetooth device.

The code would look something like below

(networking code removed to make readable)

Where we startup the board — connect to the WIFI, and then loop and check the status of the pin. In the code snippet, we wait 10 seconds before we start the next loop and only send an HTTPS request when the status changes.

However, there are a couple of issues with the code above. 1. it is power-hungry. Since the board is on and connected to WIFI at all times — it drains a significant amount of energy. Since we plan to run the device on a battery, we have to be conscious of each joule we use. 2. We use an in-memory variable lastValue as a way to compare the current state to the last state. Since the value is only held in memory — if we lose power, restart, or leave a deep sleep cycle we will lose the information. So we’ll need to persist the variable lastValue somewhere on the board. This way we can use the sleep functionality and be resilient to power downs.

Practical power management

Let's incorporate the sleep functionality. The way “deep sleep” works on an ESP device is straightforward. The device can be put into deep sleep mode with the command esp.DeepSleep(<NUMBER OF MILLISECONDS>) which as it states, will put the esp to sleep for the specified number of milliseconds.

When the device is in deep sleep all of the components power-down except for the real-time-clock. The ESP needs a clock in order to keep track of the milliseconds it’s been asleep.

When the time is complete and the board needs to WAKE from sleep, it sends a signal from the apt’ ly named WAKE pin aka GPIO 16. If we redirect the signal from WAKE to the reset (RST) pin, the device will know that it should restart the boot process.

Init deep sleep → Sleeps for specified time → WAKE signal sent → RST triggered → Repeat

Incorporating deep sleep will need some software and hardware adjustments. In the circuit diagram above we included a jumper cable from RST to D0. This is exactly the modification we’ll need to wake our device from sleep. D0 on the WEMOS is also GPIO 16.

It’s important to note that we did not solder a connection directly between RST and D0 because in order to program the board — we need to free the RST pin. If there is a connection between RST and D0 when you plug the board into a computer — the computer will not be able to find the device (due to the RST pin being interrupted). We installed a jumper here — so we can remove the jumper when we need to program, but still have a good way to close the connection when running.

The following code is verbose and contains logic to do various things. Here is a rough table of contents

Lines 1 — 19: we import some libraries and set variables. The host, fingerprint, and port are used when making an HTTPS request later

Lines 20—83: this is all the “sendMessageToLambda” which does exactly that. The function is long since we need to open the HTTPS connection, build the request, and manually listen for the response.

Line 84–132 is all of the logic related to the tracking of the door’s state. Below the code snippet, we’ll break it down further.

Looking specifically at lines 84–132 you might first notice that the loop is empty, and all of the code is in the “setup” function.

Since we don’t want to run the device for long — we won’t ever enter the loop phase. We will only execute startup, then shut down again.

Line 129 is the end of the setup function and will put the device to sleep. Lines 89 and 90 are where we read the switch's current state. This can be opened or closed, unlocked, or locked, or in the digital case, 0 or 1.

Next, you’ll notice code calling the EEPROM. Which we can think of as a tiny hard drive, and is technically a non-volatile memory that is often used in embedded systems.

We’ll be using EEPROM very lightly; to persist and read a single bit. EEPROM has a minimum write size of 4 bytes, so we’ll be using more space then we need — but that is not an issue.

The logic for EEPROM is simple

Read EEPROM values→ compare the EEPROM value with current value → If changed do something → update value stored in EEPROM → Repeat

Essentially we just want to check if there has been a change in the door’s state since the last time we turned on. The EEPROM acts as a way to communicate between those two points in time.

Taking all those ideas into account we have the resulting code. The device will turn on periodically and check the door state, it will check for change and only if there was a change will it connect to the WIFI and send an HTTPS request to a lambda function containing its current state.

Great — now lets dive into what the lambda function does

Cloud code

Once our message is fired from the device to our internet-accessible lambda function we’ll want to store the event in a database. In this case, we’ll use DynamoDB which is a simple NoSQL datastore offered by AWS.

Let's examine the code below.

Lines 1–6: are the imports and some variables we’ll use later — like a connection to the DB and a timezone related function.

Lines 11–15: just sets up the function and validates that the incoming request contains the data we need.

Lines 16–18: since the microcontroller does not have an idea of the date/time, we append it in our lambda function. While we do not account for latency from the device to the function — adding the timestamp in the function gives us a very close approximation to the actual event time. These lines get the current time and represent it as a string and int for storage purposes.

Line 19–23: finally we save the values of our table. Following a clever pattern, I picked up watching an Amazon Dynamo Deepdive. The pattern tracks versions by always appending new values to the bottom of the list and the most recent to the first value of the list. Hence the two put_item functions below.

Amazing- we can record, update, and store our state changes. The last thing we’ll want to do is read our table!

We’ll need to be able to read the table if we want to send notification or be able to query the current state of the door.

Since we also want to have a pleasing message we can build the text of the message in this function.

And I love emojis… so of course I’ve added 🔓 and 🔐 to the conditional message. Lastly, the function also reformats the date into a nice HH:MM format along with a human-readable weekday.

This function can be called via HTTPS request and will return a nicely formatted message eg

🔐 Door was last locked at 12:43 on Monday

Perfect! Now we have all of the functional parts “working” we’ll just need to polish some things up — add some kind of UI and finish installing.

Let's look at those final steps!

Project to product

Now we have the hard task of putting all of this together in a way that isn't overt — disruptive — distracting—ugly — annoying. Pretty much we want to contain this in a pleasing way so I hunted around for a nice case.

Luckily I found this portable charger that my Dad received as some marketing swag at a conference he attended last year.

The back of the battery, has a small cutout for storing the cable in transit, I snapped the back off and made a small hole on the side of the case; this way I could run the wire back into the housing to power the board. Also, the newfound space doubled as a location for the board!

The board and the original charging cable fit nicely inside. In order to stop the annoying blinking blue LED, I removed it with small pliers. The last step is to plug the small wires from the bolt into the open adaptors (photo on the right, left-hand side there is a small two-prong female plug).