#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);
}
hey
AntwortenLöschenbin 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
hallo,
AntwortenLöschenich 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