Geocracher Box Code

 #include <TinyGPS.h> 
#include <NewSoftSerial.h> 
#include <LiquidCrystal.h> 
#include <SoftwareServo.h> 
#include <math.h> 
#include <EEPROM.h> 
#include <avr/interrupt.h> 
/* 
* Dieser Code ist in der public domain. 
*/


#define RXPIN 2 
#define TXPIN 3 
#define POLULUPIN 5 
#define LIDPIN 4 
#define DRAWERPIN 6 
//#define IRPINA 0 
#define MAGPINA 0
#define STATUSPIN 13

#define EARTH_RADIUS 6378.1f 
#define DEG_TO_RAD 0.0174532925f 

//Ziel 1 Längengrad eingeben Dezimal
#define TOFINO_LAT 49.11111f
// Ziel 1 Breitengrad eingeben dezimal
#define TOFINO_LON 007.44444f

// Ziel 2 Längengrad dezimal
#define XXXX_LAT 49.8888f 
//Ziel 2 Breitengrad
#define XXXX_LON 009.8888f



// HEimgrade eingeben
#define HOME_LAT 49.0f 
#define HOME_LON 9.0f 

#define GPS_DELAY_MS 60000 
/*Hier maximale Distanz eingeben z.B. 500 m hier */
#define DIST_THRESHOLD 0.5f 

#define ADDR_TRIES 0 
#define ADDR_STAGE1 1 
#define ADDR_STAGE2 2 
#define ADDR_BACKDOOR 3 
// Bezeichnungen der Ansteuerungen
TinyGPS gps; 
SoftwareServo drawerServo; 
SoftwareServo lidServo; 
/* NewSoftSerial zur GPS Kommunikation */
NewSoftSerial nss(RXPIN,TXPIN); 
// LCD(RS,(RW),enable, D1,D2,D3,D4 LCD ansteuerung
LiquidCrystal lcd(7, 8, 9, 10, 11, 12); 
unsigned long waitUntil; 
int attemptsRemaining; 

void setup() { 
  pinMode(POLULUPIN, OUTPUT);   // erklärt Pololupin Schalter als output

  pinMode (STATUSPIN, OUTPUT);

  Serial.begin(9600); 
  Serial.print( "In setup.\n" );  
  nss.begin(4800); 
  lcd.begin(2,16); 
  lcd.clear(); 
  lcd.setCursor(0,0); 

  /* Überprüfe wieviele Versuche bisher getätigt wurden*/
  attemptsRemaining = EEPROM.read(ADDR_TRIES); 
  Serial.print( "Versuche: "); 
  Serial.print( attemptsRemaining, DEC ); 
  Serial.print("\n"); 
  if ( attemptsRemaining == 0xFF ) 
  { 
    // ERstmaliger Durchlauf
    Serial.print( "Initialisiere EEPROM\n" ); 
    initializeEEPROM(); 
    Serial.print( "Verschliesse lid\n" ); 
    lockLid(); 
    shutdown(); 
  } 
  lcd.print( " ...Lade..." );
  delay(5000);
  lcd.clear();
  lcd.print("Hallo Cacher!");
  delay(3000);
     lcd.clear();
     lcd.setCursor(0,0); 
     lcd.print( "Deine Trollibox" ); 
     lcd.setCursor(0,1); 
     lcd.print( "fuehrt Dich...");
  delay(3000);
   lcd.clear();
      lcd.setCursor(0,0); 
      lcd.print( "Zu Deinem Cache..." ); 
      lcd.setCursor(0,1); 
      lcd.print( "Geburtstagscache!");
  delay(3000);

 
   lcd.clear();
      lcd.setCursor(0,0); 
      lcd.print( "1.Ziel A9/A7" ); 
      delay(3000);

   lcd.clear();
      lcd.setCursor(0,0); 
      lcd.print( "Ziel 2: A12/A14" ); 
      lcd.setCursor(0,1); 
      lcd.print( "B9/B10");
      delay(5000);
  lcd.clear();
      lcd.setCursor(0,0); 
      lcd.print( "schwarzer Kater?" ); 
      lcd.setCursor(0,1); 
      lcd.print( "  hier Rätsel?");
 delay(5000);
     lcd.clear();
      lcd.setCursor(0,0); 
       lcd.print( attemptsRemaining );
      lcd.print( " Versuche noch" ); 
      lcd.setCursor(0,1); 
      lcd.print( "Viel Spass!");
  delay(5000);


  } 

