Dana Mah
Created October 14, 2016

Boat Data Logger

This is a module that will log data like pitch, roll, gps coords on your boat and store the data to a sdcard for analysis.

241
Boat Data Logger

Things used in this project

Hardware components

Arduino 101
Arduino 101
×1
gps module
×1
sd card reader
×1
sd card
×1

Story

Read more

Schematics

Wiring Diagram

Code

Arduino code

Arduino
//required for sdcard
#include <SPI.h>
#include <SD.h>
//required for gps
#include <TinyGPS++.h>
#include <SoftwareSerial.h>
//required for IMU
#include <CurieIMU.h>
#include <MadgwickAHRS.h>


const bool DEBUG = false;

static const int RXPin = 7, TXPin = 8;  //Pins for gps module serial port
static const uint32_t GPSBaud = 9600;   // gps baud rate, the module I have use 9600
SoftwareSerial ss(RXPin, TXPin);        // The serial connection to the GPS device
TinyGPSPlus gps;                        // The TinyGPS++ object

unsigned long last = 0UL;               // For stats that happen every 5 seconds

const int chipSelect = 10;              //sd card

//IMU
Madgwick filter;
unsigned long microsPerReading, microsPrevious;
float accelScale, gyroScale;

void setup()
{
  Serial.begin(115200);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
  }

  Serial.print("Initializing SD card...");

  // see if the card is present and can be initialized:
  if (!SD.begin(chipSelect)) {
    Serial.println("Card failed, or not present");
    // don't do anything more:
    return;
  }
  Serial.println("card initialized.");
  
  ss.begin(GPSBaud);

  Serial.println(F("imu1gps5ssd1.ino"));

  Serial.println(TinyGPSPlus::libraryVersion());

  Serial.println();

  //IMU
    // start the IMU and filter
  CurieIMU.begin();
  CurieIMU.setGyroRate(25);
  CurieIMU.setAccelerometerRate(25);
  filter.begin(25);

  // Set the accelerometer range to 2G
  CurieIMU.setAccelerometerRange(2);
  // Set the gyroscope range to 250 degrees/second
  CurieIMU.setGyroRange(250);

   //Serial.print("Starting Gyroscope calibration...");
    CurieIMU.autoCalibrateGyroOffset();
    //Serial.println(" Done");
    //Serial.print("Starting Acceleration calibration...");
    CurieIMU.autoCalibrateXAccelOffset(0);
    CurieIMU.autoCalibrateYAccelOffset(0);
    CurieIMU.autoCalibrateZAccelOffset(1);
    //Serial.println(" Done");

    //Serial.println("Enabling Gyroscope/Acceleration offset compensation");
    CurieIMU.setGyroOffsetEnabled(true);
    CurieIMU.setAccelOffsetEnabled(true);
  // initialize variables to pace updates to correct rate
  microsPerReading = 1000000 / 25;
  microsPrevious = micros();
}

