summaryrefslogtreecommitdiff
path: root/Puzzle_Box/Puzzle_Box.pde
diff options
context:
space:
mode:
Diffstat (limited to 'Puzzle_Box/Puzzle_Box.pde')
-rw-r--r--Puzzle_Box/Puzzle_Box.pde400
1 files changed, 400 insertions, 0 deletions
diff --git a/Puzzle_Box/Puzzle_Box.pde b/Puzzle_Box/Puzzle_Box.pde
new file mode 100644
index 0000000..481bdbc
--- /dev/null
+++ b/Puzzle_Box/Puzzle_Box.pde
@@ -0,0 +1,400 @@
+/*
+puzzle_box_sample.pde - Sample Arduino Puzzle Box sketch for MAKE.
+COPYRIGHT (c) 2008-2011 MIKAL HART. All Rights Reserved.
+
+This software is licensed under the terms of the Creative
+Commons "Attribution Non-Commercial Share Alike" license, version
+3.0, which grants the limited right to use or modify it NON-
+COMMERCIALLY, so long as appropriate credit is given and
+derivative works are licensed under the IDENTICAL TERMS. For
+license details see
+
+ http://creativecommons.org/licenses/by-nc-sa/3.0/
+
+This source code is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+This code is written to accompany the January, 2011 MAKE article
+entitled "Reverse Geocache Puzzle Box".
+
+This sketch illustrates how one might implement a basic puzzle box
+that incorporates rudimentary aspects of the technology in Mikal
+Hart's Reverse Geocache(tm) puzzle.
+
+"Reverse Geocache" is a trademark of Mikal Hart.
+
+For supporting libraries and more information see
+
+ http://arduiniana.org.
+*/
+
+#include <PWMServo.h>
+#include <NewSoftSerial.h>
+#include <TinyGPS.h>
+#include <EEPROM.h>
+#include <LiquidCrystal.h>
+
+void PowerOff();
+
+#if 0 // Change this to 1 if you have the version 1.0 shield
+
+/* Pin assignments for the version 1.0 shield */
+static const int GPSrx = 2, GPStx = 3;
+static const int LCD_Enable = 11, LCD_RS = 10, LCD_RW = 7;
+static const int LCD_DB4 = 19, LCD_DB5 = 17, LCD_DB6 = 18, LCD_DB7 = 16;
+static const int pololu_switch_off = 15; // Pololu switch control
+static const int servo_control = 9;
+static const int LED_pin = 12;
+
+#else
+
+/* Pin assignments for the version 1.1 shield */
+static const int GPSrx = 4, GPStx = 3; // GPS
+static const int LCD_Enable = 6, LCD_RS = 5, LCD_RW = 7; // LCD
+static const int LCD_DB4 = 16, LCD_DB5 = 17, LCD_DB6 = 18, LCD_DB7 = 19;
+static const int pololu_switch_off = 12; // Pololu switch control
+static const int servo_control = 9; // Servo control
+static const int LED_pin = 2; // The button LED
+
+#endif
+
+uint8_t eye1[8] = {
+ B00000,
+ B00000,
+ B00000,
+ B00000,
+ B00000,
+ B00001,
+ B00011,
+ B00011,
+};
+uint8_t eye2[8] = {
+ B00000,
+ B00111,
+ B01111,
+ B11000,
+ B10001,
+ B00011,
+ B00111,
+ B01110,
+};
+uint8_t eye3[8] = {
+ B00000,
+ B11100,
+ B11110,
+ B00011,
+ B10001,
+ B11000,
+ B11100,
+ B01110,
+};
+uint8_t eye4[8] = {
+ B00000,
+ B00000,
+ B00000,
+ B00000,
+ B00000,
+ B10000,
+ B11000,
+ B11000,
+};
+uint8_t eye5[8] = {
+ B00011,
+ B00011,
+ B00001,
+ B00000,
+ B00000,
+ B00000,
+ B00000,
+ B00000,
+};
+uint8_t eye6[8] = {
+ B01110,
+ B00111,
+ B00011,
+ B10001,
+ B11000,
+ B01111,
+ B00111,
+ B00000,
+};
+uint8_t eye7[8] = {
+ B01110,
+ B11100,
+ B11000,
+ B10001,
+ B00011,
+ B11110,
+ B11100,
+ B00000,
+};
+uint8_t eye8[8] = {
+ B11000,
+ B11000,
+ B10000,
+ B00000,
+ B00000,
+ B00000,
+ B00000,
+ B00000,
+};
+
+
+/* These values should be adjusted according to your needs */
+static const int CLOSED_ANGLE = 80; // degrees
+static const int OPEN_ANGLE = 180; // degrees
+static const float DEST_LATITUDE = 47.512157;
+static const float DEST_LONGITUDE = -119.498119;
+static const int RADIUS = 5; // meters
+
+/* Fixed values should not need changing */
+static const int DEF_ATTEMPT_MAX = 50;
+static const int EEPROM_OFFSET = 100;
+
+/* Eye Animation Steps (12 step program) */
+static const int eyeAnimationSteps[] = { 0, -1, -2, -2, -2, -1, 0, 1, 2, 2, 2, 1, 0 };
+static const int MAX_EYE_STEPS = 13;
+
+int currentEyeAnimationStep = 0;
+long lastLoopTime = 0;
+
+/* Loop Stage */
+int loop_stage = 0;
+
+/*
+
+While waiting for a button press
+1) Blink twice
+2) wait 1 second
+
+*/
+
+
+
+
+/* The basic objects needed */
+NewSoftSerial nss(GPSrx, GPStx);
+LiquidCrystal lcd(LCD_RS, LCD_RW, LCD_Enable, LCD_DB4, LCD_DB5, LCD_DB6, LCD_DB7);
+TinyGPS tinygps;
+int attempt_counter;
+PWMServo servo;
+
+
+
+/* A helper function to display messages of a specified duration */
+void Msg(LiquidCrystal &lcd, const char *top, const char *bottom, unsigned long del)
+{
+ lcd.clear();
+ lcd.setCursor(0, 0);
+ lcd.print(top);
+ lcd.setCursor(0, 1);
+ lcd.print(bottom);
+ delay(del);
+}
+
+// __|XX|__
+// __|XX|__
+// Location = 0 centers the eye.
+void drawEye(int location)
+{
+ lcd.clear();
+
+ location = location + 2;
+
+ for (int i = 0; i < 4; i++) {
+ lcd.setCursor(location + i, 0);
+ lcd.write(i);
+ }
+
+ for (int i = 0; i < 4; i++) {
+ lcd.setCursor(location + i, 1);
+ lcd.write(i + 4);
+ }
+}
+
+void stepEyeAnimation() {
+ //if (currentEyeAnimationStep < 12) {
+ drawEye(eyeAnimationSteps[currentEyeAnimationStep]);
+ currentEyeAnimationStep++;
+ currentEyeAnimationStep = currentEyeAnimationStep % 12;
+ //}
+}
+
+void blinkEye() {
+ // Eyes on
+ digitalWrite(LED_pin, HIGH);
+ delay(100);
+
+ // Off
+ lcd.noDisplay();
+ digitalWrite(LED_pin, LOW);
+ delay(100);
+
+ // Back on
+ lcd.display();
+ digitalWrite(LED_pin, HIGH);
+}
+
+/* The Arduino setup() function */
+void setup()
+{
+ /*Turn on switch LED*/
+ pinMode(LED_pin, OUTPUT);
+ digitalWrite(LED_pin,HIGH);
+
+ /* attach servo motor */
+ servo.attach(servo_control);
+
+ /* establish a debug session with a host computer */
+ Serial.begin(115200);
+
+ /* establish communications with the GPS module */
+ nss.begin(4800);
+
+ /* establish communication with 8x2 LCD */
+ lcd.createChar(0, eye1);
+ lcd.createChar(1, eye2);
+ lcd.createChar(2, eye3);
+ lcd.createChar(3, eye4);
+ lcd.createChar(4, eye5);
+ lcd.createChar(5, eye6);
+ lcd.createChar(6, eye7);
+ lcd.createChar(7, eye8);
+ lcd.begin(8, 2); // this for an 8x2 LCD -- adjust as needed
+
+ /* Make sure Pololu switch pin is OUTPUT and LOW */
+ pinMode(pololu_switch_off, OUTPUT);
+ digitalWrite(pololu_switch_off, LOW);
+
+ /* make sure motorized latch is closed */
+ servo.write(CLOSED_ANGLE);
+
+ /* read the attempt counter from the EEPROM */
+ attempt_counter = EEPROM.read(EEPROM_OFFSET);
+ if (attempt_counter == 0xFF) // brand new EEPROM?
+ attempt_counter = 0;
+
+ /* increment it with each run */
+ ++attempt_counter;
+
+ /* Greeting */
+ Msg(lcd, "Hello", "Jesse!", 1500);
+ Msg(lcd, "Welcome", "to your", 1500);
+ Msg(lcd, "puzzle", "box!", 1500);
+
+ /* Game over? */
+ if (attempt_counter >= DEF_ATTEMPT_MAX)
+ {
+ Msg(lcd, "Sorry!", "No more", 2000);
+ Msg(lcd, "attempts", "allowed!", 2000);
+ PowerOff();
+ }
+
+ /* Print out the attempt counter */
+ Msg(lcd, "This is", "attempt", 2000);
+ lcd.clear();
+ lcd.setCursor(0, 0);
+ lcd.print(attempt_counter);
+ lcd.print(" of ");
+ lcd.print(DEF_ATTEMPT_MAX);
+ delay(2000);
+
+ /* Save the new attempt counter */
+ EEPROM.write(EEPROM_OFFSET, attempt_counter);
+
+ Msg(lcd, "Seeking", "Signal..", 0);
+}
+
+/* The Arduino loop() function */
+void loop()
+{
+
+ if (millis() - lastLoopTime > 200) {
+ stepEyeAnimation();
+ lastLoopTime = millis();
+ }
+
+ /* Has a valid NMEA sentence been parsed? */
+ if (nss.available() && tinygps.encode(nss.read()))
+ {
+ float lat, lon;
+ unsigned long fix_age;
+
+ /* Have we established our location? */
+ tinygps.f_get_position(&lat, &lon, &fix_age);
+ if (fix_age != TinyGPS::GPS_INVALID_AGE)
+ {
+ /* Calculate the distance to the destination */
+ float distance_meters = TinyGPS::distance_between(lat, lon, DEST_LATITUDE, DEST_LONGITUDE);
+
+ /* Are we close?? */
+ if (distance_meters <= RADIUS)
+ {
+ Msg(lcd, "Access", "granted!", 2000);
+ servo.write(OPEN_ANGLE);
+ }
+
+ /* Nope. Print the distance. */
+ else
+ {
+ lcd.clear();
+ lcd.setCursor(0, 0);
+ lcd.print("Distance");
+ lcd.setCursor(0, 1);
+ if (distance_meters < 1000)
+ {
+ lcd.print((int)distance_meters);
+ lcd.print(" m.");
+ }
+
+ else
+ {
+ lcd.print((int)(distance_meters / 1000));
+ lcd.print(" km.");
+ }
+ delay(4000);
+ Msg(lcd, "Access", "Denied!", 2000);
+ }
+
+ PowerOff();
+ }
+ }
+
+ /* Turn off after 5 minutes */
+ if (millis() >= 300000)
+ PowerOff();
+}
+
+/* Called to shut off the system using the Pololu switch */
+void PowerOff()
+{
+ Msg(lcd, "Powering", "Off!", 2000);
+ lcd.clear();
+
+ /*Turn off switch LED*/
+ pinMode(LED_pin, OUTPUT);
+ digitalWrite(LED_pin,LOW);
+
+ /* Bring Pololu switch control pin HIGH to turn off */
+ digitalWrite(pololu_switch_off, HIGH);
+
+ /* This is the back door. If we get here, then the battery power */
+ /* is being bypassed by the USB port. We'll wait a couple of */
+ /* minutes and then grant access. */
+ delay(120000);
+ servo.write(OPEN_ANGLE); // and open the box
+
+ /* Reset the attempt counter */
+ EEPROM.write(EEPROM_OFFSET, 0);
+
+ /* Leave the latch open for 10 seconds */
+ delay(10000);
+
+ /* And then seal it back up */
+ servo.write(CLOSED_ANGLE);
+
+ /* Exit the program for real */
+ exit(1);
+}
+