NEWS
[gelöst] (vorübergehend) Luftgütesensor BME 680
-
Hi,
vielleicht kann mir jemand mal weiterhelfen.
Vielleicht ist meine Idee ja auch Quatsch und ich lasse das auch wieder….
Ich habe über Aliexpress einen kleinen Luftgütesensor bezogen. Das Ding nennt sich BME680. Ich wollte absichtlich ein Teil das nicht "nur" CO2 misst sondern etwas Breitbandiger ist. Dazu sollte es möglichst klein sein weil ich es in ein unauffälliges Gehäuse an der Decke platzieren möchte. Bei einem größeren Gehäuse reißt mir meine Frau lebenswichtige Teile ab :oops:
Ich habe das Teil jetzt an einem Sonoff SV mit Tasmota Firmware angeschlossen und arbeite mich seit mehreren Tagen durch Datenblätter und alle möglichen Informationen. Bosch scheint ja aus der Umrechnung ein Staatsgeheimnis zu machen.
Die Umrechnung schein so zu laufen:
Luftqualität besser: Widerstand höher
Luftqualität schlechter Widerstand geringer
Setup:
Widerstandswert nach 5 Minuten klare Luft als Basis
Luftfeuchtigkeit 50% als Basis
Gewichtung Gas/Luftfeuchte 75/25
Dazu habe ich zwar ein paar scripte gefunden, aber kann ich die Im ioBroker so einbinden?
Version 1:
#!/usr/bin/env python import bme680 import time print("""Estimate indoor air quality Runs the sensor for a burn-in period, then uses a combination of relative humidity and gas resistance to estimate indoor air quality as a percentage. Press Ctrl+C to exit """) sensor = bme680.BME680() # These oversampling settings can be tweaked to # change the balance between accuracy and noise in # the data. sensor.set_humidity_oversample(bme680.OS_2X) sensor.set_pressure_oversample(bme680.OS_4X) sensor.set_temperature_oversample(bme680.OS_8X) sensor.set_filter(bme680.FILTER_SIZE_3) sensor.set_gas_status(bme680.ENABLE_GAS_MEAS) sensor.set_gas_heater_temperature(320) sensor.set_gas_heater_duration(150) sensor.select_gas_heater_profile(0) # start_time and curr_time ensure that the # burn_in_time (in seconds) is kept track of. start_time = time.time() curr_time = time.time() burn_in_time = 300 burn_in_data = [] try: # Collect gas resistance burn-in values, then use the average # of the last 50 values to set the upper limit for calculating # gas_baseline. print("Collecting gas resistance burn-in data for 5 mins\n") while curr_time - start_time < burn_in_time: curr_time = time.time() if sensor.get_sensor_data() and sensor.data.heat_stable: gas = sensor.data.gas_resistance burn_in_data.append(gas) print("Gas: {0} Ohms".format(gas)) time.sleep(1) gas_baseline = sum(burn_in_data[-50:]) / 50.0 # Set the humidity baseline to 40%, an optimal indoor humidity. hum_baseline = 40.0 # This sets the balance between humidity and gas reading in the # calculation of air_quality_score (25:75, humidity:gas) hum_weighting = 0.25 print("Gas baseline: {0} Ohms, humidity baseline: {1:.2f} %RH\n".format(gas_baseline, hum_baseline)) while True: if sensor.get_sensor_data() and sensor.data.heat_stable: gas = sensor.data.gas_resistance gas_offset = gas_baseline - gas hum = sensor.data.humidity hum_offset = hum - hum_baseline # Calculate hum_score as the distance from the hum_baseline. if hum_offset > 0: hum_score = (100 - hum_baseline - hum_offset) / (100 - hum_baseline) * (hum_weighting * 100) else: hum_score = (hum_baseline + hum_offset) / hum_baseline * (hum_weighting * 100) # Calculate gas_score as the distance from the gas_baseline. if gas_offset > 0: gas_score = (gas / gas_baseline) * (100 - (hum_weighting * 100)) else: gas_score = 100 - (hum_weighting * 100) # Calculate air_quality_score. air_quality_score = hum_score + gas_score print("Gas: {0:.2f} Ohms,humidity: {1:.2f} %RH,air quality: {2:.2f}".format(gas, hum, air_quality_score)) time.sleep(1) except KeyboardInterrupt: pass ````Quelle: [https://github.com/pimoroni/bme680/blob … quality.py](https://github.com/pimoroni/bme680/blob/master/examples/indoor-air-quality.py) Version 2:
/***************************************************************************
This is a library for the BME680 gas, humidity, temperature & pressure sensor
Designed specifically to work with the Adafruit BME680 Breakout
----> http://www.adafruit.com/products/XXXX
These sensors use I2C or SPI to communicate, 2 or 4 pins are required
to interface.
Adafruit invests time and resources providing this open source code,
please support Adafruit andopen-source hardware by purchasing products
from Adafruit!
Written by Limor Fried & Kevin Townsend for Adafruit Industries.
BSD license, all text above must be included in any redistribution
***************************************************************************/#include <wire.h>
#include <spi.h>
#include <adafruit_sensor.h>
#include "Adafruit_BME680.h"#define BME_SCK 13
#define BME_MISO 12
#define BME_MOSI 11
#define BME_CS 10
#define SEALEVELPRESSURE_HPA (1013.25)Adafruit_BME680 bme; // I2C
//Adafruit_BME680 bme(BME_CS); // hardware SPI
//Adafruit_BME680 bme(BME_CS, BME_MOSI, BME_MISO, BME_SCK);float hum_weighting = 0.25; // so hum effect is 25% of the total air quality score
float gas_weighting = 0.75; // so gas effect is 75% of the total air quality scorefloat hum_score, gas_score;
float gas_reference = 250000;
float hum_reference = 40;
int getgasreference_count = 0;void setup() {
Serial.begin(115200);
while (!Serial);
Serial.println(F("BME680 test"));Wire.begin();
if (!bme.begin()) {
Serial.println("Could not find a valid BME680 sensor, check wiring!");
while (1);
} else Serial.println("Found a sensor");// Set up oversampling and filter initialization
bme.setTemperatureOversampling(BME680_OS_2X);
bme.setHumidityOversampling(BME680_OS_2X);
bme.setPressureOversampling(BME680_OS_2X);
bme.setIIRFilterSize(BME680_FILTER_SIZE_3);
bme.setGasHeater(320, 150); // 320°C for 150 ms
// Now run the sensor for a burn-in period, then use combination of relative humidity and gas resistance to estimate indoor air quality as a percentage.
GetGasReference();
}void loop() {
Serial.print("Temperature = ");Serial.print(bme.readTemperature());
Serial.println("°C");Serial.print(" Pressure = ");
Serial.print(bme.readPressure() / 100.0F);
Serial.println(" hPa");Serial.print(" Humidity = ");
Serial.print(bme.readHumidity());
Serial.println("%");Serial.print(" Gas = ");
Serial.print(bme.readGas());
Serial.println("R\n");
/*
This software, the ideas and concepts is Copyright (c) David Bird 2018. All rights to this software are reserved.Any redistribution or reproduction of any part or all of the contents in any form is prohibited other than the following:
1. You may print or download to a local hard disk extracts for your personal and non-commercial use only.
2. You may copy the content to individual third parties for their personal use, but only if you acknowledge the author David Bird as the source of the material.
3. You may not, except with my express written permission, distribute or commercially exploit the content.
4. You may not transmit it or store it in any other website or other form of electronic retrieval system for commercial purposes.
The above copyright ('as annotated') notice and this permission notice shall be included in all copies or substantial portions of the Software and where the
software use is visible to an end-user.THE SOFTWARE IS PROVIDED "AS IS" FOR PRIVATE USE ONLY, IT IS NOT FOR COMMERCIAL USE IN WHOLE OR PART OR CONCEPT. FOR PERSONAL USE IT IS SUPPLIED 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 AUTHOR OR COPYRIGHT HOLDER 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.
See more at http://www.dsbird.org.uk
/
//Calculate humidity contribution to IAQ index
float current_humidity = bme.readHumidity();
if (current_humidity >= 38 && current_humidity <= 42)
hum_score = 0.25100; // Humidity +/-5% around optimum
else
{ //sub-optimal
if (current_humidity < 38)
hum_score = 0.25/hum_referencecurrent_humidity100;
else
{
hum_score = ((-0.25/(100-hum_reference)*current_humidity)+0.416666)*100;
}
}//Calculate gas contribution to IAQ index
int gas_lower_limit = 5000; // Bad air quality limit
int gas_upper_limit = 50000; // Good air quality limit
if (gas_reference > gas_upper_limit) gas_reference = gas_upper_limit;
if (gas_reference < gas_lower_limit) gas_reference = gas_lower_limit;
gas_score = (0.75/(gas_upper_limit-gas_lower_limit)gas_reference -(gas_lower_limit(0.75/(gas_upper_limit-gas_lower_limit))))*100;//Combine results for the final IAQ index value (0-100% where 100% is good quality air)
float air_quality_score = hum_score + gas_score;Serial.println("Air Quality = "+String(air_quality_score,1)+"% derived from 25% of Humidity reading and 75% of Gas reading - 100% is good quality air");
Serial.println("Humidity element was : "+String(hum_score/100)+" of 0.25");
Serial.println(" Gas element was : "+String(gas_score/100)+" of 0.75");
if (bme.readGas() < 120000) Serial.println("***** Poor air quality *****");
Serial.println();
if ((getgasreference_count++)%10==0) GetGasReference();
Serial.println(CalculateIAQ(air_quality_score));
Serial.println("------------------------------------------------");
delay(2000);
}void GetGasReference(){
// Now run the sensor for a burn-in period, then use combination of relative humidity and gas resistance to estimate indoor air quality as a percentage.
Serial.println("Getting a new gas reference value");
int readings = 10;
for (int i = 0; i <= readings; i++){ // read gas for 10 x 0.150mS = 1.5secs
gas_reference += bme.readGas();
}
gas_reference = gas_reference / readings;
}String CalculateIAQ(float score){
String IAQ_text = "Air quality is ";
score = (100-score)*5;
if (score >= 301) IAQ_text += "Hazardous";
else if (score >= 201 && score <= 300 ) IAQ_text += "Very Unhealthy";
else if (score >= 176 && score <= 200 ) IAQ_text += "Unhealthy";
else if (score >= 151 && score <= 175 ) IAQ_text += "Unhealthy for Sensitive Groups";
else if (score >= 51 && score <= 150 ) IAQ_text += "Moderate";
else if (score >= 00 && score <= 50 ) IAQ_text += "Good";
return IAQ_text;
}</adafruit_sensor.h></spi.h></wire.h>Ich bekomme aus dem Sonoff die im Anhang aufgeführten Objekte geliefert Meine Umsetzung wäre jetzt eine durch ein Script einen Datenpunkt zu erzeugen der dann per VIS als Displayfläche dargestellt wird die die Farbe ändert. Ich habe mit scripten absolut wenig Erfahrung. Ich kann mir zwar manches zusammenreimen, aber so grundsätzliche Denkanstöße brauche ich immer dafür. Es wäre nett wenn mir jemand weiterhelfen könnte. Ich kann gerne auch aus der nächsten Lieferung einen BME680 als Belohnung spendieren. Die sind aber aktuell noch auf dem Postweg. [5779_bme680.jpg](/assets/uploads/files/5779_bme680.jpg)
-
Ich habe mich jetzt mangels Javasript Kentnissen an Blockly versucht.
Kann das funktionieren?
Zunächst das Script getriggert auf Änderung des Gassensors und im zweiten Bild die beiden Formeln
5779_luftgueteformel1.jpg
5779_luftguete1.jpg -
Jetzt stellt sich eigentlich die Frage wie ich aus der Formel das Ergebnis in den Datenpunkt bekomme.
Irgendwas übersehe ich da. Mir wird aktuell nur "true" reingeschrieben.
-
Für heute reichts erstmal. Mir raucht der Kopf.
Ich habe jetzt einen Wert der wie ein IAQ aussieht. Allerdings mit 8 Dezimalstellen. Das will ich noch ändern.
Dazu habe ich noch eine Ausertung erstellt mit der ich dann ein Farbfeld steuern möchte.
Allerdings habe ich das noch nicht getestet. Ich denke aber das das so nicht funktionieren wird.
Wenn jemand Vorschläge hat oder das Ganze vereinfachen kann dann bitte her damit.
5779_luftgueteauswertung.jpg
5779_luftgueteb.jpg
5779_luftguetea.jpg -
Ich habe über Aliexpress einen kleinen Luftgütesensor bezogen. Das Ding nennt sich BM680. `
Mit deinem Beitrag hast Du mich wieder irgendwie angefixt und nun denke ich gerade darüber nach mir auch so ein Teil zu bestellen. Welchen hast Du bei Aliexpress bestellt?Der Fühler soll an die I2C Schnittstelle angeschlossen werden.
Für mich kämen folgende Fühler in Frage:
https://www.amazon.de/BlueDot-BME680-Se … I2C+BME680
https://www.watterott.com/de/BME680-Breakout
Wobei dieser mein Favorit wäre:
-
Ich wollte recht klein:
https://de.aliexpress.com/item/BME680-D … 4c4djehDSC
Allerdings habe ich das Script immer noch nicht so richtig am laufen.
Irgendwie scheint auch die Formel in der Form nicht zu stimmen.
Wenn ich die Luft "verunreinige" (Taschentuch mit Alkohol) sinkt bei mir der gelesene Widerstandswert. Also genau umgekehrt zur Formel.
Da werde ich mir das am Wochenende mal genauer anschauen müssen.
-
Ich wollte recht klein: ` Und wie eingebunden, I²C- oder SPI-Bus?
> Da werde ich mir das am Wochenende mal genauer anschauen müssen.
Immer gut wenn sich schon jemand in das Thema reinhängt. :mrgreen:Ich werde dann mal meinen Favoriten bestellen.
-
Das hängt an einem Sonoff SV mit Tasmota Firmware und ist dort über I²C Bus eingebunden.
Die Daten werden per MQTT an den ioBroker gesendet und dort dann per Script (hoffentlich irgendwann einmal) weiterverarbeitet um dann in VIS als mehrfarbige Fläche (Ampel) dargestellt zu werden.
Das Projekt ist jetzt gerade eine Woche alt, aber ich bin mit Scripten nicht so 100% fit das ich wirklich lange grübeln muss wie ich was umsetzen kann.
Auch für Blockly (obwohl im Grunde einfach) bin ich wohl etwas alt.
-
Nach ein paar Tagen herumexperimentieren habe ich den Eindruck gewonnen das mit der Tasmota Firmware irgendwas nicht stimmen kann.
1. Mein Sensor verhält sich genau umgekehrt zur Beschreibung. Lege ich einen Lappen mit Spiritus neben den Sensor geht der Wert auf 9 kOhm RUNTER! Laut Bosch sollte der Wert nach oben gehen.
2. Im Datenblatt wird eine Heiz- und Ausleseperiode beschrieben. Das kann ich an den gelieferten Werten nicht erkennen. Ich sehe einen ständig schwankenden Widerstandswert der offensichtlich einfach nur ausgelesen wird ohne einen Messrhythmus zu befolgen.
3. Wenn ich die Weboberfläche des Sensors offen lasse steigt der Widerstandswert ständig an ohne das sich an der Luft etwas verändern würde.
Von daher habe ich meine Experimente mit dem Script zunächst zurückgestellt bis ich herausgefunden habe was hier falsch läuft.
-
Jetz wird es schräg.
Da mir derzeit keine bessere Lösung einfällt und Bosch offensichtlich die Formeln geheim hält werde ich langfristig vermutlich nicht beim BME680 bleiben.
Übergangsweise habe ich mir eine Lösung gebastelt die Meine Zwecke erfüllen müsste:
Aus dem angehängten Tabellenblatt (von adafruit) habe ich mir folgende Formel konstruiert:
Basistemperatur: 21°C Basisluftfeuchte: 40% Basiswert: 5 kOhm Wichtung: Temperatur: 4,7% Luftfeuchte: 8% Luftgüte: 70% A= Temperatur B= Luftfeuchte C= Widerstandswert in kOhm ((100-21-A)/(100-21)*(0,047*100))+((100-40-B)/(100-40)*(0,08*100))+((C/5)*(0,7*10))
Da hier aber die Werte jetzt noch auf IAQ angepasst werden sollten habe ich die Werte etwas verschoben damit ich bei ca. 500 ende:
((79-A)/(79*3,3))+((60-B)/(60*5,6))+((C/5)*4,9) oder ((79-A)/260,7)+((60-B)/336)+((C/5)*4,9)
Da jetzt die Werte offensichtlich nicht der gängigen IAQ Tabelle entsprechen habe ich meine Grenzwerte entsprechend angepasst.
Da ich hier mit recht wenig Umweltverschmutzung lebe habe ich den Grenzwert "offenes Fenster neben Sensor" als Übergangswert zur "normalen" Luftqualität angelegt.
Somit habe ich dann für AIQ folgende Grenzwerte:
500 - 301 Sehr gut
300 - 251 Normal
200 - 151 etwas Schlecht
150 - 101 Schlecht
100 - 51 sehr schlecht
50 - 0 Extrem schlecht
Ich denke mit dem Kompromiss kann ich aktuell arbeiten und suche jetzt einen Sensor der nicht "Top Secret" ist.
Falls mir jemand noch bei der Umsetzung in VIS helfen könnte wäre ich recht dankbar.
Ich dachte an ein farbig hinterlegtes Feld das den Wert anzeigt und entsprechend die Farbe ändert. Leider habe ich keine Ahnung welches Widget oder welche Kombination dazu geeignet wäre.
5779_slide1.jpg -
So, dank tatkräftiger Hilfe von Paul53 habe ich es geschafft eine Anzeigefläche in VIS zu basteln.
Das Script für die Auswertung:
const Luftguete = "javascript.1.Luftguete"/*Luftguete*/; function setQualitaet(x) { var y = 0; if(x > 50) y = 1; if(x > 100) y = 2; if(x > 150) y = 3; if(x > 200) y = 4; if(x > 300) y = 5; setState("javascript.1.Luftqualität"/*Luftqualität*/, y, true); } setQualitaet(getState(Luftguete).val); // Skriptstart on(Luftguete, function(dp) { // triggert bei Wertänderung setQualitaet(dp.state.val); });
Schreibt einen Datenpunkt "Luftqualität" in das Objekt Javasript.1.
In VIS nehme ich nun ein "Image 8" und frage dort den Wert des Datenpunkts ab.
Als Bilder verwende ich einfach 100x100 Bilder mit entsprechender Hintergrundfarbe. Optional wären auch Smilies denkbar.
Ich danke erst mal allen die sich auch Gedanken zu dem Thema gemacht haben und hoffe das meine vorübergehende Lösung für die ersten Schritte mit dem BME680 taugt.
Wenn das jemand nachbauen möchte darf er sich das gerne mitnehmen.
Das Erfassungssscript
on({id: 'sonoff.0.Umweltsensor.BME680_Gas', change: "ne"}, function (obj) { var value = obj.state.val; var oldValue = obj.oldState.val; setState("javascript.1.Luftguete"/*Luftguete*/, ((79 - getState("sonoff.0.Umweltsensor.BME680_Temperature").val) / 260.7 + (60 - getState("sonoff.0.Umweltsensor.BME680_Humidity").val) / 336 + (getState("sonoff.0.Umweltsensor.BME680_Gas").val / 5) * 4.9), true); });
Noch ein Hinweis: Alle beiden scripte laufen in der Instanz javascript.1. Bei einer Übernahme muss das eventuell an die eigene Situation angepasst werden.
-
@Chaot
Besten Dank für das Teilen deines Scriptes.Als Tipp für Newbies die evtl. genau so fragend vor dem PC sitzen und sich wundern warum immer nur 100 als Wert erscheint:
Ihr müsst den Zahlenbereich des angelegten Datenpunktes "Luftguete" entsprechend anpassen