void loop(){

   // open the file. note that only one file can be open at a time,
  // so you have to close this one before opening another.
  File dataFile = SD.open("gps.txt", FILE_WRITE);

  // if the file is available, write to it:
  if (dataFile) { 
    // Dispatch incoming characters
    while (ss.available() > 0){
      char c = ss.read();
      dataFile.write(c);
      gps.encode(c);
    }
    String imuRetVal = imu();
    dataFile.println(imuRetVal);
    //Serial.println("IMU Return Value:" + imuRetVal);
    dataFile.close();
  } else {
    Serial.println("error opening datalog.txt");// if the file isn't open, pop up an error:
  }
  
   if (gps.location.isUpdated()){
      Serial.print(F("LOCATION   Fix Age="));
      Serial.print(gps.location.age());
      Serial.print(F("ms Raw Lat="));
      Serial.print(gps.location.rawLat().negative ? "-" : "+");
      Serial.print(gps.location.rawLat().deg);
      Serial.print("[+");
      Serial.print(gps.location.rawLat().billionths);
      Serial.print(F(" billionths],  Raw Long="));
      Serial.print(gps.location.rawLng().negative ? "-" : "+");
      Serial.print(gps.location.rawLng().deg);
      Serial.print("[+");
      Serial.print(gps.location.rawLng().billionths);
      Serial.print(F(" billionths],  Lat="));
      Serial.print(gps.location.lat(), 6);
      Serial.print(F(" Long="));
      Serial.println(gps.location.lng(), 6);
    } else if (gps.date.isUpdated()){
      Serial.print(F("DATE       Fix Age="));
      Serial.print(gps.date.age());
      Serial.print(F("ms Raw="));
      Serial.print(gps.date.value());
      Serial.print(F(" Year="));
      Serial.print(gps.date.year());
      Serial.print(F(" Month="));
      Serial.print(gps.date.month());
      Serial.print(F(" Day="));
      Serial.println(gps.date.day());
    } else if (gps.time.isUpdated()){
      Serial.print(F("TIME       Fix Age="));
      Serial.print(gps.time.age());
      Serial.print(F("ms Raw="));
      Serial.print(gps.time.value());
      Serial.print(F(" Hour="));
      Serial.print(gps.time.hour());
      Serial.print(F(" Minute="));
      Serial.print(gps.time.minute());
      Serial.print(F(" Second="));
      Serial.print(gps.time.second());
      Serial.print(F(" Hundredths="));
      Serial.println(gps.time.centisecond());
    } else if (gps.speed.isUpdated()){
      Serial.print(F("SPEED      Fix Age="));
      Serial.print(gps.speed.age());
      Serial.print(F("ms Raw="));
      Serial.print(gps.speed.value());
      Serial.print(F(" Knots="));
      Serial.print(gps.speed.knots());
      Serial.print(F(" MPH="));
      Serial.print(gps.speed.mph());
      Serial.print(F(" m/s="));
      Serial.print(gps.speed.mps());
      Serial.print(F(" km/h="));
      Serial.println(gps.speed.kmph());
    } else if (gps.course.isUpdated()){
      Serial.print(F("COURSE     Fix Age="));
      Serial.print(gps.course.age());
      Serial.print(F("ms Raw="));
      Serial.print(gps.course.value());
      Serial.print(F(" Deg="));
      Serial.println(gps.course.deg());
    } else if (gps.altitude.isUpdated()){
      Serial.print(F("ALTITUDE   Fix Age="));
      Serial.print(gps.altitude.age());
      Serial.print(F("ms Raw="));
      Serial.print(gps.altitude.value());
      Serial.print(F(" Meters="));
      Serial.print(gps.altitude.meters());
      Serial.print(F(" Miles="));
      Serial.print(gps.altitude.miles());
      Serial.print(F(" KM="));
      Serial.print(gps.altitude.kilometers());
      Serial.print(F(" Feet="));
      Serial.println(gps.altitude.feet());
    } else if (gps.satellites.isUpdated()){
      Serial.print(F("SATELLITES Fix Age="));
      Serial.print(gps.satellites.age());
      Serial.print(F("ms Value="));
      Serial.println(gps.satellites.value());
    } else if (gps.hdop.isUpdated()){
      Serial.print(F("HDOP       Fix Age="));
      Serial.print(gps.hdop.age());
      Serial.print(F("ms Value="));
      Serial.println(gps.hdop.value());
    } else if (millis() - last > 5000){
      Serial.println();
      if (gps.location.isValid()){
//        static const double LONDON_LAT = 51.508131, LONDON_LON = -0.128002;
//        double distanceToLondon =
//          TinyGPSPlus::distanceBetween(
//            gps.location.lat(),
//            gps.location.lng(),
//            LONDON_LAT, 
//            LONDON_LON);
//        double courseToLondon =
//          TinyGPSPlus::courseTo(
//            gps.location.lat(),
//            gps.location.lng(),
//            LONDON_LAT, 
//            LONDON_LON);
  
//        Serial.print(F("LONDON     Distance="));
//        Serial.print(distanceToLondon/1000, 6);
//        Serial.print(F(" km Course-to="));
//        Serial.print(courseToLondon, 6);
//        Serial.print(F(" degrees ["));
//        Serial.print(TinyGPSPlus::cardinal(courseToLondon));
//        Serial.println(F("]"));
      }
  
      Serial.print(F("DIAGS      Chars="));
      Serial.print(gps.charsProcessed());
      Serial.print(F(" Sentences-with-Fix="));
      Serial.print(gps.sentencesWithFix());
      Serial.print(F(" Failed-checksum="));
      Serial.print(gps.failedChecksum());
      Serial.print(F(" Passed-checksum="));
      Serial.println(gps.passedChecksum());

      //Send the gps coords out
      Serial.print("$GPS,");
      Serial.print(gps.location.lat());
      Serial.print( "," );
      Serial.print(gps.location.lng());
      Serial.println(" ");

      //send the data out
      Serial.print("$DATE,");
      Serial.print(gps.date.year());
      Serial.print(F("/"));
      Serial.print(gps.date.month());
      Serial.print(F("/"));
      Serial.println(gps.date.day());

      //Send the time out.
      Serial.print("$TIME,");
      Serial.print(gps.time.hour());
      Serial.print(":");
      Serial.print(gps.time.minute());
      Serial.print(":"); 
      Serial.print(gps.time.second());
      Serial.print(" GMT");
      
      if (gps.charsProcessed() < 10)
        Serial.println(F("WARNING: No GPS data.  Check wiring."));
  
      last = millis();
      Serial.println();
   } 
}

