Add support for the BME680 sensor

This commit is contained in:
Thomas Kolb 2025-01-03 22:45:10 +01:00
parent 782692eaa3
commit f38e6fe6ae
5 changed files with 165 additions and 30 deletions

View file

@ -7,20 +7,27 @@
<script type="text/javascript">
function updateSensorTable(sensordata)
{
elements = ['temperature', 'humidity', 'co2'];
scd30_elements = ['temperature', 'humidity', 'co2'];
elements.forEach(function(item, index) {
scd30_elements.forEach(function(item, index) {
elem = document.getElementById('scd30_' + item);
val = sensordata['scd30'][item];
elem.textContent = val.toFixed(2);
});
bme680_elements = ['temperature', 'humidity', 'pressure', 'gas_resistance'];
bme680_elements.forEach(function(item, index) {
elem = document.getElementById('bme680_' + item);
val = sensordata['bme680'][item];
elem.textContent = val.toFixed(2);
});
}
function setup()
{
getSensorData(updateSensorTable);
setInterval(function() { getSensorData(updateSensorTable) }, 60000);
setInterval(function() { getSensorData(updateSensorTable) }, 30000);
}
</script>
<style>
@ -43,24 +50,40 @@
<table>
<tr>
<th>Sensor</th>
<th>Messwert</th>
<th>SCD30</th>
<th>BME680</th>
<th>Einheit</th>
</tr>
<tr>
<td>SCD30 CO₂</td>
<td class="value" id="scd30_co2">----,--</td>
<td>CO₂</td>
<td class="value" id="scd30_co2">---,--</td>
<td class="value" id="bme680_co2">---,--</td>
<td>ppm</td>
</tr>
<tr>
<td>SCD30 Temperatur</td>
<td>Temperatur</td>
<td class="value" id="scd30_temperature">-,--</td>
<td class="value" id="bme680_temperature">-,--</td>
<td>°C</td>
</tr>
<tr>
<td>SCD30 Luftfeuchtigkeit</td>
<td class="value" id="scd30_humidity">-,--</td>
<td>Luftfeuchtigkeit</td>
<td class="value" id="scd30_humidity">--,--</td>
<td class="value" id="bme680_humidity">--,--</td>
<td>%rH</td>
</tr>
<tr>
<td>Luftdruck</td>
<td class="value" id="scd30_pressure">---,--</td>
<td class="value" id="bme680_pressure">---,--</td>
<td>hPa</td>
</tr>
<tr>
<td>Gaswiderstand</td>
<td class="value" id="scd30_gas_resistance">---,--</td>
<td class="value" id="bme680_gas_resistance">---,--</td>
<td></td>
</tr>
</table>
</body>
</html>

View file

@ -1,6 +1,7 @@
#pragma once
#include <Adafruit_SCD30.h>
#include <Adafruit_BME680.h>
#include <HTTPRequest.hpp>
#include <HTTPResponse.hpp>
@ -12,7 +13,7 @@
class WebServer
{
public:
bool start(Adafruit_SCD30 *scd30);
bool start(Adafruit_SCD30 *scd30, Adafruit_BME680 *bme680);
static WebServer &instance(void)
{
@ -24,6 +25,7 @@ class WebServer
httpsserver::HTTPServer *m_server;
Adafruit_SCD30 *m_scd30;
Adafruit_BME680 *m_bme680;
WebServer(void);

View file

@ -19,6 +19,7 @@ monitor_speed = 115200
lib_deps =
zinggjm/GxEPD2@^1.5.7
adafruit/Adafruit SCD30@^1.0.11
adafruit/Adafruit BME680 Library@^2.0.5
SPI
Wire
NTPClient

View file

@ -71,6 +71,15 @@ void WebServer::handleReadout(httpsserver::HTTPRequest *req, httpsserver::HTTPRe
res->print(instance().m_scd30->relative_humidity);
res->print(", \"temperature\": ");
res->print(instance().m_scd30->temperature);
res->print("},");
res->print("\"bme680\": {\"pressure\": ");
res->print(instance().m_bme680->pressure/100.0);
res->print(", \"humidity\": ");
res->print(instance().m_bme680->humidity);
res->print(", \"temperature\": ");
res->print(instance().m_bme680->temperature);
res->print(", \"gas_resistance\": ");
res->print(instance().m_bme680->gas_resistance / 1000.0);
res->println("}}");
}
@ -155,9 +164,10 @@ void WebServer::serverTask(void *arg)
Serial.println("[server] Server died?! This should never happen.");
}
bool WebServer::start(Adafruit_SCD30 *scd30)
bool WebServer::start(Adafruit_SCD30 *scd30, Adafruit_BME680 *bme680)
{
m_scd30 = scd30;
m_bme680 = bme680;
xTaskCreatePinnedToCore(
WebServer::serverTask, /* Task function. */

View file

@ -1,5 +1,6 @@
#include <Arduino.h>
#include <Adafruit_SCD30.h>
#include <Adafruit_BME680.h>
#include <WiFi.h>
#include <WiFiMulti.h>
@ -19,8 +20,10 @@
#include <epaper.h>
Adafruit_SCD30 scd30;
Adafruit_BME680 bme680;
static timeseries_t ts_scd30_co2, ts_scd30_temperature, ts_scd30_humidity;
static timeseries_t ts_bme680_temperature, ts_bme680_humidity, ts_bme680_pressure, ts_bme680_gas_resistance;
static WiFiMulti wiFiMulti;
static bool wiFiConnectedToStation;
@ -94,6 +97,10 @@ void initSCD30(void)
timeseries_init(&ts_scd30_co2, timeClient.getEpochTime(), 15, "ppm", "%.1f");
timeseries_init(&ts_scd30_temperature, timeClient.getEpochTime(), 15, "°C", "%.1f");
timeseries_init(&ts_scd30_humidity, timeClient.getEpochTime(), 15, "%", "%.1f");
timeseries_init(&ts_bme680_temperature, timeClient.getEpochTime(), 15, "°C", "%.1f");
timeseries_init(&ts_bme680_humidity, timeClient.getEpochTime(), 15, "%", "%.1f");
timeseries_init(&ts_bme680_pressure, timeClient.getEpochTime(), 15, "hPa", "%.1f");
timeseries_init(&ts_bme680_gas_resistance, timeClient.getEpochTime(), 15, "kOhm", "%.1f");
// Try to initialize!
if (!scd30.begin()) {
@ -102,8 +109,7 @@ void initSCD30(void)
}
Serial.println("scd30: SCD30 Found!");
// TODO: check what this actually resets. Maybe also the calibration?
scd30.reset();
scd30.reset(); // note: does not reset the calibration
if (!scd30.setMeasurementInterval(15)){ // seconds
Serial.println("scd30: Failed to set measurement interval");
@ -145,6 +151,24 @@ void initSCD30(void)
}
static void initBME680(void)
{
if (!bme680.begin()) {
Serial.println(F("Could not find a valid BME680 sensor, check wiring!"));
while (1) {
delay(10);
}
}
// Set up oversampling and filter initialization
bme680.setTemperatureOversampling(BME680_OS_8X);
bme680.setHumidityOversampling(BME680_OS_2X);
bme680.setPressureOversampling(BME680_OS_4X);
bme680.setIIRFilterSize(BME680_FILTER_SIZE_3);
bme680.setGasHeater(320, 150); // 320*C for 150 ms
}
static void draw_epaper_initial_callback(GxEPD2_DISPLAY_CLASS<GxEPD2_DRIVER_CLASS, MAX_HEIGHT(GxEPD2_DRIVER_CLASS)> *display)
{
display->setRotation(1);
@ -212,16 +236,19 @@ void setup(void)
wifi_setup();
initSCD30();
initBME680();
epaper_init();
epaper_draw_and_hibernate(draw_epaper_initial_callback, false);
WebServer::instance().start(&scd30);
WebServer::instance().start(&scd30, &bme680);
}
static void draw_epaper_callback(GxEPD2_DISPLAY_CLASS<GxEPD2_DRIVER_CLASS, MAX_HEIGHT(GxEPD2_DRIVER_CLASS)> *display)
{
const int16_t line_height = 75;
display->setRotation(1);
display->setTextColor(GxEPD_BLACK);
@ -248,10 +275,10 @@ static void draw_epaper_callback(GxEPD2_DISPLAY_CLASS<GxEPD2_DRIVER_CLASS, MAX_H
display->setFont(&FreeSans12pt7b);
display->print(" ppm");
epaper_plot(150, 5, 150, 65, &ts_scd30_co2, 900);
epaper_plot(150, 0*line_height + 5, 150, 65, &ts_scd30_co2, 900);
// second line: temperature
y = 75 + FreeSans12pt7b.yAdvance*3/4;
y = 1*line_height + FreeSans12pt7b.yAdvance*3/4;
display->setCursor(0, y);
display->setFont(&FreeSans12pt7b);
@ -261,7 +288,7 @@ static void draw_epaper_callback(GxEPD2_DISPLAY_CLASS<GxEPD2_DRIVER_CLASS, MAX_H
display->setCursor(0, y);
display->setFont(&FreeSansBold12pt7b);
display->print(scd30.temperature, 2);
display->print(bme680.temperature, 2);
display->setFont(&FreeSans12pt7b);
display->print(" ");
@ -277,10 +304,10 @@ static void draw_epaper_callback(GxEPD2_DISPLAY_CLASS<GxEPD2_DRIVER_CLASS, MAX_H
display->setCursor(cursor_x, stored_baseline_y);
display->print("C");
epaper_plot(150, 80, 150, 65, &ts_scd30_temperature, 900);
epaper_plot(150, 1*line_height + 5, 150, 65, &ts_bme680_temperature, 900);
// second line: Humidity
y = 150 + FreeSans12pt7b.yAdvance*3/4;
// third line: Humidity
y = 2*line_height + FreeSans12pt7b.yAdvance*3/4;
display->setCursor(0, y);
display->setFont(&FreeSans12pt7b);
@ -290,21 +317,57 @@ static void draw_epaper_callback(GxEPD2_DISPLAY_CLASS<GxEPD2_DRIVER_CLASS, MAX_H
display->setCursor(0, y);
display->setFont(&FreeSansBold12pt7b);
display->print(scd30.relative_humidity, 1);
display->print(bme680.humidity, 1);
display->setFont(&FreeSans12pt7b);
display->print(" %rH");
epaper_plot(150, 155, 150, 65, &ts_scd30_humidity, 900);
epaper_plot(150, 2*line_height + 5, 150, 65, &ts_bme680_humidity, 900);
y = 225 + FreeSans12pt7b.yAdvance*3/4;
// fourth line: Pressure
y = 3*line_height + FreeSans12pt7b.yAdvance*3/4;
display->setCursor(0, y);
display->setFont(&FreeSans12pt7b);
display->print("Druck: ");
y += FreeSans12pt7b.yAdvance;
display->setCursor(0, y);
display->setFont(&FreeSansBold12pt7b);
display->print(bme680.pressure / 100.0, 1);
display->setFont(&FreeSans12pt7b);
display->print(" hPa");
epaper_plot(150, 3*line_height + 5, 150, 65, &ts_bme680_pressure, 900);
// fifth line: Gas resistance
y = 4*line_height + FreeSans12pt7b.yAdvance*3/4;
display->setCursor(0, y);
display->setFont(&FreeSans12pt7b);
display->print("Gas: ");
y += FreeSans12pt7b.yAdvance;
display->setCursor(0, y);
display->setFont(&FreeSansBold12pt7b);
display->print(bme680.gas_resistance * 1e-3, 2);
display->setFont(&FreeSans12pt7b);
display->print(" kOhm");
epaper_plot(150, 4*line_height + 5, 150, 65, &ts_bme680_gas_resistance, 900);
y = 5*line_height + FreeSans12pt7b.yAdvance*3/4;
display->setCursor(0, y);
display->setFont(&FreeSans12pt7b);
display->print("Aktualisiert: ");
y += FreeSans12pt7b.yAdvance;
display->setCursor(0, y);
//y += FreeSans12pt7b.yAdvance;
//display->setCursor(0, y);
display->setFont(&FreeSansBold12pt7b);
display->print(timeClient.getFormattedTime());
@ -318,7 +381,12 @@ static void upload_data(void)
String json_string = String("{"
"\"scd30_co2\":") + scd30.CO2
+ ", \"scd30_temperature\":" + scd30.temperature
+ ", \"scd30_humidity\":" + scd30.relative_humidity + "}";
+ ", \"scd30_humidity\":" + scd30.relative_humidity
+ ", \"bme680_temperature\":" + bme680.temperature
+ ", \"bme680_humidity\":" + bme680.humidity
+ ", \"bme680_pressure\":" + bme680.pressure
+ ", \"bme680_gas_resistance\":" + bme680.gas_resistance
+ "}";
// send the data to graphite
HTTPClient client;
@ -364,6 +432,32 @@ void loop(void)
first_scd30_readout = false;
}
// read synchronously from the bme680. This takes some time (100s of
// milliseconds), but the SCD30 is running autonomously, so this does not
// disturb that measurement.
if(bme680.performReading()) {
timeseries_append(&ts_bme680_temperature, bme680.temperature);
timeseries_append(&ts_bme680_humidity, bme680.humidity);
timeseries_append(&ts_bme680_pressure, bme680.pressure / 100.0);
timeseries_append(&ts_bme680_gas_resistance, bme680.gas_resistance / 1000.0);
Serial.println("BME680:");
Serial.print("Temperature: ");
Serial.print(bme680.temperature, 3);
Serial.println(" °C");
Serial.print("Rel. humidity: ");
Serial.print(bme680.humidity, 2);
Serial.println(" %");
Serial.print("Pressure: ");
Serial.print(bme680.pressure / 100.0, 2);
Serial.println(" hPa");
Serial.print("Gas resistance: ");
Serial.print(bme680.gas_resistance);
Serial.println(" Ohm");
Serial.println("");
}
Serial.println("SCD30:");
Serial.print("CO2: ");
Serial.print(scd30.CO2, 3);
Serial.println(" ppm");
@ -403,9 +497,14 @@ void loop(void)
epaper_draw_and_hibernate(draw_epaper_callback, false);
Serial.println("Display updated.");
timeseries_prune(&ts_scd30_humidity, 3600); // keep the last hour
timeseries_prune(&ts_scd30_temperature, 3600); // keep the last hour
timeseries_prune(&ts_scd30_co2, 3600); // keep the last hour
// keep the last hour
timeseries_prune(&ts_scd30_humidity, 3600);
timeseries_prune(&ts_scd30_temperature, 3600);
timeseries_prune(&ts_scd30_co2, 3600);
timeseries_prune(&ts_bme680_temperature, 3600);
timeseries_prune(&ts_bme680_humidity, 3600);
timeseries_prune(&ts_bme680_pressure, 3600);
timeseries_prune(&ts_bme680_gas_resistance, 3600);
}
delay(1000);