Question Box Puzzle
by davewking in Circuits > Microcontrollers
16624 Views, 19 Favorites, 0 Comments
Question Box Puzzle
The things I'll cover in this tutorial:
1. connecting a bunch of LEDs to a project
2. integrating a toy into a project
3. The coolness of Shapelock
4. Running motors, servos and steppers (using the motor shield)
5. Connecting an Xbee if you don't have room for the shield
6. Remote Controlling your application from a web application over Xbee
7. Connecting to I2C devices (LCD, Wiimote, another Arduino)
8. Connecting to a Parallax Knock sensor
9. Making sound with a speaker
10. Getting input from buttons
11. General Puzzle trickery
For the puzzles I thought I'd all for Myst type puzzles (you've been given a box, figure it out), and Mario style (generally you know what you're supposed to do, sometimes you even get a chance to practice parts of it before doing it for real).
The attached video shows a "level" where you have to put the Contra code in on buttons that look kind of like a NES controller.
Stuff Used in This Project
One goal of this project, as it progressed, was to complete overengineer. I think I did that.
1 - Unfinished wooden box from a craft store
7 - small push buttons
1 - Arcade push button
1 - 5-way button
1 - Serial/I2C LCD
32 - LEDs
1 - resistor
1 - GPS
1 - Keypad
1 - Parallax Knock sensor
1 - Xbee
1 - speaker
4 - Nixie Tubes
1 - Nixie Tube Shield
1 - Adafruit Motor Shield
1 - Buzz Lighyear gun disc gun
1 - Stepper
1 - Servo
1 - Pack of Shape Lock
1 - Wiimote Nunchuck
1 - Xbee shield from SeeedStudio
1 - Arduino Uno
1 - Arduino Mega (Sparkfun free day goodness)
A few random screws
Lots of random wires
Lots of batteries
LEDs
"Coin" Shooter
Taking the Gun Apart
Putting It in the Box
Running the Gun
To run a servo with the motor shield, it's exactly like running a servo connected directly to an Arduino. In fact all the motor shield does is give you a convenient 3 pin header to connect to. GOTCHA - According to the FAQ Arduino Pin 9 goes to Servo Header 1 and Pin 10 goes to Servo Header 2. On mine it was backwards (Pin 9 -> Servo Header 2, Pin 10 -> Servo Header 1).
To control a motor you basically set it up and tell it what port it's connected to, set the speed and direction and let it go. If you don't connect external power to the shield motors won't work (even though steppers do).
Check out the code attached to this step.
Note: obviously this could be done without the motor shield, but I had one around and was using an Arduino Mega, so I had plenty of pins.
Downloads
XBee
Remember when you're connecting an serial IC
IC Arduino
RX -> TX
TX -> RX
When I soldered the headers on I could have done it two different ways. I could have just soldered up a female header to fit on the pins that normally plug into the male pins that normally hook into the Arduino. The second way is to solder on new male headers on side of the shield right next to the Xbee pins. Doing it that way made it so I didn't have to figure out the pins (I was having a hard time finding documentation on it). The problem doing it this way is that the Arduion is working on 5v and the Xbee shield is wanting 3.3v. From what I hear it's 5v tolerant though, and for me it's working fine so far (it may reduce the life of the Xbee though).
GPS and Other Low Voltage Serial Devices
My project also has a GPS connected. Rather than give specifics on my GPS module (especially since it's been discontinued by Sparkfun) Ive decided to just give general help on hooking up a GPS to an Arduino. You'll want to look at the datasheet for your particular GPS before connecting. The things you want to pay the most attention to are: what the pins are, and what voltage they take. Many GPS units only tolerate 3.3v, and an Arduino works on 5v (note: there are Arduino's that work on 3.3v as well, like the Arduino Pro 3.3v). The Arduino One (and the Duemilanove) has a 3.3v power output on it. You should be able to use that to feed power to the device. You also need to protect the serial connection to the device. To do this use a Logic Level Converter, like this one from Sparkfun . This is really easy to connect, just:
Arduino Logic Level Converter
5v -----------> HV
3.3v --------> LV
RX ----------> RXI
TX -----------> TXI
GND --------> GND
then to connect to the GPS unit (or any other 3.3v serial device)
Logic Level Converter GPS
RXO ---------------------------> TX
TXO ----------------------------> RX
Direct Connections from Arduino To GPS
Arduino GPS
3.3 v -------> Vin
GND ------> GND
Translating what's coming from the Arduino to use it for whatever you're trying to do you'll need to read the datasheet or other documents more. If you're worried about getting all this working you may want to find a GPS unit where someone's worked out all the code and has a tutorial for it. If I were going to get one today I'd probably go with this unit (and tutorial) http://www.sparkfun.com/tutorials/176
Remote Control
Install Sinatra
Sinatra is a web "micro-framework". This basically means it gives you what you need to make web applications without a lot of extra stuff. Don't get me wrong, I love Rails, but for something small like this it seems like overkill. Sinatra is great for something like this. This app is built to run in Windows, but the only thing that would need to change to run in Linux is some of the serial stuff. First off, installing Ruby and Sinatra:
Download the newest Ruby 1.8 installer , I would prefer 1.9, but it looks like the serial libraries aren't being maintained and don't work with 1.9
Open a command prompt and type "gem install serialport" then type "gem install sinatra". One quirk with Ruby 1.8 is that it has a problem with rack, which get's installed with Sinatra. To fix this do "gem uninstall rack" (tell it to uninstall the binary, and uninstall even though other gems are dependent on it), then "gem install rack --version '1.2.0'. You should have all the stuff installed you need to run the program. I like to use thin to run my Ruby stuff, it's a pretty fast, easy to use Ruby web application server. To make this happen type "gem install thin" in the command prompt.
The Sinatra Application
This get's the required libraries to do Sinatra and Serial
require 'serialport'
require 'sinatra'
This is where you configure the serial port number and speed
arduinoSerialPort = 'COM22'
arduinoSerialPortSpeed = 9600
This creates the serial port object. It's not really important to understand what this means if you're not a programmer, but because we made sp equal to the SerialPort.new then sp.write is how we'll output stuff to the serial port
sp = SerialPort.new(arduinoSerialPort, arduinoSerialPortSpeed, 8, 1, SerialPort::NONE)
Normally in Sinatra you'd have a separate file in a separate directory with your template of your page. I wanted to have this app all in the same file, so I made a really simple template below. In the "routes" (the pages in URL) I replace the word BODY below with the HTML for that page I want to display.
htmlCode = "<html><head></head><body>BODY</body></html>"
Here's the first route. get just means it's a get request, which is the type of request that happens when you type in a URL or click on a link. The '/' means the homepage basically (so if I type in http://servername/ then I'll get to this code). Everything between the "do" and the "end" is executed as part of this page. Here all I do is have a body variable and I assign it a bunch of HTML for links to other pages. There are three links below one to /shootstuff, one to /lightallleds, and one for /coinsound. The last thing I do is replace the BODY in htmlCode (from above) with the HTML I defined here. Whatever the last thing is in the route get's returned and rendered as the page.
get '/' do
body = "<a href=\"/shootstuff\">Shoot Stuff</a><br />"
body += "<a href=\"/lightallleds\">Light All LEDs</a><br />"
body += "<a href=\"/coinsound\">Mario Coin Sound</a><br />"
htmlCode.gsub("BODY", body)
end
Here's another route, this one's what you get if you hit http://servername/shootstuff. The first thing it does is send a "1" over serial to my coin box. Then it just has a link to go back to the last page.
get '/shootstuff' do
sp.write "1"
body = "<b>Stuff Should be shooting</b></br>"
body += "<a href=\"/\">Return to Actions</a><br />"
htmlCode.gsub("BODY", body)
end
Similar to the route above, this one fires when someone goes to http://servername/lightallleds. It sends a 2 over serial and give a link back to the index page
get '/lightallleds' do
sp.write "2"
body = "<b>All LEDs Should Be Lit</b></br>"
body += "<a href=\"/\">Return to Actions</a><br />"
htmlCode.gsub("BODY", body)
end
This route is similar to the two above. It fires when someone hits http://servername/coinsound, writes a "3" over serial and has a link back to the index page
get '/coinsound' do
sp.write "3"
body = "<b>Coin Sound Should Have Sounded</b></br>"
body += "<a href=\"/\">Return to Actions</a><br />"
htmlCode.gsub("BODY", body)
end
Downloads
Starting Up the Sinatra Application
I2C Devices
In my puzzle box I used 3 I2C devices:
1. Web4robot Serial/I2C LCD
2. Wiimote Nunchuck
3. An Arduino Uno with a Nixie tube shield (I used this as the timer for the game).
I2C has two lines, SDA and SCL. To connect I2C devices simply connect the SDA on the Arduino to all the SDA's on the devices and connect the SCL on the Arduino to all the SCL's on all the other devices. To get good reliability it's good to connect a 1.5 K Ohm resistor between 5v and each of the lines (a pull-up resistor).
Arduino has a library called Wire that's used to communicate between I2C devices. Each I2C bus should have one master and can have up to 128 devices total. In my case I set up the Arduino Mega as the master, and everything was a slave (including the Arduino Uno that was running the Nixie tubes).
Care should be taken when connecting I2C devices to see what voltages they can tolerate. Like serial devices, many I2C devices can only tolerate 3.3v, instead of 5v, which is what many Arduinos work off of. You can use the same logic level converter I mentioned when I was talking about serial devices on I2C http://www.sparkfun.com/products/8745
A couple of really good I2C tutorial are:
http://hacknmod.com/hack/how-to-connect-multiple-arduino-microcontrollers-using-i2c/
http://www.arduino.cc/playground/Learning/I2C
Web4Robot LCD
Minimally to get this LCD working:
1. connect the SDA pin on the LCD to the SDA pin on the Arduino
2. connect the SCL pin on the LCD to the SCL pin on the Arduino
3. connect Power on the LCD to 5v on the Arduino
4. connect Gnd on the LCD to Gnd on the Arduio
I used this library to connect t the LCD http://www.arduino.cc/playground/Code/LCDi2c . It works really well, all you need to do to start sending data over to it is:
Include the libraries
#include <Wire.h>
#include <LCDI2C.h>
Set up the options for your particular LCD
int g_rows = 4;
int g_cols = 20;
LCDI2C lcd = LCDI2C(g_rows,g_cols,0x4C,1);
In setup() you need to initialize the LCD (clears it, sets up the curser, etc)
lcd.init();
Now you're ready to use it
lcd.print("Stuff to print to the LCD")
Move the Curser Down a row
lcd.setCursor(1,0);
Clear the LCD
lcd.clear();
There are many other functions such as off() on(), cursor functions, the ability to draw graphs, etc.
Wiimote Nunchuck
There are already many tutorials on connecting to and communicating with a Wiimote nunchuck. I'll give some tips on the way I did it and reference these other tutorials
I used this breakout http://www.sparkfun.com/products/9281 so I didn't have to cut off the end of the nunchuck cord. The PCB just slides into the end of the Wiimote nunchuck connector. All you need to do is solder on headers and connect
Arduino Nunchuck Breakout
5v -------------> PWR (I think 3.3v works as well, in fact it may be that it prefers 3.3v, but its 5v tolerant)
GND ---------> GND
SDA ----------> D
SCL ----------> C
After that you just use a library to communicate with it. Here's the tutorial and the library:
http://todbot.com/blog/2008/02/18/wiichuck-wii-nunchuck-adapter-available/
nunchuck_init() — init a nunchuck connected to an Arduino
nunchuck_get_data() — get a data packet from the Nunchuck
Then you can get at the data packet using various helper functions like:
nunchuck_accelx() — get X-axis acceleration
nunchuck_zbutton() — get Z-button state
Here's another good tutorial:
http://www.windmeadow.com/node/42
Second Arduino With I2C
Arduino Mega Arduino Uno
SDA -----------------> SDA
SCL -----------------> SCL
GND ----------------> GND
Vin -------------------> Vin (this may not be necessary . . . anyone know for sure).
The master (Arduino Mega) communicates the same way it normally does. The slave (Arduino Uno) is set up like this in setup():
Wire.begin(4); // join i2c bus with address #4
Wire.onReceive(receiveEvent); // register event
then you just need to define the recieveEvent (this one is from the example that comes with the Arduino IDE (it's under Wire in Examples)
void receiveEvent(int howMany)
{
while(1 < Wire.available()) // loop through all but the last
{
char c = Wire.receive(); // receive byte as a character
Serial.print(c); // print the character
}
int x = Wire.receive(); // receive byte as an integer
Serial.println(x); // print the integer
}
For mine I just have the master send one thing that tells the timer to start.
Here is a really good tutorial on connecting multiple Arduinos
http://hacknmod.com/hack/how-to-connect-multiple-arduino-microcontrollers-using-i2c/
Parallax Sound Impact Sensor
Arduino Impact Sensor
GND --------> GND
5v ------------> 5v
SIG ----------> Arduino Digital or Analog Pin of you Choice
The board has a little pot that you can adjust to tell it how loud the sound has to be to trigger the signal.
To read the signal you have a couple of choices. You can read it like a button (check for high on the pin, then you know it's fired), or you can use one of the Arduino interrupts. I wanted to use this to trigger hitting the bottom of the box to spit out a "coin". I had mixed success with this because of how loud the little motor was that shot stuff out.
Interrupts are easy to do. All you do is define the interrupt in setup like this:
attachInterrupt(0, smack, RISING);
0 means that it's on digital pin 2. smack is the name of the function it will call when the interrupt happens. You can also define when it will fire, I said RISING, which means it will fire whenever the voltage is going up (in other words the SIG pin just went high).
Then you just need to define smack
void smack()
{
//do whatever you want here, I played the mario coin sound and shot out a coin
}
Buttons
To connect a button, I do this:
Arduino Button
5v -------------> one of the button leads
button Pin --> other button lead
Set the pin to input
pinMode(buttonPin, INPUT);
Do a digital read on the pin
int reading = digitalRead(buttonPin);
That's the basics, but to get an accurate reading, especially if you're trying to detect sequences you're going to want to do something like the debounce example included with the Arduino IDE. Basically this checks to see if the same button is still pressed each time through the loop, if it's changed since the last read then it doesn't count the reading until the debounce delay has passed (a few milliseconds). This is because button readings can be a little squirly in the transition from on to off and can look like multiple button presses.
Playing Music
Arduino Speaker
GND -----------------------> one speaker lead/wire
digital or analog pin ---> other speaker lead/wire
A couple of revs ago the Arduino IDE started to include the tone library. Here's the basics (this will play the coin sound from Mario Bros), this is taken from the example that comes with the IDE:
Include the pitches.h header file
#include "pitches.h"
// notes in the melody:
int melody[] = {
NOTE_B5, NOTE_E6};
// note durations: 4 = quarter note, 8 = eighth note, etc.:
int noteDurations[] = {
16, 2 };
void playSound() {
// iterate over the notes of the melody:
for (int thisNote = 0; thisNote < 2; thisNote++) {
// to calculate the note duration, take one second
// divided by the note type.
//e.g. quarter note = 1000 / 4, eighth note = 1000/8, etc.
int noteDuration = 1000/noteDurations[thisNote];
tone(8, melody[thisNote],noteDuration);
// to distinguish the notes, set a minimum time between them.
// the note's duration + 30% seems to work well:
//int pauseBetweenNotes = noteDuration * 1.30;
delay(noteDuration + 10);
// stop the tone playing:
noTone(8);
}
}
Puzzle Stuff
Contra Code (Up Up Down Down Left Right Left Right B A)
I've got this working a couple of different ways. On one side of my puzzle box I have buttons that look somewhat like a NES controller. One way to have the user input the code is to use those buttons. Many times in video games you think you see the solution, but really it's slightly off what you think. If you notice the button on the left is different than the others. It's actually a 5 way button from Parallax. So, it moves slightly left, right, up and down. The way this button is set it's looks like maybe I didn't have enough buttons, and since it doesn't stick out far you have to use your fingernail to make it work. Like video games though when something seems a little different it's something you should pay attention to.
Discs giving devices to help with other puzzles or prizes
The way these discs are, you can connect pieces of paper, or other small things to the discs. Ideas for prizes include things like cash, gift certificates, or other paper prizes. Other small things may include wire (as described in the next section) that could be used to solve future puzzles. Hints could also be included on the paper shot out on a disc.
Stepper Blocking Access to Flexible Resistor
Notice in the attached picture that there's a stepper with a piece of card board blocking access to a flexible resitor (there's a hole in the side of the box that would allow you to access it otherwise. To solve the puzzle you have to realize that something moves the stepper (i.e. a reading from the wiimote nunchuck accelerometer, a reading from the GPS, button presses, a combination of all of them). This one feels kind of like a Myst puzzle to me, hand them the box, they starting doing things and hear the stepper moving and then figure out what they need to do. I like the idea of getting a piece of wire inside of one of the discs after solving one of the previous puzzles and using that to solve this one.
Sound Impact Sensor vs Buttons
One thing I really like about the sound impact sensor is that it's basically like a hidden button. Getting someone to push figure out there's something inside detecting an impact can make things interesting.
Combining Multiple Reading
Getting people to do multple things at the same time can be fun. Making them hold the box in a certain way and push a button that's awkward to push (or multiple buttons) can be fun
Location Stuff
This box can basically work like a reverse Geocache box. It can try to get the person to a location. Once they get there you could connect to the device via Xbee and send data over to the screen to help with other puzzles