Skip to content

Commit

Permalink
Usermod: Add support for Si7021 temperature and humidity sensors (Air…
Browse files Browse the repository at this point in the history
…coookie#2617)

* added first version (work in progress)

* added some sensors to publish

* typo

* added dependency

* mqtt si7021_* names + don't  retain

* timer to 60 s

* some changes to HA auto discovery

* added config entries (no function yet)

* renaming

* made configs work

* added getId()

* refactoring + wrong mqtt topics fixed

* retain HA auto discovery

* do not spam serial console on each sensor update

* added readme

* add update interval info

Co-authored-by: Christian Schwinne <[email protected]>
  • Loading branch information
chbartsch and Aircoookie authored Apr 3, 2022
1 parent 8b79a97 commit 9cd8aca
Show file tree
Hide file tree
Showing 4 changed files with 314 additions and 0 deletions.
69 changes: 69 additions & 0 deletions usermods/Si7021_MQTT_HA/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Si7021 to MQTT (with Home Assistant Auto Discovery) usermod

This usermod implements support for [Si7021 I²C temperature and humidity sensors](https://www.silabs.com/documents/public/data-sheets/Si7021-A20.pdf).

The sensor data will *not* be shown on the WLED UI (so far) but published via MQTT to WLED's "build in" MQTT device topic.

```
temperature: $mqttDeviceTopic/si7021_temperature
humidity: $mqttDeviceTopic/si7021_humidity
```

Additionally the following sensors can be published:

```
heat_index: $mqttDeviceTopic/si7021_heat_index
dew_point: $mqttDeviceTopic/si7021_dew_point
absolute_humidity: $mqttDeviceTopic/si7021_absolute_humidity
```

Sensor data will be updated/send every 60 seconds.

This usermod also supports Home Assistant Auto Discovery.

## Settings via Usermod Setup

- `enabled`: Enables this usermod
- `Send Dew Point, Abs. Humidity and Heat Index`: Enables additional sensors
- `Home Assistant MQTT Auto-Discovery`: Enables Home Assistant Auto Discovery

# Installation

## Hardware

Attach the Si7021 sensor to the I²C interface.

Default PINs ESP32:

```
SCL_PIN = 22;
SDA_PIN = 21;
```

Default PINs ESP8266:

```
SCL_PIN = 5;
SDA_PIN = 4;
```

## Software

Add to `build_flags` in platformio.ini:

```
-D USERMOD_SI7021_MQTT_HA
```

Add to `lib_deps` in platformio.ini:

```
adafruit/Adafruit Si7021 Library @ 1.4.0
BME280@~3.0.0
```

# Credits

- Aircoookie for making WLED
- Other usermod creators for example code (`sensors_to_mqtt` and `multi_relay` especially)
- You, for reading this
236 changes: 236 additions & 0 deletions usermods/Si7021_MQTT_HA/usermod_si7021_mqtt_ha.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,236 @@
#pragma once

// this is remixed from usermod_v2_SensorsToMqtt.h (sensors_to_mqtt usermod)
// and usermod_multi_relay.h (multi_relay usermod)

#include "wled.h"
#include <Adafruit_Si7021.h>
#include <EnvironmentCalculations.h> // EnvironmentCalculations::HeatIndex(), ::DewPoint(), ::AbsoluteHumidity()

Adafruit_Si7021 si7021;

#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 Si7021_MQTT_HA : public Usermod
{
private:
bool sensorInitialized = false;
bool mqttInitialized = false;
float sensorTemperature = 0;
float sensorHumidity = 0;
float sensorHeatIndex = 0;
float sensorDewPoint = 0;
float sensorAbsoluteHumidity= 0;
String mqttTemperatureTopic = "";
String mqttHumidityTopic = "";
String mqttHeatIndexTopic = "";
String mqttDewPointTopic = "";
String mqttAbsoluteHumidityTopic = "";
unsigned long nextMeasure = 0;
bool enabled = false;
bool haAutoDiscovery = true;
bool sendAdditionalSensors = true;

// strings to reduce flash memory usage (used more than twice)
static const char _name[];
static const char _enabled[];
static const char _sendAdditionalSensors[];
static const char _haAutoDiscovery[];

void _initializeSensor()
{
sensorInitialized = si7021.begin();
Serial.printf("Si7021_MQTT_HA: sensorInitialized = %d\n", sensorInitialized);
}

void _initializeMqtt()
{
mqttTemperatureTopic = String(mqttDeviceTopic) + "/si7021_temperature";
mqttHumidityTopic = String(mqttDeviceTopic) + "/si7021_humidity";
mqttHeatIndexTopic = String(mqttDeviceTopic) + "/si7021_heat_index";
mqttDewPointTopic = String(mqttDeviceTopic) + "/si7021_dew_point";
mqttAbsoluteHumidityTopic = String(mqttDeviceTopic) + "/si7021_absolute_humidity";

// Update and publish sensor data
_updateSensorData();
_publishSensorData();

if (haAutoDiscovery) {
_publishHAMqttSensor("temperature", "Temperature", mqttTemperatureTopic, "temperature", "°C");
_publishHAMqttSensor("humidity", "Humidity", mqttHumidityTopic, "humidity", "%");
if (sendAdditionalSensors) {
_publishHAMqttSensor("heat_index", "Heat Index", mqttHeatIndexTopic, "temperature", "°C");
_publishHAMqttSensor("dew_point", "Dew Point", mqttDewPointTopic, "", "°C");
_publishHAMqttSensor("absolute_humidity", "Absolute Humidity", mqttAbsoluteHumidityTopic, "", "g/m³");
}
}

mqttInitialized = true;
}

void _publishHAMqttSensor(
const String &name,
const String &friendly_name,
const String &state_topic,
const String &deviceClass,
const String &unitOfMeasurement)
{
if (WLED_MQTT_CONNECTED) {
String topic = String("homeassistant/sensor/") + mqttClientID + "/" + name + "/config";

StaticJsonDocument<300> doc;

doc["name"] = String(serverDescription) + " " + friendly_name;
doc["state_topic"] = state_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["name"] = String(serverDescription);
device["model"] = "WLED";
device["manufacturer"] = "Aircoookie";
device["identifiers"] = String("wled-") + String(serverDescription);
device["sw_version"] = VERSION;

String payload;
serializeJson(doc, payload);

mqtt->publish(topic.c_str(), 0, true, payload.c_str());
}
}

void _updateSensorData()
{
sensorTemperature = si7021.readTemperature();
sensorHumidity = si7021.readHumidity();

// Serial.print("Si7021_MQTT_HA: Temperature: ");
// Serial.print(sensorTemperature, 2);
// Serial.print("\tHumidity: ");
// Serial.print(sensorHumidity, 2);

if (sendAdditionalSensors) {
EnvironmentCalculations::TempUnit envTempUnit(EnvironmentCalculations::TempUnit_Celsius);
sensorHeatIndex = EnvironmentCalculations::HeatIndex(sensorTemperature, sensorHumidity, envTempUnit);
sensorDewPoint = EnvironmentCalculations::DewPoint(sensorTemperature, sensorHumidity, envTempUnit);
sensorAbsoluteHumidity = EnvironmentCalculations::AbsoluteHumidity(sensorTemperature, sensorHumidity, envTempUnit);

// Serial.print("\tHeat Index: ");
// Serial.print(sensorHeatIndex, 2);
// Serial.print("\tDew Point: ");
// Serial.print(sensorDewPoint, 2);
// Serial.print("\tAbsolute Humidity: ");
// Serial.println(sensorAbsoluteHumidity, 2);
}
// else
// Serial.println("");
}

void _publishSensorData()
{
if (WLED_MQTT_CONNECTED) {
mqtt->publish(mqttTemperatureTopic.c_str(), 0, false, String(sensorTemperature).c_str());
mqtt->publish(mqttHumidityTopic.c_str(), 0, false, String(sensorHumidity).c_str());
if (sendAdditionalSensors) {
mqtt->publish(mqttHeatIndexTopic.c_str(), 0, false, String(sensorHeatIndex).c_str());
mqtt->publish(mqttDewPointTopic.c_str(), 0, false, String(sensorDewPoint).c_str());
mqtt->publish(mqttAbsoluteHumidityTopic.c_str(), 0, false, String(sensorAbsoluteHumidity).c_str());
}
}
}

public:
void addToConfig(JsonObject& root)
{
JsonObject top = root.createNestedObject(FPSTR(_name));

top[FPSTR(_enabled)] = enabled;
top[FPSTR(_sendAdditionalSensors)] = sendAdditionalSensors;
top[FPSTR(_haAutoDiscovery)] = haAutoDiscovery;
}

bool readFromConfig(JsonObject& root)
{
JsonObject top = root[FPSTR(_name)];

bool configComplete = !top.isNull();
configComplete &= getJsonValue(top[FPSTR(_enabled)], enabled);
configComplete &= getJsonValue(top[FPSTR(_sendAdditionalSensors)], sendAdditionalSensors);
configComplete &= getJsonValue(top[FPSTR(_haAutoDiscovery)], haAutoDiscovery);

return configComplete;
}

void onMqttConnect(bool sessionPresent) {
if (mqttDeviceTopic[0] != 0)
_initializeMqtt();
}

void setup()
{
if (enabled) {
Serial.println("Si7021_MQTT_HA: Starting!");
Wire.begin(SDA_PIN, SCL_PIN);
Serial.println("Si7021_MQTT_HA: Initializing sensors.. ");
_initializeSensor();
}
}

// gets called every time WiFi is (re-)connected.
void connected()
{
nextMeasure = millis() + 5000; // Schedule next measure in 5 seconds
}

void loop()
{
yield();
if (!enabled || strip.isUpdating()) return; // !sensorFound ||

unsigned long tempTimer = millis();

if (tempTimer > nextMeasure) {
nextMeasure = tempTimer + 60000; // Schedule next measure in 60 seconds

if (!sensorInitialized) {
Serial.println("Si7021_MQTT_HA: Error! Sensors not initialized in loop()!");
_initializeSensor();
return; // lets try again next loop
}

if (WLED_MQTT_CONNECTED) {
if (!mqttInitialized)
_initializeMqtt();

// Update and publish sensor data
_updateSensorData();
_publishSensorData();
}
else {
Serial.println("Si7021_MQTT_HA: Missing MQTT connection. Not publishing data");
mqttInitialized = false;
}
}
}

uint16_t getId()
{
return USERMOD_ID_SI7021_MQTT_HA;
}
};

// strings to reduce flash memory usage (used more than twice)
const char Si7021_MQTT_HA::_name[] PROGMEM = "Si7021 MQTT (Home Assistant)";
const char Si7021_MQTT_HA::_enabled[] PROGMEM = "enabled";
const char Si7021_MQTT_HA::_sendAdditionalSensors[] PROGMEM = "Send Dew Point, Abs. Humidity and Heat Index";
const char Si7021_MQTT_HA::_haAutoDiscovery[] PROGMEM = "Home Assistant MQTT Auto-Discovery";
1 change: 1 addition & 0 deletions wled00/const.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@
#define USERMOD_ID_WIZLIGHTS 26 //Usermod "wizlights.h"
#define USERMOD_ID_WORDCLOCK 27 //Usermod "usermod_v2_word_clock.h"
#define USERMOD_ID_MY9291 28 //Usermod "usermod_MY9291.h"
#define USERMOD_ID_SI7021_MQTT_HA 29 //Usermod "usermod_si7021_mqtt_ha.h"

//Access point behavior
#define AP_BEHAVIOR_BOOT_NO_CONN 0 //Open AP when no connection after boot
Expand Down
8 changes: 8 additions & 0 deletions wled00/usermods_list.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,10 @@
#include "../usermods/MY9291/usermode_MY9291.h"
#endif

#ifdef USERMOD_SI7021_MQTT_HA
#include "../usermods/Si7021_MQTT_HA/usermod_si7021_mqtt_ha.h"
#endif

void registerUsermods()
{
/*
Expand Down Expand Up @@ -235,4 +239,8 @@ void registerUsermods()
#ifdef USERMOD_MY9291
usermods.add(new MY9291Usermod());
#endif

#ifdef USERMOD_SI7021_MQTT_HA
usermods.add(new Si7021_MQTT_HA());
#endif
}

0 comments on commit 9cd8aca

Please sign in to comment.