263 lines
7.3 KiB
C++
263 lines
7.3 KiB
C++
#include <Arduino.h>
|
|
#include <LiquidCrystal.h>
|
|
#include <math.h>
|
|
#include <SensirionI2CSen44.h>
|
|
#include <Wire.h>
|
|
|
|
// Setup Sen44 sensor.
|
|
SensirionI2CSen44 sen44;
|
|
|
|
// Sen44 measurements.
|
|
struct {
|
|
uint16_t pm1p0 = 0;
|
|
uint16_t pm2p5 = 0;
|
|
uint16_t pm4p0 = 0;
|
|
uint16_t pm10p0 = 0;
|
|
float voc = 0;
|
|
float hum = 0;
|
|
float temp = 0;
|
|
float f_temp = 0;
|
|
} sen44_mes;
|
|
|
|
// Setup LCD.
|
|
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
|
|
|
|
// LCD resolution.
|
|
enum { LCD_WIDTH = 16, LCD_HEIGHT = 2 };
|
|
|
|
// Supported values to be displayed on the LCD.
|
|
enum supported_vals { pm_1p0, pm2p5, pm4p0, pm10p0, voc, hum, temp, f_temp, empty };
|
|
|
|
// Values per screen. values[0...] is considered a screen and values[][0...]
|
|
// are its values.
|
|
enum supported_vals values[][4] = {
|
|
{temp, f_temp, hum, voc},
|
|
{pm_1p0, pm2p5, pm4p0, pm10p0},
|
|
};
|
|
|
|
// Screens and values counts.
|
|
#define SCREENS_COUNT (sizeof(values) / sizeof(values[0]))
|
|
#define VALUES_COUNT (sizeof(values[0]) / sizeof(values[0][0]))
|
|
|
|
// Current screen.
|
|
unsigned int current_screen = 0;
|
|
|
|
// Current columns and rows on the LCD.
|
|
int col = 0;
|
|
int row = 0;
|
|
|
|
// Setup buttons.
|
|
const int mode_btn = 6;
|
|
const int lcd_on_off_btn = 9;
|
|
|
|
// Setup HIGH/LOW output.
|
|
const int lcd_on_off_output = 10;
|
|
|
|
// Execution times for things that will be "parallelized".
|
|
struct {
|
|
unsigned long modes_btn = 0;
|
|
unsigned long on_off_btn = 0;
|
|
unsigned long update_lcd = 0;
|
|
unsigned long sen44 = 0;
|
|
} parallel;
|
|
|
|
// Force write data to screen.
|
|
void update_screen(void);
|
|
|
|
// Cycle between screens.
|
|
void cycle_screens(void);
|
|
|
|
// Convert Celcius to Fahrenheit.
|
|
float celsius_to_fahrenheit(float degrees);
|
|
|
|
// Calculate heat index (aka "feels like") termperature.
|
|
float heat_index(float t, float rh);
|
|
|
|
void setup(void)
|
|
{
|
|
// Setup Serial.
|
|
Serial.begin(115200);
|
|
while (!Serial)
|
|
delay(100);
|
|
|
|
// Set up the LCD's number of columns and rows.
|
|
lcd.begin(LCD_WIDTH, LCD_HEIGHT);
|
|
|
|
// Set LCD cursor to position 0, 0.
|
|
lcd.setCursor(0, 0);
|
|
lcd.print(" calibrating...");
|
|
|
|
// Setup buttons.
|
|
pinMode(mode_btn, INPUT_PULLUP);
|
|
pinMode(lcd_on_off_btn, INPUT_PULLUP);
|
|
|
|
// Setup HIGH/LOW output.
|
|
pinMode(lcd_on_off_output, OUTPUT);
|
|
digitalWrite(lcd_on_off_output, HIGH);
|
|
|
|
// Setup Sen44 sensor. From measurement to cleaning.
|
|
// Calibration takes 2 minutes.
|
|
Wire.begin();
|
|
sen44.begin(Wire);
|
|
uint16_t error;
|
|
char errorMessage[256];
|
|
error = sen44.deviceReset();
|
|
if (error) {
|
|
Serial.print("Error trying to execute getSerialNumber(): ");
|
|
errorToString(error, errorMessage, 256);
|
|
Serial.println(errorMessage);
|
|
}
|
|
error = sen44.startMeasurement();
|
|
if (error) {
|
|
Serial.print("Error trying to execute startMeasurement(): ");
|
|
errorToString(error, errorMessage, 256);
|
|
Serial.println(errorMessage);
|
|
}
|
|
sen44.startFanCleaning();
|
|
delay(120000);
|
|
}
|
|
|
|
void loop(void)
|
|
{
|
|
// "Parallel" execution for button to switch modes.
|
|
if (millis() - parallel.modes_btn >= 150) {
|
|
parallel.modes_btn = millis();
|
|
if (digitalRead(mode_btn) == LOW) {
|
|
cycle_screens();
|
|
update_screen();
|
|
}
|
|
}
|
|
|
|
// "Parallel" execution for button to turn on/off display.
|
|
if (millis() - parallel.on_off_btn >= 150) {
|
|
parallel.on_off_btn = millis();
|
|
if (digitalRead(lcd_on_off_btn) == LOW) {
|
|
if (digitalRead(lcd_on_off_output) == LOW)
|
|
digitalWrite(lcd_on_off_output, HIGH);
|
|
else
|
|
digitalWrite(lcd_on_off_output, LOW);
|
|
}
|
|
}
|
|
|
|
// "Parallel" execution for updating display.
|
|
if (millis() - parallel.update_lcd >= 2000) {
|
|
parallel.update_lcd = millis();
|
|
update_screen();
|
|
}
|
|
|
|
// "Parallel" execution for reading measurements from Sen44 sensor.
|
|
if (millis() - parallel.sen44 >= 1000) {
|
|
parallel.sen44 = millis();
|
|
uint16_t error;
|
|
char errorMessage[256];
|
|
error = sen44.readMeasuredMassConcentrationAndAmbientValues(
|
|
sen44_mes.pm1p0, sen44_mes.pm2p5, sen44_mes.pm4p0, sen44_mes.pm10p0,
|
|
sen44_mes.voc, sen44_mes.hum, sen44_mes.temp
|
|
);
|
|
if (error) {
|
|
Serial.print("Error trying to execute "
|
|
"readMeasuredMassConcentrationAndAmbientValues(): ");
|
|
errorToString(error, errorMessage, 256);
|
|
Serial.println(errorMessage);
|
|
return;
|
|
}
|
|
sen44_mes.temp = celsius_to_fahrenheit(sen44_mes.temp);
|
|
sen44_mes.f_temp = heat_index(sen44_mes.temp, sen44_mes.hum);
|
|
}
|
|
}
|
|
|
|
// Write data to screen.
|
|
void update_screen(void)
|
|
{
|
|
col = row = 0;
|
|
lcd.clear();
|
|
for (int f = 0; f < VALUES_COUNT; f++) {
|
|
lcd.setCursor(col, row);
|
|
switch (values[current_screen][f]) {
|
|
case pm_1p0:
|
|
lcd.print("01: ");
|
|
lcd.print(sen44_mes.pm1p0);
|
|
break;
|
|
case pm2p5:
|
|
lcd.print("02: ");
|
|
lcd.print(sen44_mes.pm2p5);
|
|
break;
|
|
case pm4p0:
|
|
lcd.print("04: ");
|
|
lcd.print(sen44_mes.pm4p0);
|
|
break;
|
|
case pm10p0:
|
|
lcd.print("10: ");
|
|
lcd.print(sen44_mes.pm10p0);
|
|
break;
|
|
case voc:
|
|
lcd.print("v: ");
|
|
lcd.print(round(sen44_mes.voc));
|
|
break;
|
|
case hum:
|
|
lcd.print("h: ");
|
|
lcd.print(round(sen44_mes.hum));
|
|
break;
|
|
case temp:
|
|
lcd.print("t: ");
|
|
lcd.print(round(sen44_mes.temp));
|
|
break;
|
|
case f_temp:
|
|
lcd.print("f: ");
|
|
lcd.print(round(sen44_mes.f_temp));
|
|
break;
|
|
case empty:
|
|
break;
|
|
}
|
|
// Evenly space all items on the LCD screen.
|
|
int spacing = LCD_WIDTH / (int)ceil(VALUES_COUNT / 2.0f);
|
|
col += spacing;
|
|
if (col > LCD_WIDTH - spacing) {
|
|
col = 0;
|
|
row += 1;
|
|
if (row > LCD_HEIGHT)
|
|
row = LCD_HEIGHT;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Cycle between screens.
|
|
void cycle_screens(void)
|
|
{
|
|
current_screen += 1;
|
|
if (current_screen >= SCREENS_COUNT)
|
|
current_screen = 0;
|
|
}
|
|
|
|
// Convert Celcius to Fahrenheit.
|
|
float celsius_to_fahrenheit(float degrees)
|
|
{
|
|
return degrees * 1.8 + 32;
|
|
}
|
|
|
|
// Calculate heat index (aka "feels like") termperature.
|
|
float heat_index(float t, float rh)
|
|
{
|
|
float hi = 0.0;
|
|
if (t <= 40.0)
|
|
hi = t;
|
|
else {
|
|
float hitemp = 61.0 + ((t - 68.0) * 1.2) + (rh * 0.094);
|
|
float hifinal = 0.5 * (t + hitemp);
|
|
hi = -42.379 + 2.04901523 * t + 10.14333127 * rh - 0.22475541 * t * rh -
|
|
6.83783 * (pow(10, -3)) * (pow(t, 2)) -
|
|
5.481717 * (pow(10, -2)) * (pow(rh, 2)) +
|
|
1.22874 * (pow(10, -3)) * (pow(t, 2)) * rh +
|
|
8.5282 * (pow(10, -4)) * t * (pow(rh, 2)) -
|
|
1.99 * (pow(10, -6)) * (pow(t, 2)) * (pow(rh, 2));
|
|
if (hifinal > 79.0) {
|
|
if ((rh <= 13) && (t >= 80.0) && (t <= 112.0))
|
|
hi = hi - ((13.0 - rh) / 4.0) * sqrt((17.0 - fabs(t - 95.0)) / 17.0);
|
|
else if ((rh > 85.0) && (t >= 80.0) && (t <= 87.0))
|
|
hi = hi + ((rh - 85.0) / 10.0) * ((87.0 - t) / 5.0);
|
|
} else
|
|
hi = hifinal;
|
|
}
|
|
return hi;
|
|
}
|