Compare commits
10 Commits
6d7d817d60
...
94ca0efa95
| Author | SHA1 | Date | |
|---|---|---|---|
| 94ca0efa95 | |||
| 31759c26a3 | |||
| 5a8f44cd48 | |||
| 16a0af1b24 | |||
| 0175d2db51 | |||
| 9911aa8272 | |||
| 1599185a25 | |||
| 7687db6dbd | |||
| 97b42ba949 | |||
| 2da8860215 |
14
README.md
Normal file
14
README.md
Normal file
@@ -0,0 +1,14 @@
|
||||
# Description
|
||||
|
||||
A simple AQI monitor made using Arduino Nano and Sen44 sensor.
|
||||
|
||||

|
||||
|
||||
Sen44 was found in Amazon's AQI monitor. Note, Sensirion has discontinued Sen44 and
|
||||
has replaced them with new Sen5(0,4,5) models.
|
||||
|
||||
Appropriate documentation and libraries can be found on their [website](https://sensirion.com/).
|
||||
|
||||
* [Sen44 Arduino Library](https://github.com/Sensirion/arduino-i2c-sen44)
|
||||
* [Sen44 Dependency](https://github.com/Sensirion/arduino-core)
|
||||
* [Arduino Nano](https://store.arduino.cc/products/arduino-nano)
|
||||
356
aqi.ino
356
aqi.ino
@@ -1,14 +1,250 @@
|
||||
#include <Arduino.h>
|
||||
#include <LiquidCrystal.h>
|
||||
#include <math.h>
|
||||
#include <SensirionI2CSen44.h>
|
||||
#include <Wire.h>
|
||||
#include <LiquidCrystal.h>
|
||||
|
||||
// Setup Sen44 sensor.
|
||||
SensirionI2CSen44 sen44;
|
||||
// initialize the library with the numbers of the interface pins
|
||||
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
|
||||
const int buttonPin = 6;
|
||||
|
||||
float heatIndex(float t, float rh) {
|
||||
// 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 { pm1p0, 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},
|
||||
{pm1p0, 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;
|
||||
unsigned long cleaning = 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);
|
||||
}
|
||||
|
||||
// "Parallel" execution for triggering cleaning in Sen44 after 2 days.
|
||||
if (millis() - parallel.cleaning >= 172800000) {
|
||||
parallel.cleaning = millis();
|
||||
sen44.startFanCleaning();
|
||||
}
|
||||
}
|
||||
|
||||
// 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 pm1p0:
|
||||
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;
|
||||
@@ -31,113 +267,3 @@ float heatIndex(float t, float rh) {
|
||||
}
|
||||
return hi;
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
uint16_t error;
|
||||
char errorMessage[256];
|
||||
|
||||
sen44.begin(Wire);
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
uint16_t error;
|
||||
char errorMessage[256];
|
||||
|
||||
delay(1000);
|
||||
|
||||
// Read Measurement
|
||||
uint16_t massConcentrationPm1p0;
|
||||
uint16_t massConcentrationPm2p5;
|
||||
uint16_t massConcentrationPm4p0;
|
||||
uint16_t massConcentrationPm10p0;
|
||||
float vocIndex;
|
||||
float ambientHumidity;
|
||||
float ambientTemperature;
|
||||
float feelsLikeTemperature;
|
||||
|
||||
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);
|
||||
|
||||
lcd.clear();
|
||||
|
||||
if (digitalRead(buttonPin) == LOW) {
|
||||
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);
|
||||
} else {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
BIN
aqi_monitor.webp
Normal file
BIN
aqi_monitor.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 87 KiB |
Reference in New Issue
Block a user