Browse Source

Merge branch 'alerts' of Stainloud/arduino-gps-tracker into master

tags/v1.2.0
Bertrand Lemasle 6 years ago
committed by Gitea
parent
commit
e418495ed8
47 changed files with 606 additions and 692 deletions
  1. +2
    -264
      .gitignore
  2. +22
    -0
      .vscode/c_cpp_properties.json
  3. +50
    -0
      Alerts.cpp
  4. +15
    -0
      Alerts.h
  5. +86
    -0
      Config.cpp
  6. +30
    -8
      Config.h
  7. +155
    -0
      Core.cpp
  8. +3
    -0
      Core.h
  9. +40
    -8
      Debug.cpp
  10. +4
    -2
      Debug.h
  11. +0
    -0
      Flash.cpp
  12. +0
    -0
      Flash.h
  13. +0
    -0
      Gps.cpp
  14. +0
    -0
      Gps.h
  15. +0
    -0
      GpsTracker.h
  16. +5
    -2
      GpsTracker.ino
  17. +0
    -78
      GpsTracker/Config.cpp
  18. +0
    -77
      GpsTracker/Core.cpp
  19. +0
    -13
      GpsTracker/Network.h
  20. +0
    -143
      GpsTracker/~AutoRecover.GpsTracker.vcxproj0
  21. +62
    -21
      Hardware.cpp
  22. +0
    -0
      Hardware.h
  23. +0
    -0
      Logging.cpp
  24. +0
    -0
      Logging.h
  25. +1
    -9
      MainUnit.cpp
  26. +0
    -1
      MainUnit.h
  27. +34
    -13
      Network.cpp
  28. +15
    -0
      Network.h
  29. +9
    -18
      NetworkPositionsBackup.cpp
  30. +0
    -2
      NetworkPositionsBackup.h
  31. +5
    -5
      NetworkPositionsConfig.h
  32. +0
    -0
      Pins.h
  33. +41
    -25
      Positions.cpp
  34. +0
    -0
      Positions.h
  35. +0
    -0
      PositionsBackup.cpp
  36. +0
    -0
      PositionsBackup.h
  37. +1
    -3
      Rtc.cpp
  38. +0
    -0
      Rtc.h
  39. +0
    -0
      SdCard.cpp
  40. +0
    -0
      SdCard.h
  41. +0
    -0
      SdPositionsBackup.cpp
  42. +0
    -0
      SdPositionsBackup.h
  43. +0
    -0
      SdPositionsConfig.cpp
  44. +0
    -0
      SdPositionsConfig.h
  45. +0
    -0
      Time2.cpp
  46. +0
    -0
      Time2.h
  47. +26
    -0
      gpstracker.code-workspace

+ 2
- 264
.gitignore View File

@@ -1,267 +1,5 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.

# User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates

# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs

# Build results # Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
bld/
[Bb]in/ [Bb]in/
[Oo]bj/
[Ll]og/

# Visual Studio 2015 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/

# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*

# NUNIT
*.VisualState.xml
TestResult.xml

# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c

# DNX
project.lock.json
project.fragment.lock.json
artifacts/

*_i.c
*_p.c
*_i.h
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc

# Chutzpah Test files
_Chutzpah*

# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb

# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap

# TFS 2012 Local Workspace
$tf/

# Guidance Automation Toolkit
*.gpState

# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user

# JustCode is a .NET coding add-in
.JustCode

# TeamCity is a build add-in
_TeamCity*

# DotCover is a Code Coverage Tool
*.dotCover

# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*

# MightyMoose
*.mm.*
AutoTest.Net/

# Web workbench (sass)
.sass-cache/

# Installshield output folder
[Ee]xpress/

# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html

# Click-Once directory
publish/

# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# TODO: Comment the next line if you want to checkin your web deploy settings
# but database connection strings (with potential passwords) will be unencrypted
#*.pubxml
*.publishproj

# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/

# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/packages/*
# except build/, which is used as an MSBuild target.
!**/packages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/packages/repositories.config
# NuGet v3's project.json files produces more ignoreable files
*.nuget.props
*.nuget.targets

# Microsoft Azure Build Output
csx/
*.build.csdef

# Microsoft Azure Emulator
ecf/
rcf/

# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt

# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!*.[Cc]ache/

# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
node_modules/
orleans.codegen.cs

# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/

# RIA/Silverlight projects
Generated_Code/

# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm

# SQL Server files
*.mdf
*.ldf

# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings

# Microsoft Fakes
FakesAssemblies/

# GhostDoc plugin setting file
*.GhostDoc.xml

# Node.js Tools for Visual Studio
.ntvs_analysis.dat

# Visual Studio 6 build log
*.plg

# Visual Studio 6 workspace options file
*.opt

# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions

# Paket dependency manager
.paket/paket.exe
paket-files/

# FAKE - F# Make
.fake/

# JetBrains Rider
.idea/
*.sln.iml

# CodeRush
.cr/

# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc

#VisualMicro
__vm/


#Line endings unifier
.leu
#Visual Studio code
.vscode/

+ 22
- 0
.vscode/c_cpp_properties.json View File

@@ -0,0 +1,22 @@
{
"configurations": [
{
"name": "Win32",
"includePath": [
"${workspaceFolder}/**",
"${workspaceFolder}/../libraries/SIM808",
"${workspaceFolder}/../libraries/uDS3231",
"${workspaceFolder}/../libraries/E24",
"${workspaceFolder}/../libraries/Low-Power",
"${workspaceFolder}/../libraries/ArduinoLog",
"${config:arduino.path}/tools/**",
"${config:arduino.path}/hardware/arduino/avr/**",
"${config:arduino.path}/hardware/tools/avr/avr/include/**"
],
"intelliSenseMode": "clang-x64",
"cStandard": "c11",
"cppStandard": "c++11"
}
],
"version": 4
}

+ 50
- 0
Alerts.cpp View File

@@ -0,0 +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();
}
}

+ 15
- 0
Alerts.h View File

@@ -0,0 +1,15 @@
#pragma once

#include "Positions.h"

#define ALERT_BATTERY_LEVEL_1 0
#define ALERT_BATTERY_LEVEL_2 1
#define ALERT_RTC_TEMPERATURE_FAILURE 2
#define ALERT_RTC_CLOCK_FAILURE 3

namespace alerts {

uint8_t getTriggered(PositionEntryMetadata &metadata);
void add(uint8_t mask);
void clear(PositionEntryMetadata &metadata);
}

+ 86
- 0
Config.cpp View File

@@ -0,0 +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();
}

}
}

GpsTracker/Config.h → Config.h View File

