From 39601102908b5b95cf8210ba38b85e2df8b3a64a Mon Sep 17 00:00:00 2001 From: ShonP40 <13995143+ShonP40@users.noreply.github.com> Date: Sun, 13 Feb 2022 12:45:58 +0200 Subject: [PATCH] I2C BME280 & TSL2561 support + two I2C buses (#158) * I2C BME280 support * Support for 2 I2C busses * I2C TSL2561 support * Ability to set the TSL2561 gain manually --- .gitignore | 1 + platformio.ini | 36 +++++---- src/defaults.h | 8 ++ src/main.cpp | 197 ++++++++++++++++++++++++++++++++++++++++++++++--- src/main.h | 141 ++++++++++++++++++++++++++++++++++- 5 files changed, 356 insertions(+), 27 deletions(-) diff --git a/.gitignore b/.gitignore index 9c9eb18..34863ac 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,4 @@ Settings_*.h .scripts .DS_Store cmake-build-* +build/ \ No newline at end of file diff --git a/platformio.ini b/platformio.ini index 37f1ca4..ad2021a 100644 --- a/platformio.ini +++ b/platformio.ini @@ -14,7 +14,7 @@ default_envs = esp32 [common_env_data] platform = espressif32@3.5 framework = arduino -lib_deps_external = +lib_deps_external = haimoz/SoftFilters@^0.1.0 marvinroger/AsyncMqttClient@^0.9.0 bblanchon/ArduinoJson@^6.17.3 @@ -30,28 +30,32 @@ lib_deps_external = adafruit/Adafruit Unified Sensor @ ^1.1.4 beegee-tokyo/DHT sensor library for ESPx @ ^1.18 starmbi/hp_BH1750 @ ^1.0.0 + adafruit/Adafruit BME280 Library@^2.2.2 + adafruit/Adafruit TSL2561@^1.1.0 [env:esp32] platform = ${common_env_data.platform} framework = ${common_env_data.framework} board = esp32dev -lib_deps = ${common_env_data.lib_deps_external} +lib_deps = + ${common_env_data.lib_deps_external} board_build.partitions = partitions_singleapp.csv monitor_speed = 115200 upload_speed = 1500000 monitor_filters = esp32_exception_decoder -build_flags = +build_flags = -D FIRMWARE='"esp32"' [env:adafruit-huzzah32] platform = ${common_env_data.platform} framework = ${common_env_data.framework} board = esp32dev -lib_deps = ${common_env_data.lib_deps_external} +lib_deps = + ${common_env_data.lib_deps_external} board_build.partitions = partitions_singleapp.csv monitor_speed = 115200 monitor_filters = esp32_exception_decoder -build_flags = +build_flags = -D FIRMWARE='"adafruit-huzzah32"' -D HUZZAH32 @@ -59,12 +63,13 @@ build_flags = platform = ${common_env_data.platform} framework = ${common_env_data.framework} board = esp32dev -lib_deps = ${common_env_data.lib_deps_external} +lib_deps = + ${common_env_data.lib_deps_external} board_build.partitions = partitions_singleapp.csv monitor_speed = 115200 upload_speed = 1500000 monitor_filters = esp32_exception_decoder -build_flags = +build_flags = -D CORE_DEBUG_LEVEL=2 -D FIRMWARE='"esp32-verbose"' -D VERBOSE @@ -73,13 +78,13 @@ build_flags = platform = ${common_env_data.platform} framework = ${common_env_data.framework} board = m5stick-c -lib_deps = +lib_deps = m5stack/M5StickC@^0.2.0 ${common_env_data.lib_deps_external} board_build.partitions = partitions_singleapp.csv monitor_speed = 115200 monitor_filters = esp32_exception_decoder -build_flags = +build_flags = -D M5STICK -D FIRMWARE='"m5stickc"' @@ -87,13 +92,13 @@ build_flags = platform = ${common_env_data.platform} framework = ${common_env_data.framework} board = m5stick-c -lib_deps = +lib_deps = m5stack/M5StickCPlus@^0.0.2 ${common_env_data.lib_deps_external} board_build.partitions = partitions_singleapp.csv monitor_speed = 115200 monitor_filters = esp32_exception_decoder -build_flags = +build_flags = -D M5STICK -D PLUS -D FIRMWARE='"m5stickc-plus"' @@ -102,14 +107,14 @@ build_flags = platform = ${common_env_data.platform} framework = ${common_env_data.framework} board = m5stack-atom -lib_deps = +lib_deps = fastled/FastLED@^3.4.0 m5stack/m5atom@^0.0.5 ${common_env_data.lib_deps_external} board_build.partitions = partitions_singleapp.csv monitor_speed = 115200 monitor_filters = esp32_exception_decoder -build_flags = +build_flags = -D M5ATOM -D MATRIX -D FIRMWARE='"m5atom-matrix"' @@ -118,10 +123,11 @@ build_flags = platform = ${common_env_data.platform} framework = ${common_env_data.framework} board = esp32dev -lib_deps = ${common_env_data.lib_deps_external} +lib_deps = + ${common_env_data.lib_deps_external} board_build.partitions = partitions_singleapp.csv monitor_speed = 115200 -build_flags = +build_flags = -D MACCHINA_A0 -D FIRMWARE='"macchina-a0"' diff --git a/src/defaults.h b/src/defaults.h index 25fbe2e..67e40f9 100644 --- a/src/defaults.h +++ b/src/defaults.h @@ -43,6 +43,14 @@ // Number of seconds between update checks #define CHECK_FOR_UPDATES_INTERVAL 300 +// I2C Defaults +#define DEFAULT_I2C_BUS_1_SDA 21 +#define DEFAULT_I2C_BUS_1_SCL 22 +#define DEFAULT_I2C_BUS 1 + +// TSL2561 Defaults +#define DEFAULT_TSL2561_I2C_GAIN "auto" + #ifdef VERSION #define DEFAULT_AUTO_UPDATE true #define DEFAULT_OTA_UPDATE false diff --git a/src/main.cpp b/src/main.cpp index e8d5cfb..84b7d32 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,4 +1,5 @@ -#include +#include "main.h" + bool sendTelemetry(int totalSeen, int totalFpSeen, int totalFpQueried, int totalFpReported) { if (!online) @@ -18,7 +19,7 @@ bool sendTelemetry(int totalSeen, int totalFpSeen, int totalFpQueried, int total { if (sendDiscoveryConnectivity() && sendDiscoveryUptime() && sendDiscoveryFreeMem() && sendSwitchDiscovery("Status LED", "config") && sendNumberDiscovery("Max Distance", "config") && sendSwitchDiscovery("Active Scan", "config") && sendSwitchDiscovery("Query", "config") && sendDiscoveryMotion() #ifdef SENSORS - && sendDiscoveryHumidity() && sendDiscoveryTemperature() && sendDiscoveryLux() + && sendDiscoveryHumidity() && sendDiscoveryTemperature() && sendDiscoveryLux() && sendDiscoveryBME280Temperature() && sendDiscoveryBME280Humidity() && sendDiscoveryBME280Pressure() && sendDiscoveryTSL2561Lux() #endif ) { @@ -150,9 +151,35 @@ void connectToWifi() dht11Pin = WiFiSettings.integer("dht11_pin", 0, "DHT11 sensor pin (0 for disable)"); dht22Pin = WiFiSettings.integer("dht22_pin", 0, "DHT22 sensor pin (0 for disable)"); - BH1750_I2c = WiFiSettings.string("BH1750_I2c", "", "Ambient Light Sensor - I2C address of BH1750 Sensor, like 0x23 or 0x5C."); - I2CDebug = WiFiSettings.checkbox("I2CDebug", false, "Debug I2C address. Look at the serial log to get the correct address."); + WiFiSettings.heading("I2C Settings"); + + I2CDebug = WiFiSettings.checkbox("I2CDebug", false, "Debug I2C addreses. Look at the serial log to get the correct address"); + + WiFiSettings.html("h4", "Bus 1:"); + I2C_Bus_1_SDA = WiFiSettings.integer("I2C_Bus_1_SDA", 0, 39, DEFAULT_I2C_BUS_1_SDA, "SDA pin (0 to disable)"); + I2C_Bus_1_SCL = WiFiSettings.integer("I2C_Bus_1_SCL", 0, 39, DEFAULT_I2C_BUS_1_SCL, "SCL pin (0 to disable)"); + + WiFiSettings.html("h4", "Bus 2:"); + + I2C_Bus_2_SDA = WiFiSettings.integer("I2C_Bus_2_SDA", 0, "SDA pin (0 to disable)"); + I2C_Bus_2_SCL = WiFiSettings.integer("I2C_Bus_2_SCL", 0, "SCL pin (0 to disable)"); + + WiFiSettings.heading("I2C Sensors"); + + WiFiSettings.html("h4", "BH1750 - Ambient Light Sensor:"); + BH1750_I2c_Bus = WiFiSettings.integer("BH1750_I2c_Bus", 1, 2, DEFAULT_I2C_BUS, "I2C Bus"); + BH1750_I2c = WiFiSettings.string("BH1750_I2c", "", "I2C address (0x23 or 0x5C)"); + + WiFiSettings.html("h4", "BME280 - Weather Sensor:"); + BME280_I2c_Bus = WiFiSettings.integer("BME280_I2c_Bus", 1, 2, DEFAULT_I2C_BUS, "I2C Bus"); + BME280_I2c = WiFiSettings.string("BME280_I2c", "", "I2C address (0x76 or 0x77)"); + + WiFiSettings.html("h4", "TSL2561 - Ambient Light Sensor:"); + TSL2561_I2c_Bus = WiFiSettings.integer("TSL2561_I2c_Bus", 1, 2, DEFAULT_I2C_BUS, "I2C Bus"); + TSL2561_I2c = WiFiSettings.string("TSL2561_I2c", "", "I2C address (0x39, 0x49 or 0x29)"); + TSL2561_I2c_Gain = WiFiSettings.string("TSL2561_I2c_Gain", DEFAULT_TSL2561_I2C_GAIN, "Gain (auto, 1x or 16x)"); #endif + WiFiSettings.hostname = "espresense-" + kebabify(room); if (!WiFiSettings.connect(true, 60)) @@ -188,8 +215,14 @@ void connectToWifi() Serial.print("DHT22 Sensor: "); Serial.println(dht22Pin ? "enabled" : "disabled"); Serial.print("BH1750_I2c Sensor: "); + Serial.println(BH1750_I2c + " on bus " + BH1750_I2c_Bus); + Serial.print("BME280_I2c Sensor: "); + Serial.println(BME280_I2c + " on bus " + BME280_I2c_Bus); + Serial.print("TSL2561_I2c Sensor: "); + Serial.println(TSL2561_I2c + " on bus " + TSL2561_I2c_Bus); Serial.println(BH1750_I2c); #endif + localIp = WiFi.localIP().toString(); id = slugify(room); roomsTopic = CHANNEL + "/rooms/" + id; @@ -472,13 +505,21 @@ void setup() bool state = false; // if (! BH1750.begin(BH1750_TO_GROUND)) - if (BH1750_I2c == "0x23") + if (BH1750_I2c == "0x23" && BH1750_I2c_Bus == 1) { - state = BH1750.begin(BH1750_TO_GROUND); + state = BH1750.begin(BH1750_TO_GROUND, &Wire); } - else if (BH1750_I2c == "0x5C") + else if (BH1750_I2c == "0x5C" && BH1750_I2c_Bus == 1) { - state = BH1750.begin(BH1750_TO_VCC); + state = BH1750.begin(BH1750_TO_VCC, &Wire); + } + else if (BH1750_I2c == "0x23" && BH1750_I2c_Bus == 2) + { + state = BH1750.begin(BH1750_TO_GROUND, &Wire1); + } + else if (BH1750_I2c == "0x5C" && BH1750_I2c_Bus == 2) + { + state = BH1750.begin(BH1750_TO_VCC, &Wire1); } if (!state) @@ -509,9 +550,18 @@ void setup() } } + if (I2C_Bus_1_SDA != 0 && I2C_Bus_1_SDA != 0) { + Wire.begin(I2C_Bus_1_SDA, I2C_Bus_1_SCL); + Serial.println("\nInitialized I2C Bus 1"); + } + + if (I2C_Bus_2_SDA != 0 && I2C_Bus_2_SDA != 0) { + Wire1.begin(I2C_Bus_2_SDA, I2C_Bus_2_SCL); + Serial.println("\nInitialized I2C Bus 2"); + } + if (I2CDebug) { - Wire.begin(); Serial.println("\nI2C Scanner"); } #endif @@ -629,6 +679,102 @@ void luxLoop() } } +void bme280Loop() { + + if (BME280_I2c == "0x76" && BME280_I2c_Bus == 1) { + BME280_status = BME280.begin(0x76, &Wire); + } else if (BME280_I2c == "0x77" && BME280_I2c_Bus == 1) { + BME280_status = BME280.begin(0x77, &Wire); + } else if (BME280_I2c == "0x76" && BME280_I2c_Bus == 2) { + BME280_status = BME280.begin(0x76, &Wire1); + } else if (BME280_I2c == "0x77" && BME280_I2c_Bus == 2) { + BME280_status = BME280.begin(0x77, &Wire1); + } else { + Serial.println("BME280 - Invalid I2C address"); + return; + } + + if (!BME280_status) { + Serial.println("Couldn't find a BME280 sensor, check your wiring and I2C address!"); + } + + + BME280.setSampling(Adafruit_BME280::MODE_NORMAL, + Adafruit_BME280::SAMPLING_X16, // Temperature + Adafruit_BME280::SAMPLING_X16, // Pressure + Adafruit_BME280::SAMPLING_X16, // Humidity + Adafruit_BME280::FILTER_X16, + //Adafruit_BME280::FILTER_OFF, + Adafruit_BME280::STANDBY_MS_1000 + ); + + + float temperature = BME280.readTemperature(); + float humidity = BME280.readHumidity(); + float pressure = BME280.readPressure() / 100.0F; + + if (millis() - bme280PreviousMillis >= sensorInterval) { + + mqttClient.publish((roomsTopic + "/bme280_temperature").c_str(), 0, 1, String(temperature).c_str()); + mqttClient.publish((roomsTopic + "/bme280_humidity").c_str(), 0, 1, String(humidity).c_str()); + mqttClient.publish((roomsTopic + "/bme280_pressure").c_str(), 0, 1, String(pressure).c_str()); + + bme280PreviousMillis = millis(); + } + +} + +void tsl2561Loop() { + + int tsl2561_address; + + if (TSL2561_I2c == "0x39") { + tsl2561_address = 0x39; + } else if (TSL2561_I2c == "0x29") { + tsl2561_address = 0x29; + } else if (TSL2561_I2c == "0x49") { + tsl2561_address = 0x49; + } else { + Serial.println("TSL2561 - Invalid I2C address"); + return; + } + + Adafruit_TSL2561_Unified tsl = Adafruit_TSL2561_Unified(tsl2561_address); + + if (TSL2561_I2c_Bus == 1) { + tsl.begin(&Wire); + } else if (TSL2561_I2c_Bus == 2) { + tsl.begin(&Wire1); + } + + if (TSL2561_I2c_Gain == "auto") { + tsl.enableAutoRange(true); + } else if (TSL2561_I2c_Gain == "1x") { + tsl.setGain(TSL2561_GAIN_1X); + } else if (TSL2561_I2c_Gain == "16x") { + tsl.setGain(TSL2561_GAIN_16X); + } else { + Serial.println("TSL2561 - Invalid gain"); + return; + } + + tsl.setIntegrationTime(TSL2561_INTEGRATIONTIME_402MS); + + sensors_event_t event; + tsl.getEvent(&event); + + if (event.light) { + if (millis() - tsl2561PreviousMillis >= sensorInterval) { + mqttClient.publish((roomsTopic + "/tsl2561_lux").c_str(), 0, 1, String(event.light).c_str()); + + tsl2561PreviousMillis = millis(); + } + } else { + Serial.println("TSL2561 - Sensor overloaded"); + } + +} + void l2cScanner() { if (!I2CDebug) return; @@ -644,7 +790,7 @@ void l2cScanner() error = Wire.endTransmission(); if (error == 0) { - Serial.print("I2C device found at address 0x"); + Serial.print("I2C device found on bus 1 at address 0x"); if (address < 16) { @@ -656,7 +802,34 @@ void l2cScanner() } else if (error == 4) { - Serial.print("Unknow error at address 0x"); + Serial.print("Unknow error on bus 1 at address 0x"); + if (address < 16) + { + Serial.print("0"); + } + Serial.println(address, HEX); + } + } + + for (address = 1; address < 127; address++) + { + Wire1.beginTransmission(address); + error = Wire1.endTransmission(); + if (error == 0) + { + Serial.print("I2C device found on bus 2 at address 0x"); + + if (address < 16) + { + Serial.print("0"); + } + + Serial.println(address, HEX); + nDevices++; + } + else if (error == 4) + { + Serial.print("Unknow error on bus 2 at address 0x"); if (address < 16) { Serial.print("0"); @@ -689,6 +862,8 @@ void loop() #ifdef SENSORS dhtLoop(); luxLoop(); + bme280Loop(); + tsl2561Loop(); l2cScanner(); #endif WiFiSettings.httpLoop(); diff --git a/src/main.h b/src/main.h index 9a33325..0ad9d6e 100644 --- a/src/main.h +++ b/src/main.h @@ -27,13 +27,38 @@ #ifdef SENSORS #include #include +// I2C +int I2C_Bus_1_SDA; +int I2C_Bus_1_SCL; +int I2C_Bus_2_SDA; +int I2C_Bus_2_SCL; +bool I2CDebug; + +unsigned long sensorInterval = 60000; + +//GY-302 lux sensor #include hp_BH1750 BH1750; long ms_BH1750; float lux_BH1750; int lux_BH1750_MQTT; String BH1750_I2c; -bool I2CDebug; +int BH1750_I2c_Bus; + +//I2C BME280 sensor +#include +Adafruit_BME280 BME280; +long BME280_status; +String BME280_I2c; +int BME280_I2c_Bus; +unsigned long bme280PreviousMillis = 0; + +//I2C TSL2561 sensor +#include +String TSL2561_I2c; +int TSL2561_I2c_Bus; +String TSL2561_I2c_Gain; +unsigned long tsl2561PreviousMillis = 0; #endif AsyncMqttClient mqttClient; @@ -473,8 +498,122 @@ bool sendDiscoveryLux() String discoveryTopic = "homeassistant/sensor/espresense_" + ESPMAC + "/lux/config"; return pub(discoveryTopic.c_str(), 0, true, buffer); } + +bool sendDiscoveryBME280Temperature() +{ + if (BME280_I2c.isEmpty()) return true; + + DynamicJsonDocument doc(1200); + commonDiscovery(&doc); + doc["~"] = roomsTopic; + doc["name"] = "ESPresense " + room + " BME280 Temperature"; + doc["uniq_id"] = Sprintf("espresense_%06" PRIx64 "_bme280_temperature", ESP.getEfuseMac() >> 24); + doc["avty_t"] = "~/status"; + doc["stat_t"] = "~/bme280_temperature"; + doc["dev_cla"] = "temperature"; + doc["unit_of_meas"] = "°C"; + doc["frc_upd"] = true; + + char buffer[1200]; + serializeJson(doc, buffer); + String discoveryTopic = "homeassistant/sensor/espresense_" + ESPMAC + "/bme280_temperature/config"; + + for (int i = 0; i < 10; i++) + { + if (mqttClient.publish(discoveryTopic.c_str(), 0, true, buffer)) + return true; + delay(50); + } + return false; +} + +bool sendDiscoveryBME280Humidity() +{ + if (BME280_I2c.isEmpty()) return true; + + DynamicJsonDocument doc(1200); + commonDiscovery(&doc); + doc["~"] = roomsTopic; + doc["name"] = "ESPresense " + room + " BME280 Humidity"; + doc["uniq_id"] = Sprintf("espresense_%06" PRIx64 "_bme280_humidity", ESP.getEfuseMac() >> 24); + doc["avty_t"] = "~/status"; + doc["stat_t"] = "~/bme280_humidity"; + doc["dev_cla"] = "humidity"; + doc["unit_of_meas"] = "%"; + doc["frc_upd"] = true; + + char buffer[1200]; + serializeJson(doc, buffer); + String discoveryTopic = "homeassistant/sensor/espresense_" + ESPMAC + "/bme280_humidity/config"; + + for (int i = 0; i < 10; i++) + { + if (mqttClient.publish(discoveryTopic.c_str(), 0, true, buffer)) + return true; + delay(50); + } + return false; +} + +bool sendDiscoveryBME280Pressure() +{ + if (BME280_I2c.isEmpty()) return true; + + DynamicJsonDocument doc(1200); + commonDiscovery(&doc); + doc["~"] = roomsTopic; + doc["name"] = "ESPresense " + room + " BME280 Pressure"; + doc["uniq_id"] = Sprintf("espresense_%06" PRIx64 "_bme280_pressure", ESP.getEfuseMac() >> 24); + doc["avty_t"] = "~/status"; + doc["stat_t"] = "~/bme280_pressure"; + doc["dev_cla"] = "pressure"; + doc["unit_of_meas"] = "hPa"; + doc["frc_upd"] = true; + + char buffer[1200]; + serializeJson(doc, buffer); + String discoveryTopic = "homeassistant/sensor/espresense_" + ESPMAC + "/bme280_pressure/config"; + + for (int i = 0; i < 10; i++) + { + if (mqttClient.publish(discoveryTopic.c_str(), 0, true, buffer)) + return true; + delay(50); + } + return false; +} + +bool sendDiscoveryTSL2561Lux() +{ + if (TSL2561_I2c.isEmpty()) return true; + + DynamicJsonDocument doc(1200); + commonDiscovery(&doc); + doc["~"] = roomsTopic; + doc["name"] = "ESPresense " + room + " TSL2561 Lux"; + doc["uniq_id"] = Sprintf("espresense_%06" PRIx64 "_tsl2561_lux", ESP.getEfuseMac() >> 24); + doc["avty_t"] = "~/status"; + doc["stat_t"] = "~/tsl2561_lux"; + doc["dev_cla"] = "illuminance"; + doc["unit_of_meas"] = "lx"; + doc["frc_upd"] = true; + + char buffer[1200]; + serializeJson(doc, buffer); + String discoveryTopic = "homeassistant/sensor/espresense_" + ESPMAC + "/tsl2561_lux/config"; + + for (int i = 0; i < 10; i++) + { + if (mqttClient.publish(discoveryTopic.c_str(), 0, true, buffer)) + return true; + delay(50); + } + + return false; +} #endif + bool sendSwitchDiscovery(String name, String entityCategory) { auto slug = slugify(name);