void loop() 
{
  digitalWrite( STATUSPIN, HIGH ); //?
  waitUntil = millis() + 2000; 
  if ( waitForBackdoor() ) 
  { 
   /* Wenn wir hierhin kommen ist die Hintertür getriggert worden */
   unlockLid(); 
   /*eingefügt*/
   {for ( int i = 0; i < 3; i++ )
      {
         digitalWrite( STATUSPIN, LOW );
         delay(1000);
         digitalWrite( STATUSPIN, HIGH );
         delay(1000);
      }
  }digitalWrite( STATUSPIN, LOW );
   shutdown(); 
   }
  
  else if ( EEPROM.read( ADDR_BACKDOOR ) != 0 ) 
  { 
   // löscht die Hintertür flag, wenn sie von einem voherigen Lauf gesetz wurde
   // dies ermöglicht die Hintertür mehrmals zu öffnen
   lockLid(); 
   EEPROM.write( ADDR_BACKDOOR, 0 ); 
   shutdown(); 
  } 

  if ( attemptsRemaining <= 0 ) 
  { 
   lcd.clear(); 
   lcd.setCursor(0,0); 
   lcd.print( "Ende!" ); 
   delay(3000); 
   lcd.clear(); 
   lcd.setCursor(0,0); 
   lcd.print( "Zurueck zu" ); 
   lcd.setCursor(0,1); 
   lcd.print( "Geokracher" ); 
   delay(3000); 
   shutdown(); 
  } 
  // ... Normaler Ablauf beginnt hier 
  bool stage1Complete = (EEPROM.read(ADDR_STAGE1) != 0); 
  bool stage2Complete = (EEPROM.read(ADDR_STAGE2) != 0); 

  waitUntil = millis() + 120000; 
  if ( !stage1Complete ) 
  { 
  lcd.clear(); 
  lcd.setCursor(0,0); 
  lcd.print( "Suche" ); 
  lcd.setCursor(0,1); 
  lcd.print( "Signal" ); 
  float stage1Dist = 100.0f; 
  if ( distanceTo( TOFINO_LAT, TOFINO_LON, &stage1Dist ) ) 
  { 
    /* Wir landen hier , wenn wir eine gültige Distanz haben */
    if ( stage1Dist < DIST_THRESHOLD ) 
    { 
      EEPROM.write( ADDR_STAGE1, 0xFF ); 
      lcd.clear(); 
      lcd.setCursor(0,0); 
      lcd.print( "Etappe 1" ); 
      lcd.setCursor(0,1); 
      lcd.print( "geschafft!"); 
      unlockDrawer(); 
    } 
    else
    { 
     /* Sorry, try again */
     decrAttemptsRemaining(); 
     lcd.clear(); 
     lcd.setCursor(0,0); 
     lcd.print( "Kein Zugriff!" ); 
     lcd.setCursor(0,1); 
     lcd.print( "noch " ); 
     lcd.print( attemptsRemaining ); 
     delay(20000); 
     lcd.clear(); 
     lcd.setCursor(0,0); 
     lcd.print( "ausser Bereich" ); 
     Serial.begin(9600); 
     Serial.print( "ausser Bereich" );  
     lcd.setCursor(0,1); 
     lcd.print( stage1Dist ); 
     lcd.print( " km" ); 
    } 
    delay(20000); 
    shutdown(); 
    } 
    else
    { 
     /* kein GPS Signal */
     failNoSignal(); 
    } 
  } 
  else if ( !stage2Complete ) 
  { 
   /* Dieser Codeteil ist unterschiedlich genug zu Stage 1 
      to make it awkward to write in a loop. */
   lcd.clear(); 
   lcd.setCursor(0,0); 
   lcd.print( " Suche " ); 
   lcd.setCursor(0,1); 
   lcd.print( " Signal" ); 
   float stage2Dist = 100.0f; 
   if ( distanceTo( XXXX_LAT, XXXX_LON, &stage2Dist ) ) 
   { 
    if ( stage2Dist < DIST_THRESHOLD ) 
    { 
     EEPROM.write( ADDR_STAGE2, 0xFF ); 
     lcd.clear(); 
     lcd.setCursor(0,0); 
     lcd.print( "Toll " ); 
     lcd.setCursor(0,1); 
     lcd.print( "Du bist am Ziel!" ); 
     unlockLid(); 
    } 
    else
    { 
     decrAttemptsRemaining(); 
     lcd.clear(); 
     lcd.setCursor(0,0); 
     lcd.print( "kein Zugriff!" ); 
     lcd.setCursor(0,1); 
     lcd.print( "noch" );
     lcd.print( attemptsRemaining ); 
        lcd.print( "Versuche" );
     delay(20000); 
     lcd.clear(); 
     lcd.setCursor(0,0); 
     lcd.print( "ausser Bereich" ); 
     lcd.setCursor(0,1); 
     if ( stage2Dist < 20 ) 
     { 
      lcd.print( "? km" ); 
     } 
     else if ( stage2Dist < 100 ) 
     { 
      lcd.print( "?? km" ); 
     } 
     else if (stage2Dist < 1000 ) 
     { 
      lcd.print( "??? km" ); 
     } 
     else
     { 
     lcd.print( "???? km" ); 
     } 
    } 
    delay(15000); 
    shutdown(); 
   } 
   else
   { 
    failNoSignal(); 
   } 
  } 
  else
  { 
   // Game Over! 
   lcd.clear(); 
   lcd.setCursor(0,0); 
   lcd.print( " Spielchen " ); 
   lcd.setCursor(0,1); 
   lcd.print( " ist vorbei!" ); 
   delay(10000); 

   shutdown(); 
  } 
 // wir sollten nie hierhinkommen, aber wenn doch... 
  shutdown(); 
 } 
