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:
parent
ad6c154eb6
commit
a6316b40d1
@ -120,6 +120,7 @@ build_flags =
|
|||||||
-D DECODE_SAMSUNG=true
|
-D DECODE_SAMSUNG=true
|
||||||
-D DECODE_LG=true
|
-D DECODE_LG=true
|
||||||
-DWLED_USE_MY_CONFIG
|
-DWLED_USE_MY_CONFIG
|
||||||
|
; -D USERMOD_SENSORSTOMQTT
|
||||||
|
|
||||||
build_unflags =
|
build_unflags =
|
||||||
-Wall
|
-Wall
|
||||||
@ -210,6 +211,10 @@ lib_deps =
|
|||||||
#milesburton/DallasTemperature@^3.9.0
|
#milesburton/DallasTemperature@^3.9.0
|
||||||
#For BME280 sensor uncomment following
|
#For BME280 sensor uncomment following
|
||||||
#BME280@~3.0.0
|
#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 =
|
lib_ignore =
|
||||||
AsyncTCP
|
AsyncTCP
|
||||||
|
|
||||||
|
87
usermods/sensors_to_mqtt/readme.md
Normal file
87
usermods/sensors_to_mqtt/readme.md
Normal 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
|
284
usermods/sensors_to_mqtt/usermod_v2_SensorsToMqtt.h
Normal file
284
usermods/sensors_to_mqtt/usermod_v2_SensorsToMqtt.h
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
@ -17,6 +17,9 @@
|
|||||||
#ifdef USERMOD_BUZZER
|
#ifdef USERMOD_BUZZER
|
||||||
#include "../usermods/buzzer/usermod_v2_buzzer.h"
|
#include "../usermods/buzzer/usermod_v2_buzzer.h"
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USERMOD_SENSORSTOMQTT
|
||||||
|
#include "usermod_v2_SensorsToMqtt.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
void registerUsermods()
|
void registerUsermods()
|
||||||
{
|
{
|
||||||
@ -33,4 +36,7 @@ void registerUsermods()
|
|||||||
#ifdef USERMOD_BUZZER
|
#ifdef USERMOD_BUZZER
|
||||||
usermods.add(new BuzzerUsermod());
|
usermods.add(new BuzzerUsermod());
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USERMOD_SENSORSTOMQTT
|
||||||
|
usermods.add(new UserMod_SensorsToMQTT());
|
||||||
|
#endif
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user