diff --git a/GpsTracker/Config.cpp b/GpsTracker/Config.cpp index 02fc246..4948580 100644 --- a/GpsTracker/Config.cpp +++ b/GpsTracker/Config.cpp @@ -5,53 +5,74 @@ #define LOGGER_NAME "Config" namespace config { - Config value; + namespace main { - namespace details { + config_t value; - void read() { - VERBOSE("read"); - hardware::i2c::powerOn(); - hardware::i2c::eeprom.readBlock(CONFIG_ADDR, value); - if (!String(CONFIG_SEED).equals(value.seed)) reset(); - hardware::i2c::powerOff(); + namespace details { + void read() { + VERBOSE("read"); + + hardware::i2c::powerOn(); + hardware::i2c::eeprom.readBlock(CONFIG_ADDR, value); + if (CONFIG_SEED != value.seed) reset(); //todo : reset network if seed for network is not right + hardware::i2c::powerOff(); + + NOTICE_FORMAT("read", "%d, %s, %d, %d", value.seed, value.version, value.firstEntry, value.lastEntry); +#if BACKUP_ENABLE_NETWORK + NOTICE_FORMAT("read", "%d, %d, %s, %s", value.network.saveThreshold, value.network.lastSavedEntry, value.network.apn, value.network.url); + //networkConfig_t c = { + // POSITIONS_CONFIG_NET_DEFAULT_SAVE_THRESHOLD, + // 0xFFFF, + // POSITIONS_CONFIG_NET_DEFAULT_APN, + // POSITIONS_CONFIG_NET_DEFAULT_URL, + //}; + //value.network = c; +#endif + } + + void write() { + NOTICE_FORMAT("write", "%d, %s, %d, %d", value.seed, value.version, value.firstEntry, value.lastEntry); +#if BACKUP_ENABLE_NETWORK + NOTICE_FORMAT("write", "%d, %d, %s, %s", value.network.saveThreshold, value.network.lastSavedEntry, value.network.apn, value.network.url); +#endif + hardware::i2c::powerOn(); + int written = hardware::i2c::eeprom.writeBlock(CONFIG_ADDR, value); + hardware::i2c::powerOff(); + } } - void write() { - VERBOSE_FORMAT("write", "%s, %s, %s, %d, %d", value.seed, value.version, value.apn, value.firstEntry, value.lastEntry); + void setup() { + details::read(); + //details::write(); + } - hardware::i2c::powerOn(); - int written = hardware::i2c::eeprom.writeBlock(CONFIG_ADDR, value); - hardware::i2c::powerOff(); + void save() { + details::write(); } - } - Config get() { - if (value.seed[0] == '\0') details::read(); + void reset() { + VERBOSE("reset"); - VERBOSE_FORMAT("get", "%s, %s, %s, %d, %d", value.seed, value.version, value.apn, value.firstEntry, value.lastEntry); - return value; - } + config_t config = { + CONFIG_SEED, + VERSION, + 0xFFFF, + 0xFFFF, +#if BACKUP_ENABLE_NETWORK + { + POSITIONS_CONFIG_NET_DEFAULT_SAVE_THRESHOLD, + 0xFFFF, + POSITIONS_CONFIG_NET_DEFAULT_APN, + POSITIONS_CONFIG_NET_DEFAULT_URL, + }, +#endif + }; - void set(Config config) { - value = config; - details::write(); - } + value = config; + save(); + } - void reset() { - VERBOSE("reset"); - Config config = { - CONFIG_SEED, - VERSION, - "Vodafone", //TODO : read from SD - 0xFFFF, - 0xFFFF, - }; - - value = config; - details::write(); } - - } \ No newline at end of file diff --git a/GpsTracker/Config.h b/GpsTracker/Config.h index 5de2870..c3bc7b7 100644 --- a/GpsTracker/Config.h +++ b/GpsTracker/Config.h @@ -2,44 +2,85 @@ #include +#define BACKUP_ENABLE_SDCARD 0 +#define BACKUP_ENABLE_NETWORK 1 + +#if BACKUP_ENABLE_NETWORK +#include "NetworkPositionsConfig.h" +#endif + +#define SIM808_BAUDRATE 4800 + +#define CONFIG_ADDR 0 +#define CONFIG_RESERVED_SIZE 128 +#define CONFIG_SEED 13 +#define VERSION "1.00" + +#pragma region Default configuration values + +#define MENU_TIMEOUT 10000 +/** + \def SLEEP_DEFAULT_TIME_SECONDS + Hard coded value for default sleep time between position acquisitions. + Exprimed in seconds +*/ +#define SLEEP_DEFAULT_TIME_SECONDS 1800 +#define SLEEP_DEFAULT_INCREASE_THRESHOLD 3 + +#define GPS_DEFAULT_INTERMEDIATE_TIMEOUT_MS 10000 +#define GPS_DEFAULT_TOTAL_TIMEOUT_MS 180000 + +#define NETWORK_DEFAULT_INTERMEDIATE_TIMEOUT_MS 6000 +#define NETWORK_DEFAULT_TOTAL_TIMEOUT_MS 180000 +#define NETWORK_DEFAULT_NO_NETWORK_QUALIRY_THRESHOLD 8 +#define NETWORK_DEFAULT_NO_NETWORK_TRIES 5 + +#pragma endregion + +#define SLEEP_TIMING_TIME(hours, minutes) hours * 60 + minutes +#define SLEEP_TIMING_MIN SLEEP_TIMING_TIME(0, 0) +#define SLEEP_TIMING_MAX SLEEP_TIMING_TIME(23, 59) + struct sleepTimings_t { uint8_t speed; + uint16_t timeMin; + uint16_t timeMax; uint16_t seconds; }; -struct Config { - char seed[5]; +struct config_t { + uint8_t seed; char version[5]; - char apn[20]; uint16_t firstEntry; uint16_t lastEntry; +#if BACKUP_ENABLE_NETWORK + networkConfig_t network; +#endif }; -#define CONFIG_ADDR 0 -#define CONFIG_SEED "UIYA" -#define VERSION "1.00" - -#define SLEEP_DEFAULT_TIME_SECONDS 1800 - -#define GPS_DEFAULT_INTERMEDIATE_TIMEOUT_MS 10000 -#define GPS_DEFAULT_TOTAL_TIMEOUT_MS 180000 - namespace config { static const sleepTimings_t defaultSleepTimings[] PROGMEM = { - { 5, SLEEP_DEFAULT_TIME_SECONDS }, - { 10, 1200 }, - { 20, 600 }, - { 30, 540 }, - { 50, 480 }, - { 80, 240 }, - { 100, 210 }, - { 180, 180 }, + { 3, SLEEP_TIMING_TIME(16, 00), SLEEP_TIMING_TIME(19, 59), 3600 }, + { 3, SLEEP_TIMING_TIME(20, 00), SLEEP_TIMING_MAX, SLEEP_DEFAULT_TIME_SECONDS }, + { 3, SLEEP_TIMING_MIN, SLEEP_TIMING_TIME(8, 29), SLEEP_DEFAULT_TIME_SECONDS }, + { 3, SLEEP_TIMING_TIME(8, 30), SLEEP_TIMING_TIME(15, 59), 10800 }, + + { 5, SLEEP_TIMING_MIN, SLEEP_TIMING_MAX, 900 }, + { 20, SLEEP_TIMING_MIN, SLEEP_TIMING_MAX, 600 }, + { 30, SLEEP_TIMING_MIN, SLEEP_TIMING_MAX, 540 }, + { 50, SLEEP_TIMING_MIN, SLEEP_TIMING_MAX, 480 }, + { 80, SLEEP_TIMING_MIN, SLEEP_TIMING_MAX, 240 }, + { 100, SLEEP_TIMING_MIN, SLEEP_TIMING_MAX, 210 }, + { 180, SLEEP_TIMING_MIN, SLEEP_TIMING_MAX, 180 }, }; - Config get(); - void set(Config config); + namespace main { + extern config_t value; - void reset(); + void setup(); + void save(); + void reset(); + } } \ No newline at end of file diff --git a/GpsTracker/Core.cpp b/GpsTracker/Core.cpp index f44c85f..61c8670 100644 --- a/GpsTracker/Core.cpp +++ b/GpsTracker/Core.cpp @@ -8,38 +8,57 @@ using namespace utils; namespace core { uint16_t sleepTime = SLEEP_DEFAULT_TIME_SECONDS;; + uint8_t increaseInARow = 0; void main() { - VERBOSE("main"); PositionEntryMetadata metadata; if (positions::acquire(metadata)) { positions::appendLast(metadata); - setSleepTime(); + updateSleepTime(gps::getVelocity()); } - if (positions::needsToSend()) { - positions::send(); - } + positions::doBackup(); + mainunit::deepSleep(sleepTime); } - void setSleepTime() { - setSleepTime(gps::getVelocity()); + void updateSleepTime(uint8_t velocity) { + uint16_t result = computeSleepTime(velocity); + + if (result > sleepTime) { + increaseInARow++; + if (increaseInARow < SLEEP_DEFAULT_INCREASE_THRESHOLD) return; + + } + else increaseInARow = 0; + + sleepTime = result; + NOTICE_FORMAT("updateSleepTime", "%dkmh => %d seconds", velocity, sleepTime); } - void setSleepTime(uint8_t velocity) { - sleepTime = SLEEP_DEFAULT_TIME_SECONDS; + uint16_t computeSleepTime(uint8_t velocity) { + uint16_t result; + uint16_t currentTime = 0xFFFF; + + if (rtc::isAccurate()) { + tmElements_t time; + rtc::getTime(time); + currentTime = SLEEP_TIMING_TIME(time.Hour, time.Minute); + } + for (uint8_t i = 0; i < flash::getArraySize(config::defaultSleepTimings); i++) { sleepTimings_t timing; flash::read(&config::defaultSleepTimings[i], timing); if (velocity > timing.speed) continue; + if (currentTime != 0xFFFF && (currentTime < timing.timeMin || currentTime > timing.timeMax)) continue; - sleepTime = timing.seconds; + result = timing.seconds; break; } - VERBOSE_FORMAT("setSleepTime", "%d", sleepTime); + VERBOSE_FORMAT("computeSleepTime", "%d,%d", velocity, result); + return result; } } \ No newline at end of file diff --git a/GpsTracker/Core.h b/GpsTracker/Core.h index 78172ab..7539d51 100644 --- a/GpsTracker/Core.h +++ b/GpsTracker/Core.h @@ -14,6 +14,6 @@ namespace core { extern uint16_t sleepTime; void main(); - void setSleepTime(uint8_t velocity); - void setSleepTime(); + void updateSleepTime(uint8_t velocity); + uint16_t computeSleepTime(uint8_t velocity); } \ No newline at end of file diff --git a/GpsTracker/Debug.cpp b/GpsTracker/Debug.cpp index 15697cf..ef8a2d0 100644 --- a/GpsTracker/Debug.cpp +++ b/GpsTracker/Debug.cpp @@ -1,15 +1,16 @@ #include "Debug.h" #include "Flash.h" #include "Positions.h" +#include "Core.h" #define LOGGER_NAME "Debug" #define MENU_ENTRY(name, text) const char MENU_##name[] PROGMEM = text -const char FAKE_GPS_ENTRY[] PROGMEM = "1,1,20170924184842.000,49.454862,1.144537,71.900,67.99,172.6,1,,1.3,2.2,1.8,,11,7,,,37,,"; +const char FAKE_GPS_ENTRY[] PROGMEM = "1,1,20170924184842.000,49.454862,1.144537,71.900,2.70,172.6,1,,1.3,2.2,1.8,,11,7,,,37,,"; -MENU_ENTRY(HEADER, "-- Debug Menu --"); -MENU_ENTRY(SEPARATOR, "----"); +MENU_ENTRY(HEADER, "========================\n-- Menu --"); +MENU_ENTRY(SEPARATOR, "----"); MENU_ENTRY(RUN, "[R] Run"); MENU_ENTRY(RUN_ONCE, "[r] Run once"); @@ -21,13 +22,13 @@ MENU_ENTRY(GPS_GET, "[L] Get GPS position"); MENU_ENTRY(GPS_SET, "[l] Set last GPS position"); MENU_ENTRY(RTC_SET, "[T] Get RTC time"); MENU_ENTRY(RTC_GET, "[t] Set RTC time"); -MENU_ENTRY(SD_WRITE_TEST, "[W] Write to test file"); MENU_ENTRY(EEPROM_GET_CONFIG, "[C] Get EEPROM config"); MENU_ENTRY(EEPROM_RESET_CONFIG, "[c] Reset EEPROM config"); MENU_ENTRY(EEPROM_GET_CONTENT, "[E] Get EEPROM content"); MENU_ENTRY(EEPROM_GET_ENTRIES, "[P] Get EEPROM entries"); MENU_ENTRY(EEPROM_GET_LAST_ENTRY, "[p] Get EEPROM last entry"); MENU_ENTRY(EEPROM_ADD_ENTRY, "[a] Add last entry to EEPROM"); +MENU_ENTRY(EEPROM_BACKUP_ENTRIES, "[B] Backup EEPROM entries"); MENU_ENTRY(SLEEP, "[S] Sleep for 8s"); MENU_ENTRY(SLEEP_DEEP, "[s] Deep sleep for 10s"); MENU_ENTRY(QUESTION, "?"); @@ -43,13 +44,13 @@ const PROGMEM uint8_t commandIdMapping[] = { 'l', static_cast(debug::GPSTRACKER_DEBUG_COMMAND::GPS_SET), 'T', static_cast(debug::GPSTRACKER_DEBUG_COMMAND::RTC_GET), 't', static_cast(debug::GPSTRACKER_DEBUG_COMMAND::RTC_SET), - 'W', static_cast(debug::GPSTRACKER_DEBUG_COMMAND::SD_WRITE_TEST), 'C', static_cast(debug::GPSTRACKER_DEBUG_COMMAND::EEPROM_GET_CONFIG), 'c', static_cast(debug::GPSTRACKER_DEBUG_COMMAND::EEPROM_RESET_CONFIG), 'E', static_cast(debug::GPSTRACKER_DEBUG_COMMAND::EEPROM_GET_CONTENT), 'P', static_cast(debug::GPSTRACKER_DEBUG_COMMAND::EEPROM_GET_ENTRIES), 'p', static_cast(debug::GPSTRACKER_DEBUG_COMMAND::EEPROM_GET_LAST_ENTRY), 'a', static_cast(debug::GPSTRACKER_DEBUG_COMMAND::EEPROM_ADD_ENTRY), + 'B', static_cast(debug::GPSTRACKER_DEBUG_COMMAND::EEPROM_BACKUP_ENTRIES), 'S', static_cast(debug::GPSTRACKER_DEBUG_COMMAND::SLEEP), 's', static_cast(debug::GPSTRACKER_DEBUG_COMMAND::SLEEP_DEEP), }; @@ -78,17 +79,13 @@ const char * const MENU_ENTRIES[] PROGMEM = { MENU_SEPARATOR, - MENU_SD_WRITE_TEST, - - MENU_SEPARATOR, - MENU_EEPROM_GET_CONFIG, MENU_EEPROM_RESET_CONFIG, MENU_EEPROM_GET_CONTENT, MENU_EEPROM_GET_ENTRIES, MENU_EEPROM_GET_LAST_ENTRY, MENU_EEPROM_ADD_ENTRY, - + MENU_EEPROM_BACKUP_ENTRIES, MENU_SEPARATOR, MENU_SLEEP, @@ -109,20 +106,18 @@ namespace debug { namespace details { inline void displayPosition(PositionEntry entry) { - Log.notice(F("%d%%, %dmV, %f°C, %ds %d, %s\n"), entry.metadata.batteryLevel, entry.metadata.batteryVoltage, entry.metadata.temperature, entry.metadata.timeToFix, entry.metadata.status, entry.position); + Log.notice(F("%d%%, %dmV, %f°C, %ds, %d, %s\n"), entry.metadata.batteryLevel, entry.metadata.batteryVoltage, entry.metadata.temperature, entry.metadata.timeToFix, entry.metadata.status, entry.position); } } - void waitForSerial() { - while (!Serial); - Serial.begin(DEBUG_SERIAL_SPEED); - Serial.println("Starting !"); - } - int freeRam() { return freeRam2(); } + void displayFreeRam() { + Log.notice(F("RAM: %d\n"), freeRam()); + } + GPSTRACKER_DEBUG_COMMAND parseCommand(char id) { size_t mappingArraySize = flash::getArraySize(commandIdMapping); char commandId; @@ -135,20 +130,28 @@ namespace debug { return GPSTRACKER_DEBUG_COMMAND::NONE; } - GPSTRACKER_DEBUG_COMMAND menu() { - if (!Serial) return GPSTRACKER_DEBUG_COMMAND::RUN; - + GPSTRACKER_DEBUG_COMMAND menu(uint16_t timeout) { GPSTRACKER_DEBUG_COMMAND command; size_t menuSize = flash::getArraySize(MENU_ENTRIES); - + uint8_t intermediate_timeout = 50; + do { for (uint8_t i = 0; i < menuSize; i++) { Serial.println(reinterpret_cast(pgm_read_word_near(&MENU_ENTRIES[i]))); } - while (!Serial.available()); + while (!Serial.available()) { + if (timeout > 0) { + delay(intermediate_timeout); + timeout -= intermediate_timeout; + if (timeout <= 0) { + NOTICE_MSG("menu", "Timeout expired."); + return GPSTRACKER_DEBUG_COMMAND::RUN; + } + } + } command = parseCommand(Serial.read()); - while (Serial.available()) Serial.read(); + while (Serial.available()) Serial.read(); //flushing input } while (command == GPSTRACKER_DEBUG_COMMAND::NONE); return command; @@ -164,6 +167,8 @@ namespace debug { strlcpy_P(gps::lastPosition, FAKE_GPS_ENTRY, GPS_POSITION_SIZE); NOTICE_FORMAT("setFakeGpsPosition", "Last position set to : %s", gps::lastPosition); + NOTICE_FORMAT("setFakeGpsPosition", "Speed : %d", gps::getVelocity()); + NOTICE_FORMAT("setFakeGpsPosition", "Sleep time : %d", core::computeSleepTime(gps::getVelocity())); } void getAndDisplayBattery() { @@ -181,8 +186,26 @@ namespace debug { NOTICE_FORMAT("getAndDisplayRtcTime", "%d/%d/%d %d:%d:%d", tmYearToCalendar(time.Year), time.Month, time.Day, time.Hour, time.Minute, time.Second); } + void setRtcTime() { + tmElements_t time; + gps::getTime(time); + rtc::setTime(time); + } + + void getAndDisplaySleepTimes() { + size_t arraySize = flash::getArraySize(config::defaultSleepTimings); + sleepTimings_t maxSpeedTiming; + utils::flash::read(&config::defaultSleepTimings[arraySize - 1], maxSpeedTiming); + + for (int i = 0; i <= maxSpeedTiming.speed; i++) { + core::computeSleepTime(i); + } + + NOTICE_MSG("getAndDisplaySleepTimes", "Done"); + } + void getAndDisplayEepromConfig() { - config::get(); + config::main::setup(); //forcing read again } void getAndDisplayEepromContent() { @@ -202,7 +225,7 @@ namespace debug { } void getAndDisplayEepromPositions() { - uint16_t currentEntryIndex = config::get().firstEntry; + uint16_t currentEntryIndex = config::main::value.firstEntry; PositionEntry currentEntry; hardware::i2c::powerOn(); @@ -214,11 +237,11 @@ namespace debug { } void getAndDisplayEepromLastPosition() { - uint16_t lastEntryIndex = config::get().lastEntry; + uint16_t lastEntryIndex = config::main::value.lastEntry; PositionEntry lastEntry; - positions::get(lastEntryIndex, lastEntry); - details::displayPosition(lastEntry); + if(positions::get(lastEntryIndex, lastEntry)) details::displayPosition(lastEntry); + else Log.notice(F("No position recorded\n")); } void addLastPositionToEeprom() { @@ -234,14 +257,6 @@ namespace debug { SIM808_GPS_STATUS::OFF }; - positions::appendLast(metadata); - } - - void setRtcTime() { - tmElements_t time; - gps::getTime(time); - rtc::setTime(time); - - NOTICE_MSG("setRtcTime", "Done"); + for(int i = 0; i < 10; i++) positions::appendLast(metadata); } } \ No newline at end of file diff --git a/GpsTracker/Debug.h b/GpsTracker/Debug.h index 624d99f..ee3f48f 100644 --- a/GpsTracker/Debug.h +++ b/GpsTracker/Debug.h @@ -1,67 +1,44 @@ #pragma once #include -#include - #include "Config.h" +#include "Logging.h" + +#include "Core.h" + #include "Hardware.h" #include "Gps.h" #include "Rtc.h" -#define LOG(level, f) Log.level(F("[" LOGGER_NAME "::" f "]\n")) -#define LOG_MSG(level, f, msg) Log.level(F("[" LOGGER_NAME "::" f "] " msg "\n")) -#define LOG_FORMAT(level, f, msg, ...) Log.level(F("[" LOGGER_NAME "::" f "] " msg "\n"), __VA_ARGS__) - -#ifdef _DEBUG - -#define VERBOSE(f) LOG(verbose, f) -#define VERBOSE_MSG(f, msg) LOG_MSG(verbose, f, msg) -#define VERBOSE_FORMAT(f, msg, ...) LOG_FORMAT(verbose, f, msg, __VA_ARGS__) -#else - -#define DISABLE_LOGGING 1 //TODO : does nothing if not included before ArduinoLog.h - -#define VERBOSE(f) -#define VERBOSE_MSG(f, msg) -#define VERBOSE_FORMAT(f, msg, ...) - -#endif - -#define NOTICE(f) LOG(notice, f) -#define NOTICE_MSG(f, msg) LOG_MSG(notice, f, msg) -#define NOTICE_FORMAT(f, msg, ...) LOG_FORMAT(notice, f, msg, __VA_ARGS__) - -#define DEBUG_SERIAL_SPEED 115200 - namespace debug { enum class GPSTRACKER_DEBUG_COMMAND : uint8_t { - NONE = 0, - RUN = 1, - ONCE = 2, - RAM = 3, - BATTERY = 4, - GPS_ON = 5, - GPS_OFF = 6, - GPS_GET = 7, - GPS_SET = 8, - RTC_GET = 11, - RTC_SET = 12, - SD_WRITE_TEST = 13, - EEPROM_GET_CONFIG = 14, - EEPROM_RESET_CONFIG = 15, - EEPROM_GET_CONTENT = 16, - EEPROM_GET_LAST_ENTRY = 17, - EEPROM_GET_ENTRIES = 18, - EEPROM_ADD_ENTRY = 19, - SLEEP = 20, - SLEEP_DEEP = 21 + NONE, + RUN, + ONCE, + RAM, + BATTERY, + GPS_ON, + GPS_OFF, + GPS_GET, + GPS_SET, + RTC_GET, + RTC_SET, + EEPROM_GET_CONFIG, + EEPROM_RESET_CONFIG, + EEPROM_GET_CONTENT, + EEPROM_GET_LAST_ENTRY, + EEPROM_GET_ENTRIES, + EEPROM_ADD_ENTRY, + EEPROM_BACKUP_ENTRIES, + SLEEP, + SLEEP_DEEP }; - void waitForSerial(); int freeRam(); + void displayFreeRam(); - GPSTRACKER_DEBUG_COMMAND menu(); + GPSTRACKER_DEBUG_COMMAND menu(uint16_t timeout); void getAndDisplayBattery(); void getAndDisplayGpsPosition(); @@ -70,12 +47,13 @@ namespace debug { void getAndDisplayRtcTime(); void setRtcTime(); + void getAndDisplaySleepTimes(); + void getAndDisplayEepromConfig(); void getAndDisplayEepromContent(); void getAndDisplayEepromPositions(); void getAndDisplayEepromLastPosition(); void addLastPositionToEeprom(); - inline void displayFreeRam() { Serial.println(freeRam()); } } diff --git a/GpsTracker/Gps.cpp b/GpsTracker/Gps.cpp index cbe80c8..e0173ba 100644 --- a/GpsTracker/Gps.cpp +++ b/GpsTracker/Gps.cpp @@ -25,13 +25,14 @@ namespace gps { char lastPosition[GPS_POSITION_SIZE]; SIM808_GPS_STATUS lastStatus; - SIM808_GPS_STATUS acquireCurrentPosition(uint16_t timeout) { + SIM808_GPS_STATUS acquireCurrentPosition(uint32_t timeout) { SIM808_GPS_STATUS currentStatus = SIM808_GPS_STATUS::OFF; do { currentStatus = hardware::sim808::device.getGpsStatus(); - if (currentStatus > SIM808_GPS_STATUS::NO_FIX) break; + if (currentStatus > SIM808_GPS_STATUS::FIX) break; //if we have an accurate fix, break right now + NOTICE_FORMAT("acquireCurrentPosition", "%d", currentStatus); mainunit::deepSleep(GPS_DEFAULT_INTERMEDIATE_TIMEOUT_MS / 1000); timeout -= GPS_DEFAULT_INTERMEDIATE_TIMEOUT_MS; } while (timeout > 1); @@ -41,12 +42,13 @@ namespace gps { hardware::sim808::device.getGpsPosition(lastPosition); } + NOTICE_FORMAT("acquireCurrentPosition", "%d", currentStatus); return currentStatus; } uint8_t getVelocity() { uint8_t velocity; - hardware::sim808::device.getGpsField(lastPosition, SIM808_GPS_FIELD::SPEED, &velocity); + if (!hardware::sim808::device.getGpsField(lastPosition, SIM808_GPS_FIELD::SPEED, &velocity)) velocity = 0; VERBOSE_FORMAT("getVelocity", "%d", velocity); @@ -67,6 +69,6 @@ namespace gps { time.Minute = details::parseSubstring(buffer, timeStr + TIME_MINUTE_OFFSET, 2); time.Second = details::parseSubstring(buffer, timeStr + TIME_SECOND_OFFSET, 2); - VERBOSE_FORMAT("getTime", "%d/%d/%d %d:%d:%d", tmYearToCalendar(time.Year), time.Month, time.Day, time.Hour, time.Minute, time.Second); + NOTICE_FORMAT("getTime", "%d/%d/%d %d:%d:%d", tmYearToCalendar(time.Year), time.Month, time.Day, time.Hour, time.Minute, time.Second); } } \ No newline at end of file diff --git a/GpsTracker/Gps.h b/GpsTracker/Gps.h index f5be055..f698af8 100644 --- a/GpsTracker/Gps.h +++ b/GpsTracker/Gps.h @@ -15,7 +15,7 @@ namespace gps { inline void powerOn() { hardware::sim808::gpsPowerOn(); } inline void powerOff() { hardware::sim808::gpsPowerOff(); } - SIM808_GPS_STATUS acquireCurrentPosition(uint16_t timeout); + SIM808_GPS_STATUS acquireCurrentPosition(uint32_t timeout); uint8_t getVelocity(); void getTime(tmElements_t &time); diff --git a/GpsTracker/GpsTracker.h b/GpsTracker/GpsTracker.h index 8e90e49..6863ac7 100644 --- a/GpsTracker/GpsTracker.h +++ b/GpsTracker/GpsTracker.h @@ -1,7 +1,6 @@ #pragma once #include -#include #include #include "Debug.h" diff --git a/GpsTracker/GpsTracker.ino b/GpsTracker/GpsTracker.ino index 440ca89..9b44f41 100644 --- a/GpsTracker/GpsTracker.ino +++ b/GpsTracker/GpsTracker.ino @@ -1,24 +1,32 @@ #include "GpsTracker.h" #include "Positions.h" +#if _DEBUG +#define MENU_DEFAULT_TIMEOUT 0 +#else +#define MENU_DEFAULT_TIMEOUT 10000 + +#endif bool bypassMenu = false; +uint16_t menuTimeout = MENU_DEFAULT_TIMEOUT; void setup() { -#ifdef _DEBUG - debug::waitForSerial(); - Log.begin(LOG_LEVEL_VERBOSE, &Serial); -#else - if(Serial) Log.begin(LOG_LEVEL_NOTICE, &Serial); -#endif + logging::setup(); + config::main::setup(); rtc::setup(); hardware::sim808::setup(); + + positions::setup(); } void loop() { debug::GPSTRACKER_DEBUG_COMMAND command = debug::GPSTRACKER_DEBUG_COMMAND::RUN; - if(!bypassMenu) command = debug::menu(); + if (Serial && !bypassMenu) command = debug::menu(menuTimeout); + + if (command == debug::GPSTRACKER_DEBUG_COMMAND::RUN) bypassMenu = true; + else menuTimeout = 0; //disable timeout once a command has been entered bypassMenu = command == debug::GPSTRACKER_DEBUG_COMMAND::RUN; @@ -55,7 +63,7 @@ void loop() { debug::getAndDisplayEepromConfig(); break; case debug::GPSTRACKER_DEBUG_COMMAND::EEPROM_RESET_CONFIG: - config::reset(); + config::main::reset(); break; case debug::GPSTRACKER_DEBUG_COMMAND::EEPROM_GET_CONTENT: debug::getAndDisplayEepromContent(); @@ -69,14 +77,16 @@ void loop() { case debug::GPSTRACKER_DEBUG_COMMAND::EEPROM_ADD_ENTRY: debug::addLastPositionToEeprom(); break; + case debug::GPSTRACKER_DEBUG_COMMAND::EEPROM_BACKUP_ENTRIES: + positions::doBackup(); + break; case debug::GPSTRACKER_DEBUG_COMMAND::SLEEP: mainunit::sleep(period_t::SLEEP_8S); break; case debug::GPSTRACKER_DEBUG_COMMAND::SLEEP_DEEP: mainunit::deepSleep(10); break; - case debug::GPSTRACKER_DEBUG_COMMAND::SD_WRITE_TEST: default: - Serial.println(F("Unsupported command !")); + NOTICE_MSG("loop", "Unsupported"); } } diff --git a/GpsTracker/Hardware.cpp b/GpsTracker/Hardware.cpp index 378ea82..a5fb4a9 100644 --- a/GpsTracker/Hardware.cpp +++ b/GpsTracker/Hardware.cpp @@ -1,3 +1,4 @@ +#include "Config.h" #include "Hardware.h" #include "Pins.h" #include "Debug.h" @@ -41,12 +42,10 @@ namespace hardware { } void setup() { - VERBOSE("setup"); - device.powerOnOff(true); - simSerial.begin(4800); - + NOTICE("setup"); + simSerial.begin(SIM808_BAUDRATE); device.begin(simSerial); - device.powerOnOff(false); + powerOff(); //ensure powerOff on start } void gpsPowerOn() { @@ -56,7 +55,7 @@ namespace hardware { } void gpsPowerOff() { - VERBOSE("gpsPowerOff"); + NOTICE("gpsPowerOff"); device.disableGps(); powerOffIfUnused(); } @@ -65,13 +64,12 @@ namespace hardware { VERBOSE("networkPowerOn"); powerOn(); device.setPhoneFunctionality(SIM808_PHONE_FUNCTIONALITY::FULL); - device.enableGprs(config::get().apn); } void networkPowerOff() { VERBOSE("networkPowerOff"); - device.setPhoneFunctionality(SIM808_PHONE_FUNCTIONALITY::MINIMUM); device.disableGprs(); + device.setPhoneFunctionality(SIM808_PHONE_FUNCTIONALITY::MINIMUM); powerOffIfUnused(); } @@ -82,11 +80,8 @@ namespace hardware { namespace i2c { E24 eeprom = E24(E24Size_t::E24_512K); - uint8_t poweredCount = 0; - //inline void powered() { digitalRead(I2C_PWR) == HIGH; } //TODO = replace enum with just reading the output pin ? - void powerOn() { if (!poweredCount) { VERBOSE("powerOn"); diff --git a/GpsTracker/Hardware.h b/GpsTracker/Hardware.h index df67034..4e1353a 100644 --- a/GpsTracker/Hardware.h +++ b/GpsTracker/Hardware.h @@ -1,11 +1,13 @@ #pragma once +#include #include #include namespace hardware { namespace sim808 { + extern SoftwareSerial simSerial; extern SIM808 device; void powerOn(); diff --git a/GpsTracker/Logging.cpp b/GpsTracker/Logging.cpp new file mode 100644 index 0000000..7c75bbf --- /dev/null +++ b/GpsTracker/Logging.cpp @@ -0,0 +1,18 @@ +#include "Logging.h" + +namespace logging { + + void setup() { +#if _DEBUG + while (!Serial); +#endif + + if (Serial) { + Serial.begin(LOG_SERIAL_SPEED); + Log.begin(LOG_LEVEL, &Serial); + + Log.notice(F("Starting...\n")); + } + } + +} \ No newline at end of file diff --git a/GpsTracker/Logging.h b/GpsTracker/Logging.h new file mode 100644 index 0000000..c8460a3 --- /dev/null +++ b/GpsTracker/Logging.h @@ -0,0 +1,35 @@ +#pragma once +#include "Config.h" + +//#define DISABLE_LOGGING 1 +#include + +#define LOG_SERIAL_SPEED 115200 +#if _DEBUG +#define LOG_LEVEL LOG_LEVEL_VERBOSE +#else +#define LOG_LEVEL LOG_LEVEL_NOTICE +#endif + +#define LOG(level, f) Log.level(F("[" LOGGER_NAME "::" f "]\n")) +#define LOG_MSG(level, f, msg) Log.level(F("[" LOGGER_NAME "::" f "] " msg "\n")) +#define LOG_FORMAT(level, f, msg, ...) Log.level(F("[" LOGGER_NAME "::" f "] " msg "\n"), __VA_ARGS__) + +#if _DEBUG +#define VERBOSE(f) LOG(verbose, f) +#define VERBOSE_MSG(f, msg) LOG_MSG(verbose, f, msg) +#define VERBOSE_FORMAT(f, msg, ...) LOG_FORMAT(verbose, f, msg, __VA_ARGS__) +#else +#define VERBOSE(f) +#define VERBOSE_MSG(f, msg) +#define VERBOSE_FORMAT(f, msg, ...) +#endif + +#define NOTICE(f) LOG(notice, f) +#define NOTICE_MSG(f, msg) LOG_MSG(notice, f, msg) +#define NOTICE_FORMAT(f, msg, ...) LOG_FORMAT(notice, f, msg, __VA_ARGS__) + + +namespace logging { + void setup(); +} \ No newline at end of file diff --git a/GpsTracker/MainUnit.cpp b/GpsTracker/MainUnit.cpp index eebcc74..e72e2aa 100644 --- a/GpsTracker/MainUnit.cpp +++ b/GpsTracker/MainUnit.cpp @@ -7,6 +7,23 @@ namespace mainunit { + namespace details { + + void prepareSleep() { + hardware::sim808::simSerial.end(); //avoid woke up by SoftwareSerial interrupt + delay(5); //ensure message have been printed out + } + + void wokeUp() { + tmElements_t wokeUpTime; + rtc::getTime(wokeUpTime); + VERBOSE_FORMAT("wokeUp", "%d:%d:%d", wokeUpTime.Hour, wokeUpTime.Minute, wokeUpTime.Second); + + hardware::sim808::simSerial.listen(); + } + + } + void interrupt() { detachInterrupt(digitalPinToInterrupt(RTC_WAKE)); } @@ -19,18 +36,18 @@ namespace mainunit { } void sleep(period_t period) { - NOTICE_FORMAT("sleep", "Sleeping for period : %d", period); - delay(5); + NOTICE_FORMAT("sleep", "Period : %d", period); + details::prepareSleep(); LowPower.powerDown(period, ADC_OFF, BOD_OFF); - NOTICE_MSG("sleep", "Woke up"); + details::wokeUp(); } void deepSleep(uint16_t seconds) { - NOTICE_FORMAT("deepSleep", "Deep sleeping for %d seconds", seconds); + NOTICE_FORMAT("deepSleep", "%d seconds", seconds); interruptIn(seconds); - delay(5); + details::prepareSleep(); LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF); - NOTICE_MSG("deepSleep", "Woke up"); + details::wokeUp(); } } \ No newline at end of file diff --git a/GpsTracker/Network.cpp b/GpsTracker/Network.cpp index 0372b00..878c470 100644 --- a/GpsTracker/Network.cpp +++ b/GpsTracker/Network.cpp @@ -1,8 +1,52 @@ +#include "Config.h" + +#if BACKUP_ENABLE_NETWORK +#include "Debug.h" #include "Network.h" #include "Hardware.h" +#include "MainUnit.h" + #define LOGGER_NAME "Network" namespace network { + SIM808RegistrationStatus waitForRegistered(uint32_t timeout) { + VERBOSE("waitForRegistered"); + + SIM808RegistrationStatus currentStatus; + uint8_t noReliableNetwork = 0; + + do { + currentStatus = hardware::sim808::device.getNetworkRegistrationStatus(); + if (isAvailable(currentStatus.stat)) break; + + SIM808SignalQualityReport report = hardware::sim808::device.getSignalQuality(); + NOTICE_FORMAT("waitForRegistered", "%d, [%d %ddBm]", currentStatus.stat, report.ssri, report.attenuation); + + if (report.ssri < NETWORK_DEFAULT_NO_NETWORK_QUALIRY_THRESHOLD) noReliableNetwork++; + else noReliableNetwork = 0; + if (noReliableNetwork > NETWORK_DEFAULT_NO_NETWORK_TRIES) { + NOTICE_MSG("waitForRegistered", "No reliable signal"); + break; //after a while, not network really means no network. Bailing out + } + + mainunit::deepSleep(NETWORK_DEFAULT_INTERMEDIATE_TIMEOUT_MS / 1000); + timeout -= NETWORK_DEFAULT_INTERMEDIATE_TIMEOUT_MS; + } while (timeout > 1); + + NOTICE_FORMAT("waitForRegistered", "%d, [%d %ddBm]", currentStatus.stat, report.ssri, report.attenuation); + return currentStatus; + } + + bool isAvailable(SIM808_NETWORK_REGISTRATION_STATE state) { + return state == SIM808_NETWORK_REGISTRATION_STATE::REGISTERED || + state == SIM808_NETWORK_REGISTRATION_STATE::ROAMING; + } + + bool enableGprs() { + return hardware::sim808::device.enableGprs(config::main::value.network.apn); + } + -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/GpsTracker/Network.h b/GpsTracker/Network.h index df89526..33ee41e 100644 --- a/GpsTracker/Network.h +++ b/GpsTracker/Network.h @@ -6,4 +6,8 @@ namespace network { inline void powerOn() { hardware::sim808::networkPowerOn(); } inline void powerOff() { hardware::sim808::networkPowerOff(); } + + SIM808RegistrationStatus waitForRegistered(uint32_t timeout); + bool isAvailable(SIM808_NETWORK_REGISTRATION_STATE state); + bool enableGprs(); } \ No newline at end of file diff --git a/GpsTracker/NetworkPositionsBackup.cpp b/GpsTracker/NetworkPositionsBackup.cpp new file mode 100644 index 0000000..215cd76 --- /dev/null +++ b/GpsTracker/NetworkPositionsBackup.cpp @@ -0,0 +1,93 @@ +#pragma once + +#include "Config.h" + +#if BACKUP_ENABLE_NETWORK +#include "NetworkPositionsBackup.h" +#include "Debug.h" +#include "Positions.h" +#include "Hardware.h" +#include "Network.h" + +#define LOGGER_NAME "Positions::backup::network" +#define BUFFER_SIZE 170 + +namespace positions { + namespace backup { + namespace net { + + namespace details { + + bool isBackupNeeded() { + config_t *config = &config::main::value; + return (config->network.lastSavedEntry == 0xFFFF && config ->lastEntry != 0xFFFF) || + positions::count(config->network.lastSavedEntry) > config->network.saveThreshold; + } + + bool appendPosition(PositionEntry &entry) { + char buffer[BUFFER_SIZE]; + snprintf(buffer, BUFFER_SIZE, "%d,%d,%d,%d,%d,%d,", + debug::freeRam(), + hardware::sim808::device.getSignalQuality().attenuation, + entry.metadata.batteryLevel, + entry.metadata.batteryVoltage, + static_cast(entry.metadata.temperature * 100), + static_cast(entry.metadata.status)); + + strcat(buffer, entry.position); + + NOTICE_FORMAT("appendPosition", "Sending : %s", buffer); + uint16_t responseCode = hardware::sim808::device.httpPost( + config::main::value.network.url, + F("text/csv"), + buffer, + buffer, + BUFFER_SIZE + ) == POSITIONS_CONFIG_NET_DEFAULT_EXPECTED_RESPONSE; + + NOTICE_FORMAT("appendPosition", "Response : %d", responseCode); + return responseCode; + } + + void appendPositions() { + uint16_t currentEntryIndex = config::main::value.network.lastSavedEntry + 1; + PositionEntry currentEntry; + SIM808RegistrationStatus networkStatus; + + network::powerOn(); + networkStatus = network::waitForRegistered(NETWORK_DEFAULT_TOTAL_TIMEOUT_MS); + + if (!network::isAvailable(networkStatus.stat)) NOTICE_MSG("appendPositions", "network unavailable"); + else if (!network::enableGprs()) NOTICE_MSG("appendPositions", "gprs unavailable"); + else { + hardware::i2c::powerOn(); + do { + if (!positions::get(currentEntryIndex, currentEntry)) break; + if (!appendPosition(currentEntry)) break; + + config::main::value.network.lastSavedEntry = currentEntryIndex; + config::main::save(); + + } while (positions::moveNext(currentEntryIndex)); + hardware::i2c::powerOff(); + } + + network::powerOff(); + } + + } + + void NetworkPositionsBackup::setup() { + NOTICE("setup"); + } + + void NetworkPositionsBackup::backup() { + NOTICE("backup"); + + if (!details::isBackupNeeded()) return; + details::appendPositions(); + } + } + } +} +#endif \ No newline at end of file diff --git a/GpsTracker/NetworkPositionsBackup.h b/GpsTracker/NetworkPositionsBackup.h new file mode 100644 index 0000000..da79400 --- /dev/null +++ b/GpsTracker/NetworkPositionsBackup.h @@ -0,0 +1,18 @@ +#pragma once + +#include "PositionsBackup.h" + +namespace positions { + namespace backup { + namespace net { + + class NetworkPositionsBackup : public PositionsBackup { + private: + public: + void setup(); + void backup(); + }; + + } + } +} \ No newline at end of file diff --git a/GpsTracker/NetworkPositionsConfig.h b/GpsTracker/NetworkPositionsConfig.h new file mode 100644 index 0000000..7e5f40d --- /dev/null +++ b/GpsTracker/NetworkPositionsConfig.h @@ -0,0 +1,15 @@ +#pragma once + + +#define POSITIONS_CONFIG_NET_DEFAULT_SAVE_THRESHOLD 10 +#define POSITIONS_CONFIG_NET_DEFAULT_APN "Vodafone" +#define POSITIONS_CONFIG_NET_DEFAULT_URL "http://yourserver.com/endpoint" +#define POSITIONS_CONFIG_NET_DEFAULT_EXPECTED_RESPONSE 201 + + +struct networkConfig_t { + uint8_t saveThreshold; + uint16_t lastSavedEntry; + char apn[20]; + char url[50]; +}; \ No newline at end of file diff --git a/GpsTracker/Positions.cpp b/GpsTracker/Positions.cpp index 38f6121..707cc9a 100644 --- a/GpsTracker/Positions.cpp +++ b/GpsTracker/Positions.cpp @@ -1,26 +1,60 @@ -#include "Positions.h" - -#include "Debug.h" #include "Config.h" +#include "Debug.h" +#include "Positions.h" #include "Gps.h" -#include "Network.h" + +#if BACKUP_ENABLE_SDCARD || BACKUP_ENABLE_NETWORK +#define BACKUPS_ENABLED BACKUP_ENABLE_SDCARD + BACKUP_ENABLE_NETWORK +#endif + +#if BACKUP_ENABLE_SDCARD +#include "SdPositionsBackup.h" +#endif + +#if BACKUP_ENABLE_NETWORK +#include "NetworkPositionsBackup.h" +#endif #define LOGGER_NAME "Positions" #define ENTRY_RESERVED_SIZE 128 -#define ENTRIES_ADDR ENTRY_RESERVED_SIZE +#define ENTRIES_ADDR CONFIG_RESERVED_SIZE namespace positions { +#ifdef BACKUPS_ENABLED + backup::PositionsBackup **_backups; +#endif - uint16_t _maxEntryIndex = (E24_MAX_ADDRESS(hardware::i2c::eeprom.getSize()) - ENTRIES_ADDR) / ENTRY_RESERVED_SIZE; + namespace details { + uint16_t maxEntryIndex = 0; - uint16_t getEntryAddress(uint16_t index) { - if (index > _maxEntryIndex) return -1; - return ENTRIES_ADDR + (ENTRY_RESERVED_SIZE * index); + uint16_t getEntryAddress(uint16_t index) { + if (index > maxEntryIndex) return -1; + return ENTRIES_ADDR + (ENTRY_RESERVED_SIZE * index); + } + } + + void setup() { + details::maxEntryIndex = (E24_MAX_ADDRESS(hardware::i2c::eeprom.getSize()) - ENTRIES_ADDR) / ENTRY_RESERVED_SIZE; +#ifdef BACKUPS_ENABLED + uint8_t backupIdx = 0; + _backups = new backup::PositionsBackup*[BACKUPS_ENABLED]; + +#if BACKUP_ENABLE_SDCARD + _backups[backupIdx] = new backup::sd::SdPositionsBackup(); + _backups[backupIdx]->setup(); + backupIdx++; +#endif +#if BACKUP_ENABLE_NETWORK + _backups[backupIdx] = new backup::net::NetworkPositionsBackup(); + _backups[backupIdx]->setup(); + backupIdx++; +#endif +#endif } bool acquire(PositionEntryMetadata &metadata) { - VERBOSE("acquire"); + NOTICE("acquire"); timestamp_t before; @@ -30,6 +64,8 @@ namespace positions { SIM808ChargingStatus battery = hardware::sim808::device.getChargingState(); gps::powerOff(); + NOTICE_FORMAT("acquire", "Status : %d", gpsStatus); + if (gpsStatus < SIM808_GPS_STATUS::FIX) return false; uint16_t timeToFix = rtc::getTime() - before; @@ -52,29 +88,34 @@ namespace positions { void appendLast(const PositionEntryMetadata &metadata) { VERBOSE("appendLast"); + uint16_t entryIndex; uint16_t entryAddress; PositionEntry entry = { metadata }; strlcpy(entry.position, gps::lastPosition, POSITION_SIZE); - hardware::i2c::powerOn(); - Config config = config::get(); + config_t* config = &config::main::value; + entryIndex = config->lastEntry + 1; - config.lastEntry++; - if (config.lastEntry > _maxEntryIndex) config.lastEntry = 0; - if (config.lastEntry == config.firstEntry) config.firstEntry++; - if (config.firstEntry > _maxEntryIndex) config.firstEntry = 0; + entryAddress = details::getEntryAddress(entryIndex); - entryAddress = getEntryAddress(config.lastEntry); + hardware::i2c::powerOn(); hardware::i2c::eeprom.writeBlock(entryAddress, entry); - VERBOSE_FORMAT("appendLast", "Written to EEPROM @ %X : [%d%% @ %dmV] [%f°C] [TTF : %d, Status : %d, Position : %s]", entryAddress, entry.metadata.batteryLevel, entry.metadata.batteryVoltage, entry.metadata.temperature, entry.metadata.timeToFix, entry.metadata.status, entry.position); + NOTICE_FORMAT("appendLast", "Saved @ %X : [%d%% @ %dmV] [%f°C] [TTF : %d, Status : %d, Position : %s]", entryAddress, entry.metadata.batteryLevel, entry.metadata.batteryVoltage, entry.metadata.temperature, entry.metadata.timeToFix, entry.metadata.status, entry.position); - config::set(config); + config->lastEntry++; + if (config->lastEntry > details::maxEntryIndex) config->lastEntry = 0; + if (config->lastEntry == config->firstEntry) config->firstEntry++; + if (config->firstEntry > details::maxEntryIndex) config->firstEntry = 0; + + config::main::save(); hardware::i2c::powerOff(); } bool get(uint16_t index, PositionEntry &entry) { - uint16_t entryAddress = getEntryAddress(index); + VERBOSE("get"); + + uint16_t entryAddress = details::getEntryAddress(index); if (entryAddress == -1) return false; VERBOSE_FORMAT("get", "Reading entry n°%d @ %X", index, entryAddress); @@ -83,24 +124,31 @@ namespace positions { hardware::i2c::eeprom.readBlock(entryAddress, entry); hardware::i2c::powerOff(); - VERBOSE_FORMAT("get", "Read from EEPROM @ %X : [%d%% @ %dmV] [%f°C] [%d, %s]", entryAddress, entry.metadata.batteryLevel, entry.metadata.batteryVoltage, entry.metadata.temperature, entry.metadata.timeToFix, entry.metadata.status, entry.position); + NOTICE_FORMAT("get", "Read from EEPROM @ %X : [%d%% @ %dmV] [%f°C] [TTF : %d, Status : %d, Position : %s]", entryAddress, entry.metadata.batteryLevel, entry.metadata.batteryVoltage, entry.metadata.temperature, entry.metadata.timeToFix, entry.metadata.status, entry.position); return true; } bool moveNext(uint16_t &index) { - if (index == config::get().lastEntry) return false; + if (index == config::main::value.lastEntry) return false; - if (index == _maxEntryIndex) index = 0; //could use a modulo but easier to understand that way + if (index == details::maxEntryIndex) index = 0; //could use a modulo but easier to understand that way else index++; return true; } - bool needsToSend() { - return false; - } + uint16_t count(uint16_t fromIndex) { + config_t *config = &config::main::value; + if (config->lastEntry < config->firstEntry) { config->lastEntry += details::maxEntryIndex; } - void send() { + return config->lastEntry - fromIndex; + } + void doBackup() { +#ifdef BACKUPS_ENABLED + for (int i = 0; i < BACKUPS_ENABLED; i++) { + _backups[i]->backup(); + } +#endif } } \ No newline at end of file diff --git a/GpsTracker/Positions.h b/GpsTracker/Positions.h index a2c914f..df9b29d 100644 --- a/GpsTracker/Positions.h +++ b/GpsTracker/Positions.h @@ -18,12 +18,14 @@ struct PositionEntry { }; //sizeof = 125 namespace positions { + + void setup(); bool acquire(PositionEntryMetadata &metadata); void appendLast(const PositionEntryMetadata &metadata); bool get(uint16_t index, PositionEntry &entry); bool moveNext(uint16_t &index); + uint16_t count(uint16_t fromIndex); - bool needsToSend(); - void send(); + void doBackup(); } \ No newline at end of file diff --git a/GpsTracker/PositionsBackup.cpp b/GpsTracker/PositionsBackup.cpp new file mode 100644 index 0000000..ea17229 --- /dev/null +++ b/GpsTracker/PositionsBackup.cpp @@ -0,0 +1,9 @@ +#include "PositionsBackup.h" + +namespace positions { + namespace backup { + + PositionsBackup::~PositionsBackup() {} + + } +} \ No newline at end of file diff --git a/GpsTracker/PositionsBackup.h b/GpsTracker/PositionsBackup.h new file mode 100644 index 0000000..0cd6f77 --- /dev/null +++ b/GpsTracker/PositionsBackup.h @@ -0,0 +1,14 @@ +#pragma once + +namespace positions { + namespace backup { + + class PositionsBackup { + public: + ~PositionsBackup(); + virtual void setup()=0; + virtual void backup()=0; + }; + + } +} \ No newline at end of file diff --git a/GpsTracker/Rtc.cpp b/GpsTracker/Rtc.cpp index 308ff4c..26d8b45 100644 --- a/GpsTracker/Rtc.cpp +++ b/GpsTracker/Rtc.cpp @@ -17,7 +17,7 @@ namespace rtc { hardware::i2c::powerOn(); RTC.control(DS3231_12H, DS3231_OFF); //24 hours clock RTC.control(DS3231_A1_INT_ENABLE, DS3231_OFF); //Alarm 1 OFF - RTC.control(DS3231_INT_ENABLE, DS3231_ON); //INTCN OFF + RTC.control(DS3231_INT_ENABLE, DS3231_ON); //INTCN ON hardware::i2c::powerOff(); //TODO : check wether the osc has been halted (meaning the battery could be dead) @@ -31,6 +31,14 @@ namespace rtc { return temperature; } + bool isAccurate() { + hardware::i2c::powerOn(); + bool accurate = RTC.status(DS3231_HALTED_FLAG) == DS3231_OFF; + hardware::i2c::powerOff(); + + return accurate; + } + timestamp_t getTime() { tmElements_t time; getTime(time); @@ -50,6 +58,7 @@ namespace rtc { hardware::i2c::powerOn(); RTC.writeTime(time); + RTC.control(DS3231_HALTED_FLAG, DS3231_OFF); hardware::i2c::powerOff(); } diff --git a/GpsTracker/Rtc.h b/GpsTracker/Rtc.h index 1236f53..e752893 100644 --- a/GpsTracker/Rtc.h +++ b/GpsTracker/Rtc.h @@ -7,6 +7,7 @@ namespace rtc { float getTemperature(); + bool isAccurate(); timestamp_t getTime(); void getTime(tmElements_t &time); void setTime(const tmElements_t &time); diff --git a/GpsTracker/SdCard.cpp b/GpsTracker/SdCard.cpp new file mode 100644 index 0000000..9a37dd3 --- /dev/null +++ b/GpsTracker/SdCard.cpp @@ -0,0 +1,18 @@ +#include "Config.h" + +#if BACKUP_ENABLE_SDCARD +#include "SdCard.h" + +namespace hardware { + namespace sdcard { + + SdFat filesystem; + bool available = false; + + void setup() { + available = filesystem.begin(SD_SS); + } + + } +} +#endif \ No newline at end of file diff --git a/GpsTracker/SdCard.h b/GpsTracker/SdCard.h new file mode 100644 index 0000000..fde853a --- /dev/null +++ b/GpsTracker/SdCard.h @@ -0,0 +1,14 @@ +#pragma once + +#include +#include "Pins.h" + +namespace hardware { + namespace sdcard { + + extern SdFat filesystem; + extern bool available; + + void setup(); + } +} \ No newline at end of file diff --git a/GpsTracker/SdPositionsBackup.cpp b/GpsTracker/SdPositionsBackup.cpp new file mode 100644 index 0000000..4bfcb50 --- /dev/null +++ b/GpsTracker/SdPositionsBackup.cpp @@ -0,0 +1,119 @@ +#include "SdPositionsBackup.h" +#include "SdPositionsConfig.h" +#include "SdCard.h" +#include "Hardware.h" +#include "Config.h" +#include "Positions.h" +#include "Debug.h" + +#define LOGGER_NAME "Positions::backup::sd" + +#if BACKUP_ENABLE_SDCARD +namespace positions { + namespace backup { + namespace sd { + + namespace details { + + bool isBackupNeeded(SdPositionConfig_t &sdConfig) { + Config_t referenceConfig = config::main::value; + sdConfig = config::backup::sd::get(); + + return sdConfig.lastSavedEntry == 0xFFFF || + positions::count(sdConfig.lastSavedEntry) > sdConfig.saveThreshold; + } + + void getPositionsFileName(uint16_t fileIndex, char *buffer) { + sprintf(buffer, POSITIONS_FILENAME, fileIndex); + } + + void ensurePositionsFolder() { + char positionsFolder[] = POSITIONS_FOLDER; + + hardware::sdcard::filesystem.chdir(); + if (!hardware::sdcard::filesystem.exists(positionsFolder)) { + hardware::sdcard::filesystem.mkdir(positionsFolder, true); + } + + hardware::sdcard::filesystem.chdir(positionsFolder); + } + + void selectFile(SdPositionConfig_t &sdConfig, File &file) { + char positionFileName[POSITIONS_FILENAME_LENGTH]; + + if (sdConfig.fileRecords >= sdConfig.maxRecordsPerFile) { + if (file.isOpen()) { + file.close(); + config::backup::sd::set(sdConfig); + } + + sdConfig.fileIndex++; + sdConfig.filePosition = 0; + sdConfig.fileRecords = 0; + } + + if (!file.isOpen()) { + ensurePositionsFolder(); + getPositionsFileName(sdConfig.fileIndex, positionFileName); + file.open(positionFileName, O_RDWR | O_CREAT); + } + } + + void appendPosition(File &file, SdPositionConfig_t &sdConfig, PositionEntry &entry) { + VERBOSE("appendPosition"); + + const char fieldTerminator = ','; + + file.printField(entry.metadata.batteryLevel, fieldTerminator); + file.printField(entry.metadata.batteryVoltage, fieldTerminator); + file.printField(entry.metadata.temperature, fieldTerminator); + file.printField(entry.metadata.timeToFix, fieldTerminator); + file.printField(static_cast(entry.metadata.status), fieldTerminator); + file.println(entry.position); + + sdConfig.filePosition = file.position(); + } + + void appendPositions(SdPositionConfig_t &sdConfig) { + VERBOSE("appendPositions"); + + uint16_t currentEntryIndex = sdConfig.lastSavedEntry + 1; + PositionEntry currentEntry; + File file; + + hardware::i2c::powerOn(); + do { + if (!positions::get(currentEntryIndex, currentEntry)) break; + selectFile(sdConfig, file); + appendPosition(file, sdConfig, currentEntry); + sdConfig.lastSavedEntry = currentEntryIndex; + } while (positions::moveNext(currentEntryIndex)); + hardware::i2c::powerOff(); + + if (file.isOpen()) file.close(); + config::backup::sd::set(sdConfig); + } + } + + void SdPositionsBackup::setup() { + hardware::sdcard::setup(); + } + + void SdPositionsBackup::backup() { + VERBOSE("backup"); + + if (!hardware::sdcard::available) { + VERBOSE_MSG("backup", "not available"); + return; + } + + SdPositionConfig_t sdConfig; + + if (!details::isBackupNeeded(sdConfig)) return; + details::appendPositions(sdConfig); + } + + } + } +} +#endif \ No newline at end of file diff --git a/GpsTracker/SdPositionsBackup.h b/GpsTracker/SdPositionsBackup.h new file mode 100644 index 0000000..f26aad9 --- /dev/null +++ b/GpsTracker/SdPositionsBackup.h @@ -0,0 +1,18 @@ +#pragma once + +#include "PositionsBackup.h" + +namespace positions { + namespace backup { + namespace sd { + + class SdPositionsBackup : public PositionsBackup { + private: + public: + void setup(); + void backup(); + }; + + } + } +} \ No newline at end of file diff --git a/GpsTracker/SdPositionsConfig.cpp b/GpsTracker/SdPositionsConfig.cpp new file mode 100644 index 0000000..d963f36 --- /dev/null +++ b/GpsTracker/SdPositionsConfig.cpp @@ -0,0 +1,71 @@ +#include "SdPositionsConfig.h" +#include "SdCard.h" +#include "Debug.h" + +#define LOGGER_NAME "Config::backup::sd" + +#if BACKUP_ENABLE_SDCARD +namespace config { + namespace backup { + namespace sd { + + SdPositionConfig_t value; + File configFile; + + namespace details { + + void ensureOpened() { + if (!configFile.isOpen()) { + hardware::sdcard::filesystem.chdir(); + configFile.open(POSITIONS_CONFIG_FILENAME, O_RDWR | O_CREAT); + } + + configFile.rewind(); + } + + void read() { + VERBOSE("read"); + ensureOpened(); + configFile.read((void*)&value, sizeof(value)); + if (value.seed != POSITIONS_CONFIG_SEED) reset(); + } + + void write() { + VERBOSE("write"); + ensureOpened(); + configFile.write((void*)&value, sizeof(value)); + } + + } + + SdPositionConfig_t get() { + if (value.seed != POSITIONS_CONFIG_SEED) details::read(); + + return value; + } + + void set(SdPositionConfig_t config) { + value = config; + details::write(); + } + + void reset() { + VERBOSE("reset"); + SdPositionConfig_t config = { + POSITIONS_CONFIG_SEED, + POSITIONS_CONFIG_DEFAULT_SAVE_THRESHOLD, + POSITIONS_CONFIG_DEFAULT_MAX_RECORDS_PER_FILE, + 0xFFFF, + 0, + 0, + 0 + }; + + value = config; + details::write(); + } + + } + } +} +#endif \ No newline at end of file diff --git a/GpsTracker/SdPositionsConfig.h b/GpsTracker/SdPositionsConfig.h new file mode 100644 index 0000000..f6f06fc --- /dev/null +++ b/GpsTracker/SdPositionsConfig.h @@ -0,0 +1,35 @@ +#pragma once + +#include + +#define POSITIONS_FOLDER "positions" +#define POSITIONS_FILENAME "positions-%05d.csv" +#define POSITIONS_FILENAME_LENGTH 19 +#define POSITIONS_CONFIG_FILENAME "positions.config" +#define POSITIONS_CONFIG_SEED 45 +#define POSITIONS_CONFIG_DEFAULT_SAVE_THRESHOLD 10 +#define POSITIONS_CONFIG_DEFAULT_MAX_RECORDS_PER_FILE 5 + +struct SdPositionConfig_t { + uint8_t seed; + uint8_t saveThreshold; + uint8_t maxRecordsPerFile; + uint16_t lastSavedEntry; + uint16_t fileIndex; + uint32_t filePosition; + size_t fileRecords; +}; + +namespace config { + namespace backup { + namespace sd { + + void setup(); + + SdPositionConfig_t get(); + void set(SdPositionConfig_t config); + void reset(); + + } + } +} \ No newline at end of file