@@ -14,7 +14,7 @@
#define CONFIG_ADDR 0 #define CONFIG_ADDR 0
#define CONFIG_RESERVED_SIZE 128 #define CONFIG_RESERVED_SIZE 128
#define CONFIG_SEED 13 #define CONFIG_SEED 13
#define VERSION "1.00"
#define VERSION "1.10"


#define SLEEP_TIMING_TIME(hours, minutes) hours * 60 + minutes #define SLEEP_TIMING_TIME(hours, minutes) hours * 60 + minutes


@@ -26,8 +26,15 @@
Hard coded value for default sleep time between position acquisitions. Hard coded value for default sleep time between position acquisitions.
Exprimed in seconds Exprimed in seconds
*/ */

#define CONFIG_DEFAULT_BATTERY_ALERT_LEVEL1 45
#define CONFIG_DEFAULT_BATTERY_ALERT_LEVEL2 38
#define CONFIG_DEFAULT_BATTERY_ALERT_CLEAR 60
#define CONFIG_DEFAULT_ACTIVE_ALERTS 0
#define CONFIG_DEFAULT_CONTACT_PHONE "+642568452"

#define SLEEP_DEFAULT_TIME_SECONDS 1800 #define SLEEP_DEFAULT_TIME_SECONDS 1800
#define SLEEP_DEFAULT_STOPPED_THRESHOLD 5
#define SLEEP_DEFAULT_STOPPED_THRESHOLD 5
#define SLEEP_DEFAULT_PAUSING_TIME_SECONDS 270 #define SLEEP_DEFAULT_PAUSING_TIME_SECONDS 270


#define SLEEP_TIMING_MIN SLEEP_TIMING_TIME(0, 0) #define SLEEP_TIMING_MIN SLEEP_TIMING_TIME(0, 0)
@@ -43,6 +50,14 @@
#define NETWORK_DEFAULT_NO_NETWORK_QUALITY_THRESHOLD 8 #define NETWORK_DEFAULT_NO_NETWORK_QUALITY_THRESHOLD 8
#define NETWORK_DEFAULT_NO_NETWORK_TRIES 5 #define NETWORK_DEFAULT_NO_NETWORK_TRIES 5


#define ALERTS_ON_SERIAL_IF_AVAILABLE 1
/**
\def ALERTS_ON_SERIAL_IF_AVAILABLE
Display alerts on serial when connected rather than sending an SMS.
Useful for debugging purpose and avoid costs related to SMS sending.
*/
#define ALERT_SUSPICIOUS_RTC_TEMPERATURE 0

#pragma endregion #pragma endregion


struct sleepTimings_t { struct sleepTimings_t {
@@ -53,14 +68,21 @@ struct sleepTimings_t {
}; };


struct config_t { struct config_t {
uint8_t seed;
char version[5];
uint16_t firstEntry;
uint16_t lastEntry;
uint8_t seed; //sizeof = 1
char version[5]; //sizeof = 5
uint16_t firstEntry; //sizeof = 2
uint16_t lastEntry; //sizeof = 2
#if BACKUP_ENABLE_NETWORK #if BACKUP_ENABLE_NETWORK
networkConfig_t network;
networkConfig_t network; //sizeof = 73
#else
char reserved[73];
#endif #endif
};
uint8_t alertBatteryLevel1; //sizeof = 1
uint8_t alertBatteryLevel2; //sizeof = 1
uint8_t alertBatteryLevelClear; //sizeof = 1
uint8_t activeAlerts; //sizeof = 1
char contactPhone[15]; //sizeof = 15
}; //sizeof = 29 + 73 = 102


namespace config { namespace config {



+ 155
- 0
Core.cpp View File

@@ -0,0 +1,155 @@
#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;

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 forceBackup = false;
bool acquired = false;
PositionEntryMetadata metadata;

positions::prepareBackup();
acquired = positions::acquire(metadata);

if (acquired) {
positions::appendLast(metadata);

forceBackup = updateSleepTime();
gps::preserveCurrentCoordinates();
}

alerts::clear(metadata);
alerts::add(notifyFailures(metadata));
positions::doBackup(forceBackup);

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"), static_cast<uint16_t>(metadata.temperature * 100), 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);
}

bool updateSleepTime() {
bool goingLongSleep = false;
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;
}
else if (stoppedInARow == SLEEP_DEFAULT_STOPPED_THRESHOLD) goingLongSleep = true;
}
else stoppedInARow = 0;

NOTICE_FORMAT("updateSleepTime", "%dkmh => %d seconds", velocity, sleepTime);
return goingLongSleep;
}

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;
}
}

GpsTracker/Core.h → Core.h View File

@@ -14,6 +14,9 @@ namespace core {
extern uint16_t sleepTime; extern uint16_t sleepTime;


void main(); void main();
void updateRtcTime();
bool updateSleepTime(); bool updateSleepTime();
uint16_t mapSleepTime(uint8_t velocity); uint16_t mapSleepTime(uint8_t velocity);

uint8_t notifyFailures(PositionEntryMetadata &metadata);
} }

GpsTracker/Debug.cpp → Debug.cpp View File

@@ -2,6 +2,7 @@
#include "Flash.h" #include "Flash.h"
#include "Positions.h" #include "Positions.h"
#include "Core.h" #include "Core.h"
#include "Alerts.h"


#define LOGGER_NAME "Debug" #define LOGGER_NAME "Debug"


@@ -29,7 +30,8 @@ MENU_ENTRY(EEPROM_GET_ENTRIES, "[P] Get EEPROM entries");
MENU_ENTRY(EEPROM_GET_LAST_ENTRY, "[p] Get EEPROM last entry"); MENU_ENTRY(EEPROM_GET_LAST_ENTRY, "[p] Get EEPROM last entry");
MENU_ENTRY(EEPROM_ADD_ENTRY, "[a] Add last entry to EEPROM"); MENU_ENTRY(EEPROM_ADD_ENTRY, "[a] Add last entry to EEPROM");
MENU_ENTRY(EEPROM_BACKUP_ENTRIES, "[B] Backup EEPROM entries"); MENU_ENTRY(EEPROM_BACKUP_ENTRIES, "[B] Backup EEPROM entries");
MENU_ENTRY(SLEEP, "[S] Sleep for 8s");
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(SLEEP_DEEP, "[s] Deep sleep for 10s");
MENU_ENTRY(QUESTION, "?"); MENU_ENTRY(QUESTION, "?");


