Introduction: Weasley Clock
Some of you might remember the Weasley family clock from Harry Potter : it allowed members of the Weasley family to know where each one of them was currently. We (my cousin and I) decided to make our own version of the clock. Since we didn't know how to control so many clock hands, we decided to use LEDs instead : the clock has 12 segments representing 12 different places (home, work, etc.).
Each family member has their own colour (red - blue - white - yellow - green) so whenever they reach a specific destination, their corresponding LED lights up in front of the place they currently are. If their location doesn't match any predefined one, their LED lights up in front of the "Unknown" position (number 11 on the clock)
The motivation for this project is to be able to know at a glance where every member of the family is : they don't actually live in the same countries so even when everyone is home, they're not actually in the same place !
Disclaimer : as I did not keep the final object, I won't be able to add any pictures of the final result just yet. I will get my partner in crime to take pictures as soon as she can. As we were running short on time at the end, the casing is much less evolved than what we were planning to do, and what you see in these mockups. The electronics part, however, is complete and doesn't work too bad ^^
Step 1: General Idea
Before starting it's always good to get a general idea of how the project works. We'll talk in terms of software bits rather than specific devices because the hardware that we used happened to be the one we already had, and not everything is needed. Of course in the following steps we'll go into more detail.
So how does it work exactly ?
Each member of the family has a smartphone (sorry but this is a prerequisite !) : let's take user A, who has an Android phone.
On this phone there's an app that follows their current location (the app works on iOS as well). When the app detects user A has changed location, it sends the current position (geographic coordinates) to a server. The protocol used is MQTT, so the server is called the "broker" and the app on the smartphone "publishes" to it.
A node.js application running on a server has "subscribed" to every update for user A, so it is informed of this new position. The server knows the geographic coordinates for every possible place for user A : at home, at work, etc. It compares the current position to this data and concludes the user's location. It then makes an API call on the Spark cloud for the clock to change accordingly.
How does the server know the coordinates for every possible place for this user ? It fetches them from a MySQL database every six hours, so if a user changes their favourite restaurant or their workplace, the code still works, they just have to update the coordinates in the database
Now that the general workflow is clear, we can get started !
Material needed
As stated above, you should be able to use different hardware since most of the tools we used support a lot of platforms. We do, however, recommend the following :
- Raspberry PI
- Spark Core : this project takes advantage of the very specific features offered by the Spark cloud
- 74HC595 shift registers ... but we'll come back to that soon enough !
- LEDs : lots of them ! In our case we choose to track five family members. Each one has their assigned color (red/blue/white/yellow/green) and can be in 12 different places (one of them being "Unknown") so that's 60 LEDs
- Casing : since the interesting part of this project is, in our opinion, the electronic part, the overall build process of the casing is not very well detailed (also because we didn't make it as nice as we wanted !). The internal structure was done with plywood and the external was supposed to be done with acrylic
Having 12 different places came from the inspiration for the project, the clock from Harry Potter, but you could easily change this to something else, and change the casing accordingly
Step 2: Preparing Your Raspberry Pi
The first step here is to prepare the Raspberry Pi. As you might have guessed, the Pi will host most of the software that runs on what we previously referred to as "the server".
Install a Linux distribution on your Pi
In our case, we chose to install Raspbian on our Raspberry, following the instructions on this page http://www.raspberrypi.org/downloads/ . We followed the Noobs part and everything went smoothly !
Install the MQTT broker
Once Raspbian is installed, the next step is to install Mosquitto : this MQTT broker software is open source and very easy to use. We installed it via apt-get since Aptitude is already present in the Raspbian distribution. Here are the instructions we followed from http://mosquitto.org/2013/01/mosquitto-debian-repo...
To use the new repository you should first import the repository package signing key:
wget <a href="http://repo.mosquitto.org/debian/mosquitto-repo.g..."> http://repo.mosquitto.org/debian/mosquitto-repo.g...<br></a>sudo apt-key add mosquitto-repo.gpg.key
Then make the repository available to apt:
cd /etc/apt/sources.list.d/<br>sudo wget <a href="http://repo.mosquitto.org/debian/mosquitto-stable.list"> http://repo.mosquitto.org/debian/mosquitto-stable...</a>
Then update apt information:
apt-get update
And discover what mosquitto packages are available:
apt-cache search mosquitto
Or just install:
apt-get install mosquitto
After that just launch mosquitto in daemon mode (we didn't specifiy a configuration file since we're using all the defaults settings) :
mosquitto -d
Install and configure MySQL
In our case we happened to have a Synology NAS with MySQL installed on it, so we went the easy way and used that. If you don't, you can of course install MySQL on the Raspberry (still the same one) or on any device that you want. Here are the instructions for the Pi :
http://www.raspberry-projects.com/pi/software_util...
Once that's done, you'll want to create a table for each user : the table should have their name, and should have three columns : Latitude, Longitude, Radius (in meters). This will allow us to create 11 entries, each for a different location. The radius parameter is used to add a dimension to a specific location : if for example you decide that one of your location is an entire city, the radius will be much greater than if it was just your home address.
You can now fill these entries with the data corresponding to your users.
Step 3: Install Node.js
Installing Node :
node.js is a framework that allows people to write server side applications using Javascript : it has been pretty popular for the last few years and there is a version available for every platform.
We could have written the Node application from scratch but luckily, we stumbled upon an article on Node-red : Node-red is a project that allows us to create Node.js workflows using simple building blocks. Each block receives an input message (with a title, a payload, etc.) that it processes and passes along to the next block. Node-red fits our project perfectly as it has been designed to create workflows connecting "hardware devices, APIs and online services"
In our case, the obvious choice was to use the same Raspberry Pi as previously for the Node part of the project. However, you can easily choose to use another machine, which can be on another network.
To install node.js and node-red, we followed this excellent tutorial from Adafruit : https://learn.adafruit.com/raspberry-pi-hosting-no... (the author of the article happens to use the same MQTT broker as us - it used to be called MQttitude - but the tutorial is intended as a simple introduction to Node-red)
Step 4: Create the Workflows
Once you've installed node-red, you should see a webpage like this one when you navigate to http://ip.of.your.pi:1880
This is where you create your workflows, which should end up looking like ours. The part on the left is called the palette, and that's where you will see every node available for you to use, including the ones you installed or created yourself.
Just drag a node from the left to get started. You'll see that once you do so, it displays a little red triangle on top of the module : this means the node needs to be configured. Just double click on it and enter the parameters requested.
Once you're satisfied with your setup, you should click on the deploy button on the top right part on the screen.
Our workflow
For this instruct able you should be able to get the app to work by simply using the same setup as we did.
Let's now go through the different components :
First chain
(1) "Every 6h"
This is an inject node : it doesn't receive any input but generates one with the content that you choose, and you can decide how often it does so. In our case the topic should be "SELECT * FROM Alya" where Alya is your first user (or more precisely, the name of the MySQL table for your first user).
The repeat argument is set to "interval between times", every 60 minutes, every day.
(2) DB Media Server
This is a MySQL node and can be found in the storage category. In the sidebar you should see a "Configuration" tab where you'll be able to enter the hostname, port, etc for your MySQL database. In our case we entered the local IP of the Synology NAS but if you installed MySQL on your Pi, this should be localhost. You can test the connection to make sure your parameters are correct.
(3) Store locations
This is a function node : it allows you to do whatever you want with the content you receive, and send back whatever you like, simply by writing a few lines of Javascript.
In our case the code is as follows :
//store the locations in a global variable
context.global.locations = msg.payload ;
//console.log(msg);
return msg;
As you might have understood, this chain fetches the content of the table for the user Alya and saves the geographic coordinates for the different locations in a global variable. This allows us to change the coordinates for a specific location for a user (if their workplace changes for example) directly in the database without changing anything in the code
Second chain (the main one)
(1) Geoloc Alya
This is a MQTT node. You should enter the hostname, port etc of your MQTT broker. In our case this was localhost since the broker and node-red are on the same Pi. You must also decide which topic the node should subscribe to : since we're following the user Alya, we put /owntracks/alya. This node will be activated everytime a message is published to the MQTT topic specified. In our case that will happen every time the user changes location.
(2) Compare
This is a function node, so you can enter whatever code you want : here it receives the current coordinates of the user and compares it to the known locations to decide where the user currently is. Here's the code we entered in the function
console.log("start"); var loc = eval('(' + msg.payload + ')'); var dist = 0;<br>var location = "unknown"; for( var i =0 ; i < context.global.locations.length ; i++) { // console.log(i); dist = distance(context.global.locations[i].lat,context.global.locations[i].long,loc.lat,loc.lon); console.log(context.global.locations[i].lieu ); console.log("distance "+ dist); if(dist < context.global.locations[i].radius) { location = context.global.locations[i].lieu ; } } var newMsg = { payload: "alya," + location }; return newMsg ;
function distance(lat1, lon1, lat2, lon2) { var radlat1 = Math.PI * lat1/180 var radlat2 = Math.PI * lat2/180 var radlon1 = Math.PI * lon1/180 var radlon2 = Math.PI * lon2/180 var theta = lon1-lon2 var radtheta = Math.PI * theta/180 var dist = Math.sin(radlat1) * Math.sin(radlat2) + Math.cos(radlat1) * Math.cos(radlat2) * Math.cos(radtheta); dist = Math.acos(dist) dist = dist * 180/Math.PI dist = dist * 60 * 1.1515 dist = dist * 1.60934 *1000 return dist; }
(3) Function + LED
This node is a Spark core function node : it will not be available by default, instead it should be installed by you, following the instructions on this page https://community.spark.io/t/spark-core-and-node-r...
You will then be able to enter your Core ID, your token and the name of the function that you want to call (it must be a function publicly advertised by your Core). Since we're not giving the node any parameter, it will send the result of the previous node as the payload (which should look like "alya,home")
This part might make more sense to you once you've setup your Core in a few steps ^^
Note : when we made this project there was no Spark Core node in Node-red : what we did was make a simple HTTP call instead using the Core API. However it is much easier to use a dedicated node.
So that's the workflow for one user : the five different tabs are five different workflows, strictly identical except for
- The SQL request (SELECT * FROM usernameTable)
- The subscribed MQTT topic (/owntracks/username)
- The output that's being sent to the Core ("username,location")
Step 5: Spark Core and LEDs
Now that our workflow is ready, it's time to finally light up those LEDs.
In theory this is relatively simple : after all, we're only trying to light up LEDs, which is like the "hello world" of Arduino ! However there are two small twists.
First of all, we have five users with twelve possible locations, which gives us 60 LEDs. Since only five LEDs will be on at the same time, power should not be a problem, but we still have to address 60 different outputs. Since the Spark Core cannot drive that many LEDs, we'll use the 74HC595 shift register, or more accurately, eight 74HC595 shift registers (since each one can control up to 8 outputs).
For more information on this shift register and the way it works, please visit http://www.bildr.org/2011/02/74hc595
The other small twist is that since we're daisychaining eight different shift registers, we need to use a small circuit to ensure that the signal will still be strong enough for every shift register to pick it up : this is called a push-pull line driver (credits to http://www.elcojacobs.com/ for the schematics and to explaining this on their website, it took us weeks to figure out)
In the schematic above we've only included one register : when using more, however, you should wire each register's Q9 pin to the next register's DATA pin. The LATCH and CLOCK signals should be the same for every register (in order for the registers to work properly). In our picture you can see the purple wire (data), the grey wire (clock) and the brown wire (latch) from the second board going to the first board : the data one is wired to the Q9 signal of the previous register, whereas latch and clock are wired to the output of the two push-pull line drivers
(Don't forget to add an external power source - we used a micro USB breakout board and wired Vcc to the Core and the Vcc line of the registers, and the ground to .. well to the grounds)
Step 6: Hardware Considerations
At the beginning of this project we started prototyping our circuits on breadboards. We then switched to perfboards as you can see on some pictures.
However, due to the high number of wires involved in a relatively small volume, this soon became a nightmare.
We've since created our PCBs using Fritzing : the first PCB has spots for the five different LEDs and two resistors (since the red led and the yellow one needed voltages lower than 3.3V) , and is actually simple.
The second one however saved us a lot of time, since it allowed us to "hardwire" most of the connections needed around the shift registers : using a double layer PCB prevented the result from being to big.
We had both PCBs printed by Fritzing and are very satisfied with the result. We had never created a PCB before and the different views really make it easier for first time users.
You can download the corresponding Fritzing (fzz) files, we didn't use the breadboard view so it's a little messy but the schematic view and the PCB layout are good.
Attachments
Step 7: Casing
In order to position the LEDs properly, we designed two circles that we then cut in plywood using a laser cutter. The first one has sixty wholes in it : the PCBs are to be positioned underneath it, and the LEDs should be placed so that the top of every LED comes out by the whole.
The other one has twelve wholes in the middle : in essence the LEDs on their PCBs are sandwiched between the two circles, and the wires are coming out the second (bigger) circle to be soldered onto the shift register PCB (here you can still see the perfboard version of it).
The twelve circles allowed us to separate the wire for every segment (5 LEDs + ground wire).
Unfortunately we couldn't finish the casing the way we wanted to : the idea was to add the plywood part that you see in this picture inside a white acrylic frame. The clock would look like a plain, white circle with twelve icons on it, with only a coloured dot for each user in front of their current location.
Step 8: The Core
Let's sum up : we've filled up our database with the location data for every user, we've created the node-red worklows that reacts to every new position it receives. We've also wired the LEDs and shift registers to the Spark Core so that they can be controlled by it, and we've put everything in a plywood structure that will be inside the clock. Not bad huh ?
Now that pretty much everything is ready, it's time to add code to the Core !
Adding code to the Core
The Spark Core can be used like any other Arduino-like board (except it uses a web IDE by default). In our case however we need to take advantage of one specific feature of the Core : API calls. In a few words, this allows to Core to "expose" the methods it wants, meaning they can be called using the Spark REST API.
Go to http://www.spark.io and follow the instructions to setup your Core for the first time. Once you're done, you should be able to access the IDE (screenshot taken from the spark documentation website)
You might remember the Token and Core ID we talked about earlier : now is the time to retrieve them from our Spark account and enter them in the Spark function node-red module. We had decided to call our function "led" so here's the corresponding code on the Core :
void setup() { //Register our Spark function here<br> Spark.function("led", ledControl);<br> // Configure the pins to be outputs pinMode(SER_Pin, OUTPUT); pinMode(RCLK_Pin, OUTPUT); pinMode(SRCLK_Pin, OUTPUT);<br> cycle()<br>}
This tells the Core that whenever someone sends it a call to the "led" function, it should execute the "ledControl" function.
The loop function is empty.
We also need to add the code to control the shift registers. This was greatly inspired from the bildr article mentioned earlier
void clearRegisters(){
for(int i = numOfRegisterPins - 1; i >= 0; i--){
registers[i] = 0;
}
}
void writeRegisters(){
digitalWrite(RCLK_Pin, LOW);
for(int i = numOfRegisterPins - 1; i >= 0; i--)
{
digitalWrite(SRCLK_Pin, 0);
int val = registers[i];
digitalWrite(SER_Pin, val);
digitalWrite(SRCLK_Pin, 1);
}
digitalWrite(RCLK_Pin, HIGH);
}
//set an individual pin HIGH or LOW void setRegisterPin(int index, int value){ registers[index] = value; }
void cycle(){ for(int i = 0 ; i < 30 ; i ++) { clearRegisters(); setRegisterPin(i, 1); writeRegisters(); delay(500); } }
Finally, here's the code for the ledControl function
int ledControl(String command)
{Serial.println(command) ;int pinNumber = 0 ;
int pers = 1 ;
int offset = 0 ;
clearRegisters();
String personne = command.substring(0,command.indexOf(','));
String lieu = command.substring(command.indexOf(',')+1,command.length());
if(lieu == "maison")
{
offset = 0 ;
}
else if(lieu == "work")
{
offset = 5 ;
}
else if(lieu == "friend")
{
offset = 10 ;
}
else if(lieu == "mosk")
{
offset = 15 ;
}
else if(lieu == "restaurant")
{
offset = 20 ;
}
else if(lieu == "doctor")
{
offset = 25 ;
}
else if(lieu == "shopping")
{
offset = 30 ;
}
else if(lieu == "family")
{
offset = 35 ;
}
else if(lieu == "doctor")
{
offset = 40 ;
}
else if(lieu == "city")
{
offset = 45 ;
}
else if(lieu == "abroad")
{
offset = 50 ;
}
else if(lieu == "unknown")
{
offset = 55 ;
}
if(personne=="Alya")
{
pers = 1 ;
}
else if(personne == "Assya")
{
pers = 2 ;
}
else if(personne == "Nabila")
{
pers = 3 ;
}
else if(personne == "Firdos")
{
pers = 4 ;
}
else if(personne == "Minaz")
{
pers = 5 ;
}
pinNumber = offset + pers ;
setRegisterPin(pinNumber, 1);
writeRegisters();
return 1;
}
Step 9: Configuring Owntracks
Now that your whole setup is done and your clock works, the last thing to do is to install the own tracks app : http://owntracks.org.
It is available for iOS and Android (screenshot from the Play store).
Once you've installed the app, you will be asked to add a broker to the list : in our case we had to set up a dynamic DNS since our ISP didn't support static IPs. Don't forget to set up the right NAT/PAT rules on your firewall/set top box !
For each user, you have to go into the parameters and choose the topic the app publishes to : by now you should have guessed this is /owntracks/username.
If everything goes well, reporting your location should change the LED to your position
We hope you enjoyed this project as much as we did, thanks for reading !