@@ -5,53 +5,74 @@ | |||||
#define LOGGER_NAME "Config" | #define LOGGER_NAME "Config" | ||||
namespace 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(); | |||||
} | } | ||||
} | } |
@@ -2,44 +2,85 @@ | |||||
#include <Arduino.h> | #include <Arduino.h> | ||||
#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 { | struct sleepTimings_t { | ||||
uint8_t speed; | uint8_t speed; | ||||
uint16_t timeMin; | |||||
uint16_t timeMax; | |||||
uint16_t seconds; | uint16_t seconds; | ||||
}; | }; | ||||
struct Config { | |||||
char seed[5]; | |||||
struct config_t { | |||||
uint8_t seed; | |||||
char version[5]; | char version[5]; | ||||
char apn[20]; | |||||
uint16_t firstEntry; | uint16_t firstEntry; | ||||
uint16_t lastEntry; | 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 { | namespace config { | ||||
static const sleepTimings_t defaultSleepTimings[] PROGMEM = { | 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(); | |||||
} | |||||
} | } |
@@ -8,38 +8,57 @@ using namespace utils; | |||||
namespace core { | namespace core { | ||||
uint16_t sleepTime = SLEEP_DEFAULT_TIME_SECONDS;; | uint16_t sleepTime = SLEEP_DEFAULT_TIME_SECONDS;; | ||||
uint8_t increaseInARow = 0; | |||||
void main() { | void main() { | ||||
VERBOSE("main"); | |||||
PositionEntryMetadata metadata; | PositionEntryMetadata metadata; | ||||
if (positions::acquire(metadata)) { | if (positions::acquire(metadata)) { | ||||
positions::appendLast(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++) { | for (uint8_t i = 0; i < flash::getArraySize(config::defaultSleepTimings); i++) { | ||||
sleepTimings_t timing; | sleepTimings_t timing; | ||||
flash::read(&config::defaultSleepTimings[i], timing); | flash::read(&config::defaultSleepTimings[i], timing); | ||||
if (velocity > timing.speed) continue; | if (velocity > timing.speed) continue; | ||||
if (currentTime != 0xFFFF && (currentTime < timing.timeMin || currentTime > timing.timeMax)) continue; | |||||
sleepTime = timing.seconds; | |||||
result = timing.seconds; | |||||
break; | break; | ||||
} | } | ||||
VERBOSE_FORMAT("setSleepTime", "%d", sleepTime); | |||||
VERBOSE_FORMAT("computeSleepTime", "%d,%d", velocity, result); | |||||
return result; | |||||
} | } | ||||
} | } |
@@ -14,6 +14,6 @@ namespace core { | |||||
extern uint16_t sleepTime; | extern uint16_t sleepTime; | ||||
void main(); | void main(); | ||||
void setSleepTime(uint8_t velocity); | |||||
void setSleepTime(); | |||||
void updateSleepTime(uint8_t velocity); | |||||
uint16_t computeSleepTime(uint8_t velocity); | |||||
} | } |
@@ -1,15 +1,16 @@ | |||||
#include "Debug.h" | #include "Debug.h" | ||||
#include "Flash.h" | #include "Flash.h" | ||||
#include "Positions.h" | #include "Positions.h" | ||||
#include "Core.h" | |||||
#define LOGGER_NAME "Debug" | #define LOGGER_NAME "Debug" | ||||
#define MENU_ENTRY(name, text) const char MENU_##name[] PROGMEM = text | #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, "[R] Run"); | ||||
MENU_ENTRY(RUN_ONCE, "[r] Run once"); | 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(GPS_SET, "[l] Set last GPS position"); | ||||
MENU_ENTRY(RTC_SET, "[T] Get RTC time"); | MENU_ENTRY(RTC_SET, "[T] Get RTC time"); | ||||
MENU_ENTRY(RTC_GET, "[t] Set 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_GET_CONFIG, "[C] Get EEPROM config"); | ||||
MENU_ENTRY(EEPROM_RESET_CONFIG, "[c] Reset EEPROM config"); | MENU_ENTRY(EEPROM_RESET_CONFIG, "[c] Reset EEPROM config"); | ||||
MENU_ENTRY(EEPROM_GET_CONTENT, "[E] Get EEPROM content"); | MENU_ENTRY(EEPROM_GET_CONTENT, "[E] Get EEPROM content"); | ||||
MENU_ENTRY(EEPROM_GET_ENTRIES, "[P] Get EEPROM entries"); | MENU_ENTRY(EEPROM_GET_ENTRIES, "[P] Get EEPROM entries"); | ||||
MENU_ENTRY(EEPROM_GET_LAST_ENTRY, "[p] Get EEPROM last entry"); | 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_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, "[S] Sleep for 8s"); | ||||
MENU_ENTRY(SLEEP_DEEP, "[s] Deep sleep for 10s"); | MENU_ENTRY(SLEEP_DEEP, "[s] Deep sleep for 10s"); | ||||
MENU_ENTRY(QUESTION, "?"); | MENU_ENTRY(QUESTION, "?"); | ||||
@@ -43,13 +44,13 @@ const PROGMEM uint8_t commandIdMapping[] = { | |||||
'l', static_cast<uint8_t>(debug::GPSTRACKER_DEBUG_COMMAND::GPS_SET), | 'l', static_cast<uint8_t>(debug::GPSTRACKER_DEBUG_COMMAND::GPS_SET), | ||||
'T', static_cast<uint8_t>(debug::GPSTRACKER_DEBUG_COMMAND::RTC_GET), | 'T', static_cast<uint8_t>(debug::GPSTRACKER_DEBUG_COMMAND::RTC_GET), | ||||
't', static_cast<uint8_t>(debug::GPSTRACKER_DEBUG_COMMAND::RTC_SET), | 't', static_cast<uint8_t>(debug::GPSTRACKER_DEBUG_COMMAND::RTC_SET), | ||||
'W', static_cast<uint8_t>(debug::GPSTRACKER_DEBUG_COMMAND::SD_WRITE_TEST), | |||||
'C', static_cast<uint8_t>(debug::GPSTRACKER_DEBUG_COMMAND::EEPROM_GET_CONFIG), | 'C', static_cast<uint8_t>(debug::GPSTRACKER_DEBUG_COMMAND::EEPROM_GET_CONFIG), | ||||
'c', static_cast<uint8_t>(debug::GPSTRACKER_DEBUG_COMMAND::EEPROM_RESET_CONFIG), | 'c', static_cast<uint8_t>(debug::GPSTRACKER_DEBUG_COMMAND::EEPROM_RESET_CONFIG), | ||||
'E', static_cast<uint8_t>(debug::GPSTRACKER_DEBUG_COMMAND::EEPROM_GET_CONTENT), | 'E', static_cast<uint8_t>(debug::GPSTRACKER_DEBUG_COMMAND::EEPROM_GET_CONTENT), | ||||
'P', static_cast<uint8_t>(debug::GPSTRACKER_DEBUG_COMMAND::EEPROM_GET_ENTRIES), | 'P', static_cast<uint8_t>(debug::GPSTRACKER_DEBUG_COMMAND::EEPROM_GET_ENTRIES), | ||||
'p', static_cast<uint8_t>(debug::GPSTRACKER_DEBUG_COMMAND::EEPROM_GET_LAST_ENTRY), | 'p', static_cast<uint8_t>(debug::GPSTRACKER_DEBUG_COMMAND::EEPROM_GET_LAST_ENTRY), | ||||
'a', static_cast<uint8_t>(debug::GPSTRACKER_DEBUG_COMMAND::EEPROM_ADD_ENTRY), | 'a', static_cast<uint8_t>(debug::GPSTRACKER_DEBUG_COMMAND::EEPROM_ADD_ENTRY), | ||||
'B', static_cast<uint8_t>(debug::GPSTRACKER_DEBUG_COMMAND::EEPROM_BACKUP_ENTRIES), | |||||
'S', static_cast<uint8_t>(debug::GPSTRACKER_DEBUG_COMMAND::SLEEP), | 'S', static_cast<uint8_t>(debug::GPSTRACKER_DEBUG_COMMAND::SLEEP), | ||||
's', static_cast<uint8_t>(debug::GPSTRACKER_DEBUG_COMMAND::SLEEP_DEEP), | 's', static_cast<uint8_t>(debug::GPSTRACKER_DEBUG_COMMAND::SLEEP_DEEP), | ||||
}; | }; | ||||
@@ -78,17 +79,13 @@ const char * const MENU_ENTRIES[] PROGMEM = { | |||||
MENU_SEPARATOR, | MENU_SEPARATOR, | ||||
MENU_SD_WRITE_TEST, | |||||
MENU_SEPARATOR, | |||||
MENU_EEPROM_GET_CONFIG, | MENU_EEPROM_GET_CONFIG, | ||||
MENU_EEPROM_RESET_CONFIG, | MENU_EEPROM_RESET_CONFIG, | ||||
MENU_EEPROM_GET_CONTENT, | MENU_EEPROM_GET_CONTENT, | ||||
MENU_EEPROM_GET_ENTRIES, | MENU_EEPROM_GET_ENTRIES, | ||||
MENU_EEPROM_GET_LAST_ENTRY, | MENU_EEPROM_GET_LAST_ENTRY, | ||||
MENU_EEPROM_ADD_ENTRY, | MENU_EEPROM_ADD_ENTRY, | ||||
MENU_EEPROM_BACKUP_ENTRIES, | |||||
MENU_SEPARATOR, | MENU_SEPARATOR, | ||||
MENU_SLEEP, | MENU_SLEEP, | ||||
@@ -109,20 +106,18 @@ namespace debug { | |||||
namespace details { | namespace details { | ||||
inline void displayPosition(PositionEntry entry) { | 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() { | int freeRam() { | ||||
return freeRam2(); | return freeRam2(); | ||||
} | } | ||||
void displayFreeRam() { | |||||
Log.notice(F("RAM: %d\n"), freeRam()); | |||||
} | |||||
GPSTRACKER_DEBUG_COMMAND parseCommand(char id) { | GPSTRACKER_DEBUG_COMMAND parseCommand(char id) { | ||||
size_t mappingArraySize = flash::getArraySize(commandIdMapping); | size_t mappingArraySize = flash::getArraySize(commandIdMapping); | ||||
char commandId; | char commandId; | ||||
@@ -135,20 +130,28 @@ namespace debug { | |||||
return GPSTRACKER_DEBUG_COMMAND::NONE; | 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; | GPSTRACKER_DEBUG_COMMAND command; | ||||
size_t menuSize = flash::getArraySize(MENU_ENTRIES); | size_t menuSize = flash::getArraySize(MENU_ENTRIES); | ||||
uint8_t intermediate_timeout = 50; | |||||
do { | do { | ||||
for (uint8_t i = 0; i < menuSize; i++) { | for (uint8_t i = 0; i < menuSize; i++) { | ||||
Serial.println(reinterpret_cast<const __FlashStringHelper *>(pgm_read_word_near(&MENU_ENTRIES[i]))); | Serial.println(reinterpret_cast<const __FlashStringHelper *>(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()); | command = parseCommand(Serial.read()); | ||||
while (Serial.available()) Serial.read(); | |||||
while (Serial.available()) Serial.read(); //flushing input | |||||
} while (command == GPSTRACKER_DEBUG_COMMAND::NONE); | } while (command == GPSTRACKER_DEBUG_COMMAND::NONE); | ||||
return command; | return command; | ||||
@@ -164,6 +167,8 @@ namespace debug { | |||||
strlcpy_P(gps::lastPosition, FAKE_GPS_ENTRY, GPS_POSITION_SIZE); | strlcpy_P(gps::lastPosition, FAKE_GPS_ENTRY, GPS_POSITION_SIZE); | ||||
NOTICE_FORMAT("setFakeGpsPosition", "Last position set to : %s", gps::lastPosition); | 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() { | 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); | 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() { | void getAndDisplayEepromConfig() { | ||||
config::get(); | |||||
config::main::setup(); //forcing read again | |||||
} | } | ||||
void getAndDisplayEepromContent() { | void getAndDisplayEepromContent() { | ||||
@@ -202,7 +225,7 @@ namespace debug { | |||||
} | } | ||||
void getAndDisplayEepromPositions() { | void getAndDisplayEepromPositions() { | ||||
uint16_t currentEntryIndex = config::get().firstEntry; | |||||
uint16_t currentEntryIndex = config::main::value.firstEntry; | |||||
PositionEntry currentEntry; | PositionEntry currentEntry; | ||||
hardware::i2c::powerOn(); | hardware::i2c::powerOn(); | ||||
@@ -214,11 +237,11 @@ namespace debug { | |||||
} | } | ||||
void getAndDisplayEepromLastPosition() { | void getAndDisplayEepromLastPosition() { | ||||
uint16_t lastEntryIndex = config::get().lastEntry; | |||||
uint16_t lastEntryIndex = config::main::value.lastEntry; | |||||
PositionEntry 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() { | void addLastPositionToEeprom() { | ||||
@@ -234,14 +257,6 @@ namespace debug { | |||||
SIM808_GPS_STATUS::OFF | 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); | |||||
} | } | ||||
} | } |
@@ -1,67 +1,44 @@ | |||||
#pragma once | #pragma once | ||||
#include <Arduino.h> | #include <Arduino.h> | ||||
#include <ArduinoLog.h> | |||||
#include "Config.h" | #include "Config.h" | ||||
#include "Logging.h" | |||||
#include "Core.h" | |||||
#include "Hardware.h" | #include "Hardware.h" | ||||
#include "Gps.h" | #include "Gps.h" | ||||
#include "Rtc.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 { | namespace debug { | ||||
enum class GPSTRACKER_DEBUG_COMMAND : uint8_t { | 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(); | int freeRam(); | ||||
void displayFreeRam(); | |||||
GPSTRACKER_DEBUG_COMMAND menu(); | |||||
GPSTRACKER_DEBUG_COMMAND menu(uint16_t timeout); | |||||
void getAndDisplayBattery(); | void getAndDisplayBattery(); | ||||
void getAndDisplayGpsPosition(); | void getAndDisplayGpsPosition(); | ||||
@@ -70,12 +47,13 @@ namespace debug { | |||||
void getAndDisplayRtcTime(); | void getAndDisplayRtcTime(); | ||||
void setRtcTime(); | void setRtcTime(); | ||||
void getAndDisplaySleepTimes(); | |||||
void getAndDisplayEepromConfig(); | void getAndDisplayEepromConfig(); | ||||
void getAndDisplayEepromContent(); | void getAndDisplayEepromContent(); | ||||
void getAndDisplayEepromPositions(); | void getAndDisplayEepromPositions(); | ||||
void getAndDisplayEepromLastPosition(); | void getAndDisplayEepromLastPosition(); | ||||
void addLastPositionToEeprom(); | void addLastPositionToEeprom(); | ||||
inline void displayFreeRam() { Serial.println(freeRam()); } | |||||
} | } |
@@ -25,13 +25,14 @@ namespace gps { | |||||
char lastPosition[GPS_POSITION_SIZE]; | char lastPosition[GPS_POSITION_SIZE]; | ||||
SIM808_GPS_STATUS lastStatus; | 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; | SIM808_GPS_STATUS currentStatus = SIM808_GPS_STATUS::OFF; | ||||
do { | do { | ||||
currentStatus = hardware::sim808::device.getGpsStatus(); | 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); | mainunit::deepSleep(GPS_DEFAULT_INTERMEDIATE_TIMEOUT_MS / 1000); | ||||
timeout -= GPS_DEFAULT_INTERMEDIATE_TIMEOUT_MS; | timeout -= GPS_DEFAULT_INTERMEDIATE_TIMEOUT_MS; | ||||
} while (timeout > 1); | } while (timeout > 1); | ||||
@@ -41,12 +42,13 @@ namespace gps { | |||||
hardware::sim808::device.getGpsPosition(lastPosition); | hardware::sim808::device.getGpsPosition(lastPosition); | ||||
} | } | ||||
NOTICE_FORMAT("acquireCurrentPosition", "%d", currentStatus); | |||||
return currentStatus; | return currentStatus; | ||||
} | } | ||||
uint8_t getVelocity() { | uint8_t getVelocity() { | ||||
uint8_t velocity; | 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); | VERBOSE_FORMAT("getVelocity", "%d", velocity); | ||||
@@ -67,6 +69,6 @@ namespace gps { | |||||
time.Minute = details::parseSubstring(buffer, timeStr + TIME_MINUTE_OFFSET, 2); | time.Minute = details::parseSubstring(buffer, timeStr + TIME_MINUTE_OFFSET, 2); | ||||
time.Second = details::parseSubstring(buffer, timeStr + TIME_SECOND_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); | |||||
} | } | ||||
} | } |
@@ -15,7 +15,7 @@ namespace gps { | |||||
inline void powerOn() { hardware::sim808::gpsPowerOn(); } | inline void powerOn() { hardware::sim808::gpsPowerOn(); } | ||||
inline void powerOff() { hardware::sim808::gpsPowerOff(); } | inline void powerOff() { hardware::sim808::gpsPowerOff(); } | ||||
SIM808_GPS_STATUS acquireCurrentPosition(uint16_t timeout); | |||||
SIM808_GPS_STATUS acquireCurrentPosition(uint32_t timeout); | |||||
uint8_t getVelocity(); | uint8_t getVelocity(); | ||||
void getTime(tmElements_t &time); | void getTime(tmElements_t &time); |
@@ -1,7 +1,6 @@ | |||||
#pragma once | #pragma once | ||||
#include <Arduino.h> | #include <Arduino.h> | ||||
#include <SoftwareSerial.h> | |||||
#include <SIM808.h> | #include <SIM808.h> | ||||
#include "Debug.h" | #include "Debug.h" | ||||
@@ -1,24 +1,32 @@ | |||||
#include "GpsTracker.h" | #include "GpsTracker.h" | ||||
#include "Positions.h" | #include "Positions.h" | ||||
#if _DEBUG | |||||
#define MENU_DEFAULT_TIMEOUT 0 | |||||
#else | |||||
#define MENU_DEFAULT_TIMEOUT 10000 | |||||
#endif | |||||
bool bypassMenu = false; | bool bypassMenu = false; | ||||
uint16_t menuTimeout = MENU_DEFAULT_TIMEOUT; | |||||
void setup() { | 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(); | rtc::setup(); | ||||
hardware::sim808::setup(); | hardware::sim808::setup(); | ||||
positions::setup(); | |||||
} | } | ||||
void loop() { | void loop() { | ||||
debug::GPSTRACKER_DEBUG_COMMAND command = debug::GPSTRACKER_DEBUG_COMMAND::RUN; | 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; | bypassMenu = command == debug::GPSTRACKER_DEBUG_COMMAND::RUN; | ||||
@@ -55,7 +63,7 @@ void loop() { | |||||
debug::getAndDisplayEepromConfig(); | debug::getAndDisplayEepromConfig(); | ||||
break; | break; | ||||
case debug::GPSTRACKER_DEBUG_COMMAND::EEPROM_RESET_CONFIG: | case debug::GPSTRACKER_DEBUG_COMMAND::EEPROM_RESET_CONFIG: | ||||
config::reset(); | |||||
config::main::reset(); | |||||
break; | break; | ||||
case debug::GPSTRACKER_DEBUG_COMMAND::EEPROM_GET_CONTENT: | case debug::GPSTRACKER_DEBUG_COMMAND::EEPROM_GET_CONTENT: | ||||
debug::getAndDisplayEepromContent(); | debug::getAndDisplayEepromContent(); | ||||
@@ -69,14 +77,16 @@ void loop() { | |||||
case debug::GPSTRACKER_DEBUG_COMMAND::EEPROM_ADD_ENTRY: | case debug::GPSTRACKER_DEBUG_COMMAND::EEPROM_ADD_ENTRY: | ||||
debug::addLastPositionToEeprom(); | debug::addLastPositionToEeprom(); | ||||
break; | break; | ||||
case debug::GPSTRACKER_DEBUG_COMMAND::EEPROM_BACKUP_ENTRIES: | |||||
positions::doBackup(); | |||||
break; | |||||
case debug::GPSTRACKER_DEBUG_COMMAND::SLEEP: | case debug::GPSTRACKER_DEBUG_COMMAND::SLEEP: | ||||
mainunit::sleep(period_t::SLEEP_8S); | mainunit::sleep(period_t::SLEEP_8S); | ||||
break; | break; | ||||
case debug::GPSTRACKER_DEBUG_COMMAND::SLEEP_DEEP: | case debug::GPSTRACKER_DEBUG_COMMAND::SLEEP_DEEP: | ||||
mainunit::deepSleep(10); | mainunit::deepSleep(10); | ||||
break; | break; | ||||
case debug::GPSTRACKER_DEBUG_COMMAND::SD_WRITE_TEST: | |||||
default: | default: | ||||
Serial.println(F("Unsupported command !")); | |||||
NOTICE_MSG("loop", "Unsupported"); | |||||
} | } | ||||
} | } |
@@ -1,3 +1,4 @@ | |||||
#include "Config.h" | |||||
#include "Hardware.h" | #include "Hardware.h" | ||||
#include "Pins.h" | #include "Pins.h" | ||||
#include "Debug.h" | #include "Debug.h" | ||||
@@ -41,12 +42,10 @@ namespace hardware { | |||||
} | } | ||||
void setup() { | void setup() { | ||||
VERBOSE("setup"); | |||||
device.powerOnOff(true); | |||||
simSerial.begin(4800); | |||||
NOTICE("setup"); | |||||
simSerial.begin(SIM808_BAUDRATE); | |||||
device.begin(simSerial); | device.begin(simSerial); | ||||
device.powerOnOff(false); | |||||
powerOff(); //ensure powerOff on start | |||||
} | } | ||||
void gpsPowerOn() { | void gpsPowerOn() { | ||||
@@ -56,7 +55,7 @@ namespace hardware { | |||||
} | } | ||||
void gpsPowerOff() { | void gpsPowerOff() { | ||||
VERBOSE("gpsPowerOff"); | |||||
NOTICE("gpsPowerOff"); | |||||
device.disableGps(); | device.disableGps(); | ||||
powerOffIfUnused(); | powerOffIfUnused(); | ||||
} | } | ||||
@@ -65,13 +64,12 @@ namespace hardware { | |||||
VERBOSE("networkPowerOn"); | VERBOSE("networkPowerOn"); | ||||
powerOn(); | powerOn(); | ||||
device.setPhoneFunctionality(SIM808_PHONE_FUNCTIONALITY::FULL); | device.setPhoneFunctionality(SIM808_PHONE_FUNCTIONALITY::FULL); | ||||
device.enableGprs(config::get().apn); | |||||
} | } | ||||
void networkPowerOff() { | void networkPowerOff() { | ||||
VERBOSE("networkPowerOff"); | VERBOSE("networkPowerOff"); | ||||
device.setPhoneFunctionality(SIM808_PHONE_FUNCTIONALITY::MINIMUM); | |||||
device.disableGprs(); | device.disableGprs(); | ||||
device.setPhoneFunctionality(SIM808_PHONE_FUNCTIONALITY::MINIMUM); | |||||
powerOffIfUnused(); | powerOffIfUnused(); | ||||
} | } | ||||
@@ -82,11 +80,8 @@ namespace hardware { | |||||
namespace i2c { | namespace i2c { | ||||
E24 eeprom = E24(E24Size_t::E24_512K); | E24 eeprom = E24(E24Size_t::E24_512K); | ||||
uint8_t poweredCount = 0; | uint8_t poweredCount = 0; | ||||
//inline void powered() { digitalRead(I2C_PWR) == HIGH; } //TODO = replace enum with just reading the output pin ? | |||||
void powerOn() { | void powerOn() { | ||||
if (!poweredCount) { | if (!poweredCount) { | ||||
VERBOSE("powerOn"); | VERBOSE("powerOn"); | ||||
@@ -1,11 +1,13 @@ | |||||
#pragma once | #pragma once | ||||
#include <SoftwareSerial.h> | |||||
#include <SIM808.h> | #include <SIM808.h> | ||||
#include <E24.h> | #include <E24.h> | ||||
namespace hardware { | namespace hardware { | ||||
namespace sim808 { | namespace sim808 { | ||||
extern SoftwareSerial simSerial; | |||||
extern SIM808 device; | extern SIM808 device; | ||||
void powerOn(); | void powerOn(); | ||||
@@ -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")); | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,35 @@ | |||||
#pragma once | |||||
#include "Config.h" | |||||
//#define DISABLE_LOGGING 1 | |||||
#include <ArduinoLog.h> | |||||
#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(); | |||||
} |
@@ -7,6 +7,23 @@ | |||||
namespace mainunit { | 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() { | void interrupt() { | ||||
detachInterrupt(digitalPinToInterrupt(RTC_WAKE)); | detachInterrupt(digitalPinToInterrupt(RTC_WAKE)); | ||||
} | } | ||||
@@ -19,18 +36,18 @@ namespace mainunit { | |||||
} | } | ||||
void sleep(period_t period) { | 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); | LowPower.powerDown(period, ADC_OFF, BOD_OFF); | ||||
NOTICE_MSG("sleep", "Woke up"); | |||||
details::wokeUp(); | |||||
} | } | ||||
void deepSleep(uint16_t seconds) { | void deepSleep(uint16_t seconds) { | ||||
NOTICE_FORMAT("deepSleep", "Deep sleeping for %d seconds", seconds); | |||||
NOTICE_FORMAT("deepSleep", "%d seconds", seconds); | |||||
interruptIn(seconds); | interruptIn(seconds); | ||||
delay(5); | |||||
details::prepareSleep(); | |||||
LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF); | LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF); | ||||
NOTICE_MSG("deepSleep", "Woke up"); | |||||
details::wokeUp(); | |||||
} | } | ||||
} | } |
@@ -1,8 +1,52 @@ | |||||
#include "Config.h" | |||||
#if BACKUP_ENABLE_NETWORK | |||||
#include "Debug.h" | |||||
#include "Network.h" | #include "Network.h" | ||||
#include "Hardware.h" | #include "Hardware.h" | ||||
#include "MainUnit.h" | |||||
#define LOGGER_NAME "Network" | #define LOGGER_NAME "Network" | ||||
namespace 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); | |||||
} | |||||
} | |||||
} | |||||
#endif |
@@ -6,4 +6,8 @@ namespace network { | |||||
inline void powerOn() { hardware::sim808::networkPowerOn(); } | inline void powerOn() { hardware::sim808::networkPowerOn(); } | ||||
inline void powerOff() { hardware::sim808::networkPowerOff(); } | inline void powerOff() { hardware::sim808::networkPowerOff(); } | ||||
SIM808RegistrationStatus waitForRegistered(uint32_t timeout); | |||||
bool isAvailable(SIM808_NETWORK_REGISTRATION_STATE state); | |||||
bool enableGprs(); | |||||
} | } |
@@ -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<uint16_t>(entry.metadata.temperature * 100), | |||||
static_cast<uint8_t>(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 |
@@ -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(); | |||||
}; | |||||
} | |||||
} | |||||
} |
@@ -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]; | |||||
}; |
@@ -1,26 +1,60 @@ | |||||
#include "Positions.h" | |||||
#include "Debug.h" | |||||
#include "Config.h" | #include "Config.h" | ||||
#include "Debug.h" | |||||
#include "Positions.h" | |||||
#include "Gps.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 LOGGER_NAME "Positions" | ||||
#define ENTRY_RESERVED_SIZE 128 | #define ENTRY_RESERVED_SIZE 128 | ||||
#define ENTRIES_ADDR ENTRY_RESERVED_SIZE | |||||
#define ENTRIES_ADDR CONFIG_RESERVED_SIZE | |||||
namespace positions { | 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) { | bool acquire(PositionEntryMetadata &metadata) { | ||||
VERBOSE("acquire"); | |||||
NOTICE("acquire"); | |||||
timestamp_t before; | timestamp_t before; | ||||
@@ -30,6 +64,8 @@ namespace positions { | |||||
SIM808ChargingStatus battery = hardware::sim808::device.getChargingState(); | SIM808ChargingStatus battery = hardware::sim808::device.getChargingState(); | ||||
gps::powerOff(); | gps::powerOff(); | ||||
NOTICE_FORMAT("acquire", "Status : %d", gpsStatus); | |||||
if (gpsStatus < SIM808_GPS_STATUS::FIX) return false; | if (gpsStatus < SIM808_GPS_STATUS::FIX) return false; | ||||
uint16_t timeToFix = rtc::getTime() - before; | uint16_t timeToFix = rtc::getTime() - before; | ||||
@@ -52,29 +88,34 @@ namespace positions { | |||||
void appendLast(const PositionEntryMetadata &metadata) { | void appendLast(const PositionEntryMetadata &metadata) { | ||||
VERBOSE("appendLast"); | VERBOSE("appendLast"); | ||||
uint16_t entryIndex; | |||||
uint16_t entryAddress; | uint16_t entryAddress; | ||||
PositionEntry entry = { metadata }; | PositionEntry entry = { metadata }; | ||||
strlcpy(entry.position, gps::lastPosition, POSITION_SIZE); | 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); | 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(); | hardware::i2c::powerOff(); | ||||
} | } | ||||
bool get(uint16_t index, PositionEntry &entry) { | 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; | if (entryAddress == -1) return false; | ||||
VERBOSE_FORMAT("get", "Reading entry n°%d @ %X", index, entryAddress); | VERBOSE_FORMAT("get", "Reading entry n°%d @ %X", index, entryAddress); | ||||
@@ -83,24 +124,31 @@ namespace positions { | |||||
hardware::i2c::eeprom.readBlock(entryAddress, entry); | hardware::i2c::eeprom.readBlock(entryAddress, entry); | ||||
hardware::i2c::powerOff(); | 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; | return true; | ||||
} | } | ||||
bool moveNext(uint16_t &index) { | 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++; | else index++; | ||||
return true; | 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 | |||||
} | } | ||||
} | } |
@@ -18,12 +18,14 @@ struct PositionEntry { | |||||
}; //sizeof = 125 | }; //sizeof = 125 | ||||
namespace positions { | namespace positions { | ||||
void setup(); | |||||
bool acquire(PositionEntryMetadata &metadata); | bool acquire(PositionEntryMetadata &metadata); | ||||
void appendLast(const PositionEntryMetadata &metadata); | void appendLast(const PositionEntryMetadata &metadata); | ||||
bool get(uint16_t index, PositionEntry &entry); | bool get(uint16_t index, PositionEntry &entry); | ||||
bool moveNext(uint16_t &index); | bool moveNext(uint16_t &index); | ||||
uint16_t count(uint16_t fromIndex); | |||||
bool needsToSend(); | |||||
void send(); | |||||
void doBackup(); | |||||
} | } |
@@ -0,0 +1,9 @@ | |||||
#include "PositionsBackup.h" | |||||
namespace positions { | |||||
namespace backup { | |||||
PositionsBackup::~PositionsBackup() {} | |||||
} | |||||
} |
@@ -0,0 +1,14 @@ | |||||
#pragma once | |||||
namespace positions { | |||||
namespace backup { | |||||
class PositionsBackup { | |||||
public: | |||||
~PositionsBackup(); | |||||
virtual void setup()=0; | |||||
virtual void backup()=0; | |||||
}; | |||||
} | |||||
} |
@@ -17,7 +17,7 @@ namespace rtc { | |||||
hardware::i2c::powerOn(); | hardware::i2c::powerOn(); | ||||
RTC.control(DS3231_12H, DS3231_OFF); //24 hours clock | RTC.control(DS3231_12H, DS3231_OFF); //24 hours clock | ||||
RTC.control(DS3231_A1_INT_ENABLE, DS3231_OFF); //Alarm 1 OFF | 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(); | hardware::i2c::powerOff(); | ||||
//TODO : check wether the osc has been halted (meaning the battery could be dead) | //TODO : check wether the osc has been halted (meaning the battery could be dead) | ||||
@@ -31,6 +31,14 @@ namespace rtc { | |||||
return temperature; | return temperature; | ||||
} | } | ||||
bool isAccurate() { | |||||
hardware::i2c::powerOn(); | |||||
bool accurate = RTC.status(DS3231_HALTED_FLAG) == DS3231_OFF; | |||||
hardware::i2c::powerOff(); | |||||
return accurate; | |||||
} | |||||
timestamp_t getTime() { | timestamp_t getTime() { | ||||
tmElements_t time; | tmElements_t time; | ||||
getTime(time); | getTime(time); | ||||
@@ -50,6 +58,7 @@ namespace rtc { | |||||
hardware::i2c::powerOn(); | hardware::i2c::powerOn(); | ||||
RTC.writeTime(time); | RTC.writeTime(time); | ||||
RTC.control(DS3231_HALTED_FLAG, DS3231_OFF); | |||||
hardware::i2c::powerOff(); | hardware::i2c::powerOff(); | ||||
} | } | ||||
@@ -7,6 +7,7 @@ namespace rtc { | |||||
float getTemperature(); | float getTemperature(); | ||||
bool isAccurate(); | |||||
timestamp_t getTime(); | timestamp_t getTime(); | ||||
void getTime(tmElements_t &time); | void getTime(tmElements_t &time); | ||||
void setTime(const tmElements_t &time); | void setTime(const tmElements_t &time); | ||||
@@ -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 |
@@ -0,0 +1,14 @@ | |||||
#pragma once | |||||
#include <SdFat.h> | |||||
#include "Pins.h" | |||||
namespace hardware { | |||||
namespace sdcard { | |||||
extern SdFat filesystem; | |||||
extern bool available; | |||||
void setup(); | |||||
} | |||||
} |
@@ -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<uint8_t>(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 |
@@ -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(); | |||||
}; | |||||
} | |||||
} | |||||
} |
@@ -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 |
@@ -0,0 +1,35 @@ | |||||
#pragma once | |||||
#include <Arduino.h> | |||||
#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(); | |||||
} | |||||
} | |||||
} |