rowingtechinnovations
Published © GPL3+

DIY GPS Speedcoach

Designed for crew rowing, this speedcoach is designed to track split times and total meters traveled.

BeginnerWork in progress2,455
DIY GPS Speedcoach

Things used in this project

Hardware components

Adafruit Any Feather microcontroller
×1
Adafruit Ultimate GPS FeatherWing
×1
Adafruit SSD1306 128x64 OLED Display
×1
Battery, 3.7 V
Battery, 3.7 V
×1
Arduino Nano R3
Arduino Nano R3
This can be used in place of the Feather
×1
Adafruit Ultimate GPS Breakout
Adafruit Ultimate GPS Breakout
This can be used in place of the GPS FeatherWing (MUST BE MTK3339)
×1
Directional Antenna, GNSS Active Patch Antenna with LNA
Directional Antenna, GNSS Active Patch Antenna with LNA
×1
Pushbutton Switch, Momentary
Pushbutton Switch, Momentary
×1

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)

Story

Read more

Schematics

Schematics

Wire Diagram for the GPSSP

Code

Code (GPSSP)

Arduino
Main code for the speedcoach.
#include <Adafruit_SSD1306.h>
#include <splash.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <SPI.h>
#include <SandTimer.h>
#include <SD.h>
#include <millisDelay.h>
#include <Adafruit_GPS.h>   //Libraries
#define GPSSerial Serial1
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
#define PAUSE 1000
Adafruit_GPS GPS(&GPSSerial);
File myFile;
SandTimer timer1;
#define GPSECHO false
Adafruit_SSD1306 display(SCREEN_WIDTH,SCREEN_HEIGHT,&Wire,OLED_RESET);
uint32_t timer = millis();
const int chipSelect = 4;
float ms = 0; //Meters/Second variable
float avems = 0;  //Average M/S variable
float mscounter = 0;  //Speed total (for average)
int integcounter = 0; //Counts half-seconds
float meterstraveled = 0; //Total meters
int splittotal = 0;   //Split total (in seconds)  (Essential to be an integer)
int splitseconds = 0; //Seconds on split display  (Essential to be an integer)
int splitminutes = 0; //Minutes on split display  (Essential to be an integer)
int seconds = 0;
int minutes = 0;
int hours = 0;    //Self-explanatory (for timers)
int splitaveragetotal = 0; //All split seconds added together (for average split)
int splitaverageseconds = 0; //Split average seconds
int splitaverageminutes = 0; //Split average minutes
int splitaverage = 0; //Split average
int splitaveragesecondsdisp = 0; //Display variables, fixes a bug
int splitaverageminutesdisp = 0; 
int splitmdisp = 0; 
int splitsdisp = 0;  //Display Variables
int tc = 0; //Counter variable
int dp = 0; //Data-point variable
int dpave = 0; //Average of the data-points
int dprec = 0; //Counter variable
float knot = 0;//Speed in knots recorded from GPS
float aveknots = 0;  //Speed in knots averaged over 1/2 second (equal to 1 data point and 5 knot readings)
int knotcounter = 0; //Counter variable
int sensorValue = analogRead(A3); //For the data write switch
float msfm = 0; //M/S for the meters calculation
float checkBattery(){
  //Function to check battery voltage
  float measuredvbat = analogRead(9);
  measuredvbat *= 2;    // we divided by 2, so multiply back
  measuredvbat *= 3.3;  // Multiply by 3.3V, our reference voltage
  measuredvbat /= 1024; // convert to voltage

  
  return measuredvbat;}
  millisDelay secdelay;   //Timing delay (for timer)
void setup() {
  float voltage = sensorValue * (5.0 / 1023.0);   //Converts A3 reading into a 5v equivalent
  Wire.begin();
  Wire.setClock(400000L);
   Serial.begin(115200);
    GPS.begin(9600);
GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCGGA);   
GPS.sendCommand(PGCMD_ANTENNA);   
GPSSerial.println(PMTK_Q_RELEASE);
int realhours = GPS.hour - 8;
if(realhours < 0) {realhours = realhours+24;} //For SD timestamp (24 hour format)
if (voltage == 5) {if (!SD.begin(chipSelect)) { //Begins SD card startup
    Serial.println("Card failed, or not present");
    }
  }
  uint32_t m = micros();  //Main timer initialization
display.begin(SSD1306_SWITCHCAPVCC,0x3C);
display.fillScreen(0);
display.display();
display.fillRect(0,26,26,8,1);
  display.fillTriangle(20,32,24,32,24,55,1);
  display.fillTriangle(20,26,80,13,80,26,1);
  display.fillTriangle(80,13,100,26,80,26,1);
  display.fillRect(25,26,76,31,1);
  display.fillTriangle(80,64,128,40,128,64,0);
  display.setCursor(32,25);
  display.setTextColor(0);
  display.setTextSize(4);
  display.print("RTI");
  display.setCursor(0,0);
  display.setTextColor(1);
  display.setTextSize(1);
  display.print("RTI Development");
  display.setCursor(0,8);
  display.print("v. 3.0.0");
  display.display();
  delay(2000);
  secdelay.start(1000); //Start secondary timer delay (1 Second)
File dataFile = SD.open("storage.txt", FILE_WRITE);   //Opens SD card, prints initial piece introduction + timestamp
   if (dataFile) {
    Serial.println("Writing to SD");
    dataFile.println("PIECE BEGIN - - -");
    dataFile.print(realhours);
    dataFile.print("/");
    dataFile.println(GPS.minute);
    dataFile.close();}
    timer1.start(100);

}