@@ -51,7 +53,8 @@ const PROGMEM uint8_t commandIdMapping[] = {
'p', static_cast<uint8_t>(debug::GPSTRACKER_DEBUG_COMMAND::EEPROM_GET_LAST_ENTRY), 'p', static_cast<uint8_t>(debug::GPSTRACKER_DEBUG_COMMAND::EEPROM_GET_LAST_ENTRY),
'a', static_cast<uint8_t>(debug::GPSTRACKER_DEBUG_COMMAND::EEPROM_ADD_ENTRY), 'a', static_cast<uint8_t>(debug::GPSTRACKER_DEBUG_COMMAND::EEPROM_ADD_ENTRY),
'B', static_cast<uint8_t>(debug::GPSTRACKER_DEBUG_COMMAND::EEPROM_BACKUP_ENTRIES), 'B', static_cast<uint8_t>(debug::GPSTRACKER_DEBUG_COMMAND::EEPROM_BACKUP_ENTRIES),
'S', static_cast<uint8_t>(debug::GPSTRACKER_DEBUG_COMMAND::SLEEP),
'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), 's', static_cast<uint8_t>(debug::GPSTRACKER_DEBUG_COMMAND::SLEEP_DEEP),
}; };


@@ -88,7 +91,10 @@ const char * const MENU_ENTRIES[] PROGMEM = {
MENU_EEPROM_BACKUP_ENTRIES, MENU_EEPROM_BACKUP_ENTRIES,
MENU_SEPARATOR, MENU_SEPARATOR,


MENU_SLEEP,
MENU_NOTIFY_FAILURES,
MENU_CLEAR_ALERTS,
MENU_SEPARATOR,

MENU_SLEEP_DEEP, MENU_SLEEP_DEEP,


MENU_QUESTION MENU_QUESTION
@@ -106,7 +112,7 @@ namespace debug {


namespace details { namespace details {
inline void displayPosition(PositionEntry entry) { inline void displayPosition(PositionEntry entry) {
Log.notice(F("%d%%, %dmV, %f°C, %ds, %d, %s\n"), entry.metadata.batteryLevel, entry.metadata.batteryVoltage, entry.metadata.temperature, entry.metadata.timeToFix, entry.metadata.status, entry.position);
Log.notice(F("%d%%, %dmV, %f�C, %ds, %d, %s\n"), entry.metadata.batteryLevel, entry.metadata.batteryVoltage, entry.metadata.temperature, entry.metadata.timeToFix, entry.metadata.status, entry.position);
} }
} }


@@ -122,7 +128,7 @@ namespace debug {
size_t mappingArraySize = flash::getArraySize(commandIdMapping); size_t mappingArraySize = flash::getArraySize(commandIdMapping);
char commandId; char commandId;


for (uint8_t i = 0; i < mappingArraySize; i += 2) {
for (uint8_t i = 0; i < mappingArraySize; i += 2) {
commandId = pgm_read_byte_near(commandIdMapping + i); commandId = pgm_read_byte_near(commandIdMapping + i);
if (commandId == id) return static_cast<GPSTRACKER_DEBUG_COMMAND>(pgm_read_byte_near(commandIdMapping + i + 1)); if (commandId == id) return static_cast<GPSTRACKER_DEBUG_COMMAND>(pgm_read_byte_near(commandIdMapping + i + 1));
} }
@@ -135,7 +141,7 @@ namespace debug {
size_t menuSize = flash::getArraySize(MENU_ENTRIES); size_t menuSize = flash::getArraySize(MENU_ENTRIES);
uint8_t intermediate_timeout = 50; uint8_t intermediate_timeout = 50;


do {
do {
for (uint8_t i = 0; i < menuSize; i++) { for (uint8_t i = 0; i < menuSize; i++) {
Serial.println(reinterpret_cast<const __FlashStringHelper *>(pgm_read_word_near(&MENU_ENTRIES[i]))); Serial.println(reinterpret_cast<const __FlashStringHelper *>(pgm_read_word_near(&MENU_ENTRIES[i])));
} }
@@ -153,7 +159,7 @@ namespace debug {
command = parseCommand(Serial.read()); command = parseCommand(Serial.read());
while (Serial.available()) Serial.read(); //flushing input while (Serial.available()) Serial.read(); //flushing input
} while (command == GPSTRACKER_DEBUG_COMMAND::NONE); } while (command == GPSTRACKER_DEBUG_COMMAND::NONE);
return command; return command;
} }


@@ -183,7 +189,7 @@ namespace debug {
tmElements_t time; tmElements_t time;
rtc::getTime(time); rtc::getTime(time);


NOTICE_FORMAT("getAndDisplayRtcTime", "%d/%d/%d %d:%d:%d", tmYearToCalendar(time.Year), time.Month, time.Day, time.Hour, time.Minute, time.Second);
NOTICE_FORMAT("getAndDisplayRtcTime", "%d/%d/%d %d:%d:%d %t %d", tmYearToCalendar(time.Year), time.Month, time.Day, time.Hour, time.Minute, time.Second, rtc::isAccurate(), (uint16_t)(rtc::getTemperature() * 1000));
} }


void setRtcTime() { void setRtcTime() {
@@ -259,4 +265,30 @@ namespace debug {


for(int i = 0; i < 10; i++) positions::appendLast(metadata); for(int i = 0; i < 10; i++) positions::appendLast(metadata);
} }

void notifyFailures() {
PositionEntryMetadata metadata = {
1, //all battery alerts should goes on with this
3800, //doesn't matter
ALERT_SUSPICIOUS_RTC_TEMPERATURE,
0,
SIM808_GPS_STATUS::OFF
};

uint8_t alerts = core::notifyFailures(metadata);
NOTICE_FORMAT("notifyFailures", "result : %B", alerts);
alerts::add(alerts);
}

void clearAlerts() {
PositionEntryMetadata metadata = {
100, //all battery alerts should goes off with this
3800, //doesn't matter
10,
0,
SIM808_GPS_STATUS::OFF
};

alerts::clear(metadata);
}
} }

GpsTracker/Debug.h → Debug.h View File

@@ -31,7 +31,8 @@ namespace debug {
EEPROM_GET_ENTRIES, EEPROM_GET_ENTRIES,
EEPROM_ADD_ENTRY, EEPROM_ADD_ENTRY,
EEPROM_BACKUP_ENTRIES, EEPROM_BACKUP_ENTRIES,
SLEEP,
NOTIFY_FAILURES,
CLEAR_ALERTS,
SLEEP_DEEP SLEEP_DEEP
}; };


@@ -55,5 +56,6 @@ namespace debug {
void getAndDisplayEepromLastPosition(); void getAndDisplayEepromLastPosition();
void addLastPositionToEeprom(); void addLastPositionToEeprom();



void notifyFailures();
void clearAlerts();
} }

GpsTracker/Flash.cpp → Flash.cpp View File


