envirstation first commit
This commit is contained in:
385
DHT.cpp
Normal file
385
DHT.cpp
Normal file
@@ -0,0 +1,385 @@
|
||||
/*!
|
||||
* @file DHT.cpp
|
||||
*
|
||||
* @mainpage DHT series of low cost temperature/humidity sensors.
|
||||
*
|
||||
* @section intro_sec Introduction
|
||||
*
|
||||
* This is a library for DHT series of low cost temperature/humidity sensors.
|
||||
*
|
||||
* You must have Adafruit Unified Sensor Library library installed to use this
|
||||
* class.
|
||||
*
|
||||
* Adafruit invests time and resources providing this open source code,
|
||||
* please support Adafruit andopen-source hardware by purchasing products
|
||||
* from Adafruit!
|
||||
*
|
||||
* @section author Author
|
||||
*
|
||||
* Written by Adafruit Industries.
|
||||
*
|
||||
* @section license License
|
||||
*
|
||||
* MIT license, all text above must be included in any redistribution
|
||||
*/
|
||||
|
||||
#include "DHT.h"
|
||||
|
||||
#define MIN_INTERVAL 2000 /**< min interval value */
|
||||
#define TIMEOUT -1 /**< timeout on */
|
||||
|
||||
/*!
|
||||
* @brief Instantiates a new DHT class
|
||||
* @param pin
|
||||
* pin number that sensor is connected
|
||||
* @param type
|
||||
* type of sensor
|
||||
* @param count
|
||||
* number of sensors
|
||||
*/
|
||||
DHT::DHT(uint8_t pin, uint8_t type, uint8_t count) {
|
||||
_pin = pin;
|
||||
_type = type;
|
||||
#ifdef __AVR
|
||||
_bit = digitalPinToBitMask(pin);
|
||||
_port = digitalPinToPort(pin);
|
||||
#endif
|
||||
_maxcycles =
|
||||
microsecondsToClockCycles(1000); // 1 millisecond timeout for
|
||||
// reading pulses from DHT sensor.
|
||||
// Note that count is now ignored as the DHT reading algorithm adjusts itself
|
||||
// based on the speed of the processor.
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Setup sensor pins and set pull timings
|
||||
* @param usec
|
||||
* Optionally pass pull-up time (in microseconds) before DHT reading
|
||||
*starts. Default is 55 (see function declaration in DHT.h).
|
||||
*/
|
||||
void DHT::begin(uint8_t usec) {
|
||||
// set up the pins!
|
||||
pinMode(_pin, INPUT_PULLUP);
|
||||
// Using this value makes sure that millis() - lastreadtime will be
|
||||
// >= MIN_INTERVAL right away. Note that this assignment wraps around,
|
||||
// but so will the subtraction.
|
||||
_lastreadtime = millis() - MIN_INTERVAL;
|
||||
DEBUG_PRINT("DHT max clock cycles: ");
|
||||
DEBUG_PRINTLN(_maxcycles, DEC);
|
||||
pullTime = usec;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Read temperature
|
||||
* @param S
|
||||
* Scale. Boolean value:
|
||||
* - true = Fahrenheit
|
||||
* - false = Celcius
|
||||
* @param force
|
||||
* true if in force mode
|
||||
* @return Temperature value in selected scale
|
||||
*/
|
||||
float DHT::readTemperature(bool S, bool force) {
|
||||
float f = NAN;
|
||||
|
||||
if (read(force)) {
|
||||
switch (_type) {
|
||||
case DHT11:
|
||||
f = data[2];
|
||||
if (data[3] & 0x80) {
|
||||
f = -1 - f;
|
||||
}
|
||||
f += (data[3] & 0x0f) * 0.1;
|
||||
if (S) {
|
||||
f = convertCtoF(f);
|
||||
}
|
||||
break;
|
||||
case DHT12:
|
||||
f = data[2];
|
||||
f += (data[3] & 0x0f) * 0.1;
|
||||
if (data[2] & 0x80) {
|
||||
f *= -1;
|
||||
}
|
||||
if (S) {
|
||||
f = convertCtoF(f);
|
||||
}
|
||||
break;
|
||||
case DHT22:
|
||||
case DHT21:
|
||||
f = ((word)(data[2] & 0x7F)) << 8 | data[3];
|
||||
f *= 0.1;
|
||||
if (data[2] & 0x80) {
|
||||
f *= -1;
|
||||
}
|
||||
if (S) {
|
||||
f = convertCtoF(f);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return f;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Converts Celcius to Fahrenheit
|
||||
* @param c
|
||||
* value in Celcius
|
||||
* @return float value in Fahrenheit
|
||||
*/
|
||||
float DHT::convertCtoF(float c) { return c * 1.8 + 32; }
|
||||
|
||||
/*!
|
||||
* @brief Converts Fahrenheit to Celcius
|
||||
* @param f
|
||||
* value in Fahrenheit
|
||||
* @return float value in Celcius
|
||||
*/
|
||||
float DHT::convertFtoC(float f) { return (f - 32) * 0.55555; }
|
||||
|
||||
/*!
|
||||
* @brief Read Humidity
|
||||
* @param force
|
||||
* force read mode
|
||||
* @return float value - humidity in percent
|
||||
*/
|
||||
float DHT::readHumidity(bool force) {
|
||||
float f = NAN;
|
||||
if (read(force)) {
|
||||
switch (_type) {
|
||||
case DHT11:
|
||||
case DHT12:
|
||||
f = data[0] + data[1] * 0.1;
|
||||
break;
|
||||
case DHT22:
|
||||
case DHT21:
|
||||
f = ((word)data[0]) << 8 | data[1];
|
||||
f *= 0.1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return f;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Compute Heat Index
|
||||
* Simplified version that reads temp and humidity from sensor
|
||||
* @param isFahrenheit
|
||||
* true if fahrenheit, false if celcius (default
|
||||
*true)
|
||||
* @return float heat index
|
||||
*/
|
||||
float DHT::computeHeatIndex(bool isFahrenheit) {
|
||||
float hi = computeHeatIndex(readTemperature(isFahrenheit), readHumidity(),
|
||||
isFahrenheit);
|
||||
return hi;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Compute Heat Index
|
||||
* Using both Rothfusz and Steadman's equations
|
||||
* (http://www.wpc.ncep.noaa.gov/html/heatindex_equation.shtml)
|
||||
* @param temperature
|
||||
* temperature in selected scale
|
||||
* @param percentHumidity
|
||||
* humidity in percent
|
||||
* @param isFahrenheit
|
||||
* true if fahrenheit, false if celcius
|
||||
* @return float heat index
|
||||
*/
|
||||
float DHT::computeHeatIndex(float temperature, float percentHumidity,
|
||||
bool isFahrenheit) {
|
||||
float hi;
|
||||
|
||||
if (!isFahrenheit)
|
||||
temperature = convertCtoF(temperature);
|
||||
|
||||
hi = 0.5 * (temperature + 61.0 + ((temperature - 68.0) * 1.2) +
|
||||
(percentHumidity * 0.094));
|
||||
|
||||
if (hi > 79) {
|
||||
hi = -42.379 + 2.04901523 * temperature + 10.14333127 * percentHumidity +
|
||||
-0.22475541 * temperature * percentHumidity +
|
||||
-0.00683783 * pow(temperature, 2) +
|
||||
-0.05481717 * pow(percentHumidity, 2) +
|
||||
0.00122874 * pow(temperature, 2) * percentHumidity +
|
||||
0.00085282 * temperature * pow(percentHumidity, 2) +
|
||||
-0.00000199 * pow(temperature, 2) * pow(percentHumidity, 2);
|
||||
|
||||
if ((percentHumidity < 13) && (temperature >= 80.0) &&
|
||||
(temperature <= 112.0))
|
||||
hi -= ((13.0 - percentHumidity) * 0.25) *
|
||||
sqrt((17.0 - abs(temperature - 95.0)) * 0.05882);
|
||||
|
||||
else if ((percentHumidity > 85.0) && (temperature >= 80.0) &&
|
||||
(temperature <= 87.0))
|
||||
hi += ((percentHumidity - 85.0) * 0.1) * ((87.0 - temperature) * 0.2);
|
||||
}
|
||||
|
||||
return isFahrenheit ? hi : convertFtoC(hi);
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Read value from sensor or return last one from less than two
|
||||
*seconds.
|
||||
* @param force
|
||||
* true if using force mode
|
||||
* @return float value
|
||||
*/
|
||||
bool DHT::read(bool force) {
|
||||
// Check if sensor was read less than two seconds ago and return early
|
||||
// to use last reading.
|
||||
uint32_t currenttime = millis();
|
||||
if (!force && ((currenttime - _lastreadtime) < MIN_INTERVAL)) {
|
||||
return _lastresult; // return last correct measurement
|
||||
}
|
||||
_lastreadtime = currenttime;
|
||||
|
||||
// Reset 40 bits of received data to zero.
|
||||
data[0] = data[1] = data[2] = data[3] = data[4] = 0;
|
||||
|
||||
#if defined(ESP8266)
|
||||
yield(); // Handle WiFi / reset software watchdog
|
||||
#endif
|
||||
|
||||
// Send start signal. See DHT datasheet for full signal diagram:
|
||||
// http://www.adafruit.com/datasheets/Digital%20humidity%20and%20temperature%20sensor%20AM2302.pdf
|
||||
|
||||
// Go into high impedence state to let pull-up raise data line level and
|
||||
// start the reading process.
|
||||
pinMode(_pin, INPUT_PULLUP);
|
||||
delay(1);
|
||||
|
||||
// First set data line low for a period according to sensor type
|
||||
pinMode(_pin, OUTPUT);
|
||||
digitalWrite(_pin, LOW);
|
||||
switch (_type) {
|
||||
case DHT22:
|
||||
case DHT21:
|
||||
delayMicroseconds(1100); // data sheet says "at least 1ms"
|
||||
break;
|
||||
case DHT11:
|
||||
default:
|
||||
delay(20); // data sheet says at least 18ms, 20ms just to be safe
|
||||
break;
|
||||
}
|
||||
|
||||
uint32_t cycles[80];
|
||||
{
|
||||
// End the start signal by setting data line high for 40 microseconds.
|
||||
pinMode(_pin, INPUT_PULLUP);
|
||||
|
||||
// Delay a moment to let sensor pull data line low.
|
||||
delayMicroseconds(pullTime);
|
||||
|
||||
// Now start reading the data line to get the value from the DHT sensor.
|
||||
|
||||
// Turn off interrupts temporarily because the next sections
|
||||
// are timing critical and we don't want any interruptions.
|
||||
InterruptLock lock;
|
||||
|
||||
// First expect a low signal for ~80 microseconds followed by a high signal
|
||||
// for ~80 microseconds again.
|
||||
if (expectPulse(LOW) == TIMEOUT) {
|
||||
DEBUG_PRINTLN(F("DHT timeout waiting for start signal low pulse."));
|
||||
_lastresult = false;
|
||||
return _lastresult;
|
||||
}
|
||||
if (expectPulse(HIGH) == TIMEOUT) {
|
||||
DEBUG_PRINTLN(F("DHT timeout waiting for start signal high pulse."));
|
||||
_lastresult = false;
|
||||
return _lastresult;
|
||||
}
|
||||
|
||||
// Now read the 40 bits sent by the sensor. Each bit is sent as a 50
|
||||
// microsecond low pulse followed by a variable length high pulse. If the
|
||||
// high pulse is ~28 microseconds then it's a 0 and if it's ~70 microseconds
|
||||
// then it's a 1. We measure the cycle count of the initial 50us low pulse
|
||||
// and use that to compare to the cycle count of the high pulse to determine
|
||||
// if the bit is a 0 (high state cycle count < low state cycle count), or a
|
||||
// 1 (high state cycle count > low state cycle count). Note that for speed
|
||||
// all the pulses are read into a array and then examined in a later step.
|
||||
for (int i = 0; i < 80; i += 2) {
|
||||
cycles[i] = expectPulse(LOW);
|
||||
cycles[i + 1] = expectPulse(HIGH);
|
||||
}
|
||||
} // Timing critical code is now complete.
|
||||
|
||||
// Inspect pulses and determine which ones are 0 (high state cycle count < low
|
||||
// state cycle count), or 1 (high state cycle count > low state cycle count).
|
||||
for (int i = 0; i < 40; ++i) {
|
||||
uint32_t lowCycles = cycles[2 * i];
|
||||
uint32_t highCycles = cycles[2 * i + 1];
|
||||
if ((lowCycles == TIMEOUT) || (highCycles == TIMEOUT)) {
|
||||
DEBUG_PRINTLN(F("DHT timeout waiting for pulse."));
|
||||
_lastresult = false;
|
||||
return _lastresult;
|
||||
}
|
||||
data[i / 8] <<= 1;
|
||||
// Now compare the low and high cycle times to see if the bit is a 0 or 1.
|
||||
if (highCycles > lowCycles) {
|
||||
// High cycles are greater than 50us low cycle count, must be a 1.
|
||||
data[i / 8] |= 1;
|
||||
}
|
||||
// Else high cycles are less than (or equal to, a weird case) the 50us low
|
||||
// cycle count so this must be a zero. Nothing needs to be changed in the
|
||||
// stored data.
|
||||
}
|
||||
|
||||
DEBUG_PRINTLN(F("Received from DHT:"));
|
||||
DEBUG_PRINT(data[0], HEX);
|
||||
DEBUG_PRINT(F(", "));
|
||||
DEBUG_PRINT(data[1], HEX);
|
||||
DEBUG_PRINT(F(", "));
|
||||
DEBUG_PRINT(data[2], HEX);
|
||||
DEBUG_PRINT(F(", "));
|
||||
DEBUG_PRINT(data[3], HEX);
|
||||
DEBUG_PRINT(F(", "));
|
||||
DEBUG_PRINT(data[4], HEX);
|
||||
DEBUG_PRINT(F(" =? "));
|
||||
DEBUG_PRINTLN((data[0] + data[1] + data[2] + data[3]) & 0xFF, HEX);
|
||||
|
||||
// Check we read 40 bits and that the checksum matches.
|
||||
if (data[4] == ((data[0] + data[1] + data[2] + data[3]) & 0xFF)) {
|
||||
_lastresult = true;
|
||||
return _lastresult;
|
||||
} else {
|
||||
DEBUG_PRINTLN(F("DHT checksum failure!"));
|
||||
_lastresult = false;
|
||||
return _lastresult;
|
||||
}
|
||||
}
|
||||
|
||||
// Expect the signal line to be at the specified level for a period of time and
|
||||
// return a count of loop cycles spent at that level (this cycle count can be
|
||||
// used to compare the relative time of two pulses). If more than a millisecond
|
||||
// ellapses without the level changing then the call fails with a 0 response.
|
||||
// This is adapted from Arduino's pulseInLong function (which is only available
|
||||
// in the very latest IDE versions):
|
||||
// https://github.com/arduino/Arduino/blob/master/hardware/arduino/avr/cores/arduino/wiring_pulse.c
|
||||
uint32_t DHT::expectPulse(bool level) {
|
||||
#if (F_CPU > 16000000L)
|
||||
uint32_t count = 0;
|
||||
#else
|
||||
uint16_t count = 0; // To work fast enough on slower AVR boards
|
||||
#endif
|
||||
// On AVR platforms use direct GPIO port access as it's much faster and better
|
||||
// for catching pulses that are 10's of microseconds in length:
|
||||
#ifdef __AVR
|
||||
uint8_t portState = level ? _bit : 0;
|
||||
while ((*portInputRegister(_port) & _bit) == portState) {
|
||||
if (count++ >= _maxcycles) {
|
||||
return TIMEOUT; // Exceeded timeout, fail.
|
||||
}
|
||||
}
|
||||
// Otherwise fall back to using digitalRead (this seems to be necessary on
|
||||
// ESP8266 right now, perhaps bugs in direct port access functions?).
|
||||
#else
|
||||
while (digitalRead(_pin) == level) {
|
||||
if (count++ >= _maxcycles) {
|
||||
return TIMEOUT; // Exceeded timeout, fail.
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return count;
|
||||
}
|
||||
92
DHT.h
Normal file
92
DHT.h
Normal file
@@ -0,0 +1,92 @@
|
||||
/*!
|
||||
* @file DHT.h
|
||||
*
|
||||
* This is a library for DHT series of low cost temperature/humidity sensors.
|
||||
*
|
||||
* You must have Adafruit Unified Sensor Library library installed to use this class.
|
||||
*
|
||||
* Adafruit invests time and resources providing this open source code,
|
||||
* please support Adafruit andopen-source hardware by purchasing products
|
||||
* from Adafruit!
|
||||
*
|
||||
* Written by Adafruit Industries.
|
||||
*
|
||||
* MIT license, all text above must be included in any redistribution
|
||||
*/
|
||||
|
||||
#ifndef DHT_H
|
||||
#define DHT_H
|
||||
|
||||
#include "Arduino.h"
|
||||
|
||||
/* Uncomment to enable printing out nice debug messages. */
|
||||
//#define DHT_DEBUG
|
||||
|
||||
|
||||
#define DEBUG_PRINTER Serial /**< Define where debug output will be printed. */
|
||||
|
||||
/* Setup debug printing macros. */
|
||||
#ifdef DHT_DEBUG
|
||||
#define DEBUG_PRINT(...) { DEBUG_PRINTER.print(__VA_ARGS__); }
|
||||
#define DEBUG_PRINTLN(...) { DEBUG_PRINTER.println(__VA_ARGS__); }
|
||||
#else
|
||||
#define DEBUG_PRINT(...) {} /**< Debug Print Placeholder if Debug is disabled */
|
||||
#define DEBUG_PRINTLN(...) {} /**< Debug Print Line Placeholder if Debug is disabled */
|
||||
#endif
|
||||
|
||||
/* Define types of sensors. */
|
||||
#define DHT11 11 /**< DHT TYPE 11 */
|
||||
#define DHT12 12 /**< DHY TYPE 12 */
|
||||
#define DHT22 22 /**< DHT TYPE 22 */
|
||||
#define DHT21 21 /**< DHT TYPE 21 */
|
||||
#define AM2301 21 /**< AM2301 */
|
||||
|
||||
/*!
|
||||
* @brief Class that stores state and functions for DHT
|
||||
*/
|
||||
class DHT {
|
||||
public:
|
||||
DHT(uint8_t pin, uint8_t type, uint8_t count=6);
|
||||
void begin(uint8_t usec=55);
|
||||
float readTemperature(bool S=false, bool force=false);
|
||||
float convertCtoF(float);
|
||||
float convertFtoC(float);
|
||||
float computeHeatIndex(bool isFahrenheit=true);
|
||||
float computeHeatIndex(float temperature, float percentHumidity, bool isFahrenheit=true);
|
||||
float readHumidity(bool force=false);
|
||||
bool read(bool force=false);
|
||||
|
||||
private:
|
||||
uint8_t data[5];
|
||||
uint8_t _pin, _type;
|
||||
#ifdef __AVR
|
||||
// Use direct GPIO access on an 8-bit AVR so keep track of the port and bitmask
|
||||
// for the digital pin connected to the DHT. Other platforms will use digitalRead.
|
||||
uint8_t _bit, _port;
|
||||
#endif
|
||||
uint32_t _lastreadtime, _maxcycles;
|
||||
bool _lastresult;
|
||||
uint8_t pullTime; // Time (in usec) to pull up data line before reading
|
||||
|
||||
uint32_t expectPulse(bool level);
|
||||
|
||||
};
|
||||
|
||||
/*!
|
||||
* @brief Class that defines Interrupt Lock Avaiability
|
||||
*/
|
||||
class InterruptLock {
|
||||
public:
|
||||
InterruptLock() {
|
||||
#if !defined(ARDUINO_ARCH_NRF52)
|
||||
noInterrupts();
|
||||
#endif
|
||||
}
|
||||
~InterruptLock() {
|
||||
#if !defined(ARDUINO_ARCH_NRF52)
|
||||
interrupts();
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
103
IMG_0001.h
Normal file
103
IMG_0001.h
Normal file
@@ -0,0 +1,103 @@
|
||||
#ifndef _GxBootExample_H_
|
||||
#define _GxBootExample_H_
|
||||
|
||||
#if defined(ESP8266) || defined(ESP32)
|
||||
#include <pgmspace.h>
|
||||
#else
|
||||
#include <avr/pgmspace.h>
|
||||
#endif
|
||||
|
||||
// 180 x 64 (Boot)
|
||||
const unsigned char gImage_IMG_0001[1440] PROGMEM = { /* 0X01,0X01,0XB4,0X00,0X40,0X00, */
|
||||
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
|
||||
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
|
||||
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
|
||||
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF0,0XFF,0XFF,0XFF,0XFF,
|
||||
0XFF,0XFF,0XFF,0XC0,0X1F,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X07,0XFF,0XFF,0XFF,
|
||||
0XFF,0XFF,0XFF,0X00,0X01,0XFF,0XFF,0XFF,0XFF,0XFF,0XFC,0X01,0X80,0X3F,0XFF,0XFF,
|
||||
0XFF,0XFF,0XFC,0X01,0XE0,0X1F,0XFF,0XFF,0XFF,0XFF,0XF8,0X10,0XF8,0X0F,0XFF,0XFF,
|
||||
0XFF,0XFF,0XF8,0X70,0XF8,0X0F,0XFF,0XFF,0XFF,0XFF,0XF0,0X70,0XF8,0X0F,0XFF,0XFF,
|
||||
0XFF,0XFF,0XF0,0XF0,0XF8,0X07,0XFF,0XFF,0XFF,0XFF,0XE1,0XF0,0XFC,0X07,0XFF,0XFF,
|
||||
0XFF,0XFF,0XE1,0XF0,0XFC,0X03,0XFF,0XFF,0XFF,0XFF,0XE3,0XF0,0XFC,0X01,0XFF,0XFF,
|
||||
0XFF,0XFF,0XE3,0XF0,0XFC,0X21,0XFF,0XFF,0XFF,0XFF,0XC3,0XF8,0XFE,0X21,0XFF,0XFF,
|
||||
0XFF,0XFF,0XC3,0XF8,0XFE,0X10,0XFF,0XFF,0XFF,0XFF,0XC7,0XF8,0XFE,0X10,0XFF,0XFF,
|
||||
0XFF,0XFF,0XC7,0XF8,0XFE,0X10,0XFF,0XFF,0XFF,0XFF,0XC7,0XF8,0XFF,0X18,0XFF,0XFF,
|
||||
0XFF,0XFF,0XC7,0XF8,0XFF,0X08,0XFF,0XFF,0XFF,0XFF,0XC7,0XF0,0XFF,0X00,0XFF,0XFF,
|
||||
0XFF,0XFF,0XC7,0XF0,0XFF,0X80,0XFF,0XFF,0XFF,0XFF,0XC7,0XF0,0XFF,0X80,0X7F,0XFF,
|
||||
0XFF,0XFF,0XC7,0XF0,0XFF,0XC0,0X3F,0XFF,0XFF,0XFF,0X87,0XF1,0XFF,0XC0,0X3F,0XFF,
|
||||
0XFF,0XFF,0X07,0XF1,0XFF,0XE0,0X1F,0XFF,0XFF,0XFF,0X03,0XE1,0XFF,0XE0,0X1F,0XFF,
|
||||
0XFF,0XFE,0X01,0XE1,0XFF,0XF1,0X1F,0XFF,0XFF,0XFC,0X01,0XE1,0XFF,0XF1,0X1F,0XFF,
|
||||
0XFF,0XF8,0X21,0XE1,0XFF,0XF1,0X1F,0XFF,0XFF,0XE0,0X31,0XE1,0XFF,0XF1,0X1F,0XFF,
|
||||
0XFF,0XE0,0XF1,0XF1,0XFF,0XF1,0X1F,0XFF,0XFF,0XC1,0XF1,0XF0,0XFF,0XF1,0X0F,0XFF,
|
||||
0XFF,0X83,0XF1,0XF0,0XFF,0XF1,0X0F,0XFF,0XFF,0X87,0XF1,0XF0,0XFF,0XF1,0X0F,0XFF,
|
||||
0XFF,0X07,0XF1,0XF0,0XFF,0XF0,0X8F,0XFF,0XFF,0X0F,0XF1,0XF0,0XFF,0XF0,0X8F,0XFF,
|
||||
0XFE,0X0F,0XF1,0XF0,0XFF,0XF8,0X8F,0XFF,0XFE,0X1F,0XF1,0XF0,0XFF,0XF8,0X07,0XFF,
|
||||
0XFC,0X1F,0XE1,0XF0,0XFF,0XF8,0X07,0XFF,0XFC,0X1F,0XE1,0XF8,0XFF,0XFC,0X03,0XFF,
|
||||
0XFC,0X1F,0XE1,0XF8,0XFF,0XFC,0X03,0XFF,0XFC,0X07,0XE3,0XF8,0XFF,0XFC,0X23,0XFF,
|
||||
0XF8,0X01,0XE3,0XF8,0XFF,0XFE,0X21,0XFF,0XF8,0X00,0XE3,0XF8,0XFF,0XFE,0X21,0XFF,
|
||||
0XF8,0X43,0X23,0XF8,0XFF,0XFE,0X31,0XFF,0XF8,0X40,0X03,0XF8,0XFF,0XFE,0X31,0XFF,
|
||||
0XF0,0X60,0X03,0XF8,0XFF,0XFE,0X31,0XFF,0XF0,0XE0,0X03,0XF8,0XFF,0XFE,0X31,0XFF,
|
||||
0XE0,0XFC,0X03,0XF8,0X7F,0XFE,0X30,0XFF,0XE1,0XFF,0X83,0XF8,0X7F,0XFE,0X30,0XFF,
|
||||
0XE1,0XFF,0XC3,0XFC,0X7F,0XFE,0X30,0XFF,0XE1,0XFF,0XC3,0XFC,0X7F,0XFE,0X38,0XFF,
|
||||
0XE1,0XFF,0XE3,0XFC,0X3F,0XFE,0X38,0X7F,0XE1,0XFF,0XE3,0XFC,0X3F,0XFE,0X18,0X7F,
|
||||
0XE1,0XFF,0XE3,0XFC,0X3F,0XFE,0X18,0X3F,0XE1,0XFF,0XE3,0XFC,0X3F,0XFE,0X1C,0X3F,
|
||||
0XF1,0XFF,0XE3,0XFE,0X3F,0XFF,0X0C,0X3F,0XF1,0XFF,0XE3,0XFE,0X3F,0XFF,0X0E,0X3F,
|
||||
0XE1,0XFF,0XE3,0XFE,0X1F,0XFF,0X86,0X3F,0XE1,0XFF,0XE3,0XFE,0X1F,0XFF,0X86,0X3F,
|
||||
0XE1,0XFF,0XE3,0XFE,0X1F,0XFF,0X86,0X3F,0XE3,0XFF,0XE3,0XFE,0X1F,0XFF,0X86,0X3F,
|
||||
0XE3,0XFF,0XE3,0XFF,0X1F,0XFF,0X86,0X3F,0XC3,0XFF,0XE3,0XFF,0X1F,0XFF,0X8E,0X3F,
|
||||
0XC3,0XFF,0XE3,0XFF,0X1F,0XFF,0X8E,0X3F,0XC7,0XFF,0XC3,0XFF,0X1F,0XFF,0X8E,0X3F,
|
||||
0XC7,0XFF,0XC3,0XFF,0X0F,0XFF,0X8E,0X3F,0XC3,0XFF,0XC3,0XFF,0X0F,0XFF,0X8E,0X3F,
|
||||
0XC0,0X7F,0XC7,0XFF,0X0F,0XFF,0X8E,0X1F,0XC0,0X3F,0XC7,0XFF,0X0F,0XFF,0X8E,0X0F,
|
||||
0XE0,0X0F,0XC7,0XFF,0X0F,0XFF,0X86,0X0F,0XE2,0X07,0XC7,0XFF,0X0F,0XFF,0X87,0X0F,
|
||||
0XE3,0X07,0XC7,0XFF,0X8F,0XFF,0XC7,0X8F,0XE3,0X83,0X87,0XFF,0X8F,0XFF,0XC7,0X8F,
|
||||
0XE3,0XC0,0X87,0XFF,0X8F,0XFF,0XC7,0X0F,0XE3,0XE0,0X0F,0XFF,0X8F,0XFF,0XC7,0X0F,
|
||||
0XE3,0XF0,0X07,0XFF,0X87,0XFF,0XC7,0X1F,0XE3,0XF8,0X07,0XFF,0X87,0XFF,0XC7,0X1F,
|
||||
0XC3,0XFC,0X07,0XFF,0X87,0XFF,0XC7,0X1F,0XC3,0XFE,0X07,0XFF,0X87,0XFF,0XC3,0X0F,
|
||||
0XC3,0XFE,0X07,0XFF,0X87,0XFF,0XC3,0X0F,0XC7,0XFF,0X07,0XFF,0X87,0XFF,0XC3,0X8F,
|
||||
0XC1,0XFF,0XC7,0XFF,0X87,0XFF,0XE3,0X87,0XC0,0X3F,0X87,0XFF,0X87,0XFF,0XE3,0X83,
|
||||
0XC0,0X1F,0X87,0XFF,0X87,0XFF,0XE3,0X83,0XC0,0X1F,0X87,0XFF,0X87,0XFF,0XE3,0XC3,
|
||||
0XE2,0X07,0X87,0XFF,0X83,0XFF,0XE3,0XC3,0XE1,0X07,0X87,0XFF,0X83,0XFF,0XE3,0XC3,
|
||||
0XE1,0X81,0X87,0XFF,0X83,0XFF,0XE3,0XC3,0XE1,0XC0,0X87,0XFF,0XC3,0XFF,0XE3,0XC7,
|
||||
0XE1,0XE0,0X0F,0XFF,0XC3,0XFF,0XE3,0XC7,0XE1,0XF0,0X07,0XFF,0XC3,0XFF,0XE3,0XC7,
|
||||
0XE1,0XF8,0X0F,0XFF,0XC3,0XFF,0XE3,0XC7,0XE1,0XFE,0X0F,0XFF,0XC3,0XFF,0XE3,0XC7,
|
||||
0XF1,0XFF,0X0F,0XFF,0XC3,0XFF,0XE3,0XC3,0XF1,0XFF,0X0F,0XFF,0XE3,0XFF,0XE3,0XC3,
|
||||
0XF0,0XFF,0X0F,0XFF,0XE1,0XFF,0XE3,0XE3,0XF0,0XFF,0X8F,0XFF,0XE1,0XFF,0XE3,0XE3,
|
||||
0XF8,0X7F,0X8F,0XFF,0XE1,0XFF,0XE3,0XE3,0XF8,0X3F,0X8F,0XFF,0XE1,0XFF,0XE3,0XE3,
|
||||
0XFC,0X1F,0X87,0XFF,0XE1,0XFF,0XE3,0XE3,0XFE,0X0F,0X87,0XFF,0XE1,0XFF,0XE1,0XE3,
|
||||
0XFF,0X0F,0X87,0XFF,0XE1,0XFF,0XE1,0XE3,0XFF,0X87,0X87,0XFF,0XE1,0XFF,0XE1,0XE3,
|
||||
0XFF,0X83,0X87,0XFF,0XE1,0XFF,0XF1,0XC3,0XFF,0X81,0X87,0XFF,0XE1,0XFF,0XF1,0XC3,
|
||||
0XFF,0XC0,0X87,0XFF,0XE1,0XFF,0XF1,0X83,0XFF,0XE0,0X0F,0XFF,0XF1,0XFF,0XF1,0X87,
|
||||
0XFF,0XF0,0X0F,0XFF,0XF1,0XFF,0XF1,0X87,0XFF,0XFC,0X0F,0XFF,0XE0,0XFF,0XF1,0X8F,
|
||||
0XFF,0XFE,0X0F,0XFF,0XE0,0XFF,0XF1,0X8F,0XFF,0XFE,0X0F,0XFF,0XE0,0XFF,0XE1,0X8F,
|
||||
0XFF,0XFF,0X0F,0XFF,0XE0,0XFF,0XE1,0X8F,0XFF,0XFF,0X07,0XFF,0XF0,0XFF,0XE3,0X8F,
|
||||
0XFF,0XFF,0X87,0XFF,0XF0,0XFF,0XE3,0X8F,0XFF,0XFF,0X87,0XFF,0XF0,0XFF,0XE3,0X8F,
|
||||
0XFF,0XFF,0XC3,0XFF,0XF0,0XFF,0XE3,0X8F,0XFF,0XFF,0XC3,0XFF,0XF0,0XFF,0XE3,0X8F,
|
||||
0XFF,0XFF,0XC3,0XFF,0XF0,0XFF,0XE3,0X8F,0XFF,0XFF,0XE3,0XFF,0XF0,0XFF,0XE3,0X8F,
|
||||
0XFF,0XFF,0XE3,0XFF,0XF0,0XFF,0XE3,0X8F,0XFF,0XFF,0XE1,0XFF,0XF0,0XFF,0XE3,0X0F,
|
||||
0XFF,0XFF,0XE1,0XFF,0XF0,0XFF,0XE3,0X0F,0XFF,0XFF,0XE1,0XFF,0XF0,0XFF,0XE3,0X1F,
|
||||
0XFF,0XFF,0XF1,0XFF,0XF0,0XFF,0XE3,0X1F,0XFF,0XFF,0XF0,0XFF,0XF8,0XFF,0XE3,0X1F,
|
||||
0XFF,0XFF,0XF0,0XFF,0XF8,0X7F,0XE3,0X1F,0XFF,0XFF,0XF0,0XFF,0XF8,0X7F,0XC2,0X1F,
|
||||
0XFF,0XFF,0XF8,0X7F,0XF8,0X7F,0XC0,0X1F,0XFF,0XFF,0XF8,0X7F,0XF8,0X7F,0XC4,0X1F,
|
||||
0XFF,0XFF,0XF8,0X7F,0XF8,0X7F,0XC4,0X3F,0XFF,0XFF,0XFC,0X7F,0XF8,0X7F,0XC0,0X7F,
|
||||
0XFF,0XFF,0XFC,0X7F,0XF8,0X7F,0XC0,0X7F,0XFF,0XFF,0XFC,0X7F,0XF8,0X7F,0XC0,0XFF,
|
||||
0XFF,0XFF,0XFC,0X7F,0XF8,0X7F,0XE0,0XFF,0XFF,0XFF,0XFC,0X7F,0XF8,0X7F,0XE0,0XFF,
|
||||
0XFF,0XFF,0XFC,0X3F,0XF8,0X7F,0XE1,0XFF,0XFF,0XFF,0XFC,0X3F,0XF8,0X7F,0XE1,0XFF,
|
||||
0XFF,0XFF,0XFC,0X3F,0XF8,0X7F,0XC3,0XFF,0XFF,0XFF,0XFE,0X1F,0XF8,0X7F,0XC3,0XFF,
|
||||
0XFF,0XFF,0XFE,0X1F,0XF0,0X7F,0X83,0XFF,0XFF,0XFF,0XFE,0X0F,0XF0,0X7F,0X87,0XFF,
|
||||
0XFF,0XFF,0XFF,0X0F,0XF0,0X7F,0X8F,0XFF,0XFF,0XFF,0XFF,0X87,0XF0,0X7F,0X0F,0XFF,
|
||||
0XFF,0XFF,0XFF,0X87,0XF0,0X7E,0X0F,0XFF,0XFF,0XFF,0XFF,0XC3,0XF0,0X7E,0X0F,0XFF,
|
||||
0XFF,0XFF,0XFF,0XC1,0XF0,0X7E,0X1F,0XFF,0XFF,0XFF,0XFF,0XE1,0XF0,0X7C,0X3F,0XFF,
|
||||
0XFF,0XFF,0XFF,0XE0,0XF0,0X7C,0X3F,0XFF,0XFF,0XFF,0XFF,0XF0,0XF0,0X78,0X3F,0XFF,
|
||||
0XFF,0XFF,0XFF,0XF8,0X70,0X70,0X7F,0XFF,0XFF,0XFF,0XFF,0XF8,0X38,0X60,0X7F,0XFF,
|
||||
0XFF,0XFF,0XFF,0XFC,0X3C,0X20,0XFF,0XFF,0XFF,0XFF,0XFF,0XFC,0X1C,0X01,0XFF,0XFF,
|
||||
0XFF,0XFF,0XFF,0XFE,0X18,0X03,0XFF,0XFF,0XFF,0XFF,0XFF,0XFE,0X18,0X03,0XFF,0XFF,
|
||||
0XFF,0XFF,0XFF,0XFF,0X18,0X07,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X08,0X07,0XFF,0XFF,
|
||||
0XFF,0XFF,0XFF,0XFF,0X08,0X0F,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X00,0X1F,0XFF,0XFF,
|
||||
0XFF,0XFF,0XFF,0XFF,0X80,0X3F,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X7F,0XFF,0XFF,
|
||||
0XFF,0XFF,0XFF,0XFF,0XF8,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
|
||||
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
|
||||
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
|
||||
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
|
||||
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
|
||||
};
|
||||
#endif
|
||||
304
NTPtimeESP.cpp
Normal file
304
NTPtimeESP.cpp
Normal file
@@ -0,0 +1,304 @@
|
||||
/*
|
||||
NTP
|
||||
This routine gets the unixtime from a NTP server and adjusts it to the time zone and the
|
||||
Middle European summer time if requested
|
||||
|
||||
Author: Andreas Spiess V1.0 2016-5-28
|
||||
|
||||
Based on work from John Lassen: http://www.john-lassen.de/index.php/projects/esp-8266-arduino-ide-webconfig
|
||||
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "NTPtimeESP.h"
|
||||
|
||||
#define LEAP_YEAR(Y) ( ((1970+Y)>0) && !((1970+Y)%4) && ( ((1970+Y)%100) || !((1970+Y)%400) ) )
|
||||
|
||||
#define SEC_TO_MS 1000
|
||||
#define RECV_TIMEOUT_DEFATUL 1 // 1 second
|
||||
#define SEND_INTRVL_DEFAULT 1 // 1 second
|
||||
#define MAX_SEND_INTERVAL 60 // 60 seconds
|
||||
#define MAC_RECV_TIMEOUT 60 // 60 seconds
|
||||
|
||||
const int NTP_PACKET_SIZE = 48;
|
||||
byte _packetBuffer[ NTP_PACKET_SIZE];
|
||||
static const uint8_t _monthDays[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
|
||||
|
||||
float _timeZone=0.0;
|
||||
String _NTPserver="";
|
||||
|
||||
// NTPserver is the name of the NTPserver
|
||||
|
||||
bool NTPtime::setSendInterval(unsigned long _sendInterval_) {
|
||||
bool retVal = false;
|
||||
if(_sendInterval_ <= MAX_SEND_INTERVAL) {
|
||||
_sendInterval = _sendInterval_ * SEC_TO_MS;
|
||||
retVal = true;
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
bool NTPtime::setRecvTimeout(unsigned long _recvTimeout_) {
|
||||
bool retVal = false;
|
||||
if(_recvTimeout_ <= MAC_RECV_TIMEOUT) {
|
||||
_recvTimeout = _recvTimeout_ * SEC_TO_MS;
|
||||
retVal = true;
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
NTPtime::NTPtime() {
|
||||
_sendPhase = true;
|
||||
_sentTime = 0;
|
||||
_sendInterval = SEND_INTRVL_DEFAULT * SEC_TO_MS;
|
||||
_recvTimeout = RECV_TIMEOUT_DEFATUL * SEC_TO_MS;
|
||||
}
|
||||
NTPtime::NTPtime(String NTPserver) {
|
||||
_NTPserver = NTPserver;
|
||||
_sendPhase = true;
|
||||
_sentTime = 0;
|
||||
_sendInterval = SEND_INTRVL_DEFAULT * SEC_TO_MS;
|
||||
_recvTimeout = RECV_TIMEOUT_DEFATUL * SEC_TO_MS;
|
||||
}
|
||||
|
||||
void NTPtime::setNTPPool(String NTPserver) {
|
||||
_NTPserver = NTPserver;
|
||||
}
|
||||
|
||||
void NTPtime::printDateTime(strDateTime _dateTime) {
|
||||
if (_dateTime.valid) {
|
||||
Serial.print(_dateTime.year);
|
||||
Serial.print( "-");
|
||||
Serial.print(_dateTime.month);
|
||||
Serial.print( "-");
|
||||
Serial.print(_dateTime.day);
|
||||
Serial.print( "-");
|
||||
Serial.print(_dateTime.dayofWeek);
|
||||
Serial.print( " ");
|
||||
|
||||
Serial.print(_dateTime.hour);
|
||||
Serial.print( "H ");
|
||||
Serial.print(_dateTime.minute);
|
||||
Serial.print( "M ");
|
||||
Serial.print(_dateTime.second);
|
||||
Serial.print( "S ");
|
||||
Serial.println();
|
||||
} else {
|
||||
#ifdef DEBUG_ON
|
||||
Serial.println("Invalid time !!!");
|
||||
Serial.println("");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// Converts a unix time stamp to a strDateTime structure
|
||||
strDateTime NTPtime::ConvertUnixTimestamp( unsigned long _tempTimeStamp) {
|
||||
strDateTime _tempDateTime;
|
||||
uint8_t _year, _month, _monthLength;
|
||||
uint32_t _time;
|
||||
unsigned long _days;
|
||||
|
||||
_tempDateTime.epochTime = _tempTimeStamp;
|
||||
|
||||
_time = (uint32_t)_tempTimeStamp;
|
||||
_tempDateTime.second = _time % 60;
|
||||
_time /= 60; // now it is minutes
|
||||
_tempDateTime.minute = _time % 60;
|
||||
_time /= 60; // now it is hours
|
||||
_tempDateTime.hour = _time % 24;
|
||||
_time /= 24; // now it is _days
|
||||
_tempDateTime.dayofWeek = ((_time + 4) % 7) + 1; // Sunday is day 1
|
||||
|
||||
_year = 0;
|
||||
_days = 0;
|
||||
while ((unsigned)(_days += (LEAP_YEAR(_year) ? 366 : 365)) <= _time) {
|
||||
_year++;
|
||||
}
|
||||
_tempDateTime.year = _year; // year is offset from 1970
|
||||
|
||||
_days -= LEAP_YEAR(_year) ? 366 : 365;
|
||||
_time -= _days; // now it is days in this year, starting at 0
|
||||
|
||||
_days = 0;
|
||||
_month = 0;
|
||||
_monthLength = 0;
|
||||
for (_month = 0; _month < 12; _month++) {
|
||||
if (_month == 1) { // february
|
||||
if (LEAP_YEAR(_year)) {
|
||||
_monthLength = 29;
|
||||
} else {
|
||||
_monthLength = 28;
|
||||
}
|
||||
} else {
|
||||
_monthLength = _monthDays[_month];
|
||||
}
|
||||
|
||||
if (_time >= _monthLength) {
|
||||
_time -= _monthLength;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
_tempDateTime.month = _month + 1; // jan is month 1
|
||||
_tempDateTime.day = _time + 1; // day of month
|
||||
_tempDateTime.year += 1970;
|
||||
|
||||
return _tempDateTime;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Summertime calculates the daylight saving time for middle Europe. Input: Unixtime in UTC
|
||||
//
|
||||
boolean NTPtime::summerTime(unsigned long _timeStamp ) {
|
||||
|
||||
strDateTime _tempDateTime;
|
||||
_tempDateTime = ConvertUnixTimestamp(_timeStamp);
|
||||
// printTime("Innerhalb ", _tempDateTime);
|
||||
|
||||
if (_tempDateTime.month < 3 || _tempDateTime.month > 10) return false; // keine Sommerzeit in Jan, Feb, Nov, Dez
|
||||
if (_tempDateTime.month > 3 && _tempDateTime.month < 10) return true; // Sommerzeit in Apr, Mai, Jun, Jul, Aug, Sep
|
||||
if (_tempDateTime.month == 3 && (_tempDateTime.hour + 24 * _tempDateTime.day) >= (3 + 24 * (31 - (5 * _tempDateTime.year / 4 + 4) % 7)) || _tempDateTime.month == 10 && (_tempDateTime.hour + 24 * _tempDateTime.day) < (3 + 24 * (31 - (5 * _tempDateTime.year / 4 + 1) % 7)))
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean NTPtime::daylightSavingTime(unsigned long _timeStamp) {
|
||||
|
||||
strDateTime _tempDateTime;
|
||||
_tempDateTime = ConvertUnixTimestamp(_timeStamp);
|
||||
|
||||
// here the US code
|
||||
//return false;
|
||||
// see http://stackoverflow.com/questions/5590429/calculating-daylight-saving-time-from-only-date
|
||||
// since 2007 DST begins on second Sunday of March and ends on first Sunday of November.
|
||||
// Time change occurs at 2AM locally
|
||||
if (_tempDateTime.month < 3 || _tempDateTime.month > 11) return false; //January, february, and december are out.
|
||||
if (_tempDateTime.month > 3 && _tempDateTime.month < 11) return true; //April to October are in
|
||||
int previousSunday = _tempDateTime.day - (_tempDateTime.dayofWeek - 1); // dow Sunday input was 1,
|
||||
// need it to be Sunday = 0. If 1st of month = Sunday, previousSunday=1-0=1
|
||||
//int previousSunday = day - (dow-1);
|
||||
// -------------------- March ---------------------------------------
|
||||
//In march, we are DST if our previous Sunday was = to or after the 8th.
|
||||
if (_tempDateTime.month == 3 ) { // in march, if previous Sunday is after the 8th, is DST
|
||||
// unless Sunday and hour < 2am
|
||||
if ( previousSunday >= 8 ) { // Sunday = 1
|
||||
// return true if day > 14 or (dow == 1 and hour >= 2)
|
||||
return ((_tempDateTime.day > 14) ||
|
||||
((_tempDateTime.dayofWeek == 1 && _tempDateTime.hour >= 2) || _tempDateTime.dayofWeek > 1));
|
||||
} // end if ( previousSunday >= 8 && _dateTime.dayofWeek > 0 )
|
||||
else
|
||||
{
|
||||
// previousSunday has to be < 8 to get here
|
||||
//return (previousSunday < 8 && (_tempDateTime.dayofWeek - 1) = 0 && _tempDateTime.hour >= 2)
|
||||
return false;
|
||||
} // end else
|
||||
} // end if (_tempDateTime.month == 3 )
|
||||
|
||||
// ------------------------------- November -------------------------------
|
||||
|
||||
// gets here only if month = November
|
||||
//In november we must be before the first Sunday to be dst.
|
||||
//That means the previous Sunday must be before the 2nd.
|
||||
if (previousSunday < 1)
|
||||
{
|
||||
// is not true for Sunday after 2am or any day after 1st Sunday any time
|
||||
return ((_tempDateTime.dayofWeek == 1 && _tempDateTime.hour < 2) || (_tempDateTime.dayofWeek > 1));
|
||||
//return true;
|
||||
} // end if (previousSunday < 1)
|
||||
else
|
||||
{
|
||||
// return false unless after first wk and dow = Sunday and hour < 2
|
||||
return (_tempDateTime.day <8 && _tempDateTime.dayofWeek == 1 && _tempDateTime.hour < 2);
|
||||
} // end else
|
||||
} // end boolean NTPtime::daylightSavingTime(unsigned long _timeStamp)
|
||||
|
||||
|
||||
unsigned long NTPtime::adjustTimeZone(unsigned long _timeStamp, float _timeZone, int _DayLightSaving) {
|
||||
strDateTime _tempDateTime;
|
||||
_timeStamp += (unsigned long)(_timeZone * 3600.0); // adjust timezone
|
||||
if (_DayLightSaving ==1 && summerTime(_timeStamp)) _timeStamp += 3600; // European Summer time
|
||||
if (_DayLightSaving ==2 && daylightSavingTime(_timeStamp)) _timeStamp += 3600; // US daylight time
|
||||
return _timeStamp;
|
||||
}
|
||||
|
||||
// time zone is the difference to UTC in hours
|
||||
// if _isDayLightSaving is true, time will be adjusted accordingly
|
||||
// Use returned time only after checking "ret.valid" flag
|
||||
|
||||
strDateTime NTPtime::getNTPtime(float _timeZone, int _DayLightSaving) {
|
||||
int cb;
|
||||
strDateTime _dateTime;
|
||||
unsigned long _unixTime = 0;
|
||||
_dateTime.valid = false;
|
||||
unsigned long _currentTimeStamp;
|
||||
|
||||
if (_sendPhase) {
|
||||
if (_sentTime && ((millis() - _sentTime) < _sendInterval)) {
|
||||
return _dateTime;
|
||||
}
|
||||
|
||||
_sendPhase = false;
|
||||
UDPNTPClient.begin(1337); // Port for NTP receive
|
||||
|
||||
#ifdef DEBUG_ON
|
||||
IPAddress _timeServerIP;
|
||||
WiFi.hostByName(_NTPserver.c_str(), _timeServerIP);
|
||||
Serial.println();
|
||||
Serial.println(_timeServerIP);
|
||||
Serial.println("Sending NTP packet (timout = "+String(_recvTimeout)+" , minInterval = "+String(_sendInterval)+" )");
|
||||
#endif
|
||||
|
||||
memset(_packetBuffer, 0, NTP_PACKET_SIZE);
|
||||
_packetBuffer[0] = 0b11100011; // LI, Version, Mode
|
||||
_packetBuffer[1] = 0; // Stratum, or type of clock
|
||||
_packetBuffer[2] = 6; // Polling Interval
|
||||
_packetBuffer[3] = 0xEC; // Peer Clock Precision
|
||||
_packetBuffer[12] = 49;
|
||||
_packetBuffer[13] = 0x4E;
|
||||
_packetBuffer[14] = 49;
|
||||
_packetBuffer[15] = 52;
|
||||
UDPNTPClient.beginPacket(_NTPserver.c_str(), 123);
|
||||
UDPNTPClient.write(_packetBuffer, NTP_PACKET_SIZE);
|
||||
UDPNTPClient.endPacket();
|
||||
|
||||
_sentTime = millis();
|
||||
} else {
|
||||
cb = UDPNTPClient.parsePacket();
|
||||
if (cb == 0) {
|
||||
#ifdef DEBUG_ON
|
||||
Serial.println("NTP packet received, length=0 (OOPS!) query sent since " + String((millis() - _sentTime)) + " ms ");
|
||||
|
||||
#endif
|
||||
if ((millis() - _sentTime) > _recvTimeout) {
|
||||
_sendPhase = true;
|
||||
_sentTime = 0;
|
||||
}
|
||||
} else {
|
||||
#ifdef DEBUG_ON
|
||||
Serial.print("NTP packet received, length=");
|
||||
Serial.println(cb);
|
||||
#endif
|
||||
|
||||
UDPNTPClient.read(_packetBuffer, NTP_PACKET_SIZE); // read the packet into the buffer
|
||||
unsigned long highWord = word(_packetBuffer[40], _packetBuffer[41]);
|
||||
unsigned long lowWord = word(_packetBuffer[42], _packetBuffer[43]);
|
||||
unsigned long secsSince1900 = highWord << 16 | lowWord;
|
||||
const unsigned long seventyYears = 2208988800UL;
|
||||
_unixTime = secsSince1900 - seventyYears;
|
||||
if (_unixTime > 0) {
|
||||
_currentTimeStamp = adjustTimeZone(_unixTime, _timeZone, _DayLightSaving);
|
||||
_dateTime = ConvertUnixTimestamp(_currentTimeStamp);
|
||||
_dateTime.valid = true;
|
||||
} else
|
||||
_dateTime.valid = false;
|
||||
|
||||
_sendPhase = true;
|
||||
}
|
||||
}
|
||||
|
||||
return _dateTime;
|
||||
}
|
||||
60
NTPtimeESP.h
Normal file
60
NTPtimeESP.h
Normal file
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
|
||||
NTPtime for ESP8266/ESP32
|
||||
This routine gets the unixtime from a NTP server and adjusts it to the time zone and the
|
||||
Middle European summer time if requested
|
||||
|
||||
Author: Andreas Spiess V1.0 2016-6-28
|
||||
|
||||
Based on work from John Lassen: http://www.john-lassen.de/index.php/projects/esp-8266-arduino-ide-webconfig
|
||||
|
||||
*/
|
||||
#ifndef NTPtime_h
|
||||
#define NTPtime_h
|
||||
|
||||
// #define DEBUG_ON
|
||||
|
||||
#include <Arduino.h>
|
||||
#ifdef ESP32
|
||||
#include <WiFi.h>
|
||||
#else
|
||||
#include <ESP8266WiFi.h>
|
||||
#endif
|
||||
#include <WiFiUdp.h>
|
||||
|
||||
struct strDateTime
|
||||
{
|
||||
byte hour;
|
||||
byte minute;
|
||||
byte second;
|
||||
int year;
|
||||
byte month;
|
||||
byte day;
|
||||
byte dayofWeek;
|
||||
unsigned long epochTime;
|
||||
boolean valid;
|
||||
};
|
||||
|
||||
class NTPtime {
|
||||
public:
|
||||
NTPtime();
|
||||
NTPtime(String NTPtime);
|
||||
strDateTime getNTPtime(float _timeZone, int _DayLightSaving);
|
||||
void setNTPPool(String NTPtime);
|
||||
void printDateTime(strDateTime _dateTime);
|
||||
bool setSendInterval(unsigned long _sendInterval); // in seconds
|
||||
bool setRecvTimeout(unsigned long _recvTimeout); // in seconds
|
||||
|
||||
private:
|
||||
bool _sendPhase;
|
||||
unsigned long _sentTime;
|
||||
unsigned long _sendInterval;
|
||||
unsigned long _recvTimeout;
|
||||
|
||||
strDateTime ConvertUnixTimestamp( unsigned long _tempTimeStamp);
|
||||
boolean summerTime(unsigned long _timeStamp );
|
||||
boolean daylightSavingTime(unsigned long _timeStamp);
|
||||
unsigned long adjustTimeZone(unsigned long _timeStamp, float _timeZone, int _DayLightSavingSaving);
|
||||
WiFiUDP UDPNTPClient;
|
||||
};
|
||||
#endif
|
||||
367
arduino-envirstation.ino
Normal file
367
arduino-envirstation.ino
Normal file
@@ -0,0 +1,367 @@
|
||||
|
||||
// Arduino Environment Station for Wemos D1 Mini
|
||||
|
||||
// BUSY -> D0 = GPIO16
|
||||
// RST -> D4 = GPIO2
|
||||
// DC -> D3 = GPIO0
|
||||
// CS -> D8 = GPIO15 (ADD 1Kohm pulldown resistor !!!)
|
||||
// CLK -> D5 = GPIO14
|
||||
// DIN -> D7 = GPIO13
|
||||
// GND -> GND
|
||||
// 3.3V -> 3.3V
|
||||
|
||||
|
||||
#include <Wire.h>
|
||||
#include <SPI.h>
|
||||
#include <Adafruit_BMP280.h>
|
||||
#include <ESP8266WiFi.h>
|
||||
|
||||
#define BMP_SCK (13)
|
||||
#define BMP_MISO (12)
|
||||
#define BMP_MOSI (11)
|
||||
#define BMP_CS (10)
|
||||
|
||||
#define HEAD_HEIGHT 18
|
||||
|
||||
#include "DHT.h"
|
||||
// DHT -> D6
|
||||
#define DHTPIN 12 // DHT Sensor PIN
|
||||
#define DHTTYPE DHT22 // DHT Sensor Type
|
||||
|
||||
#include "NTPtimeESP.h"
|
||||
|
||||
#include <GxEPD.h>
|
||||
#include <GxGDEW042T2/GxGDEW042T2.h>
|
||||
|
||||
#include GxEPD_BitmapExamples
|
||||
|
||||
// FreeFonts from Adafruit_GFX
|
||||
#include <Fonts/FreeMonoBold9pt7b.h>
|
||||
#include <Fonts/FreeMonoBold12pt7b.h>
|
||||
#include <Fonts/FreeMonoBold18pt7b.h>
|
||||
#include <Fonts/FreeMonoBold24pt7b.h>
|
||||
#include <Fonts/FreeSansBold18pt7b.h>
|
||||
|
||||
#include <GxIO/GxIO_SPI/GxIO_SPI.h>
|
||||
#include <GxIO/GxIO.h>
|
||||
|
||||
|
||||
|
||||
char *ssid = "lauIOT"; // Set you WiFi SSID
|
||||
char *password = "superiot1"; // Set you WiFi password
|
||||
|
||||
GxIO_Class io(SPI, /*CS=D8*/ SS, /*DC=D3*/ 0, /*RST=D4*/ 2); // arbitrary selection of D3(=0), D4(=2), selected for default of GxEPD_Class
|
||||
GxEPD_Class display(io, /*RST=D4*/ 2, /*BUSY=D0*/ 16); // default selection of D4(=2), D2(=4)
|
||||
|
||||
/*
|
||||
Display config
|
||||
|
||||
----------------------------------
|
||||
| header of 28 pixel height |
|
||||
----------------------------------
|
||||
| widget 0 | widget 1 | widget 2 |
|
||||
| 130x90 | 130x90 | 130x90 |
|
||||
| at 2,30 | at 135,30| at 138,30|
|
||||
----------------------------------
|
||||
| widget 3 | widget 4 | widget 5 |
|
||||
| 130x90 | 130x90 | 130x90 |
|
||||
| at 2,120 | at135,120| at138,120|
|
||||
----------------------------------
|
||||
| widget 6 | widget 7 | widget 8 |
|
||||
| 130x90 | 130x90 | 130x90 |
|
||||
| at 2,210 | at135,210| at138,210|
|
||||
----------------------------------
|
||||
|
||||
TODOS
|
||||
-----
|
||||
- Draw Head OK
|
||||
- - Draw Title OK
|
||||
- Draw Widget OK
|
||||
- Integrate TempS OK
|
||||
- - Draw T° OK
|
||||
- - Draw HR OK
|
||||
- Integrate Pressure OK
|
||||
- - Draw Press OK
|
||||
- Draw min max OK
|
||||
- Integrate Time OK
|
||||
- - Draw Time OK
|
||||
- Get variables private
|
||||
- Publish REST API
|
||||
- Show Temp & hydro graph
|
||||
- Make formatting nicer :)
|
||||
- Make it configurable (web interface)
|
||||
- Timezone
|
||||
- Wifi credentials
|
||||
- reset high low
|
||||
*/
|
||||
|
||||
DHT dht(DHTPIN, DHTTYPE); // Initialize DHT sensor for normal 16mhz Arduino
|
||||
|
||||
unsigned long ref_time;
|
||||
|
||||
Adafruit_BMP280 bmp; // I2C
|
||||
|
||||
NTPtime NTPch("ntp.belnet.be"); // Choose server pool as required
|
||||
WiFiServer server(80);
|
||||
|
||||
class Environment {
|
||||
public:
|
||||
strDateTime dateTime;
|
||||
|
||||
Environment() {
|
||||
this->temperature_min = 100.0;
|
||||
this->temperature_max = -100.0;
|
||||
this->humidity_min = 100.0;
|
||||
this->humidity_max = 0.0;
|
||||
this->pressure_min = 9999;
|
||||
this->pressure_max = 0;
|
||||
}
|
||||
void setDateTime(strDateTime d) {
|
||||
dateTime = d;
|
||||
}
|
||||
void setTemp(float t) {
|
||||
this->temperature = t;
|
||||
if(this->temperature < this->temperature_min ) this->temperature_min = this->temperature;
|
||||
if(this->temperature > this->temperature_max ) this->temperature_max = this->temperature;
|
||||
}
|
||||
void setPress(unsigned int p) {
|
||||
this->pressure = p;
|
||||
if(this->pressure < this->pressure_min ) this->pressure_min = this->pressure;
|
||||
if(this->pressure > this->pressure_max ) this->pressure_max = this->pressure;
|
||||
}
|
||||
void setHumid(float h) {
|
||||
this->humidity = h;
|
||||
if(this->humidity < this->humidity_min ) this->humidity_min = this->humidity;
|
||||
if(this->humidity > this->humidity_max ) this->humidity_max = this->humidity;
|
||||
}
|
||||
float getTemp() { return this->temperature; }
|
||||
float getTempMin() { return this->temperature_min; }
|
||||
float getTempMax() { return this->temperature_max; }
|
||||
unsigned int getPress() { return this->pressure; }
|
||||
unsigned int getPressMin() { return this->pressure_min; }
|
||||
unsigned int getPressMax() { return this->pressure_max; }
|
||||
float getHumid() { return this->humidity; }
|
||||
float getHumidMin() { return this->humidity_min; }
|
||||
float getHumidMax() { return this->humidity_max; }
|
||||
String formatDate() {
|
||||
char strdatetime[32];
|
||||
if(this->dateTime.valid) {
|
||||
NTPch.printDateTime(this->dateTime);
|
||||
sprintf(strdatetime,"%02i/%02i/%04i %02i:%02i\0",this->dateTime.day,this->dateTime.month,this->dateTime.year,this->dateTime.hour,this->dateTime.minute);
|
||||
return String(strdatetime);
|
||||
}
|
||||
else {
|
||||
return String("invalid date");
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
float temperature;
|
||||
float temperature_min;
|
||||
float temperature_max;
|
||||
float humidity;
|
||||
float humidity_min;
|
||||
float humidity_max;
|
||||
unsigned int pressure;
|
||||
unsigned int pressure_min;
|
||||
unsigned int pressure_max;
|
||||
};
|
||||
|
||||
Environment E;
|
||||
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(115200);
|
||||
Serial.println();
|
||||
Serial.println("Start setup");
|
||||
/* SETUP WIFI */
|
||||
Serial.println("Connecting to Wi-Fi");
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin (ssid, password);
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
Serial.print(".");
|
||||
delay(500);
|
||||
}
|
||||
Serial.println(".");
|
||||
Serial.println("WiFi connected.");
|
||||
// Start the server
|
||||
server.begin();
|
||||
Serial.println("Server started");
|
||||
|
||||
Serial.print("Use this URL : http://");
|
||||
Serial.print(WiFi.localIP());
|
||||
Serial.println("/");
|
||||
|
||||
/* SETUP TEMP SENSOR */
|
||||
display.init(115200); // enable diagnostic output on Serial
|
||||
Serial.println("setup dht");
|
||||
dht.begin();
|
||||
|
||||
/* SETUP PRESSURE SENSOR */
|
||||
Serial.println("setup pressure sensor");
|
||||
if (!bmp.begin()) {
|
||||
Serial.println(F("Could not find a valid BMP280 sensor, check wiring!"));
|
||||
while (1);
|
||||
}
|
||||
bmp.setSampling(Adafruit_BMP280::MODE_NORMAL, /* Operating Mode. */
|
||||
Adafruit_BMP280::SAMPLING_X2, /* Temp. oversampling */
|
||||
Adafruit_BMP280::SAMPLING_X16, /* Pressure oversampling */
|
||||
Adafruit_BMP280::FILTER_X16, /* Filtering. */
|
||||
Adafruit_BMP280::STANDBY_MS_500); /* Standby time. */
|
||||
|
||||
/* SETUP NTP */
|
||||
NTPch.setSendInterval(1); // set interval to 3 seconds
|
||||
NTPch.setRecvTimeout(1); // in seconds
|
||||
|
||||
/* INIT min/max to something that will be overwrited */
|
||||
E = Environment();
|
||||
|
||||
Serial.println("setup done");
|
||||
|
||||
delay(1000);
|
||||
ref_time = millis();
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
|
||||
WiFiClient client = server.available();
|
||||
|
||||
if ( (millis() - ref_time) >= 10000 ) {
|
||||
// On attends depuis plus de 10 secondes.
|
||||
Serial.println("Start Evt");
|
||||
retreiveData(&E);
|
||||
drawDisplay(&E);
|
||||
ref_time = millis();
|
||||
}
|
||||
|
||||
if (!client) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Wait until the client sends some data
|
||||
Serial.println("new client");
|
||||
while(!client.available()) {
|
||||
delay(1);
|
||||
}
|
||||
|
||||
char request[256]; /* Path max length */
|
||||
char xmlFeedback[256];
|
||||
char delim[] = "/ ";
|
||||
client.readStringUntil('\r').toCharArray(request,128);
|
||||
char *ptr = strtok(request, delim);
|
||||
String path[8]; /* MAXIMUM DEPTH OF 6 */
|
||||
|
||||
for(int i=0;ptr != NULL;i++) {
|
||||
path[i] = ptr;
|
||||
ptr = strtok(NULL, delim);
|
||||
}
|
||||
|
||||
Serial.println(" Request with arguments : [" + (String)path[1] + "][" + (String)path[2] + "][" + (String)path[3] + "] ");
|
||||
if(path[1].equals("GET")) {
|
||||
if(path[2].equals("ENVIR")) {
|
||||
sprintf(xmlFeedback,"<temperature>%.2f</temperature><humidity>%.2f</humidity><pressure>%u</pressure>",E.getTemp(),E.getHumid(),E.getPress());
|
||||
client.println("<xml>" + (String)xmlFeedback + "</xml>");
|
||||
}
|
||||
}
|
||||
/*
|
||||
* PATH 1 = GET or SET
|
||||
* PATH 2 = INSTRUCTION
|
||||
* PATH 3 = PARAMETER
|
||||
*/
|
||||
else {
|
||||
client.println("Message received !!!\n");
|
||||
}
|
||||
delay(1);
|
||||
}
|
||||
|
||||
void retreiveData(Environment *E) {
|
||||
/* RETREIVE DATA'S */
|
||||
E->setDateTime(NTPch.getNTPtime(1.0, 1));
|
||||
delay(400);
|
||||
E->setTemp( dht.readTemperature() );
|
||||
E->setHumid( dht.readHumidity() );
|
||||
E->setPress( (int)round(bmp.readPressure()/100) );
|
||||
E->setDateTime( NTPch.getNTPtime(1.0, 1));
|
||||
Serial.println("temp : " + String(E->getTemp()));
|
||||
Serial.println("humid : " + String(E->getHumid()));
|
||||
Serial.println("pressure : " + String(E->getPress()));
|
||||
}
|
||||
|
||||
void drawDisplay(Environment *E) {
|
||||
/* DRAW DATA'S */
|
||||
drawHead(E);
|
||||
drawWidget(0,"temperature", &FreeSansBold18pt7b, String(E->getTemp(),1)+"°c" , String(E->getTempMin(),1)+"c", String(E->getTempMax(),1)+"c");
|
||||
drawWidget(1,"hygrometry" , &FreeSansBold18pt7b, String(E->getHumid() ,1)+"%" , String(E->getHumidMin(),1)+"%" , String(E->getHumidMax(),1)+"%" );
|
||||
drawWidget(2,"pressure" , &FreeSansBold18pt7b, String(E->getPress() )+"Pa" , String(E->getPressMin() )+"pa" , String(E->getPressMax() )+"pa" );
|
||||
/*
|
||||
drawWidget(3,"T min-max" , &FreeMonoBold12pt7b, "bbbb", String("0123"), String("1234"));
|
||||
drawWidget(4,"H min-max" , &FreeMonoBold12pt7b, "cccc", String("0123"), String("1234"));
|
||||
drawWidget(5,"F" , &FreeMonoBold12pt7b, "dddd", String("0123"), String("1234"));
|
||||
drawWidget(6,"G" , &FreeMonoBold12pt7b, "eeee", String("0123"), String("1234"));
|
||||
drawWidget(7,"H" , &FreeMonoBold12pt7b, "ffff", String("0123"), String("1234"));
|
||||
drawWidget(8,"I" , &FreeMonoBold12pt7b, "gggg", String("0123"), String("1234"));
|
||||
*/
|
||||
display.update();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void drawHead(Environment *E) {
|
||||
// Serial.println("Draw Head");
|
||||
int16_t x1, y1;
|
||||
uint16_t w, h;
|
||||
String mydate(E->formatDate());
|
||||
display.fillScreen(GxEPD_BLACK);
|
||||
display.fillRect(0, HEAD_HEIGHT, 400, 2, GxEPD_BLACK);
|
||||
|
||||
display.setTextColor(GxEPD_WHITE);
|
||||
display.setFont(&FreeMonoBold12pt7b);
|
||||
display.getTextBounds(mydate, 0, 0, &x1, &y1, &w, &h);
|
||||
display.setCursor( (int)((400-w)/2) , HEAD_HEIGHT-2 );
|
||||
display.println(mydate);
|
||||
}
|
||||
|
||||
void drawWidget(unsigned char widgetPos, String title, const GFXfont* f, String value, String vmin, String vmax) {
|
||||
/* Header 20px height, Value 70px height */
|
||||
int16_t x1, y1;
|
||||
uint16_t w, h;
|
||||
Serial.print("Draw Widget "); Serial.println(widgetPos);
|
||||
uint16_t box_w = 130;
|
||||
uint16_t box_h = 86;
|
||||
uint16_t box_x = (int)(((widgetPos%3)*133)+2);
|
||||
uint16_t box_y = (int)(((widgetPos/3)*(box_h+2))+HEAD_HEIGHT+1);
|
||||
|
||||
uint16_t cursor_y = box_y;
|
||||
display.fillRect(box_x, box_y, box_w, box_h, GxEPD_WHITE);
|
||||
//display.setCursor(box_x, box_y + 16 );
|
||||
|
||||
display.setFont(&FreeMonoBold9pt7b);
|
||||
display.setTextColor(GxEPD_BLACK);
|
||||
display.getTextBounds(title, 0, 0, &x1, &y1, &w, &h);
|
||||
display.setCursor( (int)(box_x + (130-w)/2) , (int)(box_y + h + 0 ) );
|
||||
display.print(title);
|
||||
|
||||
/* Display value */
|
||||
display.setFont(f);
|
||||
display.getTextBounds(value, 0, 0, &x1, &y1, &w, &h);
|
||||
display.setCursor( (int)(box_x + (box_w-w)/2) , (int)(box_y + 40 + ((40-h)/2)) );
|
||||
display.print(value);
|
||||
|
||||
/* Display min value */
|
||||
display.setFont(&FreeMonoBold9pt7b);
|
||||
display.getTextBounds(vmin, 0, 0, &x1, &y1, &w, &h);
|
||||
display.setCursor( (int)(box_x + 6) , (int)(box_y + 62) );
|
||||
display.print("min:" + vmin);
|
||||
|
||||
/* Display max value */
|
||||
display.setFont(&FreeMonoBold9pt7b);
|
||||
display.getTextBounds(vmax, 0, 0, &x1, &y1, &w, &h);
|
||||
display.setCursor( (int)(box_x + 6) , (int)(box_y + 76) );
|
||||
display.print("max:" + vmax);
|
||||
|
||||
//vmin
|
||||
|
||||
// display.updateWindow(box_x, box_y, box_w, box_h, true);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user