DIY GPS Speedcoach
Designed for crew rowing, this speedcoach is designed to track split times and total meters traveled.
Components and supplies
1
Battery, 3.7 V
1
Directional Antenna, GNSS Active Patch Antenna with LNA
1
Pushbutton Switch, Momentary
1
Adafruit Ultimate GPS FeatherWing
1
Any Feather microcontroller
1
Adafruit Ultimate GPS Breakout
1
SSD1306 128x64 OLED Display
1
Arduino Nano R3
Tools and machines
1
Soldering iron (generic)
Project description
Code
Code (GPSSP)
arduino
Main code for the speedcoach.
1#include <Adafruit_SSD1306.h> 2#include <splash.h> 3#include <Wire.h> 4#include <Adafruit_GFX.h> 5#include <SPI.h> 6#include <SandTimer.h> 7#include <SD.h> 8#include <millisDelay.h> 9#include <Adafruit_GPS.h> //Libraries 10#define GPSSerial Serial1 11#define SCREEN_WIDTH 128 12#define SCREEN_HEIGHT 64 13#define OLED_RESET -1 14#define PAUSE 1000 15Adafruit_GPS GPS(&GPSSerial); 16File myFile; 17SandTimer timer1; 18#define GPSECHO false 19Adafruit_SSD1306 display(SCREEN_WIDTH,SCREEN_HEIGHT,&Wire,OLED_RESET); 20uint32_t timer = millis(); 21const int chipSelect = 4; 22float ms = 0; //Meters/Second variable 23float avems = 0; //Average M/S variable 24float mscounter = 0; //Speed total (for average) 25int integcounter = 0; //Counts half-seconds 26float meterstraveled = 0; //Total meters 27int splittotal = 0; //Split total (in seconds) (Essential to be an integer) 28int splitseconds = 0; //Seconds on split display (Essential to be an integer) 29int splitminutes = 0; //Minutes on split display (Essential to be an integer) 30int seconds = 0; 31int minutes = 0; 32int hours = 0; //Self-explanatory (for timers) 33int splitaveragetotal = 0; //All split seconds added together (for average split) 34int splitaverageseconds = 0; //Split average seconds 35int splitaverageminutes = 0; //Split average minutes 36int splitaverage = 0; //Split average 37int splitaveragesecondsdisp = 0; //Display variables, fixes a bug 38int splitaverageminutesdisp = 0; 39int splitmdisp = 0; 40int splitsdisp = 0; //Display Variables 41int tc = 0; //Counter variable 42int dp = 0; //Data-point variable 43int dpave = 0; //Average of the data-points 44int dprec = 0; //Counter variable 45float knot = 0;//Speed in knots recorded from GPS 46float aveknots = 0; //Speed in knots averaged over 1/2 second (equal to 1 data point and 5 knot readings) 47int knotcounter = 0; //Counter variable 48int sensorValue = analogRead(A3); //For the data write switch 49float msfm = 0; //M/S for the meters calculation 50float checkBattery(){ 51 //Function to check battery voltage 52 float measuredvbat = analogRead(9); 53 measuredvbat *= 2; // we divided by 2, so multiply back 54 measuredvbat *= 3.3; // Multiply by 3.3V, our reference voltage 55 measuredvbat /= 1024; // convert to voltage 56 57 58 return measuredvbat;} 59 millisDelay secdelay; //Timing delay (for timer) 60void setup() { 61 float voltage = sensorValue * (5.0 / 1023.0); //Converts A3 reading into a 5v equivalent 62 Wire.begin(); 63 Wire.setClock(400000L); 64 Serial.begin(115200); 65 GPS.begin(9600); 66GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCGGA); 67GPS.sendCommand(PGCMD_ANTENNA); 68GPSSerial.println(PMTK_Q_RELEASE); 69int realhours = GPS.hour - 8; 70if(realhours < 0) {realhours = realhours+24;} //For SD timestamp (24 hour format) 71if (voltage == 5) {if (!SD.begin(chipSelect)) { //Begins SD card startup 72 Serial.println("Card failed, or not present"); 73 } 74 } 75 uint32_t m = micros(); //Main timer initialization 76display.begin(SSD1306_SWITCHCAPVCC,0x3C); 77display.fillScreen(0); 78display.display(); 79display.fillRect(0,26,26,8,1); 80 display.fillTriangle(20,32,24,32,24,55,1); 81 display.fillTriangle(20,26,80,13,80,26,1); 82 display.fillTriangle(80,13,100,26,80,26,1); 83 display.fillRect(25,26,76,31,1); 84 display.fillTriangle(80,64,128,40,128,64,0); 85 display.setCursor(32,25); 86 display.setTextColor(0); 87 display.setTextSize(4); 88 display.print("RTI"); 89 display.setCursor(0,0); 90 display.setTextColor(1); 91 display.setTextSize(1); 92 display.print("RTI Development"); 93 display.setCursor(0,8); 94 display.print("v. 3.0.0"); 95 display.display(); 96 delay(2000); 97 secdelay.start(1000); //Start secondary timer delay (1 Second) 98File dataFile = SD.open("storage.txt", FILE_WRITE); //Opens SD card, prints initial piece introduction + timestamp 99 if (dataFile) { 100 Serial.println("Writing to SD"); 101 dataFile.println("PIECE BEGIN - - -"); 102 dataFile.print(realhours); 103 dataFile.print("/"); 104 dataFile.println(GPS.minute); 105 dataFile.close();} 106 timer1.start(100); 107 108} 109 110void loop() { 111 112 sensorValue = analogRead(A3); 113 float voltage = sensorValue * (5.0 / 1023.0); 114 115 float batVoltage = checkBattery(); //get battery voltage 116 if (secdelay.justFinished()) {seconds++; secdelay.repeat();} //Checks if 1 second has passed, updates seconds variable 117 if (seconds == 60) {seconds = 0; minutes++;} //Checks if 60 seconds have passed, resets seconds, increases minutes 118 if (minutes == 60) {minutes = 0; hours++;} //Checks if 60 minutes have passed, resets minutes, increases hours 119 char c = GPS.read(); 120 if (GPSECHO) 121 if (c) Serial.print(c); 122 if (GPS.newNMEAreceived()) { 123 Serial.println(GPS.lastNMEA()); 124 if (!GPS.parse(GPS.lastNMEA())); //Serial GPS info for debugging 125 } 126 127 if (timer > millis()) timer = millis(); //Restarts main timer 128 129 130 if (timer1.finished()) {knot = knot+GPS.speed; knotcounter++; 131 timer1.startOver();} 132 133 if (millis() - timer > 500) { //Runs every half-second (responsible for screen-flicker) 134 timer = millis(); // Resets timer 135 if (GPS.fix) { 136 aveknots = knot/knotcounter; 137 Serial.println(aveknots); 138 ms = (aveknots*0.5144444); //Converts knots to meters/second 139 Serial.println(ms); 140 knot = 0; 141 tc = 0; 142 knotcounter = 0; 143 aveknots = 0; 144 if (ms <= 0.75) {display.fillScreen(0); 145 146 display.setCursor(0,0); 147 display.setTextColor(1); 148 display.setTextSize(1); 149 150 display.print("Error: IDLE"); 151 display.setCursor(0,15); 152 display.print("T: "); 153 display.setCursor(10,15); 154 display.print(hours); 155 display.setCursor(16,15); 156 display.print(":"); 157 display.setCursor(22,15); 158 display.print(minutes); 159 display.setCursor(34,15); 160 display.print(":"); 161 display.setCursor(40,15); 162 display.print(seconds); 163 display.setCursor(0,30); 164 display.print("Battery: "); 165 display.setCursor(47,30); 166 display.print(batVoltage); 167 display.setCursor(0,45); 168 display.print("Meters: "); 169 display.setCursor(43,45); 170 display.print(meterstraveled); }//Idle screen 171 display.display(); 172 173 174 if (ms > 0.75) 175 { 176 177 msfm = ms; 178 mscounter = (mscounter+msfm); //Continually adds speed to total (for averages) 179 integcounter = (integcounter+1); //Adds +1 to variable every half second (for averages) 180 avems = mscounter/integcounter; //Generates average meters/second (for total) 181 meterstraveled = avems*(integcounter/2); //Calculates total (meters/second divided by seconds) 182 splittotal = 500/(ms); //Generates seconds per 500m 183 dp = dp + splittotal; //Adds data-points for dpave variable 184 dprec++; 185 if (dprec >= 6) {dpave = dp/6; //Calculates average 186 dprec = 0; 187 dp = 0; //Resets data-points and counter variable 188 if(voltage > 4.5) {File dataFile = SD.open("storage.txt", FILE_WRITE); 189 //Opens SD card if write switch is on 190 191 if (dataFile) { 192 Serial.println("Writing to SD"); 193 dataFile.println("S: "); 194 dataFile.print(splitminutes); 195 dataFile.print(":"); 196 dataFile.println(splitseconds); 197 dataFile.print("Ave. S "); 198 dataFile.print(splitaverageminutes); 199 dataFile.print(":"); 200 dataFile.println(splitaverageseconds); 201 dataFile.print("M: "); 202 dataFile.println(meterstraveled); 203 dataFile.print(hours); 204 dataFile.print(":"); 205 dataFile.print(minutes); 206 dataFile.print(":"); 207 dataFile.println(seconds); 208 dataFile.println(); 209 dataFile.close();}} //Prints data to SD card 210 } 211 splitminutes = dpave/60; //Generates split minutes from split seconds 212 splitseconds = dpave - (splitminutes*60); //Displays remaining seconds 213 splitaveragetotal = splitaveragetotal+splittotal; //All split times (in seconds) added together 214 splitaverage = splitaveragetotal/(integcounter); //Averages out split times w/ seconds 215 splitaverageminutes = splitaverage/60; //Calculates split minutes 216 splitaverageseconds = splitaverage-splitaverageminutes*60; //Calculates split seconds 217 splitaverageminutesdisp = splitaverageminutes; 218 splitaveragesecondsdisp = splitaverageseconds; 219 splitmdisp = splitminutes; 220 splitsdisp = splitseconds; //Display variables 221 display.fillScreen(0); 222 display.setCursor(0,0); 223 display.setTextColor(1); 224 display.setTextSize(2); 225 display.print("M: "); 226 display.setCursor(20,0); 227 display.print(meterstraveled); //Displays total meters 228 display.setCursor(0,20); 229 display.print("S: "); 230 display.setCursor(20,20); 231 display.print(splitmdisp); 232 display.setCursor(35,20); 233 display.print(":"); 234 display.setCursor(45,20); 235 display.print(splitsdisp); 236 //oled.print(" AS: "); oled.print(splitaverageminutesdisp); oled.print(":"); oled.println(splitaveragesecondsdisp); 237 //Displays split and average split 238 display.setCursor(0,40); 239 display.print("T: "); 240 display.setCursor(22,40); 241 display.print(hours); 242 display.setCursor(33,40); 243 display.print(":"); 244 display.setCursor(42,40); 245 display.print(59); 246 display.setCursor(63,40); 247 display.print(":"); 248 display.setCursor(72,40); 249 display.print(seconds); //Displays timer 250 display.display(); 251 //if (voltage >= 4.5) {oled.print("SD Write ON");} 252 //if (voltage < 4.5) {oled.print("SD Write OFF");} //Shows write permission status 253 }}}} 254
Downloadable files
Schematics
Wire Diagram for the GPSSP
Schematics
Schematics
Wire Diagram for the GPSSP
Schematics
Comments
Only logged in users can leave comments