From 9cd8acab4361530be9910ecc0f1521445c56ca47 Mon Sep 17 00:00:00 2001 From: Christian Bartsch <22025013+chbartsch@users.noreply.github.com> Date: Sun, 3 Apr 2022 22:30:37 +0200 Subject: [PATCH] Usermod: Add support for Si7021 temperature and humidity sensors (#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 --- usermods/Si7021_MQTT_HA/readme.md | 69 +++++ .../Si7021_MQTT_HA/usermod_si7021_mqtt_ha.h | 236 ++++++++++++++++++ wled00/const.h | 1 + wled00/usermods_list.cpp | 8 + 4 files changed, 314 insertions(+) create mode 100644 usermods/Si7021_MQTT_HA/readme.md create mode 100644 usermods/Si7021_MQTT_HA/usermod_si7021_mqtt_ha.h diff --git a/usermods/Si7021_MQTT_HA/readme.md b/usermods/Si7021_MQTT_HA/readme.md new file mode 100644 index 00000000..d9a96f7a --- /dev/null +++ b/usermods/Si7021_MQTT_HA/readme.md @@ -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 \ No newline at end of file diff --git a/usermods/Si7021_MQTT_HA/usermod_si7021_mqtt_ha.h b/usermods/Si7021_MQTT_HA/usermod_si7021_mqtt_ha.h new file mode 100644 index 00000000..71c22da1 --- /dev/null +++ b/usermods/Si7021_MQTT_HA/usermod_si7021_mqtt_ha.h @@ -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 +#include // 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"; diff --git a/wled00/const.h b/wled00/const.h index b4d4fbe0..6b9517d9 100644 --- a/wled00/const.h +++ b/wled00/const.h @@ -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 diff --git a/wled00/usermods_list.cpp b/wled00/usermods_list.cpp index 453ce895..6e8f6999 100644 --- a/wled00/usermods_list.cpp +++ b/wled00/usermods_list.cpp @@ -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() { /* @@ -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 }