From 054d79afa9aac6398e0b2fa452e3d389fff89da3 Mon Sep 17 00:00:00 2001 From: Rob Tillaart Date: Sun, 22 Sep 2024 11:42:37 +0200 Subject: [PATCH] 0.2.2 CHT8305 --- libraries/CHT8305/CHANGELOG.md | 12 +- libraries/CHT8305/CHT8305.cpp | 151 ++++++++++---- libraries/CHT8305/CHT8305.h | 53 +++-- libraries/CHT8305/README.md | 194 ++++++++++++------ .../CHT8305_performance.ino | 83 ++++++++ .../CHT8305_performance/performance_0.2.2.txt | 24 +++ .../CHT8305_plotter/CHT8305_plotter.ino | 65 ++++++ .../examples/CHT8305_plotter/Capture001.JPG | Bin 0 -> 19122 bytes libraries/CHT8305/keywords.txt | 18 +- libraries/CHT8305/library.json | 2 +- libraries/CHT8305/library.properties | 2 +- 11 files changed, 475 insertions(+), 129 deletions(-) create mode 100644 libraries/CHT8305/examples/CHT8305_performance/CHT8305_performance.ino create mode 100644 libraries/CHT8305/examples/CHT8305_performance/performance_0.2.2.txt create mode 100644 libraries/CHT8305/examples/CHT8305_plotter/CHT8305_plotter.ino create mode 100644 libraries/CHT8305/examples/CHT8305_plotter/Capture001.JPG diff --git a/libraries/CHT8305/CHANGELOG.md b/libraries/CHT8305/CHANGELOG.md index 29f77632..67807a40 100644 --- a/libraries/CHT8305/CHANGELOG.md +++ b/libraries/CHT8305/CHANGELOG.md @@ -6,12 +6,22 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). +## [0.2.2] - 2024-09-16 +- Fix #15, improve error handling. +- add return value **CHT8305_ERROR_BUFSIZE** +- add return value **CHT8305_ERROR_GENERIC** +- add return value **0xFFFF** for 2 functions. +- set default values 0.0 for **offset** parameters. +- fix missing names in keywords.txt +- add plotter and performance example +- update readme.md. + + ## [0.2.1] - 2024-01-30 - add multiplexing section to readme.md - update examples (URL) - minor edits - ## [0.2.0] - 2023-12-05 - refactor API, constructor, begin() - update readme.md diff --git a/libraries/CHT8305/CHT8305.cpp b/libraries/CHT8305/CHT8305.cpp index 1baac47a..3a5991fa 100644 --- a/libraries/CHT8305/CHT8305.cpp +++ b/libraries/CHT8305/CHT8305.cpp @@ -1,7 +1,7 @@ // // FILE: CHT8305.cpp // AUTHOR: Rob Tillaart -// VERSION: 0.2.1 +// VERSION: 0.2.2 // PURPOSE: Arduino library for CHT8305 temperature and humidity sensor // URL: https://github.com/RobTillaart/CHT8305 @@ -17,14 +17,24 @@ CHT8305::CHT8305(const uint8_t address, TwoWire *wire) { _wire = wire; _address = address; + _error = CHT8305_OK; } int CHT8305::begin() { - if ((_address < 0x40) || (_address > 0x43)) return CHT8305_ERROR_ADDR; - if (! isConnected()) return CHT8305_ERROR_CONNECT; - return CHT8305_OK; + if ((_address < 0x40) || (_address > 0x43)) + { + _error = CHT8305_ERROR_ADDR; + return _error; + } + if (! isConnected()) + { + _error = CHT8305_ERROR_CONNECT; + return _error; + } + _error = CHT8305_OK; + return _error; } @@ -50,27 +60,33 @@ int CHT8305::read() // do not read too fast if (millis() - _lastRead < 1000) { - return CHT8305_ERROR_LASTREAD; + _error = CHT8305_ERROR_LASTREAD; + return _error; } _lastRead = millis(); uint8_t data[4] = {0, 0, 0, 0 }; - _readRegister(CHT8305_REG_TEMPERATURE, &data[0], 4); + if (_readRegister(CHT8305_REG_TEMPERATURE, &data[0], 4) != CHT8305_OK) + { + return _error; + } - uint16_t tmp = data[0] << 8 | data[1]; + uint16_t tmp = (data[0] << 8) | data[1]; _temperature = tmp * (165.0 / 65535.0) - 40.0; - tmp = data[2] << 8 | data[3]; + + tmp = (data[2] << 8) | data[3]; _humidity = tmp * (1.0 / 655.35); // == / 65535 * 100% if (_tempOffset != 0.0) _temperature += _tempOffset; - if (_humOffset != 0.0) + if (_humOffset != 0.0) { _humidity += _humOffset; if (_humidity < 0.0) _humidity = 0.0; if (_humidity > 100.0) _humidity = 100.0; } - return CHT8305_OK; + _error = CHT8305_OK; + return _error; } @@ -79,14 +95,18 @@ int CHT8305::readTemperature() // do not read too fast if (millis() - _lastRead < 1000) { - return CHT8305_ERROR_LASTREAD; + _error = CHT8305_ERROR_LASTREAD; + return _error; } _lastRead = millis(); uint8_t data[2] = {0, 0}; - _readRegister(CHT8305_REG_TEMPERATURE, &data[0], 2); + if (_readRegister(CHT8305_REG_TEMPERATURE, &data[0], 2) != CHT8305_OK) + { + return _error; + } - uint16_t tmp = data[0] << 8 | data[1]; + uint16_t tmp = (data[0] << 8) | data[1]; _temperature = tmp * (165.0 / 65535.0) - 40.0; if (_tempOffset != 0.0) @@ -94,7 +114,8 @@ int CHT8305::readTemperature() _temperature += _tempOffset; } - return CHT8305_OK; + _error = CHT8305_OK; + return _error; } @@ -103,14 +124,18 @@ int CHT8305::readHumidity() // do not read too fast if (millis() - _lastRead < 1000) { - return CHT8305_ERROR_LASTREAD; + _error = CHT8305_ERROR_LASTREAD; + return _error; } _lastRead = millis(); uint8_t data[2] = {0, 0}; - _readRegister(CHT8305_REG_HUMIDITY, &data[0], 2); + if (_readRegister(CHT8305_REG_HUMIDITY, &data[0], 4) != CHT8305_OK) + { + return _error; + } - uint16_t tmp = data[0] << 8 | data[1]; + uint16_t tmp = (data[0] << 8) | data[1]; _humidity = tmp * (1.0 / 655.35); // == / 65535 * 100% if (_humOffset != 0.0) @@ -120,7 +145,8 @@ int CHT8305::readHumidity() if (_humidity > 100.0) _humidity = 100.0; } - return CHT8305_OK; + _error = CHT8305_OK; + return _error; } @@ -184,20 +210,27 @@ float CHT8305::getTemperatureOffset() // // CONFIGURATION REGISTER // -void CHT8305::setConfigRegister(uint16_t bitmask) +bool CHT8305::setConfigRegister(uint16_t bitmask) { uint8_t data[2]; data[0] = bitmask >> 8; data[1] = bitmask & 0xFF; - _writeRegister(2, &data[0], 2); + if (_writeRegister(2, &data[0], 2) != CHT8305_OK) + { + return false; + } + return true; } uint16_t CHT8305::getConfigRegister() { uint8_t data[2] = { 0, 0}; - _readRegister(CHT8305_REG_CONFIG, &data[0], 2); - uint16_t tmp = data[0] << 8 | data[1]; + if (_readRegister(CHT8305_REG_CONFIG, &data[0], 2) != CHT8305_OK) + { + return 0; + } + uint16_t tmp = (data[0] << 8) | data[1]; return tmp; } @@ -344,7 +377,10 @@ bool CHT8305::setAlertLevels(float temperature, float humidity) tmp = (temperature + 40.0) * (511.0 / 165.0); mask |= tmp; - _writeRegister(CHT8305_REG_ALERT, (uint8_t *)&mask, 2); + if (_writeRegister(CHT8305_REG_ALERT, (uint8_t *)&mask, 2) != CHT8305_OK) + { + return false; + } return true; } @@ -352,7 +388,10 @@ bool CHT8305::setAlertLevels(float temperature, float humidity) float CHT8305::getAlertLevelTemperature() { uint16_t data = 0; - _readRegister(CHT8305_REG_ALERT, (uint8_t *)&data, 2); + if (_readRegister(CHT8305_REG_ALERT, (uint8_t *)&data, 2) != CHT8305_OK) + { + return CHT8305_ERROR_GENERIC; + } data &= 0x01FF; data <<= 7; return data * (165.0 / 65535.0) - 40.0; @@ -362,7 +401,10 @@ float CHT8305::getAlertLevelTemperature() float CHT8305::getAlertLevelHumidity() { uint16_t data = 0; - _readRegister(CHT8305_REG_ALERT, (uint8_t *)&data, 2); + if (_readRegister(CHT8305_REG_ALERT, (uint8_t *)&data, 2) != CHT8305_OK) + { + return CHT8305_ERROR_GENERIC; + } data &= 0xFE00; return data * (100.0 / 65535.0); } @@ -375,8 +417,11 @@ float CHT8305::getAlertLevelHumidity() float CHT8305::getVoltage() { uint8_t data[2] = { 0, 0}; - _readRegister(CHT8305_REG_VOLTAGE, &data[0], 2); - uint16_t tmp = data[0] << 8 | data[1]; + if (_readRegister(CHT8305_REG_VOLTAGE, &data[0], 2) != CHT8305_OK) + { + return CHT8305_ERROR_GENERIC; + } + uint16_t tmp = (data[0] << 8) | data[1]; return tmp * (5.0 / 32768.0); // best guess } @@ -388,8 +433,11 @@ float CHT8305::getVoltage() uint16_t CHT8305::getManufacturer() { uint8_t data[2] = { 0, 0}; - _readRegister(CHT8305_REG_MANUFACTURER, &data[0], 2); - uint16_t tmp = data[0] << 8 | data[1]; + if (_readRegister(CHT8305_REG_MANUFACTURER, &data[0], 2) != CHT8305_OK) + { + return 0xFFFF; + } + uint16_t tmp = (data[0] << 8) | data[1]; return tmp; } @@ -397,12 +445,23 @@ uint16_t CHT8305::getManufacturer() uint16_t CHT8305::getVersionID() { uint8_t data[2] = { 0, 0}; - _readRegister(CHT8305_REG_VERSION, &data[0], 2); - uint16_t tmp = data[0] << 8 | data[1]; + if (_readRegister(CHT8305_REG_VERSION, &data[0], 2) != CHT8305_OK) + { + return 0xFFFF; + } + uint16_t tmp = (data[0] << 8) | data[1]; return tmp; } +int CHT8305::getLastError() +{ + int e = _error; + _error = CHT8305_OK; + return e; +} + + //////////////////////////////////////////////// // // PRIVATE @@ -412,7 +471,11 @@ int CHT8305::_readRegister(uint8_t reg, uint8_t * buf, uint8_t size) _wire->beginTransmission(_address); _wire->write(reg); int n = _wire->endTransmission(); - if (n != 0) return CHT8305_ERROR_I2C; + if (n != 0) + { + _error = CHT8305_ERROR_I2C; + return _error; + } if (reg == CHT8305_REG_TEMPERATURE) // wait for conversion... { @@ -420,14 +483,17 @@ int CHT8305::_readRegister(uint8_t reg, uint8_t * buf, uint8_t size) } n = _wire->requestFrom(_address, size); - if (n == size) + if (n != size) { - for (uint8_t i = 0; i < size; i++) - { - buf[i] = _wire->read(); - } + _error = CHT8305_ERROR_BUFSIZE; + return _error; } - return CHT8305_OK; + for (uint8_t i = 0; i < size; i++) + { + buf[i] = _wire->read(); + } + _error = CHT8305_OK; + return _error; } @@ -440,8 +506,13 @@ int CHT8305::_writeRegister(uint8_t reg, uint8_t * buf, uint8_t size) _wire->write(buf[i]); } int n = _wire->endTransmission(); - if (n != 0) return CHT8305_ERROR_I2C; - return CHT8305_OK; + if (n != 0) + { + _error = CHT8305_ERROR_I2C; + return _error; + } + _error = CHT8305_OK; + return _error; } diff --git a/libraries/CHT8305/CHT8305.h b/libraries/CHT8305/CHT8305.h index 72f8a636..4605b20c 100644 --- a/libraries/CHT8305/CHT8305.h +++ b/libraries/CHT8305/CHT8305.h @@ -2,7 +2,7 @@ // // FILE: CHT8305.h // AUTHOR: Rob Tillaart -// VERSION: 0.2.1 +// VERSION: 0.2.2 // PURPOSE: Arduino library for CHT8305 temperature and humidity sensor // URL: https://github.com/RobTillaart/CHT8305 // @@ -12,30 +12,34 @@ #include "Wire.h" -#define CHT8305_LIB_VERSION (F("0.2.1")) +#define CHT8305_LIB_VERSION (F("0.2.2")) // DEFAULT ADDRESS #ifndef CHT8305_DEFAULT_ADDRESS #define CHT8305_DEFAULT_ADDRESS 0x40 #endif + // ERRORS #define CHT8305_OK 0 #define CHT8305_ERROR_ADDR -10 #define CHT8305_ERROR_I2C -11 #define CHT8305_ERROR_CONNECT -12 +#define CHT8305_ERROR_BUFSIZE -13 #define CHT8305_ERROR_LASTREAD -20 +#define CHT8305_ERROR_GENERIC -999 -// REGISTERS -#define CHT8305_REG_TEMPERATURE 0x00 -#define CHT8305_REG_HUMIDITY 0x01 -#define CHT8305_REG_CONFIG 0x02 -#define CHT8305_REG_ALERT 0x03 -#define CHT8305_REG_VOLTAGE 0x04 -#define CHT8305_REG_MANUFACTURER 0xFE -#define CHT8305_REG_VERSION 0xFF -// REGISTER MASKS +// REGISTERS (in .h for build-CI test) +#define CHT8305_REG_TEMPERATURE 0x00 +#define CHT8305_REG_HUMIDITY 0x01 +#define CHT8305_REG_CONFIG 0x02 +#define CHT8305_REG_ALERT 0x03 +#define CHT8305_REG_VOLTAGE 0x04 +#define CHT8305_REG_MANUFACTURER 0xFE +#define CHT8305_REG_VERSION 0xFF + +// REGISTER MASKS (in .h for build-CI test) #define CHT8305_CFG_SOFT_RESET 0x8000 #define CHT8305_CFG_CLOCK_STRETCH 0x4000 #define CHT8305_CFG_HEATER 0x2000 @@ -78,15 +82,16 @@ public: // adding offsets works well in normal range - void setHumidityOffset(float offset); + void setHumidityOffset(float offset = 0.0); // might introduce under- or overflow at the ends of the sensor range - void setTemperatureOffset(float offset); + void setTemperatureOffset(float offset = 0.0); float getHumidityOffset(); float getTemperatureOffset(); // CONFIGURATION REGISTER - void setConfigRegister(uint16_t bitmask); + // default configRegister = 0x1004 (explicit no default parameter) + bool setConfigRegister(uint16_t bitmask); uint16_t getConfigRegister(); // // | bit | mask | name | description | @@ -132,13 +137,12 @@ public: void setVCCenable(bool enable = true); bool getVCCenable(); - - // ALERT FUNCTIONS - // mode trigger - // 0 T or H (default) - // 1 T - // 2 H - // 3 T and H + // ALERT FUNCTIONS + // mode trigger + // 0 T or H (default) + // 1 T + // 2 H + // 3 T and H bool setAlertTriggerMode(uint8_t mode = 0); uint8_t getAlertTriggerMode(); bool getAlertPendingStatus(); @@ -153,6 +157,8 @@ public: // VOLTAGE + // one need to call setVCCenable(true) first. + // meaning of this function is unclear. float getVoltage(); @@ -161,6 +167,9 @@ public: uint16_t getVersionID(); // may vary + // ERROR HANDLING + int getLastError(); + private: float _humOffset = 0.0; float _tempOffset = 0.0; @@ -177,6 +186,8 @@ private: void _setConfigMask(uint16_t mask); void _clrConfigMask(uint16_t mask); + + int _error = CHT8305_OK; }; diff --git a/libraries/CHT8305/README.md b/libraries/CHT8305/README.md index 5d973f89..8586d386 100644 --- a/libraries/CHT8305/README.md +++ b/libraries/CHT8305/README.md @@ -15,7 +15,8 @@ Arduino library for CHT8305 temperature and humidity sensor. **EXPERIMENTAL** minimal tested. -If you are able to test this library, please let me know your experiences. +If you are able to test this library, please let me know +your experiences. ## Description @@ -34,25 +35,25 @@ One of the interesting functions is the support of an ALERT function. This prevents the need for continuous polling of the sensor. -#### 0.2.0 Breaking change +### 0.2.0 Breaking change Version 0.2.0 introduced a breaking change. You cannot set the pins in **begin()** any more. This reduces the dependency of processor dependent Wire implementations. -The user has to call **Wire.begin()** and can optionally set the Wire pins +The user has to call **Wire.begin()** and can optionally set the Wire pins before calling **begin()**. Moved the address parameter from **begin()** to constructor. -#### Tests +### Tests - Temperature and humidity functions works on AVR. - default about 14 milliseconds at 14 bit resolution. - offset functions work. - getVoltage() function works on AVR but meaning unclear. - getManufacturer(), getVersionID() works on AVR. -- +- The ALERT functions are not tested. The reason is that the sensor I have does not expose the ALERT pin. @@ -86,20 +87,43 @@ Always check datasheet for connections. Pull ups are needed on SDA, SCL and optional to ALERT. -#### Alert +### Alert The CHT8305 has an ALERT logic output pin with an open drain structure. This output is active low. (if the breakout supports this.) -## I2C +## I2C -#### performance +### performance -I2C bus speeds is supported up to 400 KHz. +I2C bus speeds is supported up to 400 kHz. +In practice the sensor seems to work even at 800 kHz. +However not clear if the higher speed brings extra wear? + +As the sensor / library blocks on reading the temperature +using high I2C speeds does not make a difference, except +for reading Humidity. + +So a higher I2C speed does not help to read this sensor. + +See - HT8305_performance.ino -#### Addresses +| SPEED | READ | READ_T | READ_H | +|:--------:|:-------:|:--------:|:--------:| +| 100000 | 14824 | 14604 | 376 | +| 200000 | 14476 | 14360 | 224 | +| 300000 | 14368 | 14276 | 172 | +| 400000 | 14324 | 14244 | 148 | +| 500000 | 14300 | 14224 | 140 | +| 600000 | 14276 | 14212 | 132 | +| 700000 | 14272 | 14204 | 124 | +| 800000 | 14268 | 14196 | 120 | + + + +### Addresses | AD0 | Address | Notes | |:-----:|:----------:|:--------| @@ -111,19 +135,19 @@ I2C bus speeds is supported up to 400 KHz. Pull ups are needed on SDA, SCL and optional to ALERT. -#### I2C multiplexing +### I2C multiplexing Sometimes you need to control more devices than possible with the default address range the device provides. -This is possible with an I2C multiplexer e.g. TCA9548 which creates up -to eight channels (think of it as I2C subnets) which can use the complete -address range of the device. +This is possible with an I2C multiplexer e.g. TCA9548 which creates up +to eight channels (think of it as I2C subnets) which can use the complete +address range of the device. -Drawback of using a multiplexer is that it takes more administration in -your code e.g. which device is on which channel. +Drawback of using a multiplexer is that it takes more administration in +your code e.g. which device is on which channel. This will slow down the access, which must be taken into account when deciding which devices are on which channel. -Also note that switching between channels will slow down other devices +Also note that switching between channels will slow down other devices too if they are behind the multiplexer. - https://github.com/RobTillaart/TCA9548 @@ -135,16 +159,16 @@ too if they are behind the multiplexer. #include "CHT8305.h" ``` -#### Constructor +### Constructor -- **CHT8305(const uint8_t address = CHT8305_DEFAULT_ADDRESS, TwoWire \*wire = &Wire)** Constructor -with default address (0x40) and I2C bus. +- **CHT8305(const uint8_t address = CHT8305_DEFAULT_ADDRESS, TwoWire \*wire = &Wire)** +Constructor with default address (0x40) and I2C bus. - **int begin()** initializes internals. Returns error status. - **bool isConnected()** checks if address (default 0x40) can be seen on the I2C bus. -#### Core +### Core - **int read()** reads both the temperature and humidity. Can be called once per second. @@ -153,49 +177,63 @@ Can be called once per second. - **uint32_t lastRead()** returns lastRead in MilliSeconds since start sketch. Useful to check when it is time to call **read()** again, or for logging. - **float getHumidity()** returns last humidity read. -Will return the same value until **read()** or **readTemperature()** is called again. -- **float getTemperature()** returns last temperature read. Will return the same value until **read()** or **readHumidity()** is called again. +- **float getTemperature()** returns last temperature read. +Will return the same value until **read()** or **readTemperature()** is called again. -Note: read(), readTemperature() and readHumidity() blocks each other, +Note: **read()**, **readTemperature()** and **readHumidity()** block each other, so you can call only one of them every second. -#### Conversion delay +### Conversion delay -- **void setConversionDelay(uint8_t cd = 14)** default is 14 milliseconds (datasheet). -7 ms failed. 8 ms worked, so values below 8 are mapped to 8 in the library. -Expect 10 ms is pretty save. Use at own risk. +- **void setConversionDelay(uint8_t cd = 14)** default is 14 milliseconds +(from the datasheet). 7 milliseconds failed and 8 milliseconds worked. +So values below 8 milliseconds are mapped to 8 in the library. +Expect that 10 milliseconds is a pretty save value to work with. +Adjust this setting at your own risk. It might be that lower resolutions allow shorter delays. This is not tested. -- **uint8_t getConversionDelay()** returns set value. +- **uint8_t getConversionDelay()** returns the default (14) or last set value. -#### Offset +### Offset -Adding offsets works well in the "normal range" but might introduce +Adding offsets works well in the "normal range" but might introduce under- or overflow at the ends of the sensor range. These are not handled for temperature by the library (humidity since 0.1.7). - -- **void setHumidityOffset(float offset)** idem. -- **void setTemperatureOffset(float offset)** idem. + +- **void setHumidityOffset(float offset = 0.0)** idem. Default is set to +zero, for an easy reset off the offset. +There is no range check in this function, however, **read()** will +keep the value between 0 and 100%. +- **void setTemperatureOffset(float offset = 0.0)** idem. Default is set to +zero, for an easy reset off the offset. +There is no range check in this function. - **float getHumidityOffset()** idem. - **float getTemperatureOffset()** idem. -If the offset is not the same over the operational range, +If the offset is not the same over the operational range, consider a mapping function for temperature and humidity. e.g. https://github.com/RobTillaart/MultiMap -#### Configuration register +### Configuration register Check the datasheet for details of the register bits. -- **void setConfigRegister(uint16_t bitmask)** idem. Default value 0x1004. -- **uint16_t getConfigRegister()** idem. +- **bool setConfigRegister(uint16_t bitmask)** See table below. +Default value 0x1004. +Returns false if write to the config register fails. +Check **getLastError()** for possible cause. +- **uint16_t getConfigRegister()** idem. +If 0x0000 is returned it may indicate an error. +Check **getLastError()** for possible cause. + +Bits of configRegister, check datasheet for details. | bit | mask | name | description | |:-----:|:------:|:----------------|:--------------| -| 15 | 0x8000 | soft reset | 1 = reboot the sensor to default +| 15 | 0x8000 | soft reset | 1 = reboot the sensor to default | 14 | 0x4000 | clock stretch | 1 = ON, 0 = OFF (default) | 13 | 0x2000 | heater | 1 = ON, 0 = OFF (default) | 12 | 0x1000 | mode | 1 = read both (default), 0 = read T or RH @@ -207,39 +245,40 @@ Check the datasheet for details of the register bits. | 4 | 0x0010 | H-ALT | Humidity Alert status | 3 | 0x0008 | T-ALT | Temperature Alert status | 2 | 0x0004 | VCC enable | 1 = enable VCC measurement (default), 0 = disable -| 1-0 | 0x0003 | reserved. | do not change. +| 1-0 | 0x0003 | reserved. | do not change. -#### Getters / setters configuration register +### Getters / setters configuration register Note: setting **setConfigRegister(bitmask)** can be faster. -Wrapper functions for easy configuration. +Wrapper functions for easy configuration. Read the datasheet for the details. -- **void softReset()** sets the soft reset bit in the configuration, causing the sensor to reset. +- **void softReset()** sets the soft reset bit in the configuration, +causing the sensor to reset. - **void setI2CClockStretch(bool on = false)** check datasheet. - **bool getI2CClockStretch()** check datasheet. -- **void setHeaterOn(bool on = false)** switch on internal heater. +- **void setHeaterOn(bool on = false)** switch on internal heater. Can improve humidity readings. See datasheet for (limited) details. - **WARNING** User is responsible for timing as library does not support timing. - **bool getHeater()** Returns status of the heater. - **void setMeasurementMode(bool both = true)** both T and H or single value. -- **bool getMeasurementMode()** returns mode set above. +- **bool getMeasurementMode()** returns mode set above. - **bool getVCCstatus()** 1 == > 2.8V 0 == < 2.8V Useful when battery operated? - **void setTemperatureResolution(uint8_t res = 0)** 1 = 11 bit, 0 = 14 bit (default). - **uint8_t getTemperatureResolution()** idem. - **void setHumidityResolution(uint8_t res = 0)** 2 = 8 bit, 1 = 11 bit, 0 = 14 bit (default). - **uint8_t getHumidityResolution()** idem. -- **void setVCCenable(bool enable = false)** idem. -- **bool getVCCenable()** idem. +- **void setVCCenable(bool enable = false)** enable VCC measurement of the power supply. +- **bool getVCCenable()** return current status of enable bit. -#### Alert +### Alert See register 3 datasheet page 12 for details. -- **void setAlertTriggerMode(uint8_t mode)** see table below. +- **void setAlertTriggerMode(uint8_t mode = 0)** see table below. - **uint8_t getAlertTriggerMode()** returns 0, 1, 2 or 3. | mode | trigger | notes | @@ -252,7 +291,7 @@ See register 3 datasheet page 12 for details. - **bool getAlertPendingStatus()** idem. - **bool getAlertHumidityStatus()** idem. - **bool getAlertTemperatureStatus()** idem. -- **bool setAlertLevels(float temperature, float humidity)** +- **bool setAlertLevels(float temperature, float humidity)** - the values will be truncated to the nearest value possible. - the ALERT supports HIGH limit only ==> there is no LOW limit ALERT. - note: the datasheet is ambiguous with respect to the formula used. @@ -263,27 +302,53 @@ See register 3 datasheet page 12 for details. The ALERT pin triggers with a falling edge (from HIGH to LOW). -#### Voltage +### Supply voltage -VCC measurement should be enabled by means of **void setVCCenable(true)** -or by **setConfigRegister(0x0004)**. +(datasheet 1.1.5) +The chip has a feature to measure supply voltage (VCC) with 16bit output data. +VCC measurement should be enabled by means of **void setVCCenable(true)**. - **float getVoltage()** unclear what unit is used. -Best guess for now: 16 bit data implies ```voltage = 5.0V * value / 32768.0;``` +Best guess for now: 16 bit data implies ```voltage = 5.0V * value / 32767.0;``` Varied slightly 5.000 - 4.999 also for 3V3 power supply. -Conclusion: it is unclear how to interpret this register. +Conclusion: it is not 100 % clear how to interpret this register. -#### Meta data +### Meta data - **uint16_t getManufacturer()** returns 0x5959. -- **uint16_t getVersionID()** return value may differ. +- **uint16_t getVersionID()** return value may differ. Test returned 0x8305. -#### Register map +### Error handling + +Since 0.2.2 some error handling has been added. +This need to be improved in the future. + +- **getLastError()** returns 0 if all is OK. +If it doesn't return 0 an error occurred during **read()** et al. +The table gives some information where the problem occurred. +A call to **getLastError()** resets the internal error flag. + +Note: in case of e.g. an I2C error, the last values of Temperature +and Humidity do not change. + +| Error constant | value | Notes | +|:-------------------------|:--------:|:--------| +| CHT8305_OK | 0 | +| CHT8305_ERROR_ADDR | -10 | +| CHT8305_ERROR_I2C | -11 | +| CHT8305_ERROR_CONNECT | -12 | +| CHT8305_ERROR_BUFSIZE | -13 | +| CHT8305_ERROR_LASTREAD | -20 | +| CHT8305_ERROR_GENERIC | -999 | +| | 0xFFFF | patch | + + +### Register map See datasheet page 10 for details @@ -303,25 +368,26 @@ See datasheet page 10 for details #### Must - elaborate documentation. -- more testing (platforms) - #### Should -- test ESP32, other platforms? -- test performance. +- test other platforms + - ESP32, - test resolution bits. - delay ? - test configuration functions. - test ALERT functions. - test write / readRegister with a single uint16_t to simplify code. - +- test error handling. #### Could - parameter testing - parameter defaults? - +- investigate asynchronous reading + - request, ready (after 15 millis), fetch + - needs lastRequest timestamp +- configRegister => force bit 0 and 1 to 0 #### Wont diff --git a/libraries/CHT8305/examples/CHT8305_performance/CHT8305_performance.ino b/libraries/CHT8305/examples/CHT8305_performance/CHT8305_performance.ino new file mode 100644 index 00000000..97d7e907 --- /dev/null +++ b/libraries/CHT8305/examples/CHT8305_performance/CHT8305_performance.ino @@ -0,0 +1,83 @@ +// +// FILE: CHT8305_performance.ino +// AUTHOR: Rob Tillaart +// PURPOSE: I2C performance for CHT8305 I2C humidity & temperature sensor +// URL: https://github.com/RobTillaart/CHT8305 + +// Always check datasheet - front view +// +// +---------------+ +// VCC ----| VCC | +// SDA ----| SDA CHT8305 | CHECK DATASHEET. +// GND ----| GND | +// SCL ----| SCL | +// ? ----| AD0 | ? depends on address to select +// | | +// IRQ ----| ALERT | only if enabled. +// +---------------+ +// +// check datasheet +// VCC RED +// GND BLACK +// SDA YELLOW +// SCL WHITE + + +#include "CHT8305.h" + +CHT8305 CHT(0x40); // CHT8305_DEFAULT_ADDRESS = 0x40 + +uint32_t start, stop; + +void setup() +{ + Serial.begin(115200); + Serial.println(__FILE__); + Serial.print("CHT8305_LIB_VERSION: "); + Serial.println(CHT8305_LIB_VERSION); + Serial.println(); + delay(100); + + Wire.begin(); + CHT.begin(); + delay(1000); + + Serial.println("Note: reading temperature is blocking!\n"); + + Serial.println("SPEED\tREAD\tREAD_T\tREAD_H"); + for (uint32_t speed = 100000; speed <= 800000; speed += 50000) + { + Wire.setClock(speed); + + start = micros(); + CHT.read(); + stop = micros(); + Serial.print(speed); + Serial.print("\t"); + Serial.print(stop - start); + delay(1000); + + start = micros(); + CHT.readTemperature(); + stop = micros(); + Serial.print("\t"); + Serial.print(stop - start); + delay(1000); + + start = micros(); + CHT.readHumidity(); + stop = micros(); + Serial.print("\t"); + Serial.print(stop - start); + Serial.print("\n"); + delay(1000); + } +} + + +void loop() +{ +} + + +// -- END OF FILE -- diff --git a/libraries/CHT8305/examples/CHT8305_performance/performance_0.2.2.txt b/libraries/CHT8305/examples/CHT8305_performance/performance_0.2.2.txt new file mode 100644 index 00000000..2d596185 --- /dev/null +++ b/libraries/CHT8305/examples/CHT8305_performance/performance_0.2.2.txt @@ -0,0 +1,24 @@ +IDE: 1.8.19 +Board: Arduino + +CHT8305_LIB_VERSION: 0.2.2 + +Note: reading temperature is blocking! + +SPEED READ READ_T READ_H +100000 14824 14604 384 +150000 14596 14440 276 +200000 14480 14360 224 +250000 14416 14312 192 +300000 14368 14276 168 +350000 14340 14252 156 +400000 14332 14244 152 +450000 14308 14236 144 +500000 14300 14232 140 +550000 14284 14212 132 +600000 14280 14220 132 +650000 14272 14208 128 +700000 14268 14204 124 +750000 14260 14196 120 +800000 14264 14200 120 + diff --git a/libraries/CHT8305/examples/CHT8305_plotter/CHT8305_plotter.ino b/libraries/CHT8305/examples/CHT8305_plotter/CHT8305_plotter.ino new file mode 100644 index 00000000..b24b4a18 --- /dev/null +++ b/libraries/CHT8305/examples/CHT8305_plotter/CHT8305_plotter.ino @@ -0,0 +1,65 @@ +// +// FILE: CHT8305_plotter.ino +// AUTHOR: Rob Tillaart +// PURPOSE: Demo for CHT8305 I2C humidity & temperature sensor +// URL: https://github.com/RobTillaart/CHT8305 +// +// to be used with Arduino plotter. +// +// Always check datasheet - front view +// +// +---------------+ +// VCC ----| VCC | +// SDA ----| SDA CHT8305 | CHECK DATASHEET. +// GND ----| GND | +// SCL ----| SCL | +// ? ----| AD0 | ? depends on address to select +// | | +// IRQ ----| ALERT | only if enabled. +// +---------------+ +// +// check datasheet +// VCC RED +// GND BLACK +// SDA YELLOW +// SCL WHITE + + +#include "CHT8305.h" + +CHT8305 CHT(0x40); // CHT8305_DEFAULT_ADDRESS = 0x40 + + +void setup() +{ + Serial.begin(115200); + Serial.println(__FILE__); + Serial.print("CHT8305_LIB_VERSION: "); + Serial.println(CHT8305_LIB_VERSION); + Serial.println(); + + Wire.begin(); + Wire.setClock(400000); + CHT.begin(); + + delay(1000); +} + + +void loop() +{ + if (millis() - CHT.lastRead() >= 1000) + { + // READ DATA + CHT.read(); + + Serial.print(CHT.getLastError()); + Serial.print('\t'); + Serial.print(CHT.getHumidity()); + Serial.print('\t'); + Serial.println(CHT.getTemperature()); + } +} + + +// -- END OF FILE -- diff --git a/libraries/CHT8305/examples/CHT8305_plotter/Capture001.JPG b/libraries/CHT8305/examples/CHT8305_plotter/Capture001.JPG new file mode 100644 index 0000000000000000000000000000000000000000..f4d6bed0e2846b447ca70df14f866566bc368d35 GIT binary patch literal 19122 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 maintainer=Rob Tillaart sentence=Arduino library for CHT8305 temperature and humidity sensor.