#include <Arduino.h>
#include <ESP32Time.h>
ESP32Time rtc;
#define _DEBUG_
//#define _BUTTON_
#include "GPSport_config.h"
#include "NMEAGPS.h"
//#include <GPSport.h>
NMEAGPS gps; // This parses the GPS characters
gps_fix fix; // This holds on to the latest values
#include <SPI.h>
SPIClass SD_SPI = SPIClass(HSPI);
//#include <TFT_eSPI.h>
//TFT_eSPI tft = TFT_eSPI();
//#include "SdFat_config.h"
//#include <SdFat.h>
#include <SD.h>
//SdFat32 SD;
File trkFile;
//#include "U-blox_logo.h"
#ifdef _BUTTON_
const byte BTN_PIN = 27;
#endif
// 2nd. SPI pins
const byte SD_SPI_SCK = 14;
const byte SD_SPI_MOSI = 13;
const byte SD_SPI_MISO = 12;
const byte SD_SPI_CS = 15;
const byte SER2_RX = 16;
const byte SER2_TX = 17;
const byte SD_LED = 26;
const byte PWR_LED = 4;
const byte PWR_PIN = 32; // ADC4
const byte TFT_BLK = 21;
const char KITT[6][4] = {{'-', ' ', ' ', ' '}, {' ', '-', ' ', ' '},
{' ', ' ', '-', ' '}, {' ', ' ', ' ', '-'},
{' ', ' ', '-', ' '}, {' ', '-', ' ', ' '}
};
const int32_t LOCAL_TIMEZONE = -3;
const int SECONDS_PER_HOUR = 3600;
const int32_t TIMEZONE_OFFSET = LOCAL_TIMEZONE * SECONDS_PER_HOUR;
const uint32_t DEBOUNCER_TIME = 20UL;
const uint32_t BATTERY_SAMPLE_TIME = 1 * 60000UL; // 5 min en mseg
const uint32_t BLINK_INTERVAL = 1000UL;
const uint32_t CHKBATT_INTERVAL = 1000UL;
const float VREF = 4.20; //5.0;
const float K_FACTOR = VREF / 4096.0; //1024.0;
const float VOLTS_PCT[21] = {3.31,3.68,3.69,3.71,3.74,3.76,3.77,3.78,
3.79,3.81,3.82,3.84,3.88,3.92,3.95,3.98,4.00,4.05,4.09,4.13,4.18};
bool validSDcard = true;
bool createFile = true;
bool validFix = false;
bool validTime = false;
bool validDate = false;
bool sdIndicator = false;
bool lowBattery = false;
bool chkBattery = true;
bool pwrLedStatus = true;
byte battPct;
#ifdef _BUTTON_
bool lastButtonState = false;
uint32_t showtimeTime;
#endif
uint32_t tkpCounter;
uint32_t sdIndicatorTime;
uint32_t repeatsCounter;
uint32_t repeatsControl;
uint32_t repeatsInterval;
uint32_t batteryTimeOld;
uint32_t blinkTimeOld;
uint32_t chkTimeOld;
String fileName;
struct memory_s {
float lat;
float lng;
float altitude;
float speed;
float course;
} memory;
enum dmode_e {
SHOWSPEED,
SHOWHOUR,
SHOWSECS,
SHOWBATT,
SHOWBATT_OK
};
dmode_e displayMode = SHOWSPEED;
NeoGPS::time_t localTime;
//------------------------------------------------------------------------------
// convierte UTC (del GPS) a hora local
void get_local_time(NeoGPS::time_t dt) {
NeoGPS::clock_t seconds = dt; // convert date/time structure to seconds
seconds += TIMEZONE_OFFSET;
localTime = seconds; // convert seconds back to a date/time structure
}
//------------------------------------------------------------------------------
#ifdef _BUTTON_
bool get_button(byte trigger) { // return true on pressed
bool state = false;
static bool oldState = false;
static bool debouncer = false;
static uint32_t oldTime = 0;
if (debouncer) {
if (millis() - oldTime >= DEBOUNCER_TIME) {
debouncer = false;
}
}
else {
state = (digitalRead(BTN_PIN) == trigger);
if (state != oldState) {
oldState = state;
debouncer = true;
oldTime = millis();
}
}
return oldState;
}
#endif
//------------------------------------------------------------------------------
void check_battery() {
static int count = 0;
static int samplesAcc = 0;
if (count < 10) {
samplesAcc += analogRead(PWR_PIN);
// Serial.println(analogRead(PWR_PIN));
count++;
}
else {
float vBatt = samplesAcc / count * K_FACTOR;
#ifdef _DEBUG_
DEBUG_PORT.print(vBatt, 2);
DEBUG_PORT.print(F("V - ADC: "));
DEBUG_PORT.println(samplesAcc / count);
#endif
for (int i = 20; i >= 0; i--) {
if (vBatt >= VOLTS_PCT[i]) {
battPct = i * 5;
break;
}
else {
battPct = 0;
}
}
lowBattery = battPct <= 5;
samplesAcc = 0;
count = 0;
chkBattery = false;
}
}
//------------------------------------------------------------------------------
void read_gps_fix() {
while (gps.available(gpsPort)) {
fix = gps.read();
validFix = gps.is_safe();
}
}
//------------------------------------------------------------------------------
/*
// call back for file timestamps
void dateTime(uint16_t* date, uint16_t* time) {
// return date using FAT_DATE macro to format fields
*date = FAT_DATE(localTime.year + 2000, localTime.month, localTime.date);
// return time using FAT_TIME macro to format fields
*time = FAT_TIME(localTime.hours, localTime.minutes, localTime.seconds);
}
*/
//------------------------------------------------------------------------------
void setup() {
pinMode(PWR_LED, OUTPUT);
digitalWrite(PWR_LED, HIGH);
pinMode(SD_LED, OUTPUT);
digitalWrite(SD_LED, LOW);
/*
tft.begin();
tft.setRotation(0);
tft.fillScreen(TFT_BLACK);
tft.pushImage(88, 114, 64, 64, Ublox_logo);
digitalWrite(TFT_BLK, HIGH);
tft.setTextColor(TFT_RED, TFT_BLACK);
tft.setTextSize(3);
String introStr = "NEO-6M";
tft.setCursor((240 - tft.textWidth(introStr)) / 2, 160);
tft.print(introStr);
delay(100);
digitalWrite(LED_BUILTIN, LOW);
delay(2900);
*/
#ifdef _BUTTON_
pinMode(BTN_PIN, INPUT_PULLUP);
#endif
fileName.reserve(21);
#ifdef _DEBUG_
DEBUG_PORT.begin(115200);
#endif
gpsPort.begin(19200);
// SD.begin(SD_SPI_SCK, SD_SPI_MISO, SD_SPI_MOSI, SD_SPI_CS);
validSDcard = SD.begin(SD_SPI_CS, SD_SPI);
digitalWrite(SD_LED, validSDcard ? HIGH : LOW);
#ifdef _DEBUG_
DEBUG_PORT.print("SD Valida: ");
DEBUG_PORT.println(validSDcard ? "SI" : "NO");
#endif
/*
if(validSDcard) {
fileName = "adc.txt";
trkFile = SD.open(fileName, FILE_WRITE);
if(trkFile) {
for(int i=0; i<20;i++){
digitalWrite(SD_LED, LOW);
delay(100);
trkFile.println(analogRead(A0));
digitalWrite(SD_LED, HIGH);
delay(100);
}
trkFile.close();
}
}
*/
while (chkBattery) {
check_battery();
}
#ifdef _DEBUG_
DEBUG_PORT.print(F("Bateria: "));
DEBUG_PORT.print(battPct);
DEBUG_PORT.println(F("%"));
#endif
/*
uint32_t oldtime = millis();
byte i = 0;
while (!validFix) {
if (millis() - oldtime >= 200) {
oldtime = millis();
display.showStr(KITT[i]);
i = (i + 1) % 6;
}
read_gps_fix();
}
display.clear();
*/
delay(250);
// tft.fillScreen(TFT_BLACK);
// read_gps_fix();
}
void loop() {
#ifdef _BUTTON_
static uint32_t lastColonBlinkTime;
static uint32_t pressTime;
static bool hold = false;
bool newButtonState = get_button(LOW); // boton activo bajo
if (newButtonState && !lastButtonState) { // pulsado
pressTime = millis();
} else if (newButtonState && lastButtonState) { // retenido
if (!hold) {
if (millis() - pressTime >= 500UL) {
displayMode = SHOWBATT;
// display.clear();
hold = true;
}
}
} else if (!newButtonState && lastButtonState) { // liberado
if (hold) {
hold = false;
}
// display.clear();
switch (displayMode) {
case SHOWSPEED:
displayMode = SHOWHOUR;
lastColonBlinkTime = millis();
showtimeTime = lastColonBlinkTime;
break;
case SHOWHOUR:
displayMode = SHOWSECS;
lastColonBlinkTime = millis();
showtimeTime = lastColonBlinkTime;
break;
// case SHOWSECS:
// displayMode = SHOWSPEED;
// break;
// case SHOWBATT:
// displayMode = SHOWSPEED;
// break;
// case SHOWBATT_OK:
default:
displayMode = SHOWSPEED;
// break;
}
}
lastButtonState = newButtonState;
#endif
if (validFix) {
validDate = fix.valid.date && ((fix.dateTime.year + 2000) > 2020);
}
if (fix.valid.time && validDate) {
get_local_time(fix.dateTime);
rtc.setTime(localTime);
Serial.println(rtc.getSecond());
}
#ifdef _DISPLAY_
#ifdef _LUXOMETER_
if (validFix) {
display.brightness(get_brightness());
}
#endif
#ifdef _BUTTON_
switch (displayMode) {
case SHOWSPEED:
if (validFix && fix.valid.speed) {
int speed = fix.speed_kph() + 0.5;
display.showNum(speed);
}
break;
case SHOWHOUR:
if (fix.valid.time && validDate) {
int16_t time = localTime.hours * 100 + localTime.minutes;
display.showClk(time);
lastColonBlinkTime = millis();
}
break;
case SHOWSECS:
if (fix.valid.time && validDate) {
display.point();
display.showNum(localTime.seconds, true, 2);
lastColonBlinkTime = millis();
}
break;
case SHOWBATT:
display.showBar(battPct);
displayMode = SHOWBATT_OK;
break;
default:
break;
}
#else
if (validFix && fix.valid.speed) {
int speed = fix.speed_kph() + 0.5;
display.showNum(speed);
}
#endif
#endif
if (validSDcard) {
if (createFile) {
if (fix.valid.time && validDate) {
Serial.println("Valido");
char dt[21] = "/00000000_000000.csv";
int loc_year = localTime.year + 2000;
dt[1] = loc_year / 1000 + 48;
dt[2] = loc_year % 1000 / 100 + 48;
dt[3] = loc_year % 100 / 10 + 48;
dt[4] = loc_year % 10 + 48;
dt[5] = localTime.month / 10 + 48;
dt[6] = localTime.month % 10 + 48;
dt[7] = localTime.date / 10 + 48;
dt[8] = localTime.date % 10 + 48;
dt[10] = localTime.hours / 10 + 48;
dt[11] = localTime.hours % 10 + 48;
dt[12] = localTime.minutes / 10 + 48;
dt[13] = localTime.minutes % 10 + 48;
dt[14] = localTime.seconds / 10 + 48;
dt[15] = localTime.seconds % 10 + 48;
fileName = dt;
// set date time callback function
// SdFile::dateTimeCallback(dateTime);
Serial.println("fname: " + fileName);
trkFile = SD.open(fileName, FILE_WRITE);
Serial.println(trkFile);
if (trkFile) {
#ifdef _DEBUG_
DEBUG_PORT.println(fileName + F(" creado"));
#endif
sdIndicator = true;
sdIndicatorTime = millis();
digitalWrite(SD_LED, LOW);
trkFile.println(F("trackpoint,date,time,latitude,longitude,alt,speed,course"));
#ifdef _DEBUG_
DEBUG_PORT.println(F("trackpoint,date,time,latitude,longitude,alt,speed,course"));
#endif
//trkFile.sync();
trkFile.close();
createFile = false;
}
}
}
//DEBUG_PORT.println("validFix = " + String(validFix));
if (validFix) {
bool newGpsData = fix.latitude() != memory.lat;
newGpsData |= fix.longitude() != memory.lng;
newGpsData |= fix.altitude() != memory.altitude;
newGpsData |= fix.speed_kph() != memory.speed;
newGpsData |= fix.heading() != memory.course;
repeatsCounter++;
repeatsControl++;
if (repeatsControl > 340UL) repeatsInterval = 45UL;
else if (repeatsControl > 160UL) repeatsInterval = 30UL;
else if (repeatsControl > 70UL) repeatsInterval = 15UL;
else if (repeatsControl > 10UL) repeatsInterval = 10UL;
else repeatsInterval = 1UL;
if ((newGpsData) || (repeatsCounter >= repeatsInterval)) { // si los datos son repetitivos los guarda cada "repeatsInterval" seg
repeatsCounter = 0;
if (newGpsData) repeatsControl = 0;
memory.lat = fix.latitude();
memory.lng = fix.longitude();
memory.altitude = fix.altitude();
memory.speed = fix.speed_kph();
memory.course = fix.heading();
trkFile = SD.open(fileName, FILE_APPEND);
if (trkFile) {
sdIndicatorTime = millis();
sdIndicator = true;
digitalWrite(SD_LED, LOW);
trkFile.print(++tkpCounter);
trkFile.print(F(","));
#ifdef _DEBUG_
DEBUG_PORT.print(tkpCounter);
DEBUG_PORT.print(F(","));
#endif
char dt[12] = "0000/00/00,";
int loc_year = fix.dateTime.year + 2000;
dt[0] = loc_year / 1000 + 48;
dt[1] = loc_year % 1000 / 100 + 48;
dt[2] = loc_year % 100 / 10 + 48;
dt[3] = loc_year % 10 + 48;
// dt[4] = '/';
dt[5] = fix.dateTime.month / 10 + 48;
dt[6] = fix.dateTime.month % 10 + 48;
// dt[7] = dt[4];
dt[8] = fix.dateTime.date / 10 + 48;
dt[9] = fix.dateTime.date % 10 + 48;
// dt[10] = ',';
dt[11] = '\0';
trkFile.print(dt);
#ifdef _DEBUG_
DEBUG_PORT.print(dt);
#endif
strncpy(dt, "00:00:00,", 12);
dt[0] = fix.dateTime.hours / 10 + 48;
dt[1] = fix.dateTime.hours % 10 + 48;
// dt[2] = ':';
dt[3] = fix.dateTime.minutes / 10 + 48;
dt[4] = fix.dateTime.minutes % 10 + 48;
// dt[5] = dt[2];
dt[6] = fix.dateTime.seconds / 10 + 48;
dt[7] = fix.dateTime.seconds % 10 + 48;
// dt[8] = ',';
dt[9] = '\0';
trkFile.print(dt);
trkFile.print(fix.latitude(), 6) ;
trkFile.print(F(","));
trkFile.print(fix.longitude(), 6) ;
trkFile.print(F(","));
trkFile.print(fix.altitude(), 2);
trkFile.print(F(","));
trkFile.print(fix.speed_kph(), 2) ;
trkFile.print(F(","));
trkFile.println(fix.heading(), 2) ;
#ifdef _DEBUG_
DEBUG_PORT.print(dt);
DEBUG_PORT.print(fix.latitude(), 6) ;
DEBUG_PORT.print(F(","));
DEBUG_PORT.print(fix.longitude(), 6) ;
DEBUG_PORT.print(F(","));
DEBUG_PORT.print(fix.altitude(), 2);
DEBUG_PORT.print(F(","));
DEBUG_PORT.print(fix.speed_kph(), 2) ;
DEBUG_PORT.print(F(","));
DEBUG_PORT.println(fix.heading(), 2) ;
#endif
//trkFile.sync();
trkFile.close();
}
else {
createFile = true;
}
}
}
}
if (sdIndicator && (millis() - sdIndicatorTime >= 150UL)) {
sdIndicator = false;
digitalWrite(SD_LED, HIGH);
}
if (!chkBattery) {
if (millis() - batteryTimeOld >= BATTERY_SAMPLE_TIME) {
chkBattery = true;
chkTimeOld = millis();
batteryTimeOld += BATTERY_SAMPLE_TIME;
check_battery();
}
} else {
if (millis() - chkTimeOld >= CHKBATT_INTERVAL) {
chkTimeOld += CHKBATT_INTERVAL;
check_battery();
}
}
if (lowBattery) {
if (millis() - blinkTimeOld >= BLINK_INTERVAL) {
pwrLedStatus = !pwrLedStatus;
digitalWrite(PWR_LED, pwrLedStatus ? HIGH : LOW);
blinkTimeOld += BLINK_INTERVAL;
}
}
#ifdef _BUTTON_
if ((displayMode == SHOWHOUR) || (displayMode == SHOWSECS)) {
if (millis() - lastColonBlinkTime >= 500UL) {
#ifdef _LUXOMETER_
display.brightness(get_brightness());
#endif
if (displayMode == SHOWHOUR) {
int16_t time = localTime.hours * 100 + localTime.minutes;
display.showClk(time, POINT_OFF);
}
else { // displayMode == SHOWSECS
display.noPoint();
display.showNum(localTime.seconds, true, 2);
}
lastColonBlinkTime += 500;
}
if (millis() - showtimeTime >= 10000UL) {
displayMode = SHOWSPEED;
display.clear();
}
}
#endif
validFix = false;
validTime = false;
validDate = false;
read_gps_fix();
// delay(1);
}
//------------------------------------------------------------------------------