306 lines
8.0 KiB
C++
306 lines
8.0 KiB
C++
/*
|
|
* 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 "ESPNTPServerVersion.h"
|
|
|
|
#include "Log.h"
|
|
#include "DLogPrintWriter.h"
|
|
#include "DLogSyslogWriter.h"
|
|
|
|
#include "GPS.h"
|
|
#include "NTP.h"
|
|
#include "Display.h"
|
|
#include "Config.h"
|
|
|
|
DLog& dlog = DLog::getLog();
|
|
GPS gps(Serial, SYNC_PIN);
|
|
NTP ntp(gps);
|
|
Display display(gps, ntp, SDA_PIN, SCL_PIN);
|
|
Config config;
|
|
|
|
char devicename[32];
|
|
|
|
const char* SETUP_TAG = "setup";
|
|
const char* LOOP_TAG = "loop";
|
|
|
|
void logTimeFirst(DLogBuffer& buffer, DLogLevel level)
|
|
{
|
|
(void)level; // not used
|
|
struct timeval tv;
|
|
struct tm tm;
|
|
gps.getTime(&tv);
|
|
gmtime_r(&tv.tv_sec, &tm);
|
|
|
|
buffer.printf(F("%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 processOTA(const char* ota_url, const char* ota_fp)
|
|
{
|
|
static PROGMEM const char TAG[] = "processOTA";
|
|
dlog.info(FPSTR(TAG), F("OTA update"));
|
|
|
|
dlog.info(FPSTR(TAG), F("checking for OTA from: '%s'"), ota_url);
|
|
display.message("Starting OTA");
|
|
|
|
ESPhttpUpdate.rebootOnUpdate(false);
|
|
|
|
#ifdef USE_CERT_STORE
|
|
CertStore certStore;
|
|
#endif
|
|
WiFiClient *client = nullptr;
|
|
|
|
if (strncmp(ota_url, "https:", 6) == 0)
|
|
{
|
|
#ifdef USE_CERT_STORE
|
|
int numCerts = certStore.initCertStore(SPIFFS, PSTR("/certs.idx"), PSTR("/certs.ar"));
|
|
dlog.info(FPSTR(TAG), F("Number of CA certs read: %d"), numCerts);
|
|
#endif
|
|
BearSSL::WiFiClientSecure *bear = new BearSSL::WiFiClientSecure();
|
|
#ifdef USE_CERT_STORE
|
|
// Integrate the cert store with this connection
|
|
dlog.info(FPSTR(TAG), F("adding cert store to connection"));
|
|
bear->setCertStore(&certStore);
|
|
#else
|
|
bear->setInsecure();
|
|
#endif
|
|
client = bear;
|
|
}
|
|
else
|
|
{
|
|
client = new WiFiClient;
|
|
}
|
|
|
|
t_httpUpdate_return ret = ESPhttpUpdate.update(*client, ota_url, "0.4");
|
|
|
|
String reason = ESPhttpUpdate.getLastErrorString();
|
|
|
|
switch(ret)
|
|
{
|
|
case HTTP_UPDATE_OK:
|
|
dlog.info(FPSTR(TAG), F("OTA update OK! restarting..."));
|
|
dlog.end();
|
|
display.message("Success!");
|
|
delay(10000);
|
|
ESP.restart();
|
|
break;
|
|
|
|
case HTTP_UPDATE_FAILED:
|
|
dlog.info(FPSTR(TAG), F("OTA update failed! reason:'%s'"), reason.c_str());
|
|
display.message("FAILED!");
|
|
delay(10000);
|
|
break;
|
|
|
|
case HTTP_UPDATE_NO_UPDATES:
|
|
dlog.info(FPSTR(TAG), F("OTA no updates! reason:'%s'"), reason.c_str());
|
|
display.message("NO Update!");
|
|
delay(10000);
|
|
break;
|
|
|
|
default:
|
|
dlog.info(FPSTR(TAG), F("OTA update WTF? unexpected return code: %d reason: '%s'"), ret, reason.c_str());
|
|
display.message("Unknown!");
|
|
delay(10000);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void setup()
|
|
{
|
|
//delay(5000); // delay for IDE to re-open serial
|
|
|
|
//
|
|
// Setup DLog with Serial1, set the pre function to add the date/time
|
|
//
|
|
Serial1.begin(76800);
|
|
dlog.begin(new DLogPrintWriter(Serial1));
|
|
dlog.setPreFunc(&logTimeFirst);
|
|
//dlog.setLevel("GPS", DLogLevel::DLOG_LEVEL_DEBUG);
|
|
dlog.setLevel("Config", DLogLevel::DLOG_LEVEL_DEBUG);
|
|
|
|
dlog.info(SETUP_TAG, F("Startup!"));
|
|
|
|
snprintf(devicename, sizeof(devicename), "ESPNTP:%08x", ESP.getChipId());
|
|
dlog.info(SETUP_TAG, "Device name: %s", devicename);
|
|
|
|
dlog.info(SETUP_TAG, F("initializing display"));
|
|
display.begin();
|
|
|
|
|
|
dlog.info(SETUP_TAG, F("initializing serial for GPS"));
|
|
Serial.begin(9600);
|
|
Serial.swap();
|
|
|
|
dlog.info(SETUP_TAG, F("initializing GPS"));
|
|
display.message("Starting GPS");
|
|
gps.begin();
|
|
display.process();
|
|
|
|
bool force_config = false;
|
|
|
|
config.begin();
|
|
|
|
if (!config.load())
|
|
{
|
|
dlog.warning(SETUP_TAG, "no config found, forcing config portal!");
|
|
force_config = true;
|
|
}
|
|
|
|
// if the reset/config button is pressed then force config
|
|
if (digitalRead(CONFIG_PIN) == 0)
|
|
{
|
|
time_t start = millis();
|
|
|
|
while (digitalRead(CONFIG_PIN) == 0)
|
|
{
|
|
time_t delta = millis() - start;
|
|
if (!force_config && delta > CONFIG_DELAY)
|
|
{
|
|
dlog.info(SETUP_TAG, F("reset button held, forcing config!"));
|
|
force_config = true;
|
|
display.message("Force Config");
|
|
}
|
|
}
|
|
}
|
|
|
|
#if !defined(USE_NO_WIFI)
|
|
dlog.info(SETUP_TAG, F("initializing wifi"));
|
|
display.message("Starting WiFi");
|
|
WiFiSetup wifi(config, display, Serial1, false, devicename);
|
|
wifi.connect(force_config);
|
|
|
|
const char* syslog_host = config.getSyslogHost();
|
|
uint16_t syslog_port = config.getSyslogPort();
|
|
if (syslog_host != nullptr && strlen(syslog_host) > 0 && syslog_port != 0)
|
|
{
|
|
dlog.info(SETUP_TAG, "enabling syslog: '%s:%u'", syslog_host, syslog_port);
|
|
dlog.begin(new DLogSyslogWriter(syslog_host, syslog_port, devicename, ESPNTP_SERVER_VERSION));
|
|
}
|
|
const char* url = wifi.getOTAURL();
|
|
const char* fp = wifi.getOTAFP();
|
|
if (fp == nullptr)
|
|
{
|
|
fp = "";
|
|
}
|
|
|
|
if (url != nullptr)
|
|
{
|
|
processOTA(url, fp);
|
|
}
|
|
|
|
WiFiMode_t mode = WiFi.getMode();
|
|
dlog.error(SETUP_TAG, "WiFi mode: %d", mode);
|
|
#endif
|
|
|
|
display.process();
|
|
|
|
dlog.info(SETUP_TAG, F("initializing NTP"));
|
|
ntp.begin();
|
|
}
|
|
|
|
void loop()
|
|
{
|
|
static int last_wifi_status;
|
|
static IPAddress last_ip;
|
|
|
|
int wifi_status = WiFi.status();
|
|
if (wifi_status != last_wifi_status)
|
|
{
|
|
const char* status;
|
|
switch (wifi_status)
|
|
{
|
|
case WL_CONNECTED:
|
|
status = "CONNECTED";
|
|
break;
|
|
case WL_NO_SSID_AVAIL:
|
|
status = "NO_SSID";
|
|
break;
|
|
case WL_CONNECT_FAILED:
|
|
status = "FAILED";
|
|
break;
|
|
case WL_IDLE_STATUS:
|
|
status = "IDLE";
|
|
break;
|
|
case WL_DISCONNECTED:
|
|
status = "DISCONNECTED";
|
|
break;
|
|
default:
|
|
status = "<UNKNOWN>";
|
|
break;
|
|
}
|
|
dlog.info(LOOP_TAG, F("wifi status change %d -> %d '%s'"), last_wifi_status, wifi_status, status);
|
|
last_wifi_status = wifi_status;
|
|
}
|
|
|
|
IPAddress ip = WiFi.localIP();
|
|
if (ip != last_ip)
|
|
{
|
|
dlog.warning(LOOP_TAG, F("ip address change %s -> %s"), last_ip.toString().c_str(), ip.toString().c_str());
|
|
last_ip = ip;
|
|
}
|
|
|
|
#if 0
|
|
uint32_t m = millis();
|
|
if (m % 1000 == 0)
|
|
{
|
|
dlog.info("loop", "uptime: %lu millis: %lu", m);
|
|
}
|
|
#endif
|
|
|
|
gps.process();
|
|
|
|
static time_t last_seconds;
|
|
struct timeval tv;
|
|
gps.getTime(&tv);
|
|
|
|
if (tv.tv_sec != last_seconds)
|
|
{
|
|
if (tv.tv_sec != last_seconds && ((tv.tv_sec % 300) == 0 || gps.getValidDelay()))
|
|
{
|
|
dlog.info("loop", F("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)
|
|
{
|
|
dlog.warning(LOOP_TAG, F("OOPS: time went backwards: last:%lu now:%lu delta:%ld"), last_seconds, tv.tv_sec, tv.tv_sec-last_seconds);
|
|
}
|
|
|
|
display.process();
|
|
}
|
|
|
|
last_seconds = tv.tv_sec;
|
|
}
|