GpsTracker/Flash.h → Flash.h View File


GpsTracker/Gps.cpp → Gps.cpp View File


GpsTracker/Gps.h → Gps.h View File


GpsTracker/GpsTracker.h → GpsTracker.h View File


GpsTracker/GpsTracker.ino → GpsTracker.ino View File

@@ -80,8 +80,11 @@ void loop() {
case debug::GPSTRACKER_DEBUG_COMMAND::EEPROM_BACKUP_ENTRIES: case debug::GPSTRACKER_DEBUG_COMMAND::EEPROM_BACKUP_ENTRIES:
positions::doBackup(true); positions::doBackup(true);
break; break;
case debug::GPSTRACKER_DEBUG_COMMAND::SLEEP:
mainunit::sleep(period_t::SLEEP_8S);
case debug::GPSTRACKER_DEBUG_COMMAND::NOTIFY_FAILURES:
debug::notifyFailures();
break;
case debug::GPSTRACKER_DEBUG_COMMAND::CLEAR_ALERTS:
debug::clearAlerts();
break; break;
case debug::GPSTRACKER_DEBUG_COMMAND::SLEEP_DEEP: case debug::GPSTRACKER_DEBUG_COMMAND::SLEEP_DEEP:
mainunit::deepSleep(10); mainunit::deepSleep(10);

+ 0
- 78
GpsTracker/Config.cpp View File

@@ -1,78 +0,0 @@
#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", value.seed, value.version, value.firstEntry, value.lastEntry);
#if BACKUP_ENABLE_NETWORK
NOTICE_FORMAT("read", "%d, %d, %s, %s", value.network.saveThreshold, value.network.lastSavedEntry, value.network.apn, value.network.url);
//networkConfig_t c = {
// POSITIONS_CONFIG_NET_DEFAULT_SAVE_THRESHOLD,
// 0xFFFF,
// POSITIONS_CONFIG_NET_DEFAULT_APN,
// POSITIONS_CONFIG_NET_DEFAULT_URL,
//};
//value.network = c;
#endif
}

void write() {
NOTICE_FORMAT("write", "%d, %s, %d, %d", value.seed, value.version, value.firstEntry, value.lastEntry);
#if BACKUP_ENABLE_NETWORK
NOTICE_FORMAT("write", "%d, %d, %s, %s", value.network.saveThreshold, value.network.lastSavedEntry, value.network.apn, value.network.url);
#endif
hardware::i2c::powerOn();
int written = hardware::i2c::eeprom.writeBlock(CONFIG_ADDR, value);
hardware::i2c::powerOff();
}
}

void setup() {
details::read();
//details::write();
}

void save() {
details::write();
}

void reset() {
VERBOSE("reset");

config_t config = {
CONFIG_SEED,
VERSION,
0xFFFF,
0xFFFF,
#if BACKUP_ENABLE_NETWORK
{
POSITIONS_CONFIG_NET_DEFAULT_SAVE_THRESHOLD,
0xFFFF,
POSITIONS_CONFIG_NET_DEFAULT_APN,
POSITIONS_CONFIG_NET_DEFAULT_URL,
},
#endif
};

value = config;
save();
}

}
}

+ 0
- 77
GpsTracker/Core.cpp View File

@@ -1,77 +0,0 @@
#include "Core.h"
#include "Config.h"
#include "Flash.h"

#define LOGGER_NAME "Core"

using namespace utils;

namespace core {
uint16_t sleepTime = SLEEP_DEFAULT_TIME_SECONDS;
uint8_t stoppedInARow = SLEEP_DEFAULT_STOPPED_THRESHOLD - 1;

void main() {
bool forceBackup = false;
positions::prepareBackup();
PositionEntryMetadata metadata;
if (positions::acquire(metadata)) {
positions::appendLast(metadata);
forceBackup = updateSleepTime();

gps::preserveCurrentCoordinates();
}

positions::doBackup(forceBackup);
mainunit::deepSleep(sleepTime);
}

bool updateSleepTime() {
uint8_t velocity = gps::getVelocity();
uint16_t result = mapSleepTime(velocity);
bool goingLongSleep = false;

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) {
result = SLEEP_DEFAULT_PAUSING_TIME_SECONDS;
}
else if (stoppedInARow == SLEEP_DEFAULT_STOPPED_THRESHOLD) goingLongSleep = true;
}
else stoppedInARow = 0;

sleepTime = result;
NOTICE_FORMAT("updateSleepTime", "%dkmh => %d seconds", velocity, sleepTime);

return goingLongSleep;
}

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("computeSleepTime", "%d,%d", velocity, result);
return result;
}
}

+ 0
- 13
GpsTracker/Network.h View File

@@ -1,13 +0,0 @@
#pragma once

#include "Hardware.h"

namespace network {

inline void powerOn() { hardware::sim808::networkPowerOn(); }
inline void powerOff() { hardware::sim808::networkPowerOff(); }

SIM808RegistrationStatus waitForRegistered(uint32_t timeout);
bool isAvailable(SIM808_NETWORK_REGISTRATION_STATE state);
bool enableGprs();
}

+ 0
- 143
GpsTracker/~AutoRecover.GpsTracker.vcxproj0
File diff suppressed because it is too large
View File


GpsTracker/Hardware.cpp → Hardware.cpp View File

