Team C.O.B.R.A.: Reagan, Carson, Keane, Daytona, Nathan, Conrad
CreatingOutstandingBuildsRevolutionaryArtandHacks
The Spot Watch is a watch that can help you find places you've been to before without the use of a smartphone or large GPS. We like to call it a programmable compass! It has three modes: Places, where you can look through a library of places you've recorded, Setting, where you can record the place you're at by just pressing a button, and Spotting, where the watch guides you to the place you have selected in Places with a compass-like pointer.
Code Base: We've have been working on the code and we just decided to start over so there are two Arduino files, one (MicroView_GPSWatch.ino) is what we made at the Hackathon and the other is the redone one. The one at the Hackathon has no compiling errors, but is very glitchy... The other code (SpotWatch.ino) is just a compass right now that displays to the MicroView.
/***************************************************************************
This is a library example for the HMC5883 magnentometer/compass
Designed specifically to work with the Adafruit HMC5883 Breakout
http://www.adafruit.com/products/1746
*** You will also need to install the Adafruit_Sensor library! ***
These displays use I2C to communicate, 2 pins are required to interface.
Adafruit invests time and resources providing this open source code,
please support Adafruit andopen-source hardware by purchasing products
from Adafruit!
Written by Kevin Townsend for Adafruit Industries with some heading example from
Love Electronics (loveelectronics.co.uk)
This program is free software: you can redistribute it and/or modify
it under the terms of the version 3 GNU General Public License as
published by the Free Software Foundation.
This program 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. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
***************************************************************************/
#include <MicroView.h>
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_HMC5883_U.h>
#define WHEEL_SIZE 23
const uint8_t maxW = uView.getLCDWidth();
const uint8_t midW = maxW/2;
const uint8_t maxH = uView.getLCDHeight();
const uint8_t midH = maxH/2;
/* Assign a unique ID to this sensor at the same time */
Adafruit_HMC5883_Unified mag = Adafruit_HMC5883_Unified(12345);
void displaySensorDetails(void)
{
sensor_t sensor;
mag.getSensor(&sensor);
Serial.println("------------------------------------");
Serial.print ("Sensor: "); Serial.println(sensor.name);
Serial.print ("Driver Ver: "); Serial.println(sensor.version);
Serial.print ("Unique ID: "); Serial.println(sensor.sensor_id);
Serial.print ("Max Value: "); Serial.print(sensor.max_value); Serial.println(" uT");
Serial.print ("Min Value: "); Serial.print(sensor.min_value); Serial.println(" uT");
Serial.print ("Resolution: "); Serial.print(sensor.resolution); Serial.println(" uT");
Serial.println("------------------------------------");
Serial.println("");
delay(500);
}
void setup(void)
{
Serial.begin(9600);
Serial.println("HMC5883 Magnetometer Test"); Serial.println("");
/* Initialise the sensor */
if(!mag.begin())
{
/* There was a problem detecting the HMC5883 ... check your connections */
Serial.println("Ooops, no HMC5883 detected ... Check your wiring!");
while(1);
}
uView.begin(); // set up the MicroView
uView.clear(PAGE);// erase hardware memory inside the OLED
uView.display(); // display the content in the buffer
/* Display some basic information on this sensor */
displaySensorDetails();
}
void loop(void)
{
/* Get a new sensor event */
sensors_event_t event;
mag.getEvent(&event);
/* Display the results (magnetic vector values are in micro-Tesla (uT)) */
Serial.print("X: "); Serial.print(event.magnetic.x); Serial.print(" ");
Serial.print("Y: "); Serial.print(event.magnetic.y); Serial.print(" ");
Serial.print("Z: "); Serial.print(event.magnetic.z); Serial.print(" ");Serial.println("uT");
// Hold the module so that Z is pointing 'up' and you can measure the heading with x&y
// Calculate heading when the magnetometer is level, then correct for signs of axis.
float heading = atan2(event.magnetic.y, event.magnetic.x);
// Once you have your heading, you must then add your 'Declination Angle', which is the 'Error' of the magnetic field in your location.
// Find yours here: http://www.magnetic-declination.com/
// Mine is: -13* 2' W, which is ~13 Degrees, or (which we need) 0.22 radians
// If you cannot find your Declination, comment out these two lines, your compass will be slightly off.
float declinationAngle = 0.17;
heading += declinationAngle;
// Correct for when signs are reversed.
if(heading < 0)
heading += 2*PI;
// Check for wrap due to addition of declination.
if(heading > 2*PI)
heading -= 2*PI;
// Convert radians to degrees for readability.
float headingDegrees = heading * 180/M_PI;
Serial.print("Heading (degrees): "); Serial.println(headingDegrees);
drawFace();
drawTime(headingDegrees);
delay(500);
uView.display();
}
void drawFace()
{
uView.setFontType(0); // set font type 0 (Smallest)
uint8_t fontW = uView.getFontWidth();
uint8_t fontH = uView.getFontHeight();
//uView.setCursor(27, 0); // points cursor to x=27 y=0
uView.setCursor(midW-(fontW/2)-1, midH-WHEEL_SIZE+1);
uView.print("|"); // Print the "12"
uView.setCursor(midW-(fontW/2)-1, midH+WHEEL_SIZE-fontH-1);
uView.print("|"); // Print the "6"
uView.setCursor(midW-WHEEL_SIZE+1, midH-fontH/2);
uView.print("-"); // Print the "9"
uView.setCursor(midW+WHEEL_SIZE-fontW-2, midH-fontH/2);
uView.print("-"); // Print the "3"
uView.circle(midW-1, midH-1, WHEEL_SIZE);
//Draw the clock
uView.display();
}
void drawTime(float inputDegrees)
{
static boolean firstDraw = false;
static float degresssec, secx, secy;
// If mSec
// First time draw requires extra line to set up XOR's:
if (firstDraw)
{
uView.line(midW, midH, 32 + secx, 24 + secy, WHITE, XOR);
}
degresssec = (((inputDegrees * 360) / 60) + 270) * (PI / 180);
// Calculate x,y coordinates of second hand:
secx = cos(degresssec) * (WHEEL_SIZE / 1.1);
secy = sin(degresssec) * (WHEEL_SIZE / 1.1);
// Draw hands with the line function:
uView.line(midW, midH, midW+secx, midH+secy, WHITE, XOR);
// Set firstDraw flag to true, so we don't do it again.
firstDraw = true;
// Actually draw the hands with the display() function.
uView.display();
}
/*
MicroView Arduino Library
Copyright (C) 2014 GeekAmmo
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program 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. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <MicroView.h>
#include <Adafruit_GPS.h>
#include <SoftwareSerial.h>
#include <Time.h>
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_HMC5883_U.h>
#include <Adafruit_NeoPixel.h>
SoftwareSerial mySerial(3, 2); // RX, TX
Adafruit_HMC5883_Unified mag = Adafruit_HMC5883_Unified(12345);
Adafruit_GPS GPS(&mySerial);
#define WHEEL_SIZE 23
#define GPSECHO false
#define SECOND 00
//--------------------------------------------------|
// WAYPOINT |
//--------------------------------------------------|
//Please enter the latitude and longitude of your |
//desired destination: |
#define GEO_LAT 48.009551
#define GEO_LON -88.771131
//--------------------------------------------------|
//Your compass module may not line up with ours. |
//Once you run compass mode, compare to a separate |
//compass (like one found on your smartphone). |
//Point your TOP_LED north, then count clockwise |
//how many LEDs away from TOP_LED the lit LED is |
#define LED_OFFSET 0
//--------------------------------------------------|
float fLat = 0.0;
float fLon = 0.0;
int currentGoal = 0;
int goalCount = 0;
float Lat[6];
float Lon[6];
char names[6];
char letters[6] = {'A', 'B', 'C', 'D', 'E', 'F'};
int totalLocs = 6;
const uint8_t maxW = uView.getLCDWidth();
const uint8_t midW = maxW/2;
const uint8_t maxH = uView.getLCDHeight();
const uint8_t midH = maxH/2;
// Navigation location
float targetLat = Lat[0];
float targetLon = Lon[0];
// this keeps track of whether we're using the interrupt
// off by default!
boolean usingInterrupt = false;
// Trip distance
float tripDistance;
// Offset hours from gps time (UTC)
//const int offset = 1; // Central European Time
//const int offset = -4; // Eastern Daylight Time (USA)
//const int offset = -5; // Central Daylight Time (USA)
//const int offset = -8; // Pacific Standard Time (USA)
const int offset = -7; // Pacific Daylight Time (USA)
int compassOffset = LED_OFFSET;
int lastMin = 16;
int lastHour = 16;
int startLED = 0;
int startLEDlast = 16;
int lastCombined = 0;
int start = 0;
int mode = 0;
int lastDir = 16;
int dirLED_r = 0;
int dirLED_g = 0;
int dirLED_b = 255;
int compassReading;
// Calibration offsets
float magxOffset = 2.55;
float magyOffset = 27.95;
// Pushbutton setup
int button1 = 6; // the number of the pushbutton pin
int button2 = 5;
int button3 = A1;
int button4 = A0;
int buttonState;
int lastButtonState = HIGH;
long buttonHoldTime = 0; // the last time the output pin was toggled
long buttonHoldDelay = 250; // how long to hold the button down
// the following variables are long's because the time, measured in miliseconds,
// will quickly become a bigger number than can be stored in an int.
long lastDebounceTime = 0; // the last time the output pin was toggled
long debounceDelay = 50; // the debounce time; increase if the output flickers
long menuDelay = 250;
long menuTime;
void setup() {
// Set the time in the time library:
// connect at 115200 so we can read the GPS fast enough and echo without dropping chars
// also spit it out
Serial.begin(115200);
Serial.println("Adafruit GPS library basic test!");
// 9600 NMEA is the default baud rate for Adafruit MTK GPS's- some use 4800
GPS.begin(9600);
// uncomment this line to turn on RMC (recommended minimum) and GGA (fix data) including altitude
GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCGGA);
// uncomment this line to turn on only the "minimum recommended" data
//GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCONLY);
// For parsing data, we don't suggest using anything but either RMC only or RMC+GGA since
// the parser doesn't care about other sentences at this time
// Set the update rate
GPS.sendCommand(PMTK_SET_NMEA_UPDATE_1HZ); // 1 Hz update rate
// For the parsing code to work nicely and have time to sort thru the data, and
// print it out we don't suggest using anything higher than 1 Hz
// Request updates on antenna status, comment out to keep quiet
GPS.sendCommand(PGCMD_ANTENNA);
// Make input & enable pull-up resistors on switch pins for pushbutton
pinMode(button1, INPUT);
pinMode(button2, INPUT);
pinMode(button3, INPUT);
pinMode(button4, INPUT);
uView.begin(); // set up the MicroView
uView.clear(PAGE);// erase hardware memory inside the OLED
uView.display(); // display the content in the buffer
// Draw clock face (circle outline & text):
drawFace();
}
uint32_t gpsTimer = millis();
uint32_t startupTimer = millis();
uint32_t compassTimer = millis();
void loop() {
// Serial.println(digitalRead());
Serial.println(compassReading);
// read the state of the switch into a local variable:
//Serial.println(buttonState);
// read data from the GPS in the 'main loop'
char c = GPS.read();
// if you want to debug, this is a good time to do it!
if (GPSECHO)
if (c) Serial.print(c);
// if a sentence is received, we can check the checksum, parse it...
if (GPS.newNMEAreceived()) {
// a tricky thing here is if we print the NMEA sentence, or data
// we end up not listening and catching other sentences!
// so be very wary if using OUTPUT_ALLDATA and trytng to print out data
Serial.println(GPS.lastNMEA()); // this also sets the newNMEAreceived() flag to false
if (!GPS.parse(GPS.lastNMEA())) // this also sets the newNMEAreceived() flag to false
return; // we can fail to parse a sentence in which case we should just wait for another
}
// if millis() or timer wraps around, we'll just reset it
if (gpsTimer > millis()) gpsTimer = millis();
if (start == 0) {
if (GPS.fix) {
// set the Time to the latest GPS reading
setTime(GPS.hour, GPS.minute, GPS.seconds, GPS.day, GPS.month, GPS.year);
delay(50);
adjustTime(offset * SECS_PER_HOUR);
delay(500);
tripDistance = (double)calc_dist(fLat, fLon, targetLat, targetLon);
start = 1;
}
}
// approximately every 60 seconds or so, update time
if ((millis() - gpsTimer > 60000) && (start == 1)) {
gpsTimer = millis(); // reset the timer
if (GPS.fix) {
// set the Time to the latest GPS reading
setTime(GPS.hour, GPS.minute, GPS.seconds, GPS.day, GPS.month, GPS.year);
delay(50);
adjustTime(offset * SECS_PER_HOUR);
delay(500);
}
}
if (GPS.fix) {
fLat = decimalDegrees(GPS.latitude, GPS.lat);
fLon = decimalDegrees(GPS.longitude, GPS.lon);
}
//placesMode();
if(digitalRead(button4) == HIGH){
setMode();
}
}
void drawTime(float inputDegrees)
{
static boolean firstDraw = false;
static float degresssec, secx, secy;
// If mSec
// First time draw requires extra line to set up XOR's:
if (firstDraw)
{
uView.line(midW, midH, 32 + secx, 24 + secy, WHITE, XOR);
}
degresssec = (((inputDegrees * 360) / 60) + 270) * (PI / 180);
// Calculate x,y coordinates of second hand:
secx = cos(degresssec) * (WHEEL_SIZE / 1.1);
secy = sin(degresssec) * (WHEEL_SIZE / 1.1);
// Draw hands with the line function:
uView.line(midW, midH, midW+secx, midH+secy, WHITE, XOR);
// Set firstDraw flag to true, so we don't do it again.
firstDraw = true;
// Actually draw the hands with the display() function.
uView.display();
}
// Draw the clock face. That includes the circle outline and
// the 12, 3, 6, and 9 text.
void drawFace()
{
uView.setFontType(0); // set font type 0 (Smallest)
uint8_t fontW = uView.getFontWidth();
uint8_t fontH = uView.getFontHeight();
//uView.setCursor(27, 0); // points cursor to x=27 y=0
uView.setCursor(midW-(fontW/2)-1, midH-WHEEL_SIZE+1);
uView.print("|"); // Print the "12"
uView.setCursor(midW-(fontW/2)-1, midH+WHEEL_SIZE-fontH-1);
uView.print("|"); // Print the "6"
uView.setCursor(midW-WHEEL_SIZE+1, midH-fontH/2);
uView.print("-"); // Print the "9"
uView.setCursor(midW+WHEEL_SIZE-fontW-2, midH-fontH/2);
uView.print("-"); // Print the "3"
uView.circle(midW-1, midH-1, WHEEL_SIZE);
//Draw the clock
uView.display();
}
boolean finish = false;
void setMode() {
while(finish == false){
float currentLat = decimalDegrees(GPS.latitude, GPS.lat);
float currentLon = decimalDegrees(GPS.longitude, GPS.lon);
Lat[goalCount] = currentLat;
Lon[goalCount] = currentLon;
names[goalCount] = letters[goalCount];
uView.clear(PAGE);
uView.setCursor(midW-1-20, midH-1);
uView.print("Set as ");
uView.print(names[goalCount]);
uView.display();
delay(2000);
if(digitalRead(button3)== HIGH){
finish = true;
return;
}
}
delay(100);
finish = true;
}
void placesMode(){
boolean finished = false;
while(finished == false){
uView.clear(PAGE);
int rowCount = 0;
if(digitalRead(button2) == HIGH){
rowCount++;
uView.clear(PAGE);
for(totalLocs; totalLocs < 5; totalLocs++){
uView.setCursor(10, totalLocs);
uView.print(names[totalLocs]);
}
uView.setCursor(0,rowCount*10);
uView.print(">");
uView.display();
}
else if(digitalRead(button3) == HIGH){
rowCount--;
uView.clear(PAGE);
for(totalLocs; totalLocs < 5; totalLocs++){
uView.setCursor(10, totalLocs);
uView.print(names[totalLocs]);
}
uView.setCursor(0,rowCount);
uView.print(">");
uView.display();
}
else if(digitalRead(button1) == HIGH){
uView.clear(PAGE);
for(totalLocs; totalLocs < 5; totalLocs++){
uView.setCursor(10, totalLocs);
uView.print(names[totalLocs]);
}
uView.setCursor(0,rowCount);
uView.print(">");
uView.display();
targetLat = Lat[rowCount];
targetLon = Lon[rowCount];
navMode();
finished = true;
}
else {
uView.clear(PAGE);
for(totalLocs; totalLocs < 5; totalLocs++){
uView.setCursor(10, totalLocs);
uView.print(names[totalLocs]);
uView.display();
}
}
}
}
void navMode() {
boolean finishy = false;
while(finishy == false){
if (start == 1) {
compassCheck();
if ((calc_bearing(fLat, fLon, targetLat, targetLon) - compassReading) > 0) {
drawTime(calc_bearing(fLat, fLon, targetLat, targetLon)-compassReading);
}
else {
drawTime(calc_bearing(fLat, fLon, targetLat, targetLon)-compassReading+360);
}
}
else {
// if millis() or timer wraps around, we'll just reset it
if (startupTimer > millis()) startupTimer = millis();
// approximately every 10 seconds or so, update time
if (millis() - startupTimer > 200) {
startupTimer = millis(); // reset the timer
}
}
uView.display();
}
}
int calc_bearing(float flat1, float flon1, float flat2, float flon2)
{
float calc;
float bear_calc;
float x = 69.1 * (flat2 - flat1);
float y = 69.1 * (flon2 - flon1) * cos(flat1/57.3);
calc=atan2(y,x);
bear_calc= degrees(calc);
if(bear_calc<=1){
bear_calc=360+bear_calc;
}
return bear_calc;
}
unsigned long calc_dist(float flat1, float flon1, float flat2, float flon2)
{
float dist_calc=0;
float dist_calc2=0;
float diflat=0;
float diflon=0;
diflat=radians(flat2-flat1);
flat1=radians(flat1);
flat2=radians(flat2);
diflon=radians((flon2)-(flon1));
dist_calc = (sin(diflat/2.0)*sin(diflat/2.0));
dist_calc2= cos(flat1);
dist_calc2*=cos(flat2);
dist_calc2*=sin(diflon/2.0);
dist_calc2*=sin(diflon/2.0);
dist_calc +=dist_calc2;
dist_calc=(2*atan2(sqrt(dist_calc),sqrt(1.0-dist_calc)));
dist_calc*=6371000.0; //Converting to meters
return dist_calc;
}
// Convert NMEA coordinate to decimal degrees
float decimalDegrees(float nmeaCoord, char dir) {
uint16_t wholeDegrees = 0.01*nmeaCoord;
int modifier = 1;
if (dir == 'W' || dir == 'S') {
modifier = -1;
}
return (wholeDegrees + (nmeaCoord - 100.0*wholeDegrees)/60.0) * modifier;
}
void compassCheck() {
// if millis() or timer wraps around, we'll just reset it
if (compassTimer > millis()) compassTimer = millis();
// approximately every 10 seconds or so, update time
if (millis() - compassTimer > 50) {
/* Get a new sensor event */
sensors_event_t event;
mag.getEvent(&event);
float Pi = 3.14159;
compassTimer = millis(); // reset the timer
// Calculate the angle of the vector y,x
float heading = (atan2(event.magnetic.y + magyOffset,event.magnetic.x + magxOffset) * 180) / Pi;
// Normalize to 0-360
if (heading < 0)
{
heading = 360 + heading;
}
compassReading = heading;
Serial.println(compassReading);
}
}
Comments