String imu(){
  int aix, aiy, aiz;
  int gix, giy, giz;
  float ax, ay, az;
  float gx, gy, gz;
  float roll, pitch, heading;
  unsigned long microsNow;
  
  String retVal = "$IMU,";
   
  // check if it's time to read data and update the filter
  microsNow = micros();
  if (microsNow - microsPrevious >= microsPerReading) {

    // read raw data from CurieIMU
    CurieIMU.readMotionSensor(aix, aiy, aiz, gix, giy, giz);

    // convert from raw data to gravity and degrees/second units
    ax = convertRawAcceleration(aix);
    ay = convertRawAcceleration(aiy);
    az = convertRawAcceleration(aiz);
    gx = convertRawGyro(gix);
    gy = convertRawGyro(giy);
    gz = convertRawGyro(giz);

    // update the filter, which computes orientation
    filter.updateIMU(gx, gy, gz, ax, ay, az);

    // print the heading, pitch and roll
    roll = filter.getRoll();
    pitch = filter.getPitch();
    heading = filter.getYaw();

    retVal = retVal + roll + "," + pitch + "," + heading;

    Serial.println(retVal);
    
    if (DEBUG){
      Serial.print("Orientation: ");
      Serial.print(heading);
      Serial.print(" ");
      Serial.print(pitch);
      Serial.print(" ");
      Serial.println(roll);
    }
    // increment previous time, so we keep proper pace
    microsPrevious = microsPrevious + microsPerReading;
  }
  return retVal;
}


float convertRawAcceleration(int aRaw) {
  // since we are using 2G range
  // -2g maps to a raw value of -32768
  // +2g maps to a raw value of 32767
  
  float a = (aRaw * 2.0) / 32768.0;
  return a;
}

float convertRawGyro(int gRaw) {
  // since we are using 250 degrees/seconds range
  // -250 maps to a raw value of -32768
  // +250 maps to a raw value of 32767
  
  float g = (gRaw * 250.0) / 32768.0;
  return g;
}

Processing code

Java
import org.firmata.*;
import cc.arduino.*;

//Thanks to Adrian Fernandez for the beta-version
//Modified and updated as per latest IDE by Aritro Mukherjee (April,2016)
//Check the detailed tutorial @ www.hackster.io/Aritro


import processing.serial.*;
import cc.arduino.*;

int W=1400; //My Laptop's screen width 
int H=700;  //My Laptop's screen height 
float Pitch; 
float Bank; 
float Azimuth; 
float ArtificialHoizonMagnificationFactor=0.7; 
float CompassMagnificationFactor=0.85; 
float SpanAngle=120; 
int NumberOfScaleMajorDivisions; 
int NumberOfScaleMinorDivisions; 
PVector v1, v2; 


