@@ -1,50 +1,50 @@ | |||||
#pragma once | |||||
#include "Debug.h" | |||||
#include "Alerts.h" | |||||
#include "Config.h" | |||||
#include "Rtc.h" | |||||
#define LOGGER_NAME "Alerts" | |||||
namespace alerts { | |||||
uint8_t getTriggered(PositionEntryMetadata &metadata) { | |||||
config_t* config = &config::main::value; | |||||
uint8_t active = 0; | |||||
if (metadata.batteryLevel <= config->alertBatteryLevel1) bitSet(active, ALERT_BATTERY_LEVEL_1); | |||||
if (metadata.batteryLevel <= config->alertBatteryLevel2) bitSet(active, ALERT_BATTERY_LEVEL_2); | |||||
if (metadata.temperature == ALERT_SUSPICIOUS_RTC_TEMPERATURE) bitSet(active, ALERT_RTC_TEMPERATURE_FAILURE); | |||||
if (!rtc::isAccurate()) bitSet(active, ALERT_RTC_CLOCK_FAILURE); | |||||
return config->activeAlerts ^ active; | |||||
} | |||||
void add(uint8_t mask) { | |||||
config_t* config = &config::main::value; | |||||
uint8_t active = config->activeAlerts; | |||||
active |= mask; | |||||
if (config->activeAlerts == active) return; //save a write to eeprom if there is no change | |||||
config->activeAlerts = active; | |||||
config::main::save(); | |||||
} | |||||
void clear(PositionEntryMetadata &metadata) { | |||||
config_t* config = &config::main::value; | |||||
uint8_t clearMask = 0; | |||||
uint8_t active = config->activeAlerts; | |||||
if (metadata.batteryLevel >= config->alertBatteryLevelClear) clearMask |= _BV(ALERT_BATTERY_LEVEL_1) | _BV(ALERT_BATTERY_LEVEL_2); | |||||
if (metadata.temperature != ALERT_SUSPICIOUS_RTC_TEMPERATURE) bitSet(clearMask, ALERT_RTC_TEMPERATURE_FAILURE); | |||||
if (rtc::isAccurate()) bitSet(clearMask, ALERT_RTC_CLOCK_FAILURE); | |||||
active &= ~clearMask; | |||||
if (config->activeAlerts == active) return; //save a write to eeprom if there is no change | |||||
config->activeAlerts = active; | |||||
config::main::save(); | |||||
} | |||||
} | |||||
#pragma once | |||||
#include "Alerts.h" | |||||
#include "Config.h" | |||||
#include "Rtc.h" | |||||
#include "Logging.h" | |||||
#define LOGGER_NAME "Alerts" | |||||
namespace alerts { | |||||
uint8_t getTriggered(PositionEntryMetadata &metadata) { | |||||
config_t* config = &config::main::value; | |||||
uint8_t active = 0; | |||||
if (metadata.batteryLevel <= config->alertBatteryLevel1) bitSet(active, ALERT_BATTERY_LEVEL_1); | |||||
if (metadata.batteryLevel <= config->alertBatteryLevel2) bitSet(active, ALERT_BATTERY_LEVEL_2); | |||||
if (metadata.temperature == ALERT_SUSPICIOUS_RTC_TEMPERATURE) bitSet(active, ALERT_RTC_TEMPERATURE_FAILURE); | |||||
if (!rtc::isAccurate()) bitSet(active, ALERT_RTC_CLOCK_FAILURE); | |||||
return config->activeAlerts ^ active; | |||||
} | |||||
void add(uint8_t mask) { | |||||
config_t* config = &config::main::value; | |||||
uint8_t active = config->activeAlerts; | |||||
active |= mask; | |||||
if (config->activeAlerts == active) return; //save a write to eeprom if there is no change | |||||
config->activeAlerts = active; | |||||
config::main::save(); | |||||
} | |||||
void clear(PositionEntryMetadata &metadata) { | |||||
config_t* config = &config::main::value; | |||||
uint8_t clearMask = 0; | |||||
uint8_t active = config->activeAlerts; | |||||
if (metadata.batteryLevel >= config->alertBatteryLevelClear) clearMask |= _BV(ALERT_BATTERY_LEVEL_1) | _BV(ALERT_BATTERY_LEVEL_2); | |||||
if (metadata.temperature != ALERT_SUSPICIOUS_RTC_TEMPERATURE) bitSet(clearMask, ALERT_RTC_TEMPERATURE_FAILURE); | |||||
if (rtc::isAccurate()) bitSet(clearMask, ALERT_RTC_CLOCK_FAILURE); | |||||
active &= ~clearMask; | |||||
if (config->activeAlerts == active) return; //save a write to eeprom if there is no change | |||||
config->activeAlerts = active; | |||||
config::main::save(); | |||||
} | |||||
} |
@@ -1,86 +1,86 @@ | |||||
#include "Config.h" | |||||
#include "Debug.h" | |||||
#include "Hardware.h" | |||||
#define LOGGER_NAME "Config" | |||||
namespace config { | |||||
namespace main { | |||||
config_t value; | |||||
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, %d, %d, %d, %B, %s", value.seed, value.version, value.firstEntry, value.lastEntry, value.alertBatteryLevel1, value.alertBatteryLevel2, value.alertBatteryLevelClear, value.activeAlerts, value.contactPhone); | |||||
#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 | |||||
/*strcpy_P(value.version, PSTR(VERSION)); | |||||
value.alertBatteryLevel1 = CONFIG_DEFAULT_BATTERY_ALERT_LEVEL1; | |||||
value.alertBatteryLevel2 = CONFIG_DEFAULT_BATTERY_ALERT_LEVEL2; | |||||
value.alertBatteryLevelClear = CONFIG_DEFAULT_BATTERY_ALERT_CLEAR; | |||||
value.activeAlerts = CONFIG_DEFAULT_ACTIVE_ALERTS; | |||||
strcpy_P(config.contactPhone, PSTR(CONFIG_DEFAULT_CONTACT_PHONE));*/ | |||||
} | |||||
void write() { | |||||
NOTICE_FORMAT("write", "%d, %s, %d, %d, %d, %d, %d, %B, %s", value.seed, value.version, value.firstEntry, value.lastEntry, value.alertBatteryLevel1, value.alertBatteryLevel2, value.alertBatteryLevelClear, value.activeAlerts, value.contactPhone); | |||||
#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 setup() { | |||||
details::read(); | |||||
//details::write(); | |||||
} | |||||
void save() { | |||||
details::write(); | |||||
} | |||||
void reset() { | |||||
VERBOSE("reset"); | |||||
config_t config = {}; | |||||
config.seed = CONFIG_SEED; | |||||
strcpy_P(config.version, PSTR(VERSION)); | |||||
config.firstEntry = config.lastEntry = 0xFFFF; | |||||
config.alertBatteryLevel1 = CONFIG_DEFAULT_BATTERY_ALERT_LEVEL1; | |||||
config.alertBatteryLevel2 = CONFIG_DEFAULT_BATTERY_ALERT_LEVEL2; | |||||
config.alertBatteryLevelClear = CONFIG_DEFAULT_BATTERY_ALERT_CLEAR; | |||||
config.activeAlerts = CONFIG_DEFAULT_ACTIVE_ALERTS; | |||||
strcpy_P(config.contactPhone, PSTR(CONFIG_DEFAULT_CONTACT_PHONE)); | |||||
#if BACKUP_ENABLE_NETWORK | |||||
config.network.saveThreshold = POSITIONS_CONFIG_NET_DEFAULT_SAVE_THRESHOLD; | |||||
config.network.lastSavedEntry = 0xFFFF; | |||||
strcpy_P(config.network.apn, PSTR(POSITIONS_CONFIG_NET_DEFAULT_APN)); | |||||
strcpy_P(config.network.url, PSTR(POSITIONS_CONFIG_NET_DEFAULT_URL)); | |||||
#endif | |||||
value = config; | |||||
save(); | |||||
} | |||||
} | |||||
#include "Config.h" | |||||
#include "Hardware.h" | |||||
#include "Logging.h" | |||||
#define LOGGER_NAME "Config" | |||||
namespace config { | |||||
namespace main { | |||||
config_t value; | |||||
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, %d, %d, %d, %B, %s", value.seed, value.version, value.firstEntry, value.lastEntry, value.alertBatteryLevel1, value.alertBatteryLevel2, value.alertBatteryLevelClear, value.activeAlerts, value.contactPhone); | |||||
#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 | |||||
/*strcpy_P(value.version, PSTR(VERSION)); | |||||
value.alertBatteryLevel1 = CONFIG_DEFAULT_BATTERY_ALERT_LEVEL1; | |||||
value.alertBatteryLevel2 = CONFIG_DEFAULT_BATTERY_ALERT_LEVEL2; | |||||
value.alertBatteryLevelClear = CONFIG_DEFAULT_BATTERY_ALERT_CLEAR; | |||||
value.activeAlerts = CONFIG_DEFAULT_ACTIVE_ALERTS; | |||||
strcpy_P(config.contactPhone, PSTR(CONFIG_DEFAULT_CONTACT_PHONE));*/ | |||||
} | |||||
void write() { | |||||
NOTICE_FORMAT("write", "%d, %s, %d, %d, %d, %d, %d, %B, %s", value.seed, value.version, value.firstEntry, value.lastEntry, value.alertBatteryLevel1, value.alertBatteryLevel2, value.alertBatteryLevelClear, value.activeAlerts, value.contactPhone); | |||||
#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 setup() { | |||||
details::read(); | |||||
//details::write(); | |||||
} | |||||
void save() { | |||||
details::write(); | |||||
} | |||||
void reset() { | |||||
VERBOSE("reset"); | |||||
config_t config = {}; | |||||
config.seed = CONFIG_SEED; | |||||
strcpy_P(config.version, PSTR(VERSION)); | |||||
config.firstEntry = config.lastEntry = 0xFFFF; | |||||
config.alertBatteryLevel1 = CONFIG_DEFAULT_BATTERY_ALERT_LEVEL1; | |||||
config.alertBatteryLevel2 = CONFIG_DEFAULT_BATTERY_ALERT_LEVEL2; | |||||
config.alertBatteryLevelClear = CONFIG_DEFAULT_BATTERY_ALERT_CLEAR; | |||||
config.activeAlerts = CONFIG_DEFAULT_ACTIVE_ALERTS; | |||||
strcpy_P(config.contactPhone, PSTR(CONFIG_DEFAULT_CONTACT_PHONE)); | |||||
#if BACKUP_ENABLE_NETWORK | |||||
config.network.saveThreshold = POSITIONS_CONFIG_NET_DEFAULT_SAVE_THRESHOLD; | |||||
config.network.lastSavedEntry = 0xFFFF; | |||||
strcpy_P(config.network.apn, PSTR(POSITIONS_CONFIG_NET_DEFAULT_APN)); | |||||
strcpy_P(config.network.url, PSTR(POSITIONS_CONFIG_NET_DEFAULT_URL)); | |||||
#endif | |||||
value = config; | |||||
save(); | |||||
} | |||||
} | |||||
} | } |
@@ -1,160 +1,161 @@ | |||||
#include "Core.h" | |||||
#include "Config.h" | |||||
#include "Flash.h" | |||||
#include "Alerts.h" | |||||
#define LOGGER_NAME "Core" | |||||
#define SMS_BUFFER_SIZE 140 | |||||
#define NO_ALERTS_NOTIFIED 0 | |||||
using namespace utils; | |||||
namespace core { | |||||
uint16_t sleepTime = SLEEP_DEFAULT_TIME_SECONDS; | |||||
uint8_t stoppedInARow = SLEEP_DEFAULT_STOPPED_THRESHOLD - 1; | |||||
TRACKER_MOVING_STATE movingState = TRACKER_MOVING_STATE::MOVING; | |||||
namespace details { | |||||
void appendToSmsBuffer(char * buffer, const char * fmt, ...) { | |||||
va_list args; | |||||
va_start(args, fmt); | |||||
size_t bufferLeft = SMS_BUFFER_SIZE - strlen(buffer); | |||||
char * p = buffer + strlen(buffer); | |||||
vsnprintf_P(p, bufferLeft, fmt, args); | |||||
va_end(args); | |||||
} | |||||
} | |||||
void main() { | |||||
bool acquired = false; | |||||
PositionEntryMetadata metadata; | |||||
if(movingState >= TRACKER_MOVING_STATE::STOPPED) positions::prepareBackup(); | |||||
acquired = positions::acquire(metadata); | |||||
if (acquired) { | |||||
positions::appendLast(metadata); | |||||
movingState = updateSleepTime(); | |||||
gps::preserveCurrentCoordinates(); | |||||
} | |||||
alerts::clear(metadata); | |||||
alerts::add(notifyFailures(metadata)); | |||||
if(movingState >= TRACKER_MOVING_STATE::STOPPED) { | |||||
positions::doBackup(movingState == TRACKER_MOVING_STATE::STOPPED); //do not force on STATIC | |||||
} | |||||
if (acquired) updateRtcTime(); | |||||
mainunit::deepSleep(sleepTime); | |||||
} | |||||
uint8_t notifyFailures(PositionEntryMetadata &metadata) { | |||||
SIM808RegistrationStatus networkStatus; | |||||
char buffer[SMS_BUFFER_SIZE]; | |||||
const __FlashStringHelper * backupFailureString = F(" Backup battery failure ?"); | |||||
bool notified = false; | |||||
uint8_t triggered = alerts::getTriggered(metadata); | |||||
if (!triggered) return NO_ALERTS_NOTIFIED; | |||||
NOTICE_FORMAT("notifyFailures", "triggered : %B", triggered); | |||||
network::powerOn(); | |||||
networkStatus = network::waitForRegistered(NETWORK_DEFAULT_TOTAL_TIMEOUT_MS); | |||||
if (network::isAvailable(networkStatus.stat)) { | |||||
strncpy_P(buffer, PSTR("Alerts !"), SMS_BUFFER_SIZE); | |||||
if (bitRead(triggered, ALERT_BATTERY_LEVEL_1) || bitRead(triggered, ALERT_BATTERY_LEVEL_2)) { | |||||
details::appendToSmsBuffer(buffer, PSTR("\n- Battery at %d%%."), metadata.batteryLevel); | |||||
} | |||||
if (bitRead(triggered, ALERT_RTC_TEMPERATURE_FAILURE)) { | |||||
details::appendToSmsBuffer(buffer, PSTR("\n- Temperature is %dC.%S"), metadata.temperature, backupFailureString); | |||||
} | |||||
if (bitRead(triggered, ALERT_RTC_CLOCK_FAILURE)) { | |||||
details::appendToSmsBuffer(buffer, PSTR("\n- RTC was stopped.%S"), backupFailureString); | |||||
} | |||||
#if ALERTS_ON_SERIAL_IF_AVAILABLE | |||||
if(Serial) { | |||||
NOTICE_FORMAT("notifyFailure", "%s", buffer); | |||||
notified = true; | |||||
} | |||||
else { | |||||
#endif | |||||
notified = network::sendSms(buffer); | |||||
#if ALERTS_ON_SERIAL_IF_AVAILABLE | |||||
} | |||||
#endif | |||||
if (!notified) NOTICE_MSG("notifyFailure", "SMS not sent !"); | |||||
} | |||||
network::powerOff(); | |||||
return notified ? triggered : NO_ALERTS_NOTIFIED; //If not notified, the alerts state should not be persisted (so we can retry to notify them) | |||||
} | |||||
void updateRtcTime() { | |||||
tmElements_t time; | |||||
gps::getTime(time); | |||||
rtc::setTime(time); | |||||
} | |||||
TRACKER_MOVING_STATE updateSleepTime() { | |||||
TRACKER_MOVING_STATE state = TRACKER_MOVING_STATE::MOVING; | |||||
uint8_t velocity = gps::getVelocity(); | |||||
sleepTime = mapSleepTime(velocity); | |||||
if (velocity < SLEEP_TIMING_MIN_MOVING_VELOCITY) { | |||||
float distance = gps::getDistanceFromPrevious(); //did we missed positions because we were sleeping ? | |||||
if (distance > GPS_DEFAULT_MISSED_POSITION_GAP_KM) stoppedInARow = 0; | |||||
else stoppedInARow = min(stoppedInARow + 1, SLEEP_DEFAULT_STOPPED_THRESHOLD + 1); //avoid overflow on REALLY long stops | |||||
if (stoppedInARow < SLEEP_DEFAULT_STOPPED_THRESHOLD) { | |||||
sleepTime = SLEEP_DEFAULT_PAUSING_TIME_SECONDS; | |||||
state = TRACKER_MOVING_STATE::PAUSED; | |||||
} | |||||
else if (stoppedInARow == SLEEP_DEFAULT_STOPPED_THRESHOLD) state = TRACKER_MOVING_STATE::STOPPED; | |||||
else state = TRACKER_MOVING_STATE::STATIC; | |||||
} | |||||
else stoppedInARow = 0; | |||||
NOTICE_FORMAT("updateSleepTime", "%dkmh => %d seconds", velocity, sleepTime); | |||||
return state; | |||||
} | |||||
uint16_t mapSleepTime(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 = 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; | |||||
result = timing.seconds; | |||||
break; | |||||
} | |||||
VERBOSE_FORMAT("mapSleepTime", "%d,%d", velocity, result); | |||||
return result; | |||||
} | |||||
#include "Core.h" | |||||
#include "Config.h" | |||||
#include "Flash.h" | |||||
#include "Alerts.h" | |||||
#include "Logging.h" | |||||
#define LOGGER_NAME "Core" | |||||
#define SMS_BUFFER_SIZE 140 | |||||
#define NO_ALERTS_NOTIFIED 0 | |||||
using namespace utils; | |||||
namespace core { | |||||
uint16_t sleepTime = SLEEP_DEFAULT_TIME_SECONDS; | |||||
uint8_t stoppedInARow = SLEEP_DEFAULT_STOPPED_THRESHOLD - 1; | |||||
TRACKER_MOVING_STATE movingState = TRACKER_MOVING_STATE::MOVING; | |||||
namespace details { | |||||
void appendToSmsBuffer(char * buffer, const char * fmt, ...) { | |||||
va_list args; | |||||
va_start(args, fmt); | |||||
size_t bufferLeft = SMS_BUFFER_SIZE - strlen(buffer); | |||||
char * p = buffer + strlen(buffer); | |||||
vsnprintf_P(p, bufferLeft, fmt, args); | |||||
va_end(args); | |||||
} | |||||
} | |||||
void main() { | |||||
bool acquired = false; | |||||
PositionEntryMetadata metadata; | |||||
if(movingState >= TRACKER_MOVING_STATE::STOPPED) positions::prepareBackup(); | |||||
acquired = positions::acquire(metadata); | |||||
if (acquired) { | |||||
positions::appendLast(metadata); | |||||
movingState = updateSleepTime(); | |||||
gps::preserveCurrentCoordinates(); | |||||
} | |||||
alerts::clear(metadata); | |||||
alerts::add(notifyFailures(metadata)); | |||||
if(movingState >= TRACKER_MOVING_STATE::STOPPED) { | |||||
positions::doBackup(movingState == TRACKER_MOVING_STATE::STOPPED); //do not force on STATIC | |||||
} | |||||
if (acquired) updateRtcTime(); | |||||
mainunit::deepSleep(sleepTime); | |||||
} | |||||
uint8_t notifyFailures(PositionEntryMetadata &metadata) { | |||||
SIM808RegistrationStatus networkStatus; | |||||
char buffer[SMS_BUFFER_SIZE]; | |||||
const __FlashStringHelper * backupFailureString = F(" Backup battery failure ?"); | |||||
bool notified = false; | |||||
uint8_t triggered = alerts::getTriggered(metadata); | |||||
if (!triggered) return NO_ALERTS_NOTIFIED; | |||||
NOTICE_FORMAT("notifyFailures", "triggered : %B", triggered); | |||||
network::powerOn(); | |||||
networkStatus = network::waitForRegistered(NETWORK_DEFAULT_TOTAL_TIMEOUT_MS); | |||||
if (network::isAvailable(networkStatus.stat)) { | |||||
strncpy_P(buffer, PSTR("Alerts !"), SMS_BUFFER_SIZE); | |||||
if (bitRead(triggered, ALERT_BATTERY_LEVEL_1) || bitRead(triggered, ALERT_BATTERY_LEVEL_2)) { | |||||
details::appendToSmsBuffer(buffer, PSTR("\n- Battery at %d%%."), metadata.batteryLevel); | |||||
} | |||||
if (bitRead(triggered, ALERT_RTC_TEMPERATURE_FAILURE)) { | |||||
details::appendToSmsBuffer(buffer, PSTR("\n- Temperature is %dC.%S"), metadata.temperature, backupFailureString); | |||||
} | |||||
if (bitRead(triggered, ALERT_RTC_CLOCK_FAILURE)) { | |||||
details::appendToSmsBuffer(buffer, PSTR("\n- RTC was stopped.%S"), backupFailureString); | |||||
} | |||||
#if ALERTS_ON_SERIAL_IF_AVAILABLE | |||||
if(Serial) { | |||||
NOTICE_FORMAT("notifyFailure", "%s", buffer); | |||||
notified = true; | |||||
} | |||||
else { | |||||
#endif | |||||
notified = network::sendSms(buffer); | |||||
#if ALERTS_ON_SERIAL_IF_AVAILABLE | |||||
} | |||||
#endif | |||||
if (!notified) NOTICE_MSG("notifyFailure", "SMS not sent !"); | |||||
} | |||||
network::powerOff(); | |||||
return notified ? triggered : NO_ALERTS_NOTIFIED; //If not notified, the alerts state should not be persisted (so we can retry to notify them) | |||||
} | |||||
void updateRtcTime() { | |||||
tmElements_t time; | |||||
gps::getTime(time); | |||||
rtc::setTime(time); | |||||
} | |||||
TRACKER_MOVING_STATE updateSleepTime() { | |||||
TRACKER_MOVING_STATE state = TRACKER_MOVING_STATE::MOVING; | |||||
uint8_t velocity = gps::getVelocity(); | |||||
sleepTime = mapSleepTime(velocity); | |||||
if (velocity < SLEEP_TIMING_MIN_MOVING_VELOCITY) { | |||||
float distance = gps::getDistanceFromPrevious(); //did we missed positions because we were sleeping ? | |||||
if (distance > GPS_DEFAULT_MISSED_POSITION_GAP_KM) stoppedInARow = 0; | |||||
else stoppedInARow = min(stoppedInARow + 1, SLEEP_DEFAULT_STOPPED_THRESHOLD + 1); //avoid overflow on REALLY long stops | |||||
if (stoppedInARow < SLEEP_DEFAULT_STOPPED_THRESHOLD) { | |||||
sleepTime = SLEEP_DEFAULT_PAUSING_TIME_SECONDS; | |||||
state = TRACKER_MOVING_STATE::PAUSED; | |||||
} | |||||
else if (stoppedInARow == SLEEP_DEFAULT_STOPPED_THRESHOLD) state = TRACKER_MOVING_STATE::STOPPED; | |||||
else state = TRACKER_MOVING_STATE::STATIC; | |||||
} | |||||
else stoppedInARow = 0; | |||||
NOTICE_FORMAT("updateSleepTime", "%dkmh => %d seconds", velocity, sleepTime); | |||||
return state; | |||||
} | |||||
uint16_t mapSleepTime(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 = 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; | |||||
result = timing.seconds; | |||||
break; | |||||
} | |||||
VERBOSE_FORMAT("mapSleepTime", "%d,%d", velocity, result); | |||||
return result; | |||||
} | |||||
} | } |
@@ -2,12 +2,10 @@ | |||||
#include <Arduino.h> | #include <Arduino.h> | ||||
#include "Debug.h" | |||||
#include "Gps.h" | #include "Gps.h" | ||||
#include "MainUnit.h" | #include "MainUnit.h" | ||||
#include "Network.h" | #include "Network.h" | ||||
#include "Rtc.h" | #include "Rtc.h" | ||||
#include "config/Pins.h" | |||||
#include "Positions.h" | #include "Positions.h" | ||||
enum class TRACKER_MOVING_STATE : uint8_t { | enum class TRACKER_MOVING_STATE : uint8_t { | ||||
@@ -1,300 +1,291 @@ | |||||
#include "Debug.h" | |||||
#include "Flash.h" | |||||
#include "Positions.h" | |||||
#include "Core.h" | |||||
#include "Alerts.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,2.70,172.6,1,,1.3,2.2,1.8,,11,7,,,37,,"; | |||||
MENU_ENTRY(HEADER, "========================\n-- Menu --"); | |||||
MENU_ENTRY(SEPARATOR, "----"); | |||||
MENU_ENTRY(RUN, "[R] Run"); | |||||
MENU_ENTRY(RUN_ONCE, "[r] Run once"); | |||||
MENU_ENTRY(RAM, "[f] Free RAM"); | |||||
MENU_ENTRY(READ_BATTERY, "[b] Read battery"); | |||||
MENU_ENTRY(GPS_ON, "[G] GPS On"); | |||||
MENU_ENTRY(GPS_OFF, "[g] GPS Off"); | |||||
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(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(NOTIFY_FAILURES, "[F] Notify failures"); | |||||
MENU_ENTRY(CLEAR_ALERTS, "[A] Clear alerts"); | |||||
MENU_ENTRY(SLEEP_DEEP, "[s] Deep sleep for 10s"); | |||||
MENU_ENTRY(QUESTION, "?"); | |||||
const uint8_t commandIdMapping[] PROGMEM = { | |||||
'R', static_cast<uint8_t>(debug::GPSTRACKER_DEBUG_COMMAND::RUN), | |||||
'r', static_cast<uint8_t>(debug::GPSTRACKER_DEBUG_COMMAND::ONCE), | |||||
'f', static_cast<uint8_t>(debug::GPSTRACKER_DEBUG_COMMAND::RAM), | |||||
'b', static_cast<uint8_t>(debug::GPSTRACKER_DEBUG_COMMAND::BATTERY), | |||||
'G', static_cast<uint8_t>(debug::GPSTRACKER_DEBUG_COMMAND::GPS_ON), | |||||
'g', static_cast<uint8_t>(debug::GPSTRACKER_DEBUG_COMMAND::GPS_OFF), | |||||
'L', static_cast<uint8_t>(debug::GPSTRACKER_DEBUG_COMMAND::GPS_GET), | |||||
'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_SET), | |||||
'C', static_cast<uint8_t>(debug::GPSTRACKER_DEBUG_COMMAND::EEPROM_GET_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), | |||||
'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), | |||||
'a', static_cast<uint8_t>(debug::GPSTRACKER_DEBUG_COMMAND::EEPROM_ADD_ENTRY), | |||||
'B', static_cast<uint8_t>(debug::GPSTRACKER_DEBUG_COMMAND::EEPROM_BACKUP_ENTRIES), | |||||
'F', static_cast<uint8_t>(debug::GPSTRACKER_DEBUG_COMMAND::NOTIFY_FAILURES), | |||||
'A', static_cast<uint8_t>(debug::GPSTRACKER_DEBUG_COMMAND::CLEAR_ALERTS), | |||||
's', static_cast<uint8_t>(debug::GPSTRACKER_DEBUG_COMMAND::SLEEP_DEEP), | |||||
}; | |||||
const char * const MENU_ENTRIES[] PROGMEM = { | |||||
MENU_HEADER, | |||||
MENU_RUN, | |||||
MENU_RUN_ONCE, | |||||
MENU_SEPARATOR, | |||||
MENU_RAM, | |||||
MENU_READ_BATTERY, | |||||
MENU_SEPARATOR, | |||||
MENU_GPS_ON, | |||||
MENU_GPS_OFF, | |||||
MENU_GPS_GET, | |||||
MENU_GPS_SET, | |||||
MENU_SEPARATOR, | |||||
MENU_RTC_SET, | |||||
MENU_RTC_GET, | |||||
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_NOTIFY_FAILURES, | |||||
MENU_CLEAR_ALERTS, | |||||
MENU_SEPARATOR, | |||||
MENU_SLEEP_DEEP, | |||||
MENU_QUESTION | |||||
}; | |||||
static const PositionEntryMetadata fakeMetadata PROGMEM = { | |||||
100, | |||||
3800, | |||||
10, | |||||
20, | |||||
SIM808_GPS_STATUS::ACCURATE_FIX | |||||
}; | |||||
int freeRam2() { // dirty hack because putting it in namespace doesn't compile | |||||
extern int __heap_start, *__brkval; | |||||
int v; | |||||
return (int)&v - (__brkval == 0 ? (int)&__heap_start : (int)__brkval); | |||||
} | |||||
using namespace utils; | |||||
namespace debug { | |||||
namespace details { | |||||
inline void displayPosition(PositionEntry entry) { | |||||
Log.notice(F("%d,%d,%d,%d,%d,%s"), | |||||
entry.metadata.batteryLevel, | |||||
entry.metadata.batteryVoltage, | |||||
entry.metadata.temperature, | |||||
static_cast<uint8_t>(entry.metadata.status), | |||||
entry.metadata.timeToFix, | |||||
entry.position); | |||||
} | |||||
} | |||||
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; | |||||
for (uint8_t i = 0; i < mappingArraySize; i += 2) { | |||||
commandId = pgm_read_byte_near(commandIdMapping + i); | |||||
if (commandId == id) return static_cast<GPSTRACKER_DEBUG_COMMAND>(pgm_read_byte_near(commandIdMapping + i + 1)); | |||||
} | |||||
return GPSTRACKER_DEBUG_COMMAND::NONE; | |||||
} | |||||
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<const __FlashStringHelper *>(pgm_read_word_near(&MENU_ENTRIES[i]))); | |||||
} | |||||
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(); //flushing input | |||||
} while (command == GPSTRACKER_DEBUG_COMMAND::NONE); | |||||
return command; | |||||
} | |||||
void getAndDisplayGpsPosition() { | |||||
SIM808_GPS_STATUS gpsStatus = gps::acquireCurrentPosition(GPS_DEFAULT_TOTAL_TIMEOUT_MS); | |||||
NOTICE_FORMAT("getAndDisplayGpsPosition", "%d %s", gpsStatus, gps::lastPosition); | |||||
} | |||||
void setFakeGpsPosition() { | |||||
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::mapSleepTime(gps::getVelocity())); | |||||
} | |||||
void getAndDisplayBattery() { | |||||
hardware::sim808::powerOn(); | |||||
SIM808ChargingStatus status = hardware::sim808::device.getChargingState(); | |||||
hardware::sim808::powerOff(); | |||||
NOTICE_FORMAT("getAndDisplayBattery", "%d %d%% %dmV", status.state, status.level, status.voltage); | |||||
} | |||||
void getAndDisplayRtcTime() { | |||||
tmElements_t time; | |||||
rtc::getTime(time); | |||||
NOTICE_FORMAT("getAndDisplayRtcTime", "%d/%d/%d %d:%d:%d %t %d", tmYearToCalendar(time.Year), time.Month, time.Day, time.Hour, time.Minute, time.Second, rtc::isAccurate(), rtc::getTemperature()); | |||||
} | |||||
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::mapSleepTime(i); | |||||
} | |||||
NOTICE_MSG("getAndDisplaySleepTimes", "Done"); | |||||
} | |||||
void getAndDisplayEepromConfig() { | |||||
config::main::setup(); //forcing read again | |||||
} | |||||
void getAndDisplayEepromContent() { | |||||
char buffer[128]; | |||||
hardware::i2c::powerOn(); | |||||
for (int i = 0; i < 8; i++) { | |||||
hardware::i2c::eeprom.read(128 * i, buffer, 128); | |||||
for (int i = 0; i < 128; i++) { | |||||
Serial.print(buffer[i], HEX); | |||||
} | |||||
} | |||||
Serial.println(); | |||||
hardware::i2c::powerOff(); | |||||
NOTICE_MSG("getAndDisplayEepromContent", "Done"); | |||||
} | |||||
void getAndDisplayEepromPositions() { | |||||
uint16_t currentEntryIndex = config::main::value.firstEntry; | |||||
PositionEntry currentEntry; | |||||
hardware::i2c::powerOn(); | |||||
do { | |||||
if (!positions::get(currentEntryIndex, currentEntry)) break; | |||||
details::displayPosition(currentEntry); | |||||
} while (positions::moveNext(currentEntryIndex)); | |||||
hardware::i2c::powerOff(); | |||||
} | |||||
void getAndDisplayEepromLastPosition() { | |||||
uint16_t lastEntryIndex = config::main::value.lastEntry; | |||||
PositionEntry lastEntry; | |||||
if(positions::get(lastEntryIndex, lastEntry)) details::displayPosition(lastEntry); | |||||
else Log.notice(F("No position recorded\n")); | |||||
} | |||||
void addLastPositionToEeprom() { | |||||
hardware::sim808::powerOn(); | |||||
SIM808ChargingStatus status = hardware::sim808::device.getChargingState(); | |||||
hardware::sim808::powerOff(); | |||||
PositionEntryMetadata metadata = { | |||||
status.level, | |||||
status.voltage, | |||||
rtc::getTemperature(), | |||||
0, | |||||
SIM808_GPS_STATUS::OFF | |||||
}; | |||||
for(int i = 0; i < 10; i++) positions::appendLast(metadata); | |||||
} | |||||
void notifyFailures() { | |||||
PositionEntryMetadata metadata = {}; | |||||
flash::read(&fakeMetadata, metadata); | |||||
metadata.batteryLevel = 1; | |||||
metadata.temperature = ALERT_SUSPICIOUS_RTC_TEMPERATURE; | |||||
uint8_t alerts = core::notifyFailures(metadata); | |||||
NOTICE_FORMAT("notifyFailures", "result : %B", alerts); | |||||
alerts::add(alerts); | |||||
} | |||||
void clearAlerts() { | |||||
PositionEntryMetadata metadata = {}; | |||||
flash::read(&fakeMetadata, metadata); | |||||
alerts::clear(metadata); | |||||
} | |||||
#include "Debug.h" | |||||
#include "MainUnit.h" | |||||
#include "Flash.h" | |||||
#include "Positions.h" | |||||
#include "Core.h" | |||||
#include "Alerts.h" | |||||
#include "Logging.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,2.70,172.6,1,,1.3,2.2,1.8,,11,7,,,37,,"; | |||||
MENU_ENTRY(HEADER, "========================\n-- Menu --"); | |||||
MENU_ENTRY(SEPARATOR, "----"); | |||||
MENU_ENTRY(RUN, "[R] Run"); | |||||
MENU_ENTRY(RUN_ONCE, "[r] Run once"); | |||||
MENU_ENTRY(RAM, "[f] Free RAM"); | |||||
MENU_ENTRY(READ_BATTERY, "[b] Read battery"); | |||||
MENU_ENTRY(GPS_ON, "[G] GPS On"); | |||||
MENU_ENTRY(GPS_OFF, "[g] GPS Off"); | |||||
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(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(NOTIFY_FAILURES, "[F] Notify failures"); | |||||
MENU_ENTRY(CLEAR_ALERTS, "[A] Clear alerts"); | |||||
MENU_ENTRY(SLEEP_DEEP, "[s] Deep sleep for 10s"); | |||||
MENU_ENTRY(QUESTION, "?"); | |||||
const uint8_t commandIdMapping[] PROGMEM = { | |||||
'R', static_cast<uint8_t>(debug::GPSTRACKER_DEBUG_COMMAND::RUN), | |||||
'r', static_cast<uint8_t>(debug::GPSTRACKER_DEBUG_COMMAND::ONCE), | |||||
'f', static_cast<uint8_t>(debug::GPSTRACKER_DEBUG_COMMAND::RAM), | |||||
'b', static_cast<uint8_t>(debug::GPSTRACKER_DEBUG_COMMAND::BATTERY), | |||||
'G', static_cast<uint8_t>(debug::GPSTRACKER_DEBUG_COMMAND::GPS_ON), | |||||
'g', static_cast<uint8_t>(debug::GPSTRACKER_DEBUG_COMMAND::GPS_OFF), | |||||
'L', static_cast<uint8_t>(debug::GPSTRACKER_DEBUG_COMMAND::GPS_GET), | |||||
'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_SET), | |||||
'C', static_cast<uint8_t>(debug::GPSTRACKER_DEBUG_COMMAND::EEPROM_GET_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), | |||||
'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), | |||||
'a', static_cast<uint8_t>(debug::GPSTRACKER_DEBUG_COMMAND::EEPROM_ADD_ENTRY), | |||||
'B', static_cast<uint8_t>(debug::GPSTRACKER_DEBUG_COMMAND::EEPROM_BACKUP_ENTRIES), | |||||
'F', static_cast<uint8_t>(debug::GPSTRACKER_DEBUG_COMMAND::NOTIFY_FAILURES), | |||||
'A', static_cast<uint8_t>(debug::GPSTRACKER_DEBUG_COMMAND::CLEAR_ALERTS), | |||||
's', static_cast<uint8_t>(debug::GPSTRACKER_DEBUG_COMMAND::SLEEP_DEEP), | |||||
}; | |||||
const char * const MENU_ENTRIES[] PROGMEM = { | |||||
MENU_HEADER, | |||||
MENU_RUN, | |||||
MENU_RUN_ONCE, | |||||
MENU_SEPARATOR, | |||||
MENU_RAM, | |||||
MENU_READ_BATTERY, | |||||
MENU_SEPARATOR, | |||||
MENU_GPS_ON, | |||||
MENU_GPS_OFF, | |||||
MENU_GPS_GET, | |||||
MENU_GPS_SET, | |||||
MENU_SEPARATOR, | |||||
MENU_RTC_SET, | |||||
MENU_RTC_GET, | |||||
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_NOTIFY_FAILURES, | |||||
MENU_CLEAR_ALERTS, | |||||
MENU_SEPARATOR, | |||||
MENU_SLEEP_DEEP, | |||||
MENU_QUESTION | |||||
}; | |||||
static const PositionEntryMetadata fakeMetadata PROGMEM = { | |||||
100, | |||||
3800, | |||||
10, | |||||
20, | |||||
SIM808_GPS_STATUS::ACCURATE_FIX | |||||
}; | |||||
using namespace utils; | |||||
namespace debug { | |||||
namespace details { | |||||
inline void displayPosition(PositionEntry entry) { | |||||
Log.notice(F("%d,%d,%d,%d,%d,%s"), | |||||
entry.metadata.batteryLevel, | |||||
entry.metadata.batteryVoltage, | |||||
entry.metadata.temperature, | |||||
static_cast<uint8_t>(entry.metadata.status), | |||||
entry.metadata.timeToFix, | |||||
entry.position); | |||||
} | |||||
} | |||||
void displayFreeRam() { | |||||
Log.notice(F("RAM: %d\n"), mainunit::freeRam()); | |||||
} | |||||
GPSTRACKER_DEBUG_COMMAND parseCommand(char id) { | |||||
size_t mappingArraySize = flash::getArraySize(commandIdMapping); | |||||
char commandId; | |||||
for (uint8_t i = 0; i < mappingArraySize; i += 2) { | |||||
commandId = pgm_read_byte_near(commandIdMapping + i); | |||||
if (commandId == id) return static_cast<GPSTRACKER_DEBUG_COMMAND>(pgm_read_byte_near(commandIdMapping + i + 1)); | |||||
} | |||||
return GPSTRACKER_DEBUG_COMMAND::NONE; | |||||
} | |||||
GPSTRACKER_DEBUG_COMMAND menu(uint16_t timeout) { | |||||
GPSTRACKER_DEBUG_COMMAND command; | |||||
size_t menuSize = flash::getArraySize(MENU_ENTRIES); | |||||
do { | |||||
for (uint8_t i = 0; i < menuSize; i++) { | |||||
Serial.println(reinterpret_cast<const __FlashStringHelper *>(pgm_read_word_near(&MENU_ENTRIES[i]))); | |||||
} | |||||
while (!Serial.available()) { | |||||
if (timeout > 0) { | |||||
delay(MENU_INTERMEDIATE_TIMEOUT); | |||||
timeout -= MENU_INTERMEDIATE_TIMEOUT; | |||||
if (timeout <= 0) { | |||||
NOTICE_MSG("menu", "Timeout expired."); | |||||
return GPSTRACKER_DEBUG_COMMAND::RUN; | |||||
} | |||||
} | |||||
} | |||||
command = parseCommand(Serial.read()); | |||||
while (Serial.available()) Serial.read(); //flushing input | |||||
} while (command == GPSTRACKER_DEBUG_COMMAND::NONE); | |||||
return command; | |||||
} | |||||
void getAndDisplayGpsPosition() { | |||||
SIM808_GPS_STATUS gpsStatus = gps::acquireCurrentPosition(GPS_DEFAULT_TOTAL_TIMEOUT_MS); | |||||
NOTICE_FORMAT("getAndDisplayGpsPosition", "%d %s", gpsStatus, gps::lastPosition); | |||||
} | |||||
void setFakeGpsPosition() { | |||||
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::mapSleepTime(gps::getVelocity())); | |||||
} | |||||
void getAndDisplayBattery() { | |||||
hardware::sim808::powerOn(); | |||||
SIM808ChargingStatus status = hardware::sim808::device.getChargingState(); | |||||
hardware::sim808::powerOff(); | |||||
NOTICE_FORMAT("getAndDisplayBattery", "%d %d%% %dmV", status.state, status.level, status.voltage); | |||||
} | |||||
void getAndDisplayRtcTime() { | |||||
tmElements_t time; | |||||
rtc::getTime(time); | |||||
NOTICE_FORMAT("getAndDisplayRtcTime", "%d/%d/%d %d:%d:%d %t %d", tmYearToCalendar(time.Year), time.Month, time.Day, time.Hour, time.Minute, time.Second, rtc::isAccurate(), rtc::getTemperature()); | |||||
} | |||||
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::mapSleepTime(i); | |||||
} | |||||
NOTICE_MSG("getAndDisplaySleepTimes", "Done"); | |||||
} | |||||
void getAndDisplayEepromConfig() { | |||||
config::main::setup(); //forcing read again | |||||
} | |||||
void getAndDisplayEepromContent() { | |||||
char buffer[128]; | |||||
hardware::i2c::powerOn(); | |||||
for (int i = 0; i < 8; i++) { | |||||
hardware::i2c::eeprom.read(128 * i, buffer, 128); | |||||
for (int i = 0; i < 128; i++) { | |||||
Serial.print(buffer[i], HEX); | |||||
} | |||||
} | |||||
Serial.println(); | |||||
hardware::i2c::powerOff(); | |||||
NOTICE_MSG("getAndDisplayEepromContent", "Done"); | |||||
} | |||||
void getAndDisplayEepromPositions() { | |||||
uint16_t currentEntryIndex = config::main::value.firstEntry; | |||||
PositionEntry currentEntry; | |||||
hardware::i2c::powerOn(); | |||||
do { | |||||
if (!positions::get(currentEntryIndex, currentEntry)) break; | |||||
details::displayPosition(currentEntry); | |||||
} while (positions::moveNext(currentEntryIndex)); | |||||
hardware::i2c::powerOff(); | |||||
} | |||||
void getAndDisplayEepromLastPosition() { | |||||
uint16_t lastEntryIndex = config::main::value.lastEntry; | |||||
PositionEntry lastEntry; | |||||
if(positions::get(lastEntryIndex, lastEntry)) details::displayPosition(lastEntry); | |||||
else Log.notice(F("No position recorded\n")); | |||||
} | |||||
void addLastPositionToEeprom() { | |||||
hardware::sim808::powerOn(); | |||||
SIM808ChargingStatus status = hardware::sim808::device.getChargingState(); | |||||
hardware::sim808::powerOff(); | |||||
PositionEntryMetadata metadata = { | |||||
status.level, | |||||
status.voltage, | |||||
rtc::getTemperature(), | |||||
0, | |||||
SIM808_GPS_STATUS::OFF | |||||
}; | |||||
for(int i = 0; i < 10; i++) positions::appendLast(metadata); | |||||
} | |||||
void notifyFailures() { | |||||
PositionEntryMetadata metadata = {}; | |||||
flash::read(&fakeMetadata, metadata); | |||||
metadata.batteryLevel = 1; | |||||
metadata.temperature = ALERT_SUSPICIOUS_RTC_TEMPERATURE; | |||||
uint8_t alerts = core::notifyFailures(metadata); | |||||
NOTICE_FORMAT("notifyFailures", "result : %B", alerts); | |||||
alerts::add(alerts); | |||||
} | |||||
void clearAlerts() { | |||||
PositionEntryMetadata metadata = {}; | |||||
flash::read(&fakeMetadata, metadata); | |||||
alerts::clear(metadata); | |||||
} | |||||
} | } |
@@ -1,61 +1,53 @@ | |||||
#pragma once | |||||
#include <Arduino.h> | |||||
#include "Config.h" | |||||
#include "Logging.h" | |||||
#include "Core.h" | |||||
#include "Hardware.h" | |||||
#include "Gps.h" | |||||
#include "Rtc.h" | |||||
namespace debug { | |||||
enum class GPSTRACKER_DEBUG_COMMAND : uint8_t { | |||||
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, | |||||
NOTIFY_FAILURES, | |||||
CLEAR_ALERTS, | |||||
SLEEP_DEEP | |||||
}; | |||||
int freeRam(); | |||||
void displayFreeRam(); | |||||
GPSTRACKER_DEBUG_COMMAND menu(uint16_t timeout); | |||||
void getAndDisplayBattery(); | |||||
void getAndDisplayGpsPosition(); | |||||
void setFakeGpsPosition(); | |||||
void getAndDisplayRtcTime(); | |||||
void setRtcTime(); | |||||
void getAndDisplaySleepTimes(); | |||||
void getAndDisplayEepromConfig(); | |||||
void getAndDisplayEepromContent(); | |||||
void getAndDisplayEepromPositions(); | |||||
void getAndDisplayEepromLastPosition(); | |||||
void addLastPositionToEeprom(); | |||||
void notifyFailures(); | |||||
void clearAlerts(); | |||||
} | |||||
#pragma once | |||||
#include <Arduino.h> | |||||
namespace debug { | |||||
enum class GPSTRACKER_DEBUG_COMMAND : uint8_t { | |||||
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, | |||||
NOTIFY_FAILURES, | |||||
CLEAR_ALERTS, | |||||
SLEEP_DEEP | |||||
}; | |||||
int freeRam(); | |||||
void displayFreeRam(); | |||||
GPSTRACKER_DEBUG_COMMAND menu(uint16_t timeout); | |||||
void getAndDisplayBattery(); | |||||
void getAndDisplayGpsPosition(); | |||||
void setFakeGpsPosition(); | |||||
void getAndDisplayRtcTime(); | |||||
void setRtcTime(); | |||||
void getAndDisplaySleepTimes(); | |||||
void getAndDisplayEepromConfig(); | |||||
void getAndDisplayEepromContent(); | |||||
void getAndDisplayEepromPositions(); | |||||
void getAndDisplayEepromLastPosition(); | |||||
void addLastPositionToEeprom(); | |||||
void notifyFailures(); | |||||
void clearAlerts(); | |||||
} |
@@ -1,118 +1,118 @@ | |||||
#include "Gps.h" | |||||
#include "Config.h" | |||||
#include "Debug.h" | |||||
#include "Hardware.h" | |||||
#include "MainUnit.h" | |||||
#include "math.h" | |||||
#define LOGGER_NAME "Gps" | |||||
#define TIME_YEAR_OFFSET 0 | |||||
#define TIME_MONTH_OFFSET 4 | |||||
#define TIME_DAY_OFFSET 6 | |||||
#define TIME_HOUR_OFFSET 8 | |||||
#define TIME_MINUTE_OFFSET 10 | |||||
#define TIME_SECOND_OFFSET 12 | |||||
#define EARTH_RADIUS 6371 //kilometers | |||||
namespace gps { | |||||
namespace details { | |||||
uint8_t parseSubstring(char *buffer, char *start, uint8_t size) { | |||||
strlcpy(buffer, start, size + 1); | |||||
return static_cast<uint8_t>(strtoul(buffer, NULL, 10)); | |||||
} | |||||
} | |||||
char lastPosition[GPS_POSITION_SIZE]; | |||||
SIM808_GPS_STATUS lastStatus; | |||||
float previousLat = 0; | |||||
float previousLng = 0; | |||||
SIM808_GPS_STATUS acquireCurrentPosition(int32_t timeout) { | |||||
SIM808_GPS_STATUS currentStatus = SIM808_GPS_STATUS::OFF; | |||||
do { | |||||
currentStatus = hardware::sim808::device.getGpsStatus(); | |||||
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); | |||||
if (currentStatus > SIM808_GPS_STATUS::NO_FIX) { | |||||
lastStatus = currentStatus; | |||||
hardware::sim808::device.getGpsPosition(lastPosition); | |||||
} | |||||
NOTICE_FORMAT("acquireCurrentPosition", "%d", currentStatus); | |||||
return currentStatus; | |||||
} | |||||
void preserveCurrentCoordinates() { | |||||
float lat, lng; | |||||
if(!hardware::sim808::device.getGpsField(lastPosition, SIM808_GPS_FIELD::LATITUDE, &lat)) lat = 0; | |||||
if(!hardware::sim808::device.getGpsField(lastPosition, SIM808_GPS_FIELD::LONGITUDE, &lng)) lng = 0; | |||||
if (lat == 0 || lng == 0) return; | |||||
previousLat = lat; | |||||
previousLng = lng; | |||||
} | |||||
float getDistanceFromPrevious() { | |||||
float lat1, lng1, lat2, lng2; | |||||
if(!hardware::sim808::device.getGpsField(lastPosition, SIM808_GPS_FIELD::LATITUDE, &lat2)) return 0; | |||||
if(!hardware::sim808::device.getGpsField(lastPosition, SIM808_GPS_FIELD::LONGITUDE, &lng2)) return 0; | |||||
VERBOSE_FORMAT("distanceFromPrevious", "%s, %f, %f, %f, %f", lastPosition, previousLat, previousLng, lat2, lng2); | |||||
lat1 = radians(previousLat); | |||||
lng1 = radians(previousLng); | |||||
lat2 = radians(lat2); | |||||
lng2 = radians(lng2); | |||||
float dlat = lat2 - lat1; | |||||
float dlng = lng2 - lng1; | |||||
float a = ( | |||||
pow(sin(dlat / 2), 2) + | |||||
cos(lat1) * cos(lat2) * pow(sin(dlng / 2), 2) | |||||
); | |||||
a = EARTH_RADIUS * (2 * atan2(sqrt(a), sqrt(1 - a))); //kilometers | |||||
NOTICE_FORMAT("distanceFromPrevious", "%fkm", a); | |||||
return a; | |||||
} | |||||
uint8_t getVelocity() { | |||||
uint8_t velocity; | |||||
if (!hardware::sim808::device.getGpsField(lastPosition, SIM808_GPS_FIELD::SPEED, &velocity)) velocity = 0; | |||||
VERBOSE_FORMAT("getVelocity", "%d", velocity); | |||||
return velocity; | |||||
} | |||||
void getTime(tmElements_t &time) { | |||||
char *timeStr; | |||||
char buffer[5]; | |||||
hardware::sim808::device.getGpsField(lastPosition, SIM808_GPS_FIELD::UTC, &timeStr); | |||||
VERBOSE_FORMAT("getTime", "%s", timeStr); | |||||
time.Year = CalendarYrToTm(details::parseSubstring(buffer, timeStr + TIME_YEAR_OFFSET, 4)); | |||||
time.Month = details::parseSubstring(buffer, timeStr + TIME_MONTH_OFFSET, 2); | |||||
time.Day = details::parseSubstring(buffer, timeStr + TIME_DAY_OFFSET, 2); | |||||
time.Hour = details::parseSubstring(buffer, timeStr + TIME_HOUR_OFFSET, 2); | |||||
time.Minute = details::parseSubstring(buffer, timeStr + TIME_MINUTE_OFFSET, 2); | |||||
time.Second = details::parseSubstring(buffer, timeStr + TIME_SECOND_OFFSET, 2); | |||||
NOTICE_FORMAT("getTime", "%d/%d/%d %d:%d:%d", tmYearToCalendar(time.Year), time.Month, time.Day, time.Hour, time.Minute, time.Second); | |||||
} | |||||
#include "Gps.h" | |||||
#include "Config.h" | |||||
#include "Hardware.h" | |||||
#include "MainUnit.h" | |||||
#include "Logging.h" | |||||
#include "math.h" | |||||
#define LOGGER_NAME "Gps" | |||||
#define TIME_YEAR_OFFSET 0 | |||||
#define TIME_MONTH_OFFSET 4 | |||||
#define TIME_DAY_OFFSET 6 | |||||
#define TIME_HOUR_OFFSET 8 | |||||
#define TIME_MINUTE_OFFSET 10 | |||||
#define TIME_SECOND_OFFSET 12 | |||||
#define EARTH_RADIUS 6371 //kilometers | |||||
namespace gps { | |||||
namespace details { | |||||
uint8_t parseSubstring(char *buffer, char *start, uint8_t size) { | |||||
strlcpy(buffer, start, size + 1); | |||||
return static_cast<uint8_t>(strtoul(buffer, NULL, 10)); | |||||
} | |||||
} | |||||
char lastPosition[GPS_POSITION_SIZE]; | |||||
SIM808_GPS_STATUS lastStatus; | |||||
float previousLat = 0; | |||||
float previousLng = 0; | |||||
SIM808_GPS_STATUS acquireCurrentPosition(int32_t timeout) { | |||||
SIM808_GPS_STATUS currentStatus = SIM808_GPS_STATUS::OFF; | |||||
do { | |||||
currentStatus = hardware::sim808::device.getGpsStatus(); | |||||
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); | |||||
if (currentStatus > SIM808_GPS_STATUS::NO_FIX) { | |||||
lastStatus = currentStatus; | |||||
hardware::sim808::device.getGpsPosition(lastPosition); | |||||
} | |||||
NOTICE_FORMAT("acquireCurrentPosition", "%d", currentStatus); | |||||
return currentStatus; | |||||
} | |||||
void preserveCurrentCoordinates() { | |||||
float lat, lng; | |||||
if(!hardware::sim808::device.getGpsField(lastPosition, SIM808_GPS_FIELD::LATITUDE, &lat)) lat = 0; | |||||
if(!hardware::sim808::device.getGpsField(lastPosition, SIM808_GPS_FIELD::LONGITUDE, &lng)) lng = 0; | |||||
if (lat == 0 || lng == 0) return; | |||||
previousLat = lat; | |||||
previousLng = lng; | |||||
} | |||||
float getDistanceFromPrevious() { | |||||
float lat1, lng1, lat2, lng2; | |||||
if(!hardware::sim808::device.getGpsField(lastPosition, SIM808_GPS_FIELD::LATITUDE, &lat2)) return 0; | |||||
if(!hardware::sim808::device.getGpsField(lastPosition, SIM808_GPS_FIELD::LONGITUDE, &lng2)) return 0; | |||||
VERBOSE_FORMAT("distanceFromPrevious", "%s, %f, %f, %f, %f", lastPosition, previousLat, previousLng, lat2, lng2); | |||||
lat1 = radians(previousLat); | |||||
lng1 = radians(previousLng); | |||||
lat2 = radians(lat2); | |||||
lng2 = radians(lng2); | |||||
float dlat = lat2 - lat1; | |||||
float dlng = lng2 - lng1; | |||||
float a = ( | |||||
pow(sin(dlat / 2), 2) + | |||||
cos(lat1) * cos(lat2) * pow(sin(dlng / 2), 2) | |||||
); | |||||
a = EARTH_RADIUS * (2 * atan2(sqrt(a), sqrt(1 - a))); //kilometers | |||||
NOTICE_FORMAT("distanceFromPrevious", "%fkm", a); | |||||
return a; | |||||
} | |||||
uint8_t getVelocity() { | |||||
uint8_t velocity; | |||||
if (!hardware::sim808::device.getGpsField(lastPosition, SIM808_GPS_FIELD::SPEED, &velocity)) velocity = 0; | |||||
VERBOSE_FORMAT("getVelocity", "%d", velocity); | |||||
return velocity; | |||||
} | |||||
void getTime(tmElements_t &time) { | |||||
char *timeStr; | |||||
char buffer[5]; | |||||
hardware::sim808::device.getGpsField(lastPosition, SIM808_GPS_FIELD::UTC, &timeStr); | |||||
VERBOSE_FORMAT("getTime", "%s", timeStr); | |||||
time.Year = CalendarYrToTm(details::parseSubstring(buffer, timeStr + TIME_YEAR_OFFSET, 4)); | |||||
time.Month = details::parseSubstring(buffer, timeStr + TIME_MONTH_OFFSET, 2); | |||||
time.Day = details::parseSubstring(buffer, timeStr + TIME_DAY_OFFSET, 2); | |||||
time.Hour = details::parseSubstring(buffer, timeStr + TIME_HOUR_OFFSET, 2); | |||||
time.Minute = details::parseSubstring(buffer, timeStr + TIME_MINUTE_OFFSET, 2); | |||||
time.Second = details::parseSubstring(buffer, timeStr + TIME_SECOND_OFFSET, 2); | |||||
NOTICE_FORMAT("getTime", "%d/%d/%d %d:%d:%d", tmYearToCalendar(time.Year), time.Month, time.Day, time.Hour, time.Minute, time.Second); | |||||
} | |||||
} | } |
@@ -1,10 +0,0 @@ | |||||
#pragma once | |||||
#include <Arduino.h> | |||||
#include <SIM808.h> | |||||
#include "Debug.h" | |||||
#include "Config.h" | |||||
#include "Core.h" | |||||
#define LOGGER_NAME "GpsTracker" |
@@ -1,95 +1,97 @@ | |||||
#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() { | |||||
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 (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; | |||||
switch (command) { | |||||
case debug::GPSTRACKER_DEBUG_COMMAND::RUN: | |||||
case debug::GPSTRACKER_DEBUG_COMMAND::ONCE: | |||||
core::main(); | |||||
break; | |||||
case debug::GPSTRACKER_DEBUG_COMMAND::RAM: | |||||
debug::displayFreeRam(); | |||||
break; | |||||
case debug::GPSTRACKER_DEBUG_COMMAND::BATTERY: | |||||
debug::getAndDisplayBattery(); | |||||
break; | |||||
case debug::GPSTRACKER_DEBUG_COMMAND::GPS_ON: | |||||
gps::powerOn(); | |||||
break; | |||||
case debug::GPSTRACKER_DEBUG_COMMAND::GPS_OFF: | |||||
gps::powerOff(); | |||||
break; | |||||
case debug::GPSTRACKER_DEBUG_COMMAND::GPS_GET: | |||||
debug::getAndDisplayGpsPosition(); | |||||
break; | |||||
case debug::GPSTRACKER_DEBUG_COMMAND::GPS_SET: | |||||
debug::setFakeGpsPosition(); | |||||
break; | |||||
case debug::GPSTRACKER_DEBUG_COMMAND::RTC_GET: | |||||
debug::getAndDisplayRtcTime(); | |||||
break; | |||||
case debug::GPSTRACKER_DEBUG_COMMAND::RTC_SET: | |||||
debug::setRtcTime(); | |||||
break; | |||||
case debug::GPSTRACKER_DEBUG_COMMAND::EEPROM_GET_CONFIG: | |||||
debug::getAndDisplayEepromConfig(); | |||||
break; | |||||
case debug::GPSTRACKER_DEBUG_COMMAND::EEPROM_RESET_CONFIG: | |||||
config::main::reset(); | |||||
break; | |||||
case debug::GPSTRACKER_DEBUG_COMMAND::EEPROM_GET_CONTENT: | |||||
debug::getAndDisplayEepromContent(); | |||||
break; | |||||
case debug::GPSTRACKER_DEBUG_COMMAND::EEPROM_GET_ENTRIES: | |||||
debug::getAndDisplayEepromPositions(); | |||||
break; | |||||
case debug::GPSTRACKER_DEBUG_COMMAND::EEPROM_GET_LAST_ENTRY: | |||||
debug::getAndDisplayEepromLastPosition(); | |||||
break; | |||||
case debug::GPSTRACKER_DEBUG_COMMAND::EEPROM_ADD_ENTRY: | |||||
debug::addLastPositionToEeprom(); | |||||
break; | |||||
case debug::GPSTRACKER_DEBUG_COMMAND::EEPROM_BACKUP_ENTRIES: | |||||
positions::doBackup(true); | |||||
break; | |||||
case debug::GPSTRACKER_DEBUG_COMMAND::NOTIFY_FAILURES: | |||||
debug::notifyFailures(); | |||||
break; | |||||
case debug::GPSTRACKER_DEBUG_COMMAND::CLEAR_ALERTS: | |||||
debug::clearAlerts(); | |||||
break; | |||||
case debug::GPSTRACKER_DEBUG_COMMAND::SLEEP_DEEP: | |||||
mainunit::deepSleep(10); | |||||
break; | |||||
default: | |||||
NOTICE_MSG("loop", "Unsupported"); | |||||
} | |||||
} | |||||
#include <Arduino.h> | |||||
#include <SIM808.h> | |||||
#include "Debug.h" | |||||
#include "Config.h" | |||||
#include "Core.h" | |||||
#include "Positions.h" | |||||
#include "Logging.h" | |||||
#define LOGGER_NAME "GpsTracker" | |||||
bool bypassMenu = false; | |||||
uint16_t menuTimeout = MENU_TIMEOUT; | |||||
void setup() { | |||||
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 (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; | |||||
switch (command) { | |||||
case debug::GPSTRACKER_DEBUG_COMMAND::RUN: | |||||
case debug::GPSTRACKER_DEBUG_COMMAND::ONCE: | |||||
core::main(); | |||||
break; | |||||
case debug::GPSTRACKER_DEBUG_COMMAND::RAM: | |||||
debug::displayFreeRam(); | |||||
break; | |||||
case debug::GPSTRACKER_DEBUG_COMMAND::BATTERY: | |||||
debug::getAndDisplayBattery(); | |||||
break; | |||||
case debug::GPSTRACKER_DEBUG_COMMAND::GPS_ON: | |||||
gps::powerOn(); | |||||
break; | |||||
case debug::GPSTRACKER_DEBUG_COMMAND::GPS_OFF: | |||||
gps::powerOff(); | |||||
break; | |||||
case debug::GPSTRACKER_DEBUG_COMMAND::GPS_GET: | |||||
debug::getAndDisplayGpsPosition(); | |||||
break; | |||||
case debug::GPSTRACKER_DEBUG_COMMAND::GPS_SET: | |||||
debug::setFakeGpsPosition(); | |||||
break; | |||||
case debug::GPSTRACKER_DEBUG_COMMAND::RTC_GET: | |||||
debug::getAndDisplayRtcTime(); | |||||
break; | |||||
case debug::GPSTRACKER_DEBUG_COMMAND::RTC_SET: | |||||
debug::setRtcTime(); | |||||
break; | |||||
case debug::GPSTRACKER_DEBUG_COMMAND::EEPROM_GET_CONFIG: | |||||
debug::getAndDisplayEepromConfig(); | |||||
break; | |||||
case debug::GPSTRACKER_DEBUG_COMMAND::EEPROM_RESET_CONFIG: | |||||
config::main::reset(); | |||||
break; | |||||
case debug::GPSTRACKER_DEBUG_COMMAND::EEPROM_GET_CONTENT: | |||||
debug::getAndDisplayEepromContent(); | |||||
break; | |||||
case debug::GPSTRACKER_DEBUG_COMMAND::EEPROM_GET_ENTRIES: | |||||
debug::getAndDisplayEepromPositions(); | |||||
break; | |||||
case debug::GPSTRACKER_DEBUG_COMMAND::EEPROM_GET_LAST_ENTRY: | |||||
debug::getAndDisplayEepromLastPosition(); | |||||
break; | |||||
case debug::GPSTRACKER_DEBUG_COMMAND::EEPROM_ADD_ENTRY: | |||||
debug::addLastPositionToEeprom(); | |||||
break; | |||||
case debug::GPSTRACKER_DEBUG_COMMAND::EEPROM_BACKUP_ENTRIES: | |||||
positions::doBackup(true); | |||||
break; | |||||
case debug::GPSTRACKER_DEBUG_COMMAND::NOTIFY_FAILURES: | |||||
debug::notifyFailures(); | |||||
break; | |||||
case debug::GPSTRACKER_DEBUG_COMMAND::CLEAR_ALERTS: | |||||
debug::clearAlerts(); | |||||
break; | |||||
case debug::GPSTRACKER_DEBUG_COMMAND::SLEEP_DEEP: | |||||
mainunit::deepSleep(10); | |||||
break; | |||||
default: | |||||
NOTICE_MSG("loop", "Unsupported"); | |||||
} | |||||
} |
@@ -1,22 +1,19 @@ | |||||
#include "Config.h" | #include "Config.h" | ||||
#include "Hardware.h" | #include "Hardware.h" | ||||
#include "config/Pins.h" | |||||
#include "Debug.h" | |||||
#include "config/Hardware.h" | |||||
#include "Logging.h" | |||||
#include <SoftwareSerial.h> | |||||
#include <SIM808.h> | #include <SIM808.h> | ||||
#include <SIM808_Types.h> | |||||
#include <Wire.h> | #include <Wire.h> | ||||
#include <E24.h> | #include <E24.h> | ||||
namespace hardware { | namespace hardware { | ||||
#define LOGGER_NAME "Hardware::sim808" | #define LOGGER_NAME "Hardware::sim808" | ||||
namespace sim808 { | namespace sim808 { | ||||
SoftwareSerial simSerial = SoftwareSerial(SIM_TX, SIM_RX); | |||||
SIM_SERIAL_TYPE simSerial = SIM_SERIAL; | |||||
SIM808 device = SIM808(SIM_RST, SIM_PWR, SIM_STATUS); | SIM808 device = SIM808(SIM_RST, SIM_PWR, SIM_STATUS); | ||||
uint8_t networkPoweredCount = 0; | uint8_t networkPoweredCount = 0; | ||||
uint8_t gpsPoweredCount = 0; | uint8_t gpsPoweredCount = 0; | ||||
@@ -119,7 +116,7 @@ namespace hardware { | |||||
namespace i2c { | namespace i2c { | ||||
E24 eeprom = E24(E24Size_t::E24_512K); | |||||
E24 eeprom = E24(E24Size_t::E24_512K, E24_ADDRESS); | |||||
uint8_t poweredCount = 0; | uint8_t poweredCount = 0; | ||||
void powerOn() { | void powerOn() { | ||||
@@ -1,31 +1,31 @@ | |||||
#pragma once | |||||
#include <SoftwareSerial.h> | |||||
#include <SIM808.h> | |||||
#include <E24.h> | |||||
namespace hardware { | |||||
namespace sim808 { | |||||
extern SoftwareSerial simSerial; | |||||
extern SIM808 device; | |||||
void powerOn(); | |||||
void powerOff(); | |||||
void setup(); | |||||
void gpsPowerOn(); | |||||
void gpsPowerOff(); | |||||
void networkPowerOn(); | |||||
void networkPowerOff(); | |||||
} | |||||
namespace i2c { | |||||
extern E24 eeprom; | |||||
void powerOn(); | |||||
void powerOff(bool forced = false); | |||||
} | |||||
#pragma once | |||||
#include "config/Hardware.h" | |||||
#include <SIM808.h> | |||||
#include <E24.h> | |||||
namespace hardware { | |||||
namespace sim808 { | |||||
extern SIM_SERIAL_TYPE simSerial; | |||||
extern SIM808 device; | |||||
void powerOn(); | |||||
void powerOff(); | |||||
void setup(); | |||||
void gpsPowerOn(); | |||||
void gpsPowerOff(); | |||||
void networkPowerOn(); | |||||
void networkPowerOff(); | |||||
} | |||||
namespace i2c { | |||||
extern E24 eeprom; | |||||
void powerOn(); | |||||
void powerOff(bool forced = false); | |||||
} | |||||
} | } |
@@ -1,10 +1,13 @@ | |||||
#include "MainUnit.h" | #include "MainUnit.h" | ||||
#include "Rtc.h" | #include "Rtc.h" | ||||
#include "config/Pins.h" | |||||
#include "Debug.h" | |||||
#include "Hardware.h" | |||||
#include "config/Hardware.h" | |||||
#include "Logging.h" | |||||
#define LOGGER_NAME "MainUnit" | #define LOGGER_NAME "MainUnit" | ||||
extern int __heap_start, *__brkval; | |||||
namespace mainunit { | namespace mainunit { | ||||
namespace details { | namespace details { | ||||
@@ -42,4 +45,9 @@ namespace mainunit { | |||||
LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF); | LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF); | ||||
details::wokeUp(); | details::wokeUp(); | ||||
} | } | ||||
int freeRam() { | |||||
int v; | |||||
return (int)&v - (__brkval == 0 ? (int)&__heap_start : (int)__brkval); | |||||
} | |||||
} | } |
@@ -1,8 +1,9 @@ | |||||
#pragma once | |||||
#include <Arduino.h> | |||||
#include <LowPower.h> | |||||
namespace mainunit { | |||||
void deepSleep(uint16_t seconds); | |||||
#pragma once | |||||
#include <Arduino.h> | |||||
#include <LowPower.h> | |||||
namespace mainunit { | |||||
void deepSleep(uint16_t seconds); | |||||
int freeRam(); | |||||
} | } |
@@ -1,76 +1,76 @@ | |||||
#include "Config.h" | |||||
#include "Debug.h" | |||||
#include "Network.h" | |||||
#include "Hardware.h" | |||||
#include "MainUnit.h" | |||||
#include "Rtc.h" | |||||
#define LOGGER_NAME "Network" | |||||
namespace network { | |||||
timestamp_t _poweredOnTime; | |||||
void powerOn() { | |||||
hardware::sim808::networkPowerOn(); | |||||
_poweredOnTime = rtc::getTime(); | |||||
} | |||||
void powerOff() { | |||||
hardware::sim808::networkPowerOff(); | |||||
_poweredOnTime = 0; | |||||
} | |||||
__attribute__((__optimize__("O2"))) | |||||
SIM808RegistrationStatus waitForRegistered(uint32_t timeout, bool relativeToPowerOnTime = true) { | |||||
SIM808RegistrationStatus currentStatus; | |||||
SIM808SignalQualityReport report; | |||||
uint8_t noReliableNetwork = 0; | |||||
if (relativeToPowerOnTime) timeout -= (rtc::getTime() - _poweredOnTime) * 1000; | |||||
currentStatus = hardware::sim808::device.getNetworkRegistrationStatus(); | |||||
report = hardware::sim808::device.getSignalQuality(); | |||||
do { | |||||
if (isAvailable(currentStatus.stat)) break; | |||||
NOTICE_FORMAT("waitForRegistered", "%d, [%d %ddBm]", currentStatus.stat, report.ssri, report.attenuation); | |||||
if (report.ssri < NETWORK_DEFAULT_NO_NETWORK_QUALITY_THRESHOLD) noReliableNetwork++; | |||||
else noReliableNetwork = 0; | |||||
if (noReliableNetwork > NETWORK_DEFAULT_NO_NETWORK_TRIES) { | |||||
NOTICE_MSG("waitForRegistered", "No reliable signal"); | |||||
break; //after a while, no network really means no network. Bailing out | |||||
} | |||||
mainunit::deepSleep(NETWORK_DEFAULT_INTERMEDIATE_TIMEOUT_MS / 1000); | |||||
timeout -= NETWORK_DEFAULT_INTERMEDIATE_TIMEOUT_MS; | |||||
currentStatus = hardware::sim808::device.getNetworkRegistrationStatus(); | |||||
report = hardware::sim808::device.getSignalQuality(); | |||||
} 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 static_cast<int8_t>(state) & | |||||
(static_cast<int8_t>(SIM808_NETWORK_REGISTRATION_STATE::REGISTERED) | static_cast<int8_t>(SIM808_NETWORK_REGISTRATION_STATE::ROAMING)) | |||||
!= 0; | |||||
} | |||||
bool enableGprs() { | |||||
return hardware::sim808::device.enableGprs(config::main::value.network.apn); | |||||
} | |||||
bool sendSms(const char * msg) { | |||||
const char * phoneNumber = config::main::value.contactPhone; | |||||
NOTICE_FORMAT("sendSms", "%s, %s", phoneNumber, msg); | |||||
return hardware::sim808::device.sendSms(phoneNumber, msg); | |||||
} | |||||
#include "Config.h" | |||||
#include "Network.h" | |||||
#include "Hardware.h" | |||||
#include "MainUnit.h" | |||||
#include "Rtc.h" | |||||
#include "Logging.h" | |||||
#define LOGGER_NAME "Network" | |||||
namespace network { | |||||
timestamp_t _poweredOnTime; | |||||
void powerOn() { | |||||
hardware::sim808::networkPowerOn(); | |||||
_poweredOnTime = rtc::getTime(); | |||||
} | |||||
void powerOff() { | |||||
hardware::sim808::networkPowerOff(); | |||||
_poweredOnTime = 0; | |||||
} | |||||
__attribute__((__optimize__("O2"))) | |||||
SIM808RegistrationStatus waitForRegistered(uint32_t timeout, bool relativeToPowerOnTime = true) { | |||||
SIM808RegistrationStatus currentStatus; | |||||
SIM808SignalQualityReport report; | |||||
uint8_t noReliableNetwork = 0; | |||||
if (relativeToPowerOnTime) timeout -= (rtc::getTime() - _poweredOnTime) * 1000; | |||||
currentStatus = hardware::sim808::device.getNetworkRegistrationStatus(); | |||||
report = hardware::sim808::device.getSignalQuality(); | |||||
do { | |||||
if (isAvailable(currentStatus.stat)) break; | |||||
NOTICE_FORMAT("waitForRegistered", "%d, [%d %ddBm]", currentStatus.stat, report.ssri, report.attenuation); | |||||
if (report.ssri < NETWORK_DEFAULT_NO_NETWORK_QUALITY_THRESHOLD) noReliableNetwork++; | |||||
else noReliableNetwork = 0; | |||||
if (noReliableNetwork > NETWORK_DEFAULT_NO_NETWORK_TRIES) { | |||||
NOTICE_MSG("waitForRegistered", "No reliable signal"); | |||||
break; //after a while, no network really means no network. Bailing out | |||||
} | |||||
mainunit::deepSleep(NETWORK_DEFAULT_INTERMEDIATE_TIMEOUT_MS / 1000); | |||||
timeout -= NETWORK_DEFAULT_INTERMEDIATE_TIMEOUT_MS; | |||||
currentStatus = hardware::sim808::device.getNetworkRegistrationStatus(); | |||||
report = hardware::sim808::device.getSignalQuality(); | |||||
} 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 static_cast<int8_t>(state) & | |||||
(static_cast<int8_t>(SIM808_NETWORK_REGISTRATION_STATE::REGISTERED) | static_cast<int8_t>(SIM808_NETWORK_REGISTRATION_STATE::ROAMING)) | |||||
!= 0; | |||||
} | |||||
bool enableGprs() { | |||||
return hardware::sim808::device.enableGprs(config::main::value.network.apn); | |||||
} | |||||
bool sendSms(const char * msg) { | |||||
const char * phoneNumber = config::main::value.contactPhone; | |||||
NOTICE_FORMAT("sendSms", "%s, %s", phoneNumber, msg); | |||||
return hardware::sim808::device.sendSms(phoneNumber, msg); | |||||
} | |||||
} | } |
@@ -1,112 +1,114 @@ | |||||
#pragma once | |||||
#include "Config.h" | |||||
#if BACKUP_ENABLE_NETWORK | |||||
#include "NetworkPositionsBackup.h" | |||||
#include "Debug.h" | |||||
#include "Hardware.h" | |||||
#include "Network.h" | |||||
#define LOGGER_NAME "Positions::backup::network" | |||||
#define BUFFER_SIZE 170 | |||||
namespace positions { | |||||
namespace backup { | |||||
namespace net { | |||||
uint8_t networkUnavailableInARow = 0; | |||||
uint8_t networkUnavailablePostpone = 1; | |||||
bool NetworkPositionsBackup::isBackupNeeded(bool forPrepare) { | |||||
config_t *config = &config::main::value; | |||||
return (config->network.lastSavedEntry == 0xFFFF && config->lastEntry != 0xFFFF) || | |||||
positions::count(config->network.lastSavedEntry) > (config->network.saveThreshold * networkUnavailablePostpone) - (forPrepare ? 1 : 0); | |||||
} | |||||
bool NetworkPositionsBackup::appendPosition(PositionEntry &entry) { | |||||
char buffer[BUFFER_SIZE]; | |||||
snprintf_P(buffer, BUFFER_SIZE, PSTR("%d,%d,%d,%d,%d,%d,%d,%s"), | |||||
debug::freeRam(), | |||||
hardware::sim808::device.getSignalQuality().attenuation, | |||||
entry.metadata.batteryLevel, | |||||
entry.metadata.batteryVoltage, | |||||
entry.metadata.temperature, | |||||
static_cast<uint8_t>(entry.metadata.status), | |||||
entry.metadata.timeToFix, | |||||
entry.position); | |||||
NOTICE_FORMAT("appendPosition", "Sending : %s", buffer); | |||||
uint16_t responseCode = hardware::sim808::device.httpPost( | |||||
config::main::value.network.url, | |||||
F("text/gpstracker"), | |||||
buffer, | |||||
buffer, | |||||
BUFFER_SIZE | |||||
); | |||||
NOTICE_FORMAT("appendPosition", "Response : %d", responseCode); | |||||
return responseCode == POSITIONS_CONFIG_NET_DEFAULT_EXPECTED_RESPONSE; | |||||
} | |||||
//__attribute__((__optimize__("O2"))) | |||||
void NetworkPositionsBackup::appendPositions() { | |||||
uint16_t currentEntryIndex = config::main::value.network.lastSavedEntry + 1; | |||||
PositionEntry currentEntry; | |||||
SIM808RegistrationStatus networkStatus; | |||||
//avoid edge case where if 0, whole set of positions will be sent again | |||||
if (!positions::count(config::main::value.network.lastSavedEntry)) return; | |||||
network::powerOn(); | |||||
networkStatus = network::waitForRegistered(NETWORK_DEFAULT_TOTAL_TIMEOUT_MS); | |||||
if (!network::isAvailable(networkStatus.stat) || !network::enableGprs()) { | |||||
networkUnavailableInARow = min(networkUnavailableInARow + 1, POSITIONS_CONFIG_NET_DEFAULT_UNAVAILABLE_NETWORK_POSTPONE_THRESHOLD + 1); //avoid increment overflow | |||||
NOTICE_MSG("appendPositions", "network or gprs unavailable"); | |||||
if (networkUnavailableInARow > POSITIONS_CONFIG_NET_DEFAULT_UNAVAILABLE_NETWORK_POSTPONE_THRESHOLD) { | |||||
networkUnavailablePostpone++; | |||||
} | |||||
} | |||||
else { | |||||
networkUnavailableInARow = 0; | |||||
networkUnavailablePostpone = 1; | |||||
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() {} | |||||
void NetworkPositionsBackup::prepare() { | |||||
NOTICE("prepare"); | |||||
if (!isBackupNeeded(true)) return; | |||||
network::powerOn(); | |||||
} | |||||
void NetworkPositionsBackup::backup(bool force) { | |||||
NOTICE("backup"); | |||||
if (force || isBackupNeeded(false)) { | |||||
appendPositions(); | |||||
} | |||||
network::powerOff(); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
#pragma once | |||||
#include "Config.h" | |||||
#if BACKUP_ENABLE_NETWORK | |||||
#include "NetworkPositionsBackup.h" | |||||
#include "MainUnit.h" | |||||
#include "Hardware.h" | |||||
#include "Network.h" | |||||
#include "Logging.h" | |||||
#define LOGGER_NAME "Positions::backup::network" | |||||
#define BUFFER_SIZE 170 | |||||
namespace positions { | |||||
namespace backup { | |||||
namespace net { | |||||
uint8_t networkUnavailableInARow = 0; | |||||
uint8_t networkUnavailablePostpone = 1; | |||||
bool NetworkPositionsBackup::isBackupNeeded(bool forPrepare) { | |||||
config_t *config = &config::main::value; | |||||
return (config->network.lastSavedEntry == 0xFFFF && config->lastEntry != 0xFFFF) || | |||||
positions::count(config->network.lastSavedEntry) > (config->network.saveThreshold * networkUnavailablePostpone) - (forPrepare ? 1 : 0); | |||||
} | |||||
bool NetworkPositionsBackup::appendPosition(PositionEntry &entry) { | |||||
char buffer[BUFFER_SIZE]; | |||||
snprintf_P(buffer, BUFFER_SIZE, PSTR("%d,%d,%d,%d,%d,%d,%d,%s"), | |||||
mainunit::freeRam(), | |||||
hardware::sim808::device.getSignalQuality().attenuation, | |||||
entry.metadata.batteryLevel, | |||||
entry.metadata.batteryVoltage, | |||||
entry.metadata.temperature, | |||||
static_cast<uint8_t>(entry.metadata.status), | |||||
entry.metadata.timeToFix, | |||||
entry.position); | |||||
NOTICE_FORMAT("appendPosition", "Sending : %s", buffer); | |||||
uint16_t responseCode = hardware::sim808::device.httpPost( | |||||
config::main::value.network.url, | |||||
F("text/gpstracker"), | |||||
buffer, | |||||
buffer, | |||||
BUFFER_SIZE | |||||
); | |||||
NOTICE_FORMAT("appendPosition", "Response : %d", responseCode); | |||||
return responseCode == POSITIONS_CONFIG_NET_DEFAULT_EXPECTED_RESPONSE; | |||||
} | |||||
//__attribute__((__optimize__("O2"))) | |||||
void NetworkPositionsBackup::appendPositions() { | |||||
uint16_t currentEntryIndex = config::main::value.network.lastSavedEntry + 1; | |||||
PositionEntry currentEntry; | |||||
SIM808RegistrationStatus networkStatus; | |||||
//avoid edge case where if 0, whole set of positions will be sent again | |||||
if (!positions::count(config::main::value.network.lastSavedEntry)) return; | |||||
network::powerOn(); | |||||
networkStatus = network::waitForRegistered(NETWORK_DEFAULT_TOTAL_TIMEOUT_MS); | |||||
if (!network::isAvailable(networkStatus.stat) || !network::enableGprs()) { | |||||
networkUnavailableInARow = min(networkUnavailableInARow + 1, POSITIONS_CONFIG_NET_DEFAULT_UNAVAILABLE_NETWORK_POSTPONE_THRESHOLD + 1); //avoid increment overflow | |||||
NOTICE_MSG("appendPositions", "network or gprs unavailable"); | |||||
if (networkUnavailableInARow > POSITIONS_CONFIG_NET_DEFAULT_UNAVAILABLE_NETWORK_POSTPONE_THRESHOLD) { | |||||
networkUnavailablePostpone++; | |||||
} | |||||
} | |||||
else { | |||||
networkUnavailableInARow = 0; | |||||
networkUnavailablePostpone = 1; | |||||
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() {} | |||||
void NetworkPositionsBackup::prepare() { | |||||
NOTICE("prepare"); | |||||
if (!isBackupNeeded(true)) return; | |||||
network::powerOn(); | |||||
} | |||||
void NetworkPositionsBackup::backup(bool force) { | |||||
NOTICE("backup"); | |||||
if (force || isBackupNeeded(false)) { | |||||
appendPositions(); | |||||
} | |||||
network::powerOff(); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
#endif | #endif |
@@ -1,195 +1,196 @@ | |||||
#include "Config.h" | |||||
#include "Debug.h" | |||||
#include "Positions.h" | |||||
#include "Gps.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 CONFIG_RESERVED_SIZE | |||||
namespace positions { | |||||
#if BACKUPS_ENABLED > 1 | |||||
backup::PositionsBackup **_backups; | |||||
#elif BACKUPS_ENABLED == 1 | |||||
backup::PositionsBackup * _backup; | |||||
#endif | |||||
namespace details { | |||||
uint16_t maxEntryIndex = 0; | |||||
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; | |||||
#if BACKUPS_ENABLED > 0 | |||||
backup::PositionsBackup * backup = NULL; | |||||
#if BACKUPS_ENABLED > 1 | |||||
uint8_t backupIdx = 0; | |||||
_backups = new backup::PositionsBackup*[BACKUPS_ENABLED]; | |||||
#endif //BACKUPS_ENABLED > 1 | |||||
#if BACKUP_ENABLE_SDCARD | |||||
backup = new backup::sd::SdPositionsBackup(); | |||||
backup->setup(); | |||||
#if BACKUPS_ENABLED > 1 | |||||
_backups[backupIdx] = backup; | |||||
backupIdx++; | |||||
#endif //BACKUPS_ENABLED > 1 | |||||
#endif //BACKUP_ENABLE_SDCARD | |||||
#if BACKUP_ENABLE_NETWORK | |||||
backup = new backup::net::NetworkPositionsBackup(); | |||||
backup->setup(); | |||||
#if BACKUPS_ENABLED > 1 | |||||
_backups[backupIdx] = backup; | |||||
backupIdx++; | |||||
#endif //BACKUPS_ENABLED > 1 | |||||
#endif //BACKUP_ENABLE_NETWORK | |||||
#if BACKUPS_ENABLED == 1 | |||||
_backup = backup; | |||||
#endif //BACKUPS_ENABLED == 1 | |||||
#endif //BACKUPS_ENABLED > 0 | |||||
} | |||||
bool acquire(PositionEntryMetadata &metadata) { | |||||
NOTICE("acquire"); | |||||
timestamp_t before; | |||||
gps::powerOn(); | |||||
before = rtc::getTime(); | |||||
SIM808_GPS_STATUS gpsStatus = gps::acquireCurrentPosition(GPS_DEFAULT_TOTAL_TIMEOUT_MS); | |||||
uint16_t timeToFix = rtc::getTime() - before; | |||||
SIM808ChargingStatus battery = hardware::sim808::device.getChargingState(); | |||||
gps::powerOff(); | |||||
bool acquired = gpsStatus >= SIM808_GPS_STATUS::FIX; //prety useless wins 14 bytes on the hex size rather than return gpStatus >= ... | |||||
NOTICE_FORMAT("acquire", "Status : %d", gpsStatus); | |||||
metadata = { | |||||
battery.level, | |||||
battery.voltage, | |||||
rtc::getTemperature(), | |||||
timeToFix, | |||||
gpsStatus | |||||
}; | |||||
return acquired; | |||||
} | |||||
void appendLast(const PositionEntryMetadata &metadata) { | |||||
VERBOSE("appendLast"); | |||||
uint16_t entryIndex; | |||||
uint16_t entryAddress; | |||||
PositionEntry entry = { metadata }; | |||||
strlcpy(entry.position, gps::lastPosition, POSITION_SIZE); | |||||
config_t* config = &config::main::value; | |||||
entryIndex = config->lastEntry + 1; | |||||
entryAddress = details::getEntryAddress(entryIndex); | |||||
hardware::i2c::powerOn(); | |||||
hardware::i2c::eeprom.writeBlock(entryAddress, entry); | |||||
NOTICE_FORMAT("appendLast", "Saved @ %X : %d,%d,%d,%d,%d,%s", | |||||
entryAddress, | |||||
entry.metadata.batteryLevel, | |||||
entry.metadata.batteryVoltage, | |||||
entry.metadata.temperature, | |||||
static_cast<uint8_t>(entry.metadata.status), | |||||
entry.metadata.timeToFix, | |||||
entry.position); | |||||
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) { | |||||
VERBOSE("get"); | |||||
uint16_t entryAddress = details::getEntryAddress(index); | |||||
if (entryAddress == -1) return false; | |||||
VERBOSE_FORMAT("get", "Reading entry %d @ %X", index, entryAddress); | |||||
hardware::i2c::powerOn(); | |||||
hardware::i2c::eeprom.readBlock(entryAddress, entry); | |||||
hardware::i2c::powerOff(); | |||||
NOTICE_FORMAT("get", "Read from EEPROM @ %X : %d,%d,%d,%d,%d,%s", | |||||
entryAddress, | |||||
entry.metadata.batteryLevel, | |||||
entry.metadata.batteryVoltage, | |||||
entry.metadata.temperature, | |||||
static_cast<uint8_t>(entry.metadata.status), | |||||
entry.metadata.timeToFix, | |||||
entry.position); | |||||
return true; | |||||
} | |||||
bool moveNext(uint16_t &index) { | |||||
if (index == config::main::value.lastEntry) return false; | |||||
if (index == details::maxEntryIndex) index = 0; //could use a modulo but easier to understand that way | |||||
else index++; | |||||
return true; | |||||
} | |||||
uint16_t count(uint16_t fromIndex) { | |||||
config_t *config = &config::main::value; | |||||
uint16_t lastEntry = config->lastEntry; | |||||
if (lastEntry < fromIndex) { lastEntry += details::maxEntryIndex; } | |||||
return lastEntry - fromIndex; | |||||
} | |||||
void prepareBackup() { | |||||
#if BACKUPS_ENABLED > 1 | |||||
for (int i = 0; i < BACKUPS_ENABLED; i++) { | |||||
_backups[i]->prepare(); | |||||
} | |||||
#elif BACKUPS_ENABLED == 1 | |||||
_backup->prepare(); | |||||
#endif | |||||
} | |||||
void doBackup(bool force) { | |||||
#if BACKUPS_ENABLED > 1 | |||||
for (int i = 0; i < BACKUPS_ENABLED; i++) { | |||||
_backups[i]->backup(force); | |||||
} | |||||
#elif BACKUPS_ENABLED == 1 | |||||
_backup->backup(force); | |||||
#endif | |||||
} | |||||
#include "Config.h" | |||||
#include "Positions.h" | |||||
#include "Gps.h" | |||||
#include "Rtc.h" | |||||
#include "Logging.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 CONFIG_RESERVED_SIZE | |||||
namespace positions { | |||||
#if BACKUPS_ENABLED > 1 | |||||
backup::PositionsBackup **_backups; | |||||
#elif BACKUPS_ENABLED == 1 | |||||
backup::PositionsBackup * _backup; | |||||
#endif | |||||
namespace details { | |||||
uint16_t maxEntryIndex = 0; | |||||
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; | |||||
#if BACKUPS_ENABLED > 0 | |||||
backup::PositionsBackup * backup = NULL; | |||||
#if BACKUPS_ENABLED > 1 | |||||
uint8_t backupIdx = 0; | |||||
_backups = new backup::PositionsBackup*[BACKUPS_ENABLED]; | |||||
#endif //BACKUPS_ENABLED > 1 | |||||
#if BACKUP_ENABLE_SDCARD | |||||
backup = new backup::sd::SdPositionsBackup(); | |||||
backup->setup(); | |||||
#if BACKUPS_ENABLED > 1 | |||||
_backups[backupIdx] = backup; | |||||
backupIdx++; | |||||
#endif //BACKUPS_ENABLED > 1 | |||||
#endif //BACKUP_ENABLE_SDCARD | |||||
#if BACKUP_ENABLE_NETWORK | |||||
backup = new backup::net::NetworkPositionsBackup(); | |||||
backup->setup(); | |||||
#if BACKUPS_ENABLED > 1 | |||||
_backups[backupIdx] = backup; | |||||
backupIdx++; | |||||
#endif //BACKUPS_ENABLED > 1 | |||||
#endif //BACKUP_ENABLE_NETWORK | |||||
#if BACKUPS_ENABLED == 1 | |||||
_backup = backup; | |||||
#endif //BACKUPS_ENABLED == 1 | |||||
#endif //BACKUPS_ENABLED > 0 | |||||
} | |||||
bool acquire(PositionEntryMetadata &metadata) { | |||||
NOTICE("acquire"); | |||||
timestamp_t before; | |||||
gps::powerOn(); | |||||
before = rtc::getTime(); | |||||
SIM808_GPS_STATUS gpsStatus = gps::acquireCurrentPosition(GPS_DEFAULT_TOTAL_TIMEOUT_MS); | |||||
uint16_t timeToFix = rtc::getTime() - before; | |||||
SIM808ChargingStatus battery = hardware::sim808::device.getChargingState(); | |||||
gps::powerOff(); | |||||
bool acquired = gpsStatus >= SIM808_GPS_STATUS::FIX; //prety useless wins 14 bytes on the hex size rather than return gpStatus >= ... | |||||
NOTICE_FORMAT("acquire", "Status : %d", gpsStatus); | |||||
metadata = { | |||||
battery.level, | |||||
battery.voltage, | |||||
rtc::getTemperature(), | |||||
timeToFix, | |||||
gpsStatus | |||||
}; | |||||
return acquired; | |||||
} | |||||
void appendLast(const PositionEntryMetadata &metadata) { | |||||
VERBOSE("appendLast"); | |||||
uint16_t entryIndex; | |||||
uint16_t entryAddress; | |||||
PositionEntry entry = { metadata }; | |||||
strlcpy(entry.position, gps::lastPosition, POSITION_SIZE); | |||||
config_t* config = &config::main::value; | |||||
entryIndex = config->lastEntry + 1; | |||||
entryAddress = details::getEntryAddress(entryIndex); | |||||
hardware::i2c::powerOn(); | |||||
hardware::i2c::eeprom.writeBlock(entryAddress, entry); | |||||
NOTICE_FORMAT("appendLast", "Saved @ %X : %d,%d,%d,%d,%d,%s", | |||||
entryAddress, | |||||
entry.metadata.batteryLevel, | |||||
entry.metadata.batteryVoltage, | |||||
entry.metadata.temperature, | |||||
static_cast<uint8_t>(entry.metadata.status), | |||||
entry.metadata.timeToFix, | |||||
entry.position); | |||||
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) { | |||||
VERBOSE("get"); | |||||
uint16_t entryAddress = details::getEntryAddress(index); | |||||
if (entryAddress == -1) return false; | |||||
VERBOSE_FORMAT("get", "Reading entry %d @ %X", index, entryAddress); | |||||
hardware::i2c::powerOn(); | |||||
hardware::i2c::eeprom.readBlock(entryAddress, entry); | |||||
hardware::i2c::powerOff(); | |||||
NOTICE_FORMAT("get", "Read from EEPROM @ %X : %d,%d,%d,%d,%d,%s", | |||||
entryAddress, | |||||
entry.metadata.batteryLevel, | |||||
entry.metadata.batteryVoltage, | |||||
entry.metadata.temperature, | |||||
static_cast<uint8_t>(entry.metadata.status), | |||||
entry.metadata.timeToFix, | |||||
entry.position); | |||||
return true; | |||||
} | |||||
bool moveNext(uint16_t &index) { | |||||
if (index == config::main::value.lastEntry) return false; | |||||
if (index == details::maxEntryIndex) index = 0; //could use a modulo but easier to understand that way | |||||
else index++; | |||||
return true; | |||||
} | |||||
uint16_t count(uint16_t fromIndex) { | |||||
config_t *config = &config::main::value; | |||||
uint16_t lastEntry = config->lastEntry; | |||||
if (lastEntry < fromIndex) { lastEntry += details::maxEntryIndex; } | |||||
return lastEntry - fromIndex; | |||||
} | |||||
void prepareBackup() { | |||||
#if BACKUPS_ENABLED > 1 | |||||
for (int i = 0; i < BACKUPS_ENABLED; i++) { | |||||
_backups[i]->prepare(); | |||||
} | |||||
#elif BACKUPS_ENABLED == 1 | |||||
_backup->prepare(); | |||||
#endif | |||||
} | |||||
void doBackup(bool force) { | |||||
#if BACKUPS_ENABLED > 1 | |||||
for (int i = 0; i < BACKUPS_ENABLED; i++) { | |||||
_backups[i]->backup(force); | |||||
} | |||||
#elif BACKUPS_ENABLED == 1 | |||||
_backup->backup(force); | |||||
#endif | |||||
} | |||||
} | } |
@@ -1,7 +1,7 @@ | |||||
#include "Debug.h" | |||||
#include "Rtc.h" | #include "Rtc.h" | ||||
#include "config/Pins.h" | |||||
#include "config/Hardware.h" | |||||
#include "Hardware.h" | |||||
#include "Logging.h" | |||||
#include <Wire.h> | #include <Wire.h> | ||||
#include <uDS3231.h> | #include <uDS3231.h> | ||||
@@ -2,7 +2,7 @@ | |||||
#if BACKUP_ENABLE_SDCARD | #if BACKUP_ENABLE_SDCARD | ||||
#include "SdCard.h" | #include "SdCard.h" | ||||
#include "config/Pins.h" | |||||
#include "config/Hardware.h" | |||||
namespace hardware { | namespace hardware { | ||||
namespace sdcard { | namespace sdcard { | ||||
@@ -1,119 +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(bool force) { | |||||
VERBOSE("backup"); | |||||
if (!hardware::sdcard::available) { | |||||
VERBOSE_MSG("backup", "not available"); | |||||
return; | |||||
} | |||||
SdPositionConfig_t sdConfig; | |||||
if (!details::isBackupNeeded(sdConfig)) return; | |||||
details::appendPositions(sdConfig); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
#include "SdPositionsBackup.h" | |||||
#include "SdPositionsConfig.h" | |||||
#include "SdCard.h" | |||||
#include "Hardware.h" | |||||
#include "Config.h" | |||||
#include "Positions.h" | |||||
#include "Logging.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(bool force) { | |||||
VERBOSE("backup"); | |||||
if (!hardware::sdcard::available) { | |||||
VERBOSE_MSG("backup", "not available"); | |||||
return; | |||||
} | |||||
SdPositionConfig_t sdConfig; | |||||
if (!details::isBackupNeeded(sdConfig)) return; | |||||
details::appendPositions(sdConfig); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
#endif | #endif |
@@ -1,71 +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(); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
#include "SdPositionsConfig.h" | |||||
#include "SdCard.h" | |||||
#include "Logging.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 | #endif |
@@ -3,6 +3,9 @@ | |||||
*/ | */ | ||||
#pragma once | #pragma once | ||||
#include <E24.h> | |||||
#include <SoftwareSerial.h> | |||||
#define SIM_RST 5 ///< SIM808 RESET | #define SIM_RST 5 ///< SIM808 RESET | ||||
#define SIM_RX 6 ///< SIM808 RXD | #define SIM_RX 6 ///< SIM808 RXD | ||||
#define SIM_TX 7 ///< SIM808 TXD | #define SIM_TX 7 ///< SIM808 TXD | ||||
@@ -14,3 +17,8 @@ | |||||
#define SIM_RI 2 ///< Interrupt pin connected to SIM RI. | #define SIM_RI 2 ///< Interrupt pin connected to SIM RI. | ||||
#define RTC_WAKE 3 ///< Interrupt pin connected to DS3231 !INT/SQW | #define RTC_WAKE 3 ///< Interrupt pin connected to DS3231 !INT/SQW | ||||
#define E24_ADDRESS E24_DEFAULT_ADDR ///< I2C address of the 24xxx chip | |||||
#define SIM_SERIAL_TYPE SoftwareSerial ///< Type of variable that holds the Serial communication with SIM808 | |||||
#define SIM_SERIAL SIM_SERIAL_TYPE(SIM_TX, SIM_RX) ///< Definition of the instance that holds the Serial communication with SIM808 |
@@ -34,4 +34,10 @@ | |||||
When hooked up on Serial port, determine how much milliseconds | When hooked up on Serial port, determine how much milliseconds | ||||
to wait for a user input before proceeding. | to wait for a user input before proceeding. | ||||
*/ | */ | ||||
#define MENU_TIMEOUT 20000 | |||||
#if _DEBUG | |||||
#define MENU_TIMEOUT 0 | |||||
#else | |||||
#define MENU_TIMEOUT 20000 | |||||
#endif | |||||
#define MENU_INTERMEDIATE_TIMEOUT 50 |