Usermod SensorsToMqtt: Add bmp280+ccs811+si7021 sensors to wled and publish them via MQTT to Home Assistant (#1588)

* Adding SensorsToMqtt usermod, which allows WLED to read bmp280/SI7021/CCS811 sensors and publish it via MQTT to e.g. Home Assistant.

* undo some changes we dont want upstream

* undo some changes we dont want upstream2

Co-authored-by: Machiel Pronk <machiel.pronk89@gmail.com>
This commit is contained in:
ChessSpider 2020-12-29 19:35:59 +01:00 committed by GitHub
parent ad6c154eb6
commit a6316b40d1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 389 additions and 7 deletions

View File

@ -120,6 +120,7 @@ build_flags =
-D DECODE_SAMSUNG=true
-D DECODE_LG=true
-DWLED_USE_MY_CONFIG
; -D USERMOD_SENSORSTOMQTT
build_unflags =
-Wall
@ -210,6 +211,10 @@ lib_deps =
#milesburton/DallasTemperature@^3.9.0
#For BME280 sensor uncomment following
#BME280@~3.0.0
; adafruit/Adafruit BMP280 Library @ 2.1.0
; adafruit/Adafruit CCS811 Library @ 1.0.4
; adafruit/Adafruit Si7021 Library @ 1.4.0
lib_ignore =
AsyncTCP

View File

@ -0,0 +1,87 @@
# Sensors To Home Assistant (or mqtt)
This usermod will publish values of the BMP280, CCS811 and Si7021 sensors to Home Assistant via MQTT.
Its using home assistant automatic device discovery feature.
The use of Home Assistant is not mandatory; it will publish the sensor values via MQTT just fine without it.
Its resusing the mqtt connection set in the WLED web user interface.
## Maintainer
twitter.com/mpronk89
## Features
- Reads BMP280, CCS811 and Si7021 senors
- Publishes via MQTT, configured via webui of wled
- Announces device in Home Assistant for easy setup
- Efficient energy usage
- Updates every 60 seconds
## Example mqtt topics:
`$mqttDeviceTopic` is set in webui of WLED!
```
temperature: $mqttDeviceTopic/temperature
pressure: $mqttDeviceTopic/pressure
humidity: $mqttDeviceTopic/humidity
tvoc: $mqttDeviceTopic/tvoc
eCO2: $mqttDeviceTopic/eco2
IAQ: $mqttDeviceTopic/iaq
```
# Installation
## Hardware
### Requirements
1. BMP280/CCS811/Si7021 sensor. E.g. https://aliexpress.com/item/32979998543.html
2. A microcontroller which can talk i2c, e.g. esp32
### installation
Attach the sensor to the i2c interface.
Default PINs esp32:
```
SCL_PIN = 22;
SDA_PIN = 21;
```
Default PINs ESP8266:
```
SCL_PIN = 5;
SDA_PIN = 4;
```
## Enable in WLED
1. Copy `usermod_v2_SensorsToMqtt.h` into the `wled00` directory.
2. Add to `build_flags` in platformio.ini:
```
-D USERMOD_SENSORSTOMQTT
```
3. And add to `lib_deps` in platformio.ini:
```
adafruit/Adafruit BMP280 Library @ 2.1.0
adafruit/Adafruit CCS811 Library @ 1.0.4
adafruit/Adafruit Si7021 Library @ 1.4.0
```
The #ifdefs in `usermods_list.cpp` should do the rest :)
# Credits
- Aircoookie for making WLED
- Other usermod creators for example code
- Bouke_Regnerus for https://community.home-assistant.io/t/example-indoor-air-quality-text-sensor-using-ccs811-sensor/125854
- You, for reading this

View File

@ -0,0 +1,284 @@
#pragma once
#include "wled.h"
#include <Arduino.h>
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BMP280.h>
#include <Adafruit_CCS811.h>
#include <Adafruit_Si7021.h>
Adafruit_BMP280 bmp;
Adafruit_Si7021 si7021;
Adafruit_CCS811 ccs811;
#ifdef ARDUINO_ARCH_ESP32 //ESP32 boards
uint8_t SCL_PIN = 22;
uint8_t SDA_PIN = 21;
#else //ESP8266 boards
uint8_t SCL_PIN = 5;
uint8_t SDA_PIN = 4;
#endif
class UserMod_SensorsToMQTT : public Usermod
{
private:
bool initialized = false;
bool mqttInitialized = false;
float SensorPressure = 0;
float SensorTemperature = 0;
float SensorHumidity = 0;
char *SensorIaq = "Unknown";
String mqttTemperatureTopic = "";
String mqttHumidityTopic = "";
String mqttPressureTopic = "";
String mqttTvocTopic = "";
String mqttEco2Topic = "";
String mqttIaqTopic = "";
unsigned int SensorTvoc = 0;
unsigned int SensorEco2 = 0;
unsigned long nextMeasure = 0;
void _initialize()
{
initialized = bmp.begin(BMP280_ADDRESS_ALT);
bmp.setSampling(Adafruit_BMP280::MODE_NORMAL, /* Operating Mode. */
Adafruit_BMP280::SAMPLING_X16, /* Temp. oversampling */
Adafruit_BMP280::SAMPLING_X16, /* Pressure oversampling */
Adafruit_BMP280::FILTER_X16, /* Filtering. */
Adafruit_BMP280::STANDBY_MS_2000); /* Refresh values every 20 seconds */
initialized &= si7021.begin();
initialized &= ccs811.begin();
ccs811.setDriveMode(CCS811_DRIVE_MODE_10SEC); /* Refresh values every 10s */
Serial.print(initialized);
}
void _mqttInitialize()
{
mqttTemperatureTopic = String(mqttDeviceTopic) + "/temperature";
mqttPressureTopic = String(mqttDeviceTopic) + "/pressure";
mqttHumidityTopic = String(mqttDeviceTopic) + "/humidity";
mqttTvocTopic = String(mqttDeviceTopic) + "/tvoc";
mqttEco2Topic = String(mqttDeviceTopic) + "/eco2";
mqttIaqTopic = String(mqttDeviceTopic) + "/iaq";
String t = String("homeassistant/sensor/") + mqttClientID + "/temperature/config";
_createMqttSensor("temperature", mqttTemperatureTopic, "temperature", "°C");
_createMqttSensor("pressure", mqttPressureTopic, "pressure", "hPa");
_createMqttSensor("humidity", mqttHumidityTopic, "humidity", "%");
_createMqttSensor("tvoc", mqttTvocTopic, "", "ppb");
_createMqttSensor("eco2", mqttEco2Topic, "", "ppm");
_createMqttSensor("iaq", mqttIaqTopic, "", "");
}
void _createMqttSensor(const String &name, const String &topic, const String &deviceClass, const String &unitOfMeasurement)
{
String t = String("homeassistant/sensor/") + mqttClientID + "/" + name + "/config";
StaticJsonDocument<300> doc;
doc["name"] = name;
doc["state_topic"] = topic;
doc["unique_id"] = String(mqttClientID) + name;
if (unitOfMeasurement != "")
doc["unit_of_measurement"] = unitOfMeasurement;
if (deviceClass != "")
doc["device_class"] = deviceClass;
doc["expire_after"] = 1800;
JsonObject device = doc.createNestedObject("device"); // attach the sensor to the same device
device["identifiers"] = String("wled-sensor-") + mqttClientID;
device["manufacturer"] = "Aircoookie";
device["model"] = "WLED";
device["sw_version"] = VERSION;
device["name"] = mqttClientID;
String temp;
serializeJson(doc, temp);
Serial.println(t);
Serial.println(temp);
mqtt->publish(t.c_str(), 0, true, temp.c_str());
}
void _updateSensorData()
{
SensorTemperature = bmp.readTemperature();
SensorHumidity = si7021.readHumidity();
SensorPressure = (bmp.readPressure() / 100.0F);
ccs811.setEnvironmentalData(SensorHumidity, SensorTemperature);
ccs811.readData();
SensorTvoc = ccs811.getTVOC();
SensorEco2 = ccs811.geteCO2();
SensorIaq = _getIaqIndex(SensorHumidity, SensorTvoc, SensorEco2);
Serial.printf("%f c, %f humidity, %f hPA, %u tvoc, %u Eco2, %s iaq\n",
SensorTemperature, SensorHumidity, SensorPressure,
SensorTvoc, SensorEco2, SensorIaq);
}
/**
* Credits: Bouke_Regnerus @ https://community.home-assistant.io/t/example-indoor-air-quality-text-sensor-using-ccs811-sensor/125854
*/
char *_getIaqIndex(float humidity, int tvoc, int eco2)
{
int iaq_index = 0;
/*
* Transform indoor humidity values to IAQ points according to Indoor Air Quality UK:
* http://www.iaquk.org.uk/
*/
if (humidity < 10 or humidity > 90)
{
iaq_index += 1;
}
else if (humidity < 20 or humidity > 80)
{
iaq_index += 2;
}
else if (humidity < 30 or humidity > 70)
{
iaq_index += 3;
}
else if (humidity < 40 or humidity > 60)
{
iaq_index += 4;
}
else if (humidity >= 40 and humidity <= 60)
{
iaq_index += 5;
}
/*
* Transform eCO2 values to IAQ points according to Indoor Air Quality UK:
* http://www.iaquk.org.uk/
*/
if (eco2 <= 600)
{
iaq_index += 5;
}
else if (eco2 <= 800)
{
iaq_index += 4;
}
else if (eco2 <= 1500)
{
iaq_index += 3;
}
else if (eco2 <= 1800)
{
iaq_index += 2;
}
else if (eco2 > 1800)
{
iaq_index += 1;
}
/*
* Transform TVOC values to IAQ points according to German environmental guidelines:
* https://www.repcomsrl.com/wp-content/uploads/2017/06/Environmental_Sensing_VOC_Product_Brochure_EN.pdf
*/
if (tvoc <= 65)
{
iaq_index += 5;
}
else if (tvoc <= 220)
{
iaq_index += 4;
}
else if (tvoc <= 660)
{
iaq_index += 3;
}
else if (tvoc <= 2200)
{
iaq_index += 2;
}
else if (tvoc > 2200)
{
iaq_index += 1;
}
if (iaq_index <= 6)
{
return "Unhealty";
}
else if (iaq_index <= 9)
{
return "Poor";
}
else if (iaq_index <= 12)
{
return "Moderate";
}
else if (iaq_index <= 14)
{
return "Good";
}
else if (iaq_index > 14)
{
return "Excellent";
}
}
public:
void setup()
{
Serial.println("Starting!");
Wire.begin(SDA_PIN, SCL_PIN);
Serial.println("Initializing sensors.. ");
_initialize();
}
// gets called every time WiFi is (re-)connected.
void connected()
{
nextMeasure = millis() + 5000; // Schedule next measure in 5 seconds
}
void loop()
{
unsigned long tempTimer = millis();
if (tempTimer > nextMeasure)
{
nextMeasure = tempTimer + 60000; // Schedule next measure in 60 seconds
if (!initialized)
{
Serial.println("Error! Sensors not initialized in loop()!");
_initialize();
return; // lets try again next loop
}
if (mqtt != nullptr && mqtt->connected())
{
if (!mqttInitialized)
{
_mqttInitialize();
mqttInitialized = true;
}
// Update sensor data
_updateSensorData();
// Create string populated with user defined device topic from the UI,
// and the read temperature, humidity and pressure.
// Then publish to MQTT server.
mqtt->publish(mqttTemperatureTopic.c_str(), 0, true, String(SensorTemperature).c_str());
mqtt->publish(mqttPressureTopic.c_str(), 0, true, String(SensorPressure).c_str());
mqtt->publish(mqttHumidityTopic.c_str(), 0, true, String(SensorHumidity).c_str());
mqtt->publish(mqttTvocTopic.c_str(), 0, true, String(SensorTvoc).c_str());
mqtt->publish(mqttEco2Topic.c_str(), 0, true, String(SensorEco2).c_str());
mqtt->publish(mqttIaqTopic.c_str(), 0, true, String(SensorIaq).c_str());
}
else
{
Serial.println("Missing MQTT connection. Not publishing data");
mqttInitialized = false;
}
}
}
};

View File

@ -17,6 +17,9 @@
#ifdef USERMOD_BUZZER
#include "../usermods/buzzer/usermod_v2_buzzer.h"
#endif
#ifdef USERMOD_SENSORSTOMQTT
#include "usermod_v2_SensorsToMqtt.h"
#endif
void registerUsermods()
{
@ -33,4 +36,7 @@ void registerUsermods()
#ifdef USERMOD_BUZZER
usermods.add(new BuzzerUsermod());
#endif
#ifdef USERMOD_SENSORSTOMQTT
usermods.add(new UserMod_SensorsToMQTT());
#endif
}