diff --git a/.cproject b/.cproject deleted file mode 100644 index 7cedfd6..0000000 --- a/.cproject +++ /dev/null @@ -1,57 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/.gitignore b/.gitignore index 7911b6b..0db0561 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,12 @@ +.metadata .DS_Store +.ino.cpp +.project +.cproject +.settings +Release +Debug +libraries +core *.bak *-bak -*.old -build -sdkconfig diff --git a/.gitmodules b/.gitmodules index 6879e83..e69de29 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,9 +0,0 @@ -[submodule "components/u8g2"] - path = components/u8g2 - url = https://github.com/olikraus/u8g2.git -[submodule "components/minmea/minmea"] - path = components/minmea/minmea - url = https://github.com/kosma/minmea.git -[submodule "components/esp32-snippets/esp32-snippets"] - path = components/esp32-snippets/esp32-snippets - url = https://github.com/nkolban/esp32-snippets.git diff --git a/.project b/.project deleted file mode 100644 index 6f9dbee..0000000 --- a/.project +++ /dev/null @@ -1,27 +0,0 @@ - - - esp_gps_ntp - - - - - - org.eclipse.cdt.managedbuilder.core.genmakebuilder - clean,full,incremental, - - - - - org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder - full,incremental, - - - - - - org.eclipse.cdt.core.cnature - org.eclipse.cdt.core.ccnature - org.eclipse.cdt.managedbuilder.core.managedBuildNature - org.eclipse.cdt.managedbuilder.core.ScannerConfigNature - - diff --git a/.settings/language.settings.xml b/.settings/language.settings.xml deleted file mode 100644 index 8a0f91f..0000000 --- a/.settings/language.settings.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/.settings/org.eclipse.cdt.core.prefs b/.settings/org.eclipse.cdt.core.prefs deleted file mode 100644 index 530eb67..0000000 --- a/.settings/org.eclipse.cdt.core.prefs +++ /dev/null @@ -1,12 +0,0 @@ -eclipse.preferences.version=1 -environment/project/cdt.managedbuild.toolchain.gnu.cross.base.285975342/BATCH_BUILD/delimiter=\: -environment/project/cdt.managedbuild.toolchain.gnu.cross.base.285975342/BATCH_BUILD/operation=append -environment/project/cdt.managedbuild.toolchain.gnu.cross.base.285975342/BATCH_BUILD/value=1 -environment/project/cdt.managedbuild.toolchain.gnu.cross.base.285975342/IDF_PATH/delimiter=\: -environment/project/cdt.managedbuild.toolchain.gnu.cross.base.285975342/IDF_PATH/operation=append -environment/project/cdt.managedbuild.toolchain.gnu.cross.base.285975342/IDF_PATH/value=${HOME}/esp/esp-idf -environment/project/cdt.managedbuild.toolchain.gnu.cross.base.285975342/PATH/delimiter=\: -environment/project/cdt.managedbuild.toolchain.gnu.cross.base.285975342/PATH/operation=replace -environment/project/cdt.managedbuild.toolchain.gnu.cross.base.285975342/PATH/value=/bin\:/usr/bin\:/usr/sbin\:/sbin\:${HOME}/esp/xtensa-esp32-elf/bin -environment/project/cdt.managedbuild.toolchain.gnu.cross.base.285975342/append=true -environment/project/cdt.managedbuild.toolchain.gnu.cross.base.285975342/appendContributed=true diff --git a/ESPNTPServer.cpp b/ESPNTPServer.cpp new file mode 100644 index 0000000..d42e271 --- /dev/null +++ b/ESPNTPServer.cpp @@ -0,0 +1,170 @@ +/* + * ESPNTPServer.cpp + * + * Copyright 2017 Christopher B. Liebman + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * + * Created on: Oct 29, 2017 + * Author: liebman + */ + +#include "ESPNTPServer.h" +#include // htonl() & ntohl() +#include "GPS.h" +#include "NTP.h" + +//#include "Logger.h" + +GPS gps(Serial, SYNC_PIN); +NTP ntp(gps); + +#if defined(USE_OLED_DISPLAY) +SSD1306Wire display(0x3c, SDA_PIN, SCL_PIN); +#endif + +#include "Log.h" + +const char* SETUP_TAG = "setup"; +const char* LOOP_TAG = "loop"; + +void logTimeFirst(Print* print) +{ + static struct timeval tv; + gps.getTime(&tv); + static struct tm tm; + gmtime_r(&tv.tv_sec, &tm); + + print->printf("%04d/%02d/%02d %02d:%02d:%02d.%06ld ", + tm.tm_year+1900, + tm.tm_mon+1, + tm.tm_mday, + tm.tm_hour, + tm.tm_min, + tm.tm_sec, + tv.tv_usec); +} + +void setup() +{ + delay(5000); // delay for IDE to re-open serial + Serial1.begin(115200); + logger.setSize(256); + logger.setPrint(&Serial1); + logger.setPreFunc(&logTimeFirst); + logger.info(SETUP_TAG, "\nStartup!"); + + +#if !defined(USE_NO_WIFI) + logger.info(SETUP_TAG, "initializing wifi"); + WiFiManager wifi; + wifi.setDebugStream(Serial1); + //wifi.setDebugOutput(false); + String ssid = "SynchroClock" + String(ESP.getChipId()); + wifi.autoConnect(ssid.c_str(), NULL); +#endif + +#if defined(USE_OLED_DISPLAY) + logger.info(SETUP_TAG, "initializing display"); + if (!display.init()) + { + logger.info(SETUP_TAG, "display.init() failed!"); + } + display.flipScreenVertically(); + display.setFont(ArialMT_Plain_10); +#endif + + logger.info(SETUP_TAG, "initializing serial for GPS"); + Serial.begin(9600); + Serial.swap(); + + logger.info(SETUP_TAG, "initializing GPS"); + gps.begin(); + + logger.info(SETUP_TAG, "initializing NTP"); + ntp.begin(); +} + +void loop() +{ + static uint32_t min_loop; + static uint32_t max_loop; + static uint32_t last_loop; + uint32_t start_loop = millis(); + + gps.process(); + + static time_t last_seconds; + struct timeval tv; + gps.getTime(&tv); + if (tv.tv_sec != last_seconds) + { + struct tm tm; + gmtime_r(&tv.tv_sec, &tm); + char ts[64]; + snprintf(ts, 63, "%04d/%02d/%02d %02d:%02d:%02d.%06ld", + tm.tm_year+1900, + tm.tm_mon+1, + tm.tm_mday, + tm.tm_hour, + tm.tm_min, + tm.tm_sec, + tv.tv_usec); + if (tv.tv_sec != last_seconds && ((tv.tv_sec % 60) == 0 || gps.getValidDelay())) + { + logger.info("loop", "jitter:%lu valid_count:%lu valid:%s gpsvalid:%s numsat:%d heap:%ld valid_delay:%d", + gps.getJitter(), + gps.getValidCount(), + gps.isValid() ? "true" : "false", + gps.isGPSValid() ? "true" : "false", + gps.getSatelliteCount(), + ESP.getFreeHeap(), + gps.getValidDelay()); + } + + if (tv.tv_sec < last_seconds) + { + logger.warning(LOOP_TAG, "%s: OOPS: time went backwards: last:%lu now:%lu\n", ts, last_seconds, tv.tv_sec); + } + +#if defined(USE_OLED_DISPLAY) + // + // Update the display + // + display.clear(); + display.setTextAlignment(TEXT_ALIGN_LEFT); + display.setFont(ArialMT_Plain_10); + display.drawString(0, 0, ts); + display.drawString(0, 10, "Address: "+WiFi.localIP().toString()); + display.drawString(0, 20, "Sat Count: " + String(gps.getSatelliteCount())); + display.drawString(0, 30, "Requests: " + String(ntp.getReqCount())); + display.drawString(0, 40, "Responses: " + String(ntp.getRspCount())); + snprintf(ts, 63, "loop: %d / %d / %d", last_loop, min_loop, max_loop); + display.drawString(0, 50, String(ts)); + // write the buffer to the display + display.display(); +#endif + } + + last_seconds = tv.tv_sec; + last_loop = millis() - start_loop; + if (min_loop == 0 || last_loop < min_loop) + { + min_loop = last_loop; + } + if (max_loop == 0 || last_loop > max_loop) + { + max_loop = last_loop; + } +} diff --git a/ESPNTPServer.h b/ESPNTPServer.h new file mode 100644 index 0000000..d566ca1 --- /dev/null +++ b/ESPNTPServer.h @@ -0,0 +1,43 @@ +/* + * ESPNTPServer.h + * + * Copyright 2017 Christopher B. Liebman + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * + * Created on: Oct 29, 2017 + * Author: liebman + */ + +#define USE_OLED_DISPLAY +//#define USE_NO_WIFI + +#ifndef _ESPNTPServer_H_ +#define _ESPNTPServer_H_ +#include "Arduino.h" +#include "Ticker.h" +#if !defined(USE_NO_WIFI) +#include "WiFiManager.h" +#endif +#include + +#include "SSD1306Wire.h" + +// pin definitions +#define SYNC_PIN 14 // (GPIO14) pin tied to 1hz square wave from GPS +#define SDA_PIN 4 +#define SCL_PIN 5 + + +#endif /* _ESPNTPServer_H_ */ diff --git a/GPS.cpp b/GPS.cpp new file mode 100644 index 0000000..48c7840 --- /dev/null +++ b/GPS.cpp @@ -0,0 +1,289 @@ +/* + * GPS.cpp + * + * Copyright 2017 Christopher B. Liebman + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Created on: Feb 26, 2018 + * Author: chris.l + */ + +#include +#include "GPS.h" +#include +#include +#include + +#include "Log.h" +static const char* TAG = "GPS"; + +static std::function _pps; + +static ICACHE_RAM_ATTR void _pps_isr() +{ + if (_pps) + { + _pps(); + } +} + +static ICACHE_RAM_ATTR void _timer_handler(std::function *func) +{ + (*func)(); +} + +GPS::GPS(Stream& gps_stream, int pps_pin) : + _stream(gps_stream), + _nmea(_buffer, NMEA_BUFFER_SIZE), + _pps_timer(), + _seconds(0), + _valid_delay(0), + _valid_count(0), + _min_micros(0), + _max_micros(0), + _last_micros(0), + _timeouts(0), + _pps_pin(pps_pin), + _gps_valid(false), + _valid(false), + _nmea_late(true) +{ + _reason[0] = '\0'; +} + +GPS::~GPS() +{ + end(); +} + +void GPS::begin() +{ + PPS_TIMIMG_PIN_INIT(); + _pps = std::bind( &GPS::pps, this); + _invalidate = std::bind( &GPS::timeout, this); + _nmea_timeout = std::bind( &GPS::nmeaTimeout, this); + _pps_timer.attach_ms(VALID_TIMER_MS, _timer_handler, &_invalidate); + pinMode(_pps_pin, INPUT); + attachInterrupt(_pps_pin, _pps_isr, RISING); +} + +void GPS::end() +{ + _pps_timer.detach(); + detachInterrupt(_pps_pin); + _pps = nullptr; +} + +void GPS::getTime(struct timeval* tv) +{ + uint32_t cur_micros = micros(); + tv->tv_sec = _seconds; + tv->tv_usec = (uint32_t)(cur_micros - _last_micros); + + // + // if micros_delta is at or bigger than one second then + // use the max just under 1 second. + // + if (tv->tv_usec >= 1000000 || tv->tv_usec < 0) + { + tv->tv_usec = 999999; + } +} + +double GPS::getDispersion() +{ + return us2s(MAX(abs(MICROS_PER_SEC-_max_micros), abs(MICROS_PER_SEC-_min_micros))); +} + +void GPS::process() +{ + if (_reason[0] != '\0') + { + logger.warning(TAG, "REASON: %s", _reason); + _reason[0] = '\0'; + } + + while (_stream.available() > 0) + { + if (_nmea.process(_stream.read())) + { + struct timeval tv; + getTime(&tv); + logger.debug(TAG, "'%s'", _nmea.getSentence()); + + if (_nmea.isValid() && _nmea.getNumSatellites() >= 4) + { + // + // if it was a GGA and its valid then check and maybe update the time + // + const char * id = _nmea.getMessageID(); + if (_nmea.getYear() > 2000 && strcmp("RMC", id) == 0) + { + struct tm tm; + tm.tm_year = _nmea.getYear() - 1900; + tm.tm_mon = _nmea.getMonth() - 1; + tm.tm_mday = _nmea.getDay(); + tm.tm_hour = _nmea.getHour(); + tm.tm_min = _nmea.getMinute(); + tm.tm_sec = _nmea.getSecond(); + time_t new_seconds = mktime(&tm); + + // + // we only update seconds if the message arrived in the last half of a second, + // if its in the first half then its most likely delayed from the previous second. + time_t old_seconds = _seconds; + if (old_seconds != new_seconds) + { + if (!_nmea_late) + { + _seconds = new_seconds; + logger.info(TAG, "adjusting seconds from %lu to %lu from:'%s'", old_seconds, new_seconds, _nmea.getSentence()); + } + else + { + logger.warning(TAG, "ignoring late NMEA time: '%s'",_nmea.getSentence()); + } + } + + _nmea_late = false; + _nmea_timer.attach_ms(NMEA_TIMER_MS, _timer_handler, &_nmea_timeout); + + // + // if gps was not valid, it is now + // + if (!_gps_valid) + { + _valid_delay = VALID_DELAY; + _gps_valid = true; + logger.warning(TAG, "GPS valid!"); + } + } + } + else /* nmea not valid or sat count < 4 */ + { + if (_gps_valid) + { + invalidate("NMEA:%s SATS:%d from: '%s'", + _nmea.isValid() ? "valid" : "invalid", + _nmea.getNumSatellites(), _nmea.getSentence()); + } + } + } + } +} + +void GPS::nmeaTimeout() +{ + _nmea_late = true; +} + +/* + * Mark as not valid + */ +void ICACHE_RAM_ATTR GPS::timeout() +{ + if (_valid) + { + invalidate("timeout!"); + } + _pps_timer.attach_ms(VALID_TIMER_MS, _timer_handler, &_invalidate); +} + +/* + * Mark as not valid + */ +void ICACHE_RAM_ATTR GPS::invalidate(const char* fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + vsnprintf(_reason, REASON_SIZE-1, fmt, ap); + _reason[REASON_SIZE-1] = '\0'; + va_end(ap); + + _valid = false; + _gps_valid = false; + _last_micros = 0; +} + +/* + * Interrupt handler for a PPS (Pulse Per Second) signal from GPS module. + */ +void ICACHE_RAM_ATTR GPS::pps() +{ + PPS_TIMING_PIN_ON(); + + uint32_t cur_micros = micros(); + (void)cur_micros; + + // + // don't trust PPS if GPS is not valid. + // + if (!_gps_valid) + { + PPS_TIMING_PIN_OFF(); + return; + } + + // + // if we are still counting down then keep waiting + // + if (_valid_delay) + { + --_valid_delay; + if (_valid_delay == 0) + { + // clear stats and mark us valid + _min_micros = 0; + _max_micros = 0; + _valid = true; + ++_valid_count; + } + } + + // + // restart the validity timer, if it runs out we invalidate our data. + // + _pps_timer.attach_ms(VALID_TIMER_MS, &_timer_handler, &_invalidate); + + // + // increment seconds + // + _seconds += 1; + + // + // the first time around we just initialize the last value + // + if (_last_micros == 0) + { + _last_micros = cur_micros; + PPS_TIMING_PIN_OFF(); + return; + } + + uint32_t micros_count = cur_micros - _last_micros; + _last_micros = cur_micros; + + if (_min_micros == 0 || micros_count < _min_micros) + { + _min_micros = micros_count; + } + + if (micros_count > _max_micros) + { + _max_micros = micros_count; + } + + PPS_TIMING_PIN_OFF(); +} + diff --git a/GPS.h b/GPS.h new file mode 100644 index 0000000..a9c3778 --- /dev/null +++ b/GPS.h @@ -0,0 +1,104 @@ +/* + * GPS.h + * + * Copyright 2017 Christopher B. Liebman + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Created on: Feb 26, 2018 + * Author: chris.l + */ + +#ifndef GPS_H_ +#define GPS_H_ +#include "Arduino.h" +#include "MicroNMEA.h" +#include "Ticker.h" + +#define REASON_SIZE 128 +#define NMEA_BUFFER_SIZE 128 +#define PPS_TIMING_PIN 12 // (GPIO12) if defined PPS interrupt will make high during processing +#define VALID_DELAY 120 // delay (seconds) from gps valid to valid +#define VALID_TIMER_MS 1001 // if this timer expires we invalidate! +#define NMEA_TIMER_MS 1100 // if this timer expires we mark NMEA time late + +#define MICROS_PER_SEC 1000000 + +#define us2s(x) (((double)x)/(double)MICROS_PER_SEC) // microseconds to seconds + +// simple versions - we don't worry about side effects +#define MAX(a, b) ((a) < (b) ? (b) : (a)) +#define MIN(a, b) ((a) < (b) ? (a) : (b)) + +#if defined(PPS_TIMING_PIN) +#define PPS_TIMIMG_PIN_INIT() {digitalWrite(PPS_TIMING_PIN, LOW); pinMode(PPS_TIMING_PIN, OUTPUT);} +#define PPS_TIMING_PIN_ON() digitalWrite(PPS_TIMING_PIN, HIGH) +#define PPS_TIMING_PIN_OFF() digitalWrite(PPS_TIMING_PIN, LOW) +#else +#define PPS_TIMIMG_PIN_INIT() +#define PPS_TIMING_PIN_ON() +#define PPS_TIMING_PIN_OFF() +#endif + +class GPS +{ +public: + GPS(Stream& gps_serial, int pps_pin); + virtual ~GPS(); + + void begin(); + void process(); + void end(); + + bool isValid() { return _valid; } + bool isGPSValid() { return _gps_valid; } + uint32_t getJitter() { return _max_micros - _min_micros; } + uint32_t getValidCount() { return _valid_count; } + uint32_t getValidDelay() { return _valid_delay; } + uint8_t getSatelliteCount() { return _nmea.getNumSatellites(); } + time_t getSeconds() { return _seconds; } + void getTime(struct timeval* tv); + double getDispersion(); + + // we don't allow copying this guy! + GPS(const GPS&) = delete; + GPS& operator=(const GPS&) = delete; + +private: + Stream& _stream; + char _buffer[NMEA_BUFFER_SIZE]; + MicroNMEA _nmea; + Ticker _pps_timer; + Ticker _nmea_timer; + std::function _invalidate; + std::function _nmea_timeout; + volatile time_t _seconds; + volatile uint32_t _valid_delay; // delay (seconds) from gps_valid until we thing we are valid + volatile uint32_t _valid_count; // number of times we have gone valid + volatile uint32_t _min_micros; + volatile uint32_t _max_micros; + volatile uint32_t _last_micros; + volatile uint32_t _timeouts; + + uint8_t _pps_pin; + bool _gps_valid; + volatile bool _valid; + volatile bool _nmea_late; + char _reason[REASON_SIZE]; + void pps(); // interrupt handler + void timeout(); + void invalidate(const char* fmt, ...); + void nmeaTimeout(); +}; + +#endif /* GPS_H_ */ diff --git a/Log.cpp b/Log.cpp new file mode 100644 index 0000000..d756120 --- /dev/null +++ b/Log.cpp @@ -0,0 +1,160 @@ +/* + * Log.cpp + * + * Copyright 2017 Christopher B. Liebman + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Created on: Mar 2, 2018 + * Author: chris.l + */ +#include "Arduino.h" +#include "Log.h" + +static const char* level_names[LOG_LEVEL_MAX] = { + "NONE", + "ERROR", + "WARN", + "INFO", + "DEBUG", + "TRACE" +}; + +Log& Log::getLog() +{ + static Log log; + return log; +} + +Log::Log() : + _level(LOG_LEVEL_INFO), + _size(LOG_MAX_SIZE), + _buffer(nullptr), + _print(nullptr) +{ +} + +Log::~Log() +{ + if (_buffer != nullptr) + { + delete[] _buffer; + } +} + +void Log::setSize(size_t size) +{ + _size = size; + if (_buffer) + { + delete[] _buffer; + _buffer = nullptr; + } +} + +void Log::setLevel(LogLevel level) +{ + _level = level; +} + +void Log::setLevel(const char* name, LogLevel level) +{ + _levels[name] = level; +} + +void Log::setPrint(Print *print) +{ + _print = print; +} +void Log::setPreFunc(std::function func) +{ + pre_func = func; +} + +void Log::error(const char* name, const char* fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + print(name, LOG_LEVEL_ERROR, fmt, ap); + va_end(ap); +} + +void Log::warning(const char* name, const char* fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + print(name, LOG_LEVEL_WARNING, fmt, ap); + va_end(ap); +} + +void Log::info(const char* name, const char* fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + print(name, LOG_LEVEL_INFO, fmt, ap); + va_end(ap); +} + +void Log::debug(const char* name, const char* fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + print(name, LOG_LEVEL_DEBUG, fmt, ap); + va_end(ap); +} + +void Log::trace(const char* name, const char* fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + print(name, LOG_LEVEL_TRACE, fmt, ap); + va_end(ap); +} + +void Log::print(const char* name, LogLevel level, const char* fmt, va_list ap) +{ + LogLevel limit = _level; + if (_levels.find(name) != _levels.end()) + { + limit = _levels[name]; + } + + if (level > limit) + { + return; + } + + // + // allocate the buffer on first use + // + if (_buffer == nullptr) + { + _buffer = new char[_size]; + } + + if (_print == nullptr) + { + _print = &Serial; + } + + vsnprintf(_buffer, _size-1, fmt, ap); + + if (pre_func) + { + pre_func(_print); + } + + _print->printf("%s %5s %s\n", name, level_names[level], _buffer); +} + +Log& logger = Log::getLog(); diff --git a/Log.h b/Log.h new file mode 100644 index 0000000..3fc5ce4 --- /dev/null +++ b/Log.h @@ -0,0 +1,82 @@ +/* + * Log.h + * + * Copyright 2017 Christopher B. Liebman + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Created on: Mar 2, 2018 + * Author: chris.l + */ + +#ifndef LOG_H_ +#define LOG_H_ + +#include "Print.h" +#include +#include +#include + +typedef enum log_level +{ + LOG_LEVEL_NONE = 0, + LOG_LEVEL_ERROR, + LOG_LEVEL_WARNING, + LOG_LEVEL_INFO, + LOG_LEVEL_DEBUG, + LOG_LEVEL_TRACE, + LOG_LEVEL_MAX, +} LogLevel; + +#define LOG_MAX_SIZE 80 +#define LOG_LEVEL_DEFAULT LOG_LEVEL_INFO + +class Log +{ +public: + static Log& getLog(); + + void setSize(size_t size); + void setLevel(LogLevel level); + void setLevel(const char* name, LogLevel level); + void setPrint(Print *print); + void setPreFunc(std::function func); + + void error(const char* name, const char* fmt, ...); + void warning(const char* name, const char* fmt, ...); + void info(const char* name, const char* fmt, ...); + void debug(const char* name, const char* fmt, ...); + void trace(const char* name, const char* fmt, ...); + + // we don't allow copying this guy! + Log(const Log&) = delete; + Log& operator=(const Log&) = delete; + +private: + LogLevel _level; // default log level + size_t _size; + char* _buffer; + std::map _levels; + Print* _print; + + std::function pre_func; + + + Log(); + virtual ~Log(); + void print(const char* name, LogLevel level, const char*fmt, va_list ap); +}; + +extern Log& logger; + +#endif /* LOG_H_ */ diff --git a/Makefile b/Makefile deleted file mode 100644 index 30c4733..0000000 --- a/Makefile +++ /dev/null @@ -1,9 +0,0 @@ -# -# This is a project Makefile. It is assumed the directory this Makefile resides in is a -# project subdirectory. -# - -PROJECT_NAME := esp-gps-ntp - -include $(IDF_PATH)/make/project.mk - diff --git a/NTP.cpp b/NTP.cpp new file mode 100644 index 0000000..f5a6712 --- /dev/null +++ b/NTP.cpp @@ -0,0 +1,221 @@ +/* + * NTP.cpp + * + * Copyright 2017 Christopher B. Liebman + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Created on: Feb 27, 2018 + * Author: chris.l + */ + +#include +#include // htonl() & ntohl() + +#include "NTP.h" + +#include "Log.h" +static const char* TAG = "NTP"; + +//#define NTP_PACKET_DEBUG + +#define NTP_PORT 123 +#define PRECISION_COUNT 10000 + +typedef struct ntp_packet +{ + uint8_t flags; + uint8_t stratum; + uint8_t poll; + int8_t precision; + uint32_t delay; + uint32_t dispersion; + uint8_t ref_id[4]; + NTPTime ref_time; + NTPTime orig_time; + NTPTime recv_time; + NTPTime xmit_time; +} NTPPacket; + +#define LI_NONE 0 +#define LI_SIXTY_ONE 1 +#define LI_FIFTY_NINE 2 +#define LI_NOSYNC 3 + +#define MODE_RESERVED 0 +#define MODE_ACTIVE 1 +#define MODE_PASSIVE 2 +#define MODE_CLIENT 3 +#define MODE_SERVER 4 +#define MODE_BROADCAST 5 +#define MODE_CONTROL 6 +#define MODE_PRIVATE 7 + +#define NTP_VERSION 4 + +#define REF_ID "PPS " // "GPS " when we have one! + +#define setLI(value) ((value&0x03)<<6) +#define setVERS(value) ((value&0x07)<<3) +#define setMODE(value) ((value&0x07)) + +#define getLI(value) ((value>>6)&0x03) +#define getVERS(value) ((value>>3)&0x07) +#define getMODE(value) (value&0x07) + +#define SEVENTY_YEARS 2208988800L +#define toEPOCH(t) ((uint32_t)t-SEVENTY_YEARS) +#define toNTP(t) ((uint32_t)t+SEVENTY_YEARS) + +#ifdef NTP_PACKET_DEBUG +#include +char* timestr(long int t) +{ + t = toEPOCH(t); + return ctime(&t); +} + +void dumpNTPPacket(NTPPacket* ntp) +{ + dbprintf("size: %u\n", sizeof(*ntp)); + dbprintf("firstbyte: 0x%02x\n", *(uint8_t*)ntp); + dbprintf("li: %u\n", getLI(ntp->flags)); + dbprintf("version: %u\n", getVERS(ntp->flags)); + dbprintf("mode: %u\n", getMODE(ntp->flags)); + dbprintf("stratum: %u\n", ntp->stratum); + dbprintf("poll: %u\n", ntp->poll); + dbprintf("precision: %d\n", ntp->precision); + dbprintf("delay: %u\n", ntp->delay); + dbprintf("dispersion: %u\n", ntp->dispersion); + dbprintf("ref_id: %02x:%02x:%02x:%02x\n", ntp->ref_id[0], ntp->ref_id[1], ntp->ref_id[2], ntp->ref_id[3]); + dbprintf("ref_time: %08x:%08x\n", ntp->ref_time.seconds, ntp->ref_time.fraction); + dbprintf("orig_time: %08x:%08x\n", ntp->orig_time.seconds, ntp->orig_time.fraction); + dbprintf("recv_time: %08x:%08x\n", ntp->recv_time.seconds, ntp->recv_time.fraction); + dbprintf("xmit_time: %08x:%08x\n", ntp->xmit_time.seconds, ntp->xmit_time.fraction); +} +#else +#define dumpNTPPacket(x) +#endif + +NTP::NTP(GPS& gps) : + _gps(gps), + _udp(), + _req_count(0), + _rsp_count(0), + _precision(0) +{ +} + +NTP::~NTP() +{ +} + +void NTP::begin() +{ + _precision = computePrecision(); + while (!_udp.listen(NTP_PORT)) + { + logger.error(TAG, "failed to listen on port %d! Will retry in a bit...", NTP_PORT); + delay(1000); + logger.warning(TAG, "setup: retrying!"); + } + using namespace std::placeholders; // for _1, _2, _3... + + _udp.onPacket(std::bind( &NTP::ntp, this, _1)); +} + +int8_t NTP::computePrecision() +{ + NTPTime t; + unsigned long start = micros(); + for (int i = 0; i < PRECISION_COUNT; ++i) + { + getNTPTime(&t); + } + unsigned long end = micros(); + double total = (double)(end - start) / 1000000.0; + double time = total / PRECISION_COUNT; + double prec = log2(time); + logger.info(TAG, "computePrecision: total:%f time:%f prec:%f (%d)", total, time, prec, (int8_t)prec); + return (int8_t)prec; +} + +void NTP::getNTPTime(NTPTime *time) +{ + struct timeval tv; + _gps.getTime(&tv); + time->seconds = toNTP(tv.tv_sec); + + double percent = us2s(tv.tv_usec); + time->fraction = (uint32_t)(percent * (double)4294967296L); +} + +void NTP::ntp(AsyncUDPPacket& aup) +{ + ++_req_count; + NTPPacket ntp; + NTPTime recv_time; + getNTPTime(&recv_time); + if (aup.length() != sizeof(NTPPacket)) + { + logger.warning(TAG, "recievePacket: ignoring packet with bad length: %d < %d", aup.length(), sizeof(NTPPacket)); + return; + } + + if (!_gps.isValid()) + { + logger.warning(TAG, "recievePacket: GPS data not valid!"); + return; + } + + memcpy(&ntp, aup.data(), sizeof(ntp)); + ntp.delay = ntohl(ntp.delay); + ntp.dispersion = ntohl(ntp.dispersion); + ntp.orig_time.seconds = ntohl(ntp.orig_time.seconds); + ntp.orig_time.fraction = ntohl(ntp.orig_time.fraction); + ntp.ref_time.seconds = ntohl(ntp.ref_time.seconds); + ntp.ref_time.fraction = ntohl(ntp.ref_time.fraction); + ntp.recv_time.seconds = ntohl(ntp.recv_time.seconds); + ntp.recv_time.fraction = ntohl(ntp.recv_time.fraction); + ntp.xmit_time.seconds = ntohl(ntp.xmit_time.seconds); + ntp.xmit_time.fraction = ntohl(ntp.xmit_time.fraction); + dumpNTPPacket(&ntp); + + // + // Build the response + // + ntp.flags = setLI(LI_NONE) | setVERS(NTP_VERSION) | setMODE(MODE_SERVER); + ntp.stratum = 1; + ntp.precision = _precision; + // TODO: compute actual root delay, and root dispersion + ntp.delay = 1; //(uint32)(0.000001 * 65536.0); + ntp.dispersion = 1; //(uint32_t)(_gps.getDispersion() * 65536.0); // TODO: pre-calculate this? + strncpy((char*)ntp.ref_id, REF_ID, sizeof(ntp.ref_id)); + ntp.orig_time = ntp.xmit_time; + ntp.recv_time = recv_time; + getNTPTime(&(ntp.ref_time)); + dumpNTPPacket(&ntp); + ntp.delay = htonl(ntp.delay); + ntp.dispersion = htonl(ntp.dispersion); + ntp.orig_time.seconds = htonl(ntp.orig_time.seconds); + ntp.orig_time.fraction = htonl(ntp.orig_time.fraction); + ntp.ref_time.seconds = htonl(ntp.ref_time.seconds); + ntp.ref_time.fraction = htonl(ntp.ref_time.fraction); + ntp.recv_time.seconds = htonl(ntp.recv_time.seconds); + ntp.recv_time.fraction = htonl(ntp.recv_time.fraction); + getNTPTime(&(ntp.xmit_time)); + ntp.xmit_time.seconds = htonl(ntp.xmit_time.seconds); + ntp.xmit_time.fraction = htonl(ntp.xmit_time.fraction); + aup.write((uint8_t*)&ntp, sizeof(ntp)); + ++_rsp_count; +} diff --git a/NTP.h b/NTP.h new file mode 100644 index 0000000..5eb7633 --- /dev/null +++ b/NTP.h @@ -0,0 +1,58 @@ +/* + * NTP.h + * + * Copyright 2017 Christopher B. Liebman + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Created on: Feb 27, 2018 + * Author: chris.l + */ + +#ifndef NTP_H_ +#define NTP_H_ + +#include "Arduino.h" +#include "ESPAsyncUDP.h" +#include "GPS.h" + +typedef struct ntp_time +{ + uint32_t seconds; + uint32_t fraction; +} NTPTime; + +class NTP +{ +public: + NTP(GPS& gps); + virtual ~NTP(); + + void begin(); + + uint32_t getReqCount() { return _req_count; } + uint32_t getRspCount() { return _rsp_count; } + +private: + GPS& _gps; + AsyncUDP _udp; + uint32_t _req_count; + uint32_t _rsp_count; + uint8_t _precision; + + void getNTPTime(NTPTime *time); + int8_t computePrecision(); + void ntp(AsyncUDPPacket& aup); +}; + +#endif /* NTP_H_ */ diff --git a/README.md b/README.md index 404c300..8d7adeb 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,6 @@ # ESPNTPServer -GPS fueled ESP32 based NTP Server +GPS fueled ESP8266 based NTP Server -GPS: Adafruit Untimate GPS - -This project has git submodules so please use --recursive when you clone! If you forget then run: `git submodule update --init`! After a `git pull` you should run `git submodule update --init --recursive` to make sure submodules are up to date. +GPS: any with standard NMEA sentences & a pulse per second signal.. Work in progress.... diff --git a/WireUtils.cpp b/WireUtils.cpp new file mode 100644 index 0000000..295c2fc --- /dev/null +++ b/WireUtils.cpp @@ -0,0 +1,112 @@ +/* + * WireUtils.cpp + * + * Copyright 2017 Christopher B. Liebman + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * + * Created on: May 26, 2017 + * Author: liebman + */ + +#include "WireUtils.h" + +#define DEBUG +#include "Logger.h" + +WireUtilsC::WireUtilsC() +{ +} + +/** + * This routine turns off the I2C bus and clears it + * on return SCA and SCL pins are tri-state inputs. + * You need to call Wire.begin() after this to re-enable I2C + * This routine does NOT use the Wire library at all. + * + * returns 0 if bus cleared + * 1 if SCL held low. + * 2 if SDA held low by slave clock stretch for > 2sec + * 3 if SDA held low after 20 clocks. + */ +int WireUtilsC::clearBus() +{ + dbprintln("WireUtils::ClearBus: attempting to clean i2c bus"); +#if defined(TWCR) && defined(TWEN) + TWCR &= ~(_BV(TWEN)); //Disable the Atmel 2-Wire interface so we can control the SDA and SCL pins directly +#endif + pinMode(SDA, INPUT_PULLUP); // Make SDA (data) and SCL (clock) pins Inputs with pullup. + pinMode(SCL, INPUT_PULLUP); + + boolean SCL_LOW = (digitalRead(SCL) == LOW); // Check is SCL is Low. + if (SCL_LOW) + { //If it is held low Arduno cannot become the I2C master. + dbprintln("WireUtils::ClearBus: Failed! SCL held low!"); + return 1; //I2C bus error. Could not clear SCL clock line held low + } + + boolean SDA_LOW = (digitalRead(SDA) == LOW); // vi. Check SDA input. + int clockCount = 20; // > 2x9 clock + + while (SDA_LOW && (clockCount > 0)) + { // vii. If SDA is Low, + clockCount--; + // Note: I2C bus is open collector so do NOT drive SCL or SDA high. + pinMode(SCL, INPUT); // release SCL pullup so that when made output it will be LOW + pinMode(SCL, OUTPUT); // then clock SCL Low + delayMicroseconds(10); // for >5uS + pinMode(SCL, INPUT); // release SCL LOW + pinMode(SCL, INPUT_PULLUP); // turn on pullup resistors again + // do not force high as slave may be holding it low for clock stretching. + delayMicroseconds(10); // for >5uS + // The >5uS is so that even the slowest I2C devices are handled. + SCL_LOW = (digitalRead(SCL) == LOW); // Check if SCL is Low. + int counter = 20; + while (SCL_LOW && (counter > 0)) + { // loop waiting for SCL to become High only wait 2sec. + counter--; + delay(100); + SCL_LOW = (digitalRead(SCL) == LOW); + } + if (SCL_LOW) + { // still low after 2 sec error + dbprintln("WireUtils::ClearBus: Failed! SCL clock line held low by slave clock stretch for >2sec"); + return 2; // I2C bus error. Could not clear. SCL clock line held low by slave clock stretch for >2sec + } + SDA_LOW = (digitalRead(SDA) == LOW); // and check SDA input again and loop + } + if (SDA_LOW) + { // still low + dbprintln("WireUtils::ClearBus: Failed! SDA data line still held low"); + return 3; // I2C bus error. Could not clear. SDA data line held low + } + + // else pull SDA line low for Start or Repeated Start + pinMode(SDA, INPUT); // remove pullup. + pinMode(SDA, OUTPUT); // and then make it LOW i.e. send an I2C Start or Repeated start control. + // When there is only one I2C master a Start or Repeat Start has the same function as a Stop and clears the bus. + /// A Repeat Start is a Start occurring after a Start with no intervening Stop. + delayMicroseconds(10); // wait >5uS + pinMode(SDA, INPUT); // remove output low + pinMode(SDA, INPUT_PULLUP); // and make SDA high i.e. send I2C STOP control. + delayMicroseconds(10); // x. wait >5uS + pinMode(SDA, INPUT); // and reset pins as tri-state inputs which is the default state on reset + pinMode(SCL, INPUT); + dbprintln("WireUtils::ClearBus: Success!"); + return 0; // all ok +} + +// act like wire library (even though I don't like it) +WireUtilsC WireUtils = WireUtilsC(); + diff --git a/WireUtils.h b/WireUtils.h new file mode 100644 index 0000000..f20dd54 --- /dev/null +++ b/WireUtils.h @@ -0,0 +1,35 @@ +/* + * WireUtils.h + * + * Copyright 2017 Christopher B. Liebman + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * + * Created on: May 26, 2017 + * Author: liebman + */ + +#ifndef WIREUTILS_H_ +#define WIREUTILS_H_ +#include + +class WireUtilsC { +public: + WireUtilsC(); + int clearBus(); +}; + +extern WireUtilsC WireUtils; + +#endif /* WIREUTILS_H_ */ diff --git a/components/esp32-snippets/component.mk b/components/esp32-snippets/component.mk deleted file mode 100644 index 589ab8e..0000000 --- a/components/esp32-snippets/component.mk +++ /dev/null @@ -1,3 +0,0 @@ -COMPONENT_OBJS := esp32-snippets/hardware/displays/U8G2/u8g2_esp32_hal.o -COMPONENT_SRCDIRS := esp32-snippets/hardware/displays/U8G2 -COMPONENT_ADD_INCLUDEDIRS := esp32-snippets/hardware/displays/U8G2 diff --git a/components/esp32-snippets/esp32-snippets b/components/esp32-snippets/esp32-snippets deleted file mode 160000 index b1b56c7..0000000 --- a/components/esp32-snippets/esp32-snippets +++ /dev/null @@ -1 +0,0 @@ -Subproject commit b1b56c7ef0b3cdfc6cbd1b109b2b24c48c8c52e2 diff --git a/components/minmea/component.mk b/components/minmea/component.mk deleted file mode 100644 index 5698cf7..0000000 --- a/components/minmea/component.mk +++ /dev/null @@ -1,4 +0,0 @@ -COMPONENT_OBJS := minmea/minmea.o -COMPONENT_SRCDIRS := minmea -COMPONENT_ADD_INCLUDEDIRS := minmea -CPPFLAGS += -Dtimegm=mktime diff --git a/components/minmea/minmea b/components/minmea/minmea deleted file mode 160000 index cd27e72..0000000 --- a/components/minmea/minmea +++ /dev/null @@ -1 +0,0 @@ -Subproject commit cd27e72970cd50b46e9c95c1c0076a49c1ceaf33 diff --git a/components/u8g2 b/components/u8g2 deleted file mode 160000 index ea7dbc4..0000000 --- a/components/u8g2 +++ /dev/null @@ -1 +0,0 @@ -Subproject commit ea7dbc407cd68419322da57cdbdfc00521c86458 diff --git a/main/Kconfig.projbuild b/main/Kconfig.projbuild deleted file mode 100644 index 0d84a3a..0000000 --- a/main/Kconfig.projbuild +++ /dev/null @@ -1,162 +0,0 @@ -menu "ESP GPS NTP Server Configuration" - -choice WPS_TYPE - prompt "WPS mode" - default WPS_TYPE_PBC - help - WPS type for the esp32 to use. - -config WPS_TYPE_PBC - bool "PBC" -config WPS_TYPE_PIN - bool "PIN" -config WPS_TYPE_DISABLE - bool "disable" -endchoice - -choice APP_LOG_LEVEL - prompt "APP Log Level" - default APP_LOG_LEVEL_INFO - help - Logging level for APP main - -config APP_LOG_LEVEL_NONE - bool "No output" -config APP_LOG_LEVEL_ERROR - bool "Error" -config APP_LOG_LEVEL_WARN - bool "Warning" -config APP_LOG_LEVEL_INFO - bool "Info" -config APP_LOG_LEVEL_DEBUG - bool "Debug" -config APP_LOG_LEVEL_VERBOSE - bool "Verbose" -endchoice - -config APP_LOG_LEVEL - int - default 0 if APP_LOG_LEVEL_NONE - default 1 if APP_LOG_LEVEL_ERROR - default 2 if APP_LOG_LEVEL_WARN - default 3 if APP_LOG_LEVEL_INFO - default 4 if APP_LOG_LEVEL_DEBUG - default 5 if APP_LOG_LEVEL_VERBOSE - - -choice GPS_LOG_LEVEL - prompt "GPS Log Level" - default GPS_LOG_LEVEL_INFO - help - Logging level for GPS task - -config GPS_LOG_LEVEL_NONE - bool "No output" -config GPS_LOG_LEVEL_ERROR - bool "Error" -config GPS_LOG_LEVEL_WARN - bool "Warning" -config GPS_LOG_LEVEL_INFO - bool "Info" -config GPS_LOG_LEVEL_DEBUG - bool "Debug" -config GPS_LOG_LEVEL_VERBOSE - bool "Verbose" -endchoice - -config GPS_LOG_LEVEL - int - default 0 if GPS_LOG_LEVEL_NONE - default 1 if GPS_LOG_LEVEL_ERROR - default 2 if GPS_LOG_LEVEL_WARN - default 3 if GPS_LOG_LEVEL_INFO - default 4 if GPS_LOG_LEVEL_DEBUG - default 5 if GPS_LOG_LEVEL_VERBOSE - -choice NTP_LOG_LEVEL - prompt "NTP Log Level" - default NTP_LOG_LEVEL_INFO - help - Logging level for NTP task - -config NTP_LOG_LEVEL_NONE - bool "No output" -config NTP_LOG_LEVEL_ERROR - bool "Error" -config NTP_LOG_LEVEL_WARN - bool "Warning" -config NTP_LOG_LEVEL_INFO - bool "Info" -config NTP_LOG_LEVEL_DEBUG - bool "Debug" -config NTP_LOG_LEVEL_VERBOSE - bool "Verbose" -endchoice - -config NTP_LOG_LEVEL - int - default 0 if NTP_LOG_LEVEL_NONE - default 1 if NTP_LOG_LEVEL_ERROR - default 2 if NTP_LOG_LEVEL_WARN - default 3 if NTP_LOG_LEVEL_INFO - default 4 if NTP_LOG_LEVEL_DEBUG - default 5 if NTP_LOG_LEVEL_VERBOSE - -choice DISPLAY_LOG_LEVEL - prompt "DISPLAY Log Level" - default DISPLAY_LOG_LEVEL_INFO - help - Logging level for DISPLAY task - -config DISPLAY_LOG_LEVEL_NONE - bool "No output" -config DISPLAY_LOG_LEVEL_ERROR - bool "Error" -config DISPLAY_LOG_LEVEL_WARN - bool "Warning" -config DISPLAY_LOG_LEVEL_INFO - bool "Info" -config DISPLAY_LOG_LEVEL_DEBUG - bool "Debug" -config DISPLAY_LOG_LEVEL_VERBOSE - bool "Verbose" -endchoice - -config DISPLAY_LOG_LEVEL - int - default 0 if DISPLAY_LOG_LEVEL_NONE - default 1 if DISPLAY_LOG_LEVEL_ERROR - default 2 if DISPLAY_LOG_LEVEL_WARN - default 3 if DISPLAY_LOG_LEVEL_INFO - default 4 if DISPLAY_LOG_LEVEL_DEBUG - default 5 if DISPLAY_LOG_LEVEL_VERBOSE - -choice U8G2_HAL_LOG_LEVEL - prompt "U8G2_HAL Log Level" - default U8G2_HAL_LOG_LEVEL_INFO - help - Logging level for U8G2_HAL implementation - -config U8G2_HAL_LOG_LEVEL_NONE - bool "No output" -config U8G2_HAL_LOG_LEVEL_ERROR - bool "Error" -config U8G2_HAL_LOG_LEVEL_WARN - bool "Warning" -config U8G2_HAL_LOG_LEVEL_INFO - bool "Info" -config U8G2_HAL_LOG_LEVEL_DEBUG - bool "Debug" -config U8G2_HAL_LOG_LEVEL_VERBOSE - bool "Verbose" -endchoice - -config U8G2_HAL_LOG_LEVEL - int - default 0 if U8G2_HAL_LOG_LEVEL_NONE - default 1 if U8G2_HAL_LOG_LEVEL_ERROR - default 2 if U8G2_HAL_LOG_LEVEL_WARN - default 3 if U8G2_HAL_LOG_LEVEL_INFO - default 4 if U8G2_HAL_LOG_LEVEL_DEBUG - default 5 if U8G2_HAL_LOG_LEVEL_VERBOSE -endmenu diff --git a/main/component.mk b/main/component.mk deleted file mode 100644 index 20c0e6b..0000000 --- a/main/component.mk +++ /dev/null @@ -1,8 +0,0 @@ -# -# "main" pseudo-component makefile. -# -# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) - -COMPONENT_ADD_INCLUDEDIRS := . - -COMPONENT_SRCDIRS := $(COMPONENT_ADD_INCLUDEDIRS) diff --git a/main/display_task.c b/main/display_task.c deleted file mode 100644 index fe0d8e6..0000000 --- a/main/display_task.c +++ /dev/null @@ -1,133 +0,0 @@ -/* - * display_task.c - * - * Created on: Jan 21, 2018 - * Author: chris.l - */ - -#include "esp_gps_ntp.h" -#include "freertos/queue.h" -#include "u8g2.h" -#include "u8g2_esp32_hal.h" -#include -#include -#include "tcpip_adapter.h" - -static const char *TAG = "DSP"; -static xQueueHandle dsp_queue = NULL; -static u8g2_t dsp; - -void IRAM_ATTR triggerDisplay() -{ - BaseType_t xHigherPriorityTaskWoken = pdFALSE; - bool value = true; - - if (dsp_queue != NULL) - { - xQueueSendFromISR(dsp_queue, &value, &xHigherPriorityTaskWoken ); - } -} - -static void dsp_task() -{ - GPSTaskStatus gps; - NTPTaskStatus ntp; - static long unsigned int duration; - static bool blink; - const int buf_size = 64; - char buf[buf_size]; - struct tm t; - tcpip_adapter_ip_info_t ip; - memset(&ip, 0, sizeof(tcpip_adapter_ip_info_t)); - bool value; - ESP_LOGI(TAG, "starting!"); - - dsp_queue = xQueueCreate(10, sizeof(value)); - - u8g2_esp32_hal_t u8g2_esp32_hal = U8G2_ESP32_HAL_DEFAULT; - u8g2_esp32_hal.sda = SDA_PIN; - u8g2_esp32_hal.scl = SCL_PIN; - u8g2_esp32_hal_init(u8g2_esp32_hal); - - u8g2_Setup_ssd1306_i2c_128x64_noname_f(&dsp, U8G2_R0, u8g2_esp32_i2c_byte_cb, u8g2_esp32_gpio_and_delay_cb); // init u8g2 structure - u8g2_SetI2CAddress(&dsp, 0x3c<<1); - ESP_LOGD(TAG, "initializing display"); - u8g2_InitDisplay(&dsp); // send init sequence to the display, display is in sleep mode after this, - ESP_LOGD(TAG, "waking display"); - u8g2_ClearDisplay(&dsp); - u8g2_SetPowerSave(&dsp, 0); // wake up display - u8g2_SetFont(&dsp, u8g2_font_timR08_tr); - u8g2_SetFontMode(&dsp, 0); - - while(1) - { - if (xQueueReceive(dsp_queue, &value, pdMS_TO_TICKS(10000))) - { - micros_t start = micros(); - getGPSTaskStatus(&gps); - getNTPTaskStatus(&ntp); - u8g2_ClearBuffer(&dsp); - gmtime_r(&gps.seconds, &t); - snprintf(buf, buf_size-1, "UTC: %04d/%02d/%02d %02d:%02d:%02d", t.tm_year+1900, t.tm_mon+1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec); - u8g2_DrawStr(&dsp, 0, 8, buf); - snprintf(buf, buf_size-1, "Req: %u", ntp.req_count); - u8g2_DrawStr(&dsp, 0, 16, buf); - snprintf(buf, buf_size-1, "Rsp: %u", ntp.rsp_count); - u8g2_DrawStr(&dsp, 64, 16, buf); - snprintf(buf, buf_size-1, "Sat: %d", gps.sat_count); - u8g2_DrawStr(&dsp, 0, 24, buf); - snprintf(buf, buf_size-1, "Fix: %d", gps.fix_quality); - u8g2_DrawStr(&dsp, 64, 24, buf); - snprintf(buf, buf_size, "Valid: %d", gps.valid_count); - u8g2_DrawStr(&dsp, 0, 32, buf); - snprintf(buf, buf_size, "Timeout: %d", gps.timeouts); - u8g2_DrawStr(&dsp, 64, 32, buf); - - if (gps.valid_in) - { - snprintf(buf, buf_size, "Valid In: %d", gps.valid_in); - u8g2_DrawStr(&dsp, 0, 40, buf); - } - - gmtime_r(&gps.valid_since, &t); - if (gps.valid) - { - snprintf(buf, buf_size-1, "Valid: %04d/%02d/%02d %02d:%02d:%02d", t.tm_year+1900, t.tm_mon+1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec); - u8g2_DrawStr(&dsp, 0, 48, buf); - } - else - { - if (blink) - { - snprintf(buf, buf_size-1, "Invld: %04d/%02d/%02d %02d:%02d:%02d", t.tm_year+1900, t.tm_mon+1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec); - u8g2_DrawStr(&dsp, 0, 48, buf); - } - } - gmtime_r(&gps.initial_valid, &t); - snprintf(buf, buf_size-1, "Start: %04d/%02d/%02d %02d:%02d:%02d", t.tm_year+1900, t.tm_mon+1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec); - u8g2_DrawStr(&dsp, 0, 56, buf); - if (tcpip_adapter_get_ip_info(ESP_IF_WIFI_STA, &ip) == 0) { - snprintf(buf, buf_size-1, "IP: "IPSTR, IP2STR(&ip.ip)); - u8g2_DrawStr(&dsp, 0, 64, buf); - } - u8g2_SendBuffer(&dsp); - micros_t end = micros(); - duration = end - start; - blink = !blink; - } - else - { - ESP_LOGD(TAG, "timeout waiting for display queue!"); - } - } -} - - -void start_display() -{ - esp_log_level_set(TAG, CONFIG_DISPLAY_LOG_LEVEL); - esp_log_level_set("u8g2_hal", CONFIG_U8G2_HAL_LOG_LEVEL); - - xTaskCreatePinnedToCore(dsp_task, "dsp_task", 1024*4, NULL, DSP_TASK_PRI, NULL, 1); - -} diff --git a/main/esp_gps_ntp.h b/main/esp_gps_ntp.h deleted file mode 100644 index 595541a..0000000 --- a/main/esp_gps_ntp.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * esp_gps_ntp.h - * - * Created on: Jan 21, 2018 - * Author: chris.l - */ - -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#define LOG_LOCAL_LEVEL ESP_LOG_VERBOSE // we have separate config choices for all app parts -#include "esp_log.h" - -#ifndef MAIN_ESP_GPS_NTP_H_ -#define MAIN_ESP_GPS_NTP_H_ - -#define MICROS_PER_SEC 1000000 -#define us2s(x) (((double)x)/(double)MICROS_PER_SEC) // microseconds to seconds - -#define SDA_PIN (GPIO_NUM_21) -#define SCL_PIN (GPIO_NUM_22) - -typedef uint64_t micros_t; -typedef struct gps_task_status -{ - uint32_t timeouts; - time_t seconds; - micros_t min_micros; - micros_t max_micros; - bool gps_valid; - bool valid; - uint32_t valid_in; - time_t valid_since; - uint32_t valid_count; - time_t initial_valid; - int sat_count; - int fix_quality; -} GPSTaskStatus; - -typedef struct ntp_task_status -{ - uint32_t req_count; - uint32_t rsp_count; -} NTPTaskStatus; - -#define GPS_TASK_PRI configMAX_PRIORITIES -#define NTP_TASK_PRI (configMAX_PRIORITIES-1) -#define DSP_TASK_PRI (configMAX_PRIORITIES-2) - -extern void start_gps(); -extern void start_ntp(); -extern void start_display(); - -extern void triggerDisplay(); -extern void getTime(time_t *secs, time_t *usecs); -extern void getGPSTaskStatus(GPSTaskStatus* status); -extern void getNTPTaskStatus(NTPTaskStatus* status); -extern bool isTimeValid(); -extern micros_t micros(); -extern uint32_t getDispersion(); - -#endif /* MAIN_ESP_GPS_NTP_H_ */ diff --git a/main/esp_gps_ntp_main.c b/main/esp_gps_ntp_main.c deleted file mode 100644 index bb677d4..0000000 --- a/main/esp_gps_ntp_main.c +++ /dev/null @@ -1,151 +0,0 @@ -/* Hello World Example - - This example code is in the Public Domain (or CC0 licensed, at your option.) - - Unless required by applicable law or agreed to in writing, this - software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR - CONDITIONS OF ANY KIND, either express or implied. -*/ - -#include "esp_gps_ntp.h" -#include -#include "esp_system.h" - -#include "esp_wifi.h" -#include "esp_wps.h" -#include "esp_event_loop.h" -#include "nvs_flash.h" - - -/*set wps mode via "make menuconfig"*/ -#if CONFIG_WPS_TYPE_PBC -#define WPS_TEST_MODE WPS_TYPE_PBC -#elif CONFIG_WPS_TYPE_PIN -#define WPS_TEST_MODE WPS_TYPE_PIN -#else -#define WPS_TEST_MODE WPS_TYPE_DISABLE -#endif /*CONFIG_WPS_TYPE_PBC*/ - - -#ifndef PIN2STR -#define PIN2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5], (a)[6], (a)[7] -#define PINSTR "%c%c%c%c%c%c%c%c" -#endif - - -static const char *TAG = "ESP_GPS_NTP"; - -static esp_wps_config_t config = WPS_CONFIG_INIT_DEFAULT(WPS_TEST_MODE); - -static esp_err_t event_handler(void *ctx, system_event_t *event) -{ - switch(event->event_id) - { - case SYSTEM_EVENT_STA_START: - ESP_LOGI(TAG, "SYSTEM_EVENT_STA_START"); - break; - - case SYSTEM_EVENT_STA_GOT_IP: - ESP_LOGI(TAG, "SYSTEM_EVENT_STA_GOT_IP"); - ESP_LOGI(TAG, "got ip:%s\n", ip4addr_ntoa(&event->event_info.got_ip.ip_info.ip)); - start_ntp(); - break; - - case SYSTEM_EVENT_STA_DISCONNECTED: - ESP_LOGI(TAG, "SYSTEM_EVENT_STA_DISCONNECTED"); - ESP_ERROR_CHECK(esp_wifi_connect()); - break; - - case SYSTEM_EVENT_STA_WPS_ER_SUCCESS: - /*point: the function esp_wifi_wps_start() only get ssid & password - * so call the function esp_wifi_connect() here - * */ - ESP_LOGI(TAG, "SYSTEM_EVENT_STA_WPS_ER_SUCCESS"); - ESP_ERROR_CHECK(esp_wifi_wps_disable()); - ESP_ERROR_CHECK(esp_wifi_connect()); - break; - - case SYSTEM_EVENT_STA_WPS_ER_FAILED: - ESP_LOGI(TAG, "SYSTEM_EVENT_STA_WPS_ER_FAILED"); - ESP_ERROR_CHECK(esp_wifi_wps_disable()); - ESP_ERROR_CHECK(esp_wifi_wps_enable(&config)); - ESP_ERROR_CHECK(esp_wifi_wps_start(0)); - break; - - case SYSTEM_EVENT_STA_WPS_ER_TIMEOUT: - ESP_LOGI(TAG, "SYSTEM_EVENT_STA_WPS_ER_TIMEOUT"); - ESP_ERROR_CHECK(esp_wifi_wps_disable()); - ESP_ERROR_CHECK(esp_wifi_wps_enable(&config)); - ESP_ERROR_CHECK(esp_wifi_wps_start(0)); - break; - - case SYSTEM_EVENT_STA_WPS_ER_PIN: - ESP_LOGI(TAG, "SYSTEM_EVENT_STA_WPS_ER_PIN"); - /*show the PIN code here*/ - ESP_LOGI(TAG, "WPS_PIN = "PINSTR, PIN2STR(event->event_info.sta_er_pin.pin_code)); - break; - - default: - break; - } - return ESP_OK; -} - -/*init wifi as sta and start wps*/ -static void start_wifi(void) -{ - tcpip_adapter_init(); - ESP_ERROR_CHECK(esp_event_loop_init(event_handler, NULL)); - - wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); - - ESP_ERROR_CHECK(esp_wifi_init(&cfg)); - - ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); - ESP_ERROR_CHECK(esp_wifi_start()); - - if (esp_wifi_connect()) - { - ESP_LOGI(TAG, "wifi not configured, starting wps..."); - - - ESP_ERROR_CHECK(esp_wifi_wps_enable(&config)); - ESP_ERROR_CHECK(esp_wifi_wps_start(0)); - } -} - -micros_t IRAM_ATTR micros() -{ - return esp_timer_get_time(); -} - -void app_main() -{ - esp_log_level_set(TAG, CONFIG_APP_LOG_LEVEL); - ESP_LOGI(TAG, "ESP32 GPS NTP Server"); - - /* Print chip information */ - esp_chip_info_t chip_info; - esp_chip_info(&chip_info); - ESP_LOGI(TAG, "This is ESP32 chip with %d CPU cores, WiFi%s%s, ", - chip_info.cores, - (chip_info.features & CHIP_FEATURE_BT) ? "/BT" : "", - (chip_info.features & CHIP_FEATURE_BLE) ? "/BLE" : ""); - - ESP_LOGI(TAG, "silicon revision %d, ", chip_info.revision); - - ESP_LOGI(TAG, "***** RUNNING on core %d *****", xPortGetCoreID()); - fflush(stdout); - - /* Initialize NVS — it is used to store PHY calibration data */ - esp_err_t ret = nvs_flash_init(); - if (ret == ESP_ERR_NVS_NO_FREE_PAGES) { - ESP_ERROR_CHECK(nvs_flash_erase()); - ret = nvs_flash_init(); - } - ESP_ERROR_CHECK( ret ); - - start_wifi(); - start_gps(); - start_display(); -} diff --git a/main/gps_task.c b/main/gps_task.c deleted file mode 100644 index 919eb30..0000000 --- a/main/gps_task.c +++ /dev/null @@ -1,480 +0,0 @@ -/* - * gps_task.c - * - * Created on: Jan 20, 2018 - * Author: chris.l - */ - -#include "esp_gps_ntp.h" - -#include "driver/uart.h" -#include "freertos/timers.h" -#include "sys/time.h" -#include "minmea.h" - -static const char *TAG = "GPS"; - -#define RX_BUF_SIZE 255 - -#define ESP_INTR_FLAG_DEFAULT 0 - -// simple versions - we don't worry about side effects -#define MAX(a, b) ((a) < (b) ? (b) : (a)) -#define MIN(a, b) ((a) < (b) ? (a) : (b)) - -#define VALIDITY_TIMER_MS 1010 -#define PPS_VALID_COUNT 10 // must have at least this many "good" PPS interrupts to be valid - -#define PPS_PIN (GPIO_NUM_19) -#define TXD_PIN (GPIO_NUM_17) -#define RXD_PIN (GPIO_NUM_16) -#define LED_PIN (GPIO_NUM_15) - -#define GPS_UART (UART_NUM_2) - -static TimerHandle_t timer; -static volatile uint32_t timeouts; -static volatile time_t seconds; -static volatile micros_t last_micros; -static volatile micros_t min_micros; -static volatile micros_t max_micros; -static volatile bool gps_valid; -static volatile bool valid; -static volatile uint32_t valid_in; -static volatile time_t valid_since; -static volatile uint32_t valid_count; -static volatile time_t initial_valid; -static uint32_t dispersion; -static volatile char reason[128]; - -typedef struct gps_status -{ - bool valid; - int sat_count; - int fix_quality; -} GPSStatus; - -static GPSStatus gps_status; - - - -#ifdef LED_PIN -#define DEBUG_LED_ON() gpio_set_level(LED_PIN, 1) -#define DEBUG_LED_OFF() gpio_set_level(LED_PIN, 0) -#define DEBUG_LED_INIT() init_debug_led() -static void init_debug_led() -{ - // - // set up the LED pin - // - - gpio_config_t io_conf; - //interrupt disabled - io_conf.intr_type = GPIO_PIN_INTR_DISABLE; - //set as input mode - io_conf.mode = GPIO_MODE_OUTPUT; - //bit mask of the pins that you want to set,e.g.GPIO18/19 - io_conf.pin_bit_mask = (1ULL<timeouts = timeouts; - status->seconds = seconds; - status->min_micros = min_micros; - status->max_micros = max_micros; - status->gps_valid = gps_valid; - status->valid = valid; - status->valid_in = valid_in; - status->valid_since = valid_since; - status->valid_count = valid_count; - status->initial_valid = initial_valid; - status->sat_count = gps_status.sat_count; - status->fix_quality = gps_status.fix_quality; -} - -static void IRAM_ATTR pps_isr_handler(void* arg) -{ - DEBUG_LED_ON(); - micros_t cur_micros = micros(); - // restart the validity timer - xTimerReset(timer, 0); - - // - // trigger a display update each second - // - triggerDisplay(); - - // - // don't trust PPS if GPS is not valid. - // - if (!gps_valid) - { - DEBUG_LED_OFF(); - return; - } - - // - // if we are still counting down then keep waiting - // - if (valid_in) - { - --valid_in; - if (valid_in == 0) - { - // clear stats and mark us valid - min_micros = 0; - max_micros = 0; - valid = true; - valid_since = seconds; - ++valid_count; - reason[0] = '\0'; - if (initial_valid == 0) - { - initial_valid = seconds; - } - } - } - - seconds++; - // - // the first time around we just initialize the last value - // - if (last_micros == 0) - { - last_micros = cur_micros; - DEBUG_LED_OFF(); - return; - } - - uint32_t micros_count = cur_micros - last_micros; - last_micros = cur_micros; - - if (min_micros == 0 || micros_count < min_micros) - { - min_micros = micros_count; - } - - if (micros_count > max_micros) - { - max_micros = micros_count; - } - - DEBUG_LED_OFF(); -} - -// -// read a single NMEA record -// -static char *readNMEA() -{ - static uint8_t line[RX_BUF_SIZE+1]; - int size; - uint8_t *p = line; - *p = '\0'; - while(1) - { - if (p >= line+RX_BUF_SIZE) - { - ESP_LOGI(TAG, "Read buffer overflow! size %d bytes: '%s'", p-line, line); - } - size = uart_read_bytes(GPS_UART, (unsigned char *)p, 1, portMAX_DELAY); - if (size == 1) - { - if (*p == '$') - { - // - // '$' starts a record so reset pointer to start - // - p = line; - *p = '$'; - } - else if (*p == '\r' || line[0] != '$') - { - // - // skip newlin and any chars if buffer does not start with '$' - continue; - } - else if (*p == '\n') - { - // - // got a record! - // - *p = 0; - break; - } - p++; - } - } - - return (char*)line; -} - - - -static void IRAM_ATTR invalidate(const char* fmt, ...) -{ - va_list argp; - va_start(argp, fmt); - if (reason[0] == '\0') - { - vsnprintf((char*)reason, sizeof(reason)-1, fmt, argp); - } - va_end(argp); - - if (valid) - { - valid_since = seconds; - } - - valid = false; - gps_valid = false; - last_micros = 0; - valid_in = 0; -} - -static void init_pps() -{ - // - // set up the PPS interrupt - // - - gpio_config_t io_conf; - //interrupt of falling edge - io_conf.intr_type = GPIO_PIN_INTR_POSEDGE; - //set as input mode - io_conf.mode = GPIO_MODE_INPUT; - //bit mask of the pins that you want to set,e.g.GPIO18/19 - io_conf.pin_bit_mask = (1ULL< ts.tv_sec) - { - timewarps += 1; - ESP_LOGD(TAG, "$RMC: ignoring timewarp back! delayed serial? %lu -> %lu", seconds, ts.tv_sec); - if (timewarps > 1) - { - invalidate("time warped backwards too many (%d) times!", timewarps); - timewarps = 0; - } - } - else if (seconds != ts.tv_sec) - { - ESP_LOGW(TAG, "$RMC: adjusting seconds %lu -> %lu", seconds, ts.tv_sec); - seconds = ts.tv_sec; - } - else - { - timewarps = 0; - } - - // - // if gps was not valid, it is now - // - if (!gps_valid) - { - gps_valid = true; - valid_in = PPS_VALID_COUNT; - ESP_LOGI(TAG, "gps valid!"); - } - } - else - { - if (valid) - { - invalidate("GPS $RMC"); - } - } - } - else - { - ESP_LOGE(TAG, "$RMC: failed to parse line: %s", line); - } - } - break; - - case MINMEA_SENTENCE_GGA: - { - struct minmea_sentence_gga frame; - if (minmea_parse_gga(&frame, line)) { - ESP_LOGD(TAG, "$GGA: seconds: %lu timeouts: %u fix: %d sats: %d", seconds, timeouts, frame.fix_quality, frame.satellites_tracked); - gps_status.sat_count = frame.satellites_tracked; - gps_status.fix_quality = frame.fix_quality; - } - else - { - ESP_LOGE(TAG, "$GGA: failed to parse line: %s", line); - } - } - break; - - case MINMEA_SENTENCE_GSV: - case MINMEA_SENTENCE_GSA: - case MINMEA_SENTENCE_GLL: - case MINMEA_SENTENCE_GST: - case MINMEA_SENTENCE_VTG: - case MINMEA_SENTENCE_ZDA: - ESP_LOGV(TAG, "IGNORING: %s", line); - break; - - case MINMEA_INVALID: - ESP_LOGE(TAG, "INVALID: '%s'", line); - break; - - case MINMEA_UNKNOWN: - ESP_LOGW(TAG, "UNKNOWN: '%s'", line); - break; - } - - // - // Recompute dispersion periodically - // - static time_t last_seconds; - if (seconds != last_seconds) - { - double disp = us2s(MAX(abs(MICROS_PER_SEC-max_micros), abs(MICROS_PER_SEC-min_micros))); - dispersion = (uint32_t)(disp * 65536.0); -#if 0 - ESP_LOGI(TAG, "min: %llu max: %llu jitter: %llu sats: %d fix: %d valid_count: %u valid_in: %u valid: %s", - min_micros, max_micros, max_micros - min_micros, gps_status.sat_count, gps_status.fix_quality, valid_count, valid_in, valid ? "true" : "false"); -#endif - } - last_seconds = seconds; - - - } -} - -void start_gps() -{ - esp_log_level_set(TAG, CONFIG_GPS_LOG_LEVEL); - - xTaskCreatePinnedToCore(gps_task, "gps_task", 1024*2, NULL, GPS_TASK_PRI, NULL, 1); - -} diff --git a/main/ntp_task.c b/main/ntp_task.c deleted file mode 100644 index 0566570..0000000 --- a/main/ntp_task.c +++ /dev/null @@ -1,255 +0,0 @@ -/* - * ntp_task.c - * - * Created on: Jan 21, 2018 - * Author: chris.l - */ - -#include "esp_gps_ntp.h" - -#include -#include -#include - - -static const char *TAG = "NTP"; - -#define NTP_PORT 123 - - -typedef struct ntp_time -{ - uint32_t seconds; - uint32_t fraction; -} NTPTime; - -typedef struct ntp_packet -{ - uint8_t flags; - uint8_t stratum; - uint8_t poll; - int8_t precision; - uint32_t delay; - uint32_t dispersion; - uint8_t ref_id[4]; - NTPTime ref_time; - NTPTime orig_time; - NTPTime recv_time; - NTPTime xmit_time; -} NTPPacket; - -#ifdef NTP_PACKET_DEBUG -void dumpNTPPacket(NTPPacket* ntp) -{ - dbprintf("size: %u\n", sizeof(*ntp)); - dbprintf("firstbyte: 0x%02x\n", *(uint8_t*)ntp); - dbprintf("li: %u\n", getLI(ntp->flags)); - dbprintf("version: %u\n", getVERS(ntp->flags)); - dbprintf("mode: %u\n", getMODE(ntp->flags)); - dbprintf("stratum: %u\n", ntp->stratum); - dbprintf("poll: %u\n", ntp->poll); - dbprintf("precision: %d\n", ntp->precision); - dbprintf("delay: %u\n", ntp->delay); - dbprintf("dispersion: %u\n", ntp->dispersion); - dbprintf("ref_id: %02x:%02x:%02x:%02x\n", ntp->ref_id[0], ntp->ref_id[1], ntp->ref_id[2], ntp->ref_id[3]); - dbprintf("ref_time: %08x:%08x\n", ntp->ref_time.seconds, ntp->ref_time.fraction); - dbprintf("orig_time: %08x:%08x\n", ntp->orig_time.seconds, ntp->orig_time.fraction); - dbprintf("recv_time: %08x:%08x\n", ntp->recv_time.seconds, ntp->recv_time.fraction); - dbprintf("xmit_time: %08x:%08x\n", ntp->xmit_time.seconds, ntp->xmit_time.fraction); -} -#else -#define dumpNTPPacket(x) -#endif - -#define PRECISION_COUNT 10000 - -#define LI_NONE 0 -#define LI_SIXTY_ONE 1 -#define LI_FIFTY_NINE 2 -#define LI_NOSYNC 3 - -#define MODE_RESERVED 0 -#define MODE_ACTIVE 1 -#define MODE_PASSIVE 2 -#define MODE_CLIENT 3 -#define MODE_SERVER 4 -#define MODE_BROADCAST 5 -#define MODE_CONTROL 6 -#define MODE_PRIVATE 7 - -#define NTP_VERSION 4 - -#define REF_ID "PPS " // "GPS " when we have one! - -#define setLI(value) ((value&0x03)<<6) -#define setVERS(value) ((value&0x07)<<3) -#define setMODE(value) ((value&0x07)) - -#define getLI(value) ((value>>6)&0x03) -#define getVERS(value) ((value>>3)&0x07) -#define getMODE(value) (value&0x07) - -#define SEVENTY_YEARS 2208988800L -#define toEPOCH(t) ((uint32_t)t-SEVENTY_YEARS) -#define toNTP(t) ((uint32_t)t+SEVENTY_YEARS) - -static int8_t precision; -static uint32_t req_count; -static uint32_t rsp_count; - -void getNTPTaskStatus(NTPTaskStatus* status) -{ - status->req_count = req_count; - status->rsp_count = rsp_count; -} - -static void getNTPTime(NTPTime *time) -{ - time_t seconds; - time_t usecs; - getTime(&seconds, &usecs); - time->seconds = toNTP(seconds); - - // - // if micros_delta is at or bigger than one second then - // use the max fraction. - // - if (usecs >= 1000000) - { - time->fraction = 0xffffffff; - return; - } - - time->fraction = (uint32_t)(us2s(usecs) * (double)4294967296L); -} - -int8_t computePrecision() -{ - NTPTime t; - unsigned long start = micros(); - for (int i = 0; i < PRECISION_COUNT; ++i) - { - getNTPTime(&t); - } - unsigned long end = micros(); - double total = (double)(end - start) / 1000000.0; - double time = total / PRECISION_COUNT; - double prec = log2(time); - ESP_LOGI(TAG, "computePrecision: total:%f time:%f prec:%f\n", total, time, prec); - return (int8_t)prec; -} - -static void ntp_task() -{ - static NTPPacket ntp; - static int udp_server = -1; - - ESP_LOGI(TAG, "starting!"); - - precision = computePrecision(); - ESP_LOGI(TAG, "precision: %d", precision); - if ((udp_server=socket(AF_INET, SOCK_DGRAM, 0)) == -1) - { - ESP_LOGI(TAG, "could not create socket: %d", errno); - return; - } - - int yes = 1; - if (setsockopt(udp_server,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(yes)) < 0) - { - ESP_LOGI(TAG, "could not set socket option: %d", errno); - return; - } - - struct sockaddr_in addr; - memset((char *) &addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_port = htons(NTP_PORT); - addr.sin_addr.s_addr = htonl(INADDR_ANY); - if(bind(udp_server , (struct sockaddr*)&addr, sizeof(addr)) == -1) - { - ESP_LOGI(TAG, "could not bind socket: %d", errno); - return; - } - - while(1) - { - int len; - int slen = sizeof(addr); - if ((len = recvfrom(udp_server, &ntp, sizeof(ntp), 0, (struct sockaddr *) &addr, (socklen_t *)&slen)) == -1) - { - ESP_LOGI(TAG, "could not receive data: %d", errno); - continue; - } - - ++req_count; - - NTPTime recv_time; - getNTPTime(&recv_time); - - if (len != sizeof(NTPPacket)) - { - ESP_LOGI(TAG, "ignoring packet with bad length: %d < %d\n", len, sizeof(ntp)); - continue; - } - - if (!isTimeValid()) - { - ESP_LOGI(TAG, "GPS time data not valid!"); - continue; - } - - ntp.delay = ntohl(ntp.delay); - ntp.dispersion = ntohl(ntp.dispersion); - ntp.orig_time.seconds = ntohl(ntp.orig_time.seconds); - ntp.orig_time.fraction = ntohl(ntp.orig_time.fraction); - ntp.ref_time.seconds = ntohl(ntp.ref_time.seconds); - ntp.ref_time.fraction = ntohl(ntp.ref_time.fraction); - ntp.recv_time.seconds = ntohl(ntp.recv_time.seconds); - ntp.recv_time.fraction = ntohl(ntp.recv_time.fraction); - ntp.xmit_time.seconds = ntohl(ntp.xmit_time.seconds); - ntp.xmit_time.fraction = ntohl(ntp.xmit_time.fraction); - dumpNTPPacket(&ntp); - - // - // Build the response - // - ntp.flags = setLI(LI_NONE) | setVERS(NTP_VERSION) | setMODE(MODE_SERVER); - ntp.stratum = 1; - ntp.precision = precision; - // TODO: compute actual root delay, and root dispersion - ntp.delay = (uint32_t)(0.000001 * 65536); - ntp.dispersion = getDispersion(); - strncpy((char*)ntp.ref_id, REF_ID, sizeof(ntp.ref_id)); - ntp.orig_time = ntp.xmit_time; - ntp.recv_time = recv_time; - getNTPTime(&(ntp.ref_time)); - dumpNTPPacket(&ntp); - ntp.delay = htonl(ntp.delay); - ntp.dispersion = htonl(ntp.dispersion); - ntp.orig_time.seconds = htonl(ntp.orig_time.seconds); - ntp.orig_time.fraction = htonl(ntp.orig_time.fraction); - ntp.ref_time.seconds = htonl(ntp.ref_time.seconds); - ntp.ref_time.fraction = htonl(ntp.ref_time.fraction); - ntp.recv_time.seconds = htonl(ntp.recv_time.seconds); - ntp.recv_time.fraction = htonl(ntp.recv_time.fraction); - getNTPTime(&(ntp.xmit_time)); - ntp.xmit_time.seconds = htonl(ntp.xmit_time.seconds); - ntp.xmit_time.fraction = htonl(ntp.xmit_time.fraction); - - int sent = sendto(udp_server, &ntp, sizeof(ntp), 0, (struct sockaddr*) &addr, sizeof(addr)); - if(sent < 0) - { - ESP_LOGI(TAG, "could not send data: %d", errno); - continue; - } - ++rsp_count; - } -} - -void start_ntp() -{ - esp_log_level_set(TAG, CONFIG_NTP_LOG_LEVEL); - - xTaskCreatePinnedToCore(ntp_task, "ntp_task", 1024*2, NULL, NTP_TASK_PRI, NULL, 1); -}