#pragma once #include "LocationProvider.h" #include #include #include #ifndef GPS_EN #ifdef PIN_GPS_EN #define GPS_EN PIN_GPS_EN #else #define GPS_EN (-1) #endif #endif #ifndef PIN_GPS_EN_ACTIVE #define PIN_GPS_EN_ACTIVE HIGH #endif #ifndef GPS_RESET #ifdef PIN_GPS_RESET #define GPS_RESET PIN_GPS_RESET #else #define GPS_RESET (-1) #endif #endif #ifndef GPS_RESET_FORCE #ifdef PIN_GPS_RESET_ACTIVE #define GPS_RESET_FORCE PIN_GPS_RESET_ACTIVE #else #define GPS_RESET_FORCE LOW #endif #endif class MicroNMEALocationProvider : public LocationProvider { char _nmeaBuffer[100]; MicroNMEA nmea; mesh::RTCClock* _clock; Stream* _gps_serial; RefCountedDigitalPin* _peripher_power; int8_t _claims = 0; int _pin_reset; int _pin_en; long next_check = 0; long time_valid = 0; unsigned long _last_time_sync = 0; static const unsigned long TIME_SYNC_INTERVAL = 1800000; // Re-sync every 30 minutes public : MicroNMEALocationProvider(Stream& ser, mesh::RTCClock* clock = NULL, int pin_reset = GPS_RESET, int pin_en = GPS_EN,RefCountedDigitalPin* peripher_power=NULL) : _gps_serial(&ser), nmea(_nmeaBuffer, sizeof(_nmeaBuffer)), _pin_reset(pin_reset), _pin_en(pin_en), _clock(clock), _peripher_power(peripher_power) { if (_pin_reset != -1) { pinMode(_pin_reset, OUTPUT); digitalWrite(_pin_reset, GPS_RESET_FORCE); } if (_pin_en != -1) { pinMode(_pin_en, OUTPUT); digitalWrite(_pin_en, LOW); } } void claim() { _claims++; if (_claims > 0) { if (_peripher_power) _peripher_power->claim(); } } void release() { if (_claims == 0) return; // avoid negative _claims _claims--; if (_peripher_power) _peripher_power->release(); } void begin() override { claim(); if (_pin_en != -1) { digitalWrite(_pin_en, PIN_GPS_EN_ACTIVE); } if (_pin_reset != -1) { digitalWrite(_pin_reset, !GPS_RESET_FORCE); } } void reset() override { if (_pin_reset != -1) { digitalWrite(_pin_reset, GPS_RESET_FORCE); delay(10); digitalWrite(_pin_reset, !GPS_RESET_FORCE); } } void stop() override { if (_pin_en != -1) { digitalWrite(_pin_en, !PIN_GPS_EN_ACTIVE); } if (_pin_reset != -1) { digitalWrite(_pin_reset, GPS_RESET_FORCE); } release(); } bool isEnabled() override { // directly read the enable pin if present as gps can be // activated/deactivated outside of here ... if (_pin_en != -1) { return digitalRead(_pin_en) == PIN_GPS_EN_ACTIVE; } else { return true; // no enable so must be active } } void syncTime() override { nmea.clear(); LocationProvider::syncTime(); } long getLatitude() override { return nmea.getLatitude(); } long getLongitude() override { return nmea.getLongitude(); } long getAltitude() override { long alt = 0; nmea.getAltitude(alt); return alt; } long satellitesCount() override { return nmea.getNumSatellites(); } bool isValid() override { return nmea.isValid(); } long getTimestamp() override { DateTime dt(nmea.getYear(), nmea.getMonth(),nmea.getDay(),nmea.getHour(),nmea.getMinute(),nmea.getSecond()); return dt.unixtime(); } void sendSentence(const char *sentence) override { nmea.sendSentence(*_gps_serial, sentence); } void loop() override { while (_gps_serial->available()) { char c = _gps_serial->read(); #ifdef GPS_NMEA_DEBUG Serial.print(c); #endif nmea.process(c); } if (!isValid()) time_valid = 0; if (millis() > next_check) { next_check = millis() + 1000; // Re-enable time sync periodically when GPS has valid fix if (!_time_sync_needed && _clock != NULL && (millis() - _last_time_sync) > TIME_SYNC_INTERVAL) { _time_sync_needed = true; } if (_time_sync_needed && time_valid > 2) { if (_clock != NULL) { _clock->setCurrentTime(getTimestamp()); _time_sync_needed = false; _last_time_sync = millis(); } } if (isValid()) { time_valid ++; } } } };