diff --git a/diozero-core/src/main/java/com/diozero/devices/AirQualitySensorInterface.java b/diozero-core/src/main/java/com/diozero/devices/AirQualitySensorInterface.java new file mode 100644 index 00000000..27db0d31 --- /dev/null +++ b/diozero-core/src/main/java/com/diozero/devices/AirQualitySensorInterface.java @@ -0,0 +1,84 @@ +package com.diozero.devices; +/*- + * #%L + * Organisation: diozero + * Project: diozero - Core + * Filename: AirQualitySensorInterface.java + * + * This file is part of the diozero project. More information about this project + * can be found at https://www.diozero.com/. + * %% + * Copyright (C) 2016 - 2024 diozero + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +/** + * An air-quality sensor uses relative humidity and a "resistance" value to measure air quaility. + */ +public interface AirQualitySensorInterface extends HygrometerInterface { + /** + * "Standard" humidity baseline typically used for indoor air-quality. + */ + float STANDARD_INDOOR_HUMIDITY = 40f; + + float getGasResistance(); + + /** + * Calculates the indoor air quality as a percentage based on a baseline reading. Based off of Pimoroni's + * indoor-air-quality.py + *

+ * Under non-calibrated, general usage, it is recommended that the sensor "warm up" in the current mode for at least + * 30 minutes before taking readings. After that time, any kind of statistical baseline (e.g. average) for the gas + * and humidity readings can be used to get an indication of general air-quality. + *

+ * + * @param gasReading current reading + * @param gasBaseline the "baseline" to score off of + * @param humidityReading current reading + * @param humidityBaseline the "baseline" to score off of + * @param humidityWeighting weighting applied to scoring (a good default is 0.25f) + */ + static float airQuality(float gasReading, float gasBaseline, float humidityReading, float humidityBaseline, + float humidityWeighting) { + float gasOffset = gasBaseline - gasReading; + float humidityOffset = humidityReading - humidityBaseline; + + float humidityScore; + if (humidityOffset > 0) { + humidityScore = (100 - humidityBaseline - humidityOffset) / (100 - humidityBaseline) * (humidityWeighting * 100); + } + else { + humidityScore = (humidityBaseline + humidityOffset) / humidityBaseline * (humidityWeighting * 100); + } + + float gasScore; + if (gasOffset > 0) { + gasScore = (gasReading / gasBaseline) * (100 - (humidityWeighting * 100)); + } + else { + gasScore = 100 - (humidityWeighting * 100); + } + + // Calculate air_quality_score. + return humidityScore + gasScore; + } + +} diff --git a/diozero-core/src/main/java/com/diozero/devices/BME68x.java b/diozero-core/src/main/java/com/diozero/devices/BME68x.java index 34c3a5c9..7fd176c3 100644 --- a/diozero-core/src/main/java/com/diozero/devices/BME68x.java +++ b/diozero-core/src/main/java/com/diozero/devices/BME68x.java @@ -47,7 +47,7 @@ * https://github.com/pimoroni/bme680-python * https://github.com/knobtviker/bme680 */ -public class BME68x implements BarometerInterface, ThermometerInterface, HygrometerInterface { +public class BME68x implements BarometerInterface, ThermometerInterface, AirQualitySensorInterface { // Chip vendor for the BME680 private static final String CHIP_VENDOR = "Bosch"; // Chip name for the BME680 @@ -552,7 +552,7 @@ public BME68x(final I2CDeviceInterface device) { * @param controller I2C bus the sensor is connected to. * @param address I2C address of the sensor. * @param humidityOversampling Humidity oversampling. - * @param termperatureOversampling Temperature oversampling. + * @param temperatureOversampling Temperature oversampling. * @param pressureOversampling Pressure oversampling. * @param filter Infinite Impulse Response (IIR) filter. * @param standbyDuration Standby time between sequential mode @@ -570,7 +570,7 @@ public BME68x(final int controller, final int address, OversamplingMultiplier hu * * @param device I2C device. * @param humidityOversampling Humidity oversampling. - * @param termperatureOversampling Temperature oversampling. + * @param temperatureOversampling Temperature oversampling. * @param pressureOversampling Pressure oversampling. * @param filter Infinite Impulse Response (IIR) filter. * @param standbyDuration Standby time between sequential mode @@ -910,6 +910,7 @@ public float getRelativeHumidity() { return getSensorData()[0].getHumidity(); } + @Override public float getGasResistance() { return getSensorData()[0].getGasResistance(); } @@ -2013,43 +2014,4 @@ public String toString() { } } - /** - * Calculates the indoor air quality as a percentage based on a baseline reading. Based off of Pimoroni's - * indoor-air-quality.py - *

- * Under non-calibrated, general usage, it is recommended that the sensor "warm up" in the current mode for at least - * 30 minutes before taking readings. After that time, any kind of statistical baseline (e.g. average) for the gas - * and humidity readings can be used to get an indication of general air-quality. - *

- * - * @param gasReading current reading - * @param gasBaseline the "baseline" to score off of - * @param humidityReading current reading - * @param humidityBaseline the "baseline" to score off of - * @param humidityWeighting weighting applied to scoring (a good default is 0.25f) - */ - public static float airQuality(float gasReading, float gasBaseline, float humidityReading, float humidityBaseline, - float humidityWeighting) { - float gasOffset = gasBaseline - gasReading; - float humidityOffset = humidityReading - humidityBaseline; - - float humidityScore; - if (humidityOffset > 0) { - humidityScore = (100 - humidityBaseline - humidityOffset) / (100 - humidityBaseline) * (humidityWeighting * 100); - } - else { - humidityScore = (humidityBaseline + humidityOffset) / humidityBaseline * (humidityWeighting * 100); - } - - float gasScore; - if (gasOffset > 0) { - gasScore = (gasReading / gasBaseline) * (100 - (humidityWeighting * 100)); - } - else { - gasScore = 100 - (humidityWeighting * 100); - } - - // Calculate air_quality_score. - return humidityScore + gasScore; - } } diff --git a/diozero-sampleapps/src/main/java/com/diozero/sampleapps/BME68xTest.java b/diozero-sampleapps/src/main/java/com/diozero/sampleapps/BME68xTest.java index 8b11f10f..e3d07b53 100644 --- a/diozero-sampleapps/src/main/java/com/diozero/sampleapps/BME68xTest.java +++ b/diozero-sampleapps/src/main/java/com/diozero/sampleapps/BME68xTest.java @@ -34,6 +34,7 @@ import java.util.ArrayList; import java.util.List; +import com.diozero.devices.AirQualitySensorInterface; import com.diozero.devices.BME68x; import com.diozero.devices.BME68x.Data; import com.diozero.devices.BME68x.HeaterConfig; @@ -277,7 +278,7 @@ private static void iaqTest(BME68x bme68x) { float hum = data[0].getHumidity(); // Calculate air_quality_score. - float air_quality_score = BME68x.airQuality(gas, gas_baseline, hum, hum_baseline, hum_weighting); + float air_quality_score = AirQualitySensorInterface.airQuality(gas, gas_baseline, hum, hum_baseline, hum_weighting); System.out.format("Gas: %,.2f Ohms, humidity: %,.2f %%RH, air quality: %,.2f%n", gas, hum, air_quality_score); SleepUtil.sleepSeconds(1);