/* 
 * versucht die Distanz zu berechnen Länge und Breite lat lon, über GPS sensor. 
 * Returns true iff the sensor returned a valid reading. 
 * On successful return, result will hold the great circle distance to the specified point. 
 */

bool distanceTo( float targetLat, float targetLon, float* result) 

 int numFixes = 0; 
 while ( millis() < waitUntil ) 
 { 
  if (nss.available()) 
  { 
   int c = nss.read(); 
   if (gps.encode(c)) 
   { 
    unsigned long fix_age; 
    float flat,flon; 
    gps.f_get_position(&flat, &flon, &fix_age); 
    if ( fix_age > 60000 ) continue; 
    if ( numFixes < 5 ) 
    { 
     numFixes++; 
     continue; 
    } 
    flat = fabs(flat); 
    flon = fabs(flon); 

    float dist = gcd( flat * DEG_TO_RAD, flon * DEG_TO_RAD, targetLat * DEG_TO_RAD, targetLon * DEG_TO_RAD ); 
                Serial.print( "Current lat: " );
                Serial.print( flat, 6 );
                Serial.print( "\nCurrent lon: " );
                Serial.print( flon, 6 );
                Serial.print( "\nTarget lat: " );
                Serial.print( targetLat, 6 );
                Serial.print( "\nTarget lon: " );
                Serial.print( targetLon, 6 );
                Serial.print( "\nDistance: " );
                Serial.print( dist );
    *result = dist; 
    return true; 
   } 
  } 
 } 
 return false; 