Serial port;
float Phi;    //Dimensional axis
float Theta;
float Psi;

String Lat = "";
String Lng = "";
String gDate = "";
String gTime = "";

void setup() 
{ 
  size(1400, 700); 
  rectMode(CENTER); 
  smooth(); 
  strokeCap(SQUARE);//Optional 
  
  //println(Serial.list()); //Shows your connected serial ports
  //port = new Serial(this, Serial.list()[0], 115200); 
  port = new Serial(this, Serial.list()[1], 9600);
  println(Serial.list()[1]);
  //Up there you should select port which arduino connected and same baud rate.
  port.bufferUntil('\n'); 
}
void draw() 
{ 
  background(0); 
  translate(W/4, H/2.1);  
  MakeAnglesDependentOnMPU6050(); 
  Horizon(); 
  rotate(-Bank); 
  PitchScale(); 
  Axis(); 
  rotate(Bank); 
  Borders(); 
  Plane(); 
  ShowAngles(); 
  Compass(); 
  ShowAzimuth(); 
  delay(500);
}
void serialEvent(Serial port) //Reading the datas by Processing.
{
   String input = port.readStringUntil('\n');
  if(input != null){
    input = trim(input);
    //String[] values = split(input, '\t');
    String[] values = split(input, ',');
    //println(input);
    if (values[0].equals("$IMU")){
      if(values.length == 4){
        float phi = float(values[1]);
        float theta = float(values[2]); 
        float psi = float(values[3]); 
        psi = psi -.05;
        print(phi);
        print(" ");
        print(theta);
        print(" ");
        println(psi);
        Phi = phi;
        Theta = theta;
        Psi = psi;
      }
    } 
    if (values[0].equals("$GPS")){
      if(values.length == 3){
        String lat = values[1];
        String lng = values[2];
        Lat = lat;
        Lng = lng;
      }
    }  
    if (values[0].equals("$DATE")){
      if(values.length == 2){
        String date = values[1];
        gDate = date;
        println(gDate);
      }
    } 
    if (values[0].equals("$TIME")){
      if(values.length == 2){
        String time = values[1];
        gTime = time;
        println(gTime);
      }
    } 
  }
}
void MakeAnglesDependentOnMPU6050() 
{ 
  //Bank =-Phi/5; 
  //Pitch=Theta*10; 
  //Azimuth=Psi;
  
  //Bank =Phi/1000;
  //Pitch=Theta/1000; 
  //Azimuth=Psi;
  
  Bank=-Phi/100;
  Bank= float(nf(Bank, 3, 2));
  //Bank=0;
  //println(Theta);
  if (Theta > 400) Theta = 400;
  if (Theta < -400) Theta = -400;
  Pitch=Theta * 5; 
  Azimuth=-Psi;
}
void Horizon() 
{ 
  scale(ArtificialHoizonMagnificationFactor); 
  noStroke(); 
  fill(0, 180, 255); 
  rect(0, -100, 900, 1000); 
  fill(95, 55, 40); 
  rotate(-Bank); 
  rect(0, 400+Pitch, 900, 800); 
  rotate(Bank); 
  rotate(-PI-PI/6); 
  SpanAngle=120; 
  NumberOfScaleMajorDivisions=12; 
  NumberOfScaleMinorDivisions=24;  
  CircularScale(); 
  rotate(PI+PI/6); 
  rotate(-PI/6);  
  CircularScale(); 
  rotate(PI/6); 
}
void ShowAzimuth() 
{ 
  fill(50); 
  noStroke(); 
  rect(20, 470, 440, 50); 
  int Azimuth1=round(Azimuth); 
  textAlign(CORNER); 
  textSize(35); 
  fill(255); 
  text("Azimuth:  "+Azimuth1+" Deg", 80, 477, 500, 60); 
  textSize(40);
  fill(25,25,150);
  text("SIMULATOR", -350, 477, 500, 60);
  fill(255);
  //text("Lat:" + Lat, -450, 520, 500, 60); 
  //text("Lon:" + Lng, -450, 565, 500, 60); 
  //text("Date:" + gDate, -150, 520, 500, 60); 
  //text("Time:" + gTime, -150, 565, 500, 60); 
}
void Compass() 
{ 
  translate(2*W/3, 0); 
  scale(CompassMagnificationFactor); 
  noFill(); 
  stroke(100); 
  strokeWeight(80); 
  ellipse(0, 0, 750, 750); 
  strokeWeight(50); 
  stroke(50); 
  fill(0, 0, 40); 
  ellipse(0, 0, 610, 610); 
  for (int k=255;k>0;k=k-5) 
  { 
    noStroke(); 
    fill(0, 0, 255-k); 
    ellipse(0, 0, 2*k, 2*k); 
  } 
  strokeWeight(20); 
  NumberOfScaleMajorDivisions=18; 
  NumberOfScaleMinorDivisions=36;  
  SpanAngle=180; 
  CircularScale(); 
  rotate(PI); 
  SpanAngle=180; 
  CircularScale(); 
  rotate(-PI); 
  fill(255); 
  textSize(60); 
  textAlign(CENTER); 
  text("W", -375, 0, 100, 80); 
  text("E", 370, 0, 100, 80); 
  text("N", 0, -365, 100, 80); 
  text("S", 0, 375, 100, 80); 
  textSize(30); 
  text("COMPASS", 0, -130, 500, 80); 
  rotate(PI/4); 
  textSize(40); 
  text("NW", -370, 0, 100, 50); 
  text("SE", 365, 0, 100, 50); 
  text("NE", 0, -355, 100, 50); 
  text("SW", 0, 365, 100, 50); 
  rotate(-PI/4); 
  CompassPointer(); 
}
void CompassPointer() 
{ 
  rotate(PI+radians(Azimuth));  
  stroke(0); 
  strokeWeight(4); 
  fill(100, 255, 100); 
  triangle(-20, -210, 20, -210, 0, 270); 
  triangle(-15, 210, 15, 210, 0, 270); 
  ellipse(0, 0, 45, 45);   
  fill(0, 0, 50); 
  noStroke(); 
  ellipse(0, 0, 10, 10); 
  triangle(-20, -213, 20, -213, 0, -190); 
  triangle(-15, -215, 15, -215, 0, -200); 
  rotate(-PI-radians(Azimuth)); 
}
void Plane() 
{ 
  fill(0); 
  strokeWeight(1); 
  stroke(0, 255, 0); 
  triangle(-20, 0, 20, 0, 0, 25); 
  rect(110, 0, 140, 20); 
  rect(-110, 0, 140, 20); 
}
void CircularScale() 
{ 
  float GaugeWidth=800;  
  textSize(GaugeWidth/30); 
  float StrokeWidth=1; 
  float an; 
  float DivxPhasorCloser; 
  float DivxPhasorDistal; 
  float DivyPhasorCloser; 
  float DivyPhasorDistal; 
  strokeWeight(2*StrokeWidth); 
  stroke(255);
  float DivCloserPhasorLenght=GaugeWidth/2-GaugeWidth/9-StrokeWidth; 
  float DivDistalPhasorLenght=GaugeWidth/2-GaugeWidth/7.5-StrokeWidth;
  for (int Division=0;Division<NumberOfScaleMinorDivisions+1;Division++) 
  { 
    an=SpanAngle/2+Division*SpanAngle/NumberOfScaleMinorDivisions;  
    DivxPhasorCloser=DivCloserPhasorLenght*cos(radians(an)); 
    DivxPhasorDistal=DivDistalPhasorLenght*cos(radians(an)); 
    DivyPhasorCloser=DivCloserPhasorLenght*sin(radians(an)); 
    DivyPhasorDistal=DivDistalPhasorLenght*sin(radians(an));   
    line(DivxPhasorCloser, DivyPhasorCloser, DivxPhasorDistal, DivyPhasorDistal); 
  }
  DivCloserPhasorLenght=GaugeWidth/2-GaugeWidth/10-StrokeWidth; 
  DivDistalPhasorLenght=GaugeWidth/2-GaugeWidth/7.4-StrokeWidth;
  for (int Division=0;Division<NumberOfScaleMajorDivisions+1;Division++) 
  { 
    an=SpanAngle/2+Division*SpanAngle/NumberOfScaleMajorDivisions;  
    DivxPhasorCloser=DivCloserPhasorLenght*cos(radians(an)); 
    DivxPhasorDistal=DivDistalPhasorLenght*cos(radians(an)); 
    DivyPhasorCloser=DivCloserPhasorLenght*sin(radians(an)); 
    DivyPhasorDistal=DivDistalPhasorLenght*sin(radians(an)); 
    if (Division==NumberOfScaleMajorDivisions/2|Division==0|Division==NumberOfScaleMajorDivisions) 
    { 
      strokeWeight(15); 
      stroke(0); 
      line(DivxPhasorCloser, DivyPhasorCloser, DivxPhasorDistal, DivyPhasorDistal); 
      strokeWeight(8); 
      stroke(100, 255, 100); 
      line(DivxPhasorCloser, DivyPhasorCloser, DivxPhasorDistal, DivyPhasorDistal); 
    } 
    else 
    { 
      strokeWeight(3); 
      stroke(255); 
      line(DivxPhasorCloser, DivyPhasorCloser, DivxPhasorDistal, DivyPhasorDistal); 
    } 
  } 
}
void Axis() 
{ 
  stroke(255, 0, 0); 
  strokeWeight(3); 
  line(-115, 0, 115, 0); 
  line(0, 280, 0, -280); 
  fill(100, 255, 100); 
  stroke(0); 
  triangle(0, -285, -10, -255, 10, -255); 
  triangle(0, 285, -10, 255, 10, 255); 
}
void ShowAngles() 
{ 
  textSize(30); 
  fill(50); 
  noStroke(); 
  rect(-150, 400, 280, 40); 
  rect(150, 400, 280, 40); 
  rect(-150, 449, 280, 40); 
  rect(-150, 496, 280, 40); 
  rect(150, 449, 280, 40);
  rect(150, 496, 280, 40); 
  fill(255); 
  Pitch=Pitch/5; 
  int Pitch1=round(Pitch);  
  text("Pitch:  "+Pitch1+" Deg", -20, 411, 500, 60); 
  text("Bank:  "+Bank*100+" Deg", 280, 411, 500, 60); 
  text("Lat: " + Lat, -20, 460, 500, 60); 
  text("" + gDate, -20, 505, 500, 60);
  text("Lng: " + Lng, 280, 460, 500, 60); 
  text("" + gTime, 280, 505, 500, 60); 
}
void Borders() 
{ 
  noFill(); 
  stroke(0); 
  strokeWeight(400); 
  rect(0, 0, 1100, 1100); 
  strokeWeight(200); 
  ellipse(0, 0, 1000, 1000); 
  fill(0); 
  noStroke(); 
  rect(4*W/5, 0, W, 2*H); 
  rect(-4*W/5, 0, W, 2*H); 
}
void PitchScale() 
{  
  stroke(255); 
  fill(255); 
  strokeWeight(3); 
  textSize(24); 
  textAlign(CENTER); 
  for (int i=-4;i<5;i++) 
  {  
    if ((i==0)==false) 
    { 
      line(110, 50*i, -110, 50*i); 
    }  
    text(""+i*10, 140, 50*i, 100, 30); 
    text(""+i*10, -140, 50*i, 100, 30); 
  } 
  textAlign(CORNER); 
  strokeWeight(2); 
  for (int i=-9;i<10;i++) 
  {  
    if ((i==0)==false) 
    {    
      line(25, 25*i, -25, 25*i); 
    } 
  } 
}

Credits

Dana Mah

Dana Mah

12 projects • 27 followers
I'm a hobbyist interested in microcontroller solutions to simple problems.

Comments