From d9b777030890000ea6c2e070024f0a3d04e98c42 Mon Sep 17 00:00:00 2001 From: Lubos Petrovic Date: Mon, 7 Dec 2020 22:49:41 +0100 Subject: [PATCH] GPS support (hw uart) --- Board320_240.cpp | 103 +++++++++++++++++++++++++++++++++++++-------- Board320_240.h | 5 +++ BoardInterface.cpp | 16 +++++-- LiveData.cpp | 6 +++ LiveData.h | 13 +++++- RELEASENOTES.md | 2 + config.h | 1 + evDash.ino | 10 +---- menu.h | 7 +-- 9 files changed, 129 insertions(+), 34 deletions(-) diff --git a/Board320_240.cpp b/Board320_240.cpp index 3ae21d0..4112a89 100644 --- a/Board320_240.cpp +++ b/Board320_240.cpp @@ -7,6 +7,7 @@ #include //#include //#include +#include #include "config.h" #include "BoardInterface.h" #include "Board320_240.h" @@ -63,6 +64,12 @@ void Board320_240::afterSetup() { Serial.println("WiFi init completed...");*/ } + // Init GPS + if (liveData->settings.gpsHwSerialPort <= 2) { + gpsHwUart = new HardwareSerial(liveData->settings.gpsHwSerialPort); + gpsHwUart->begin(9600); + } + // SD card if (liveData->settings.sdcardEnabled == 1) { if (sdcardMount() && liveData->settings.sdcardAutstartLog == 1) { @@ -866,13 +873,13 @@ String Board320_240::menuItemCaption(int16_t menuItemId, String title) { /*case MENU_WIFI: suffix = "n/a"; switch (WiFi.status()) { -WL_CONNECTED: suffix = "CONNECTED"; break; -WL_NO_SHIELD: suffix = "NO_SHIELD"; break; -WL_IDLE_STATUS: suffix = "IDLE_STATUS"; break; -WL_SCAN_COMPLETED: suffix = "SCAN_COMPLETED"; break; -WL_CONNECT_FAILED: suffix = "CONNECT_FAILED"; break; -WL_CONNECTION_LOST: suffix = "CONNECTION_LOST"; break; -WL_DISCONNECTED: suffix = "DISCONNECTED"; break; + WL_CONNECTED: suffix = "CONNECTED"; break; + WL_NO_SHIELD: suffix = "NO_SHIELD"; break; + WL_IDLE_STATUS: suffix = "IDLE_STATUS"; break; + WL_SCAN_COMPLETED: suffix = "SCAN_COMPLETED"; break; + WL_CONNECT_FAILED: suffix = "CONNECT_FAILED"; break; + WL_CONNECTION_LOST: suffix = "CONNECTION_LOST"; break; + WL_DISCONNECTED: suffix = "DISCONNECTED"; break; } break;*/ case MENU_GPRS: sprintf(tmpStr1, "[%s] %s", (liveData->settings.gprsEnabled == 1) ? "on" : "off", liveData->settings.gprsApn); suffix = tmpStr1; break; @@ -883,6 +890,7 @@ WL_DISCONNECTED: suffix = "DISCONNECTED"; break; case MENU_PREDRAWN_GRAPHS: suffix = (liveData->settings.predrawnChargingGraphs == 1) ? "[on]" : "[off]"; break; case MENU_HEADLIGHTS_REMINDER: suffix = (liveData->settings.headlightsReminder == 1) ? "[on]" : "[off]"; break; case MENU_DEBUG_SCREEN: suffix = (liveData->settings.debugScreen == 1) ? "[on]" : "[off]"; break; + case MENU_GPS: sprintf(tmpStr1, "[HW UART=%d]", liveData->settings.gpsHwSerialPort); suffix = (liveData->settings.gpsHwSerialPort == 255) ? "[off]" : tmpStr1; break; // case MENU_SDCARD_ENABLED: sprintf(tmpStr1, "[%s]", (liveData->settings.sdcardEnabled == 1) ? "on" : "off"); suffix = tmpStr1; break; case MENU_SDCARD_AUTOSTARTLOG: sprintf(tmpStr1, "[%s]", (liveData->settings.sdcardEnabled == 0) ? "n/a" : (liveData->settings.sdcardAutstartLog == 1) ? "on" : "off"); suffix = tmpStr1; break; @@ -1029,6 +1037,7 @@ void Board320_240::menuItemClick() { // Pre-drawn charg.graphs off/on case MENU_PREDRAWN_GRAPHS: liveData->settings.predrawnChargingGraphs = (liveData->settings.predrawnChargingGraphs == 1) ? 0 : 1; showMenu(); return; break; case MENU_HEADLIGHTS_REMINDER: liveData->settings.headlightsReminder = (liveData->settings.headlightsReminder == 1) ? 0 : 1; showMenu(); return; break; + case MENU_GPS: liveData->settings.gpsHwSerialPort = (liveData->settings.gpsHwSerialPort == 2) ? 255 : liveData->settings.gpsHwSerialPort + 1; showMenu(); return; break; // Wifi menu case MENU_WIFI_ENABLED: liveData->settings.wifiEnabled = (liveData->settings.wifiEnabled == 1) ? 0 : 1; showMenu(); return; break; case MENU_WIFI_SSID: return; break; @@ -1169,12 +1178,12 @@ void Board320_240::redrawScreen() { spr.fillCircle(310, 10, 3, (liveData->params.sdcardInit == 1) ? (liveData->params.sdcardRecording) ? - (strlen(liveData->params.sdcardFilename) != 0) ? - TFT_GREEN /* assigned filename (opsec from bms or gsm/gps timestamp */: - TFT_BLUE /* recording started but waiting for data */ : - TFT_ORANGE /* sdcard init ready but recording not started*/ : - TFT_YELLOW /* failed to initialize sdcard */ - ); + (strlen(liveData->params.sdcardFilename) != 0) ? + TFT_GREEN /* assigned filename (opsec from bms or gsm/gps timestamp */ : + TFT_BLUE /* recording started but waiting for data */ : + TFT_ORANGE /* sdcard init ready but recording not started*/ : + TFT_YELLOW /* failed to initialize sdcard */ + ); } // BLE not connected @@ -1272,27 +1281,50 @@ void Board320_240::mainLoop() { hideMenu(); } + // GPS process + if (gpsHwUart != NULL) { + unsigned long start = millis(); + do { + while (gpsHwUart->available()) + gps.encode(gpsHwUart->read()); + } while (millis() - start < 20); + // + syncGPS(); + } + + // currentTime + struct tm now; + getLocalTime(&now, 0); + liveData->params.currentTime = mktime(&now); + // SD card recording if (liveData->params.sdcardInit && liveData->params.sdcardRecording && liveData->params.sdcardCanNotify) { + + //Serial.println(&now, "%y%m%d%H%M"); + // create filename if (liveData->params.operationTimeSec > 0 && strlen(liveData->params.sdcardFilename) == 0) { sprintf(liveData->params.sdcardFilename, "/%llu.json", uint64_t(liveData->params.operationTimeSec / 60)); - Serial.println("Log filename: "); + Serial.print("Log filename by opTimeSec: "); + Serial.println(liveData->params.sdcardFilename); + } + if (liveData->params.currTimeSyncWithGps && strlen(liveData->params.sdcardFilename) < 15) { + strftime(liveData->params.sdcardFilename, sizeof(liveData->params.sdcardFilename), "/%y%m%d%H%M.json", &now); + Serial.print("Log filename by GPS: "); Serial.println(liveData->params.sdcardFilename); } // append buffer, clear buffer & notify state if (strlen(liveData->params.sdcardFilename) != 0) { - liveData->params.sdcardCanNotify = false; File file = SD.open(liveData->params.sdcardFilename, FILE_APPEND); if (!file) { Serial.println("Failed to open file for appending"); File file = SD.open(liveData->params.sdcardFilename, FILE_WRITE); - } + } if (!file) { Serial.println("Failed to create file"); - } + } if (file) { Serial.println("Save buffer to SD card"); serializeParamsToJson(file); @@ -1301,6 +1333,10 @@ void Board320_240::mainLoop() { } } } + + // Shutdown when car is off + if (liveData->params.automaticShutdownTimer != 0 && liveData->params.currentTime - liveData->params.automaticShutdownTimer > 5) + shutdownDevice(); } /** @@ -1381,4 +1417,37 @@ void Board320_240::sdcardToggleRecording() { } } +/** + Sync gps data +*/ +void Board320_240::syncGPS() { + + if (gps.location.isValid()) { + liveData->params.gpsLat = gps.location.lat(); + liveData->params.gpsLon = gps.location.lng(); + if (gps.altitude.isValid()) + liveData->params.gpsAlt = gps.altitude.meters(); + } + if (gps.satellites.isValid()) { + liveData->params.gpsSat = gps.satellites.value(); + //Serial.print("GPS satellites: "); + //Serial.println(liveData->params.gpsSat); + } + if (!liveData->params.currTimeSyncWithGps && gps.date.isValid() && gps.time.isValid()) { + liveData->params.currTimeSyncWithGps = true; + + struct tm tm; + tm.tm_year = gps.date.year() - 1900; + tm.tm_mon = gps.date.month() - 1; + tm.tm_mday = gps.date.day(); + tm.tm_hour = gps.time.hour(); + tm.tm_min = gps.time.minute(); + tm.tm_sec = gps.time.second(); + time_t t = mktime(&tm); + printf("%02d%02d%02d%02d%02d%02d\n", gps.date.year() - 2000, gps.date.month() - 1, gps.date.day(), gps.time.hour(), gps.time.minute(), gps.time.second()); + struct timeval now = { .tv_sec = t }; + settimeofday(&now, NULL); + } +} + #endif // BOARD320_240_CPP diff --git a/Board320_240.h b/Board320_240.h index 1d6de3b..0dc4f27 100644 --- a/Board320_240.h +++ b/Board320_240.h @@ -14,6 +14,7 @@ // #include +#include #include "BoardInterface.h" class Board320_240 : public BoardInterface { @@ -23,6 +24,8 @@ class Board320_240 : public BoardInterface { TFT_eSPI tft = TFT_eSPI(); TFT_eSprite spr = TFT_eSprite(&tft); //SPIClass spiSD(HSPI); + HardwareSerial* gpsHwUart = NULL; + TinyGPSPlus gps; char tmpStr1[20]; char tmpStr2[20]; char tmpStr3[20]; @@ -46,6 +49,8 @@ class Board320_240 : public BoardInterface { // SD card bool sdcardMount() override; void sdcardToggleRecording() override; + // GPS + void syncGPS(); // Basic GUI void setBrightness(byte lcdBrightnessPerc) override; void displayMessage(const char* row1, const char* row2) override; diff --git a/BoardInterface.cpp b/BoardInterface.cpp index 0f47d00..54d9ded 100644 --- a/BoardInterface.cpp +++ b/BoardInterface.cpp @@ -87,7 +87,7 @@ void BoardInterface::loadSettings() { // Default settings liveData->settings.initFlag = 183; - liveData->settings.settingsVersion = 4; + liveData->settings.settingsVersion = 5; liveData->settings.carType = CAR_KIA_ENIRO_2020_64; tmpStr = "00:00:00:00:00:00"; // Pair via menu (middle button) tmpStr.toCharArray(liveData->settings.obdMacAddress, tmpStr.length() + 1); @@ -126,6 +126,7 @@ void BoardInterface::loadSettings() { tmpStr = "not_set"; tmpStr.toCharArray(liveData->settings.remoteApiKey, tmpStr.length() + 1); liveData->settings.headlightsReminder = 0; + liveData->settings.gpsHwSerialPort = 255; // off // Load settings and replace default values Serial.println("Reading settings from eeprom."); @@ -174,7 +175,11 @@ void BoardInterface::loadSettings() { tmpStr.toCharArray(liveData->tmpSettings.remoteApiUrl, tmpStr.length() + 1); tmpStr = "example"; tmpStr.toCharArray(liveData->tmpSettings.remoteApiKey, tmpStr.length() + 1); - liveData->settings.headlightsReminder = 0; + liveData->tmpSettings.headlightsReminder = 0; + } + if (liveData->tmpSettings.settingsVersion == 4) { + liveData->tmpSettings.settingsVersion = 5; + liveData->tmpSettings.gpsHwSerialPort = 255; // off } // Save upgraded structure @@ -231,7 +236,7 @@ void BoardInterface::customConsoleCommand(String cmd) { */ bool BoardInterface::serializeParamsToJson(File file, bool inclApiKey) { - StaticJsonDocument<1024> jsonData; + StaticJsonDocument<1500> jsonData; if (inclApiKey) jsonData["apiKey"] = liveData->settings.remoteApiKey; @@ -240,6 +245,11 @@ bool BoardInterface::serializeParamsToJson(File file, bool inclApiKey) { jsonData["batTotalKwh"] = liveData->params.batteryTotalAvailableKWh; jsonData["currTime"] = liveData->params.currentTime; jsonData["opTime"] = liveData->params.operationTimeSec; + + jsonData["gpsSat"] = liveData->params.gpsSat; + jsonData["lat"] = liveData->params.gpsLat; + jsonData["lon"] = liveData->params.gpsLon; + jsonData["alt"] = liveData->params.gpsAlt; jsonData["socPerc"] = liveData->params.socPerc; jsonData["sohPerc"] = liveData->params.sohPerc; diff --git a/LiveData.cpp b/LiveData.cpp index acd5401..45d41bd 100644 --- a/LiveData.cpp +++ b/LiveData.cpp @@ -20,6 +20,12 @@ void LiveData::initParams() { String tmpStr = ""; tmpStr.toCharArray(params.sdcardFilename, tmpStr.length() + 1); params.sdcardCanNotify = false; + // Gps + params.currTimeSyncWithGps = false; + params.gpsLat = -1; + params.gpsLon = -1; + params.gpsSat = 0; + params.gpsAlt = -1; // Car data params.ignitionOn = false; params.ignitionOnPrevious = false; diff --git a/LiveData.h b/LiveData.h index 04ce4c4..fc89913 100644 --- a/LiveData.h +++ b/LiveData.h @@ -42,6 +42,12 @@ typedef struct { // SIM time_t lastDataSent; bool sim800l_enabled; + // GPS + bool currTimeSyncWithGps; + float gpsLat; + float gpsLon; + byte gpsSat; // satellites count + int16_t gpsAlt; // SD card bool sdcardInit; bool sdcardRecording; @@ -136,7 +142,7 @@ typedef struct { // Setting stored to flash typedef struct { byte initFlag; // 183 value - byte settingsVersion; // current 4 + byte settingsVersion; // current 5 // === settings version 1 // ================================= uint16_t carType; // 0 - Kia eNiro 2020, 1 - Hyundai Kona 2020, 2 - Hyudai Ioniq 2018 @@ -176,7 +182,10 @@ typedef struct { char remoteApiUrl[64]; char remoteApiKey[32]; // - bool headlightsReminder; + byte headlightsReminder; + // === settings version 5 + // ================================= + byte gpsHwSerialPort; // 255-off, 0,1,2 - hw serial // } SETTINGS_STRUC; diff --git a/RELEASENOTES.md b/RELEASENOTES.md index d5f8aed..3935311 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -2,6 +2,8 @@ ### Next version - tech refactoring: `hexToDecFromResponse`, `decFromResponse` +- added support for GPS module on HW UART (user HWUART=2 for m5stack NEO-M8N) +- sd card logging - added gps sat/lat/lot/alt + SD filename + time is synchronized from GPS ### v2.1.0 2020-12-06 - m5stack mute speaker diff --git a/config.h b/config.h index 634b073..e3da701 100644 --- a/config.h +++ b/config.h @@ -81,6 +81,7 @@ typedef struct { #define MENU_REMOTE_UPLOAD 309 #define MENU_HEADLIGHTS_REMINDER 310 #define MENU_DEBUG_SCREEN 311 +#define MENU_GPS 312 // #define MENU_DISTANCE_UNIT 401 #define MENU_TEMPERATURE_UNIT 402 diff --git a/evDash.ino b/evDash.ino index 980c1c2..6ae1d38 100644 --- a/evDash.ino +++ b/evDash.ino @@ -2,7 +2,7 @@ Project renamed from eNiroDashboard to evDash !! working only with OBD BLE 4.0 adapters - !! Supported adapter is Vgate ICar Pro (must be BLE4.0 version) + !! Supported adapter is Vgate ICar Pro (must be BLE4.0 version) !! Not working with standard BLUETOOTH 3 adapters Serial console commands @@ -659,14 +659,6 @@ void loop() { #endif // SIM800L_ENABLED board->mainLoop(); - - // currentTime & 1ms delay - struct tm now; - getLocalTime(&now, 0); - liveData->params.currentTime = mktime(&now); - // Shutdown when car is off - if (liveData->params.automaticShutdownTimer != 0 && liveData->params.currentTime - liveData->params.automaticShutdownTimer > 5) - board->shutdownDevice(); if (board->scanDevices) { board->scanDevices = false; startBleScan(); diff --git a/menu.h b/menu.h index b80e593..4417893 100644 --- a/menu.h +++ b/menu.h @@ -25,10 +25,11 @@ MENU_ITEM menuItemsSource[100] = { {300, 3, 0, "<- parent menu"}, // {MENU_WIFI, 3, -1, "[dev] WiFi network"}, - {MENU_GPRS, 3, -1, "[dev] GSM/GPRS"}, - {MENU_REMOTE_UPLOAD, 3, -1, "[dev] Remote upload"}, - {MENU_NTP, 3, -1, "[dev] NTP"}, {MENU_SDCARD, 3, -1, "SD card"}, + {MENU_GPS, 3, -1, "GPS"}, + {MENU_GPRS, 3, -1, "[dev] GSM/GPRS"}, + //{MENU_REMOTE_UPLOAD, 3, -1, "[dev] Remote upload"}, + //{MENU_NTP, 3, -1, "[dev] NTP"}, {MENU_SCREEN_ROTATION, 3, -1, "Screen rotation"}, {MENU_DEFAULT_SCREEN, 3, -1, "Default screen"}, {MENU_SCREEN_BRIGHTNESS, 3, -1, "LCD brightness"},