added CO2 and VOC sensor
This commit is contained in:
14
.vscode/c_cpp_properties.json
vendored
14
.vscode/c_cpp_properties.json
vendored
@@ -10,10 +10,15 @@
|
|||||||
"includePath": [
|
"includePath": [
|
||||||
"/Users/willemoldemans/Documents/PROJECTEN/AQS-main/Firmware/PM25SensorESP32/include",
|
"/Users/willemoldemans/Documents/PROJECTEN/AQS-main/Firmware/PM25SensorESP32/include",
|
||||||
"/Users/willemoldemans/Documents/PROJECTEN/AQS-main/Firmware/PM25SensorESP32",
|
"/Users/willemoldemans/Documents/PROJECTEN/AQS-main/Firmware/PM25SensorESP32",
|
||||||
|
"/Users/willemoldemans/Documents/PROJECTEN/AQS-main/Firmware/PM25SensorESP32/.pio/libdeps/esp32/Adafruit SGP30 Sensor",
|
||||||
|
"/Users/willemoldemans/Documents/PROJECTEN/AQS-main/Firmware/PM25SensorESP32/.pio/libdeps/esp32/Adafruit SCD30",
|
||||||
|
"/Users/willemoldemans/Documents/PROJECTEN/AQS-main/Firmware/PM25SensorESP32/.pio/libdeps/esp32/Adafruit BusIO",
|
||||||
|
"/Users/willemoldemans/.platformio/packages/framework-arduinoespressif32/libraries/Wire/src",
|
||||||
"/Users/willemoldemans/Documents/PROJECTEN/AQS-main/Firmware/PM25SensorESP32/.pio/libdeps/esp32/TJpg_Decoder/src",
|
"/Users/willemoldemans/Documents/PROJECTEN/AQS-main/Firmware/PM25SensorESP32/.pio/libdeps/esp32/TJpg_Decoder/src",
|
||||||
"/Users/willemoldemans/.platformio/packages/framework-arduinoespressif32/libraries/SD/src",
|
"/Users/willemoldemans/.platformio/packages/framework-arduinoespressif32/libraries/SD/src",
|
||||||
"/Users/willemoldemans/Documents/PROJECTEN/AQS-main/Firmware/PM25SensorESP32/.pio/libdeps/esp32/RunningMedian",
|
"/Users/willemoldemans/Documents/PROJECTEN/AQS-main/Firmware/PM25SensorESP32/.pio/libdeps/esp32/RunningMedian",
|
||||||
"/Users/willemoldemans/Documents/PROJECTEN/AQS-main/Firmware/PM25SensorESP32/.pio/libdeps/esp32/PubSubClient/src",
|
"/Users/willemoldemans/Documents/PROJECTEN/AQS-main/Firmware/PM25SensorESP32/.pio/libdeps/esp32/PubSubClient/src",
|
||||||
|
"/Users/willemoldemans/Documents/PROJECTEN/AQS-main/Firmware/PM25SensorESP32/.pio/libdeps/esp32/Adafruit Unified Sensor",
|
||||||
"/Users/willemoldemans/Documents/PROJECTEN/AQS-main/Firmware/PM25SensorESP32/.pio/libdeps/esp32/TFT_eSPI",
|
"/Users/willemoldemans/Documents/PROJECTEN/AQS-main/Firmware/PM25SensorESP32/.pio/libdeps/esp32/TFT_eSPI",
|
||||||
"/Users/willemoldemans/.platformio/packages/framework-arduinoespressif32/libraries/SPI/src",
|
"/Users/willemoldemans/.platformio/packages/framework-arduinoespressif32/libraries/SPI/src",
|
||||||
"/Users/willemoldemans/.platformio/packages/framework-arduinoespressif32/libraries/SPIFFS/src",
|
"/Users/willemoldemans/.platformio/packages/framework-arduinoespressif32/libraries/SPIFFS/src",
|
||||||
@@ -82,6 +87,7 @@
|
|||||||
"/Users/willemoldemans/.platformio/packages/framework-arduinoespressif32/tools/sdk/include/fb_gfx",
|
"/Users/willemoldemans/.platformio/packages/framework-arduinoespressif32/tools/sdk/include/fb_gfx",
|
||||||
"/Users/willemoldemans/.platformio/packages/framework-arduinoespressif32/cores/esp32",
|
"/Users/willemoldemans/.platformio/packages/framework-arduinoespressif32/cores/esp32",
|
||||||
"/Users/willemoldemans/.platformio/packages/framework-arduinoespressif32/variants/esp32",
|
"/Users/willemoldemans/.platformio/packages/framework-arduinoespressif32/variants/esp32",
|
||||||
|
"/Users/willemoldemans/Documents/PROJECTEN/AQS-main/Firmware/PM25SensorESP32/.pio/libdeps/esp32/Adafruit GFX Library",
|
||||||
"/Users/willemoldemans/.platformio/packages/framework-arduinoespressif32/libraries/ArduinoOTA/src",
|
"/Users/willemoldemans/.platformio/packages/framework-arduinoespressif32/libraries/ArduinoOTA/src",
|
||||||
"/Users/willemoldemans/.platformio/packages/framework-arduinoespressif32/libraries/AsyncUDP/src",
|
"/Users/willemoldemans/.platformio/packages/framework-arduinoespressif32/libraries/AsyncUDP/src",
|
||||||
"/Users/willemoldemans/.platformio/packages/framework-arduinoespressif32/libraries/AzureIoT/src",
|
"/Users/willemoldemans/.platformio/packages/framework-arduinoespressif32/libraries/AzureIoT/src",
|
||||||
@@ -104,7 +110,6 @@
|
|||||||
"/Users/willemoldemans/.platformio/packages/framework-arduinoespressif32/libraries/WebServer/src",
|
"/Users/willemoldemans/.platformio/packages/framework-arduinoespressif32/libraries/WebServer/src",
|
||||||
"/Users/willemoldemans/.platformio/packages/framework-arduinoespressif32/libraries/WiFiClientSecure/src",
|
"/Users/willemoldemans/.platformio/packages/framework-arduinoespressif32/libraries/WiFiClientSecure/src",
|
||||||
"/Users/willemoldemans/.platformio/packages/framework-arduinoespressif32/libraries/WiFiProv/src",
|
"/Users/willemoldemans/.platformio/packages/framework-arduinoespressif32/libraries/WiFiProv/src",
|
||||||
"/Users/willemoldemans/.platformio/packages/framework-arduinoespressif32/libraries/Wire/src",
|
|
||||||
"/Users/willemoldemans/.platformio/packages/tool-unity",
|
"/Users/willemoldemans/.platformio/packages/tool-unity",
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
@@ -113,10 +118,15 @@
|
|||||||
"path": [
|
"path": [
|
||||||
"/Users/willemoldemans/Documents/PROJECTEN/AQS-main/Firmware/PM25SensorESP32/include",
|
"/Users/willemoldemans/Documents/PROJECTEN/AQS-main/Firmware/PM25SensorESP32/include",
|
||||||
"/Users/willemoldemans/Documents/PROJECTEN/AQS-main/Firmware/PM25SensorESP32",
|
"/Users/willemoldemans/Documents/PROJECTEN/AQS-main/Firmware/PM25SensorESP32",
|
||||||
|
"/Users/willemoldemans/Documents/PROJECTEN/AQS-main/Firmware/PM25SensorESP32/.pio/libdeps/esp32/Adafruit SGP30 Sensor",
|
||||||
|
"/Users/willemoldemans/Documents/PROJECTEN/AQS-main/Firmware/PM25SensorESP32/.pio/libdeps/esp32/Adafruit SCD30",
|
||||||
|
"/Users/willemoldemans/Documents/PROJECTEN/AQS-main/Firmware/PM25SensorESP32/.pio/libdeps/esp32/Adafruit BusIO",
|
||||||
|
"/Users/willemoldemans/.platformio/packages/framework-arduinoespressif32/libraries/Wire/src",
|
||||||
"/Users/willemoldemans/Documents/PROJECTEN/AQS-main/Firmware/PM25SensorESP32/.pio/libdeps/esp32/TJpg_Decoder/src",
|
"/Users/willemoldemans/Documents/PROJECTEN/AQS-main/Firmware/PM25SensorESP32/.pio/libdeps/esp32/TJpg_Decoder/src",
|
||||||
"/Users/willemoldemans/.platformio/packages/framework-arduinoespressif32/libraries/SD/src",
|
"/Users/willemoldemans/.platformio/packages/framework-arduinoespressif32/libraries/SD/src",
|
||||||
"/Users/willemoldemans/Documents/PROJECTEN/AQS-main/Firmware/PM25SensorESP32/.pio/libdeps/esp32/RunningMedian",
|
"/Users/willemoldemans/Documents/PROJECTEN/AQS-main/Firmware/PM25SensorESP32/.pio/libdeps/esp32/RunningMedian",
|
||||||
"/Users/willemoldemans/Documents/PROJECTEN/AQS-main/Firmware/PM25SensorESP32/.pio/libdeps/esp32/PubSubClient/src",
|
"/Users/willemoldemans/Documents/PROJECTEN/AQS-main/Firmware/PM25SensorESP32/.pio/libdeps/esp32/PubSubClient/src",
|
||||||
|
"/Users/willemoldemans/Documents/PROJECTEN/AQS-main/Firmware/PM25SensorESP32/.pio/libdeps/esp32/Adafruit Unified Sensor",
|
||||||
"/Users/willemoldemans/Documents/PROJECTEN/AQS-main/Firmware/PM25SensorESP32/.pio/libdeps/esp32/TFT_eSPI",
|
"/Users/willemoldemans/Documents/PROJECTEN/AQS-main/Firmware/PM25SensorESP32/.pio/libdeps/esp32/TFT_eSPI",
|
||||||
"/Users/willemoldemans/.platformio/packages/framework-arduinoespressif32/libraries/SPI/src",
|
"/Users/willemoldemans/.platformio/packages/framework-arduinoespressif32/libraries/SPI/src",
|
||||||
"/Users/willemoldemans/.platformio/packages/framework-arduinoespressif32/libraries/SPIFFS/src",
|
"/Users/willemoldemans/.platformio/packages/framework-arduinoespressif32/libraries/SPIFFS/src",
|
||||||
@@ -185,6 +195,7 @@
|
|||||||
"/Users/willemoldemans/.platformio/packages/framework-arduinoespressif32/tools/sdk/include/fb_gfx",
|
"/Users/willemoldemans/.platformio/packages/framework-arduinoespressif32/tools/sdk/include/fb_gfx",
|
||||||
"/Users/willemoldemans/.platformio/packages/framework-arduinoespressif32/cores/esp32",
|
"/Users/willemoldemans/.platformio/packages/framework-arduinoespressif32/cores/esp32",
|
||||||
"/Users/willemoldemans/.platformio/packages/framework-arduinoespressif32/variants/esp32",
|
"/Users/willemoldemans/.platformio/packages/framework-arduinoespressif32/variants/esp32",
|
||||||
|
"/Users/willemoldemans/Documents/PROJECTEN/AQS-main/Firmware/PM25SensorESP32/.pio/libdeps/esp32/Adafruit GFX Library",
|
||||||
"/Users/willemoldemans/.platformio/packages/framework-arduinoespressif32/libraries/ArduinoOTA/src",
|
"/Users/willemoldemans/.platformio/packages/framework-arduinoespressif32/libraries/ArduinoOTA/src",
|
||||||
"/Users/willemoldemans/.platformio/packages/framework-arduinoespressif32/libraries/AsyncUDP/src",
|
"/Users/willemoldemans/.platformio/packages/framework-arduinoespressif32/libraries/AsyncUDP/src",
|
||||||
"/Users/willemoldemans/.platformio/packages/framework-arduinoespressif32/libraries/AzureIoT/src",
|
"/Users/willemoldemans/.platformio/packages/framework-arduinoespressif32/libraries/AzureIoT/src",
|
||||||
@@ -207,7 +218,6 @@
|
|||||||
"/Users/willemoldemans/.platformio/packages/framework-arduinoespressif32/libraries/WebServer/src",
|
"/Users/willemoldemans/.platformio/packages/framework-arduinoespressif32/libraries/WebServer/src",
|
||||||
"/Users/willemoldemans/.platformio/packages/framework-arduinoespressif32/libraries/WiFiClientSecure/src",
|
"/Users/willemoldemans/.platformio/packages/framework-arduinoespressif32/libraries/WiFiClientSecure/src",
|
||||||
"/Users/willemoldemans/.platformio/packages/framework-arduinoespressif32/libraries/WiFiProv/src",
|
"/Users/willemoldemans/.platformio/packages/framework-arduinoespressif32/libraries/WiFiProv/src",
|
||||||
"/Users/willemoldemans/.platformio/packages/framework-arduinoespressif32/libraries/Wire/src",
|
|
||||||
"/Users/willemoldemans/.platformio/packages/tool-unity",
|
"/Users/willemoldemans/.platformio/packages/tool-unity",
|
||||||
""
|
""
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -3,36 +3,53 @@
|
|||||||
Adafruit_SCD30 scd30;
|
Adafruit_SCD30 scd30;
|
||||||
|
|
||||||
#define CO2_INTERVAL 120000 //ms
|
#define CO2_INTERVAL 120000 //ms
|
||||||
|
#define SCD30TEMPMAX 50
|
||||||
|
#define SDC30HUMMAX 100
|
||||||
|
#define SCD30CO2MAX 20000
|
||||||
|
#define SCD30MIN 0
|
||||||
|
|
||||||
uint8_t CO2_samples = 3600 / g_pms_report_period;
|
uint8_t CO2_samples = 3600 / g_pms_report_period;
|
||||||
|
|
||||||
AQSSensor SCD30_temperature("Temperature", device_name, CO2_samples);
|
AQSSensor SCD30_temperature("Temperature", SCD30_temp, "degC", device_name, CO2_samples, SCD30MIN, SCD30TEMPMAX);
|
||||||
AQSSensor SCD30_Humidity("Humidity", device_name, CO2_samples);
|
AQSSensor SCD30_Humidity("Humidity", SCD30_hum, "%RH", device_name, CO2_samples, SCD30MIN, SDC30HUMMAX);
|
||||||
AQSSensor SCD30_CO2("CO2", device_name, CO2_samples);
|
AQSSensor SCD30_CO2("CO2", SCD30_co2, "PPM", device_name, CO2_samples, SCD30MIN, SCD30CO2MAX);
|
||||||
|
|
||||||
uint32_t CO2_lastUpdate = 0;
|
uint32_t CO2_lastUpdate = 0;
|
||||||
|
|
||||||
void initCO2sensor(void)
|
void initCO2sensor(void)
|
||||||
{
|
{
|
||||||
Serial.print("Init SCD30:");
|
Serial.println("SCD30: init");
|
||||||
if (!scd30.begin(true))
|
if (!scd30.begin())
|
||||||
{
|
{
|
||||||
Serial.println("Failed to find SCD30 device");
|
Serial.println("SCD30: Failed to find sensor");
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//configure sensor
|
||||||
|
scd30.setMeasurementInterval(g_pms_report_period);
|
||||||
|
|
||||||
addSensorToList(&SCD30_temperature);
|
addSensorToList(&SCD30_temperature);
|
||||||
addSensorToList(&SCD30_Humidity);
|
addSensorToList(&SCD30_Humidity);
|
||||||
addSensorToList(&SCD30_CO2);
|
addSensorToList(&SCD30_CO2);
|
||||||
|
|
||||||
Serial.println(" OK");
|
//check callibration Mode
|
||||||
|
scd30.selfCalibrationEnabled(false);
|
||||||
|
scd30.read();
|
||||||
|
Serial.println("SCD30: init OK");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleCO2sensor(void)
|
void handleCO2sensor(void)
|
||||||
{
|
{
|
||||||
uint32_t currentmillis = millis();
|
uint32_t currentmillis = millis();
|
||||||
|
if ((currentmillis - CO2_lastUpdate > (g_pms_report_period * 1000)) || (!CO2_lastUpdate) && (scd30.dataReady()))
|
||||||
if (currentmillis - CO2_lastUpdate > CO2_INTERVAL)
|
|
||||||
{
|
{
|
||||||
|
if (!scd30.read())
|
||||||
|
{
|
||||||
|
Serial.println("SCD30: read error!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
SCD30_temperature.set(uint32_t(scd30.temperature));
|
SCD30_temperature.set(uint32_t(scd30.temperature));
|
||||||
SCD30_Humidity.set(uint32_t(scd30.relative_humidity));
|
SCD30_Humidity.set(uint32_t(scd30.relative_humidity));
|
||||||
SCD30_CO2.set(uint32_t(scd30.CO2));
|
SCD30_CO2.set(uint32_t(scd30.CO2));
|
||||||
@@ -40,5 +57,7 @@ void handleCO2sensor(void)
|
|||||||
SCD30_temperature.publish();
|
SCD30_temperature.publish();
|
||||||
SCD30_Humidity.publish();
|
SCD30_Humidity.publish();
|
||||||
SCD30_CO2.publish();
|
SCD30_CO2.publish();
|
||||||
|
|
||||||
|
CO2_lastUpdate = currentmillis;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
69
VOC_sensor.cpp
Normal file
69
VOC_sensor.cpp
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
#include "VOC_sensor.h"
|
||||||
|
|
||||||
|
Adafruit_SGP30 sgp;
|
||||||
|
uint8_t VOC_samples = 3600 / g_pms_report_period;
|
||||||
|
#define VOC_MIN 0
|
||||||
|
#define VOC_MAX 10000
|
||||||
|
#define VOC_INTERVAL 120000 //ms
|
||||||
|
|
||||||
|
uint32_t lastVOCtime = 0;
|
||||||
|
//sensors
|
||||||
|
AQSSensor SGP30_tvoc("TVOC", SGP30_TVOC, "ppb", device_name, VOC_samples, VOC_MIN, VOC_MAX);
|
||||||
|
AQSSensor SGP30_eco2("eCO2", SGP30_eCO2, "ppm", device_name, VOC_samples, VOC_MIN, VOC_MAX);
|
||||||
|
AQSSensor SGP30_rawh2("Raw_H2", SGP30_rawH2, "#", device_name, VOC_samples, VOC_MIN, VOC_MAX);
|
||||||
|
AQSSensor SGP30_rawethanol("Raw_Ethanol", SGP30_rawEthanol, "#", device_name, VOC_samples, VOC_MIN, VOC_MAX);
|
||||||
|
|
||||||
|
void initVOCsensor(void)
|
||||||
|
{
|
||||||
|
Serial.println("VOCSensor: Init SGP30: ");
|
||||||
|
|
||||||
|
if (!sgp.begin())
|
||||||
|
{
|
||||||
|
Serial.println("VOCSensor: Init Failed (SGP30 not found)");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Serial.printf("VOCSensor: serial =0x%X%X%X\n", sgp.serialnumber[0], sgp.serialnumber[1], sgp.serialnumber[2]);
|
||||||
|
addSensorToList(&SGP30_tvoc);
|
||||||
|
addSensorToList(&SGP30_eco2);
|
||||||
|
addSensorToList(&SGP30_rawh2);
|
||||||
|
addSensorToList(&SGP30_rawethanol);
|
||||||
|
|
||||||
|
sgp.IAQmeasure();
|
||||||
|
sgp.IAQmeasureRaw();
|
||||||
|
|
||||||
|
Serial.println("VOCSensor: Init OK");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleVOCsensor(void)
|
||||||
|
{
|
||||||
|
uint32_t timenow = millis();
|
||||||
|
if ((timenow - lastVOCtime > VOC_INTERVAL) || !lastVOCtime)
|
||||||
|
{
|
||||||
|
if (!sgp.IAQmeasure())
|
||||||
|
{
|
||||||
|
Serial.println("VOCSensor: Measurement failed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SGP30_tvoc.set(sgp.TVOC);
|
||||||
|
SGP30_eco2.set(sgp.eCO2);
|
||||||
|
|
||||||
|
SGP30_tvoc.publish();
|
||||||
|
SGP30_eco2.publish();
|
||||||
|
|
||||||
|
if (!sgp.IAQmeasureRaw())
|
||||||
|
{
|
||||||
|
Serial.println("VOCSensor: Raw Measurement failed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SGP30_rawethanol.set(sgp.rawEthanol);
|
||||||
|
SGP30_rawh2.set(sgp.rawH2);
|
||||||
|
|
||||||
|
SGP30_rawethanol.publish();
|
||||||
|
SGP30_rawh2.publish();
|
||||||
|
|
||||||
|
lastVOCtime = timenow;
|
||||||
|
}
|
||||||
|
}
|
||||||
11
VOC_sensor.h
Normal file
11
VOC_sensor.h
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Arduino.h"
|
||||||
|
#include "config.h"
|
||||||
|
#include "sensor.h"
|
||||||
|
|
||||||
|
#include "Adafruit_SGP30.h"
|
||||||
|
|
||||||
|
|
||||||
|
void initVOCsensor(void);
|
||||||
|
void handleVOCsensor(void);
|
||||||
110
lcd.cpp
110
lcd.cpp
@@ -36,6 +36,8 @@
|
|||||||
TFT_eSPI tft = TFT_eSPI();
|
TFT_eSPI tft = TFT_eSPI();
|
||||||
TFT_eSprite needle = TFT_eSprite(&tft); // Sprite object for needle
|
TFT_eSprite needle = TFT_eSprite(&tft); // Sprite object for needle
|
||||||
TFT_eSprite spr = TFT_eSprite(&tft); // Sprite for meter reading
|
TFT_eSprite spr = TFT_eSprite(&tft); // Sprite for meter reading
|
||||||
|
TFT_eSprite nameSpr = TFT_eSprite(&tft);
|
||||||
|
TFT_eSprite unitSpr = TFT_eSprite(&tft);
|
||||||
|
|
||||||
// Jpeg image array attached to this sketch
|
// Jpeg image array attached to this sketch
|
||||||
#include "dial.h"
|
#include "dial.h"
|
||||||
@@ -46,6 +48,7 @@ TFT_eSprite spr = TFT_eSprite(&tft); // Sprite for meter reading
|
|||||||
uint16_t *tft_buffer;
|
uint16_t *tft_buffer;
|
||||||
bool buffer_loaded = false;
|
bool buffer_loaded = false;
|
||||||
uint16_t spr_width = 0;
|
uint16_t spr_width = 0;
|
||||||
|
uint16_t name_spr_width = 0;
|
||||||
|
|
||||||
void createNeedle(void);
|
void createNeedle(void);
|
||||||
void plotNeedle(int16_t angle, uint16_t ms_delay);
|
void plotNeedle(int16_t angle, uint16_t ms_delay);
|
||||||
@@ -99,9 +102,16 @@ void initLCD()
|
|||||||
spr.pushSprite(DIAL_CENTRE_X - spr_width / 2, DIAL_CENTRE_Y - spr.fontHeight() / 2);
|
spr.pushSprite(DIAL_CENTRE_X - spr_width / 2, DIAL_CENTRE_Y - spr.fontHeight() / 2);
|
||||||
|
|
||||||
// Plot the label text
|
// Plot the label text
|
||||||
tft.setTextColor(TFT_WHITE, bg_color);
|
nameSpr.setTextFont(3);
|
||||||
tft.setTextDatum(MC_DATUM);
|
name_spr_width = nameSpr.textWidth("Temperature");
|
||||||
tft.drawString("(PM2.5 ug/m3)", DIAL_CENTRE_X, DIAL_CENTRE_Y + 48, 2);
|
nameSpr.createSprite(name_spr_width, nameSpr.fontHeight() * 2 + 2);
|
||||||
|
nameSpr.fillSprite(bg_color);
|
||||||
|
nameSpr.setTextColor(TFT_WHITE, bg_color);
|
||||||
|
nameSpr.setTextDatum(MC_DATUM);
|
||||||
|
nameSpr.setTextPadding(name_spr_width);
|
||||||
|
nameSpr.drawString("Sensor Name", name_spr_width / 2, nameSpr.fontHeight() / 2);
|
||||||
|
nameSpr.drawString("Unit", name_spr_width / 2, nameSpr.fontHeight() / 2 * 3 + 2); nameSpr.pushSprite(DIAL_CENTRE_X - name_spr_width / 2, DIAL_CENTRE_Y + 40, 2);
|
||||||
|
nameSpr.pushSprite(DIAL_CENTRE_X - name_spr_width / 2, DIAL_CENTRE_Y + 40, 2);
|
||||||
|
|
||||||
// Define where the needle pivot point is on the TFT before
|
// Define where the needle pivot point is on the TFT before
|
||||||
// creating the needle so boundary calculation is correct
|
// creating the needle so boundary calculation is correct
|
||||||
@@ -120,11 +130,101 @@ void initLCD()
|
|||||||
// =======================================================================================
|
// =======================================================================================
|
||||||
// Loop
|
// Loop
|
||||||
// =======================================================================================
|
// =======================================================================================
|
||||||
|
enum DISPLAY_STATE
|
||||||
|
{
|
||||||
|
DISPLAY_PM1P0,
|
||||||
|
DISPLAY_PM2P5,
|
||||||
|
DISPLAY_PM10P0,
|
||||||
|
DISPLAY_TEMP,
|
||||||
|
DISPLAY_HUM,
|
||||||
|
DISPLAY_CO2,
|
||||||
|
DISPLAY_LAST
|
||||||
|
};
|
||||||
|
|
||||||
|
DISPLAY_STATE displaystate = DISPLAY_PM1P0;
|
||||||
|
uint32_t display_last_update = 0;
|
||||||
|
|
||||||
|
#define MAXGUAGE 240
|
||||||
|
#define MINGUAGE 0
|
||||||
|
#define DISPLAY_ROTATE 10 //sec
|
||||||
|
|
||||||
void handleLCD()
|
void handleLCD()
|
||||||
{
|
{
|
||||||
|
static uint32_t value;
|
||||||
|
static uint16_t angle;
|
||||||
|
DISPLAY_STATE displaystate_next = DISPLAY_PM1P0;
|
||||||
|
sensor_e nextSensor = AE_1P0;
|
||||||
|
|
||||||
|
switch (displaystate)
|
||||||
|
{
|
||||||
|
case DISPLAY_PM1P0:
|
||||||
|
{
|
||||||
|
nextSensor = AE_1P0;
|
||||||
|
displaystate_next = DISPLAY_PM2P5;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case DISPLAY_PM2P5:
|
||||||
|
{
|
||||||
|
nextSensor = AE_2P5;
|
||||||
|
displaystate_next = DISPLAY_PM10P0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case DISPLAY_PM10P0:
|
||||||
|
{
|
||||||
|
nextSensor = AE_10P0;
|
||||||
|
displaystate_next = DISPLAY_TEMP;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case DISPLAY_TEMP:
|
||||||
|
{
|
||||||
|
nextSensor = SCD30_temp;
|
||||||
|
displaystate_next = DISPLAY_HUM;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case DISPLAY_HUM:
|
||||||
|
{
|
||||||
|
nextSensor = SCD30_hum;
|
||||||
|
displaystate_next = DISPLAY_CO2;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case DISPLAY_CO2:
|
||||||
|
{
|
||||||
|
nextSensor = SCD30_hum;
|
||||||
|
displaystate_next = DISPLAY_PM1P0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DISPLAY_LAST:
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
displaystate_next = DISPLAY_PM1P0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
//rotate display
|
||||||
|
uint32_t timenow = millis();
|
||||||
|
if (timenow - display_last_update > (DISPLAY_ROTATE * 1000))
|
||||||
|
{
|
||||||
|
displaystate = displaystate_next;
|
||||||
|
display_last_update = timenow;
|
||||||
|
Serial.println("LCD next State");
|
||||||
|
}
|
||||||
//calculate value to angle
|
//calculate value to angle
|
||||||
uint16_t angle = map(getLCDvalue(),0,50,0,240);
|
|
||||||
plotNeedle(angle,15, getLCDvalue());
|
AQSSensor *sensor = getSensor(nextSensor);
|
||||||
|
if (sensor == NULL)
|
||||||
|
{
|
||||||
|
Serial.println("LCD: getSensor=NULL!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
angle = map(value, sensor->getMin(), sensor->getMax(), MINGUAGE, MAXGUAGE);
|
||||||
|
|
||||||
|
plotNeedle(angle, 15, value);
|
||||||
|
|
||||||
|
nameSpr.drawString(sensor->getName().c_str(), name_spr_width / 2, nameSpr.fontHeight() / 2);
|
||||||
|
nameSpr.drawString(sensor->getUnit().c_str(), name_spr_width / 2, nameSpr.fontHeight() / 2 * 3 + 2);
|
||||||
|
nameSpr.pushSprite(DIAL_CENTRE_X - name_spr_width / 2, DIAL_CENTRE_Y + 40, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
// =======================================================================================
|
// =======================================================================================
|
||||||
|
|||||||
2
lcd.h
2
lcd.h
@@ -1,7 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "Arduino.h"
|
#include "Arduino.h"
|
||||||
#include "particles.h"
|
#include "sensor.h"
|
||||||
|
|
||||||
void initLCD();
|
void initLCD();
|
||||||
void handleLCD();
|
void handleLCD();
|
||||||
|
|||||||
11
main.cpp
11
main.cpp
@@ -36,9 +36,10 @@
|
|||||||
/*--------------------------- Modules ------------------------------------*/
|
/*--------------------------- Modules ------------------------------------*/
|
||||||
#include "lcd.h"
|
#include "lcd.h"
|
||||||
#include "mqtt.h"
|
#include "mqtt.h"
|
||||||
|
#include "sensor.h"
|
||||||
#include "particles.h"
|
#include "particles.h"
|
||||||
#include "CO2_sensor.h"
|
#include "CO2_sensor.h"
|
||||||
#include "sensor.h"
|
#include "VOC_sensor.h"
|
||||||
#include "button.h"
|
#include "button.h"
|
||||||
|
|
||||||
/*--------------------------- Program ---------------------------------------*/
|
/*--------------------------- Program ---------------------------------------*/
|
||||||
@@ -52,12 +53,15 @@ void setup()
|
|||||||
Serial.print("Air Quality Sensor starting up, v");
|
Serial.print("Air Quality Sensor starting up, v");
|
||||||
Serial.println(VERSION);
|
Serial.println(VERSION);
|
||||||
|
|
||||||
|
digitalWrite(5, false);
|
||||||
|
|
||||||
initWifi();
|
initWifi();
|
||||||
initMQTT();
|
initMQTT();
|
||||||
initSensor();
|
initSensor();
|
||||||
initParticles();
|
initParticles();
|
||||||
initCO2sensor();
|
initCO2sensor();
|
||||||
initLCD();
|
initVOCsensor();
|
||||||
|
//initLCD();
|
||||||
initButtons();
|
initButtons();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,8 +72,9 @@ void loop()
|
|||||||
{
|
{
|
||||||
handleParticles();
|
handleParticles();
|
||||||
handleCO2sensor();
|
handleCO2sensor();
|
||||||
|
handleVOCsensor();
|
||||||
handleButtons();
|
handleButtons();
|
||||||
handleSensor();
|
handleSensor();
|
||||||
handleLCD();
|
//handleLCD();
|
||||||
handleMQTT();
|
handleMQTT();
|
||||||
}
|
}
|
||||||
|
|||||||
5
mqtt.cpp
5
mqtt.cpp
@@ -137,6 +137,11 @@ void handleMQTT(void)
|
|||||||
if (!client.connected())
|
if (!client.connected())
|
||||||
{
|
{
|
||||||
reconnectMqtt();
|
reconnectMqtt();
|
||||||
|
if(!client.connected())
|
||||||
|
{
|
||||||
|
Serial.print("OK");
|
||||||
|
}
|
||||||
|
Serial.println("");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
client.loop(); // Process any outstanding MQTT messages
|
client.loop(); // Process any outstanding MQTT messages
|
||||||
|
|||||||
@@ -10,6 +10,9 @@ char g_command_topic[50]; // MQTT topic for receiving commands
|
|||||||
#define PMS_STATE_ASLEEP 0 // Low power mode, laser and fan off
|
#define PMS_STATE_ASLEEP 0 // Low power mode, laser and fan off
|
||||||
#define PMS_STATE_WAKING_UP 1 // Laser and fan on, not ready yet
|
#define PMS_STATE_WAKING_UP 1 // Laser and fan on, not ready yet
|
||||||
#define PMS_STATE_READY 2 // Warmed up, ready to give data
|
#define PMS_STATE_READY 2 // Warmed up, ready to give data
|
||||||
|
#define PMSMAX 50
|
||||||
|
#define PSMMIN 0
|
||||||
|
#define PPDMAX 2000
|
||||||
uint8_t g_pms_state = PMS_STATE_WAKING_UP;
|
uint8_t g_pms_state = PMS_STATE_WAKING_UP;
|
||||||
|
|
||||||
uint32_t g_pms_state_start = 0; // Timestamp when PMS state last changed
|
uint32_t g_pms_state_start = 0; // Timestamp when PMS state last changed
|
||||||
@@ -18,20 +21,19 @@ uint8_t g_pms_ppd_readings_taken = 0; // 0/1: whether PPD readings have been tak
|
|||||||
|
|
||||||
uint8_t samples = 3600 / g_pms_report_period;
|
uint8_t samples = 3600 / g_pms_report_period;
|
||||||
|
|
||||||
AQSSensor g_pm1p0_sp("SP_1P0", device_name, samples);
|
// name enum unit device name samples/h, min, max
|
||||||
AQSSensor g_pm2p5_sp("SP_2P5", device_name, samples);
|
AQSSensor g_pm1p0_sp("SP_1P0", SP_1P0, "", device_name, samples, PSMMIN, PMSMAX);
|
||||||
AQSSensor g_pm10p0_sp("SP_10P0", device_name, samples);
|
AQSSensor g_pm2p5_sp("SP_2P5", SP_2P5, "", device_name, samples, PSMMIN, PMSMAX);
|
||||||
|
AQSSensor g_pm10p0_sp("SP_10P0", SP_10P0, "", device_name, samples, PSMMIN, PMSMAX);
|
||||||
AQSSensor g_pm1p0_ae("AE_1P0", device_name, samples);
|
AQSSensor g_pm1p0_ae("AE_1P0", AE_1P0, "ug/m3", device_name, samples, PSMMIN, PMSMAX);
|
||||||
AQSSensor g_pm2p5_ae("AE_2P5", device_name, samples);
|
AQSSensor g_pm2p5_ae("AE_2P5", AE_2P5, "ug/m3", device_name, samples, PSMMIN, PMSMAX);
|
||||||
AQSSensor g_pm10p0_ae("AE_10P0", device_name, samples);
|
AQSSensor g_pm10p0_ae("AE_10P0", AE_10P0, "ug/m3", device_name, samples, PSMMIN, PMSMAX);
|
||||||
|
AQSSensor g_pm0p3_ppd("PPD_0P3", PPD_0P3, "", device_name, samples, PSMMIN, PPDMAX);
|
||||||
AQSSensor g_pm0p3_ppd("PPD_0P3", device_name, samples);
|
AQSSensor g_pm0p5_ppd("PPD_0P5", PPD_0P5, "", device_name, samples, PSMMIN, PPDMAX);
|
||||||
AQSSensor g_pm0p5_ppd("PPD_0P5", device_name, samples);
|
AQSSensor g_pm1p0_ppd("PPD_1P0", PPD_1P0, "", device_name, samples, PSMMIN, PPDMAX);
|
||||||
AQSSensor g_pm1p0_ppd("PPD_1P0", device_name, samples);
|
AQSSensor g_pm2p5_ppd("PPD_2P5", PPD_2P5, "", device_name, samples, PSMMIN, PPDMAX);
|
||||||
AQSSensor g_pm2p5_ppd("PPD_2P5", device_name, samples);
|
AQSSensor g_pm5p0_ppd("PPD_5P0", PPD_5P0, "", device_name, samples, PSMMIN, PPDMAX);
|
||||||
AQSSensor g_pm5p0_ppd("PPD_5P0", device_name, samples);
|
AQSSensor g_pm10p0_ppd("PPD_10P0",PPD_10P0, "", device_name, samples, PSMMIN, PPDMAX);
|
||||||
AQSSensor g_pm10p0_ppd("PPD_10P0", device_name, samples);
|
|
||||||
|
|
||||||
void PMS_publishSensors(void );
|
void PMS_publishSensors(void );
|
||||||
|
|
||||||
@@ -62,7 +64,6 @@ uint32_t getLCDvalue(void)
|
|||||||
|
|
||||||
void PMS_publishSensors(void )
|
void PMS_publishSensors(void )
|
||||||
{
|
{
|
||||||
String message_string;
|
|
||||||
|
|
||||||
/* Report PM1.0 AE value */
|
/* Report PM1.0 AE value */
|
||||||
g_pm1p0_ae.publish();
|
g_pm1p0_ae.publish();
|
||||||
@@ -87,7 +88,7 @@ void PMS_publishSensors(void )
|
|||||||
|
|
||||||
void initParticles( void )
|
void initParticles( void )
|
||||||
{
|
{
|
||||||
Serial.print("initPMS:");
|
Serial.println("PMS: Init");
|
||||||
// Open a connection to the PMS and put it into passive mode
|
// Open a connection to the PMS and put it into passive mode
|
||||||
Serial2.begin(PMS_BAUD_RATE, SERIAL_8N1, PMS_RX_PIN, PMS_TX_PIN); // Connection for PMS5003
|
Serial2.begin(PMS_BAUD_RATE, SERIAL_8N1, PMS_RX_PIN, PMS_TX_PIN); // Connection for PMS5003
|
||||||
pms.activeMode(); // Tell PMS to stop sending data automatically
|
pms.activeMode(); // Tell PMS to stop sending data automatically
|
||||||
@@ -98,7 +99,7 @@ void initParticles( void )
|
|||||||
|
|
||||||
PMS_AddSensors();
|
PMS_AddSensors();
|
||||||
|
|
||||||
Serial.println(" OK");
|
Serial.println("PMS: Init OK");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,7 +113,7 @@ void handleParticles( void )
|
|||||||
if (time_now - g_pms_state_start >= ((g_pms_report_period * 1000) - (g_pms_warmup_period * 1000)))
|
if (time_now - g_pms_state_start >= ((g_pms_report_period * 1000) - (g_pms_warmup_period * 1000)))
|
||||||
{
|
{
|
||||||
// It's time to wake up the sensor
|
// It's time to wake up the sensor
|
||||||
Serial.println("Waking up sensor");
|
Serial.println("PMS: Waking up sensor");
|
||||||
pms.wakeUp();
|
pms.wakeUp();
|
||||||
g_pms_state_start = time_now;
|
g_pms_state_start = time_now;
|
||||||
g_pms_state = PMS_STATE_WAKING_UP;
|
g_pms_state = PMS_STATE_WAKING_UP;
|
||||||
@@ -170,4 +171,5 @@ void handleParticles( void )
|
|||||||
g_pms_state = PMS_STATE_ASLEEP;
|
g_pms_state = PMS_STATE_ASLEEP;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ lib_deps =
|
|||||||
robtillaart/RunningMedian
|
robtillaart/RunningMedian
|
||||||
bodmer/Tjpg_decoder
|
bodmer/Tjpg_decoder
|
||||||
adafruit/Adafruit SCD30
|
adafruit/Adafruit SCD30
|
||||||
adafruit/Adafruit BusIO
|
adafruit/Adafruit BusIO
|
||||||
|
|
||||||
[env:esp32]
|
[env:esp32]
|
||||||
platform = espressif32
|
platform = espressif32
|
||||||
@@ -32,4 +32,4 @@ monitor_speed = 115200
|
|||||||
lib_deps =
|
lib_deps =
|
||||||
${common.lib_deps_builtin}
|
${common.lib_deps_builtin}
|
||||||
${common.lib_deps}
|
${common.lib_deps}
|
||||||
|
adafruit/Adafruit SGP30 Sensor@^2.0.0
|
||||||
|
|||||||
33
sensor.cpp
33
sensor.cpp
@@ -1,6 +1,5 @@
|
|||||||
#include "sensor.h"
|
#include "sensor.h"
|
||||||
|
|
||||||
|
|
||||||
std::vector<AQSSensor*> sensorList;
|
std::vector<AQSSensor*> sensorList;
|
||||||
|
|
||||||
void publishAll(void);
|
void publishAll(void);
|
||||||
@@ -17,7 +16,7 @@ void reportToSerial(void)
|
|||||||
void addSensorToList(AQSSensor* sensor)
|
void addSensorToList(AQSSensor* sensor)
|
||||||
{
|
{
|
||||||
sensorList.push_back(sensor);
|
sensorList.push_back(sensor);
|
||||||
Serial.printf("AQSSensor: added %s", sensor->getName().c_str());
|
Serial.printf("sensor: added %s\n", sensor->getName().c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -27,16 +26,40 @@ void publishAll(void)
|
|||||||
{
|
{
|
||||||
sensor->print();
|
sensor->print();
|
||||||
sensor->publishMQTT();
|
sensor->publishMQTT();
|
||||||
|
sensor->published();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void initSensor(void)
|
void initSensor(void)
|
||||||
{
|
{
|
||||||
Serial.print("InitSensor:");
|
Serial.println("Sensor: Init");
|
||||||
Serial.println(" OK");
|
Serial.println("Sensor: Init OK");
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleSensor(void)
|
void handleSensor(void)
|
||||||
{
|
{
|
||||||
publishAll();
|
publishAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AQSSensor* getSensor(sensor_e sensor)
|
||||||
|
{
|
||||||
|
for(auto && thissensor : sensorList)
|
||||||
|
{
|
||||||
|
if(thissensor->getSensor() == sensor)
|
||||||
|
{
|
||||||
|
return thissensor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t getSensorValue(sensor_e sensor)
|
||||||
|
{
|
||||||
|
AQSSensor *thissensor = getSensor(sensor);
|
||||||
|
if(thissensor == NULL)
|
||||||
|
{
|
||||||
|
Serial.println("Sensor: failed to get sensor");
|
||||||
|
return 999;
|
||||||
|
}
|
||||||
|
return thissensor->value();
|
||||||
|
}
|
||||||
|
|||||||
58
sensor.h
58
sensor.h
@@ -3,21 +3,57 @@
|
|||||||
#include "Arduino.h"
|
#include "Arduino.h"
|
||||||
#include "mqtt.h"
|
#include "mqtt.h"
|
||||||
|
|
||||||
|
enum sensor_e
|
||||||
|
{
|
||||||
|
//PMS sensors
|
||||||
|
SP_1P0,
|
||||||
|
SP_2P5,
|
||||||
|
SP_10P0,
|
||||||
|
AE_1P0,
|
||||||
|
AE_2P5,
|
||||||
|
AE_10P0,
|
||||||
|
PPD_0P3,
|
||||||
|
PPD_0P5,
|
||||||
|
PPD_1P0,
|
||||||
|
PPD_2P5,
|
||||||
|
PPD_5P0,
|
||||||
|
PPD_10P0,
|
||||||
|
SCD30_temp,
|
||||||
|
SCD30_hum,
|
||||||
|
SCD30_co2,
|
||||||
|
SGP30_TVOC,
|
||||||
|
SGP30_eCO2,
|
||||||
|
SGP30_rawH2,
|
||||||
|
SGP30_rawEthanol,
|
||||||
|
sensor_last
|
||||||
|
};
|
||||||
|
|
||||||
class AQSSensor
|
class AQSSensor
|
||||||
{
|
{
|
||||||
char _topic[50];
|
char _topic[50];
|
||||||
char _topic_1h[50];
|
char _topic_1h[50];
|
||||||
char _topic_24h[255];
|
char _topic_24h[255];
|
||||||
const String _name;
|
const String _name;
|
||||||
|
const sensor_e _sensor;
|
||||||
|
const String _unit;
|
||||||
uint32_t _value;
|
uint32_t _value;
|
||||||
RunningMedian _average1h;
|
RunningMedian _average1h;
|
||||||
RunningMedian _average24h;
|
RunningMedian _average24h;
|
||||||
bool _publish = false;
|
bool _publish = false;
|
||||||
|
const uint32_t _scaleMin;
|
||||||
|
const uint32_t _scaleMax;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AQSSensor(String name, String deviceID, uint8_t avgSamples) : _name(name),
|
AQSSensor(String name, sensor_e sensor, String unit,
|
||||||
_average1h(avgSamples),
|
String deviceID, uint8_t avgSamples,
|
||||||
_average24h(avgSamples * 24)
|
uint32_t scaleMin, uint32_t scaleMax)
|
||||||
|
: _name(name),
|
||||||
|
_sensor(sensor),
|
||||||
|
_unit(unit),
|
||||||
|
_average1h(avgSamples),
|
||||||
|
_average24h(avgSamples * 24),
|
||||||
|
_scaleMin(scaleMin),
|
||||||
|
_scaleMax(scaleMax)
|
||||||
{
|
{
|
||||||
sprintf(_topic, "Sensors/%s/%s", deviceID.c_str(), name.c_str());
|
sprintf(_topic, "Sensors/%s/%s", deviceID.c_str(), name.c_str());
|
||||||
sprintf(_topic_1h, "Sensors/%s/%s/1h/", deviceID.c_str(), name.c_str());
|
sprintf(_topic_1h, "Sensors/%s/%s/1h/", deviceID.c_str(), name.c_str());
|
||||||
@@ -34,6 +70,11 @@ public:
|
|||||||
return _name;
|
return _name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void published(void)
|
||||||
|
{
|
||||||
|
_publish = false;
|
||||||
|
}
|
||||||
|
|
||||||
void publishMQTT(void)
|
void publishMQTT(void)
|
||||||
{
|
{
|
||||||
if (_publish == true)
|
if (_publish == true)
|
||||||
@@ -58,13 +99,22 @@ public:
|
|||||||
{
|
{
|
||||||
if (_publish)
|
if (_publish)
|
||||||
{
|
{
|
||||||
Serial.printf("sensor:%s = %d\n", _name.c_str(), _value);
|
Serial.printf("sensor: report: %s = %d\n", _name.c_str(), _value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sensor_e getSensor(void) { return _sensor; }
|
||||||
|
|
||||||
|
String getUnit(void) { return _unit;}
|
||||||
|
|
||||||
|
uint32_t getMin(void) { return _scaleMin; }
|
||||||
|
uint32_t getMax(void) { return _scaleMax; }
|
||||||
};
|
};
|
||||||
|
|
||||||
void addSensorToList(AQSSensor *sensor);
|
void addSensorToList(AQSSensor *sensor);
|
||||||
void reportToSerial(void);
|
void reportToSerial(void);
|
||||||
|
AQSSensor *getSensor(sensor_e sensor);
|
||||||
|
uint32_t getSensorValue(sensor_e sensor);
|
||||||
|
|
||||||
void initSensor(void);
|
void initSensor(void);
|
||||||
void handleSensor(void);
|
void handleSensor(void);
|
||||||
Reference in New Issue
Block a user