Puzzlebox Code


This is the code I used for my puzzlebox (viewed here), you can copy and paste it and use it as you see fit. Just replace values with the ones you want. It is color coded to separate comments, code, values you want to change, etc. Kudos to Mikal for help with the code!

Start of Code:

#include <PWMServo.h>
#include <SoftwareSerial.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

/* These values should be adjusted according to your needs */
static const int CLOSED_ANGLE = 90; // degrees
static const int OPEN_ANGLE = 2; // degrees
static const float DEST_LATITUDE = 49.889291;
static const float DEST_LONGITUDE = -97.122241;
static const int RADIUS = 500; // meters

/* Fixed values should not need changing */
static const int DEF_ATTEMPT_MAX = 20;
static const int EEPROM_OFFSET = 20;

/* The basic objects needed */
SoftwareSerial 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);
}

/* The Arduino setup() function */
void setup()
{
  /* 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.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;

  /* Copyright notice */
  Msg(lcd, "To Beth", "From: V.", 1500);

  /* Greeting */
  Msg(lcd, "Welcome", "to your", 2000);
  Msg(lcd, "puzzle", "box!", 2000);

  /* 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(" - "); 
  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()
{
  /* 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(); 

  /* 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);
}

No comments:

Post a Comment