Home Automation: Phase 1 (by alaric)
I've always had a nerdish fascination with home automation.
There's been a recent trend towards the "Internet of Things" (a.k.a. "IoT"), which is related, but different. The IoT seems to focus more on home devices talking to centralised Web services, which is a hateful model (we all know why: those central services are outside of your control, so unless you're paying a subscription, you are the product rather than the customer; and if they're shut down, your devices become useless; and they can leak your private information whenever they want; and they can take control of your home; and it all falls apart if your Internet connection goes down; and I'm sure there's others).
No, I want to have my house under computer control - but with those computers under MY control. This is something I've planned for for ages, but as with all hardware projects, getting started is tricky; I need to commit to a final design and then afford to buy the parts, and that's scary because I might find out that the parts don't quite work together in the way I wanted. Unlike with software, hardware hacking requires up-front commitment of resources, that can't be backed out of. Scary!
So the trick is to split the thing into small parts, with flexible interconnections, so I can iteratively prototype parts and then connect them up in due course. And this last week I took the first step - building an announcement system in the living room.
The hardware's pretty simple; a black plastic box, with two speakers set into the sides, driven by a 3W stereo amplifier module from a Raspberry Pi, via a dual-gang potentiometer from the Pi's analogue audio output. The Pi needs five volts, as do the isolation relays (more on those later), while the amplifier module requires nine volts, so there are 5v and 9v positive linear regulators on a bit of veroboard, along with the isolation relays.
It's all squeezed into the box, from which three cables protrude: the twelve-volt power input, Ethernet to the Pi, and an HDMI cable to hook it up to the TV in future. It will also, in future, grow an extra cable connecting the isolation relays to the front door, where a doorbell and a door-open sensor will close a five-volt circuit to energise the two isolation relays, the outputs of which will trigger the Pi's 3.3-volt logic input lines. But we don't have a doorbell or a door-open sensor yet, so that will wait. I couldn't fit the speakers inside the box, so they have to protrude, which is annoying (something to fix for version 2). As their mounting holes are floating out in space, the speakers are held in place with judicious use of black Sugru.
The Pi's analogue audio output is very noisy. I didn't expect superb audio, as it's going to be announcing messages rather than playing wonderful music (except maybe via the TV over HDMI if I set up XBMC), but the level of background hum and squealing when the CPU is busy is a pain, so I'm going to try a USB sound card when it arrives.
The inside is an atrocious mess of wires:
But once it's all jammed in and secured with cable ties and the lid screwed on, it looks quite neat:
Ok, the HDMI cable dangling to the TV is ugly. I'll probably put another bit of trunking in for that (the 12v and Ethernet lines go through the upper trunking).
However, the fun part is (as usual) the software.
I've written a little Chicken Scheme app called "speakd" to manage speaking. It uses a disk-based priority queue (implemented as an SQLite database, in fact) of events that it should inform us of, and sits in a loop pulling out the next event and processing it. The events are high-level things like "doorbell press" or "time for dinner" or "it's N o'clock" (events can have parameters), which can originate from manual intervention, scheduled events from cron, hardware events like button presses, or whatever - anything I can arrange to run the command-line tool that inserts a properly-formatted event into the queue.
There is then a configuration file full of handlers for different event types, which are actually Scheme code that performs arbitrary computation upon the parameters within the event to produce a list of actions to perform. The available actions are currently to play a WAV, MP3 or OGG file, or to convert Lojban or English text to speech (using espeak
), or to pause for a specified duration; but things like flashing lights could be added as well.
If a handler completes, it's removed from the queue. If a handler fails by throwing an exception, then it is left in the queue but its "retries" counter is incremented and the exception error message recorded in an otherwise-NULL field. Events with a retry count of 10 or more are ignored, to allow for transient errors to be corrected, but repeated offenders are rejected.
The configuration file I'm working on looks like this:
`((test . ,(lambda () '((jingle-ogg "Rooster_crowing.oga") (text-jbo ".i cipra") (pause 2) (text-en "Test") (nop) (jingle-wav "test.wav")))) (dinner-ready . ,(lambda () `((jingle-wav "dinnerbell.wav") (text-jbo ".i ju'i lo sanmi ku co'u seljukpa") (text-en "Dinner's ready!")))) ; Hourly time chime (time . ,(lambda (hour) `((text-jbo ".i li " ,(number->string hour) " tcika") (text-en "It's " ,(number->string hour) " o'clock")))) ; 7:50am reminder (weekday-morning . ,(lambda (notes-jbo notes-en) (list `(jingle-ogg "Rooster_crowing.oga") `(pause 1) (if notes-jbo `(text-jbo ".i coi rodo cerni zo'u " ,notes-jbo) `(text-jbo ".i coi rodo")) (if notes-en `(text-en "Good morning: " ,notes-en) `(text-en "Good morning!"))))) ; 8:10am reminder (weekday-morning-ttl . ,(lambda () '((text-jbo ".i ko cliva") (text-en "Time to leave!")))) )
The excellent crowing rooster OGG file is from Wikipedia.
On Sunday evening, my children were in the lounge while I made dinner. When it was ready, I ssh-ed into the raspberry pi (in due course, I'll make a Web interface to do this for me) and typed:
speakd-event '(dinner-ready)'
They were roused by the sound of the dinner bell, followed by the announcement in Lojban and then English. Truly, this is life in the twenty-first century, at last!
I've a little work still to do on "speakd"; I need to hook it into the startup scripts so it's running from boot, document it better, and add it to the Chicken egg list. But you can have a play with it yourself for now, by typing:
fossil clone https://www.kitten-technologies.co.uk/project/speakd speakd.fossil mkdir speakd cd speakd fossil open ../speakd.fossil
Next Steps
I still need to wire this thing up to a doorbell and door-open sensor, so it can start reacting to environmental events as well as scheduled announcements and manual messages; the next step will be to make some more of them, having fed back experiences from construction version one, to go in other rooms. I'd like one in my workshop for announcements (there's no other way I'll know if there's somebody at the door from all the way back there!), and to drive a plethora of sensors. I'd like one in the kitchen with a touch screen for recipe viewing, background music choosing, and kitchen timing (I have a neat idea for a recipe schedule file format that encodes a Gantt chart, directly fed into a timer, with the ability to merge recipes and coordinate their expected ready times then feed back to tell me when to start everything!), and ones in the bedrooms to issue wake-up calls.
Background music could be supported by speakd, by having it play audio from an "idle stream" when it doesn't have its own announcements to do; then a separate music player daemon can provide the idle stream and just receive pause/resume commands from speakd.
Version two of the hardware should have better speaker mounting (perhaps I should laser-cut a custom case, now I have a better grasp of how everything fits together inside?), and I'll research using switched-mode regulators for the five volt supply to the Pi; dropping seven or so volts at 300mA means that the regulator makes good use if its heatsink. Perhaps the unit in the workshop can inherit the linear regulator from my prototype unit, as it'll need to keep warm in winter!
Coordinating announcements (and distributing events like doorbell presses) over a distributed network of announcement machines is an interesting technical challenge. I have some thoughts about doing this in an easy-to-use way by implementing a distributed state machine engine. Watch this space...
So why do this?
Because I think that making my house able to talk to me is a useful feature. It means that computers can announce stuff to me when I'm not looking at a laptop or fiddling with my phone, so I can be reminded to eat or go to bed when I'm absorbed in a book. It means I can be given a warning that it's nearly time to go in the morning when I'm rushing around getting the children ready for the school run. It means that announcements can be sent to people in my house who don't have laptops or phones, such as the children, or visitors who may have those devices but we don't know about them.
It's a lot of work for this relatively small benefit, but the effort is fun (fiddling with computers!). Also, it means I'll get a network of Raspberry Pis around my house, that can be used for other stuff (monitoring sensors, driving USB external hard disks for a distributed Ugarit storage cluster, kitchen touchscreen, etc).