void loop() {

  sensorValue = analogRead(A3);
  float voltage = sensorValue * (5.0 / 1023.0);

  float batVoltage = checkBattery();  //get battery voltage
  if (secdelay.justFinished()) {seconds++; secdelay.repeat();} //Checks if 1 second has passed, updates seconds variable
  if (seconds == 60) {seconds = 0; minutes++;}                 //Checks if 60 seconds have passed, resets seconds, increases minutes
  if (minutes == 60) {minutes = 0; hours++;}                   //Checks if 60 minutes have passed, resets minutes, increases hours
  char c = GPS.read();
  if (GPSECHO)
    if (c) Serial.print(c);
  if (GPS.newNMEAreceived()) {
    Serial.println(GPS.lastNMEA()); 
    if (!GPS.parse(GPS.lastNMEA()));  //Serial GPS info for debugging
  }
 
  if (timer > millis()) timer = millis();   //Restarts main timer
  
    
    if (timer1.finished()) {knot = knot+GPS.speed; knotcounter++;
    timer1.startOver();} 
  
  if (millis() - timer > 500) {   //Runs every half-second (responsible for screen-flicker)
    timer = millis(); // Resets timer
     if (GPS.fix) {
      aveknots = knot/knotcounter;
      Serial.println(aveknots);
      ms = (aveknots*0.5144444); //Converts knots to meters/second
      Serial.println(ms);
      knot = 0;
      tc = 0;
      knotcounter = 0;
      aveknots = 0;
      if (ms <= 0.75) {display.fillScreen(0);
                       
                       display.setCursor(0,0);
                       display.setTextColor(1);
                       display.setTextSize(1);
                       
                       display.print("Error: IDLE");
                       display.setCursor(0,15);
                       display.print("T: "); 
                       display.setCursor(10,15);
                       display.print(hours); 
                       display.setCursor(16,15);
                       display.print(":"); 
                       display.setCursor(22,15);
                       display.print(minutes); 
                       display.setCursor(34,15);
                       display.print(":"); 
                       display.setCursor(40,15);
                       display.print(seconds);
                       display.setCursor(0,30);
                       display.print("Battery: ");
                       display.setCursor(47,30);
                       display.print(batVoltage);
                       display.setCursor(0,45);
                       display.print("Meters: ");
                       display.setCursor(43,45);
                       display.print(meterstraveled); }//Idle screen 
                       display.display();
                   
                   
     if (ms > 0.75) 
      {
       
      msfm = ms;
      mscounter = (mscounter+msfm);  //Continually adds speed to total (for averages)
      integcounter = (integcounter+1); //Adds +1 to variable every half second (for averages)
      avems = mscounter/integcounter; //Generates average meters/second (for total)
      meterstraveled = avems*(integcounter/2); //Calculates total (meters/second divided by seconds)
      splittotal = 500/(ms); //Generates seconds per 500m
      dp = dp + splittotal;  //Adds data-points for dpave variable
      dprec++;
      if (dprec >= 6) {dpave = dp/6; //Calculates average
                       dprec = 0; 
                       dp = 0; //Resets data-points and counter variable
                       if(voltage > 4.5) {File dataFile = SD.open("storage.txt", FILE_WRITE);
                       //Opens SD card if write switch is on

  if (dataFile) {
    Serial.println("Writing to SD");
    dataFile.println("S: ");
    dataFile.print(splitminutes);
    dataFile.print(":");
    dataFile.println(splitseconds);
    dataFile.print("Ave. S ");
    dataFile.print(splitaverageminutes); 
    dataFile.print(":");
    dataFile.println(splitaverageseconds);
    dataFile.print("M: ");
    dataFile.println(meterstraveled);
    dataFile.print(hours);
    dataFile.print(":");
    dataFile.print(minutes);
    dataFile.print(":");
    dataFile.println(seconds);
    dataFile.println();
    dataFile.close();}}   //Prints data to SD card
    }
      splitminutes = dpave/60; //Generates split minutes from split seconds 
      splitseconds = dpave - (splitminutes*60); //Displays remaining seconds
      splitaveragetotal = splitaveragetotal+splittotal;   //All split times (in seconds) added together
      splitaverage = splitaveragetotal/(integcounter);  //Averages out split times w/ seconds
      splitaverageminutes = splitaverage/60;   //Calculates split minutes
      splitaverageseconds = splitaverage-splitaverageminutes*60;   //Calculates split seconds
      splitaverageminutesdisp = splitaverageminutes;
      splitaveragesecondsdisp = splitaverageseconds;
      splitmdisp = splitminutes;
      splitsdisp = splitseconds;      //Display variables
      display.fillScreen(0);
      display.setCursor(0,0);
      display.setTextColor(1);
      display.setTextSize(2);
      display.print("M: "); 
      display.setCursor(20,0);
      display.print(meterstraveled);  //Displays total meters
      display.setCursor(0,20);
      display.print("S: "); 
      display.setCursor(20,20);
      display.print(splitmdisp);
      display.setCursor(35,20); 
      display.print(":"); 
      display.setCursor(45,20);
      display.print(splitsdisp); 
      //oled.print("   AS: "); oled.print(splitaverageminutesdisp); oled.print(":");  oled.println(splitaveragesecondsdisp);
        //Displays split and average split
      display.setCursor(0,40);
      display.print("T: "); 
      display.setCursor(22,40);
      display.print(hours); 
      display.setCursor(33,40);
      display.print(":"); 
      display.setCursor(42,40);
      display.print(59); 
      display.setCursor(63,40);
      display.print(":"); 
      display.setCursor(72,40);
      display.print(seconds); //Displays timer
      display.display();
      //if (voltage >= 4.5) {oled.print("SD Write ON");}
      //if (voltage < 4.5) {oled.print("SD Write OFF");} //Shows write permission status
      }}}}

Credits

rowingtechinnovations

rowingtechinnovations

1 project • 5 followers

Comments