-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsettings.h
470 lines (406 loc) · 21.4 KB
/
settings.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
#ifndef __SETTINGS_H_
#define __SETTINGS_H_
#define LV //to identify region
#define _I2C_OLED //define to load proper lib
#define OLED_WAKEUP_BTN D1
#define ABSOLUTE_ZERO_TEMP_C -273 //-273.15
#ifdef DEBUG
#define PRINTLN(s) Serial.println(s)
#define PRINT(s) Serial.print(s)
#define PRINTF(s,m) Serial.printf(s,m)
#define PRINTLN2(s,m) Serial.println(s,m)
#define PRINT2(s,m) Serial.print(s,m)
#else
#define PRINTLN(s)
#define PRINT(s)
#define PRINTF(s,m)
#define PRINTLN2(s,m)
#define PRINT2(s,m)
#endif
//sensor libs
#include "DHT.h"
#include "src/Adafruit_BMP085_Library/Adafruit_BMP085.h"
#ifdef _I2C_OLED
#include "src/esp8266-oled-ssd1306/src/SSD1306Wire.h"
#include "src/esp8266-oled-ssd1306/src/OLEDDisplayUi.h"
#endif
//other peripheral libs
#include "Wire.h"
#include <ESPWiFi.h>
#include <ESP8266WiFi.h>
//service libs
#include <ESPHTTPClient.h>
#include <JsonListener.h>
#include "OpenWeatherMapCurrent.h"
#include "OpenWeatherMapForecast.h"
//system libs
#ifdef CCS811_STORE_BASELINE
#define _EEPROM_
#elif defined CCS811_RESTORE_BASELINE
#define _EEPROM_
#endif
#ifdef _EEPROM_
/**
* Due to the nature of this flash memory (NOR) a full sector erase must be done prior to write any new data.
* If a power failure (intended or not) happens during this process the sector data is lost.
* Also, writing data to a NOR memory can be done byte by byte but only to change a 1 to a 0.
* The only way to turn 0s to 1s is to perform a sector erase which turns all memory positions in that sector to 1.
* But sector erasing must be done in full sectors, thus wearing out the flash memory faster.
*
* Ro overcome this use sector rotation.
*/
#define NO_GLOBAL_EEPROM //disable default eeprom object
#include <EEPROM_Rotate.h>
#define EEPROM_BASELINE_START_1B 0xA5
#define EEPROM_BASELINE_START_2B 0xB2
#define EEPROM_SECTOR_POOL_COUNT 4 //use 4 sectors in pool
#define EEPROM_SECTOR_POOL_SIZE 4096 //1 sector size
EEPROM_Rotate EEPROMPool;
#endif
#include <time.h> // time() ctime()
#include <sys/time.h> // struct timeval
#include <Ticker.h> //Works like a "thread", where a secondary function will run when necessary.
// The library use no interupts of the hardware timers and works with the micros() / millis() function
// if repeats is 0 the ticker runs in endless mode.
#include "WeatherStationFonts.h"
#include "WeatherStationImages.h"
/***************************
* Interrupt driven events
**************************/
#include "src/PinButtonEventISR/src/PinButtonEventISR.h"
PinButtonEventISR D7Button(OLED_WAKEUP_BTN);
/***************************
* Time Settings
**************************/
#define STYLE_24HR
#include <TZ.h>
#ifdef LV
#define MYTZ TZ_Europe_Riga
//#define NTP_SERVERS "0.lv.pool.ntp.org", "1.lv.pool.ntp.org", "2.lv.pool.ntp.org"
#endif
/***************************
* WIFI Settings
**************************/
//include credentials
#include "secret.h" //WIFI_SSID, WIFI_PWD
WiFiClient client;
/***************************
* Begin CCS811 Settings
**************************/
/*
* #TODO save baseline to eeprom(flash) by push button and by timer (every 12h?): https://pastebin.com/KGigYPr6
*
* NB! the sensor is insensitive for CO2. The trick is that the CO2 reading assumes the sensor is inside a building
* (iAQ - indoor air quality), and that humans are the (only) producer of CO2.
* So the gas sensor measures VOCs, assumes they are from humans, maps that to the amount of humans,
* and then maps that to the CO2 they would produce. Thus, TVOC and CO2 correlate.
*
* "metal oxide sensors do not give absolute readings". Yes they pretend with their CO2 and TVOC registers, but they don't.
* They measure the resistance of their metal oxide layer, and then check how much that deviates from normal resistance,
* and that deviation maps to a CO2/TVOC readout. The problem is with this normal resistance, quoting the data sheet again
* "The resistance RS varies from sensor to sensor (manufacturing variation), from use-case to use-case, and over time."
* Bottom line, all sensors give a different CO2/TVOC reading, but when it goes up it at least we know air got worse.
*
* NB clean (normal) air resistance varies: "To mitigate this problem, the output of the sensor is normalized:
* RS is divided by RA. The value of RA is known as the baseline. RA cannot be determined by a one-time calibration;
* it is maintained on-the-fly in software". By power cycling, you effectively remove the clean air knowledge built up
* by the sensor: if you power a sensor on in bad air, it only has that as a reference and considers that as clean.
*
* VOCs detected
* Alcohols, Aldehydes, Ketones, Organic Acids, Amines, Aliphatic and Aromatic Hydrocarbons
* Cross sensitivity
* Humidity and Hydrogen
*
* eCO2
* The equivalent CO2 (eCO2) output range for CCS811 is from
* 400ppm up to 32768ppm.
* eTVOC
* The equivalent Total Volatile Organic Compound (eTVOC)
* output range for CCS811 is from 0ppb up to 29206ppb.
*
*****************************************************************************
* CO2
* 250-400ppm Normal background concentration in outdoor ambient air
* 400-1,000ppm Concentrations typical of occupied indoor spaces with good air exchange
* 1,000-2,000ppm Complaints of drowsiness and poor air.
* 2,000-5,000 ppm Headaches, sleepiness and stagnant, stale, stuffy air. Poor concentration, loss of attention,
* increased heart rate and slight nausea may also be present.
* 5,000 Workplace exposure limit (as 8-hour TWA) in most jurisdictions.
* >40,000 ppm Exposure may lead to serious oxygen deprivation resulting in permanent brain damage, coma, even death.
*
* CO
* 9 ppm CO Max prolonged exposure (ASHRAE standard)
* 35 ppm CO Max exposure for 8 hour work day (OSHA)
* 800 ppm CO Death within 2 to 3 hours
* 12,800 ppm CO Death within 1 to 3 minutes
*
* - ppb (v) is parts per billion by volume (i.e., volume of gaseous pollutant per 10^9 volumes of ambient air).
* - µg/m3 is micrograms of gaseous pollutant per cubic meter of ambient air.
*
* The conversion assumes an ambient pressure of 1 atmosphere (1 atm = 1.01325 bar) and a temperature of 25 degrees Celsius.
* Sulphur dioxide (SO2) 1 ppb = 2.62 µg/m3 = 64.07 g/mol
* Nitrogen dioxide (NO2) 1 ppb = 1.88 µg/m3 = 46.01 g/mol
* Nitric oxide (NO) 1 ppb = 1.25 µg/m3 = 30.01 g/mol
* Ozone (O3) 1 ppb = 2.00 µg/m3 = 48 g/mol
* Carbon monoxide (CO) 1 ppb = 1.15 µg/m3 = 28.01 g/mol
* Benzene 1 ppb = 3.19 µg/m3
* Ammonia (NH3) 1 ppb = 0.70 µg/m3 = 17.03 g/mol
*
* The general equation is µg/m^3 = M * (ppb)*(12.187) / (273.15 + °C), where
* M is the molecular weight of the gaseous pollutant.
*
* Concentration (µg/m3) = M x concentration (ppb) ÷ 24.45
* Concentration (ppb) = 24.45 x concentration (µg/m3) ÷ M
* The number 24.45 in the equation is the volume (litres) of a mole (gram molecular weight) of a gas when the temperature is at 25°C and the pressure is at 1 atmosphere (1 atm = 1.01325 bar).
*
* In view of the fact that there are few controlled human exposure studies and the results are not confirmed,
* and that the results of epidemiological studies are inconsistent, it is today not possible to conclude that
* sensory irritation is associated with the sum of mass concentrations of VOCs at the low exposure levels
* typically encountered in non-industrial indoor air. Therefore, although the likelihood of sensory effects
* will increase with increasing TVOC concentration, at present no precise guidance can be given on which levels of TVOC
* are of concern from a health and comfort point of view, and the magnitude of protection margins needed cannot be estimated.
* Nevertheless, the general need for improved source control to diminish the pollution load on the indoor environments
* from health, comfort, energy efficiency and sustainability points of view leads to the recommendation that VOC levels
* in indoor air should be kept As Low As Reasonably - Achievable (ALARA).
*
* ... air leakage in most homes, and in non-residential buildings too, will generally remove the chemicals faster
* than the researchers reported for the plants tested by NASA.
* The most effective household plants reportedly included aloe vera, English ivy,
* and Boston fern for removing chemicals and biological compounds.
* https://cdn.sparkfun.com/assets/learn_tutorials/1/4/3/ECA_Report19.pdf
*/
//#include "src/SparkFun_CCS811_Arduino_Library/src/SparkFunCCS811.h" //Click here to get the library: http://librarymanager/All#SparkFun_CCS811
#include "src/CCS811-master/src/ccs811.h"
//#define CCS811_ADDR 0x5B //2nd I2C Address (HIGH)
#define CCS811_ADDR 0x5A //1st I2C Address (LOW)
CCS811 MCU811b(CCS811_ADDR);
/*
* The CCS811 supports compensation for relative humidity and ambient temperature.
* The ENV_DATA registers can be updated with the temperature and humidity (TH) values on each cycle.
* The data sheet requirement that current temp value plus 25 is written
* to the ENV_DATA’s temperature register.
* NB If the application supports temperature or humidity compensation, but not both, the data sheet’s
* default value must be written to the register corresponding to the unsupported environment
* parameter. The user must not write zero to the unsupported temperature or humidity field of the ENV_DATA register.
*/
/***************************
* Begin HDC1080 Settings
**************************/
/*
* NB sensor is unable to compensate for the heat produced by the CCS811
* This can cause the displayed temperature to be up to 15° higher than the actual ambient temperature.
* To compensate for this take the difference between an initial temperature reading (with a cold sensor)
* and a final temperature reading (after letting the sensor warm up).
* Then subtract this value from `BMEtempC` to ensure the proper calibration is taking place.
*
* TODO# One of the solution is to disable heating (???) for CCS811 by set ccs811.setDriveMode(CCS811_DRIVE_MODE_IDLE);
* after measure, you will see how values of HDC1080 slowly returning to normal.
*
* The HDC1080 has two modes of operation:
* - sleep mode
* - measurement mode.
* After power up, the HDC1080 is in sleep mode. In this mode, the HDC1080 waits for I2C input including commands
* to configure the conversion times, read the status of the battery, trigger a measurement, and read measurements.
* Once it receives a command to trigger a measurement, the HDC1080 moves from sleep mode to measurement mode.
* After completing the measurement the HDC1080 returns to sleep mode.
*/
#include "src/ClosedCube_HDC1080/ClosedCube_HDC1080.h"
#define HDC1080_I2C_ADDR 0x40
#define HDC1080_TEMP_RESOLUTION_BITS HDC1080_RESOLUTION_11BIT //conversion times:
// 11bit 3.65ms
// 14bit 6.35ms
#define HDC1080_HUMID_RESOLUTION_BITS HDC1080_RESOLUTION_8BIT
// 8bit 2.5ms
// 11bit 3.85ms
// 14bit 6.5ms
ClosedCube_HDC1080 hdc1080;
/***************************
* Begin Atmosphere and Light Sensor Settings
**************************/
#define BH1750FVI_I2C_ADDR 0b0100011 // BH1750FVI:
// Slave Address is 2 types, it is determined by ADDR Terminal
// ADDR = ‘H’ ( ADDR ≧ 0.7VCC ) → “1011100“ -> 0x5c
// ADDR = 'L' ( ADDR ≦ 0.3VCC ) → “0100011“ -> 0x23
//#define MTREG_DEFAULT 69 //changable range of MTreg.
// Min. Typ. Max.
//
//changeable bin 0001_1111 0100_0101 1111_1110
//range of MTreg dec 31 69 254
//sensitivity: (default*0.45) (default) (default*3.68)
#define LUX_PER_COUNT(X) ( (X) * 0.83 ) //H-reslution mode : Illuminance per 1 count ( lx / count ) = 1 / 1.2 *( 69 / X )
//H-reslution mode2 : Illuminance per 1 count ( lx / count ) = 1 / 1.2 *( 69 / X ) / 2
#define BH1750FVI_POWERON 0b00000001
#define BH1750FVI_RESET_RDATA 0b00000111 // remove previous measurement result.
// Asynchronous reset: It is necessary on power supply sequence
// Reset command is not acceptable in Power Down mode.
//HResMode is suitable for darkness ( less than 10 lx )
//#define BH1750FVI_HRES_MODE_CONTINUOUS 0b00010000 //Start measurement at 1lx resolution.
#define BH1750FVI_HRES_MODE_ONCE 0b00100000 //Automatically set to Power Down mode after measurement.
//Measurement Time is typically 120ms
//#define BH1750FVI_HRES_MODE2_CONTINUOUS 0b00010001 //Start measurement at 0.5lx resolution.
//#define BH1750FVI_HRES_MODE2_ONCE 0b00100001 //Automatically set to Power Down mode after measurement.
//Measurement Time is typically 120ms
//#define BH1750FVI_LRES_MODE_CONTINUOUS 0b00010011 //Start measurement at 4lx resolution.
//#define BH1750FVI_LRES_MODE_ONCE 0b00100011 //Automatically set to Power Down mode after measurement.
//Measurement Time is typically 16ms
/**
* The BMP180 delivers the uncompensated value of pressure and temperature
* The E2PROM has stored 176 bit of individual calibration data. This is used to
* compensate offset, temperature dependence and other parameters of the sensor.
* - UP = Uncalibrated pressure data (16 to 19 bit)
* - UT = uncalibrated temperature data (16 bit)
* For calculating temperature in °C and pressure in hPa, the calibration data has to be used.
*
* The 176 bit E2PROM is partitioned in 11 words of 16 bit each. These contain 11 calibration
* coefficients.
*
* NB Every sensor module has individual coefficients.
* //pressure calibration
* ac1 = 7800
* ac2 = -1154
* ac3 = -14445
* ac4 = 33350
*
* b1 = 6515
* b2 = 44
*
* mb = -32768 <- not used
*
* //temperature calibration
* ac5 = 25614
* ac6 = 18868
* mc = -11786
* md = 2446
*
* Before the first calculation of temperature and pressure, the master reads out the E2PROM data.
* The data communication can be checked by checking that none of the words has the value 0 or
* 0xFFFF.
*/
//Factory Temperature calibration param override
// For pressure calculation factory calibration will be used
//@see adafruit library to enable pressure and temperature calibration fix from inside
// fixing ~7*C offset from thermometer readings
#define AC5 24140
#define AC6 19911
#define MC -10237
#define MD 2446
Adafruit_BMP085 bmp; //has own temperature sensor
#define BMP180_I2C_ADDR 0b1110111 // BMP180: address:0x77
// The LSB of the device address distinguishes between read (1) and write (0) operation,
// corresponding to address 0xEF (read) and 0xEE (write).
// read : 0b1110111[1]
// write: 0b1110111[0]
/***************************
* Begin Oled display Settings
**************************/
// Display Settings
#ifdef _I2C_OLED
const int I2C_DISPLAY_ADDRESS = 0x3c; //it's harcoded, thus if not in datasheet must be guessed
//Wire lib for ESP8266 and ESP32 allows you to specify the SDA (and SCL) pins of the I2C bus.
#if defined(ESP8266)
//https://github.com/esp8266/Arduino/blob/master/variants/nodemcu/pins_arduino.h
const int SDA_PIN = D3; //0
const int SDC_PIN = D4; //2
#else
//const int SDA_PIN = GPIO5;
//const int SDC_PIN = GPIO4
const int SDA_PIN = GPIO0;
const int SDC_PIN = GPIO2
#endif
// Initialize the oled display for address 0x3c
//128x64 by default
SSD1306Wire display(I2C_DISPLAY_ADDRESS, SDA_PIN, SDC_PIN, GEOMETRY_128_64);
#endif
// Initialize the oled display UI
OLEDDisplayUi ui(&display);
/***************************
* Begin thingspeak.com settings
***************************/
//@see secret.h for credentials THINGSPEAK_API_WRITE, THINGSPEAK_API_READ
#define THINGSPEAK_SERVER "api.thingspeak.com"
#define THINGSPEAK_PORT 80
/***************************
* Begin OpenWeatherMap settings
***************************/
const boolean IS_METRIC = true;
// @see secret.h for credentials
String OPEN_WEATHER_MAP_LOCATION = "Riga,LV";
// Pick a language:
String OPEN_WEATHER_MAP_LANGUAGE = "en";
const uint8_t MAX_FORECASTS = 3;
// Adjust according to your language
const String WDAY_NAMES[] = {"SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT"};
const String MONTH_NAMES[] = {"JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"};
OpenWeatherMapCurrentData currentWeather;
OpenWeatherMapCurrent currentWeatherClient;
OpenWeatherMapForecastData forecasts[MAX_FORECASTS];
OpenWeatherMapForecast forecastClient;
/***************************
* Begin ticker settings
**************************/
//service update interval (forecast, time, etc)
#define TS_WRITE_UPDATE_INTERVAL_1M_SEC 60 //max Message update interval limit for home licence 1 upd/sec
//total messages per year 33 million (~90k/day)
#define FORECAST_UPDATE_INTERVAL_12H_SECS 12*60*60 // Update every 12 hours
#define CURR_WEATHER_UPDATE_INTERVAL_20M_SECS 20*60 // Update every 20 minutes
#define DISPLAY_SLEEP_INTERVAL_5M_SECS 5*60 //put display into sleep after 5 min
#define SENSORS_READ_AWAKE_INTERVAL_3S_SECS 3 //while display is awake poll sensors frequently
// when it's off, then poll based on upload time to server (ThingSpeak)
//#define CCS811_BASELINE_RESTORE_INTERVAL_ONESHOT_SECS 20*60 // is called once after 23 minutes have passed since boot
bool ccs811BaselineRestored = false;
uint8_t ccs811BaselineRestoreCount = 0;
Ticker forecastUpdateTicker;
Ticker weatherUpdateTicker;
Ticker sensorsReadTicker;
Ticker sensorsDataUploadTicker;
Ticker displaySleepTicker;
// read remote service data; flag changed in the ticker function every UPDATE_INTERVAL_SECS
bool readyForWeatherServiceUpdate = false;
bool readyForCurrentWeatherUpdate = false;
// read sensors; flag changed in the ticker function every 5 sec
bool readyForSensorsRead = false;
// post local sensor data to remote services;
bool readyForSensorDataUpload = false;
/***************************
* End ticker Settings
**************************/
/******************************
* Basic init
*****************************/
time_t now;
unsigned int tempLight = 0;
unsigned int atmPressure = 0;
int atmAlt = 0; // calculate altitude, assuming 'standard' barometric
// pressure of 1013.25 millibar = 101325 Pascal
float temp = ABSOLUTE_ZERO_TEMP_C; //temperature
float ccs811hdc1080Temp = 0;
uint8_t humidity = 0; //humidity
unsigned int eCO2 = 0;
unsigned int eTVOC = 0;
uint16_t ccs811_baseline = 0;
/******************************
* EEPROM Prototypes
*****************************/
uint16_t eepromGetBaseline();
uint16_t eepromStoreBaseline(uint16_t baseline);
/******************************
* Sensor Prototypes
*****************************/
void uploadTemperatureHumidity();
void readLight();
void readAtmosphere();
void readTemperatureHumidity();
void readCCS811b();
/******************************
* End of Sensor Prototypes
*****************************/
void connectWifi();
int8_t getWifiQuality();
/******************************
* Timer(ticker) flag update Prototypes
*****************************/
void setReadyForWeatherServiceUpdate();
void setReadyForCurrentWeatherUpdate();
void setReadyForSensorsRead();
void setReadyForSensorDataUpload();
#endif /* define __SETTINGS_H_*/