diff --git a/environments.ini b/environments.ini index a849a34397..bc545ba0c8 100644 --- a/environments.ini +++ b/environments.ini @@ -5,6 +5,51 @@ ;List of environments that can be build ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +[env:tinypico-bmp390-usb] +platform = espressif32 +board = tinypico +framework = arduino + +monitor_speed = 115200 +;upload_speed = 921600 + +;board_build.partitions = huge_app.csv +board_build.partitions = partitions/min_spiffs.csv + +build_flags = + -DGateway_Name=\"OMG_TinyPICO_BMP390\" + -DZsensorBMP390=\"BMP390\" + -DDEFAULT_I2C_SDA=21 + -DDEFAULT_I2C_SCL=22 + -DLOG_LEVEL=LOG_LEVEL_NOTICE + -DENV_NAME=\"tinypico-bmp390-usb\" + -DZwebUI=\"WebUI\" + -DZwebServer=true + -DZmqttDiscovery=\"HADiscovery\" + -DvalueAsATopic=true + -UZgatewayBT + -UZgatewayBLE + -UZgatewayBLEConnect + -UZgatewayIR + -UZgatewayLORA + -UZgatewayRF + -UZgatewayRTL_433 + -UZgatewayPilight + -UZgatewaySERIAL + +lib_deps = + ${env.lib_deps} + ${com-esp32.lib_deps} + ${libraries.ble} + ${libraries.decoder} + adafruit/Adafruit BMP3XX Library + adafruit/Adafruit Unified Sensor + +lib_ignore = + NimBLE-Arduino + TheengsDecoder + + [env:rfbridge] platform = ${com.esp8266_platform} board = esp8285 @@ -232,6 +277,7 @@ build_flags = '-DZsensorADC="ADC"' '-DZsensorBH1750="BH1750"' '-DZsensorBME280="BME280"' + '-DZsensorBMP390="BMP390"' '-DBME280Correction=-3.4' '-DZsensorHTU21="HTU21"' '-DZsensorAHTx0="AHTx0"' diff --git a/main/User_config.h b/main/User_config.h index 31a8351004..1ee47b79df 100644 --- a/main/User_config.h +++ b/main/User_config.h @@ -356,6 +356,7 @@ extern ss_cnt_parameters cnt_parameters_array[]; //#define ZsensorTEMT6000 "TEMT6000" //ESP8266 //#define ZsensorTSL2561 "TSL2561" //ESP8266, Arduino, ESP32 //#define ZsensorBME280 "BME280" //ESP8266, Arduino, ESP32 +//#define ZsensorBMP390 "BMP390" //ESP8266, Arduino, ESP32 //#define ZsensorHTU21 "HTU21" //ESP8266, Arduino, ESP32 //#define ZsensorLM75 "LM75" //ESP8266, Arduino, ESP32 //#define ZsensorDHT "DHT" //ESP8266, Arduino, ESP32, Sonoff RF Bridge diff --git a/main/config_BMP390.h b/main/config_BMP390.h new file mode 100644 index 0000000000..1c37dca63a --- /dev/null +++ b/main/config_BMP390.h @@ -0,0 +1,105 @@ +/* + Theengs OpenMQTTGateway - We Unite Sensors in One Open-Source Interface + + Act as a gateway between your 433mhz, infrared IR, BLE, LoRa signal and one interface like an MQTT broker + Send and receiving command by MQTT + + This files enables you to set parameters for the BMP390 sensor. + + Copyright: (c) Hans-Juergen Dinges + + This file is part of OpenMQTTGateway. + + OpenMQTTGateway is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OpenMQTTGateway is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + Connection Schemata: + -------------------- + + BMP390 ------> ESP8266 + ============================== + Vcc ----------------> 5V/3.3V (5V or 3.3V depends on the BMP390 board variant) + GND ----------------> GND + SCL ----------------> D1 + SDA ----------------> D2 + +*/ +#ifndef config_BMP390_h +#define config_BMP390_h + +extern void setupZsensorBMP390(); +extern void MeasureTempAndPressureBMP390(); + +#ifndef bmp390_always +# define bmp390_always true // if false when the current value of the parameter is the same as previous one don't send it by MQTT +#endif +#ifndef TimeBetweenReadingbmp390 +# define TimeBetweenReadingbmp390 30000 +#endif + +/*----------------------------USER PARAMETERS-----------------------------*/ +/*-------------DEFINE YOUR MQTT PARAMETERS BELOW----------------*/ +#ifndef BMP390TOPIC +# define BMP390TOPIC "/CLIMAtoMQTT/bmp390" +#endif + +// I2C address options for BMP390 boards (commonly 0x76 or 0x77) +#ifndef BMP390_I2C_ADDR1 +# define BMP390_I2C_ADDR1 0x77 +#endif +#ifndef BMP390_I2C_ADDR2 +# define BMP390_I2C_ADDR2 0x76 +#endif + +// Only supported for ESP +#ifndef BMP390_PIN_SDA +# define BMP390_PIN_SDA SDA +#endif +#ifndef BMP390_PIN_SCL +# define BMP390_PIN_SCL SCL +#endif + +// Oversampling for BMP390 devices + +#ifndef BMP390TemperatureOversample +// BMP390TemperatureOversample - Values: +// ------------------------ +// 0, skipped +// 1 through 5, oversampling *2, *4, *8, *16, *32 respectively (BMP3XX does not expose 1x) +# define BMP390TemperatureOversample 1 +#endif + +#ifndef BMP390PressureOversample +// BMP390PressureOversample - Values: +// ------------------------- +// 0, skipped +// 1 through 5, oversampling *2, *4, *8, *16, *32 respectively (BMP3XX does not expose 1x) +# define BMP390PressureOversample 1 +#endif + +// Temperature correction for BMP390 devices + +#ifndef BMP390Correction +// BMP390Correction - Correction in Celsius of temperature reported by BMP390 sensor. Both Celsius and Fahrenheit temperatures are adjusted. +// ------------------------- +// Value is a float +// ie Compiler Directive '-DBMP390Correction=-3.4' +# define BMP390Correction 0 +#endif + +// Sea-level pressure reference in hPa (used to compute altitude) +#ifndef BMP390_SEALEVEL_HPA +# define BMP390_SEALEVEL_HPA 1013.25f +#endif + +#endif diff --git a/main/main.cpp b/main/main.cpp index 7452920d80..38c6f8911c 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -164,6 +164,9 @@ Preferences preferences; #ifdef ZsensorBME280 # include "config_BME280.h" #endif +#ifdef ZsensorBMP390 +# include "config_BMP390.h" +#endif #ifdef ZsensorHTU21 # include "config_HTU21.h" #endif @@ -1484,6 +1487,10 @@ void setup() { setupZsensorBME280(); modules.add(ZsensorBME280); #endif +#ifdef ZsensorBMP390 + setupZsensorBMP390(); + modules.add(ZsensorBMP390); +#endif #ifdef ZsensorHTU21 setupZsensorHTU21(); modules.add(ZsensorHTU21); @@ -2623,6 +2630,9 @@ void loop() { #ifdef ZsensorBME280 MeasureTempHumAndPressure(); //Addon to measure Temperature, Humidity, Pressure and Altitude with a Bosch BME280/BMP280 #endif +#ifdef ZsensorBMP390 + MeasureTempAndPressureBMP390(); //Addon to measure Temperature and Pressure with a Bosch BMP390 +#endif #ifdef ZsensorHTU21 MeasureTempHum(); //Addon to measure Temperature, Humidity, of a HTU21 sensor #endif diff --git a/main/mqttDiscovery.cpp b/main/mqttDiscovery.cpp index 6a249e9db7..ad1222ea43 100644 --- a/main/mqttDiscovery.cpp +++ b/main/mqttDiscovery.cpp @@ -820,6 +820,19 @@ void pubMqttDiscovery() { true, BMETOPIC, will_Topic, nullptr); # endif +# ifdef ZsensorBMP390 +# include "config_BMP390.h" + const char* BMP390sensor[][13] = { + {HASS_TYPE_SENSOR, "BMP390: Temp", "bmp390-temp", HASS_CLASS_TEMPERATURE, jsonTempc, "", "", HASS_UNIT_CELSIUS, stateClassMeasurement, nullptr, nullptr, nullptr, nullptr}, + {HASS_TYPE_SENSOR, "BMP390: Pressure", "bmp390-pressure", HASS_CLASS_PRESSURE, jsonPa, "", "", HASS_UNIT_HPA, stateClassMeasurement, nullptr, nullptr, nullptr, nullptr}, + {HASS_TYPE_SENSOR, "BMP390: Altitude", "bmp390-altim", "", jsonAltim, "", "", HASS_UNIT_METER, stateClassMeasurement, nullptr, nullptr, nullptr, nullptr}, + {HASS_TYPE_SENSOR, "BMP390: Altitude (ft)", "bmp390-altift", "", jsonAltif, "", "", HASS_UNIT_FT, stateClassMeasurement, nullptr, nullptr, nullptr, nullptr}}; + + THEENGS_LOG_TRACE(F("bmp390Discovery" CR)); + createDiscoveryFromList(nullptr, BMP390sensor, 4, nullptr, nullptr, nullptr, + true, BMP390TOPIC, will_Topic, nullptr); +# endif + # ifdef ZsensorHTU21 # include "config_HTU21.h" const char* HTUsensor[][13] = { @@ -1152,4 +1165,4 @@ void pubMqttDiscovery() { } #else void pubMqttDiscovery() {} -#endif \ No newline at end of file +#endif diff --git a/main/sensorBMP390.cpp b/main/sensorBMP390.cpp new file mode 100644 index 0000000000..a3da3564be --- /dev/null +++ b/main/sensorBMP390.cpp @@ -0,0 +1,203 @@ +/* + OpenMQTTGateway Addon - ESP8266 or Arduino program for home automation + + Act as a gateway between your 433mhz, infrared IR, BLE, LoRa signal and one interface like an MQTT broker + Send and receiving command by MQTT + + This is the Climate Addon: + - Measures Temperature and Pressure + - Generates Values for: Temperature in degrees C and F, Pressure in Pa, Altitude in Meter and Feet + - Required Hardware Module: Bosch BMP390 + - Required Library: Adafruit BMP3XX + + Connection Schemata: + -------------------- + + BMP390 ------> ESP8266 + ===================================================== + Vcc ----------------> 5V/3.3V (5V or 3.3V depends on the BMP390 board variant) + GND ----------------> GND + SCL ----------------> D1 + SDA ----------------> D2 + + Copyright: (c) Hans-Juergen Dinges + + This file is part of OpenMQTTGateway. + + OpenMQTTGateway is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OpenMQTTGateway is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +#include "User_config.h" + +#ifdef ZsensorBMP390 +# include + +# include "Adafruit_BMP3XX.h" +# include "TheengsCommon.h" +# include "Wire.h" // Library for communication with I2C / TWI devices +# include "config_BMP390.h" + +//Time used to wait for an interval before resending measured values +static unsigned long timebmp390 = 0; +static uint8_t BMP390_i2c_addr = BMP390_I2C_ADDR1; // Default I2C address for BMP390 is 0x77, alternate is 0x76 +static Adafruit_BMP3XX bmp; + +static inline uint8_t mapOversample(int v) { + // Values: + // ------------------------- + // 0, skipped + // 1 through 5, oversampling *2, *4, *8, *16, *32 respectively (BMP3XX does not expose 1x) + switch (v) { + case 0: return BMP3_NO_OVERSAMPLING; + case 1: return BMP3_OVERSAMPLING_2X; + case 2: return BMP3_OVERSAMPLING_4X; + case 3: return BMP3_OVERSAMPLING_8X; + case 4: return BMP3_OVERSAMPLING_16X; + case 5: return BMP3_OVERSAMPLING_32X; + default: return BMP3_OVERSAMPLING_4X; + } +} + +void setupZsensorBMP390() { + // Allow custom pins on ESP Platforms + Wire.begin(BMP390_PIN_SDA, BMP390_PIN_SCL); + + THEENGS_LOG_NOTICE(F("Setup BMP390 on address: %X" CR), BMP390_i2c_addr); + + delay(10); // Gives the Sensor enough time to turn on (The BMP390 requires 2ms to start up) + + bool ok = bmp.begin_I2C(BMP390_I2C_ADDR1); + if (ok) { + BMP390_i2c_addr = BMP390_I2C_ADDR1; + } else { + ok = bmp.begin_I2C(BMP390_I2C_ADDR2); + if (ok) { + BMP390_i2c_addr = BMP390_I2C_ADDR2; + } + } + + if (!ok) { + THEENGS_LOG_NOTICE(F("Bosch BMP390 failed (not found at 0x76/0x77)" CR)); + return; + } + + //***Operation settings*****************************// + + // tempOverSample - Values: + // ------------------------ + // 0, skipped + // 1 through 5, oversampling *1, *2, *4, *8, *16 respectively + bmp.setTemperatureOversampling(mapOversample(BMP390TemperatureOversample)); + + // pressOverSample - Values: + // ------------------------- + // 0, skipped + // 1 through 5, oversampling *1, *2, *4, *8, *16 respectively + bmp.setPressureOversampling(mapOversample(BMP390PressureOversample)); + + // Filter can be off or number of FIR coefficients - Values: + // --------------------------------------------------------- + // 0, filter off + // 1, coefficients = 2 + // 2, coefficients = 4 + // 3, coefficients = 8 + // 4, coefficients = 16 + bmp.setIIRFilterCoeff(BMP3_IIR_FILTER_COEFF_3); + + // Output Data Rate - Values: + // -------------------------- + // BMP3_ODR_xx_HZ (see Adafruit_BMP3XX.h) + bmp.setOutputDataRate(BMP3_ODR_50_HZ); + + THEENGS_LOG_NOTICE(F("Bosch BMP390 successfully initialized: %X" CR), BMP390_i2c_addr); +} + +void MeasureTempAndPressureBMP390() { + if (millis() > (timebmp390 + TimeBetweenReadingbmp390)) { + timebmp390 = millis(); + static float persisted_bmp_tempc; + static float persisted_bmp_tempf; + static float persisted_bmp_pa; + static float persisted_bmp_altim; + static float persisted_bmp_altift; + + bool read_ok = bmp.performReading(); + float BmpTempC = NAN; + float BmpTempF = NAN; + float BmpPa = NAN; + float BmpAltiM = NAN; + float BmpAltiFt = NAN; + + if (read_ok) { + BmpTempC = bmp.temperature + (float)BMP390Correction; + BmpTempF = (BmpTempC * 9.0f / 5.0f) + 32.0f; + BmpPa = bmp.pressure; + BmpAltiM = bmp.readAltitude((float)BMP390_SEALEVEL_HPA); + BmpAltiFt = BmpAltiM * 3.280839895f; + } + + // Check if reads failed and exit early (to try again). + if (!read_ok || isnan(BmpTempC) || isnan(BmpTempF) || isnan(BmpPa) || isnan(BmpAltiM) || isnan(BmpAltiFt)) { + THEENGS_LOG_ERROR(F("Failed to read from BMP390!" CR)); + } else { + THEENGS_LOG_TRACE(F("Creating BMP390 buffer" CR)); + StaticJsonDocument BMP390dataBuffer; + JsonObject BMP390data = BMP390dataBuffer.to(); + // Generate Temperature in degrees C + if (BmpTempC != persisted_bmp_tempc || bmp390_always) { + BMP390data["tempc"] = (float)BmpTempC; + } else { + THEENGS_LOG_TRACE(F("Same Degrees C don't send it" CR)); + } + + // Generate Temperature in degrees F + if (BmpTempF != persisted_bmp_tempf || bmp390_always) { + BMP390data["tempf"] = (float)BmpTempF; + } else { + THEENGS_LOG_TRACE(F("Same Degrees F don't send it" CR)); + } + + // Generate Pressure in Pa + if (BmpPa != persisted_bmp_pa || bmp390_always) { + BMP390data["pa"] = (float)BmpPa; + } else { + THEENGS_LOG_TRACE(F("Same Pressure don't send it" CR)); + } + + // Generate Altitude in Meter + if (BmpAltiM != persisted_bmp_altim || bmp390_always) { + THEENGS_LOG_TRACE(F("Sending Altitude Meter to MQTT" CR)); + BMP390data["altim"] = (float)BmpAltiM; + } else { + THEENGS_LOG_TRACE(F("Same Altitude Meter don't send it" CR)); + } + + // Generate Altitude in Feet + if (BmpAltiFt != persisted_bmp_altift || bmp390_always) { + BMP390data["altift"] = (float)BmpAltiFt; + } else { + THEENGS_LOG_TRACE(F("Same Altitude Feet don't send it" CR)); + } + BMP390data["origin"] = BMP390TOPIC; + enqueueJsonObject(BMP390data); + } + + persisted_bmp_tempc = BmpTempC; + persisted_bmp_tempf = BmpTempF; + persisted_bmp_pa = BmpPa; + persisted_bmp_altim = BmpAltiM; + persisted_bmp_altift = BmpAltiFt; + } +} + +#endif diff --git a/main/sensorBMP390.h b/main/sensorBMP390.h new file mode 100644 index 0000000000..854e45017c --- /dev/null +++ b/main/sensorBMP390.h @@ -0,0 +1,5 @@ +#pragma once +#include + +bool setupBMP390(); +bool readBMP390(float &tempC, float &pressurePa, float &altM); diff --git a/main/webUI.cpp b/main/webUI.cpp index b3011169d1..ad50608663 100644 --- a/main/webUI.cpp +++ b/main/webUI.cpp @@ -2073,13 +2073,15 @@ void webUIPubPrint(const char* topicori, JsonObject& data) { break; } # endif -# ifdef ZsensorBME280 +# if defined(ZsensorBME280) || defined(ZsensorBMP390) case webUIHash("CLIMAtoMQTT"): { // {"tempc":17.06,"tempf":62.708,"hum":50.0752,"pa":98876.14,"altim":205.8725,"altift":675.4348} // Line 1 - strlcpy(message->line1, "bme280", WEBUI_TEXT_WIDTH); + const String origin = data["origin"] | ""; + const bool isBmp390 = (origin.indexOf("bmp390") >= 0); + strlcpy(message->line1, isBmp390 ? "bmp390" : "bme280", WEBUI_TEXT_WIDTH); // Line 2 @@ -2101,11 +2103,24 @@ void webUIPubPrint(const char* topicori, JsonObject& data) { // Line 3 String line3 = ""; - float humidity = data["hum"]; - if (data.containsKey("hum") && humidity <= 100 && humidity >= 0) { - char hum[5]; - dtostrf(humidity, 3, 1, hum); - line3 += "hum: " + (String)hum + "% "; + if (isBmp390) { + if (data.containsKey("altim") && data.containsKey("altift")) { + char altitude[7]; + if (displayMetric) { + dtostrf((float)data["altim"], 4, 1, altitude); + line3 = "alt: " + (String)altitude + " m"; + } else { + dtostrf((float)data["altift"], 4, 1, altitude); + line3 = "alt: " + (String)altitude + " ft"; + } + } + } else { + float humidity = data["hum"]; + if (data.containsKey("hum") && humidity <= 100 && humidity >= 0) { + char hum[5]; + dtostrf(humidity, 3, 1, hum); + line3 += "hum: " + (String)hum + "% "; + } } line3.toCharArray(message->line3, WEBUI_TEXT_WIDTH); @@ -2604,18 +2619,21 @@ void SerialWeb::begin() { Dummy virtual functions carried over from Serial */ int SerialWeb::available(void) { + return 0; } /* Dummy virtual functions carried over from Serial */ int SerialWeb::peek(void) { + return -1; } /* Dummy virtual functions carried over from Serial */ int SerialWeb::read(void) { + return -1; } /* diff --git a/platformio.ini b/platformio.ini index 92da37f09d..b6d843cb17 100644 --- a/platformio.ini +++ b/platformio.ini @@ -28,7 +28,7 @@ extra_configs = ; into a separate file called prod_env.ini, it will be automatically read by pio ; ; an example (prod_env.ini.example) is available into the same folder as this file. ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - +default_envs = tinypico-bmp390-usb ;default_envs = rfbridge ;default_envs = rfbridge-direct ;default_envs = esp32dev-all-test