2018-10-30 15:17:56 +08:00
|
|
|
/*
|
|
|
|
|
|
|
|
AirQuality.ino
|
2018-11-06 13:18:39 +08:00
|
|
|
|
2019-01-20 14:35:02 +08:00
|
|
|
ATMEGA328P (Arduino UNO/Pro Trinket) only
|
2018-10-30 15:17:56 +08:00
|
|
|
|
|
|
|
Universal 8bit Graphics Library (https://github.com/olikraus/u8g2/)
|
|
|
|
|
2018-11-06 13:18:39 +08:00
|
|
|
Copyright (c) 2018, olikraus@gmail.com
|
2018-10-30 15:17:56 +08:00
|
|
|
All rights reserved.
|
|
|
|
|
|
|
|
Redistribution and use in source and binary forms, with or without modification,
|
|
|
|
are permitted provided that the following conditions are met:
|
|
|
|
|
|
|
|
* Redistributions of source code must retain the above copyright notice, this list
|
|
|
|
of conditions and the following disclaimer.
|
|
|
|
|
|
|
|
* Redistributions in binary form must reproduce the above copyright notice, this
|
|
|
|
list of conditions and the following disclaimer in the documentation and/or other
|
|
|
|
materials provided with the distribution.
|
|
|
|
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
|
|
|
CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
|
|
|
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
|
|
|
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
|
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
|
|
|
|
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
|
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
|
|
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
|
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
|
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
|
|
|
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
|
|
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
|
|
|
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
|
2018-11-10 06:17:55 +08:00
|
|
|
http://shelvin.de/arduino-in-den-sleep_mode_pwr_down-schlaf-modus-setzen/
|
|
|
|
|
2018-12-10 06:28:09 +08:00
|
|
|
|
|
|
|
battery symbol
|
|
|
|
trinket 3.3V requires at least 3.5V (https://learn.adafruit.com/introducing-pro-trinket/pinouts)
|
|
|
|
Assuming 3x AAA and a fresh cell with 1.5V: --> 4.5V
|
|
|
|
There are 6 battery symbols
|
|
|
|
symbol 0: < 3.5V
|
|
|
|
symbol 1: 3.5 - 3.7
|
|
|
|
symbol 2: 3.7 - 3.9
|
|
|
|
symbol 3: 3.9 - 4.1
|
|
|
|
symbol 4: 4.1 - 4-5
|
|
|
|
symbol 5: >= 4.5V
|
|
|
|
1V difference --> 5 steps --> 1V / 5 --> 0.2V step
|
2018-12-16 06:43:11 +08:00
|
|
|
|
|
|
|
R1 = 470K
|
|
|
|
R2 = 100K
|
|
|
|
Rsum = 570K
|
2018-12-24 02:12:16 +08:00
|
|
|
|
|
|
|
analogRead will use the internal 1.1V reference signal
|
|
|
|
|
2018-12-16 06:43:11 +08:00
|
|
|
Um = 100K * 4.5V / 570K = 0.7894 V --> *1024/1.1 --> 735 (analogRead)
|
|
|
|
Um = 100K * 3.5V / 570K = 0.6140 V --> 571 (analogRead)
|
2018-12-10 06:28:09 +08:00
|
|
|
|
2018-12-16 06:43:11 +08:00
|
|
|
735 - 571 = 164 --> 33
|
2018-12-24 02:12:16 +08:00
|
|
|
|
|
|
|
analogRead*1.1/1024 = Um, Um = 100 * Ubat/570
|
|
|
|
analogRead*1.1/1024 = 10 * Ubat/57
|
|
|
|
analogRead*1.1*57/10240 = Ubat
|
|
|
|
Ubat = analogRead*1.1*57/10240 = analogRead*0.006123 = analogRead/163
|
|
|
|
|
|
|
|
|
2018-10-30 15:17:56 +08:00
|
|
|
*/
|
|
|
|
|
2018-11-10 06:17:55 +08:00
|
|
|
|
2018-10-30 15:17:56 +08:00
|
|
|
#include <Arduino.h>
|
|
|
|
#include <Wire.h>
|
|
|
|
#include <U8g2lib.h>
|
|
|
|
#include <Adafruit_SGP30.h>
|
2018-12-10 06:28:09 +08:00
|
|
|
#include <SHTSensor.h> // adafruit sht31 lib v1.0.0 has a wrong 500 milliseconds delay, so use the SHTSensor.h lib
|
2018-11-06 13:18:39 +08:00
|
|
|
#include <avr/sleep.h>
|
2018-10-30 15:17:56 +08:00
|
|
|
|
|
|
|
|
2018-12-09 19:42:41 +08:00
|
|
|
//===================================================
|
|
|
|
// Constants: Timing values for the sensor & display state machine
|
|
|
|
// all values are "seconds"
|
|
|
|
|
|
|
|
// define startup calibration time: 2h
|
|
|
|
//#define STARTUP_TIME (60*60*2)
|
2019-01-04 06:36:05 +08:00
|
|
|
// five minutes
|
|
|
|
#define STARTUP_TIME (60*5)
|
2018-12-09 19:42:41 +08:00
|
|
|
|
|
|
|
// define duration after which the display is disabled: 30 seconds
|
|
|
|
#define DISPLAY_TIME ((30))
|
|
|
|
|
|
|
|
// Datasheet, page 8:
|
|
|
|
// For the first 15s after the “Init_air_quality” command the sensor
|
|
|
|
// is in an initialization phase during which a “Measure_air_quality”
|
|
|
|
// command returns fixed values
|
|
|
|
// --> sensor warmup time
|
|
|
|
#define SENSOR_WARMUP_TIME ((16))
|
|
|
|
|
2018-12-16 06:43:11 +08:00
|
|
|
// SENSOR_MEASURE_TIME defines the duration how long the measurement
|
|
|
|
// shell be aktive (after SENSOR_WARMUP_TIME)
|
2018-12-09 19:42:41 +08:00
|
|
|
#define SENSOR_MEASURE_TIME (14)
|
|
|
|
|
|
|
|
// This is the time, after which the gas sensor should do another measurement.
|
|
|
|
// The sum of SENSOR_WARMUP_TIME and SENSOR_MEASURE_TIME must
|
|
|
|
// be lesser than SENSOR_SAMPLE_TIME
|
2019-01-04 06:36:05 +08:00
|
|
|
// every 7 minutes, so there are at least two measures per history entry
|
|
|
|
#define SENSOR_SAMPLE_TIME (7*60)
|
2018-12-09 19:42:41 +08:00
|
|
|
|
|
|
|
// This is the time after which a new history entry is generated
|
|
|
|
// There are 96 entries in the history table (HIST_CNT)
|
2019-01-04 06:36:05 +08:00
|
|
|
// 15*60 = 15 minutes --> 96 * 15 minutes --> 24h
|
2018-12-09 19:42:41 +08:00
|
|
|
#define NEW_HISTORY_DELAY (15*60)
|
|
|
|
//#define NEW_HISTORY_DELAY 2
|
|
|
|
|
|
|
|
// changing the content of the display is done via "shakes"
|
|
|
|
// shakes are detected by a tilt switsch.
|
|
|
|
// This value defines the number of shake events to change the display page.
|
2018-12-10 06:28:09 +08:00
|
|
|
#define NEW_DISPLAY_SHAKE_THRESHOLD 7
|
2018-12-09 19:42:41 +08:00
|
|
|
|
|
|
|
// number of seconds, for which a new display page is fixed
|
|
|
|
// this means, for this duration, the user can not change the display page
|
|
|
|
#define NEW_DISPLAY_COOL_DOWN 3
|
|
|
|
|
|
|
|
|
2018-12-16 06:43:11 +08:00
|
|
|
// Battery empty value
|
|
|
|
// U = 100K * 3.5V / 570K = 0.6140 V --> 571 (analogRead)
|
|
|
|
#define BATTERY_LOW 571
|
|
|
|
// step width for each battery symbol
|
|
|
|
#define BATTERY_STEP 33
|
|
|
|
|
2018-10-30 15:17:56 +08:00
|
|
|
|
2018-11-10 06:17:55 +08:00
|
|
|
U8G2_SSD1306_128X64_NONAME_2_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);
|
2018-12-09 19:42:41 +08:00
|
|
|
|
2018-12-10 06:28:09 +08:00
|
|
|
SHTSensor sht; // temperature & humidity sensor
|
2018-11-10 06:17:55 +08:00
|
|
|
|
2018-10-30 15:17:56 +08:00
|
|
|
Adafruit_SGP30 sgp; // air quality sensor
|
|
|
|
|
2018-12-10 06:28:09 +08:00
|
|
|
U8G2LOG u8g2log;
|
2018-10-30 15:17:56 +08:00
|
|
|
|
|
|
|
// setup the terminal (U8G2LOG) and connect to u8g2 for automatic refresh of the display
|
|
|
|
// The size (width * height) depends on the selected font and the display
|
|
|
|
|
2018-11-10 15:54:07 +08:00
|
|
|
#define U8LOG_WIDTH 24
|
|
|
|
#define U8LOG_HEIGHT 5
|
2018-10-30 15:17:56 +08:00
|
|
|
uint8_t u8log_buffer[U8LOG_WIDTH*U8LOG_HEIGHT];
|
|
|
|
|
2018-11-01 05:57:23 +08:00
|
|
|
//===================================================
|
2018-11-10 06:17:55 +08:00
|
|
|
// Constants: Font definitions for U8g2
|
2018-11-01 05:57:23 +08:00
|
|
|
|
2018-11-10 06:17:55 +08:00
|
|
|
#define FONT_NARROW u8g2_font_mercutio_basic_nbp_tr
|
2018-11-01 05:57:23 +08:00
|
|
|
#define FONT_SMALL u8g2_font_helvB08_tf
|
|
|
|
#define FONT_MED_NUM u8g2_font_helvB14_tn
|
|
|
|
//#define FONT_BIG u8g2_font_inr49_mn
|
|
|
|
#define FONT_BIG u8g2_font_logisoso38_tn
|
|
|
|
|
2018-11-07 05:03:21 +08:00
|
|
|
//===================================================
|
2018-11-10 06:17:55 +08:00
|
|
|
// Constants: State values for the sensor & display coordination
|
2018-11-07 05:03:21 +08:00
|
|
|
|
|
|
|
#define STATE_RESET 0
|
|
|
|
#define STATE_STARTUP_DISP_ON 1
|
|
|
|
#define STATE_STARTUP_DISP_OFF 2
|
|
|
|
#define STATE_WARMUP_DISP_ON 11
|
|
|
|
#define STATE_WARMUP_DISP_OFF 12
|
|
|
|
#define STATE_MEASURE_DISP_ON 21
|
|
|
|
#define STATE_MEASURE_DISP_OFF 22
|
|
|
|
#define STATE_SENSOR_SLEEP_DISP_OFF 32
|
|
|
|
|
|
|
|
|
2018-11-01 05:57:23 +08:00
|
|
|
//===================================================
|
2018-11-10 06:17:55 +08:00
|
|
|
// Constants: Temperature boundaries
|
2018-11-01 05:57:23 +08:00
|
|
|
|
|
|
|
#define TEMP_LOW -20
|
|
|
|
#define TEMP_HIGH (TEMP_LOW+120)
|
|
|
|
|
2018-11-10 06:17:55 +08:00
|
|
|
//===================================================
|
|
|
|
// Constants: Number of different display pages
|
2018-12-09 19:42:41 +08:00
|
|
|
#define DISPLAY_PAGE_CNT 3
|
2018-11-10 06:17:55 +08:00
|
|
|
|
|
|
|
//===================================================
|
|
|
|
// Constants: History
|
|
|
|
|
|
|
|
#define HIST_CNT 96
|
|
|
|
|
|
|
|
// history sample time: number of seconds between each history entry
|
2018-12-09 19:42:41 +08:00
|
|
|
// --> see NEW_HISTORY_DELAY
|
2018-11-10 06:17:55 +08:00
|
|
|
// 15 min = 15*60 seconds: 96 entries for 24h
|
|
|
|
|
|
|
|
//===================================================
|
|
|
|
// State variable for the sensor & display coordination
|
|
|
|
|
|
|
|
uint8_t state = STATE_RESET; // assign STATE_xxx constants
|
|
|
|
|
2018-11-11 23:03:06 +08:00
|
|
|
uint8_t is_display_enabled = 0; // modified by enable_display() and disable_display()
|
|
|
|
|
|
|
|
|
2018-11-10 06:17:55 +08:00
|
|
|
//===================================================
|
|
|
|
// Variables: Air quality sensor related varables
|
|
|
|
|
2019-01-20 14:35:02 +08:00
|
|
|
uint8_t is_ethanol_read = 0;
|
|
|
|
|
2018-11-10 06:17:55 +08:00
|
|
|
float temperature_raw;
|
|
|
|
float humidity_raw;
|
|
|
|
uint8_t temperature; /* with offset and multiplied by 2 */
|
|
|
|
uint8_t humidity; /* *2 */
|
|
|
|
uint16_t tvoc_raw;
|
|
|
|
uint16_t tvoc_lp;
|
|
|
|
uint16_t eco2_raw;
|
|
|
|
uint16_t eco2_lp;
|
2019-01-20 14:35:02 +08:00
|
|
|
uint16_t ethanol_raw = 0;
|
2018-11-10 06:17:55 +08:00
|
|
|
int is_air_quality_available = 0;
|
|
|
|
|
2019-01-20 14:35:02 +08:00
|
|
|
|
2018-11-11 23:03:06 +08:00
|
|
|
// Calibration values, values seem to be 0x8a27 and 0x08a98 for my sensor, so 0 will be used as not set
|
|
|
|
uint16_t eco2_base = 0; // calibration value eCO2
|
|
|
|
uint16_t tvoc_base = 0; // calibration value TVOC
|
2018-11-10 06:17:55 +08:00
|
|
|
|
2018-12-16 06:43:11 +08:00
|
|
|
//===================================================
|
|
|
|
uint16_t battery_raw;
|
|
|
|
uint16_t battery_glyph;
|
|
|
|
|
2018-11-10 06:17:55 +08:00
|
|
|
//===================================================
|
|
|
|
// Variables: Timer for the state machine
|
|
|
|
|
|
|
|
volatile uint32_t wdt_count = 0;
|
2018-11-11 23:03:06 +08:00
|
|
|
volatile uint8_t wdt_sec = 0;
|
|
|
|
volatile uint8_t wdt_min = 0;
|
|
|
|
volatile uint8_t wdt_hour = 0;
|
|
|
|
volatile uint16_t wdt_day = 0;
|
|
|
|
|
2018-11-10 06:17:55 +08:00
|
|
|
|
|
|
|
volatile uint16_t startup_timer = 0;
|
|
|
|
volatile uint16_t sensor_sample_timer = SENSOR_SAMPLE_TIME;
|
2018-11-14 04:47:55 +08:00
|
|
|
volatile uint8_t is_sensor_sample_timer_alarm = 0; // if zero: wake up gas sensor
|
2018-11-10 06:17:55 +08:00
|
|
|
volatile uint8_t display_timer = 0;
|
|
|
|
|
|
|
|
volatile uint8_t sensor_warmup_timer = 0;
|
|
|
|
volatile uint8_t sensor_measure_timer = 0;
|
|
|
|
|
|
|
|
volatile uint8_t new_display_cool_down_timer = 0;
|
|
|
|
|
|
|
|
volatile uint8_t is_wdt_irq = 0;
|
|
|
|
|
2018-11-14 04:47:55 +08:00
|
|
|
volatile uint8_t is_new_history_entry = 0;
|
|
|
|
volatile uint16_t new_history_timer = NEW_HISTORY_DELAY;
|
|
|
|
|
2018-12-10 06:28:09 +08:00
|
|
|
//volatile uint16_t battery_level = 0; // in millivolt
|
2018-11-14 04:47:55 +08:00
|
|
|
|
|
|
|
|
2018-12-09 19:42:41 +08:00
|
|
|
uint32_t millis_sensor; // Debugging: Duration of the sensour measurement
|
|
|
|
uint32_t millis_display; // Debugging: Duration of the display refresh
|
2018-11-10 06:17:55 +08:00
|
|
|
|
2018-11-11 23:03:06 +08:00
|
|
|
|
2018-11-10 06:17:55 +08:00
|
|
|
//===================================================
|
|
|
|
// Variables: Shake detection
|
|
|
|
|
|
|
|
volatile uint8_t is_shake = 0;
|
|
|
|
volatile uint8_t shake_cnt = 0;
|
|
|
|
volatile uint8_t shake_last_cnt = 0;
|
|
|
|
|
|
|
|
//===================================================
|
2018-12-09 19:42:41 +08:00
|
|
|
// Variables: Current visible display page
|
2018-11-10 06:17:55 +08:00
|
|
|
|
2018-12-09 19:42:41 +08:00
|
|
|
uint8_t current_display_page = 0; // 0 .. DISPLAY_PAGE_CNT - 1
|
2018-11-10 06:17:55 +08:00
|
|
|
|
|
|
|
//===================================================
|
|
|
|
// Variables: History management
|
|
|
|
|
2018-11-01 05:57:23 +08:00
|
|
|
uint8_t hist_start = 0;
|
|
|
|
uint8_t hist_end = 1;
|
|
|
|
uint8_t hist_last = 0;
|
|
|
|
|
2018-11-10 06:17:55 +08:00
|
|
|
uint16_t hist_eco2_max[HIST_CNT];
|
|
|
|
uint16_t hist_eco2_min[HIST_CNT];
|
|
|
|
|
|
|
|
//uint8_t hist_temp_max[HIST_CNT];
|
|
|
|
//uint8_t hist_temp_min[HIST_CNT];
|
|
|
|
|
|
|
|
//uint8_t hist_rh_max[HIST_CNT];
|
|
|
|
//uint8_t hist_rh_min[HIST_CNT];
|
|
|
|
|
|
|
|
//===================================================
|
2018-11-01 05:57:23 +08:00
|
|
|
|
|
|
|
void hist_append(void)
|
|
|
|
{
|
|
|
|
hist_last = hist_end;
|
|
|
|
|
|
|
|
if ( hist_end == hist_start )
|
|
|
|
{ // history is full
|
|
|
|
hist_start++;
|
|
|
|
if ( hist_start >= HIST_CNT )
|
|
|
|
hist_start = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
hist_end++;
|
|
|
|
if ( hist_end >= HIST_CNT )
|
|
|
|
{
|
|
|
|
hist_end = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-11-10 06:17:55 +08:00
|
|
|
uint16_t maximum(uint16_t a, uint16_t b)
|
|
|
|
{
|
|
|
|
if ( a < b )
|
|
|
|
return b;
|
|
|
|
return a;
|
|
|
|
}
|
2018-11-01 05:57:23 +08:00
|
|
|
|
2018-11-10 06:17:55 +08:00
|
|
|
uint16_t minimum(uint16_t a, uint16_t b)
|
|
|
|
{
|
|
|
|
if ( a > b )
|
|
|
|
return b;
|
|
|
|
return a;
|
|
|
|
}
|
2018-11-01 05:57:23 +08:00
|
|
|
|
|
|
|
|
2018-11-10 06:17:55 +08:00
|
|
|
/* append current values to last history entry */
|
|
|
|
void add_hist_minmax(void)
|
|
|
|
{
|
|
|
|
|
|
|
|
hist_eco2_max[hist_last] = maximum(hist_eco2_max[hist_last], eco2_raw);
|
|
|
|
hist_eco2_min[hist_last] = minimum(hist_eco2_min[hist_last], eco2_raw);
|
2018-11-01 05:57:23 +08:00
|
|
|
|
2018-11-10 06:17:55 +08:00
|
|
|
//hist_temp_max[hist_last] = maximum(hist_temp_max[hist_last], temperature);
|
|
|
|
//hist_temp_min[hist_last] = minimum(hist_temp_min[hist_last], temperature);
|
2018-11-06 13:18:39 +08:00
|
|
|
|
2018-11-10 06:17:55 +08:00
|
|
|
//hist_rh_max[hist_last] = maximum(hist_rh_max[hist_last], humidity);
|
|
|
|
//hist_rh_min[hist_last] = minimum(hist_rh_min[hist_last], humidity);
|
|
|
|
}
|
2018-11-06 13:18:39 +08:00
|
|
|
|
2018-11-10 06:17:55 +08:00
|
|
|
void add_hist_new(void)
|
|
|
|
{
|
|
|
|
hist_append();
|
2018-11-06 13:18:39 +08:00
|
|
|
|
2018-11-10 06:17:55 +08:00
|
|
|
hist_eco2_max[hist_last] = eco2_raw;
|
|
|
|
hist_eco2_min[hist_last] = eco2_raw;
|
2018-11-06 13:18:39 +08:00
|
|
|
|
2018-11-10 06:17:55 +08:00
|
|
|
//hist_temp_max[hist_last] = temperature;
|
|
|
|
//hist_temp_min[hist_last] = temperature;
|
2018-11-06 13:18:39 +08:00
|
|
|
|
2018-11-10 06:17:55 +08:00
|
|
|
//hist_rh_max[hist_last] = humidity;
|
|
|
|
//hist_rh_min[hist_last] = humidity;
|
2018-11-06 13:18:39 +08:00
|
|
|
|
2018-11-10 06:17:55 +08:00
|
|
|
}
|
2018-11-06 13:18:39 +08:00
|
|
|
|
2018-11-08 05:48:04 +08:00
|
|
|
|
|
|
|
|
2018-11-06 13:18:39 +08:00
|
|
|
|
2018-11-07 05:03:21 +08:00
|
|
|
|
2018-11-10 06:17:55 +08:00
|
|
|
//===================================================
|
2018-11-06 13:18:39 +08:00
|
|
|
|
2018-11-07 05:03:21 +08:00
|
|
|
|
2018-11-06 13:18:39 +08:00
|
|
|
ISR(WDT_vect)
|
|
|
|
{
|
2018-11-10 06:17:55 +08:00
|
|
|
is_wdt_irq = 1;
|
2018-11-06 13:18:39 +08:00
|
|
|
wdt_count++;
|
2018-11-11 23:03:06 +08:00
|
|
|
|
|
|
|
|
|
|
|
wdt_sec++;
|
|
|
|
if ( wdt_sec >= 60 )
|
|
|
|
{
|
|
|
|
wdt_sec = 0;
|
|
|
|
wdt_min++;
|
|
|
|
if ( wdt_min >= 60 )
|
|
|
|
{
|
|
|
|
wdt_min = 0;
|
|
|
|
wdt_hour++;
|
|
|
|
if ( wdt_hour >= 24 )
|
2019-01-06 16:51:37 +08:00
|
|
|
{
|
|
|
|
wdt_hour = 0;
|
2018-11-11 23:03:06 +08:00
|
|
|
wdt_day++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-06 13:18:39 +08:00
|
|
|
if ( startup_timer > 0 )
|
|
|
|
startup_timer--;
|
|
|
|
if ( display_timer > 0 )
|
|
|
|
display_timer--;
|
|
|
|
if ( sensor_warmup_timer > 0 )
|
|
|
|
sensor_warmup_timer--;
|
|
|
|
if ( sensor_measure_timer > 0 )
|
|
|
|
sensor_measure_timer--;
|
|
|
|
if ( sensor_sample_timer > 0 )
|
|
|
|
{
|
|
|
|
sensor_sample_timer--;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
sensor_sample_timer = SENSOR_SAMPLE_TIME;
|
2018-11-07 05:03:21 +08:00
|
|
|
if ( is_sensor_sample_timer_alarm == 0 )
|
|
|
|
is_sensor_sample_timer_alarm = 1;
|
2018-11-06 13:18:39 +08:00
|
|
|
}
|
2018-11-14 04:47:55 +08:00
|
|
|
|
|
|
|
if ( new_history_timer > 0 )
|
|
|
|
{
|
|
|
|
new_history_timer--;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
new_history_timer = NEW_HISTORY_DELAY;
|
|
|
|
if ( is_new_history_entry == 0 )
|
|
|
|
is_new_history_entry = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-11-07 05:03:21 +08:00
|
|
|
shake_last_cnt = shake_cnt;
|
|
|
|
shake_cnt = 0;
|
2018-11-06 13:18:39 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void enableWDT()
|
|
|
|
{
|
|
|
|
MCUSR = 0; // clear all reset flags including the WDT flag
|
|
|
|
WDTCSR = B00011000; // enable bit 4 (WDCE) and bit 3 (WDE) to change the prescalar
|
|
|
|
WDTCSR = B01000110; // Enable watchdog IRQ and set prescaler to 128k --> 1 sec
|
|
|
|
}
|
|
|
|
|
|
|
|
void reducePower(void)
|
|
|
|
{
|
2018-12-10 06:28:09 +08:00
|
|
|
//ADCSRA = ADCSRA & B01111111; // ADC abschalten, ADEN bit7 zu 0
|
2018-11-06 13:18:39 +08:00
|
|
|
ACSR = B10000000; // Analogen Comparator abschalten, ACD bit7 zu 1
|
2019-01-04 06:36:05 +08:00
|
|
|
DIDR0 = DIDR0 | B00111111; // Digitale Eingangspuffer ausschalten, analoge Eingangs Pins 0-5 auf 1
|
2018-11-06 13:18:39 +08:00
|
|
|
}
|
|
|
|
|
2018-11-07 05:03:21 +08:00
|
|
|
|
2018-12-09 19:42:41 +08:00
|
|
|
// argument for attachInterrupt
|
|
|
|
// This is called if something happens on the tilt switch
|
|
|
|
|
2018-11-07 05:03:21 +08:00
|
|
|
void detectShake(void)
|
|
|
|
{
|
2018-12-09 19:42:41 +08:00
|
|
|
is_shake = 1; // used and cleared in is_display_on_event()
|
2018-11-07 05:03:21 +08:00
|
|
|
if ( shake_cnt < 255 )
|
2018-12-09 19:42:41 +08:00
|
|
|
shake_cnt++; // used and cleared in ISR(WDT_vect)
|
2018-11-07 05:03:21 +08:00
|
|
|
}
|
|
|
|
|
2018-11-01 05:57:23 +08:00
|
|
|
//===================================================
|
2018-10-30 15:17:56 +08:00
|
|
|
|
|
|
|
|
|
|
|
void setup(void) {
|
2018-12-09 19:42:41 +08:00
|
|
|
uint8_t i;
|
2018-11-06 13:18:39 +08:00
|
|
|
reducePower();
|
2018-12-10 06:28:09 +08:00
|
|
|
analogReference(INTERNAL); // use the internal 1.1V reference of the ATmega
|
|
|
|
|
2018-11-10 06:17:55 +08:00
|
|
|
Wire.begin();
|
2018-11-06 13:18:39 +08:00
|
|
|
|
2018-10-30 15:17:56 +08:00
|
|
|
u8g2.begin();
|
2018-11-01 05:57:23 +08:00
|
|
|
u8g2.enableUTF8Print();
|
2018-10-30 15:17:56 +08:00
|
|
|
|
2018-11-10 06:17:55 +08:00
|
|
|
u8g2.setFont(FONT_NARROW); // set the font for the terminal window
|
2018-10-30 15:17:56 +08:00
|
|
|
u8g2log.begin(u8g2, U8LOG_WIDTH, U8LOG_HEIGHT, u8log_buffer);
|
|
|
|
u8g2log.setLineHeightOffset(0); // set extra space between lines in pixel, this can be negative
|
|
|
|
u8g2log.setRedrawMode(0); // 0: Update screen with newline, 1: Update screen for every char
|
2018-11-01 05:57:23 +08:00
|
|
|
|
2018-10-30 15:17:56 +08:00
|
|
|
|
|
|
|
u8g2log.print(F("Air Quality\n"));
|
2018-11-10 06:17:55 +08:00
|
|
|
|
2018-12-09 19:42:41 +08:00
|
|
|
for( i = 0; i < HIST_CNT; i++ )
|
|
|
|
{
|
|
|
|
hist_eco2_max[i] = 0;
|
|
|
|
hist_eco2_min[i] = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-12-10 06:28:09 +08:00
|
|
|
if (sht.init()) {
|
2018-11-10 06:17:55 +08:00
|
|
|
u8g2log.print(F("SHT31 found at 0x44\n"));
|
|
|
|
} else {
|
|
|
|
u8g2log.print(F("SHT31 not found\n"));
|
|
|
|
while (1);
|
|
|
|
}
|
|
|
|
sht.setAccuracy(SHTSensor::SHT_ACCURACY_HIGH);
|
2018-10-30 15:17:56 +08:00
|
|
|
|
2018-11-11 23:03:06 +08:00
|
|
|
|
2018-10-30 15:17:56 +08:00
|
|
|
if (sgp.begin()) {
|
|
|
|
u8g2log.print(F("SGP30 found\n"));
|
|
|
|
} else {
|
|
|
|
u8g2log.print(F("SGP30 not found\n"));
|
|
|
|
while (1);
|
|
|
|
}
|
|
|
|
|
|
|
|
u8g2log.print(F("SGP #"));
|
|
|
|
u8g2log.print(sgp.serialnumber[0], HEX);
|
|
|
|
u8g2log.print(sgp.serialnumber[1], HEX);
|
|
|
|
u8g2log.println(sgp.serialnumber[2], HEX);
|
2018-11-10 06:17:55 +08:00
|
|
|
|
2018-10-30 15:17:56 +08:00
|
|
|
|
2018-11-01 05:57:23 +08:00
|
|
|
delay(1000);
|
2018-11-06 13:18:39 +08:00
|
|
|
|
2018-12-16 06:43:11 +08:00
|
|
|
// tilt detection at pin 2
|
|
|
|
//pinMode(2, INPUT_PULLUP);
|
|
|
|
//attachInterrupt(digitalPinToInterrupt(2), detectShake, CHANGE);
|
|
|
|
|
|
|
|
// tilt detection at pin 3
|
2018-11-15 04:39:58 +08:00
|
|
|
pinMode(3, INPUT_PULLUP);
|
|
|
|
attachInterrupt(digitalPinToInterrupt(3), detectShake, CHANGE);
|
2018-11-10 06:17:55 +08:00
|
|
|
|
|
|
|
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
|
|
|
|
|
2018-11-06 13:18:39 +08:00
|
|
|
enableWDT();
|
2018-10-30 15:17:56 +08:00
|
|
|
}
|
|
|
|
|
2018-12-10 06:28:09 +08:00
|
|
|
|
|
|
|
//===================================================
|
2019-01-20 14:35:02 +08:00
|
|
|
// read ethanol value from SGP30
|
|
|
|
// bypass Adafruit lib, because this command is not included
|
|
|
|
|
|
|
|
|
|
|
|
// not used at the moment
|
|
|
|
uint16_t readEthanol(void)
|
|
|
|
{
|
|
|
|
uint8_t buf[6];
|
|
|
|
uint8_t i;
|
|
|
|
|
|
|
|
Wire.beginTransmission(0x58);
|
|
|
|
Wire.write(0x20); // 0x2050: measure signals command
|
|
|
|
Wire.write(0x50);
|
|
|
|
Wire.endTransmission();
|
|
|
|
|
|
|
|
delay(200); // max 200ms according to the datasheet
|
|
|
|
|
|
|
|
if (Wire.requestFrom(0x58, 6) != 6)
|
|
|
|
return 0;
|
|
|
|
for (i=0; i<6; i++)
|
|
|
|
buf[i] = Wire.read();
|
|
|
|
|
|
|
|
// each data value has: msb, lsb and crc
|
|
|
|
// ethanol value is the second data returned
|
|
|
|
// crc is ignored
|
|
|
|
return (((uint16_t)buf[3])<<8) + (uint16_t)buf[4];
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//===================================================
|
|
|
|
|
2018-12-10 06:28:09 +08:00
|
|
|
void readBatteryVoltageLevel(void)
|
|
|
|
{
|
2018-12-16 06:43:11 +08:00
|
|
|
battery_raw = analogRead(0);
|
|
|
|
battery_glyph = 0;
|
|
|
|
if ( battery_raw >= BATTERY_LOW )
|
|
|
|
{
|
|
|
|
battery_glyph = (battery_raw - BATTERY_LOW)/BATTERY_STEP;
|
|
|
|
if ( battery_glyph > 5 )
|
|
|
|
battery_glyph = 5;
|
|
|
|
}
|
|
|
|
battery_glyph += 48;
|
2018-12-10 06:28:09 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-11-10 06:17:55 +08:00
|
|
|
//===================================================
|
|
|
|
|
2018-10-30 15:17:56 +08:00
|
|
|
/*
|
|
|
|
Calculate absolute humidity [mg/m^3]
|
|
|
|
Args: Temperature [°C], humidity [%RH]
|
|
|
|
*/
|
|
|
|
|
2018-11-08 05:48:04 +08:00
|
|
|
uint32_t getAbsoluteHumidity(float temperature, float humidity)
|
|
|
|
{
|
2018-10-30 15:17:56 +08:00
|
|
|
// approximation formula from Sensirion SGP30 Driver Integration chapter 3.15
|
|
|
|
const float absoluteHumidity = 216.7f * ((humidity / 100.0f) * 6.112f * exp((17.62f * temperature) / (243.12f + temperature)) / (273.15f + temperature)); // [g/m^3]
|
|
|
|
const uint32_t absoluteHumidityScaled = static_cast<uint32_t>(1000.0f * absoluteHumidity); // [mg/m^3]
|
|
|
|
return absoluteHumidityScaled;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void startAirQuality(void)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void readAirQuality(void)
|
|
|
|
{
|
2018-11-10 06:17:55 +08:00
|
|
|
|
2018-11-01 05:57:23 +08:00
|
|
|
|
2019-01-20 14:35:02 +08:00
|
|
|
if ( is_ethanol_read == 0 )
|
2018-11-01 05:57:23 +08:00
|
|
|
{
|
2019-01-20 14:35:02 +08:00
|
|
|
// normal air quality read
|
|
|
|
sht.readSample();
|
|
|
|
temperature_raw = sht.getTemperature();
|
|
|
|
humidity_raw = sht.getHumidity();
|
|
|
|
|
|
|
|
if ( temperature_raw <= (float)TEMP_LOW )
|
|
|
|
{
|
|
|
|
temperature = 0;
|
|
|
|
}
|
|
|
|
else if ( temperature_raw >= (float)TEMP_HIGH )
|
|
|
|
{
|
|
|
|
temperature = TEMP_HIGH - TEMP_LOW;
|
|
|
|
temperature *= 2;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
temperature = (uint8_t)((temperature_raw-(float)TEMP_LOW)*2.0);
|
|
|
|
}
|
|
|
|
|
|
|
|
humidity = (uint8_t)((humidity_raw)*2.0);
|
2018-11-01 05:57:23 +08:00
|
|
|
|
2019-01-20 14:35:02 +08:00
|
|
|
sgp.setHumidity(getAbsoluteHumidity(temperature_raw, humidity_raw));
|
|
|
|
|
|
|
|
is_air_quality_available = sgp.IAQmeasure();
|
2018-11-10 06:17:55 +08:00
|
|
|
|
2019-01-20 14:35:02 +08:00
|
|
|
if ( is_air_quality_available )
|
|
|
|
{
|
|
|
|
tvoc_raw = sgp.TVOC;
|
|
|
|
eco2_raw = sgp.eCO2;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
tvoc_raw = 0;
|
|
|
|
eco2_raw = 400;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( is_new_history_entry != 0 )
|
|
|
|
{
|
|
|
|
add_hist_new();
|
|
|
|
is_new_history_entry = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
add_hist_minmax();
|
|
|
|
}
|
2018-10-30 15:17:56 +08:00
|
|
|
|
2019-01-20 14:35:02 +08:00
|
|
|
// ethanol should be read only, if the display is active:
|
|
|
|
/* not used
|
|
|
|
if ( is_display_enabled )
|
|
|
|
{
|
|
|
|
is_ethanol_read = 1;
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
|
2018-11-14 04:47:55 +08:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2019-01-20 14:35:02 +08:00
|
|
|
|
|
|
|
// special ethanol read
|
|
|
|
//ethanol_raw = readEthanol();
|
|
|
|
|
|
|
|
// according to the datasheet, the baseline values are corrupted... so restore them if available
|
|
|
|
//if ( eco2_base != 0 )
|
|
|
|
// sgp.setIAQBaseline(eco2_base, tvoc_base); // Restore the baseline values
|
|
|
|
|
|
|
|
is_ethanol_read = 0; // next: read normal air quality values
|
2018-11-14 04:47:55 +08:00
|
|
|
}
|
2018-11-01 05:57:23 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
uint16_t get_uint8(void *ptr, uint8_t pos)
|
|
|
|
{
|
|
|
|
return ((uint8_t *)ptr)[pos];
|
|
|
|
}
|
|
|
|
|
|
|
|
uint16_t get_uint16(void *ptr, uint8_t pos)
|
|
|
|
{
|
|
|
|
return ((uint16_t *)ptr)[pos];
|
|
|
|
}
|
|
|
|
|
|
|
|
void draw_temperature(uint16_t x)
|
|
|
|
{
|
|
|
|
uint8_t frac;
|
|
|
|
frac = x & 1;
|
|
|
|
x >>= 1;
|
|
|
|
x += TEMP_LOW;
|
|
|
|
x = x & 0x0ff;
|
|
|
|
u8g2.print(x);
|
|
|
|
u8g2.print('.');
|
|
|
|
if ( frac )
|
|
|
|
u8g2.print('5');
|
|
|
|
else
|
|
|
|
u8g2.print('0');
|
|
|
|
}
|
|
|
|
|
|
|
|
void draw_humidity(uint16_t x)
|
|
|
|
{
|
|
|
|
uint8_t frac;
|
|
|
|
frac = x & 1;
|
|
|
|
x >>= 1;
|
|
|
|
u8g2.print(x);
|
|
|
|
u8g2.print('.');
|
|
|
|
if ( frac )
|
|
|
|
u8g2.print('5');
|
|
|
|
else
|
|
|
|
u8g2.print('0');
|
|
|
|
}
|
|
|
|
|
|
|
|
void draw_16bit(uint16_t x)
|
|
|
|
{
|
|
|
|
u8g2.print(x);
|
|
|
|
}
|
|
|
|
|
|
|
|
void draw_graph( uint16_t (*get_val)(void *ptr, uint8_t pos), void *min_array, void *max_array, void (*draw_value)(uint16_t x))
|
|
|
|
{
|
|
|
|
uint8_t i, ii, x;
|
|
|
|
uint16_t max = 0;
|
|
|
|
uint16_t min = 0x0ffff;
|
|
|
|
uint16_t delta;
|
|
|
|
uint8_t ymin, ymax;
|
|
|
|
i = hist_start;
|
|
|
|
for(;;)
|
|
|
|
{
|
|
|
|
ii = i;
|
|
|
|
i++;
|
|
|
|
if ( i >= HIST_CNT )
|
|
|
|
i = 0;
|
|
|
|
if ( i == hist_end )
|
|
|
|
break;
|
|
|
|
if ( min > get_val(min_array, ii) )
|
|
|
|
min = get_val(min_array, ii);
|
|
|
|
if ( max < get_val(max_array, ii) )
|
|
|
|
max = get_val(max_array, ii);
|
|
|
|
}
|
|
|
|
if ( min > max )
|
2018-12-09 19:42:41 +08:00
|
|
|
{
|
|
|
|
min = 0;
|
|
|
|
max = 600;
|
2018-11-01 05:57:23 +08:00
|
|
|
return;
|
2018-12-09 19:42:41 +08:00
|
|
|
}
|
2018-11-08 05:48:04 +08:00
|
|
|
if ( min + 30 >= max )
|
|
|
|
max = min + 30;
|
2018-11-01 05:57:23 +08:00
|
|
|
|
|
|
|
if ( max - min < 100 )
|
|
|
|
{
|
|
|
|
max += 19;
|
|
|
|
max /= 20;
|
|
|
|
max *= 20;
|
|
|
|
|
|
|
|
min /= 20;
|
|
|
|
min *= 20;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
max += 199;
|
|
|
|
max /= 200;
|
|
|
|
max *= 200;
|
|
|
|
|
|
|
|
min /= 200;
|
|
|
|
min *= 200;
|
|
|
|
}
|
|
|
|
|
|
|
|
delta = max-min;
|
|
|
|
|
|
|
|
i = hist_start;
|
|
|
|
x = 127-HIST_CNT;
|
2018-12-09 19:42:41 +08:00
|
|
|
|
|
|
|
u8g2.setFont(FONT_NARROW);
|
|
|
|
u8g2.setDrawColor(1);
|
|
|
|
|
2018-11-01 05:57:23 +08:00
|
|
|
for(;;)
|
|
|
|
{
|
|
|
|
ii = i;
|
|
|
|
i++;
|
|
|
|
if ( i >= HIST_CNT )
|
|
|
|
i = 0;
|
|
|
|
if ( i == hist_end )
|
|
|
|
break;
|
2018-11-08 05:48:04 +08:00
|
|
|
ymin = ((unsigned long)(get_val(min_array, ii) - min)*30UL)/delta;
|
|
|
|
ymax = ((unsigned long)(get_val(max_array, ii) - min)*30UL)/delta;
|
2018-11-01 05:57:23 +08:00
|
|
|
u8g2.drawVLine(x, 63-ymax, ymax-ymin+1);
|
|
|
|
x++;
|
|
|
|
}
|
2018-11-08 05:48:04 +08:00
|
|
|
u8g2.setCursor(0, 46);
|
2018-11-01 05:57:23 +08:00
|
|
|
draw_value(max);
|
|
|
|
u8g2.setCursor(0, 63);
|
|
|
|
draw_value(min);
|
|
|
|
}
|
2018-10-30 15:17:56 +08:00
|
|
|
|
2018-11-01 05:57:23 +08:00
|
|
|
//===================================================
|
|
|
|
|
2018-11-08 05:48:04 +08:00
|
|
|
void draw_1_2_eco2_history(u8g2_uint_t x, u8g2_uint_t y)
|
|
|
|
{
|
|
|
|
|
|
|
|
draw_graph( get_uint16, hist_eco2_min, hist_eco2_max, draw_16bit);
|
|
|
|
}
|
|
|
|
|
2018-11-04 02:46:14 +08:00
|
|
|
void draw_1_4_temperature(u8g2_uint_t x, u8g2_uint_t y)
|
2018-11-01 05:57:23 +08:00
|
|
|
{
|
|
|
|
u8g2.setFont(FONT_MED_NUM);
|
|
|
|
|
2018-11-04 02:46:14 +08:00
|
|
|
u8g2.setCursor(x, 28+y);
|
2018-11-01 05:57:23 +08:00
|
|
|
draw_temperature(temperature);
|
|
|
|
|
|
|
|
u8g2.setFont(FONT_SMALL);
|
|
|
|
|
2018-11-04 02:46:14 +08:00
|
|
|
u8g2.setCursor(x, 10+y);
|
2018-11-01 05:57:23 +08:00
|
|
|
u8g2.print(F("°C"));
|
2018-11-04 02:46:14 +08:00
|
|
|
}
|
2018-11-01 05:57:23 +08:00
|
|
|
|
2018-11-04 02:46:14 +08:00
|
|
|
void draw_1_4_humidity(u8g2_uint_t x, u8g2_uint_t y)
|
|
|
|
{
|
|
|
|
u8g2.setFont(FONT_MED_NUM);
|
|
|
|
u8g2.setCursor(x, 28+y);
|
|
|
|
draw_humidity(humidity);
|
|
|
|
|
|
|
|
u8g2.setFont(FONT_SMALL);
|
|
|
|
u8g2.setCursor(x, 10+y);
|
|
|
|
u8g2.print(F("%RH"));
|
|
|
|
}
|
2018-11-01 05:57:23 +08:00
|
|
|
|
2018-11-15 04:39:58 +08:00
|
|
|
void draw_gray_out(u8g2_uint_t x, u8g2_uint_t y)
|
|
|
|
{
|
|
|
|
if ( state == STATE_WARMUP_DISP_ON )
|
|
|
|
{
|
|
|
|
uint8_t xx, yy;
|
|
|
|
u8g2.setDrawColor(0);
|
|
|
|
for( yy = y+14; yy < y+28; yy+=1 )
|
|
|
|
{
|
|
|
|
for( xx = 0; xx < 52; xx += 2 )
|
|
|
|
{
|
|
|
|
u8g2.drawPixel(xx+x+(yy&1), yy);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
u8g2.setDrawColor(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-04 02:46:14 +08:00
|
|
|
void draw_1_4_eco2(u8g2_uint_t x, u8g2_uint_t y)
|
|
|
|
{
|
|
|
|
u8g2.setFont(FONT_SMALL);
|
2018-11-01 05:57:23 +08:00
|
|
|
|
2018-11-08 05:48:04 +08:00
|
|
|
u8g2.setCursor(x, y+10);
|
2018-11-04 02:46:14 +08:00
|
|
|
u8g2.print(F("ppm CO"));
|
2018-11-08 05:48:04 +08:00
|
|
|
u8g2.setCursor(x+40, y+15);
|
2018-11-04 02:46:14 +08:00
|
|
|
u8g2.print(F("²"));
|
2018-11-01 05:57:23 +08:00
|
|
|
|
2018-11-04 02:46:14 +08:00
|
|
|
u8g2.setFont(FONT_MED_NUM);
|
|
|
|
|
2018-11-08 05:48:04 +08:00
|
|
|
u8g2.setCursor(x, y+28);
|
2018-11-04 02:46:14 +08:00
|
|
|
u8g2.print(eco2_raw);
|
2018-11-15 04:39:58 +08:00
|
|
|
|
|
|
|
draw_gray_out(x, y);
|
2018-11-04 02:46:14 +08:00
|
|
|
}
|
2018-11-01 05:57:23 +08:00
|
|
|
|
2018-11-04 02:46:14 +08:00
|
|
|
void draw_1_4_tvoc(u8g2_uint_t x, u8g2_uint_t y)
|
|
|
|
{
|
|
|
|
u8g2.setFont(FONT_SMALL);
|
2018-11-08 05:48:04 +08:00
|
|
|
u8g2.setCursor(x, y+10);
|
2018-11-04 02:46:14 +08:00
|
|
|
u8g2.print(F("ppb TVOC"));
|
|
|
|
|
|
|
|
u8g2.setFont(FONT_MED_NUM);
|
2018-11-01 05:57:23 +08:00
|
|
|
|
2018-11-08 05:48:04 +08:00
|
|
|
u8g2.setCursor(x, y+28);
|
2018-11-04 02:46:14 +08:00
|
|
|
u8g2.print(tvoc_raw);
|
2018-11-15 04:39:58 +08:00
|
|
|
|
|
|
|
draw_gray_out(x, y);
|
2018-11-04 02:46:14 +08:00
|
|
|
}
|
|
|
|
|
2018-11-10 06:17:55 +08:00
|
|
|
void draw_1_4_base(u8g2_uint_t x, u8g2_uint_t y)
|
2018-11-06 13:18:39 +08:00
|
|
|
{
|
|
|
|
u8g2.setFont(FONT_SMALL);
|
2018-11-10 06:17:55 +08:00
|
|
|
u8g2.setCursor(x, y+11);
|
|
|
|
u8g2.print(F("Sec "));
|
|
|
|
u8g2.print(wdt_count);
|
2018-11-06 13:18:39 +08:00
|
|
|
|
2018-11-10 06:17:55 +08:00
|
|
|
u8g2.setCursor(x, y+21);
|
|
|
|
u8g2.print(F("B-C "));
|
|
|
|
u8g2.print(eco2_base, HEX);
|
|
|
|
|
|
|
|
u8g2.setCursor(x, y+31);
|
|
|
|
u8g2.print(F("B-T "));
|
|
|
|
u8g2.print(tvoc_base, HEX);
|
2018-11-06 13:18:39 +08:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2018-11-10 06:17:55 +08:00
|
|
|
void draw_1_4_delay(u8g2_uint_t x, u8g2_uint_t y)
|
|
|
|
{
|
|
|
|
u8g2.setFont(FONT_SMALL);
|
|
|
|
u8g2.setCursor(x, y+11);
|
|
|
|
u8g2.print(F("Sens "));
|
|
|
|
u8g2.print(millis_sensor);
|
|
|
|
|
|
|
|
u8g2.setCursor(x, y+21);
|
|
|
|
u8g2.print(F("Disp "));
|
|
|
|
u8g2.print(millis_display);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2018-11-11 23:03:06 +08:00
|
|
|
void draw_1_4_uptime(u8g2_uint_t x, u8g2_uint_t y)
|
|
|
|
{
|
|
|
|
u8g2.setFont(FONT_SMALL);
|
|
|
|
u8g2.setCursor(x, y+11);
|
|
|
|
u8g2.print(F("Day "));
|
|
|
|
u8g2.print(wdt_day);
|
|
|
|
|
|
|
|
u8g2.setCursor(x, y+21);
|
|
|
|
u8g2.print(F("Hour "));
|
|
|
|
u8g2.print(wdt_hour);
|
|
|
|
|
|
|
|
u8g2.setCursor(x, y+31);
|
|
|
|
u8g2.print(F("Min "));
|
|
|
|
u8g2.print(wdt_min);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2018-11-10 06:17:55 +08:00
|
|
|
|
2018-11-07 05:03:21 +08:00
|
|
|
void draw_1_4_system(u8g2_uint_t x, u8g2_uint_t y)
|
|
|
|
{
|
|
|
|
u8g2.setFont(FONT_SMALL);
|
2018-11-10 06:17:55 +08:00
|
|
|
u8g2.setCursor(x, y+11);
|
2019-01-20 14:35:02 +08:00
|
|
|
u8g2.print(F("ETH "));
|
|
|
|
u8g2.print(ethanol_raw, HEX);
|
2018-11-10 06:17:55 +08:00
|
|
|
|
|
|
|
u8g2.setCursor(x, y+21);
|
2019-01-20 14:35:02 +08:00
|
|
|
u8g2.print(F("St "));
|
2018-11-07 05:03:21 +08:00
|
|
|
u8g2.print(state);
|
2019-01-20 14:35:02 +08:00
|
|
|
u8g2.print(F(" Sh "));
|
|
|
|
u8g2.print(shake_last_cnt);
|
2018-11-07 05:03:21 +08:00
|
|
|
|
2018-12-16 06:43:11 +08:00
|
|
|
u8g2.setCursor(x, y+31);
|
|
|
|
u8g2.print(F("Bat "));
|
|
|
|
u8g2.print(battery_raw);
|
|
|
|
|
2018-11-07 05:03:21 +08:00
|
|
|
}
|
|
|
|
|
2018-11-10 20:23:15 +08:00
|
|
|
void draw_1_4_battery(u8g2_uint_t x, u8g2_uint_t y)
|
|
|
|
{
|
|
|
|
u8g2.setFont(u8g2_font_battery19_tn);
|
2018-12-16 06:43:11 +08:00
|
|
|
u8g2.drawGlyph(x, y+20, battery_glyph);
|
2018-11-10 20:23:15 +08:00
|
|
|
}
|
|
|
|
|
2018-11-04 14:32:55 +08:00
|
|
|
void draw_1_4_emoticon(u8g2_uint_t x, u8g2_uint_t y)
|
|
|
|
{
|
|
|
|
uint8_t tvoc_idx, eco2_idx, emo_idx;
|
|
|
|
if ( tvoc_raw <= 75 )
|
|
|
|
tvoc_idx = 1;
|
|
|
|
else if ( tvoc_raw <= 150 )
|
|
|
|
tvoc_idx = 2;
|
|
|
|
else if ( tvoc_raw <= 300 )
|
|
|
|
tvoc_idx = 3;
|
|
|
|
else if ( tvoc_raw <= 500 )
|
|
|
|
tvoc_idx = 4;
|
|
|
|
else if ( tvoc_raw <= 1000 )
|
|
|
|
tvoc_idx = 5;
|
|
|
|
else if ( tvoc_raw <= 1500 )
|
|
|
|
tvoc_idx = 6;
|
|
|
|
else if ( tvoc_raw <= 3000 )
|
|
|
|
tvoc_idx = 7;
|
|
|
|
else if ( tvoc_raw <= 5000 )
|
|
|
|
tvoc_idx = 8;
|
|
|
|
else
|
|
|
|
tvoc_idx = 9;
|
|
|
|
|
|
|
|
if ( eco2_raw <= 500 )
|
|
|
|
eco2_idx = 1;
|
|
|
|
else if ( eco2_raw <= 600 )
|
|
|
|
eco2_idx = 2;
|
|
|
|
else if ( eco2_raw <= 800 )
|
|
|
|
eco2_idx = 3;
|
|
|
|
else if ( eco2_raw <= 1000 )
|
|
|
|
eco2_idx = 4;
|
|
|
|
else if ( eco2_raw <= 1200 )
|
|
|
|
eco2_idx = 5;
|
|
|
|
else if ( eco2_raw <= 1400 )
|
|
|
|
eco2_idx = 6;
|
|
|
|
else if ( eco2_raw <= 5000 )
|
|
|
|
eco2_idx = 7;
|
|
|
|
else if ( eco2_raw <= 10000 )
|
|
|
|
eco2_idx = 8;
|
|
|
|
else
|
|
|
|
eco2_idx = 9;
|
|
|
|
|
|
|
|
emo_idx = tvoc_idx;
|
|
|
|
if ( emo_idx < eco2_idx )
|
|
|
|
emo_idx = eco2_idx;
|
|
|
|
u8g2.setFont(u8g2_font_emoticons21_tr);
|
2018-11-08 05:48:04 +08:00
|
|
|
u8g2.drawGlyph(x+20, y+21, 32+emo_idx);
|
2018-11-12 03:17:44 +08:00
|
|
|
|
|
|
|
|
2018-11-04 14:32:55 +08:00
|
|
|
}
|
|
|
|
|
2018-11-04 02:46:14 +08:00
|
|
|
//===================================================
|
|
|
|
|
|
|
|
void draw_all_numbers(void)
|
|
|
|
{
|
|
|
|
u8g2.setFontMode(1);
|
|
|
|
u8g2.firstPage();
|
|
|
|
do {
|
|
|
|
u8g2.drawHLine(0, 32, 128);
|
|
|
|
u8g2.drawVLine(62, 0, 64);
|
|
|
|
|
|
|
|
|
2018-11-04 14:32:55 +08:00
|
|
|
draw_1_4_temperature(1, 0);
|
|
|
|
draw_1_4_humidity(66, 0);
|
|
|
|
|
|
|
|
if ( is_air_quality_available )
|
|
|
|
{
|
2018-11-08 05:48:04 +08:00
|
|
|
draw_1_4_eco2(1, 32+3);
|
|
|
|
draw_1_4_tvoc(66, 32+3);
|
2018-11-04 14:32:55 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
} while ( u8g2.nextPage() );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void draw_with_emo(void)
|
|
|
|
{
|
|
|
|
u8g2.setFontMode(1);
|
|
|
|
u8g2.firstPage();
|
|
|
|
do {
|
2018-11-10 15:54:07 +08:00
|
|
|
//u8g2.drawHLine(0, 32, 128);
|
|
|
|
//u8g2.drawVLine(62, 0, 64);
|
|
|
|
|
2018-11-04 14:32:55 +08:00
|
|
|
u8g2.drawHLine(0, 32, 128);
|
2018-11-10 15:54:07 +08:00
|
|
|
u8g2.drawVLine(62-14, 0, 32);
|
|
|
|
u8g2.drawVLine(98, 0, 32);
|
2018-11-10 20:23:15 +08:00
|
|
|
u8g2.drawVLine(62-8, 32, 32);
|
|
|
|
u8g2.drawVLine(128-12, 32, 32);
|
2018-11-10 15:54:07 +08:00
|
|
|
|
2018-11-04 14:32:55 +08:00
|
|
|
|
2018-11-04 02:46:14 +08:00
|
|
|
draw_1_4_temperature(1, 0);
|
2018-11-10 15:54:07 +08:00
|
|
|
draw_1_4_humidity(66-14, 0);
|
|
|
|
draw_1_4_emoticon(127-20-21, 4);
|
2018-11-06 13:18:39 +08:00
|
|
|
//draw_1_4_wdt_count(66, 0);
|
2018-11-04 02:46:14 +08:00
|
|
|
|
|
|
|
if ( is_air_quality_available )
|
|
|
|
{
|
2018-11-08 05:48:04 +08:00
|
|
|
draw_1_4_eco2(1, 32+3);
|
2018-11-10 15:54:07 +08:00
|
|
|
//draw_1_4_emoticon(66, 32+6);
|
2018-11-10 20:23:15 +08:00
|
|
|
draw_1_4_tvoc(66-8, 32+3);
|
2018-11-01 05:57:23 +08:00
|
|
|
}
|
|
|
|
|
2018-11-10 20:23:15 +08:00
|
|
|
draw_1_4_battery(128-8, 32+7);
|
2018-11-01 05:57:23 +08:00
|
|
|
} while ( u8g2.nextPage() );
|
|
|
|
}
|
|
|
|
|
2018-11-10 06:17:55 +08:00
|
|
|
void draw_system(void)
|
|
|
|
{
|
|
|
|
u8g2.setFontMode(1);
|
|
|
|
u8g2.firstPage();
|
|
|
|
do {
|
|
|
|
u8g2.drawHLine(0, 32, 128);
|
|
|
|
u8g2.drawVLine(62, 0, 64);
|
|
|
|
|
|
|
|
|
2018-11-11 23:03:06 +08:00
|
|
|
draw_1_4_uptime(1, 0);
|
|
|
|
//draw_1_4_delay(1,0);
|
2018-11-10 06:17:55 +08:00
|
|
|
//draw_1_4_temperature(1, 0);
|
|
|
|
draw_1_4_eco2(66, 0);
|
|
|
|
//draw_1_4_humidity(66, 0);
|
|
|
|
//draw_1_4_wdt_count(66, 0);
|
|
|
|
|
|
|
|
draw_1_4_base(0, 32);
|
|
|
|
draw_1_4_system(66, 32);
|
|
|
|
|
|
|
|
} while ( u8g2.nextPage() );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-11-08 05:48:04 +08:00
|
|
|
void draw_with_history(void)
|
|
|
|
{
|
|
|
|
u8g2.setFontMode(1);
|
|
|
|
u8g2.firstPage();
|
|
|
|
do {
|
|
|
|
u8g2.drawHLine(0, 32, 128);
|
|
|
|
u8g2.drawVLine(62-20, 0, 32);
|
|
|
|
u8g2.drawVLine(98, 0, 32);
|
|
|
|
|
|
|
|
|
|
|
|
draw_1_4_temperature(1, 0);
|
|
|
|
if ( is_air_quality_available )
|
|
|
|
{
|
|
|
|
draw_1_4_eco2(66-20, 0);
|
|
|
|
draw_1_4_emoticon(127-20-21, 4);
|
|
|
|
}
|
|
|
|
|
|
|
|
draw_1_2_eco2_history(0, 32);
|
2018-12-09 19:42:41 +08:00
|
|
|
|
|
|
|
|
2018-11-08 05:48:04 +08:00
|
|
|
|
|
|
|
} while ( u8g2.nextPage() );
|
|
|
|
}
|
|
|
|
|
2018-11-06 13:18:39 +08:00
|
|
|
//===================================================
|
|
|
|
|
|
|
|
|
2018-11-07 05:03:21 +08:00
|
|
|
uint8_t is_display_on_event(void)
|
|
|
|
{
|
2018-12-09 19:42:41 +08:00
|
|
|
if ( is_shake > 0 )
|
|
|
|
{
|
|
|
|
is_shake = 0;
|
|
|
|
return 1;
|
|
|
|
}
|
2018-11-07 05:03:21 +08:00
|
|
|
if ( shake_last_cnt >= 1 )
|
|
|
|
return 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-12-09 19:42:41 +08:00
|
|
|
void handle_new_display_page(void)
|
2018-11-10 06:17:55 +08:00
|
|
|
{
|
|
|
|
if ( new_display_cool_down_timer > 0 )
|
|
|
|
{
|
|
|
|
new_display_cool_down_timer--;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if ( shake_last_cnt > NEW_DISPLAY_SHAKE_THRESHOLD )
|
|
|
|
{
|
|
|
|
new_display_cool_down_timer = NEW_DISPLAY_COOL_DOWN;
|
2018-12-09 19:42:41 +08:00
|
|
|
current_display_page++;
|
|
|
|
if ( current_display_page >= DISPLAY_PAGE_CNT )
|
|
|
|
current_display_page = 0;
|
2018-11-10 06:17:55 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-11 23:03:06 +08:00
|
|
|
void disable_display(void)
|
|
|
|
{
|
|
|
|
u8g2.clear();
|
|
|
|
u8g2.setPowerSave(1);
|
|
|
|
is_display_enabled = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void enable_display(void)
|
|
|
|
{
|
|
|
|
is_display_enabled = 1;
|
|
|
|
u8g2.setPowerSave(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void disable_sensors(void)
|
|
|
|
{
|
|
|
|
/* both sensors will go to idle/sleep mode when the I2C soft reset is sent */
|
|
|
|
Wire.beginTransmission(0);
|
|
|
|
Wire.write(6);
|
|
|
|
Wire.endTransmission(true); // true: send full stop on I2C
|
2018-11-12 03:17:44 +08:00
|
|
|
|
2018-11-11 23:03:06 +08:00
|
|
|
delay(5);
|
2018-11-12 03:17:44 +08:00
|
|
|
|
2018-11-11 23:03:06 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void enable_sensors(void)
|
|
|
|
{
|
|
|
|
sht.init();
|
2018-11-12 03:17:44 +08:00
|
|
|
sgp.IAQinit();
|
2018-11-11 23:03:06 +08:00
|
|
|
delay(5);
|
|
|
|
}
|
|
|
|
|
2018-11-07 05:03:21 +08:00
|
|
|
//===================================================
|
|
|
|
|
2018-11-06 13:18:39 +08:00
|
|
|
|
|
|
|
void next_state(void)
|
|
|
|
{
|
2018-11-12 03:17:44 +08:00
|
|
|
uint32_t start;
|
2018-11-06 13:18:39 +08:00
|
|
|
switch(state)
|
|
|
|
{
|
|
|
|
case STATE_RESET:
|
2018-11-11 23:03:06 +08:00
|
|
|
enable_display();
|
2018-11-06 13:18:39 +08:00
|
|
|
startup_timer = STARTUP_TIME;
|
|
|
|
state = STATE_STARTUP_DISP_ON;
|
2018-11-07 05:03:21 +08:00
|
|
|
display_timer = DISPLAY_TIME;
|
2018-11-06 13:18:39 +08:00
|
|
|
break;
|
2018-11-07 05:03:21 +08:00
|
|
|
|
|
|
|
// - - - Start Up - - -
|
|
|
|
|
2018-11-06 13:18:39 +08:00
|
|
|
case STATE_STARTUP_DISP_ON:
|
2018-11-12 03:17:44 +08:00
|
|
|
start = millis();
|
|
|
|
sgp.getIAQBaseline(&eco2_base, &tvoc_base); // always store the calibration values during measure
|
|
|
|
readAirQuality();
|
|
|
|
millis_sensor = millis() - start;
|
|
|
|
|
2018-11-07 05:03:21 +08:00
|
|
|
if ( is_display_on_event() )
|
2018-11-11 23:03:06 +08:00
|
|
|
{
|
|
|
|
// display is already enabled, but the timer is reseted
|
2018-11-07 05:03:21 +08:00
|
|
|
display_timer = DISPLAY_TIME;
|
2018-11-11 23:03:06 +08:00
|
|
|
}
|
|
|
|
|
2018-11-06 13:18:39 +08:00
|
|
|
if ( display_timer == 0 )
|
|
|
|
{
|
2018-11-11 23:03:06 +08:00
|
|
|
disable_display();
|
2018-11-06 13:18:39 +08:00
|
|
|
state = STATE_STARTUP_DISP_OFF;
|
|
|
|
}
|
2018-11-07 05:03:21 +08:00
|
|
|
else if ( startup_timer == 0 )
|
2018-11-06 13:18:39 +08:00
|
|
|
{
|
2018-11-07 05:03:21 +08:00
|
|
|
sensor_measure_timer = SENSOR_MEASURE_TIME;
|
|
|
|
state = STATE_MEASURE_DISP_ON;
|
2018-11-06 13:18:39 +08:00
|
|
|
}
|
|
|
|
break;
|
2018-11-11 23:03:06 +08:00
|
|
|
|
2018-11-06 13:18:39 +08:00
|
|
|
case STATE_STARTUP_DISP_OFF:
|
2018-11-12 03:17:44 +08:00
|
|
|
start = millis();
|
|
|
|
sgp.getIAQBaseline(&eco2_base, &tvoc_base); // always store the calibration values during measure
|
|
|
|
readAirQuality();
|
|
|
|
millis_sensor = millis() - start;
|
|
|
|
|
2018-11-07 05:03:21 +08:00
|
|
|
if ( is_display_on_event() )
|
|
|
|
{
|
2018-11-11 23:03:06 +08:00
|
|
|
enable_display();
|
|
|
|
display_timer = DISPLAY_TIME;
|
2018-12-09 19:42:41 +08:00
|
|
|
new_display_cool_down_timer = NEW_DISPLAY_COOL_DOWN; // ensure, that the display page is visible for a while
|
2018-11-07 05:03:21 +08:00
|
|
|
state = STATE_STARTUP_DISP_ON;
|
|
|
|
}
|
|
|
|
else if ( startup_timer == 0 )
|
|
|
|
{
|
|
|
|
sensor_measure_timer = SENSOR_MEASURE_TIME;
|
|
|
|
state = STATE_MEASURE_DISP_OFF;
|
|
|
|
}
|
2018-11-06 13:18:39 +08:00
|
|
|
break;
|
2018-11-07 05:03:21 +08:00
|
|
|
|
|
|
|
// - - - Sensor Warm Up - - -
|
|
|
|
|
|
|
|
case STATE_WARMUP_DISP_ON: // DONE
|
|
|
|
if ( is_display_on_event() )
|
2018-11-11 23:03:06 +08:00
|
|
|
{
|
|
|
|
// display is already enabled, but the timer is reseted
|
2018-11-07 05:03:21 +08:00
|
|
|
display_timer = DISPLAY_TIME;
|
2018-11-11 23:03:06 +08:00
|
|
|
}
|
2018-11-07 05:03:21 +08:00
|
|
|
|
2018-11-06 13:18:39 +08:00
|
|
|
if ( display_timer == 0 )
|
|
|
|
{
|
2018-11-11 23:03:06 +08:00
|
|
|
disable_display();
|
2018-11-06 13:18:39 +08:00
|
|
|
state = STATE_WARMUP_DISP_OFF;
|
|
|
|
}
|
2018-11-07 05:03:21 +08:00
|
|
|
else if ( sensor_warmup_timer == 0 )
|
2018-11-06 13:18:39 +08:00
|
|
|
{
|
2018-11-12 03:17:44 +08:00
|
|
|
// set the baseline at the end of the warmup
|
|
|
|
// this is described in the "SGP30 Driver Integration"
|
|
|
|
if ( eco2_base != 0 )
|
|
|
|
sgp.setIAQBaseline(eco2_base, tvoc_base); // Restore the baseline values
|
|
|
|
|
2018-11-07 05:03:21 +08:00
|
|
|
sensor_measure_timer = SENSOR_MEASURE_TIME;
|
|
|
|
state = STATE_MEASURE_DISP_ON;
|
2018-11-06 13:18:39 +08:00
|
|
|
}
|
|
|
|
break;
|
2018-11-07 05:03:21 +08:00
|
|
|
|
|
|
|
case STATE_WARMUP_DISP_OFF: // DONE
|
|
|
|
if ( is_display_on_event() )
|
|
|
|
{
|
2018-11-11 23:03:06 +08:00
|
|
|
enable_display();
|
2018-11-07 05:03:21 +08:00
|
|
|
display_timer = DISPLAY_TIME;
|
2018-12-09 19:42:41 +08:00
|
|
|
new_display_cool_down_timer = NEW_DISPLAY_COOL_DOWN; // ensure, that the display page is visible for a while
|
2018-11-07 05:03:21 +08:00
|
|
|
state = STATE_WARMUP_DISP_ON;
|
|
|
|
}
|
|
|
|
else if ( sensor_warmup_timer == 0 )
|
|
|
|
{
|
2018-11-12 03:17:44 +08:00
|
|
|
// set the baseline at the end of the warmup
|
|
|
|
// this is described in the "SGP30 Driver Integration"
|
|
|
|
if ( eco2_base != 0 )
|
|
|
|
sgp.setIAQBaseline(eco2_base, tvoc_base); // Restore the baseline values
|
|
|
|
|
2018-11-07 05:03:21 +08:00
|
|
|
sensor_measure_timer = SENSOR_MEASURE_TIME;
|
|
|
|
state = STATE_MEASURE_DISP_OFF;
|
|
|
|
}
|
2018-11-06 13:18:39 +08:00
|
|
|
break;
|
2018-11-07 05:03:21 +08:00
|
|
|
|
|
|
|
// - - - Sensor Measure - - -
|
|
|
|
|
|
|
|
case STATE_MEASURE_DISP_ON: // DONE
|
2018-11-12 03:17:44 +08:00
|
|
|
start = millis();
|
2018-11-11 23:03:06 +08:00
|
|
|
sgp.getIAQBaseline(&eco2_base, &tvoc_base); // always store the calibration values during measure
|
2018-11-12 03:17:44 +08:00
|
|
|
readAirQuality();
|
|
|
|
millis_sensor = millis() - start;
|
2018-11-11 23:03:06 +08:00
|
|
|
|
2018-11-07 05:03:21 +08:00
|
|
|
if ( is_display_on_event() )
|
2018-11-11 23:03:06 +08:00
|
|
|
{
|
|
|
|
// display is already enabled, but the timer is reseted
|
2018-11-07 05:03:21 +08:00
|
|
|
display_timer = DISPLAY_TIME;
|
2018-11-11 23:03:06 +08:00
|
|
|
}
|
2018-11-07 05:03:21 +08:00
|
|
|
|
2018-11-06 13:18:39 +08:00
|
|
|
if ( display_timer == 0 )
|
|
|
|
{
|
2018-11-11 23:03:06 +08:00
|
|
|
disable_display();
|
2018-11-06 13:18:39 +08:00
|
|
|
state = STATE_MEASURE_DISP_OFF;
|
|
|
|
}
|
2018-11-07 05:03:21 +08:00
|
|
|
else if ( sensor_measure_timer == 0 )
|
2018-11-06 13:18:39 +08:00
|
|
|
{
|
2018-11-07 05:03:21 +08:00
|
|
|
// ignored: Measurement is continued until the display goes off.
|
2018-11-06 13:18:39 +08:00
|
|
|
}
|
|
|
|
break;
|
2018-11-07 05:03:21 +08:00
|
|
|
|
|
|
|
case STATE_MEASURE_DISP_OFF: // DONE
|
2018-11-12 03:17:44 +08:00
|
|
|
start = millis();
|
2018-11-11 23:03:06 +08:00
|
|
|
sgp.getIAQBaseline(&eco2_base, &tvoc_base); // always store the calibration values during measure
|
2018-11-12 03:17:44 +08:00
|
|
|
readAirQuality();
|
|
|
|
millis_sensor = millis() - start;
|
2018-11-11 23:03:06 +08:00
|
|
|
|
2018-11-07 05:03:21 +08:00
|
|
|
if ( is_display_on_event() )
|
|
|
|
{
|
2018-11-11 23:03:06 +08:00
|
|
|
enable_display();
|
2018-11-07 05:03:21 +08:00
|
|
|
display_timer = DISPLAY_TIME;
|
2018-12-09 19:42:41 +08:00
|
|
|
new_display_cool_down_timer = NEW_DISPLAY_COOL_DOWN; // ensure, that the display page is visible for a while
|
2018-11-07 05:03:21 +08:00
|
|
|
state = STATE_MEASURE_DISP_ON;
|
|
|
|
}
|
|
|
|
else if ( sensor_measure_timer == 0 )
|
|
|
|
{
|
2018-11-11 23:03:06 +08:00
|
|
|
disable_sensors();
|
2018-11-07 05:03:21 +08:00
|
|
|
state = STATE_SENSOR_SLEEP_DISP_OFF;
|
|
|
|
}
|
2018-11-06 13:18:39 +08:00
|
|
|
break;
|
2018-11-07 05:03:21 +08:00
|
|
|
|
|
|
|
// - - - Sensor Sleep - - -
|
|
|
|
|
|
|
|
case STATE_SENSOR_SLEEP_DISP_OFF: // DONE
|
|
|
|
if ( is_display_on_event() )
|
|
|
|
{
|
2018-11-11 23:03:06 +08:00
|
|
|
enable_display();
|
2018-11-07 05:03:21 +08:00
|
|
|
display_timer = DISPLAY_TIME;
|
2018-12-09 19:42:41 +08:00
|
|
|
new_display_cool_down_timer = NEW_DISPLAY_COOL_DOWN; // ensure, that the display page is visible for a while
|
2018-11-11 23:03:06 +08:00
|
|
|
|
|
|
|
enable_sensors();
|
|
|
|
|
|
|
|
sensor_warmup_timer = SENSOR_WARMUP_TIME;
|
2018-11-07 05:03:21 +08:00
|
|
|
state = STATE_WARMUP_DISP_ON;
|
|
|
|
}
|
|
|
|
else if ( is_sensor_sample_timer_alarm != 0 )
|
|
|
|
{
|
|
|
|
is_sensor_sample_timer_alarm = 0;
|
2018-11-11 23:03:06 +08:00
|
|
|
|
|
|
|
enable_sensors();
|
2018-11-12 03:17:44 +08:00
|
|
|
|
2018-11-07 05:03:21 +08:00
|
|
|
sensor_warmup_timer = SENSOR_WARMUP_TIME;
|
|
|
|
state = STATE_WARMUP_DISP_OFF;
|
|
|
|
}
|
2018-11-06 13:18:39 +08:00
|
|
|
break;
|
2018-11-07 05:03:21 +08:00
|
|
|
|
2018-11-06 13:18:39 +08:00
|
|
|
default:
|
|
|
|
state = STATE_RESET;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2018-11-01 05:57:23 +08:00
|
|
|
//===================================================
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void loop(void) {
|
|
|
|
uint8_t i;
|
|
|
|
u8g2_uint_t g;
|
2018-11-10 06:17:55 +08:00
|
|
|
uint32_t start;
|
|
|
|
|
|
|
|
if ( is_wdt_irq )
|
|
|
|
{
|
|
|
|
is_wdt_irq = 0;
|
|
|
|
|
|
|
|
next_state();
|
2018-12-16 06:43:11 +08:00
|
|
|
readBatteryVoltageLevel();
|
2018-12-09 19:42:41 +08:00
|
|
|
handle_new_display_page();
|
2018-11-10 06:17:55 +08:00
|
|
|
|
2018-11-11 23:03:06 +08:00
|
|
|
if ( is_display_enabled ) // "is_display_enabled" will be calculated in next_state()
|
|
|
|
{
|
|
|
|
start = millis();
|
2018-12-09 19:42:41 +08:00
|
|
|
if ( current_display_page == 0 )
|
2018-11-11 23:03:06 +08:00
|
|
|
draw_with_emo();
|
2018-12-09 19:42:41 +08:00
|
|
|
else if ( current_display_page == 1 )
|
2018-11-11 23:03:06 +08:00
|
|
|
draw_with_history();
|
2018-12-09 19:42:41 +08:00
|
|
|
else if ( current_display_page == 2 )
|
2018-11-11 23:03:06 +08:00
|
|
|
draw_system();
|
|
|
|
millis_display = millis() - start;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
millis_display = 0;
|
|
|
|
}
|
2018-11-10 06:17:55 +08:00
|
|
|
}
|
2018-11-01 05:57:23 +08:00
|
|
|
|
2018-11-06 13:18:39 +08:00
|
|
|
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
|
2018-11-10 06:17:55 +08:00
|
|
|
//noInterrupts();
|
2018-11-06 13:18:39 +08:00
|
|
|
sleep_enable();
|
2018-11-10 06:17:55 +08:00
|
|
|
//interrupts();
|
2018-11-06 13:18:39 +08:00
|
|
|
sleep_mode();
|
|
|
|
sleep_disable();
|
2018-11-01 05:57:23 +08:00
|
|
|
}
|