Skip to content

Commit

Permalink
Usermod SensorsToMqtt: Add bmp280+ccs811+si7021 sensors to wled and p…
Browse files Browse the repository at this point in the history
…ublish them via MQTT to Home Assistant (wled#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 <[email protected]>
  • Loading branch information
ChessSpider and mpronk authored Dec 29, 2020
1 parent ad6c154 commit a6316b4
Show file tree
Hide file tree
Showing 4 changed files with 389 additions and 7 deletions.
5 changes: 5 additions & 0 deletions platformio.ini
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ build_flags =
-D DECODE_SAMSUNG=true
-D DECODE_LG=true
-DWLED_USE_MY_CONFIG
; -D USERMOD_SENSORSTOMQTT

build_unflags =
-Wall
Expand Down Expand Up @@ -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

Expand Down
87 changes: 87 additions & 0 deletions usermods/sensors_to_mqtt/readme.md
Original file line number Diff line number Diff line change
@@ -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 changes: 284 additions & 0 deletions usermods/sensors_to_mqtt/usermod_v2_SensorsToMqtt.h
Original file line number Diff line number Diff line change
@@ -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;
}
}
}
};
Loading

0 comments on commit a6316b4

Please sign in to comment.