/* 
 * Das ist eine super Behelfslösung, um alle Pin-Wechsel-Interrupts auszuschalten.
 * Es beendet alle seriellen Aktivitäten, um einen brauchbaren Zustand nach der Funktion zu erreichen.
 * Das Restore des Arduino ist nur über Stromzufuhrbeenden zu erreichen, was an USB oder BatteriePack schwierig ist.
 * Russ benutzte dieses als ein hack, damit NewSoftSerial and SoftwareServo schön zusammenspielen aber
 * er denkt daß die neuste Version der NewSoftSerial hat einen sauberereren Weg.
 */
void disablePCI() 

 PCICR = 0; 
 PCMSK2 = 0; 
 PCMSK0 = 0; 
 PCMSK1 = 0; 

   
// Blockiert bis Infrarot code empfangen 
// oder der Ablauf des Programmes wird erreicht (wechselt zu wahr, wenn der Code empfangen ist) 
// liest die waitUntil Variable, um zu definieren wie lange auf die HIntertür gewartet wird. 

bool waitForBackdoor() 
{
  // 10 second wait
  unsigned long backdoorWaitUntil = millis() + 10000;
  while ( millis() < backdoorWaitUntil )
  {
    if ( analogRead( MAGPINA ) > 512 )
    {
      return true;
    }
  }
  return false;
}
/*Hier der Infrarotcode

uint8_t pulseCount = 0; 
unsigned long pulseStart; 
bool pulseActive; 

while ( millis() < waitUntil ) 
 {  int irval = analogRead(IRPINA); 
    if (irval < 0x0F) 
  {
    // Impuls gestarted 
    if ( !pulseActive ) 
   {
    pulseActive = true; 
    pulseStart = millis(); 
   } 
  } 
  else
  { 
   if ( pulseActive ) 
   { 
    pulseActive = false; 
    unsigned long duration = millis() - pulseStart; 
    if ( (duration > 5) && (duration < 15) ) 
    { 
     pulseCount++; 
    } 
   }
   } 
  }
 // 4 oder mehr Impulse in einer definierten Zeitdauer spezifiziert durch waitUntil öffnet die Hintertür
 if (pulseCount >= 4) 
 { 
  EEPROM.write( ADDR_BACKDOOR, 0xFF ); 
  return true; 
 } 
 return false; 
}*/

/* 
* Diese Funkuion arbeitet nur, wenn der Arduino an einem Batteriepack angeschlossen ist.
* An USB steckt er in einer undefierten Schleife am Ende?
*/
void shutdown() 

 lcd.clear(); 
 lcd.setCursor(0,0); 
 lcd.print( "Ich schalte ab."); 
 delay(2000); 
 digitalWrite( POLULUPIN, HIGH ); 


void unlockDrawer() 

// disablePCI  muss vor jeder servo Aktivität aufgerufen werden!  
 nss.end();
 disablePCI(); 
 drawerServo.attach(DRAWERPIN); 
 // Dieses "stepping" ist nur notwendig um die Limitation der Software Servo library zuerreichen, 
 // welche eine überflüssige Version der Servo library ist 
 // Russ diagnostizierte das Problem mit dem Pin Wechsel der Interrupts und hat keine Zeit gehabt es zu ersetzen.
 for ( int i = 5; i <= 100; i++ ) 
 { 
 drawerServo.write(i); 
 SoftwareServo::refresh(); 
 delay(15); 
 } 
 drawerServo.detach(); 

// neu1
void lockDrawer() 

// disablePCI  muss vor jeder servo Aktivität aufgerufen werden!  
 nss.end();
 disablePCI(); 
 drawerServo.attach(DRAWERPIN); 
 // Dieses "stepping" ist nur notwendig um die Limitation der Software Servo library zuerreichen, 
 // welche eine überflüssige Version der Servo library ist 
 // Russ diagnostizierte das Problem mit dem Pin Wechsel der Interrupts und hat keine Zeit gehabt es zu ersetzen.
 for (int i = 100; i >= 5; i-- ) 
 { 
 drawerServo.write(i); 
 SoftwareServo::refresh(); 
 delay(15); 
 } 
 drawerServo.detach(); 


 void unlockLid() 

// disablePCI  muss vor jeder servo Aktivität aufgerufen werden!  
 nss.end();
 disablePCI(); 
 lidServo.attach(LIDPIN); 
