Warning: This post gets pretty detailed and bit-twiddly. So, if your eyes glaze over at the site of code, you may want to read my post about good things going on in comics, instead.
Spoiler Alert: My Kids Are Nerds
There used to be a place called Magiquest at the local mall here in the Western Suburbs of Chicago. It was, basically, a LARP for kids – think Harry Potter meets scavenger hunt. The experience boiled down purchasing a wand for your kids to use, they got a book of quests to complete, and then they ran around a large space waving their wands at interactive displays that would react to their wand. By pointing their wand at all the correct interactive displays, kids could complete quests and collect runes on their way to leveling up and facing evermore complex quests. My kids fell in love with it.
The local version closed (I think it’s a bowling alley now), but we were fortunate enough to find one at the Great Wolf Lodge in Wisconsin Dells. My kids would have played the game all day, if we had let them. The excitement of controlling things by waving a magic wand at them was apparent. I was thinking of a way to get that same excitement closer to home.
Spoiler Alert: I Am A Big Nerd, Too…
It occurred to me that the Magiquest wands were really nothing more than single-button IR remotes. I knew they must also pass some information about the wand so that the interaction could be associated with the waver, but I was hopeful that I’d be able to find a way to at least identify a MQ wand being waved and find a way to respond to it. I decided to break the challenge down into several smaller engineering challenges.
I decided to go with a Raspberry Pi as my platform. I didn’t have any experience with the micro-computing platforms, but I did have experience with Linux, so it seemed like a more natural transition than Arduino. Plus, there were a number of libraries available to help accomplish my goals.
Challenge 1: Getting The Pi Up And Running
The first challenge was to get a Pi up and running. I splurged and bought the Radio Shack starter kit that included a Raspberry Pi B+. My reasoning was that it came with a breadboard and several sensors and controls that would eventually prove useful. You could certainly get what’s needed for less, but I wanted to start with everything I needed and this was the fastest way there. The kit comes with a NOOBS card, but I new I wanted to use the GPIO right away so I downloaded and installed Occidentalis out of the gate. After downloading the OS, I used SD Formatter to create the bootable image. I plugged the power in and hooked the Pi up to an HDMI-compatible monitor (although, I am using an HDMI > DVI adapter), inserted the card and fired up the Pi. I used the raspi-config to expand the file system and make sure my keyboard and mouse were working. After saving my config, I was greeted with the GUI for the Pi and was able to do any number of basic tasks like use the terminal and browse the web.
Check.
Challenge 2: Control An LED
Now that I had a functioning Pi, the next challenge was to be able to turn on and off an LED using shell commands. I started with this excellent tutorial from Gordon @ Drogon. Doing so requires installing the wiringPi library. I found some good instructions on how to install the library. Once the library is installed, we need to wire up an LED.
I used the ribbon cable and breakout from the Pi Starter Kit to connect to a bread board. The wiring was simple enough. I connected a ground from the breakout to the ground rail. Then, I used a 270 Ω resistor to connect the short/flat side of the LED to the ground rail. Using a jumper, I connected the positive side of the LED to breakout pin #23. The result looks like this:
At this point I was ready to test. One note, the standard mode of wiringPi has different pin numbers than the Broadcom numbers reflected here. You can set a mode that uses the BCM GPIO numbers, but I stuck with the standard PIN assignments. Gordon has a good GPIO to wiringPi reference that shows that I’m interested in wiringPi pin #4.
Side note, this diagram was created with an awesome app provided by the good folks at Fritzing.
All that was left was to type the shell commands to get 3.3V of power passed to the LED. First we need to set the mode for pin 4 to output, then we need to tell it to turn on. I opened LXTerminal on the Pi and typed:
gpio mode 4 out
gpio write 4 1
And, voila! The LED lit up. Typing,
gpio write 4 0
turned it off again.
Check.
Challenge 3: Success Still Seems Remote
For this challenge, I needed to make some supplemental investments. First, I ordered the Sensor Pack 900 from adafruit, All I really needed was the IR sensor, which will set you back $1.95, but I got the assortment in anticipation of further projects. I also got the mini-remote, which was $4.95 but, again, you can probably use any remote you have lying around the house.
Once they arrived, I needed to update my wiring to include the IR sensor. I also knew that I wanted to distinguish between the wands, and I needed 2 outputs to make this happen. So, I added a second LED to the setup. Here’s what the result looks like.
Now, all that was left was to install the necessary libraries and write the code to handle the interaction. For this, I leaned heavily on an excellent tutorial at ozzmaker.com. In addition to wiringPi, you’ll need to install LIRC and its client libraries, as well as make a couple changes to the environment config.
There are a couple of points of interest, as well. First, the file “/etc/lirc/lircd.conf” is where the magic happens. Second, irrecord is a command that helps you set up a lircd.conf file for “well-behaved” remotes. Third, the MQ wands are not well-behaved. Fourth, we start to use C as the programming language to make things happen, so you’ll need to have an up-to-date gcc compiler installed.
Finally, mode2 is a command you will get to know and love. If you stop lirc and start mode2, you can see the raw output of the IR pulses being received by the sensor. This is not important now, but will be in the next challenge.
I followed the steps of the tutorial to a T, using irrecord to capture the output of pressing 1 and 2 on the remote and replaced /etc/lirc/lircd.conf with the new file, restarted /etc/init.c/lirc and compiled an executable based on the code in the tutorial (ignoring button 3), and everything worked. I was able to press 1 and toggle the red LED, and press 2 to toggle the green LED.
Check.
Challenge 4: Making Magic
This step was where I went most off the gilded path. I needed to break it down into sub-challenges. I needed to understand the signals the wands were sending, adjust the environment and code to support the new signals, and make sure that what I expected to happen was happening. It looked like this:
- Record the signals from the wands
- Analyze the signals from the wands to understand what they meant
- Adjust the lircd.conf file
- Edit the C code to respond accordingly
- Test
Step 1: Record
So, to record the output from the wands, I used mode2 mentioned in the last challenge. To help collect it, I dumped the output of mode2 into a text file to use for analysis. I did this using
mode2 -r -m -d /dev/lirc0 > wand-one-test.txt
The -r option forces raw mode, and the -m option cleans up the display to make it easier to edit later. Then, I proceeded to wave the wand at the IR sensor multiple times. The output of mode2 includes the wait time between waves, so I used Sublime Text to remove it and use Search/Replace to create a comma-delimited, one-wave-per-line version of the output. I created one for each wand and imported them into the same Excel file. That formed the basis of my analysis.
Step 2: Analyze
When I looked at the files, it became apparent what was going on. For each wand, I created a line with the MIN for all columns, a line for the MAX for all columns, and subtracted to find the range for each entry. I then found the ABS for the difference between wands to see the difference between the two wands. The output looked like this:
Up front, where the ranges seem consistent across wands, I assume this is identifying that it is a wand. Next, there is a section where the variation is not strong within the responses for each wand, but they are different between wands, so I assume that represents the identity of the wand. At the end, there is a section where there is a high degree of variation within the responses for each wand, so I assume those have to do with the particularities of each wave so they map to acceleration or orientation. The next step was to use this information to make stuff happen.
I created a second tab to calculate the average of the input coming from each wand. I’ve included the spreadsheet to use as a template in the resources section of the site.
Step 3: Configure
The next step is to create a file to replace /etc/lirc/lircd.conf in order to have lirc listen for the right signals. The code looks like this (replace the codes with the codes from your wand:
begin remote name WANDS flags RAW_CODES eps 30 aeps 100 frequency 37500 ptrail 0 repeat 0 0 gap 110000 begin raw_codes name FIRST_WAND 238 856 244 860 243 856 238 865 265 848 213 861 236 856 242 860 239 859 236 861 522 631 240 859 237 863 233 858 235 859 523 636 520 633 240 855 238 869 508 635 521 633 522 635 525 627 260 847 237 858 240 857 524 629 236 857 236 861 521 632 522 639 226 868 234 859 242 856 236 857 239 856 240 860 239 844 523 632 367 766 236 860 239 858 237 856 242 854 239 858 237 860 379 757 380 751 260 837 236 860 237 860 521 634 376 748 367 755 371 755 238 name SECOND_WAND 244 880 294 849 286 846 253 881 254 882 250 897 239 884 255 889 252 880 273 861 547 663 537 656 235 896 539 652 535 659 556 655 230 888 241 890 538 658 546 645 242 891 546 659 533 656 533 666 234 898 535 660 238 898 236 898 527 662 533 674 223 894 539 663 249 866 255 879 257 879 284 851 273 860 250 886 542 658 236 888 262 877 285 844 288 848 254 879 253 879 259 877 402 771 375 774 250 881 546 666 236 885 249 886 253 880 249 884 398 766 407 end raw_codes end remote
The first thing to notice is that the flags are set to RAW_CODES. This is because we are going to use the raw codes from the wands, rather than a hex value. Next, give a name to the codes for each wand that is easy to remember and use in the code that makes the whole thing work. You want to copy this file over /etc/lirc/lircd.conf and then enter:
sudo /etc/init.d/lirc restart
And, then, LIRC is running with the new codes in place.
Step 4: Code
The next step is to create an executable to make it all work. First, create a file called wands-example.c and use the following code. I started with ozzmaker’s code for the remote:
#include <wiringPi.h> #include <errno.h> #include <stdlib.h> #include <string.h> #include <lirc/lirc_client.h> #include <time.h> void flipLED(int led); //the wiringPi pin numbers #define LED1 4 #define LED2 5 #define ON 1 #define OFF 0 int main(int argc, char *argv[]) { struct lirc_config *config; //timer for buttons int buttonTimer = millis(); char *code; char *c; //initiate pins if (wiringPiSetup () == -1) exit (1) ; pinMode (LED1, OUTPUT); pinMode (LED2, OUTPUT); //initiate lirc if(lirc_init("lirc",1)==-1) exit(EXIT_FAILURE); //read default lirc config at/etc/lirc/lircd.conf if(lirc_readconfig(NULL,&config,NULL)==0) { while(lirc_nextcode(&code)==0) { if(code==NULL) continue;{ if(millis() - buttonTimer > 400){ if(strstr (code,"FIRST_WAND")){ printf("Abracadabra!\n"); flipLED(LED1); buttonTimer = millis(); } if(strstr (code,"SECOND_WAND")){ printf("Alla kazam!\n"); flipLED(LED2); buttonTimer = millis(); } } } free(code); } lirc_freeconfig(config); } lirc_deinit(); exit(EXIT_SUCCESS); } void flipLED (int led) { if(digitalRead(led)==ON) digitalWrite(led, OFF); else digitalWrite(led, ON); }
After the includes and defining some constants, the first thing to do is start LIRC and set up the wiringPi pins. Then, it starts listening for an event. Once an event is fired, it looks for the codes we set in the lircd.conf file for the different wands. If there’s a match on a wand, I had it fire off a message to the console, then toggle one of the LEDs. Once I saved the code, I compiled it with gcc:
gcc -o wands wand-code.c -lwiringPi -llirc_client
As ozzmaker suggests, I created an empty LIRC file the first time I ran it:
touch /etc/lirc/lircrc
Then, to run it, type:
sudo ./wands
There were no errors, so all that was left was to get the kids to grab their wands and give it a test run.
Step 5: Test
I showed the kids where the IR sensor was and told them to shake their wands at it. After a few waves, I saw “Abracadabra!” print on the screen and the green LED lit up. It took a few more waves, but the red one came on and turned off, too. So, I went in and made some adjustments to the lircd.conf file. The EPS and AEPS lines are settings that determine error thresholds. EPS is a relative percentage for error forgiveness, while AEPS is absolute in milliseconds. Making these numbers larger allows greater variations to be recognized as hits, but too large a variation would make it difficult to distinguish between the wands. After some trial and error, I landed on an EPS of 35 and an AEPS of 200. The wands now work 9 times out of 10.
Check, and… Mate!
Conclusion
This is really only the first step on a journey to bring the magic of Magiquest home. Since our original test, we’ve made some progress extending the physical computing output to control other things using the wand. I’ll write about that in another post.
Another thing I’d like to try is to create multiple lircd.conf entries for each wand and tighten up the error thresholds so we can get soft, medium, and hard waves of the wand registering and handle them differently.
The greatest part about all of this is that my kids have seen how the power of computing can extend into the real world and it has inspired them to think of new ways to use physical computing. They already have plans to make a haunted house for Halloween using the wands. I think I will probably move to Arduino sensors and use the RPi as a central server to handle event processing. That way, we can have more sensors at a lower cost, use the Pi to control physical computing outputs, and track an individual wands interactions to create sequenced events.
When all is said and done, it took me approximately a week of time after the kids went to bed to get this far. Hopefully, my experience helps you up the learning curve and get up and running even more quickly. If you have Magiquest wands and no Magiquest nearby, this opens up some possibilities to bring the magic into your home. If you do, I look forward to seeing the cool things you make happen.