Skip to content

Instantly share code, notes, and snippets.

@Dan-Q
Created May 4, 2018 07:33
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Dan-Q/db7dae18e3b61f45d8526f36dcd6f49f to your computer and use it in GitHub Desktop.
Save Dan-Q/db7dae18e3b61f45d8526f36dcd6f49f to your computer and use it in GitHub Desktop.
GC591VV "You Can't Do It Alone!" geocache source code
This is the source code the the Arduinos powering my GC591VV "You Can't Do It Alone!" cache:
further details can be found at https://danq.me/2014/08/13/gc591vv/
synchronise_clocks.ino - this program sets the clock on the device to the date and time of
compilation: run it at the same time on both boxes to synchronise them
it_takes_two_cache.ino - this is the program that runs on both boxes when they're to be
deployed to the field; some constants need setting:
* SALT - needs to be set to a random number (the same for both boxes); this helps to prevent
cryptanalysis of the output given only one box
* SECOND_UNIT - needs to be set to true on the one box, false on the other
* TARGET_NORTH, TARGET_WEST - the Northings and Westings parts of the GZ of the actual cache
container, expressed as a long decimal
#include <LiquidCrystal.h>
#include <Wire.h>
#include <RTClib.h>
// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
// initialize the real-time clock (DS1307 chip)
RTC_DS1307 RTC;
const int LIFESPAN = 900; // How many seconds does each code-cycle last. Default 900 (15 minutes).
const int EPOCH = 104; // Number of days before code-cycle completes and starts over. Default 104.
const long SALT = 17628435; // To avoid trivial cryptanalysis, a secret salt is added to the random seed.
const boolean SECOND_UNIT = false; // Is this device the second of the two units? "false" for unit A, "true" for unit B.
const unsigned long TARGET_NORTH = 5112123L; // 51° 12.123'
const unsigned long RNG_NORTH_MIN = 1000000L;
const unsigned long TARGET_WEST = 112123L; // 001° 12.123'
const unsigned long RNG_WEST_MIN = 100000L;
void setup() {
Wire.begin();
RTC.begin();
// set up the LCD's number of columns and rows:
lcd.begin(16, 2);
}
/* Given the current DateTime, returns the number of the current code-cycle:
* i.e. a number between 0 and 9983 (changing every 15 minutes throughout a
* 104-day cycle) [assuming default constant values].
*/
int code_cycle_number(DateTime now){
int day_count = now.unixtime() / 86400L;
// calculate code number
int code_number = ((day_count % EPOCH) * (24 * (3600 / LIFESPAN))) +
(now.hour() * (3600 / LIFESPAN)) +
(now.minute() / (LIFESPAN / 60));
return code_number;
}
/* Given the current DateTime, returns the number of whole seconds remaining
* in the current code-cycle: i.e. a number between 900 and 1 [assuming
* default constant values].
*/
int seconds_left_this_cycle(DateTime now){
int secs_left = LIFESPAN;
secs_left -= (now.minute() % 15) * 60;
secs_left -= now.second();
return secs_left;
}
/* Given a random seed (typically based on the current date and time) and a
* boolean - "true" if this is the "unit B" (the second of the two paired
* devices), "false" otherwise - returns a formatted string suitable for
* outputting to a single 16-character LCD screen layer (7 digits, one space,
* 8 digits [because longitude of the same precision potentially has one more
* digit]).
* Note that to avoid having to think about pointers, the THIRD parameter is
* the character array (string buffer) to be populated with the string, rather
* than this function truly returning a value.
*/
void partial_coordinates(int seed, boolean second_unit, char buf[]){
randomSeed(seed);
unsigned long rOffsetNorth = random(TARGET_NORTH - RNG_NORTH_MIN);
unsigned long rOffsetWest = random(TARGET_WEST - RNG_WEST_MIN);
unsigned long rNorth = rOffsetNorth + RNG_NORTH_MIN;
unsigned long rWest = rOffsetWest + RNG_WEST_MIN;
unsigned long rAlternateNorth = TARGET_NORTH - rNorth;
unsigned long rAlternateWest = TARGET_WEST - rWest;
if(second_unit){ // if we're the second unit, show the alternate coords
sprintf(buf, "%07lu %08lu", rAlternateNorth, rAlternateWest);
} else { // we're the first unit, show the regular coords
sprintf(buf, "%07lu %08lu", rNorth, rWest);
}
}
void loop() {
DateTime now = RTC.now();
int code_cycle = code_cycle_number(now);
int secs_left = seconds_left_this_cycle(now);
char line1[16], line2[16];
// print the cycle number and the number of seconds left this cycle
sprintf(line1, "Code %04d (%3ds)", code_cycle, secs_left);
lcd.setCursor(0, 0);
lcd.print(line1);
// print the partial coordinates
partial_coordinates(SALT + code_cycle, SECOND_UNIT, line2);
lcd.setCursor(0, 1);
lcd.print(line2);
// DEBUG: print the current datetime
//sprintf(line2, "%04d%02d%02d %02d%02d%02d", now.year(), now.month(), now.day(), now.hour(), now.minute(), now.second());
//lcd.setCursor(0, 1);
//lcd.print(line2);
delay(1000);
}
#include <LiquidCrystal.h>
#include <Wire.h>
#include <RTClib.h>
// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
// initialize the real-time clock (DS1307 chip)
RTC_DS1307 RTC;
void setup() {
Wire.begin();
RTC.begin();
Serial.begin(9600);
// set up the LCD's number of columns and rows:
lcd.begin(16, 2);
// set the DS1307 system clock to the date and time of compilation
RTC.adjust(DateTime(__DATE__, __TIME__));
lcd.setCursor(0, 0);
lcd.print(__DATE__);
lcd.setCursor(0, 1);
lcd.print(__TIME__);
}
void loop() {
delay(1000);
lcd.setCursor(0, 1);
Serial.println(RTC.now().unixtime());
lcd.print(RTC.now().unixtime());
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment