Merge branch 'develop' into feature/battery-monitoring
Handled conflicts in src/web.cpp, and rebuild frontendmaster
commit
1a65ef30c6
File diff suppressed because one or more lines are too long
|
@ -11,10 +11,19 @@ var CurrentView = {
|
|||
return Weather.loadCurrent();
|
||||
},
|
||||
view: function() {
|
||||
|
||||
let timeHeading = null;
|
||||
if (Weather.current !== null && Weather.current.time) {
|
||||
const currentTime = new Date(Weather.current.time*1000);
|
||||
timeHeading = m('h2.subtitle',
|
||||
'as of ' + currentTime.toLocaleString()
|
||||
);
|
||||
}
|
||||
|
||||
return m('div.section', [m('div.container',
|
||||
(Weather.current == null) ? [m('.notification', 'Loading current conditions...')] : [
|
||||
m('h1.title', 'Current Conditions'),
|
||||
m('h2.subtitle', 'as of ' + Weather.current.time),
|
||||
timeHeading,
|
||||
m('.level', [
|
||||
m('.level-item.has-text-centered', m("div",[
|
||||
m('p.heading"', 'Temperature'),
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include "config.h"
|
||||
#include "time.h"
|
||||
#include <Arduino.h>
|
||||
#include <EEPROM.h>
|
||||
|
||||
|
@ -103,6 +104,7 @@ bool cUWeatherConfig::setNTPHost(const char* buff)
|
|||
{
|
||||
if (strlen(buff) >= 32) return false;
|
||||
strncpy((char*)_data.ntp_host, buff, 32);
|
||||
uweather_time_init(); // restart time
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include "update.h"
|
||||
#include "bme280.h"
|
||||
#include "config.h"
|
||||
#include "time.h"
|
||||
|
||||
#ifndef SERIAL_BAUD
|
||||
#define SERIAL_BAUD 74880
|
||||
|
@ -71,6 +72,7 @@ void setup()
|
|||
//
|
||||
bme280_init();
|
||||
|
||||
uweather_time_init();
|
||||
uweather_update_init();
|
||||
|
||||
}
|
||||
|
@ -79,5 +81,6 @@ void loop()
|
|||
{
|
||||
uweather_web_handle();
|
||||
uweather_update_handle();
|
||||
uweather_time_handle();
|
||||
delay(10);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,118 @@
|
|||
#include <time.h>
|
||||
#include <Arduino.h>
|
||||
#include <WiFiUdp.h>
|
||||
#include <ESP8266WiFi.h>
|
||||
#include "config.h"
|
||||
|
||||
#define LOCAL_NTP_PORT 2390
|
||||
#define NTP_RETRY_INTERVAL 300000
|
||||
#define DEFAULT_NTP_HOST "time.nist.gov"
|
||||
#define NTP_PACKET_SIZE 48
|
||||
|
||||
enum state { INIT, SENT, IDLE, NO_HOST} ntp_state;
|
||||
byte packet_buffer[NTP_PACKET_SIZE]; // NTP timestamp is in the first 48 bytes
|
||||
WiFiUDP udp;
|
||||
unsigned long time_offset = 0;
|
||||
unsigned long ntp_timestamp = 0;
|
||||
unsigned long retry_at = 0;
|
||||
IPAddress host;
|
||||
|
||||
const char* ntp_get_hostname()
|
||||
{
|
||||
const char* host_in_cfg = (const char*) Config._data.ntp_host;
|
||||
if (host_in_cfg[0] == '\0') {
|
||||
return DEFAULT_NTP_HOST;
|
||||
}
|
||||
return host_in_cfg;
|
||||
}
|
||||
|
||||
void uweather_time_init()
|
||||
{
|
||||
ntp_state = state::INIT;
|
||||
time_offset = 0;
|
||||
ntp_timestamp = 0;
|
||||
retry_at = 0;
|
||||
udp.begin(LOCAL_NTP_PORT);
|
||||
}
|
||||
|
||||
void uweather_time_handle()
|
||||
{
|
||||
switch (ntp_state) {
|
||||
case state::INIT:
|
||||
{
|
||||
if (!WiFi.isConnected())
|
||||
return;
|
||||
|
||||
// resolve ntp hostname
|
||||
const char* hostname = ntp_get_hostname();
|
||||
if (!WiFi.hostByName(hostname, host)) {
|
||||
ntp_state = state::NO_HOST;
|
||||
Serial.printf("Unable to resolve ntp host: %s\n", hostname);
|
||||
retry_at = millis() + NTP_RETRY_INTERVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
// send packet to ntp server
|
||||
Serial.printf("Sending ntp packet to %s\n", hostname);
|
||||
memset(packet_buffer, 0, NTP_PACKET_SIZE);
|
||||
packet_buffer[0] = 0b11100011; // LI, Version, Mode
|
||||
packet_buffer[1] = 0;
|
||||
packet_buffer[2] = 6;
|
||||
packet_buffer[3] = 0xEC;
|
||||
packet_buffer[12] = 49;
|
||||
packet_buffer[13] = 0x4E;
|
||||
packet_buffer[14] = 49;
|
||||
packet_buffer[15] = 52;
|
||||
udp.beginPacket(host, 123);
|
||||
udp.write(packet_buffer, NTP_PACKET_SIZE);
|
||||
udp.endPacket();
|
||||
ntp_state = state::SENT;
|
||||
}
|
||||
|
||||
case state::SENT:
|
||||
{
|
||||
int cb = udp.parsePacket();
|
||||
if (!cb) break;
|
||||
udp.read(packet_buffer, NTP_PACKET_SIZE);
|
||||
unsigned long high = word(packet_buffer[40], packet_buffer[41]);
|
||||
unsigned long low = word(packet_buffer[42], packet_buffer[43]);
|
||||
unsigned long secs = (high << 16 | low) - 2208988800UL; // subtract 70 years from time
|
||||
time_offset = millis();
|
||||
ntp_timestamp = secs;
|
||||
ntp_state = state::IDLE;
|
||||
Serial.printf("Received NTP response, current timestamp is %lu\n", secs);
|
||||
}
|
||||
case state::IDLE:
|
||||
break;
|
||||
|
||||
case state::NO_HOST:
|
||||
if (millis() > retry_at) {
|
||||
state::INIT;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool has_timestamp()
|
||||
{
|
||||
return ntp_timestamp > 0;
|
||||
}
|
||||
|
||||
void set_timestamp(unsigned long ts)
|
||||
{
|
||||
time_offset = millis() / 1000;
|
||||
ntp_timestamp = ts;
|
||||
}
|
||||
|
||||
unsigned long timestamp(unsigned long milliseconds)
|
||||
{
|
||||
if (!has_timestamp()) return 0;
|
||||
return (milliseconds / 1000) - time_offset + ntp_timestamp;
|
||||
}
|
||||
|
||||
unsigned long current_timestamp()
|
||||
{
|
||||
return timestamp(millis());
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
#ifndef _UWEATHER_NTP_H_
|
||||
#define _UWEATHER_NTP_H_
|
||||
|
||||
void uweather_time_init();
|
||||
void uweather_time_handle();
|
||||
|
||||
unsigned long timestamp(unsigned long);
|
||||
unsigned long current_timestamp();
|
||||
|
||||
#endif /** End _UWEATHER_NTP_H_ include guard */
|
24
src/web.cpp
24
src/web.cpp
|
@ -7,6 +7,7 @@
|
|||
#include "config.h"
|
||||
#include "version.h"
|
||||
#include "battery.h"
|
||||
#include "time.h"
|
||||
|
||||
/**
|
||||
* How long to wait after the OK to shutdown before no longer
|
||||
|
@ -81,14 +82,20 @@ void handle_firmware(HttpRequest& req, HttpResponse& res)
|
|||
|
||||
void handle_weather_current(HttpRequest& req, HttpResponse& res)
|
||||
{
|
||||
char buff[4][10] = {};
|
||||
unsigned long now = current_timestamp();
|
||||
char buff[5][10] = {};
|
||||
dtostrf(BME.readTemperature(), 2, 1, buff[0]);
|
||||
dtostrf(BME.readPressure(), 2, 1, buff[1]);
|
||||
dtostrf(BME.readHumidity(), 2, 1, buff[2]);
|
||||
dtostrf(battery_get_voltage(), 2, 1, buff[3]);
|
||||
Serial.printf("temp= %s, pres= %s, hum=%s\r\n", buff[0], buff[1], buff[2]);
|
||||
dtostrf(battery_get_voltage(),2, 1, buff[3]);
|
||||
if (now) {
|
||||
sprintf(buff[4], "%d", now);
|
||||
} else {
|
||||
strcpy(buff[4], "null");
|
||||
}
|
||||
res.headers.set("Content-Type", "application/json");
|
||||
content.printf("{\"time\": null, \"temperature\": %s, \"pressure\": %s, \"humidity\": %s, \"battery\": { \"voltage\": %s, \"is_charging\": %s, \"percent\": %d}}",
|
||||
content.printf("{\"time\": %s, \"temperature\": %s, \"pressure\": %s, \"humidity\": %s, \"battery\": { \"voltage\": %s, \"is_charging\": %s, \"percent\": %d}}",
|
||||
buff[4],
|
||||
buff[0],
|
||||
buff[1],
|
||||
buff[2],
|
||||
|
@ -184,6 +191,11 @@ void handle_config_set(HttpRequest& req, HttpResponse& res)
|
|||
} else if (strcmp("mdns_hostname", key) == 0) {
|
||||
result = Config.setMDNSHostname(value);
|
||||
} else if (strcmp("ntp_host", key) == 0) {
|
||||
IPAddress ntp_host_addr;
|
||||
if (value[0] != '\0' && !WiFi.hostByName(value, ntp_host_addr)) {
|
||||
client_error("Unable to resolve host", res);
|
||||
return;
|
||||
}
|
||||
result = Config.setNTPHost(value);
|
||||
} else if (strcmp("mqtt_host", key) == 0) {
|
||||
result = Config.setMQTTHost(value);
|
||||
|
@ -206,9 +218,9 @@ void handle_config_set(HttpRequest& req, HttpResponse& res)
|
|||
// return json object with property
|
||||
char buff[64];
|
||||
if (is_str) {
|
||||
sprintf(buff, "{\"%s\": \"%s\"}", key, value);
|
||||
sprintf(buff, "{\"property\": \"%s\", \"value\": \"%s\"}", key, value);
|
||||
} else {
|
||||
sprintf(buff, "{\"%s\": %s}", key, value);
|
||||
sprintf(buff, "{\"property\": \"%s\", \"value\": %s}", key, value);
|
||||
}
|
||||
res.headers.set("Content-Type", "application/json");
|
||||
content.print(buff);
|
||||
|
|
Loading…
Reference in New Issue