From 9911aa827214bd62ec9d60d7087e7000c7b19c6b Mon Sep 17 00:00:00 2001 From: igor Date: Wed, 12 Jul 2023 18:51:20 -0700 Subject: [PATCH] Complete rewrite. --- aqi.ino | 385 +++++++++++++++++++++++++++++++++----------------------- 1 file changed, 229 insertions(+), 156 deletions(-) diff --git a/aqi.ino b/aqi.ino index 3c1f0ee..b664cdd 100644 --- a/aqi.ino +++ b/aqi.ino @@ -1,33 +1,242 @@ #include +#include +#include #include #include -#include +// 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); -const int buttonPin = 6; -// to simulate "parallelism" -unsigned long currentTime; -unsigned long sen44Time = 0; -unsigned long lcdTime = 0; -unsigned long buttonTime = 0; +// LCD resolution. +enum { LCD_WIDTH = 16, LCD_HEIGHT = 2 }; -// display standard values by default -bool isStandardValues = true; +// Supported values to be displayed on the LCD. +enum supported_vals { pm_1p0, pm2p5, pm4p0, pm10p0, voc, hum, temp, f_temp, empty }; -// sen44 values -uint16_t massConcentrationPm1p0 = 0; -uint16_t massConcentrationPm2p5 = 0; -uint16_t massConcentrationPm4p0 = 0; -uint16_t massConcentrationPm10p0 = 0; -float vocIndex = 0; -float ambientHumidity = 0; -float ambientTemperature = 0; -float feelsLikeTemperature = 0; +// 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}, +}; -// calculate heat index -float heatIndex(float t, float rh) +// 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); + } +} + +// Force 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("1: "); + lcd.print(sen44_mes.pm1p0); + break; + case pm2p5: + lcd.print("2: "); + lcd.print(sen44_mes.pm2p5); + break; + case pm4p0: + lcd.print("4: "); + 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) @@ -51,139 +260,3 @@ float heatIndex(float t, float rh) } return hi; } - -// convert celcius to fahrenheit -float celsiusToFahrenheit(float degrees) -{ - return degrees * 1.8 + 32; -} - -void setup() -{ - Serial.begin(115200); - while (!Serial) - delay(100); - - // set up the LCD's number of columns and rows: - lcd.begin(16, 2); - - // set up the button pin - pinMode(buttonPin, INPUT_PULLUP); - - 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); - } - - // Start Measurement - error = sen44.startMeasurement(); - - if (error) { - Serial.print("Error trying to execute startMeasurement(): "); - errorToString(error, errorMessage, 256); - Serial.println(errorMessage); - } - - // Clean Fan - sen44.startFanCleaning(); - - // calibrate - lcd.setCursor(0, 0); - lcd.print(" calibrating..."); - - // allow 2 minute calibration - delay(120000); -} - -void update_lcd() -{ - lcd.clear(); - - if (isStandardValues) { - lcd.setCursor(0, 0); - lcd.print("t:"); - lcd.print(round(ambientTemperature)); - - lcd.setCursor(8, 0); - lcd.print("h:"); - lcd.print(round(ambientHumidity)); - - lcd.setCursor(0, 1); - lcd.print("f:"); - lcd.print(round(feelsLikeTemperature)); - - lcd.setCursor(8, 1); - lcd.print("v:"); - lcd.print(vocIndex, 0); - } else { - lcd.setCursor(0, 0); - lcd.print(" 1:"); - lcd.print(massConcentrationPm1p0); - - lcd.setCursor(8, 0); - lcd.print(" 2:"); - lcd.print(massConcentrationPm2p5); - - lcd.setCursor(0, 1); - lcd.print(" 4:"); - lcd.print(massConcentrationPm4p0); - - lcd.setCursor(8, 1); - lcd.print("10:"); - lcd.print(massConcentrationPm10p0); - } -} - -void loop() -{ - currentTime = millis(); - - // modes button - if (currentTime - buttonTime >= 100) { - buttonTime = currentTime; - - if (digitalRead(buttonPin) == LOW) { - isStandardValues = !isStandardValues; - update_lcd(); - } - } - - // sen44 - if (currentTime - sen44Time >= 1000) { - sen44Time = currentTime; - - uint16_t error; - char errorMessage[256]; - - // Read Measurement - error = sen44.readMeasuredMassConcentrationAndAmbientValues( - massConcentrationPm1p0, massConcentrationPm2p5, massConcentrationPm4p0, - massConcentrationPm10p0, vocIndex, ambientHumidity, ambientTemperature); - - if (error) { - Serial.print("Error trying to execute " - "readMeasuredMassConcentrationAndAmbientValues(): "); - errorToString(error, errorMessage, 256); - Serial.println(errorMessage); - return; - } - - ambientTemperature = celsiusToFahrenheit(ambientTemperature); - feelsLikeTemperature = heatIndex(ambientTemperature, ambientHumidity); - } - - // Display - if (currentTime - lcdTime >= 2000) { - lcdTime = currentTime; - update_lcd(); - } -}