#include <SPI.h>
#include <SD.h>
#include <Keypad.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 16, 2);
#define PIN_CTRL A6 // listen stop button pin
const byte PIN_SD_CS = 10; // chip select pin for SD card
const byte PIN_PPS = 3; // 1PPS from GPS chip
const byte PIN_SENSOR = 2; // Vibration sensor pin
const uint16_t DEL = 500; // Aesthetic delay, ms
const uint32_t TIMEOUT = 300000; // Acquisition timeout window, ms
//--------------------------------- Initializing keypad ----------------------------------
const byte ROWS = 4;
const byte COLS = 4;
char keys[ROWS][COLS] = {
{ '1', '2', '3', 'A' },
{ '4', '5', '6', 'B' },
{ '7', '8', '9', 'C' },
{ '*', '0', '#', 'D' }
};
byte colPins[COLS] = { A3, A2, A1, A0 }; // Pins connected to C1, C2, C3, C4
byte rowPins[ROWS] = { 7, 6, 5, 4 }; // Pins connected to R1, R2, R3, R4
Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS);
//----------------------------------------------------------------------------------------
uint32_t position[] = {0, 0}; // position consists of line and point numbers
uint16_t FFID = 0;
bool error = false; // this flag is set if there is a problem (i.e. log file corrupt etc...)
volatile uint32_t pps_ms = 0; // Milliseconds count at PPS occurance
volatile uint32_t event_ms = 0; // Milliseconds count at event occurance
volatile bool event_ = false; // Vibration sensor event
void setup() {
Serial.begin(9600); // not needed in real version
lcd.init();
lcd.backlight();
pinMode(PIN_SD_CS, OUTPUT);
pinMode(PIN_PPS, INPUT);
pinMode(8, OUTPUT); // to simulate 1PPS
//------------------------------- Checking if micro SD card is present and functional ----------------------
lcd.print("SDcard init...");
lcd.setCursor(0, 1);
if (!SD.begin(PIN_SD_CS)) {
lcd.print(" failed");
error = true;
return;
}
//------------------------------- Checking/creating log.txt and setting up FFID ----------------------------
if (!SD.exists("log.txt")) {
File logfile = SD.open("log.txt", FILE_WRITE);
if (logfile) {
logfile.println("file_cnt;line_number;shot_point;year-month-day;hour:minute:sec.microsec;uht;gga");
logfile.close();
FFID = 1;
}
else {
lcd.print("file corrupt");
error = true;
return;
}
}
else {
File logfile = SD.open("log.txt");
if (logfile) {
while (logfile.available()) {
if (int(logfile.read()) == 13) FFID++;
}
logfile.close();
}
else {
lcd.print("file corrupt");
error = true;
return;
}
}
delay(DEL);
lcd.setCursor(0, 1);
lcd.print("Successful");
delay(DEL*2);
}
void loop() {
if (!error) { // Checking there is no global error
if (PositionSet(0)) { // Setting line number
if (PositionSet(1)) { // Setting point number
attachInterrupt(digitalPinToInterrupt(PIN_PPS), pps_isr, RISING);
//---------------------------- Waiting for 1PPS ---------------------------------------
//while ((millis() - pps_ms) > 1000) { // uncomment when 1PPS available
lcd.clear();
lcd.print("Waiting for 1PPS");
delay(DEL);
//} // uncomment when 1PPS available
//--------------------------------------------------------------------------------------
lcd.clear();
lcd.print("Go...");
lcd.setCursor(0, 1);
lcd.print("push btn to abrt");
uint32_t timeout_start_ms = millis(); // Starting timeout clock
event_ = false;
attachInterrupt(digitalPinToInterrupt(PIN_SENSOR), event_isr, RISING);
unsigned long ms_cnt_old = 0; // to simulate 1PPS
unsigned long ms_cnt_new; // to simulate 1PPS
while(!event_ && analogRead(PIN_CTRL) < 500 && millis() < (timeout_start_ms + TIMEOUT)) {
ms_cnt_new = millis(); // to simulate 1PPS
if ((ms_cnt_new - ms_cnt_old) > 1000) { // to simulate 1PPS
digitalWrite(8, HIGH); // to simulate 1PPS
delay(1); // to simulate 1PPS
digitalWrite(8, LOW); // to simulate 1PPS
ms_cnt_old = ms_cnt_new; // to simulate 1PPS
} // to simulate 1PPS
}
detachInterrupt(digitalPinToInterrupt(PIN_SENSOR));
detachInterrupt(digitalPinToInterrupt(PIN_PPS));
if (!event_) {
lcd.clear();
if (analogRead(PIN_CTRL) > 500) lcd.print(" ABORTING...");
else lcd.print(" TIMEOUT");
lcd.noCursor();
delay(4*DEL);
}
else {
uint16_t ms = event_ms - pps_ms;
String ms_str = String(ms);
if (ms < 10) ms_str = "00" + ms_str;
else if (ms < 100) ms_str = "0" + ms_str;
//read date and gga here...
lcd.clear();
lcd.print("hh:mm:ss." + ms_str); // insert real time when GNSS chip is available
lcd.setCursor(0, 1);
lcd.print("Recording data..");
delay(4*DEL);
if (SD.exists("log.txt")) {
String logDataString = String(FFID) + ';' + String(position[0]) + ';' + String(position[1]) + ';' + "yyyy-mm-dd;hh:mm:ss." + ms_str + "000;0.0;$GNGGA,................"; // constructing line to be added to the log file on SD card
File logfile = SD.open("log.txt", FILE_WRITE);
if (logfile) {
logfile.println(logDataString);
logfile.close();
FFID++;
}
else {
lcd.setCursor(0, 1);
lcd.print(" ");
lcd.setCursor(0, 1);
lcd.print("file corrupt");
lcd.noCursor();
error = true; // setting global error flag UP
}
}
else {
lcd.setCursor(0, 1);
lcd.print(" ");
lcd.setCursor(0, 1);
lcd.print("file corrupt");
lcd.noCursor();
error = true; // setting global error flag UP
}
}
////////// Reading file back from SD card just for testing. To be removed. ////////////////////
File FileToRead = SD.open("log.txt");
if (FileToRead) {
Serial.println("Reading file log.txt");
while (FileToRead.available()) {
Serial.write(FileToRead.read());
}
//SD.remove("log.txt");
FileToRead.close();
}
else {
Serial.println("error opening log.txt");
}
//////////////////////////////////////////////////////////////////////////////////////////////////////
}
}
}
}
///////////////////////////// Procedures and Functions ////////////////////////////////
void pps_isr() { // 1PPS signal interrupt service routine
pps_ms = millis();
// read time only
}
void event_isr() { // Vibration sensor event interrupt service routine
event_ = true;
event_ms = millis();
}
bool PositionSet(int param) { // Entering line/point number from keypad
lcd.clear();
if (param == 0) lcd.print("Enter line num:");
else if (param == 1) lcd.print("Enter point num:");
lcd.setCursor(0, 1);
if (position[param]) lcd.print(String(position[param]));
char key;
while (true) {
updateCursor();
key = keypad.getKey();
if (key) {
switch (key) {
case 'A':
if (position[param] < 999999999) position[param]++;
break;
case 'B':
if (position[param] > 1) position[param]--;
break;
case 'C':
position[param] = position[param] / 10; // uint32_t divided by 10 and converted back to uint32_t
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
if (position[param] < 100000000) {
position[param] = position[param] * 10 + (key - '0'); // (char - '0') - converts single char to int
break;
}
}
if (key == '#') break;
else if (key == 'D') {
if (position[param] > 0 && position[param] < 1000000000) {
break;
}
}
lcd.setCursor(0, 1);
lcd.print(" ");
lcd.setCursor(0, 1);
if (position[param] > 0) lcd.print(position[param]);
}
}
if (key == '#') return false;
else return true;
}
void updateCursor() { // Blinking cursor
if (millis() / 250 % 2 == 0 ) {
lcd.cursor();
} else {
lcd.noCursor();
}
}