diff --git a/BUILDS.md b/BUILDS.md index 53ebba456..18f25042b 100644 --- a/BUILDS.md +++ b/BUILDS.md @@ -113,6 +113,7 @@ | USE_DHT12 | - | - | - | - | x | - | - | | USE_DS1624 | - | - | - | - | x | - | - | | USE_AHT1x | - | - | - | - | - | - | - | +| USE_HDC1080 | - | - | - | - | - | - | - | | USE_WEMOS_MOTOR_V1 | - | - | - | - | x | - | - | | | | | | | | | | | Feature or Sensor | minimal | lite | tasmota | knx | sensors | ir | display | Remarks diff --git a/I2CDEVICES.md b/I2CDEVICES.md index f9cf5b0e3..75c0a1a9a 100644 --- a/I2CDEVICES.md +++ b/I2CDEVICES.md @@ -66,3 +66,4 @@ Index | Define | Driver | Device | Address(es) | Description 42 | USE_DS1624 | xsns_59 | DS1624 | 0x48 - 0x4F | Temperature sensor 43 | USE_AHT1x | xsns_63 | AHT10/15 | 0x38 | Temperature and humidity sensor 44 | USE_WEMOS_MOTOR_V1 | xdrv_34 | | 0x2D - 0x30 | WEMOS motor shield v1.0.0 (6612FNG) + 45 | USE_HDC1080 | xsns_65 | HDC1080 | 0x40 | Temperature and Humidity sensor diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h index a62c6d191..40cbb5ce4 100644 --- a/tasmota/my_user_config.h +++ b/tasmota/my_user_config.h @@ -494,6 +494,8 @@ // #define USE_DS1624 // [I2cDriver42] Enable DS1624, DS1621 temperature sensor (I2C addresses 0x48 - 0x4F) (+1k2 code) // #define USE_AHT1x // [I2cDriver43] Enable AHT10/15 humidity and temperature sensor (I2C address 0x38) (+0k8 code) // #define USE_WEMOS_MOTOR_V1 // [I2cDriver44] Enable Wemos motor driver V1 (I2C addresses 0x2D - 0x30) (+0k7 code) +// #define USE_HDC1080 // [I2cDriver92] Enable HDC1080 temperature/humidity sensor + // #define WEMOS_MOTOR_V1_ADDR 0x30 // Default I2C address 0x30 // #define WEMOS_MOTOR_V1_FREQ 1000 // Default frequency diff --git a/tasmota/support_features.ino b/tasmota/support_features.ino index 961dd1091..410ae7bee 100644 --- a/tasmota/support_features.ino +++ b/tasmota/support_features.ino @@ -323,7 +323,6 @@ void GetFeatures(void) #ifdef USE_TM1638 feature_sns1 |= 0x80000000; // xsns_28_tm1638.ino #endif - /*********************************************************************************************/ feature_sns2 = 0x00000000; diff --git a/tasmota/tasmota_post.h b/tasmota/tasmota_post.h index 7cf0fff75..dea6b1ca3 100644 --- a/tasmota/tasmota_post.h +++ b/tasmota/tasmota_post.h @@ -182,6 +182,9 @@ extern "C" void custom_crash_callback(struct rst_info * rst_info, uint32_t stack #define USE_DHT12 // Add I2C code for DHT12 temperature and humidity sensor (+0k7 code) #define USE_DS1624 // Add I2C code for DS1624, DS1621 sensor //#define USE_AHT1x // Enable AHT10/15 humidity and temperature sensor (I2C address 0x38) (+0k8 code) +//#define USE_HDC1080 // Enable HDC1080 temperature/humidity sensor + + #define USE_WEMOS_MOTOR_V1 // Enable Wemos motor driver V1 (I2C addresses 0x2D - 0x30) (+0k7 code) #define WEMOS_MOTOR_V1_ADDR 0x30 // Default I2C address 0x30 #define WEMOS_MOTOR_V1_FREQ 1000 // Default frequency diff --git a/tasmota/xsns_65_hdc1080.ino b/tasmota/xsns_65_hdc1080.ino new file mode 100644 index 000000000..74de68383 --- /dev/null +++ b/tasmota/xsns_65_hdc1080.ino @@ -0,0 +1,349 @@ +/* + xsns_92_hdc1080.ino - Texas Instruments HDC1080 temperature and humidity sensor support for Tasmota + + Copyright (C) 2020 Luis Teixeira + + This program 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. + + This program 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 . +*/ + +#ifdef USE_I2C +#ifdef USE_HDC1080 + +/*********************************************************************************************\ + * HDC1080 - Temperature and Humidy sensor + * + * Source: Luis Teixeira + * + * I2C Address: 0x40 +\*********************************************************************************************/ + +#define XSNS_65 65 +#define XI2C_45 45 // See I2CDEVICES.md + +#define HDC1080_ADDR 0x40 + +// Registers: + +#define HDC_REG_TEMP 0x00 // Temperature register +#define HDC_REG_RH 0x01 // Humidity register +#define HDC_REG_CONFIG 0x02 // Configuration register +#define HDC_REG_SERIAL1 0xFB // First 2 bytes of the serial ID +#define HDC_REG_SERIAL2 0xFC // Mid 2 bytes of the serial ID +#define HDC_REG_SERIAL3 0xFD // Last bits of the serial ID +#define HDC_REG_MAN_ID 0xFE // Manufacturer ID +#define HDC_REG_DEV_ID 0xFF // Device ID + +// Expected constant values of some of the registers: + +#define HDC1080_MAN_ID 0x5449 // Manufacturer ID (Texas Instruments) +#define HDC1080_DEV_ID 0x1050 // Device ID (valid for the HDC1080) + +// Possible values for the configuration register fields: + +#define HDC1080_RST_ON 0x8000 +#define HDC1080_HEAT_ON 0x2000 +#define HDC1080_MODE_ON 0x1000 // acquision mode (temperature + humidity) +#define HDC1080_TRES_11 0x400 +#define HDC1080_HRES_11 0x100 +#define HDC1080_HRES_8 0x80 + +// Constants: + +#define HDC1080_CONV_TIME 15 // Assume 6.50 + 6.35 ms + x of conversion delay for this device +#define HDC1080_TEMP_MULT 0.0025177 +#define HDC1080_RH_MULT 0.0025177 +#define HDC1080_TEMP_OFFSET 40.0 + +const char* hdc_type_name = "HDC1080"; +uint16_t hdc_manufacturer_id = 0; +uint16_t hdc_device_id = 0; + +float hdc_temperature = 0.0; +float hdc_humidity = 0.0; + +uint8_t hdc_valid = 0; + +bool is_reading = false; +uint32_t hdc_next_read; + +/** + * Reads the device ID register. + * + */ +uint16_t HdcReadDeviceId(void) { + return I2cRead16(HDC1080_ADDR, HDC_REG_DEV_ID); +} + +/** + * Reads the manufacturer ID register. + * + */ +uint16_t HdcReadManufacturerId(void) { + return I2cRead16(HDC1080_ADDR, HDC_REG_MAN_ID); +} + +/** + * Overwrites the configuration register with the provided config + */ +void HdcConfig(uint16_t config) { + I2cWrite16(HDC1080_ADDR, HDC_REG_CONFIG, config); +} + +/** + * Performs a soft reset on the device. + * + * RST = 1 -> software reset + * + */ +void HdcReset(void) { + uint16_t current = I2cRead16(HDC1080_ADDR, HDC_REG_CONFIG); + + // bit 15 of the configuration register contains the RST flag + // so we set it to 1: + + current |= 0x8000; + + I2cWrite16(HDC1080_ADDR, HDC_REG_CONFIG, current); + + delay(HDC1080_CONV_TIME); // Not sure how long it takes to reset. Assuming this value. +} + +/** + * Performs the write portion of the HDC1080 sensor transaction. This + * action of writing to a register signals the beginning of the operation + * (e.g. data acquisition). + * + * addr: the address of the I2C device we are talking to. + * reg: the register where we are writing to. + * + * returns: 0 if the transmission was successfully completed, != 0 otherwise. + */ +int8_t HdcTransactionOpen(uint8_t addr, uint8_t reg) { + Wire.beginTransmission((uint8_t) addr); + Wire.write((uint8_t) reg); + return Wire.endTransmission(); +} + +/** + * Performs the read portion of the HDC1080 sensor transaction. + * + * addr: the address of the I2C device we are talking to. + * reg_data: the pointer to the memory location where we will place the bytes that were read from the device + * len: the number of bytes we expect to read + * + * returns: if the read operation was successful. != 0 otherwise. + */ +int8_t HdcTransactionClose(uint8_t addr, uint8_t *reg_data, uint16_t len) { + if (len != Wire.requestFrom((uint8_t) addr, (uint8_t) len)) { + return 1; + } + + while (len--) { + *reg_data = (uint8_t)Wire.read(); + reg_data++; + } + + return 0; +} + +/** + * The various initialization steps for this sensor. + * + */ +void HdcInit(void) { + HdcReset(); + HdcConfig(HDC1080_MODE_ON); +} + +/** + * Triggers the single transaction read of the T/RH sensor. + * + */ +bool HdcTriggerRead(void) { + int8_t status = HdcTransactionOpen(HDC1080_ADDR, HDC_REG_TEMP); + + hdc_next_read = millis() + HDC1080_CONV_TIME; + + if(status) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("HdcTriggerRead: failed to open the transaction for HDC_REG_TEMP. Status = %d"), status); + + return false; + } + + is_reading = true; + + return true; +} + +/** + * Performs a temperature and humidity measurement, and calls + * the conversion function providing the results in the correct + * unit according to the device settings. + * + * returns: false if something failed during the read process. + * + */ +bool HdcRead(void) { + int8_t status = 0; + uint8_t sensor_data[4]; + uint16_t temp_data = 0; + uint16_t rh_data = 0; + + is_reading = false; + + status = HdcTransactionClose(HDC1080_ADDR, sensor_data, 4); + + if(status) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("HdcRead: failed to read HDC_REG_TEMP. Status = %d"), status); + + return false; + } + + temp_data = (uint16_t) ((sensor_data[0] << 8) | sensor_data[1]); + rh_data = (uint16_t) ((sensor_data[2] << 8) | sensor_data[3]); + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("HdcRead: temperature raw data: 0x%04x; humidity raw data: 0x%04x"), temp_data, rh_data); + + // read the temperature from the first 16 bits of the result + + hdc_temperature = ConvertTemp(HDC1080_TEMP_MULT * (float) (temp_data) - HDC1080_TEMP_OFFSET); + + hdc_humidity = HDC1080_RH_MULT * (float) (rh_data); + + if (hdc_humidity > 100) { hdc_humidity = 100.0; } + if (hdc_humidity < 0) { hdc_humidity = 0.01; } + + ConvertHumidity(hdc_humidity); // Set global humidity + + hdc_valid = SENSOR_MAX_MISS; + + return true; +} + +/** + * Performs the detection of the HTC1080 sensor. + * + */ + +void HdcDetect(void) { + if (I2cActive(HDC1080_ADDR)) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("HdcDetect: Address = 0x%02X already in use."), HDC1080_ADDR); + + return; + } + + hdc_manufacturer_id = HdcReadManufacturerId(); + hdc_device_id = HdcReadDeviceId(); + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("HdcDetect: detected device with manufacturerId = 0x%04X and deviceId = 0x%04X"), hdc_manufacturer_id, hdc_device_id); + + if (hdc_device_id == HDC1080_DEV_ID) { + HdcInit(); + I2cSetActiveFound(HDC1080_ADDR, hdc_type_name); + } +} + +/** + * As the name suggests, this function is called every second + * for performing driver related logic. + * + */ +void HdcEverySecond(void) { + if (uptime &1) { // Every 2 seconds + if (!HdcTriggerRead()) { + AddLogMissed((char*) hdc_type_name, hdc_valid); + } + } +} + +/** + * Tasmota boilerplate for presenting the sensor data in the web UI, JSON for + * the MQTT messages, and so on. + * + */ +void HdcShow(bool json) { + if (hdc_valid) { + char temperature[33]; + + dtostrfd(hdc_temperature, Settings.flag2.temperature_resolution, temperature); + char humidity[33]; + dtostrfd(hdc_humidity, Settings.flag2.humidity_resolution, humidity); + + if (json) { + ResponseAppend_P(JSON_SNS_TEMPHUM, hdc_type_name, temperature, humidity); +#ifdef USE_DOMOTICZ + if (0 == tele_period) { + DomoticzTempHumSensor(temperature, humidity); + } +#endif // USE_DOMOTICZ +#ifdef USE_KNX + if (0 == tele_period) { + KnxSensor(KNX_TEMPERATURE, hdc_temperature); + KnxSensor(KNX_HUMIDITY, hdc_humidity); + } +#endif // USE_KNX +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_TEMP, hdc_type_name, temperature, TempUnit()); + WSContentSend_PD(HTTP_SNS_HUM, hdc_type_name, humidity); +#endif // USE_WEBSERVER + } + } +} + +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ + +bool Xsns65(uint8_t function) +{ + if (!I2cEnabled(XI2C_45)) { + AddLog_P(LOG_LEVEL_DEBUG, PSTR("Xsns65: I2C driver not enabled for this device.")); + + return false; + } + + bool result = false; + + if (FUNC_INIT == function) { + HdcDetect(); + } + else if (hdc_device_id) { + switch (function) { + case FUNC_EVERY_50_MSECOND: + if(is_reading && TimeReached(hdc_next_read)) { + if(!HdcRead()) { + AddLogMissed((char*) hdc_type_name, hdc_valid); + } + } + break; + case FUNC_EVERY_SECOND: + HdcEverySecond(); + break; + case FUNC_JSON_APPEND: + HdcShow(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + HdcShow(0); + break; +#endif // USE_WEBSERVER + } + } + return result; +} + +#endif // USE_HDC1080 +#endif // USE_I2C +