@@ -18,6 +18,8 @@ namespace hardware {
namespace sim808 { namespace sim808 {
SoftwareSerial simSerial = SoftwareSerial(SIM_TX, SIM_RX); SoftwareSerial simSerial = SoftwareSerial(SIM_TX, SIM_RX);
SIM808 device = SIM808(SIM_RST, SIM_PWR, SIM_STATUS); SIM808 device = SIM808(SIM_RST, SIM_PWR, SIM_STATUS);
uint8_t networkPoweredCount = 0;
uint8_t gpsPoweredCount = 0;


void powerOn() { void powerOn() {
VERBOSE("powerOn"); VERBOSE("powerOn");
@@ -25,14 +27,19 @@ namespace hardware {
if (!poweredOn) return; if (!poweredOn) return;


device.init(); device.init();
networkPoweredCount = gpsPoweredCount = 0;
} }


void powerOff() { void powerOff() {
VERBOSE("powerOff"); VERBOSE("powerOff");
device.powerOnOff(false); device.powerOnOff(false);
networkPoweredCount = gpsPoweredCount = 0;
} }


void powerOffIfUnused() { void powerOffIfUnused() {
//does not rely on count for safety
//if there is a bug somewhere, the device will consume more battery,
//but will not fail due to an over aggressive battery saving strategy
bool gpsPowered = false; bool gpsPowered = false;


if ((!device.getGpsPowerState(&gpsPowered) || !gpsPowered) && if ((!device.getGpsPowerState(&gpsPowered) || !gpsPowered) &&
@@ -49,13 +56,30 @@ namespace hardware {
} }


void gpsPowerOn() { void gpsPowerOn() {
if(gpsPoweredCount) {
gpsPoweredCount++;
return;
}

VERBOSE("gpsPowerOn"); VERBOSE("gpsPowerOn");
powerOn(); powerOn();
if(!networkPoweredCount) {
//SIM808 turns phone on by default but we don't need it for gps only
device.setPhoneFunctionality(SIM808_PHONE_FUNCTIONALITY::MINIMUM);
}
device.enableGps(); device.enableGps();
} }


void gpsPowerOff() { void gpsPowerOff() {
if (!device.powered()) return;
if (!device.powered()) {
networkPoweredCount = gpsPoweredCount = 0; //just to be sure counts == 0
return;
}

if(gpsPoweredCount > 1) {
gpsPoweredCount--;
return;
}


VERBOSE("gpsPowerOff"); VERBOSE("gpsPowerOff");
device.disableGps(); device.disableGps();
@@ -63,18 +87,30 @@ namespace hardware {
} }


void networkPowerOn() { void networkPowerOn() {
if(networkPoweredCount) {
networkPoweredCount++;
return;
}

VERBOSE("networkPowerOn"); VERBOSE("networkPowerOn");
powerOn(); powerOn();
device.setPhoneFunctionality(SIM808_PHONE_FUNCTIONALITY::FULL); device.setPhoneFunctionality(SIM808_PHONE_FUNCTIONALITY::FULL);
} }


void networkPowerOff() { void networkPowerOff() {
if (!device.powered()) return;
if (!device.powered()) {
networkPoweredCount = gpsPoweredCount = 0; //just to be sure counts == 0
return;
}

if(networkPoweredCount > 1) {
networkPoweredCount--;
return;
}


VERBOSE("networkPowerOff"); VERBOSE("networkPowerOff");
device.disableGprs(); device.disableGprs();
device.setPhoneFunctionality(SIM808_PHONE_FUNCTIONALITY::MINIMUM); device.setPhoneFunctionality(SIM808_PHONE_FUNCTIONALITY::MINIMUM);

powerOffIfUnused(); powerOffIfUnused();
} }
} }
@@ -87,32 +123,37 @@ namespace hardware {
uint8_t poweredCount = 0; uint8_t poweredCount = 0;


void powerOn() { void powerOn() {
if (!poweredCount) {
VERBOSE("powerOn");
digitalWrite(I2C_PWR, HIGH);
pinMode(I2C_PWR, OUTPUT);

Wire.begin();
if(poweredCount) {
poweredCount++;
return;
} }


poweredCount++;
VERBOSE("powerOn");
digitalWrite(I2C_PWR, HIGH);
pinMode(I2C_PWR, OUTPUT);

Wire.begin();
poweredCount = 1;
} }


void powerOff(bool forced = false) { void powerOff(bool forced = false) {
if (poweredCount == 1 || forced) {
VERBOSE("powerOff");
pinMode(I2C_PWR, INPUT);
digitalWrite(I2C_PWR, LOW);
if(poweredCount > 1 && !forced) {
poweredCount--;
return;
}

VERBOSE("powerOff");
pinMode(I2C_PWR, INPUT);
digitalWrite(I2C_PWR, LOW);


//turn off i2c
TWCR &= ~(bit(TWEN) | bit(TWIE) | bit(TWEA));
//turn off i2c
TWCR &= ~(bit(TWEN) | bit(TWIE) | bit(TWEA));


//disable i2c internal pull ups
digitalWrite(A4, LOW);
digitalWrite(A5, LOW);
}
//disable i2c internal pull ups
digitalWrite(A4, LOW);
digitalWrite(A5, LOW);


poweredCount--;
poweredCount = 0;
} }
} }
} }

GpsTracker/Hardware.h → Hardware.h View File


GpsTracker/Logging.cpp → Logging.cpp View File


GpsTracker/Logging.h → Logging.h View File


GpsTracker/MainUnit.cpp → MainUnit.cpp View File

@@ -11,7 +11,7 @@ namespace mainunit {


void prepareSleep() { void prepareSleep() {
hardware::sim808::simSerial.end(); //avoid woke up by SoftwareSerial interrupt hardware::sim808::simSerial.end(); //avoid woke up by SoftwareSerial interrupt
delay(5); //ensure message have been printed out
delay(5); //ensure log messages have been printed out
} }


void wokeUp() { void wokeUp() {
@@ -35,14 +35,6 @@ namespace mainunit {
attachInterrupt(digitalPinToInterrupt(RTC_WAKE), interrupt, FALLING); attachInterrupt(digitalPinToInterrupt(RTC_WAKE), interrupt, FALLING);
} }


void sleep(period_t period) {
NOTICE_FORMAT("sleep", "Period : %d", period);
details::prepareSleep();
LowPower.powerDown(period, ADC_OFF, BOD_OFF);
details::wokeUp();

}

void deepSleep(uint16_t seconds) { void deepSleep(uint16_t seconds) {
NOTICE_FORMAT("deepSleep", "%d seconds", seconds); NOTICE_FORMAT("deepSleep", "%d seconds", seconds);
interruptIn(seconds); interruptIn(seconds);

GpsTracker/MainUnit.h → MainUnit.h View File

@@ -4,6 +4,5 @@
#include <LowPower.h> #include <LowPower.h>


namespace mainunit { namespace mainunit {
void sleep(period_t period);
void deepSleep(uint16_t seconds); void deepSleep(uint16_t seconds);
} }

GpsTracker/Network.cpp → Network.cpp View File

@@ -1,55 +1,76 @@
#include "Config.h" #include "Config.h"


#if BACKUP_ENABLE_NETWORK
#include "Debug.h" #include "Debug.h"
#include "Network.h" #include "Network.h"
#include "Hardware.h" #include "Hardware.h"
#include "MainUnit.h" #include "MainUnit.h"
#include "Rtc.h"


#define LOGGER_NAME "Network" #define LOGGER_NAME "Network"


namespace network { namespace network {


timestamp_t _poweredOnTime;

void powerOn() {
hardware::sim808::networkPowerOn();
_poweredOnTime = rtc::getTime();
}

void powerOff() {
hardware::sim808::networkPowerOff();
_poweredOnTime = 0;
}

__attribute__((__optimize__("O2"))) __attribute__((__optimize__("O2")))
SIM808RegistrationStatus waitForRegistered(uint32_t timeout) {
SIM808RegistrationStatus waitForRegistered(uint32_t timeout, bool relativeToPowerOnTime = true) {


SIM808RegistrationStatus currentStatus; SIM808RegistrationStatus currentStatus;
SIM808SignalQualityReport report; SIM808SignalQualityReport report;
uint8_t noReliableNetwork = 0; uint8_t noReliableNetwork = 0;


if (relativeToPowerOnTime) timeout -= (rtc::getTime() - _poweredOnTime) * 1000;

currentStatus = hardware::sim808::device.getNetworkRegistrationStatus();
report = hardware::sim808::device.getSignalQuality();

do { do {
currentStatus = hardware::sim808::device.getNetworkRegistrationStatus();
if (isAvailable(currentStatus.stat)) break; if (isAvailable(currentStatus.stat)) break;


report = hardware::sim808::device.getSignalQuality();
NOTICE_FORMAT("waitForRegistered", "%d, [%d %ddBm]", currentStatus.stat, report.ssri, report.attenuation); NOTICE_FORMAT("waitForRegistered", "%d, [%d %ddBm]", currentStatus.stat, report.ssri, report.attenuation);
if (report.ssri < NETWORK_DEFAULT_NO_NETWORK_QUALITY_THRESHOLD) noReliableNetwork++; if (report.ssri < NETWORK_DEFAULT_NO_NETWORK_QUALITY_THRESHOLD) noReliableNetwork++;
else noReliableNetwork = 0; else noReliableNetwork = 0;
if (noReliableNetwork > NETWORK_DEFAULT_NO_NETWORK_TRIES) { if (noReliableNetwork > NETWORK_DEFAULT_NO_NETWORK_TRIES) {
NOTICE_MSG("waitForRegistered", "No reliable signal"); NOTICE_MSG("waitForRegistered", "No reliable signal");
break; //after a while, not network really means no network. Bailing out
break; //after a while, no network really means no network. Bailing out
} }


mainunit::deepSleep(NETWORK_DEFAULT_INTERMEDIATE_TIMEOUT_MS / 1000); mainunit::deepSleep(NETWORK_DEFAULT_INTERMEDIATE_TIMEOUT_MS / 1000);
timeout -= NETWORK_DEFAULT_INTERMEDIATE_TIMEOUT_MS; timeout -= NETWORK_DEFAULT_INTERMEDIATE_TIMEOUT_MS;


currentStatus = hardware::sim808::device.getNetworkRegistrationStatus();
report = hardware::sim808::device.getSignalQuality();
} while (timeout > 1); } while (timeout > 1);


report = hardware::sim808::device.getSignalQuality();
NOTICE_FORMAT("waitForRegistered", "%d, [%d %ddBm]", currentStatus.stat, report.ssri, report.attenuation); NOTICE_FORMAT("waitForRegistered", "%d, [%d %ddBm]", currentStatus.stat, report.ssri, report.attenuation);
return currentStatus; return currentStatus;
} }


bool isAvailable(SIM808_NETWORK_REGISTRATION_STATE state) { bool isAvailable(SIM808_NETWORK_REGISTRATION_STATE state) {
return state == SIM808_NETWORK_REGISTRATION_STATE::REGISTERED ||
state == SIM808_NETWORK_REGISTRATION_STATE::ROAMING;
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() { bool enableGprs() {
return hardware::sim808::device.enableGprs(config::main::value.network.apn); return hardware::sim808::device.enableGprs(config::main::value.network.apn);
} }
}
#endif

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);
}
}

+ 15
- 0
Network.h View File

@@ -0,0 +1,15 @@
#pragma once

#include "Hardware.h"

namespace network {

void powerOn();
void powerOff();

SIM808RegistrationStatus waitForRegistered(uint32_t timeout, bool relativeToPowerOnTime = true);
bool isAvailable(SIM808_NETWORK_REGISTRATION_STATE state);
bool enableGprs();

bool sendSms(const char * msg);
}

GpsTracker/NetworkPositionsBackup.cpp → NetworkPositionsBackup.cpp View File

@@ -26,16 +26,15 @@ namespace positions {


bool NetworkPositionsBackup::appendPosition(PositionEntry &entry) { bool NetworkPositionsBackup::appendPosition(PositionEntry &entry) {
char buffer[BUFFER_SIZE]; char buffer[BUFFER_SIZE];
snprintf(buffer, BUFFER_SIZE, "%d,%d,%d,%d,%d,%d,%d,",
snprintf_P(buffer, BUFFER_SIZE, PSTR("%d,%d,%d,%d,%d,%d,%d,%s"),
debug::freeRam(), debug::freeRam(),
hardware::sim808::device.getSignalQuality().attenuation, hardware::sim808::device.getSignalQuality().attenuation,
entry.metadata.batteryLevel, entry.metadata.batteryLevel,
entry.metadata.batteryVoltage, entry.metadata.batteryVoltage,
static_cast<uint16_t>(entry.metadata.temperature * 100), static_cast<uint16_t>(entry.metadata.temperature * 100),
static_cast<uint8_t>(entry.metadata.status), static_cast<uint8_t>(entry.metadata.status),
entry.metadata.timeToFix);

strcat(buffer, entry.position);
entry.metadata.timeToFix,
entry.position);


NOTICE_FORMAT("appendPosition", "Sending : %s", buffer); NOTICE_FORMAT("appendPosition", "Sending : %s", buffer);
uint16_t responseCode = hardware::sim808::device.httpPost( uint16_t responseCode = hardware::sim808::device.httpPost(
@@ -53,15 +52,14 @@ namespace positions {
//__attribute__((__optimize__("O2"))) //__attribute__((__optimize__("O2")))
void NetworkPositionsBackup::appendPositions() { void NetworkPositionsBackup::appendPositions() {
uint16_t currentEntryIndex = config::main::value.network.lastSavedEntry + 1; uint16_t currentEntryIndex = config::main::value.network.lastSavedEntry + 1;
uint32_t networkTimeout = 0;
PositionEntry currentEntry; PositionEntry currentEntry;
SIM808RegistrationStatus networkStatus; SIM808RegistrationStatus networkStatus;


network::powerOn();
networkTimeout = NETWORK_DEFAULT_TOTAL_TIMEOUT_MS;
if (_prepareTime > 0) networkTimeout -= (rtc::getTime() - _prepareTime) * 1000;
//avoid edge case where if 0, whole set of positions will be sent again
if (!positions::count(config::main::value.network.lastSavedEntry)) return;


networkStatus = network::waitForRegistered(networkTimeout);
network::powerOn();
networkStatus = network::waitForRegistered(NETWORK_DEFAULT_TOTAL_TIMEOUT_MS);


if (!network::isAvailable(networkStatus.stat) || !network::enableGprs()) { if (!network::isAvailable(networkStatus.stat) || !network::enableGprs()) {
networkUnavailableInARow = min(networkUnavailableInARow + 1, POSITIONS_CONFIG_NET_DEFAULT_UNAVAILABLE_NETWORK_POSTPONE_THRESHOLD + 1); //avoid increment overflow networkUnavailableInARow = min(networkUnavailableInARow + 1, POSITIONS_CONFIG_NET_DEFAULT_UNAVAILABLE_NETWORK_POSTPONE_THRESHOLD + 1); //avoid increment overflow
@@ -90,20 +88,13 @@ namespace positions {
network::powerOff(); network::powerOff();
} }


void NetworkPositionsBackup::setup() {
NOTICE("setup");
}
void NetworkPositionsBackup::setup() {}


void NetworkPositionsBackup::prepare() { void NetworkPositionsBackup::prepare() {
NOTICE("prepare"); NOTICE("prepare");


if (!isBackupNeeded(true)) {
_prepareTime = 0;
return;
}

if (!isBackupNeeded(true)) return;
network::powerOn(); network::powerOn();
_prepareTime = rtc::getTime();
} }


void NetworkPositionsBackup::backup(bool force) { void NetworkPositionsBackup::backup(bool force) {

GpsTracker/NetworkPositionsBackup.h → NetworkPositionsBackup.h View File

@@ -10,8 +10,6 @@ namespace positions {


class NetworkPositionsBackup : public PositionsBackup { class NetworkPositionsBackup : public PositionsBackup {
private: private:
timestamp_t _prepareTime;

bool isBackupNeeded(bool forPrepare); bool isBackupNeeded(bool forPrepare);
bool appendPosition(PositionEntry &entry); bool appendPosition(PositionEntry &entry);
void appendPositions(); void appendPositions();

GpsTracker/NetworkPositionsConfig.h → NetworkPositionsConfig.h View File

@@ -9,8 +9,8 @@




struct networkConfig_t { struct networkConfig_t {
uint8_t saveThreshold;
uint16_t lastSavedEntry;
char apn[20];
char url[50];
};
uint8_t saveThreshold; //sizeof = 1
uint16_t lastSavedEntry; //sizeof = 2
char apn[20]; //sizeof = 20
char url[50]; //sizeof = 50
}; //sizeof = 73

GpsTracker/Pins.h → Pins.h View File


GpsTracker/Positions.cpp → Positions.cpp View File

@@ -4,7 +4,7 @@
#include "Gps.h" #include "Gps.h"


#if BACKUP_ENABLE_SDCARD || BACKUP_ENABLE_NETWORK #if BACKUP_ENABLE_SDCARD || BACKUP_ENABLE_NETWORK
#define BACKUPS_ENABLED BACKUP_ENABLE_SDCARD + BACKUP_ENABLE_NETWORK
#define BACKUPS_ENABLED (BACKUP_ENABLE_SDCARD + BACKUP_ENABLE_NETWORK)
#endif #endif


#if BACKUP_ENABLE_SDCARD #if BACKUP_ENABLE_SDCARD
@@ -21,8 +21,10 @@
#define ENTRIES_ADDR CONFIG_RESERVED_SIZE #define ENTRIES_ADDR CONFIG_RESERVED_SIZE


namespace positions { namespace positions {
#ifdef BACKUPS_ENABLED
#if BACKUPS_ENABLED > 1
backup::PositionsBackup **_backups; backup::PositionsBackup **_backups;
#elif BACKUPS_ENABLED == 1
backup::PositionsBackup * _backup;
#endif #endif


namespace details { namespace details {
@@ -36,21 +38,37 @@ namespace positions {


void setup() { void setup() {
details::maxEntryIndex = (E24_MAX_ADDRESS(hardware::i2c::eeprom.getSize()) - ENTRIES_ADDR) / ENTRY_RESERVED_SIZE; details::maxEntryIndex = (E24_MAX_ADDRESS(hardware::i2c::eeprom.getSize()) - ENTRIES_ADDR) / ENTRY_RESERVED_SIZE;
#ifdef BACKUPS_ENABLED

#if BACKUPS_ENABLED > 0
backup::PositionsBackup * backup = NULL;
#if BACKUPS_ENABLED > 1
uint8_t backupIdx = 0; uint8_t backupIdx = 0;
_backups = new backup::PositionsBackup*[BACKUPS_ENABLED]; _backups = new backup::PositionsBackup*[BACKUPS_ENABLED];
#endif //BACKUPS_ENABLED > 1


#if BACKUP_ENABLE_SDCARD #if BACKUP_ENABLE_SDCARD
_backups[backupIdx] = new backup::sd::SdPositionsBackup();
_backups[backupIdx]->setup();
backup = new backup::sd::SdPositionsBackup();
backup->setup();
#if BACKUPS_ENABLED > 1
_backups[backupIdx] = backup;
backupIdx++; backupIdx++;
#endif
#endif //BACKUPS_ENABLED > 1
#endif //BACKUP_ENABLE_SDCARD

#if BACKUP_ENABLE_NETWORK #if BACKUP_ENABLE_NETWORK
_backups[backupIdx] = new backup::net::NetworkPositionsBackup();
_backups[backupIdx]->setup();
backup = new backup::net::NetworkPositionsBackup();
backup->setup();
#if BACKUPS_ENABLED > 1
_backups[backupIdx] = backup;
backupIdx++; backupIdx++;
#endif
#endif
#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) { bool acquire(PositionEntryMetadata &metadata) {
@@ -61,19 +79,13 @@ namespace positions {
gps::powerOn(); gps::powerOn();
before = rtc::getTime(); before = rtc::getTime();
SIM808_GPS_STATUS gpsStatus = gps::acquireCurrentPosition(GPS_DEFAULT_TOTAL_TIMEOUT_MS); SIM808_GPS_STATUS gpsStatus = gps::acquireCurrentPosition(GPS_DEFAULT_TOTAL_TIMEOUT_MS);
uint16_t timeToFix = rtc::getTime() - before;
SIM808ChargingStatus battery = hardware::sim808::device.getChargingState(); SIM808ChargingStatus battery = hardware::sim808::device.getChargingState();
gps::powerOff(); 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); NOTICE_FORMAT("acquire", "Status : %d", gpsStatus);


if (gpsStatus < SIM808_GPS_STATUS::FIX) return false;

uint16_t timeToFix = rtc::getTime() - before;

tmElements_t time;
gps::getTime(time);
rtc::setTime(time);

metadata = { metadata = {
battery.level, battery.level,
battery.voltage, battery.voltage,
@@ -82,7 +94,7 @@ namespace positions {
gpsStatus gpsStatus
}; };


return true;
return acquired;
} }


void appendLast(const PositionEntryMetadata &metadata) { void appendLast(const PositionEntryMetadata &metadata) {
@@ -101,7 +113,7 @@ namespace positions {
hardware::i2c::powerOn(); hardware::i2c::powerOn();
hardware::i2c::eeprom.writeBlock(entryAddress, entry); hardware::i2c::eeprom.writeBlock(entryAddress, entry);


NOTICE_FORMAT("appendLast", "Saved @ %X : [%d%% @ %dmV] [%f°C] [TTF : %d, Status : %d, Position : %s]", entryAddress, entry.metadata.batteryLevel, entry.metadata.batteryVoltage, entry.metadata.temperature, entry.metadata.timeToFix, entry.metadata.status, entry.position);
NOTICE_FORMAT("appendLast", "Saved @ %X : [%d%% @ %dmV] [%f�C] [TTF : %d, Status : %d, Position : %s]", entryAddress, entry.metadata.batteryLevel, entry.metadata.batteryVoltage, entry.metadata.temperature, entry.metadata.timeToFix, entry.metadata.status, entry.position);


config->lastEntry++; config->lastEntry++;
if (config->lastEntry > details::maxEntryIndex) config->lastEntry = 0; if (config->lastEntry > details::maxEntryIndex) config->lastEntry = 0;
@@ -118,19 +130,19 @@ namespace positions {
uint16_t entryAddress = details::getEntryAddress(index); uint16_t entryAddress = details::getEntryAddress(index);
if (entryAddress == -1) return false; if (entryAddress == -1) return false;


VERBOSE_FORMAT("get", "Reading entry n°%d @ %X", index, entryAddress);
VERBOSE_FORMAT("get", "Reading entry n�%d @ %X", index, entryAddress);


hardware::i2c::powerOn(); hardware::i2c::powerOn();
hardware::i2c::eeprom.readBlock(entryAddress, entry); hardware::i2c::eeprom.readBlock(entryAddress, entry);
hardware::i2c::powerOff(); hardware::i2c::powerOff();


NOTICE_FORMAT("get", "Read from EEPROM @ %X : [%d%% @ %dmV] [%f°C] [TTF : %d, Status : %d, Position : %s]", entryAddress, entry.metadata.batteryLevel, entry.metadata.batteryVoltage, entry.metadata.temperature, entry.metadata.timeToFix, entry.metadata.status, entry.position);
NOTICE_FORMAT("get", "Read from EEPROM @ %X : [%d%% @ %dmV] [%f�C] [TTF : %d, Status : %d, Position : %s]", entryAddress, entry.metadata.batteryLevel, entry.metadata.batteryVoltage, entry.metadata.temperature, entry.metadata.timeToFix, entry.metadata.status, entry.position);
return true; return true;
} }


bool moveNext(uint16_t &index) { bool moveNext(uint16_t &index) {
if (index == config::main::value.lastEntry) return false; if (index == config::main::value.lastEntry) return false;
if (index == details::maxEntryIndex) index = 0; //could use a modulo but easier to understand that way if (index == details::maxEntryIndex) index = 0; //could use a modulo but easier to understand that way
else index++; else index++;


@@ -147,18 +159,22 @@ namespace positions {
} }


void prepareBackup() { void prepareBackup() {
#ifdef BACKUPS_ENABLED
#if BACKUPS_ENABLED > 1
for (int i = 0; i < BACKUPS_ENABLED; i++) { for (int i = 0; i < BACKUPS_ENABLED; i++) {
_backups[i]->prepare(); _backups[i]->prepare();
} }
#elif BACKUPS_ENABLED == 1
_backup->prepare();
#endif #endif
} }


void doBackup(bool force) { void doBackup(bool force) {
#ifdef BACKUPS_ENABLED
#if BACKUPS_ENABLED > 1
for (int i = 0; i < BACKUPS_ENABLED; i++) { for (int i = 0; i < BACKUPS_ENABLED; i++) {
_backups[i]->backup(force); _backups[i]->backup(force);
} }
#elif BACKUPS_ENABLED == 1
_backup->backup(force);
#endif #endif
} }
} }

GpsTracker/Positions.h → Positions.h View File


GpsTracker/PositionsBackup.cpp → PositionsBackup.cpp View File


GpsTracker/PositionsBackup.h → PositionsBackup.h View File


GpsTracker/Rtc.cpp → Rtc.cpp View File

@@ -11,7 +11,7 @@
using namespace utils; using namespace utils;


namespace rtc { namespace rtc {
void setup() { void setup() {
VERBOSE("setup"); VERBOSE("setup");
hardware::i2c::powerOn(); hardware::i2c::powerOn();
@@ -19,8 +19,6 @@ namespace rtc {
RTC.control(DS3231_A1_INT_ENABLE, DS3231_OFF); //Alarm 1 OFF RTC.control(DS3231_A1_INT_ENABLE, DS3231_OFF); //Alarm 1 OFF
RTC.control(DS3231_INT_ENABLE, DS3231_ON); //INTCN ON RTC.control(DS3231_INT_ENABLE, DS3231_ON); //INTCN ON
hardware::i2c::powerOff(); hardware::i2c::powerOff();

//TODO : check wether the osc has been halted (meaning the battery could be dead)
} }


float getTemperature() { float getTemperature() {

GpsTracker/Rtc.h → Rtc.h View File


GpsTracker/SdCard.cpp → SdCard.cpp View File


GpsTracker/SdCard.h → SdCard.h View File


GpsTracker/SdPositionsBackup.cpp → SdPositionsBackup.cpp View File


GpsTracker/SdPositionsBackup.h → SdPositionsBackup.h View File


GpsTracker/SdPositionsConfig.cpp → SdPositionsConfig.cpp View File


GpsTracker/SdPositionsConfig.h → SdPositionsConfig.h View File


GpsTracker/Time2.cpp → Time2.cpp View File


GpsTracker/Time2.h → Time2.h View File


+ 26
- 0
gpstracker.code-workspace View File

@@ -0,0 +1,26 @@
{
"folders": [
{
"path": "."
},
{
"path": "../libraries/SIM808"
},
{
"path": "../libraries/uDS3231"
},
{
"path": "../libraries/E24"
},
{
"path": "../libraries/Low-Power"
},
{
"path": "../libraries/ArduinoLog"
}
],
"settings": {
"C_Cpp.intelliSenseEngineFallback": "Disabled",
"C_Cpp.intelliSenseEngine": "Tag Parser"
}
}

Loading…
Cancel
Save