// Dieses "stepping" ist nur notwendig um die Limitation der Software Servo library zuerreichen, 
// welche eine überflüssige Version der Servo library ist 
// Russ diagnostizierte das Problem mit dem Pin Wechsel der Interrupts und hat keine Zeit gehabt es zu ersetzen.

 for ( int i = 50; i >= 15; i-- ) 
 { 
 lidServo.write(i); 
 SoftwareServo::refresh(); 
 delay(15); 
 } 
 lidServo.detach(); 

 //neu2
void lockLid() 

 // disablePCI  muss vor jeder servo Aktivität aufgerufen werden! 
  nss.end();
 disablePCI(); 
 lidServo.attach(LIDPIN); 
 // Dieses "stepping" ist nur notwendig um die Limitation der Software Servo library zuerreichen, 
 // welche eine überflüssige Version der Servo library ist 
 // Russ diagnostizierte das Problem mit dem Pin Wechsel der Interrupts und hat keine Zeit gehabt es zu ersetzen.
 for ( int i = 5; i <= 100; i++ ) 
 { 
 lidServo.write(i); 
 SoftwareServo::refresh(); 
 delay(15); 
 } 
 lidServo.detach(); 


/*beim Full rotationsservo:
void rotateLidServo(bool lock, int steps) 

 disablePCI(); 
 // In diesem Setup, ist der Deckelmotor ein kontinuierlich rotierender Servo und so wird ein Winkel 
 // Wert geschrieben. 
 lidServo.attach(LIDPIN); 
 lidServo.write((lock) ? 0 : 180); 
 for ( int i = 0; i < steps; i++ ) 
 { 
  SoftwareServo::refresh(); 
  delayMicroseconds(15000); 
 } 
 lidServo.detach(); 


void unlockLid() 

// gib ein bißchen mehr drive zum Öffnen nur für den Fall 
rotateLidServo(false, 20); 


void lockLid() 

rotateLidServo(true, 17); 

*/
/* 
* Große Kreis Distanz Kalkulation.
*/
float gcd(float lat_a, float lon_a, float lat_b, float lon_b)
{
  const float two=2.0;
   float d = two*asin(sqrt(square(sin((lat_a-lat_b)/two)) + cos(lat_a)*cos(lat_b)*square(sin((lon_b-lon_a)/two))));
  return fabs(EARTH_RADIUS * d);
}
 

void decrAttemptsRemaining() 

 attemptsRemaining--; 
 // speichere den neuen Status. 
 EEPROM.write(ADDR_TRIES, attemptsRemaining); 


void failNoSignal() 

  decrAttemptsRemaining(); 
  lcd.clear(); 
  lcd.setCursor(0,0); 
  lcd.print( "kein Zugriff!" ); 
  lcd.setCursor(0,1); 
  lcd.print( attemptsRemaining ); 
  lcd.print( "Versuche" ); 
  delay(5000); 
  lcd.clear(); 
  lcd.setCursor(0,0); 
  lcd.print( "Kein Signal!" ); 
  delay(5000); 
  shutdown(); 



/* Diese Funktion resets die Box in ihren Originalzustand. */
 void initializeEEPROM() 
 { 
 // 40 Versuche
   EEPROM.write(ADDR_TRIES, 41); 
   EEPROM.write(ADDR_STAGE1, 0); 
   EEPROM.write(ADDR_STAGE2, 0); 
   EEPROM.write(ADDR_BACKDOOR, 0); 
 }

2 Kommentare:

  1. hey
    bin gerade selbst am bauen der box.

    könntest du dich bitte mal bei mir melden falls du das liest?
    hät noch ein paar fragen dazu :)

    danke!!

    lg lukas

    AntwortenLöschen
  2. hallo,
    ich würde gerne auch so ein ähnliches Objekt starten und könnte ich mit dir in kontakt treten... Besonders beim programmieren habe ich meine Probleme...
    Danke und Gruß
    Christopher

    AntwortenLöschen