#include <Wire.h>
#include <LIS3MDL.h>
#include <SPI.h>
#include <SD.h>
#include <TinyGPS++.h>
#define CONFIG 0
#define STANDBY 1
#define COMPENSACAO 2
#define TESTES 3
#define GRAVAR 4
#define BUTTON1 2
#define BUTTON2 3
#define BUTTON3 4
#define RED 5
#define GREEN 6
#define BLUE 7
LIS3MDL mag;
int state = STANDBY;
//#define ARDUINO_SD_CS 4 //SD Shield 3.0
#define ARDUINO_SD_CS 10 //Adafruit Breakout Micro-SD
int fileCount = 0;
int testCount = 0;
int countValues = 0;
unsigned long initialTime = millis();
boolean calibrated = false;
//arduino example code for getting the calibrated magnetometer data
//calibrated_values[3] is the global array where the calibrated data will be placed
//calibrated_values[3]: [0]=Xc, [1]=Yc, [2]=Zc float calibrated_values[3];
float magBias[3],magScale[3];
float mRes = 10.*4912./32760.0;
//transformation(float uncalibrated_values[3]) is the function of the magnetometer data correction
//uncalibrated_values[3] is the array of the non calibrated magnetometer data
//uncalibrated_values[3]: [0]=Xnc, [1]=Ync, [2]=Znc
float uncalibrated_values[3];
/////////////////////////
// Log File Defintions //
/////////////////////////
// Keep in mind, the SD library has max file name lengths of 8.3 - 8 char prefix,
// and a 3 char suffix.
// Our log files are called "gpslogXX.csv, so "gpslog99.csv" is our max file.
#define LOG_FILE_PREFIX "gpslog" // Name of the log file.
#define MAX_LOG_FILES 100 // Number of log files that can be made
#define MAX_COUNT_VALUES 1500 // Number of log values that can be made for calibration
#define LOG_FILE_SUFFIX "csv" // Suffix of the log file
char logFileName[13]; // Char string to store the log file name
// Data to be logged:
#define LOG_COLUMN_COUNT 13
char * log_col_names[LOG_COLUMN_COUNT] = {"milis", "longitude", "latitude", "altitude", "speed", "course",
"date", "time", "satellites", "X", "Y", "Z", "Mag"}; // log_col_names is printed at the top of the file.
//////////////////////
// Log Rate Control //
//////////////////////
#define LOG_RATE 10 // 10 Hz
unsigned long lastLog = 0; // Global var to keep of last time we logged unsigned long startTime = 0; // Global var to get start time log unsigned long saveSDTime = 0; // Global var to get start time log
/////////////////////////
// TinyGPS Definitions //
/////////////////////////
TinyGPSPlus tinyGPS; // tinyGPSPlus object to be used throughout
#define GPS_BAUD 9600 // GPS module's default baud rate
#define SERIAL_BAUD 115200 // Serial default baud rate
////////////////////////////////////////////////
// Arduino GPS Shield Serial Port Definitions //
////////////////////////////////////////////////
// If you're using an Arduino Uno, Mega, RedBoard, or any board that uses the
// 0/1 UART for programming/Serial monitor-ing, use SoftwareSerial:
#include <SoftwareSerial.h>
#define ARDUINO_GPS_RX 9 // GPS TX, Arduino RX pin
#define ARDUINO_GPS_TX 8 // GPS RX, Arduino TX pin
SoftwareSerial ssGPS(ARDUINO_GPS_RX, ARDUINO_GPS_TX); // Create a
SoftwareSerial
// Set gpsPort to either ssGPS if using SoftwareSerial or Serial1 if using an
// Arduino with a dedicated hardware serial port
//#define gpsPort ssGPS // Alternatively, use Serial1 on the Leonardo
// Define the serial monitor port. On the Uno, Mega, and Leonardo this is 'Serial'
// on other boards this may be 'SerialUSB'
#define SerialMonitor Serial
File logFile; // Open the log file void setup()
{ SerialMonitor.begin(SERIAL_BAUD); Wire.begin(); pinMode(BUTTON1,INPUT_PULLUP); pinMode(BUTTON2,INPUT_PULLUP); pinMode(BUTTON3,INPUT_PULLUP); pinMode(RED,OUTPUT); pinMode(GREEN,OUTPUT); pinMode(BLUE,OUTPUT);
if (!mag.init())
{
SerialMonitor.println("Failed to detect and initialize magnetometer!");
while (1);
} mag.enableDefault(); delay(500); ssGPS.begin(GPS_BAUD); delay(500);
SerialMonitor.print("Initializing SD card...");
// see if the card is present and can be initialized: if (!SD.begin(ARDUINO_SD_CS)) { SerialMonitor.println("Card failed, or not present");
// don't do anything more:
return;
}
SerialMonitor.println("Card initialized.");
delay(500);
}
void loop()
{
automato();
}
void medirValores()
{
mag.read();
uncalibrated_values[0] = mag.m.x / 100.0;
uncalibrated_values[1] = mag.m.y / 100.0;
uncalibrated_values[2] = mag.m.z / 100.0;
// Calculate the magnetometer values in milliGauss
// Include factory calibration per data sheet and user environmental corrections
//uncalibrated_values[0] = (float)mag.m.xmRes - magBias[0]; // get actual magnetometer value, this depends on scale being set
//uncalibrated_values[1] = (float)mag.m.ymRes - magBias[1];
//uncalibrated_values[2] = (float)mag.m.z*mRes - magBias[2];
transformation(uncalibrated_values);
}
void magValues()
{
if(countValues==0){
logFile = SD.open("calibValues.csv", FILE_WRITE);
logFile.println("X,Y,Z");
}
mag.read();
uncalibrated_values_for_calib[0] = mag.m.x / 100.0; uncalibrated_values_for_calib[1] = mag.m.y / 100.0; uncalibrated_values_for_calib[2] = mag.m.z / 100.0; logFile.print(uncalibrated_values_for_calib[0]); logFile.print(','); logFile.print(uncalibrated_values_for_calib[1]); logFile.print(','); logFile.print(uncalibrated_values_for_calib[2]); logFile.println();
logFile.close();
logFile = SD.open(logFileName, FILE_WRITE);
}
void mostrarValores()
{
//Serial.flush();
if ((lastLog + 1000/LOG_RATE) <= millis())
{
lastLog = millis(); // Update the lastLog variable SerialMonitor.print(millis() - startTime); SerialMonitor.print(","); SerialMonitor.print(tinyGPS.location.lng(),7); SerialMonitor.print(","); SerialMonitor.print(tinyGPS.location.lat(),7); SerialMonitor.print(","); SerialMonitor.print(tinyGPS.altitude.meters()); SerialMonitor.print(",");
SerialMonitor.print(tinyGPS.speed.kmph()); SerialMonitor.print(","); SerialMonitor.print(tinyGPS.course.deg()); SerialMonitor.print(","); SerialMonitor.print(tinyGPS.date.value()); SerialMonitor.print(","); SerialMonitor.print(tinyGPS.time.value()); SerialMonitor.print(","); SerialMonitor.print(tinyGPS.satellites.value()); SerialMonitor.print(","); SerialMonitor.print(calibrated_values[0]); SerialMonitor.print(","); SerialMonitor.print(calibrated_values[1]); SerialMonitor.print(","); SerialMonitor.print(calibrated_values[2]); SerialMonitor.print(",");
SerialMonitor.print(sqrt(sq(calibrated_values[0]) + sq(calibrated_values[1])
- sq(calibrated_values[2]))); SerialMonitor.println();
}
}
void transformation(float uncalibrated_values[3])
{
//calibration_matrix[3][3] is the transformation matrix
//replace M11, M12,..,M33 with your transformation matrix data
//inicial calibration
/* double calibration_matrix[3][3] =
{
{1.98, 0.144, -0.213},
{-0.056, 2.047, 0.099},
{0.073, 0.011, 2.047}
};
//bias[3] is the bias
//replace Bx, By, Bz with your bias data double bias[3] =
{
24.986,
-15.469,
-7.664
}; */
//local calibration (Trajouce)
double calibration_matrix[3][3] =
{
{1.548, 0.032, 0.014},
{0.07, 1.61, 0.023},
{0.006, -0.043, 1.646}
};
//bias[3] is the bias
//replace Bx, By, Bz with your bias data double bias[3] =
{
16.907,
-7.197,
-8.028
};
/*//local calibration (Venda Nova)
double calibration_matrix[3][3] =
{
{1.494, 0.02, -0.022},
{0.051, 1.522, 0.102},
{-0.004, -0.06, 1.558}
};
//bias[3] is the bias
//replace Bx, By, Bz with your bias data double bias[3] =
{
16.687,
-7.782,
-7.733
}; */
//calculation
for (int i=0; i<3; ++i) uncalibrated_values[i] = uncalibrated_values[i] - bias[i];
float result[3] = {0, 0, 0};
for (int i=0; i<3; ++i)
for (int j=0; j<3; ++j)
result[i] += calibration_matrix[i][j] * uncalibrated_values[j];
for (int i=0; i<3; ++i) calibrated_values[i] = result[i];
}
void automato()
{ switch(state){ case STANDBY: whiteON();
SerialMonitor.println(state);
if(digitalRead(BUTTON1)==LOW){
SerialMonitor.println("Compensacao");
count = 0;
state = COMPENSACAO;
}
else if(digitalRead(BUTTON2)==LOW){ SerialMonitor.println("Test" + String(testCount) + ":"); state = TESTES;
lastLog = 0;
startTime = millis();
}
else if(digitalRead(BUTTON3)==LOW){
// open the file. note that only one file can be open at a time,
// so you have to close this one before opening another. updateFileName(); // Each time we start, create a new file, increment the number
logFile = SD.open(logFileName, FILE_WRITE); printHeader(); // Print a header at the top of the new file state = GRAVAR;
lastLog = 0;
startTime = millis();
}
break;
case COMPENSACAO:
redON(); if(!calibrated){ if(countValues<MAX_COUNT_VALUES) magValues();
countValues++;
}else{ logFile.close(); calibrated = true;
}
} else{ redOFF(); delay(500);
}
if(digitalRead(BUTTON2)==LOW || digitalRead(BUTTON3)==LOW){
logFile.close(); whiteON(); delay(1000);
state = STANDBY;
}
break;
case TESTES:
greenON();
if(digitalRead(BUTTON1)==LOW || digitalRead(BUTTON3)==LOW){
whiteON();
delay(1000);
state = STANDBY;
} medirValores(); mostrarValores();
//delay(100);
break;
case GRAVAR: blueON(); medirValores(); gpsTracker();
//delay(100);
if(digitalRead(BUTTON1)==LOW || digitalRead(BUTTON2)==LOW){
logFile.close(); whiteON(); fileCount++; delay(1000);
state = STANDBY;
}
break;
}
}
void whiteON(){ digitalWrite(RED,HIGH); digitalWrite(GREEN,HIGH); digitalWrite(BLUE,HIGH);
}
void redON(){ digitalWrite(RED,HIGH); digitalWrite(GREEN,LOW); digitalWrite(BLUE,LOW);
}
void redOFF(){ digitalWrite(RED,LOW); digitalWrite(GREEN,LOW); digitalWrite(BLUE,LOW);
}
void greenON(){
digitalWrite(RED,LOW); digitalWrite(GREEN,HIGH); digitalWrite(BLUE,LOW);
}
void blueON(){ digitalWrite(RED,LOW); digitalWrite(GREEN,LOW); digitalWrite(BLUE,HIGH);
}
void magCalibration(float * dest1, float * dest2) {
uint16_t ii = 0, sample_count = 0;
int32_t mag_bias[3] = {0, 0, 0}, mag_scale[3] = {0, 0, 0};
int16_t mag_max[3] = {0x8000, 0x8000, 0x8000}, mag_min[3] = {0x7FFF, 0x7FFF,
0x7FFF}, mag_temp[3] = {0, 0, 0};
SerialMonitor.println("Mag Calibration: Wave device in a figure eight until done!");
sample_count = 1500; // at 100 Hz ODR, new mag data is available every 10 ms for(ii = 0; ii < sample_count; ii++) {
mag.read(); // Read the mag data mag_temp[0] = mag.m.x; mag_temp[1] = mag.m.y; mag_temp[2] = mag.m.z;
for (int jj = 0; jj < 3; jj++) { if(mag_temp[jj] > mag_max[jj]) mag_max[jj] = mag_temp[jj]; if(mag_temp[jj] < mag_min[jj]) mag_min[jj] = mag_temp[jj];
}
delay(135); // at 8 Hz ODR, new mag data is available every 125 ms
}
// Get hard iron correction
mag_bias[0] = (mag_max[0] + mag_min[0])/2; // get average x mag bias in counts mag_bias[1] = (mag_max[1] + mag_min[1])/2; // get average y mag bias in counts mag_bias[2] = (mag_max[2] + mag_min[2])/2; // get average z mag bias in counts dest1[0] = (float) mag_bias[0]*mRes; // save mag biases in G for main program dest1[1] = (float) mag_bias[1]*mRes;
dest1[2] = (float) mag_bias[2]*mRes;
// Get soft iron correction estimate
mag_scale[0] = (mag_max[0] - mag_min[0])/2; // get average x axis max chord length in counts
mag_scale[1] = (mag_max[1] - mag_min[1])/2; // get average y axis max chord length in counts
mag_scale[2] = (mag_max[2] - mag_min[2])/2; // get average z axis max chord
length in counts
float avg_rad = mag_scale[0] + mag_scale[1] + mag_scale[2];
avg_rad /= 3.0;
dest2[0] = avg_rad/((float)mag_scale[0]); dest2[1] = avg_rad/((float)mag_scale[1]); dest2[2] = avg_rad/((float)mag_scale[2]); SerialMonitor.println("Mag Calibration done!");
}
byte writeSDCard()
{
if (logFile)
{ // Print longitude, latitude, altitude (in feet), speed (in mph), course
// in (degrees), date, time, and number of satellites. logFile.print(millis() - startTime);
logFile.print(','); logFile.print(tinyGPS.location.lng(), 7); logFile.print(','); logFile.print(tinyGPS.location.lat(), 7); logFile.print(','); logFile.print(tinyGPS.altitude.meters(), 1); logFile.print(','); logFile.print(tinyGPS.speed.kmph(), 1); logFile.print(','); logFile.print(tinyGPS.course.deg(), 1);
logFile.print(','); logFile.print(tinyGPS.date.value()); logFile.print(','); logFile.print(tinyGPS.time.value()); logFile.print(','); logFile.print(tinyGPS.satellites.value()); logFile.print(','); logFile.print(calibrated_values[0]); logFile.print(','); logFile.print(calibrated_values[1]); logFile.print(','); logFile.print(calibrated_values[2]);
logFile.print(',');
logFile.print(sqrt(sq(calibrated_values[0]) + sq(calibrated_values[1]) +
sq(calibrated_values[2]))); logFile.println(); logFile.close();
logFile = SD.open(logFileName, FILE_WRITE);
return 1; // Return success
}
return 0; // If we failed to open the file, return fail
}
// printHeader() - prints our eight column names to the top of our log file void printHeader()
{
if (logFile) // If the log file opened, print our column names to the file
{
int i = 0;
for (; i < LOG_COLUMN_COUNT; i++)
{
logFile.print(log_col_names[i]);
if (i < LOG_COLUMN_COUNT - 1) // If it's anything but the last column logFile.print(','); // print a comma
else // If it's the last column logFile.println(); // print a new line
}
}
}
// updateFileName() - Looks through the log files already present on a card,
// and creates a new file with an incremented file index. void updateFileName()
{
int i = 0;
for (; i < MAX_LOG_FILES; i++)
{
memset(logFileName, 0, strlen(logFileName)); // Clear logFileName string
// Set logFileName to "gpslogXX.csv":
sprintf(logFileName, "%s%d.%s", LOG_FILE_PREFIX, i, LOG_FILE_SUFFIX);
if (!SD.exists(logFileName)) // If a file doesn't exist
{
break; // Break out of this loop. We found our index
}
else // Otherwise:
{ SerialMonitor.print(logFileName);
SerialMonitor.println(" exists"); // Print a debug statement
}
}
SerialMonitor.print("File name: "); SerialMonitor.println(logFileName); // Debug print the file name
}
void gpsTracker()
{
if ((lastLog + 1000/LOG_RATE) <= millis())
{ // If it's been LOG_RATE milliseconds since the last log:
if (tinyGPS.location.isValid()) // If the GPS data is vaild
{
lastLog = millis(); // Update the lastLog variable if (writeSDCard()) // Log the GPS data
{
SerialMonitor.println("GPS logged."); // Print a debug message
}
else // If we failed to log GPS
{ // Print an error, don't update lastLog
SerialMonitor.println("Failed to log new GPS data.");
}
}
else // If GPS data isn't valid
{
// Print a debug message. Maybe we don't have enough satellites yet. SerialMonitor.print("No GPS data. Sats: "); SerialMonitor.println(tinyGPS.satellites.value());
}
}
// If we're not logging, continue to "feed" the tinyGPS object:
while (ssGPS.available())
tinyGPS.encode(ssGPS.read());
}