From 28499bee82f3fb7d0ba9a976a1cc5e2311abd728 Mon Sep 17 00:00:00 2001 From: Cedric von Gunten Date: Sun, 7 Jan 2024 16:14:19 +0100 Subject: [PATCH] feature: added qmp6988 sensor --- README.md | 5 +- components/qmp6988/.eil.yml | 23 ++ components/qmp6988/CMakeLists.txt | 11 + components/qmp6988/LICENSE | 28 ++ components/qmp6988/component.mk | 2 + components/qmp6988/qmp6988.c | 319 ++++++++++++++++++ components/qmp6988/qmp6988.h | 197 +++++++++++ devtools/persons.yml | 4 + examples/qmp6988/default/CMakeLists.txt | 8 + examples/qmp6988/default/Makefile | 6 + examples/qmp6988/default/README.md | 16 + examples/qmp6988/default/main/CMakeLists.txt | 2 + .../qmp6988/default/main/Kconfig.projbuild | 43 +++ examples/qmp6988/default/main/component.mk | 1 + examples/qmp6988/default/main/main.c | 116 +++++++ .../default/sdkconfig.defaults.esp8266 | 1 + 16 files changed, 781 insertions(+), 1 deletion(-) create mode 100644 components/qmp6988/.eil.yml create mode 100644 components/qmp6988/CMakeLists.txt create mode 100644 components/qmp6988/LICENSE create mode 100644 components/qmp6988/component.mk create mode 100644 components/qmp6988/qmp6988.c create mode 100644 components/qmp6988/qmp6988.h create mode 100644 examples/qmp6988/default/CMakeLists.txt create mode 100644 examples/qmp6988/default/Makefile create mode 100644 examples/qmp6988/default/README.md create mode 100644 examples/qmp6988/default/main/CMakeLists.txt create mode 100644 examples/qmp6988/default/main/Kconfig.projbuild create mode 100644 examples/qmp6988/default/main/component.mk create mode 100644 examples/qmp6988/default/main/main.c create mode 100644 examples/qmp6988/default/sdkconfig.defaults.esp8266 diff --git a/README.md b/README.md index a2e85999..38e12bfb 100644 --- a/README.md +++ b/README.md @@ -252,6 +252,7 @@ or [GitLab examples](https://gitlab.com/UncleRus/esp-idf-lib/tree/master/example | **bmp280** | Driver for BMP280/BME280 digital pressure sensor | MIT | esp32, esp8266, esp32s2, esp32c3 | yes | | **dps310** | Driver for DPS310 barometric pressure sensor | ISC | esp32, esp8266, esp32s2, esp32c3 | yes | | **ms5611** | Driver for barometic pressure sensor MS5611-01BA03 | BSD-3-Clause | esp32, esp8266, esp32s2, esp32c3 | yes | +| **qmp6988** | Driver for QMP6988 digital temperature and pressure sensor | BSD-3-Clause | esp32, esp8266, esp32s2, esp32c3 | yes | ### Real-time clocks @@ -286,6 +287,7 @@ or [GitLab examples](https://gitlab.com/UncleRus/esp-idf-lib/tree/master/example | **mcp960x** | Driver for MCP9600/MCP9601, thermocouple EMF to temperature converter | BSD-3-Clause | esp32, esp8266, esp32s2, esp32c3 | yes | | **mcp9808** | Driver for MCP9808 digital temperature sensor | BSD-3-Clause | esp32, esp8266, esp32s2, esp32c3 | yes | | **ms5611** | Driver for barometic pressure sensor MS5611-01BA03 | BSD-3-Clause | esp32, esp8266, esp32s2, esp32c3 | yes | +| **qmp6988** | Driver for QMP6988 digital temperature and pressure sensor | BSD-3-Clause | esp32, esp8266, esp32s2, esp32c3 | yes | | **sfa3x** | Driver for SFA30 formaldehyde detection module (I2C) | BSD-3-Clause | esp32, esp8266, esp32s2, esp32c3 | yes | | **sht3x** | Driver for Sensirion SHT30/SHT31/SHT35 digital temperature and humidity sensor | BSD-3-Clause | esp32, esp8266, esp32s2, esp32c3 | yes | | **sht4x** | Driver for Sensirion SHT40/SHT41/SHT45 digital temperature and humidity sensor | BSD-3-Clause | esp32, esp8266, esp32s2, esp32c3 | yes | @@ -308,6 +310,7 @@ or [GitLab examples](https://gitlab.com/UncleRus/esp-idf-lib/tree/master/example - [BernhardG](https://gitlab.com/mrnice): `ms5611` - [BhuvanchandraD](https://github.com/bhuvanchandra): `ds3231` - [Brian Schwind](https://github.com/bschwind): `tsl2561` `tsl4531` +- [Cedric von Gunten](https://github.com/vonguced): `qmp6988` - [Christian Skjerning](https://github.com/slimcdk): `sts3x` - [David Douard](https://github.com/douardda): `mhz19b` - [Erriez](https://github.com/Erriez): `mhz19b` @@ -332,7 +335,7 @@ or [GitLab examples](https://gitlab.com/UncleRus/esp-idf-lib/tree/master/example - Pavel Merzlyakov: `ds1302` - [Raghav Jha](https://github.com/horsemann07): `mpu6050` - RichardA: `ds3231` -- [Ruslan V. Uss](https://github.com/UncleRus): `ads111x` `aht` `am2320` `bh1750` `bh1900nux` `bme680` `bmp180` `bmp280` `button` `calibration` `ccs811` `dht` `ds1302` `ds1307` `ds18x20` `ds3231` `ds3502` `encoder` `framebuffer` `hd44780` `hdc1000` `hmc5883l` `hx711` `i2cdev` `ina219` `ina260` `ina3221` `led_strip` `led_strip_spi` `max31725` `max31855` `max31865` `max7219` `mcp23008` `mcp23x17` `mcp342x` `mcp4725` `mcp960x` `mcp9808` `mpu6050` `ms5611` `onewire` `pca9557` `pca9685` `pcf8563` `pcf8574` `pcf8575` `pcf8591` `qmc5883l` `rda5807m` `scd30` `scd4x` `sfa3x` `sgp40` `sht3x` `sht4x` `si7021` `sts21` `sts3x` `tca6424a` `tca9548` `tca95x5` `tda74xx` `tsl2561` `tsl4531` `tsys01` `ultrasonic` `wiegand` +- [Ruslan V. Uss](https://github.com/UncleRus): `ads111x` `aht` `am2320` `bh1750` `bh1900nux` `bme680` `bmp180` `bmp280` `button` `calibration` `ccs811` `dht` `ds1302` `ds1307` `ds18x20` `ds3231` `ds3502` `encoder` `framebuffer` `hd44780` `hdc1000` `hmc5883l` `hx711` `i2cdev` `ina219` `ina260` `ina3221` `led_strip` `led_strip_spi` `max31725` `max31855` `max31865` `max7219` `mcp23008` `mcp23x17` `mcp342x` `mcp4725` `mcp960x` `mcp9808` `mpu6050` `ms5611` `onewire` `pca9557` `pca9685` `pcf8563` `pcf8574` `pcf8575` `pcf8591` `qmc5883l` `qmp6988` `rda5807m` `scd30` `scd4x` `sfa3x` `sgp40` `sht3x` `sht4x` `si7021` `sts21` `sts3x` `tca6424a` `tca9548` `tca95x5` `tda74xx` `tsl2561` `tsl4531` `tsys01` `ultrasonic` `wiegand` - [Sensirion AG](https://github.com/Sensirion): `scd30` `scd4x` `sfa3x` - [sheinz](https://github.com/sheinz): `bmp280` - [Thanh Pham](https://github.com/panoti): `pcf8591` diff --git a/components/qmp6988/.eil.yml b/components/qmp6988/.eil.yml new file mode 100644 index 00000000..210db5b0 --- /dev/null +++ b/components/qmp6988/.eil.yml @@ -0,0 +1,23 @@ +name: qmp6988 +description: Driver for QMP6988 digital temperature and pressure sensor +version: 0.0.1 +groups: + - temperature + - pressure +code_owners: vonguced +depends: + - i2cdev + - log + - esp_idf_lib_helpers +thread_safe: yes +targets: + - esp32 + - esp8266 + - esp32s2 + - esp32c3 +license: BSD-3 +copyrights: + - name: UncleRus + year: 2016 + - name: vonguced + year: 2024 diff --git a/components/qmp6988/CMakeLists.txt b/components/qmp6988/CMakeLists.txt new file mode 100644 index 00000000..2522582a --- /dev/null +++ b/components/qmp6988/CMakeLists.txt @@ -0,0 +1,11 @@ +if(${IDF_VERSION_MAJOR} STREQUAL 4 AND ${IDF_VERSION_MINOR} STREQUAL 1 AND ${IDF_VERSION_PATCH} STREQUAL 3) + set(req i2cdev log esp_idf_lib_helpers) +else() + set(req i2cdev log esp_idf_lib_helpers esp_timer) +endif() + +idf_component_register( + SRCS qmp6988.c + INCLUDE_DIRS . + REQUIRES ${req} +) \ No newline at end of file diff --git a/components/qmp6988/LICENSE b/components/qmp6988/LICENSE new file mode 100644 index 00000000..cc54db00 --- /dev/null +++ b/components/qmp6988/LICENSE @@ -0,0 +1,28 @@ +Copyright (c) 2019 Ruslan V. Uss (https://github.com/UncleRus) +Copyright (c) 2022 m5stack (https://github.com/m5stack) +Copyright (c) 2024 vonguced (https://github.com/vonguced) + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of itscontributors +may be used to endorse or promote products derived from this software without +specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/components/qmp6988/component.mk b/components/qmp6988/component.mk new file mode 100644 index 00000000..4071e351 --- /dev/null +++ b/components/qmp6988/component.mk @@ -0,0 +1,2 @@ +COMPONENT_ADD_INCLUDEDIRS = . +COMPONENT_DEPENDS = i2cdev log esp_idf_lib_helpers \ No newline at end of file diff --git a/components/qmp6988/qmp6988.c b/components/qmp6988/qmp6988.c new file mode 100644 index 00000000..ed90ce0b --- /dev/null +++ b/components/qmp6988/qmp6988.c @@ -0,0 +1,319 @@ +#include +#include +#include +#include +#include +#include +#include "qmp6988.h" + +#define I2C_FREQ_HZ 1000000 // 1MHz + +static const char *TAG = "qmp6988"; + +// due to the fact that ticks can be smaller than portTICK_PERIOD_MS, one and +// a half tick period added to the duration to be sure that waiting time for +// the results is long enough +#define TIME_TO_TICKS(ms) (1 + ((ms) + (portTICK_PERIOD_MS - 1) + portTICK_PERIOD_MS / 2) / portTICK_PERIOD_MS) + +#define CHECK(x) \ + do \ + { \ + esp_err_t __; \ + if ((__ = x) != ESP_OK) \ + return __; \ + } \ + while (0) +#define CHECK_ARG(VAL) \ + do \ + { \ + if (!(VAL)) \ + return ESP_ERR_INVALID_ARG; \ + } \ + while (0) + +static esp_err_t write_reg(qmp6988_t *dev, uint8_t out_reg, uint8_t cmd) +{ + I2C_DEV_TAKE_MUTEX(&dev->i2c_dev); + I2C_DEV_CHECK(&dev->i2c_dev, i2c_dev_write(&dev->i2c_dev, &out_reg, sizeof(out_reg), &cmd, sizeof(cmd))); + I2C_DEV_GIVE_MUTEX(&dev->i2c_dev); + + return ESP_OK; +} + +esp_err_t qmp6988_read_reg(qmp6988_t *dev, uint8_t out_reg, qmp6988_raw_data_t *raw_data) +{ + CHECK(i2c_dev_read(&dev->i2c_dev, &out_reg, sizeof(uint8_t), raw_data, sizeof(qmp6988_raw_data_t))); + return ESP_OK; +} + +esp_err_t qmp6988_device_check(qmp6988_t *dev) +{ + qmp6988_raw_data_t chip_id = 0x00; + qmp6988_read_reg(dev, QMP6988_CHIP_ID_REG, &chip_id); + + if (chip_id == QMP6988_CHIP_ID) + { + return ESP_OK; + } + else + { + ESP_LOGE(TAG, "QMP6988 chip id not matching. Expected: 0x%02X got: 0x%02X", QMP6988_CHIP_ID, chip_id); + return ESP_OK; + return ESP_ERR_INVALID_RESPONSE; + } + + return ESP_FAIL; +} + +esp_err_t qmp6988_get_calibration_data(qmp6988_t *dev) +{ + uint8_t a_data_uint8_tr[QMP6988_CALIBRATION_DATA_LENGTH] = { 0 }; + int len; + + for (len = 0; len < QMP6988_CALIBRATION_DATA_LENGTH; len += 1) + { + CHECK(qmp6988_read_reg(dev, QMP6988_CALIBRATION_DATA_START + len, &a_data_uint8_tr[len])); + } + + dev->qmp6988_cali.COE_a0 = (QMP6988_S32_t)(((QMP6988_S32_t)a_data_uint8_tr[18] << SHIFT_LEFT_12_POSITION) | (a_data_uint8_tr[19] << SHIFT_LEFT_4_POSITION) | (a_data_uint8_tr[24] & 0x0f)) << 12; + dev->qmp6988_cali.COE_a0 = dev->qmp6988_cali.COE_a0 >> 12; + + dev->qmp6988_cali.COE_a1 = (QMP6988_S16_t)((a_data_uint8_tr[20] << SHIFT_LEFT_8_POSITION) | a_data_uint8_tr[21]); + + dev->qmp6988_cali.COE_a2 = (QMP6988_S16_t)((a_data_uint8_tr[22] << SHIFT_LEFT_8_POSITION) | a_data_uint8_tr[23]); + + dev->qmp6988_cali.COE_b00 + = (QMP6988_S32_t)((((QMP6988_S32_t)a_data_uint8_tr[0] << SHIFT_LEFT_12_POSITION) | (a_data_uint8_tr[1] << SHIFT_LEFT_4_POSITION) | ((a_data_uint8_tr[24] & 0xf0) >> SHIFT_RIGHT_4_POSITION)) + << 12); + dev->qmp6988_cali.COE_b00 = dev->qmp6988_cali.COE_b00 >> 12; + + dev->qmp6988_cali.COE_bt1 = (QMP6988_S16_t)((a_data_uint8_tr[2] << SHIFT_LEFT_8_POSITION) | a_data_uint8_tr[3]); + + dev->qmp6988_cali.COE_bt2 = (QMP6988_S16_t)((a_data_uint8_tr[4] << SHIFT_LEFT_8_POSITION) | a_data_uint8_tr[5]); + + dev->qmp6988_cali.COE_bp1 = (QMP6988_S16_t)((a_data_uint8_tr[6] << SHIFT_LEFT_8_POSITION) | a_data_uint8_tr[7]); + + dev->qmp6988_cali.COE_b11 = (QMP6988_S16_t)((a_data_uint8_tr[8] << SHIFT_LEFT_8_POSITION) | a_data_uint8_tr[9]); + + dev->qmp6988_cali.COE_bp2 = (QMP6988_S16_t)((a_data_uint8_tr[10] << SHIFT_LEFT_8_POSITION) | a_data_uint8_tr[11]); + + dev->qmp6988_cali.COE_b12 = (QMP6988_S16_t)((a_data_uint8_tr[12] << SHIFT_LEFT_8_POSITION) | a_data_uint8_tr[13]); + + dev->qmp6988_cali.COE_b21 = (QMP6988_S16_t)((a_data_uint8_tr[14] << SHIFT_LEFT_8_POSITION) | a_data_uint8_tr[15]); + + dev->qmp6988_cali.COE_bp3 = (QMP6988_S16_t)((a_data_uint8_tr[16] << SHIFT_LEFT_8_POSITION) | a_data_uint8_tr[17]); + + dev->ik.a0 = dev->qmp6988_cali.COE_a0; // 20Q4 + dev->ik.b00 = dev->qmp6988_cali.COE_b00; // 20Q4 + + dev->ik.a1 = 3608L * (QMP6988_S32_t)dev->qmp6988_cali.COE_a1 - 1731677965L; // 31Q23 + dev->ik.a2 = 16889L * (QMP6988_S32_t)dev->qmp6988_cali.COE_a2 - 87619360L; // 30Q47 + + dev->ik.bt1 = 2982L * (QMP6988_S64_t)dev->qmp6988_cali.COE_bt1 + 107370906L; // 28Q15 + dev->ik.bt2 = 329854L * (QMP6988_S64_t)dev->qmp6988_cali.COE_bt2 + 108083093L; // 34Q38 + dev->ik.bp1 = 19923L * (QMP6988_S64_t)dev->qmp6988_cali.COE_bp1 + 1133836764L; // 31Q20 + dev->ik.b11 = 2406L * (QMP6988_S64_t)dev->qmp6988_cali.COE_b11 + 118215883L; // 28Q34 + dev->ik.bp2 = 3079L * (QMP6988_S64_t)dev->qmp6988_cali.COE_bp2 - 181579595L; // 29Q43 + dev->ik.b12 = 6846L * (QMP6988_S64_t)dev->qmp6988_cali.COE_b12 + 85590281L; // 29Q53 + dev->ik.b21 = 13836L * (QMP6988_S64_t)dev->qmp6988_cali.COE_b21 + 79333336L; // 29Q60 + dev->ik.bp3 = 2915L * (QMP6988_S64_t)dev->qmp6988_cali.COE_bp3 + 157155561L; // 28Q65 + return ESP_OK; +} + +esp_err_t qmp6988_setup_powermode(qmp6988_t *dev, qmp6988_power_mode_t power_mode) +{ + uint8_t data = 0x00; + + dev->power_mode = power_mode; + CHECK(qmp6988_read_reg(dev, QMP6988_CTRLMEAS_REG, &data)); + data &= 0xfc; + data |= power_mode; + CHECK(write_reg(dev, QMP6988_CTRLMEAS_REG, data)); + + vTaskDelay(TIME_TO_TICKS(20)); + + return ESP_OK; +} + +esp_err_t qmp6988_set_filter(qmp6988_t *dev, qmp6988_filter_t filter_mode) +{ + dev->filter_mode = filter_mode; + CHECK(write_reg(dev, QMP6988_CONFIG_REG, filter_mode)); + vTaskDelay(TIME_TO_TICKS(20)); + + return ESP_OK; +} + +esp_err_t qmp6988_set_p_oversampling(qmp6988_t *dev, qmp6988_oversampling_t oversampling_p_mode) +{ + qmp6988_raw_data_t data = 0x00; + + dev->oversampling_p_mode = oversampling_p_mode; + CHECK(qmp6988_read_reg(dev, QMP6988_CTRLMEAS_REG, &data)); + data &= 0xe3; + data |= (oversampling_p_mode << 2); + CHECK(write_reg(dev, QMP6988_CTRLMEAS_REG, data)); + vTaskDelay(TIME_TO_TICKS(20)); + + return ESP_OK; +} + +esp_err_t qmp6988_set_t_oversampling(qmp6988_t *dev, qmp6988_oversampling_t oversampling_t_mode) +{ + qmp6988_raw_data_t data = 0x00; + + dev->oversampling_t_mode = oversampling_t_mode; + CHECK(qmp6988_read_reg(dev, QMP6988_CTRLMEAS_REG, &data)); + data &= 0x1f; + data |= (oversampling_t_mode << 5); + CHECK(write_reg(dev, QMP6988_CTRLMEAS_REG, data)); + vTaskDelay(TIME_TO_TICKS(20)); + + return ESP_OK; +} + +QMP6988_S16_t qmp6988_conv_Tx_02e(qmp6988_ik_data_t *ik, QMP6988_S32_t dt) +{ + QMP6988_S16_t ret; + QMP6988_S64_t wk1, wk2; + + // wk1: 60Q4 // bit size + wk1 = ((QMP6988_S64_t)ik->a1 * (QMP6988_S64_t)dt); // 31Q23+24-1=54 (54Q23) + wk2 = ((QMP6988_S64_t)ik->a2 * (QMP6988_S64_t)dt) >> 14; // 30Q47+24-1=53 (39Q33) + wk2 = (wk2 * (QMP6988_S64_t)dt) >> 10; // 39Q33+24-1=62 (52Q23) + wk2 = ((wk1 + wk2) / 32767) >> 19; // 54,52->55Q23 (20Q04) + ret = (QMP6988_S16_t)((ik->a0 + wk2) >> 4); // 21Q4 -> 17Q0 + return ret; +} + +QMP6988_S32_t qmp6988_get_pressure_02e(qmp6988_ik_data_t *ik, QMP6988_S32_t dp, QMP6988_S16_t tx) +{ + QMP6988_S32_t ret; + QMP6988_S64_t wk1, wk2, wk3; + + // wk1 = 48Q16 // bit size + wk1 = ((QMP6988_S64_t)ik->bt1 * (QMP6988_S64_t)tx); // 28Q15+16-1=43 (43Q15) + wk2 = ((QMP6988_S64_t)ik->bp1 * (QMP6988_S64_t)dp) >> 5; // 31Q20+24-1=54 (49Q15) + wk1 += wk2; // 43,49->50Q15 + wk2 = ((QMP6988_S64_t)ik->bt2 * (QMP6988_S64_t)tx) >> 1; // 34Q38+16-1=49 (48Q37) + wk2 = (wk2 * (QMP6988_S64_t)tx) >> 8; // 48Q37+16-1=63 (55Q29) + wk3 = wk2; // 55Q29 + wk2 = ((QMP6988_S64_t)ik->b11 * (QMP6988_S64_t)tx) >> 4; // 28Q34+16-1=43 (39Q30) + wk2 = (wk2 * (QMP6988_S64_t)dp) >> 1; // 39Q30+24-1=62 (61Q29) + wk3 += wk2; // 55,61->62Q29 + wk2 = ((QMP6988_S64_t)ik->bp2 * (QMP6988_S64_t)dp) >> 13; // 29Q43+24-1=52 (39Q30) + wk2 = (wk2 * (QMP6988_S64_t)dp) >> 1; // 39Q30+24-1=62 (61Q29) + wk3 += wk2; // 62,61->63Q29 + wk1 += wk3 >> 14; // Q29 >> 14 -> Q15 + wk2 = ((QMP6988_S64_t)ik->b12 * (QMP6988_S64_t)tx); // 29Q53+16-1=45 (45Q53) + wk2 = (wk2 * (QMP6988_S64_t)tx) >> 22; // 45Q53+16-1=61 (39Q31) + wk2 = (wk2 * (QMP6988_S64_t)dp) >> 1; // 39Q31+24-1=62 (61Q30) + wk3 = wk2; // 61Q30 + wk2 = ((QMP6988_S64_t)ik->b21 * (QMP6988_S64_t)tx) >> 6; // 29Q60+16-1=45 (39Q54) + wk2 = (wk2 * (QMP6988_S64_t)dp) >> 23; // 39Q54+24-1=62 (39Q31) + wk2 = (wk2 * (QMP6988_S64_t)dp) >> 1; // 39Q31+24-1=62 (61Q20) + wk3 += wk2; // 61,61->62Q30 + wk2 = ((QMP6988_S64_t)ik->bp3 * (QMP6988_S64_t)dp) >> 12; // 28Q65+24-1=51 (39Q53) + wk2 = (wk2 * (QMP6988_S64_t)dp) >> 23; // 39Q53+24-1=62 (39Q30) + wk2 = (wk2 * (QMP6988_S64_t)dp); // 39Q30+24-1=62 (62Q30) + wk3 += wk2; // 62,62->63Q30 + wk1 += wk3 >> 15; // Q30 >> 15 = Q15 + wk1 /= 32767L; + wk1 >>= 11; // Q15 >> 7 = Q4 + wk1 += ik->b00; // Q4 + 20Q4 + // wk1 >>= 4; // 28Q4 -> 24Q0 + ret = (QMP6988_S32_t)wk1; + return ret; +} + +float qmp6988_calc_altitude(float pressure, float temp) +{ + float altitude; + + altitude = (pow((101325 / pressure), 1 / 5.257) - 1) * (temp + 273.15) / 0.0065; + ESP_LOGI(TAG, "altitude = %f\r\n", altitude); + return altitude; +} + +float qmp6988_calc_pressure(qmp6988_t *dev) +{ + QMP6988_U32_t P_read, T_read; + QMP6988_S32_t P_raw, T_raw; + uint8_t a_data_uint8_tr[6] = { 0 }; + QMP6988_S32_t T_int, P_int; + int len; + + // press + for (len = 0; len < 3; len += 1) + { + CHECK(qmp6988_read_reg(dev, QMP6988_PRESSURE_MSB_REG + len, &a_data_uint8_tr[len])); + } + P_read = (QMP6988_U32_t)((((QMP6988_U32_t)(a_data_uint8_tr[0])) << SHIFT_LEFT_16_POSITION) | (((QMP6988_U16_t)(a_data_uint8_tr[1])) << SHIFT_LEFT_8_POSITION) | (a_data_uint8_tr[2])); + P_raw = (QMP6988_S32_t)(P_read - SUBTRACTOR); + + // temp + for (len = 3; len < 6; len += 1) + { + CHECK(qmp6988_read_reg(dev, QMP6988_TEMPERATURE_MSB_REG + len - 3, &a_data_uint8_tr[len])); + } + T_read = (QMP6988_U32_t)((((QMP6988_U32_t)(a_data_uint8_tr[3])) << SHIFT_LEFT_16_POSITION) | (((QMP6988_U16_t)(a_data_uint8_tr[4])) << SHIFT_LEFT_8_POSITION) | (a_data_uint8_tr[5])); + T_raw = (QMP6988_S32_t)(T_read - SUBTRACTOR); + + T_int = qmp6988_conv_Tx_02e(&(dev->ik), T_raw); + P_int = qmp6988_get_pressure_02e(&(dev->ik), P_raw, T_int); + dev->temperature = (float)T_int / 256.0f; + dev->pressure = (float)P_int / 16.0f; + + return dev->pressure; +} + +float qmp6988_calc_temperature(qmp6988_t *dev) +{ + qmp6988_calc_pressure(dev); + return dev->temperature; +} + +/////////////////////////////////////////////////////////////////////////// + +esp_err_t qmp6988_init_desc(qmp6988_t *dev, uint8_t addr, i2c_port_t port, gpio_num_t sda_gpio, gpio_num_t scl_gpio) +{ + CHECK_ARG(dev); + + dev->i2c_dev.port = port; + dev->i2c_dev.addr = addr; + dev->i2c_dev.cfg.sda_io_num = sda_gpio; + dev->i2c_dev.cfg.scl_io_num = scl_gpio; +#if HELPER_TARGET_IS_ESP32 + dev->i2c_dev.cfg.master.clk_speed = I2C_FREQ_HZ; +#endif + + return i2c_dev_create_mutex(&dev->i2c_dev); +} + +esp_err_t qmp6988_free_desc(qmp6988_t *dev) +{ + CHECK_ARG(dev); + + return i2c_dev_delete_mutex(&dev->i2c_dev); +} + +esp_err_t qmp6988_init(qmp6988_t *dev) +{ + CHECK_ARG(dev); + + dev->power_mode = QMP6988_SLEEP_MODE; + dev->filter_mode = QMP6988_FILTERCOEFF_4; + dev->oversampling_t_mode = QMP6988_OVERSAMPLING_1X; + dev->oversampling_p_mode = QMP6988_OVERSAMPLING_8X; + + CHECK(qmp6988_device_check(dev)); + + CHECK(write_reg(dev, QMP6988_RESET_REG, 0xe6)); + vTaskDelay(TIME_TO_TICKS(20)); // need to wait a short moment after reset + CHECK(qmp6988_get_calibration_data(dev)); + CHECK(qmp6988_setup_powermode(dev, QMP6988_SLEEP_MODE)); + CHECK(qmp6988_set_filter(dev, QMP6988_FILTERCOEFF_4)); + CHECK(qmp6988_set_p_oversampling(dev, QMP6988_OVERSAMPLING_8X)); + CHECK(qmp6988_set_t_oversampling(dev, QMP6988_OVERSAMPLING_1X)); + return ESP_OK; +} diff --git a/components/qmp6988/qmp6988.h b/components/qmp6988/qmp6988.h new file mode 100644 index 00000000..8012215c --- /dev/null +++ b/components/qmp6988/qmp6988.h @@ -0,0 +1,197 @@ + +#ifndef __QMP6988_H__ +#define __QMP6988_H__ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define QMP6988_U16_t uint16_t +#define QMP6988_S16_t int16_t +#define QMP6988_U32_t uint32_t +#define QMP6988_S32_t int32_t +#define QMP6988_U64_t uint64_t +#define QMP6988_S64_t int64_t + +#define QMP6988_I2C_ADDR_GND 0x70 +#define QMP6988_I2C_ADDR_VDD 0x56 + +#define QMP6988_CHIP_ID 0x5C + +#define QMP6988_CHIP_ID_REG 0xD1 +#define QMP6988_RESET_REG 0xE0 /* Device reset register */ +#define QMP6988_DEVICE_STAT_REG 0xF3 /* Device state register */ +#define QMP6988_CTRLMEAS_REG 0xF4 /* Measurement Condition Control Register */ +#define QMP6988_PRESSURE_MSB_REG 0xF7 /* Pressure MSB Register */ +#define QMP6988_TEMPERATURE_MSB_REG 0xFA /* Temperature MSB Reg */ + +#define SUBTRACTOR 8388608 + +/* compensation calculation */ +#define QMP6988_CALIBRATION_DATA_START 0xA0 /* QMP6988 compensation coefficients */ +#define QMP6988_CALIBRATION_DATA_LENGTH 25 + +#define SHIFT_RIGHT_4_POSITION 4 +#define SHIFT_LEFT_2_POSITION 2 +#define SHIFT_LEFT_4_POSITION 4 +#define SHIFT_LEFT_5_POSITION 5 +#define SHIFT_LEFT_8_POSITION 8 +#define SHIFT_LEFT_12_POSITION 12 +#define SHIFT_LEFT_16_POSITION 16 + +/** + * Possible measurement modes + */ +typedef enum { + QMP6988_SLEEP_MODE = 0x00, // sleep mode + QMP6988_FORCED_MODE = 0x01, // one measurement then sleep again + QMP6988_NORMAL_MODE = 0x03 // power mode +} qmp6988_power_mode_t; + +#define QMP6988_CTRLMEAS_REG_MODE__POS 0 +#define QMP6988_CTRLMEAS_REG_MODE__MSK 0x03 +#define QMP6988_CTRLMEAS_REG_MODE__LEN 2 + +/** + * Possible filter modes + */ +typedef enum { + QMP6988_FILTERCOEFF_OFF = 0x00, + QMP6988_FILTERCOEFF_2 = 0x01, + QMP6988_FILTERCOEFF_4 = 0x02, + QMP6988_FILTERCOEFF_8 = 0x03, + QMP6988_FILTERCOEFF_16 = 0x04, + QMP6988_FILTERCOEFF_32 = 0x05 +} qmp6988_filter_t; + +#define QMP6988_CONFIG_REG 0xF1 /*IIR filter co-efficient setting Register*/ +#define QMP6988_CONFIG_REG_FILTER__POS 0 +#define QMP6988_CONFIG_REG_FILTER__MSK 0x07 +#define QMP6988_CONFIG_REG_FILTER__LEN 3 + +/** + * Possible oversampling modes + */ +typedef enum { + QMP6988_OVERSAMPLING_SKIPPED = 0x00, + QMP6988_OVERSAMPLING_1X = 0x01, + QMP6988_OVERSAMPLING_2X = 0x02, + QMP6988_OVERSAMPLING_4X = 0x03, + QMP6988_OVERSAMPLING_8X = 0x04, + QMP6988_OVERSAMPLING_16X = 0x05, + QMP6988_OVERSAMPLING_32X = 0x06, + QMP6988_OVERSAMPLING_64X = 0x07 +} qmp6988_oversampling_t; + +#define QMP6988_CTRLMEAS_REG_OSRST__POS 5 +#define QMP6988_CTRLMEAS_REG_OSRST__MSK 0xE0 +#define QMP6988_CTRLMEAS_REG_OSRST__LEN 3 + +#define QMP6988_CTRLMEAS_REG_OSRSP__POS 2 +#define QMP6988_CTRLMEAS_REG_OSRSP__MSK 0x1C +#define QMP6988_CTRLMEAS_REG_OSRSP__LEN 3 + +// #define QMP6988_RAW_DATA_SIZE 8 +typedef uint8_t qmp6988_raw_data_t; + +typedef struct _qmp6988_cali_data +{ + QMP6988_S32_t COE_a0; + QMP6988_S16_t COE_a1; + QMP6988_S16_t COE_a2; + QMP6988_S32_t COE_b00; + QMP6988_S16_t COE_bt1; + QMP6988_S16_t COE_bt2; + QMP6988_S16_t COE_bp1; + QMP6988_S16_t COE_b11; + QMP6988_S16_t COE_bp2; + QMP6988_S16_t COE_b12; + QMP6988_S16_t COE_b21; + QMP6988_S16_t COE_bp3; +} qmp6988_cali_data_t; + +typedef struct _qmp6988_fk_data +{ + float a0, b00; + float a1, a2, bt1, bt2, bp1, b11, bp2, b12, b21, bp3; +} qmp6988_fk_data_t; + +typedef struct _qmp6988_ik_data +{ + QMP6988_S32_t a0, b00; + QMP6988_S32_t a1, a2; + QMP6988_S64_t bt1, bt2, bp1, b11, bp2, b12, b21, bp3; +} qmp6988_ik_data_t; + +/** + * Device descriptor + */ +typedef struct +{ + i2c_dev_t i2c_dev; //!< I2C device descriptor + + qmp6988_power_mode_t power_mode; //!< used power mode + qmp6988_filter_t filter_mode; //!< used filter mode + qmp6988_oversampling_t oversampling_t_mode; //!< used oversampling temp mode + qmp6988_oversampling_t oversampling_p_mode; //!< used oversampling pressure mode + + qmp6988_cali_data_t qmp6988_cali; + qmp6988_ik_data_t ik; + + float temperature; + float pressure; + + // bool meas_started; //!< indicates whether measurement started + // uint64_t meas_start_time; //!< measurement start time in us + // bool meas_first; //!< first measurement in periodic mode +} qmp6988_t; + +/** + * @brief Initialize device descriptor + * + * @param dev Device descriptor + * @param port I2C port + * @param addr Device address + * @param sda_gpio SDA GPIO + * @param scl_gpio SCL GPIO + * @return `ESP_OK` on success + */ +esp_err_t qmp6988_init_desc(qmp6988_t *dev, uint8_t addr, i2c_port_t port, gpio_num_t sda_gpio, gpio_num_t scl_gpio); + +/** + * @brief Free device descriptor + * + * @param dev Device descriptor + * @return `ESP_OK` on success + */ +esp_err_t qmp6988_free_desc(qmp6988_t *dev); + +/** + * @brief Initialize sensor + * + * @param dev Device descriptor + * @return `ESP_OK` on success + */ +esp_err_t qmp6988_init(qmp6988_t *dev); + +esp_err_t qmp6988_setup_powermode(qmp6988_t *dev, qmp6988_power_mode_t power_mode); + +esp_err_t qmp6988_set_filter(qmp6988_t *dev, qmp6988_filter_t filter_mode); + +esp_err_t qmp6988_set_p_oversampling(qmp6988_t *dev, qmp6988_oversampling_t oversampling_p_mode); + +esp_err_t qmp6988_set_t_oversampling(qmp6988_t *dev, qmp6988_oversampling_t oversampling_t_mode); + +float qmp6988_calc_pressure(qmp6988_t *dev); + +float qmp6988_calc_temperature(qmp6988_t *dev); + +#ifdef __cplusplus +} +#endif + +#endif /* __QMP6988_H__ */ diff --git a/devtools/persons.yml b/devtools/persons.yml index 9e8c3989..ff3c7773 100644 --- a/devtools/persons.yml +++ b/devtools/persons.yml @@ -160,3 +160,7 @@ - name: mmarkwort full_name: Manuel Markwort gh_id: mmarkwort + +- name: vonguced + full_name: Cedric von Gunten + gh_id: vonguced diff --git a/examples/qmp6988/default/CMakeLists.txt b/examples/qmp6988/default/CMakeLists.txt new file mode 100644 index 00000000..cc8d7c9d --- /dev/null +++ b/examples/qmp6988/default/CMakeLists.txt @@ -0,0 +1,8 @@ +# The following lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +set(EXTRA_COMPONENT_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/../../../components) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(example-qmp6988) diff --git a/examples/qmp6988/default/Makefile b/examples/qmp6988/default/Makefile new file mode 100644 index 00000000..13be8b39 --- /dev/null +++ b/examples/qmp6988/default/Makefile @@ -0,0 +1,6 @@ +#V := 1 +PROJECT_NAME := example-qmp6988 + +EXTRA_COMPONENT_DIRS := $(CURDIR)/../../../components + +include $(IDF_PATH)/make/project.mk diff --git a/examples/qmp6988/default/README.md b/examples/qmp6988/default/README.md new file mode 100644 index 00000000..50eaa10c --- /dev/null +++ b/examples/qmp6988/default/README.md @@ -0,0 +1,16 @@ +# Example for `qmp6988` driver + +## What it does + +The example configures a `qmp6988` device. + +## Wiring + + +Connect `SCL` and `SDA` pins to the following pins with appropriate pull-up +resistors. + +| Name | Description | Defaults | +|------|-------------|----------| +| `CONFIG_EXAMPLE_I2C_MASTER_SCL` | GPIO number for `SCL` | "5" for `esp8266`, "6" for `esp32c3`, "19" for `esp32`, `esp32s2`, and `esp32s3` | +| `CONFIG_EXAMPLE_I2C_MASTER_SDA` | GPIO number for `SDA` | "4" for `esp8266`, "5" for `esp32c3`, "18" for `esp32`, `esp32s2`, and `esp32s3` | diff --git a/examples/qmp6988/default/main/CMakeLists.txt b/examples/qmp6988/default/main/CMakeLists.txt new file mode 100644 index 00000000..cf2c455c --- /dev/null +++ b/examples/qmp6988/default/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "main.c" + INCLUDE_DIRS ".") diff --git a/examples/qmp6988/default/main/Kconfig.projbuild b/examples/qmp6988/default/main/Kconfig.projbuild new file mode 100644 index 00000000..35e335cc --- /dev/null +++ b/examples/qmp6988/default/main/Kconfig.projbuild @@ -0,0 +1,43 @@ +menu "Example configuration" + choice EXAMPLE_SHT3X_DEMO + prompt "Demo mode" + default EXAMPLE_QMP6988_DEMO_NORMAL + help + Choose how to masure values from the sensor. See the main.c for + details. + + config EXAMPLE_QMP6988_DEMO_FORCED + bool "Forced mode" + help + In this example the qmp6988 makes a single measurement before entering + sleep mode again. + + config EXAMPLE_SHT3X_DEMO_NORMAL + bool "Normal mode" + help + In this example the qmp6988 makes continuous measurements. + + endchoice + + config EXAMPLE_QMP6988_ADDR + hex "I2C address of QMP6988" + default 0x70 + help + I2C address of QMP6988, default 0x70. + + config EXAMPLE_I2C_MASTER_SCL + int "SCL GPIO Number" + default 5 if IDF_TARGET_ESP8266 + default 6 if IDF_TARGET_ESP32C3 + default 19 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 + help + GPIO number for I2C Master clock line. + + config EXAMPLE_I2C_MASTER_SDA + int "SDA GPIO Number" + default 4 if IDF_TARGET_ESP8266 + default 5 if IDF_TARGET_ESP32C3 + default 18 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 + help + GPIO number for I2C Master data line. +endmenu diff --git a/examples/qmp6988/default/main/component.mk b/examples/qmp6988/default/main/component.mk new file mode 100644 index 00000000..7700ea99 --- /dev/null +++ b/examples/qmp6988/default/main/component.mk @@ -0,0 +1 @@ +COMPONENT_ADD_INCLUDEDIRS = . include/ diff --git a/examples/qmp6988/default/main/main.c b/examples/qmp6988/default/main/main.c new file mode 100644 index 00000000..3d8187c9 --- /dev/null +++ b/examples/qmp6988/default/main/main.c @@ -0,0 +1,116 @@ +/** + * Simple example with QMP6988 sensor. + * + * It shows different user task implementations in *forced mode* and + * *normal mode*. + */ + +#include +#include +#include +#include +#include +#include +#include + +/* float is used in printf(). you need non-default configuration in + * sdkconfig for ESP8266, which is enabled by default for this + * example. see sdkconfig.defaults.esp8266 + */ + +#ifndef APP_CPU_NUM +#define APP_CPU_NUM PRO_CPU_NUM +#endif + +static qmp6988_t dev; + +#if defined(CONFIG_EXAMPLE_QMP6988_DEMO_FORCED) + +/* + * User task that triggers a measurement every 5 seconds. Due to power + * efficiency reasons it uses *Forced* mode. + */ +void task(void *pvParameters) +{ + float temperature; + float pressure; + + TickType_t last_wakeup = xTaskGetTickCount(); + + /* We can lower the standard filter and oversampling to use less power + and in low accuracy examples e.g. a weather station */ + + ESP_ERROR_CHECK(qmp6988_set_filter(&dev, QMP6988_FILTERCOEFF_OFF)); + ESP_ERROR_CHECK(qmp6988_set_p_oversampling(&dev, QMP6988_OVERSAMPLING_2X)); + ESP_ERROR_CHECK(qmp6988_set_t_oversampling(&dev, QMP6988_OVERSAMPLING_1X)); + + while (1) + { + /*Set forced mode for qmp6988 (need to set this each time as it falls + back to sleep mode automatically after a single measurement). */ + if (qmp6988_setup_powermode(&dev, QMP6988_FORCED_MODE) == ESP_OK) + printf("Power mode set to forced mode\n"); + else + printf("Sensor error\n"); + + /* Wait until forced measurement is ready (constant time of at least 5.5 ms + according to the modified oversampling and filter modes.*/ + vTaskDelay(1); + + /*Calculate pressure values (this includes temperature as well, + as temp is needed to calc pressure)*/ + pressure = qmp6988_calc_pressure(&dev); + temperature = dev.temperature; + printf("QMP6988 Sensor: %.2f °C, %.2f Pa\n", temperature, pressure); + + // Wait until 5 seconds (cycle time) are over. + vTaskDelayUntil(&last_wakeup, pdMS_TO_TICKS(5000)); + } +} + +#else // CONFIG_QMP6988_DEMO_NORMAL +/* + * User task that fetches latest measurement results of sensor every 2 + * seconds. It starts the QMP6988 in normal (periodic) mode. + */ +void task(void *pvParameters) +{ + float temperature; + float pressure; + + // Set normal mode for qmp6988. + if (qmp6988_setup_powermode(&dev, QMP6988_NORMAL_MODE) == ESP_OK) + printf("Power mode set to normal mode\n"); + else + printf("Sensor error\n"); + + /* Wait until first measurement is ready (constant time of at least 10.6 ms + according to the default oversampling and filter modes.*/ + vTaskDelay(2); + + TickType_t last_wakeup = xTaskGetTickCount(); + + while (1) + { + /*Calculate pressure values (this includes temperature as well, + as temp is needed to calc pressure)*/ + pressure = qmp6988_calc_pressure(&dev); + temperature = dev.temperature; + printf("QMP6988 Sensor: %.2f °C, %.2f Pa\n", temperature, pressure); + + // Wait until 2 seconds (cycle time) are over. + vTaskDelayUntil(&last_wakeup, pdMS_TO_TICKS(2000)); + } +} + +#endif + +void app_main() +{ + ESP_ERROR_CHECK(i2cdev_init()); + + ESP_ERROR_CHECK(qmp6988_init_desc(&dev, CONFIG_EXAMPLE_QMP6988_ADDR, 0, CONFIG_EXAMPLE_I2C_MASTER_SDA, CONFIG_EXAMPLE_I2C_MASTER_SCL)); + ESP_ERROR_CHECK(qmp6988_init(&dev)); + + xTaskCreatePinnedToCore(task, "qmp6988_test", configMINIMAL_STACK_SIZE * 8, NULL, 5, NULL, APP_CPU_NUM); +} diff --git a/examples/qmp6988/default/sdkconfig.defaults.esp8266 b/examples/qmp6988/default/sdkconfig.defaults.esp8266 new file mode 100644 index 00000000..79a53171 --- /dev/null +++ b/examples/qmp6988/default/sdkconfig.defaults.esp8266 @@ -0,0 +1 @@ +CONFIG_NEWLIB_LIBRARY_LEVEL_NORMAL=y