💾 Archived View for capsule.adrianhesketh.com › 2019 › 11 › 24 › alarm-project-pi-go-aws-iot captured on 2024-06-16 at 12:29:22. Gemini links have been rewritten to link to archived content
⬅️ Previous capture (2021-11-30)
-=-=-=-=-=-=-
Last year, [I built a door sensor with my son](/2018/08/23/holiday-project-building-an-internet-connected-door-sensor-for-10/) and he wanted to do a bit of a refresh. The previous door alarm was based on an ESP8266 microcontroller, and used a simple integration with a REST API to publish data to the cloud to monitor the status of the door. It also didn't really function as a traditional burglar alarm. It only beeped when the door opened and closed.
He was keen to have an alarm with a digital code, so we set out to build a new one that worked in a similar way to commercially available alarms. I found the experience of developing for the ESP8266 a little tedious, so decided to spend a little more and use the Raspberry Pi Zero W as the base platform. Children can get bored if there's too much hassle, so I wanted to use something relatively easy. The Pi Zero W costs about £10, but is significantly more powerful than the ESP8266 and can run Python, Node, Go and other programming languages on the ARM chip. It's also capable of running the Linux operating system, which makes managing it straightforward if not complete overkill for this.
There are operating systems like Mongoose [0] that are a good choice for deploying updates to IoT fleets, but I wanted to limit the amount of things to learn in the one project.
For the keypad, we did a search for a 4x4 matrix keypad and found these:
They work by having a pin for each column, and a pin for each row. It's then up to your software to continuously check whether the pins have been pulled low by pressing a button on the pad which is how I ended up [writing a library](/2019/09/28/raspberry-pi-4x4-keypad-with-go/)
I asked my son how the alarm should work and showed him how to use a state transition diagram to describe its operation:
With this in place, we could start building out the logic for the system before the keypad even arrived from China. The logic for the alarm is at [1] with a suite of unit tests to check that the behaviour is expected at [2]
Once the keypad arrived, we could create a basic prototype on a breadboard and check that it worked OK. You can see that there's a buzzer in the circuit. It's not very loud, but it was all I had hanging around. We've ordered a 12v buzzer and some transistors, along with a power supply to try that out next.
Once we had that in place, we set to putting it in a box, starting with soldering onto stripboard.
We then put it in a wooden box that you can get from the local craft store (Hobby Craft) for £2.
It looked quite smart when it was done.
It then went through a few extra revisions. First, we added a 7-segment display so that we could see what was going on. That's how I ended up [writing a library](/2019/10/05/raspberry-pi-7-segment-display-with-go/) for displays.
There was just one small problem. The previous version of the system on the ESP8266 reported its state changes to an API in AWS, so we hooked up [AWS IoT](/2019/11/04/aws-iot-with-go/) and added the ability for the system to report the status of the alarm _and_ to subscribe to receive changes in the virtual device state.
The Go code to do that is pretty straightforward. The `New` function at [3] receives a Go channel as a parameter. Remote status changes are then pushed onto that channel by the code. The `New` function also returns a channel which is where the alarm posts its updates to the remote state.
We also added a switch to disable the alarm. It's good practice to use a debounce function to prevent switches from triggering back and forth while the mechanism completes. The code stops a state change from taking less than 10ms, which works well in practice.
// Debounce a pin. func Debounce(pin rpio.Pin) func() (s rpio.State, updated bool) { pin.PullUp() lastChange := time.Now() state := pin.Read() return func() (rpio.State, bool) { if time.Now().Before(lastChange.Add(time.Millisecond * 10)) { return state, false } prev := state state = pin.Read() return state, prev != state } }
Once we'd added a simple API using AWS Lambda [4] my son was able to add voice commands (arming and resetting the alarm etc.) using Google Assistant and IFTT [5]. He did that part completely on his own but I did have to accept the authentication key as a URL paramater rather than being a HTTP header, because I couldn't see a way to support complex HTTP requests in IFTT.
This project ended up taking a few months of ocassional Saturdays to complete, but it was a lot of fun.
Using CloudFlare workers to add CORS support to a backend API