Rounding out my in depth discussion of my aquarium nodebot from part 1 and part 2, I'm going to talk about the software architecture. The software is currently composed of two pieces: a Node.js based server and a small Python "driver." The python code is going to get ripped out soon though because I was informed that there is, in fact, a Node.js module for interacting with GPIO on the Raspberry Pi. Thanks Robert!
The server is broken down into four pieces: the scheduler, the configuration manager, the lighting control, and communications between the other three. Each piece runs in its own process, and I'm using cluster to manage processes and the interactions between them. This setup is a bit overkill for what I'm doing, but I kinda wanted to play with formally isolated modules and a pure message-passing only interface between them. The modules are structured such that they have no knowledge about the other modules, and instead listen for events. The basic architecture looks like this:
The master process provides logging capabilities and also serves as the communication glue between the other processes. It listens for messages sent from other processes. Messages have a type, a message, and a destination. The destination can either be broadcast or it can be sent to a specific process. Most often, modules use the destination as a way to send the message to just the master, such as for logging.
The scheduler process is responsible for scheduling when lights are to be turned on and off. It starts by fetching sunrise and sunset times from earthtools.org using the request module. It then has to do a little dance with the times because everything is in UTC, not local time. It figures out where the midnight rollover is relative to the sunrise and sunset times and reorders the times as need be. This is necessary because either sunrise, sunset, sunrise twilight, or sunset twilight can be the first event of the UTC day, depending on where in the world you live (I wrote this code to hopefully work in any timezone). Once this is done, the next event is scheduled using the node-schedule package, which calls a callback that first a light event.
The lighting control process listens for light events and then calls the GPIO driver program that toggles the appropriate pins on the Raspberry Pi. This is hands-down the simplest of the four processes, and I don't really have anything else to say about it.
Finally, there is the configuration process. This process starts a web server that hosts the configuration page. Right now, the configuration page is just a simple webpage written using Twitter's boostrap. It provides the ability to switch between manual and automatic mode, and to set the lights when in manual mode. When the save button is clicked, an XHR request is made back to the configuration process with the updated information.
I'm planning on rewriting the configuration page though. Right now, automatic mode is very automatic. There is no configuration over which twilight is used (it always uses astronomical, not civil or nautical), and there is no way to override any of this. Ideally, the sunrise/sunset times would have a configurable offset, and the twilight times could be hard-coded to coincide with when we usually go to bed, perhaps even supporting different weekend/weeknight settings. It will also give me a good opportunity to experiment with the recently released 1.0 version of Ember.js.
All in due time, of course. The great thing about this setup is that most things are done in software. The hardware is finished and supports everything I could want to do, and I have working software in place already. This gives me the advantage of being able experiment and add new features at my leisure going forward. This is just the beginning!