Hardware components | ||||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 |
I wanted to create a device that would log the movement of a boat while fishing. The features of the Arduino 101 contained much of the hardware required to do this. The only pieces missing was a GPS sensor and a SD card reader/writer and a battery.
The Arduino 101 is ideal because it contained a 6 axis accelerometer and gyroscope.
In my project, I used the accelerometer and gyroscope data from the Arduino 101, multiple pieces of data from the GPS and write that data to a SD card. I was also able to use Aritro Mukherjee - DIY Flight Instruments for Horizon and Compass with a few modifications to connect the data logger to a PC using over the usb debug port that allowed me to add visualization to what to logger was doing.
A few things that would like to add to this project would be to use the onboard bluetooth to send the data to a mobile device, either a phone or tablet, and to convert the PC part to a mobile app. So that the everything could be mounted and wireless.
Some code changes I plan to make are:
- Add a serial event, this will allow me to make configuration changes to the data logger using the processing app.
- Add a map to the processing app, this will allow me to plot the gps coordinates from the data logger.
- Way points, it would be nice to send the data logger a way point and have the processing app point the direction to it.
Some hardware changes:
- A compass module, it would be nice to have a true compass instead of using the gps and accelerometer/gyroscope to determine direction.
- Temperature sensor
All in all, this project was fairly simple to make, most of the code was taken from samples for the various parts and altered a bit to fit my needs. Hopefully if you are looking at this project your able to use the code provided to help build your own project. In the code I had left a lot of print statements so you can use the serial monitor to follow what the code is doing. Otherwise if you need to save space most of the print statements can be removed.
Thanks.
//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;
}
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);
}
}
}
Comments