xsns_09_bmp.ino 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650
  1. /*
  2. xsns_09_bmp.ino - BMP pressure, temperature, humidity and gas sensor support for Sonoff-Tasmota
  3. Copyright (C) 2018 Heiko Krupp and Theo Arends
  4. This program is free software: you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation, either version 3 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program. If not, see <http://www.gnu.org/licenses/>.
  14. */
  15. #ifdef USE_I2C
  16. #ifdef USE_BMP
  17. /*********************************************************************************************\
  18. * BMP085, BMP180, BMP280, BME280, BME680 - Pressure, Temperature, Humidity (BME280/BME680) and gas (BME680)
  19. *
  20. * Source: Heiko Krupp and Adafruit Industries
  21. *
  22. * I2C Address: 0x76 or 0x77
  23. \*********************************************************************************************/
  24. #define XSNS_09 9
  25. #define BMP_ADDR1 0x76
  26. #define BMP_ADDR2 0x77
  27. #define BMP180_CHIPID 0x55
  28. #define BMP280_CHIPID 0x58
  29. #define BME280_CHIPID 0x60
  30. #define BME680_CHIPID 0x61
  31. #define BMP_REGISTER_CHIPID 0xD0
  32. #define BMP_MAX_SENSORS 2
  33. const char kBmpTypes[] PROGMEM = "BMP180|BMP280|BME280|BME680";
  34. typedef struct {
  35. uint8_t bmp_address; // I2C bus address
  36. char bmp_name[7]; // Sensor name - "BMPXXX"
  37. uint8_t bmp_type;
  38. uint8_t bmp_model;
  39. #ifdef USE_BME680
  40. uint8_t bme680_state;
  41. float bmp_gas_resistance;
  42. #endif // USE_BME680
  43. float bmp_temperature;
  44. float bmp_pressure;
  45. float bmp_humidity;
  46. } bmp_sensors_t;
  47. uint8_t bmp_addresses[] = { BMP_ADDR1, BMP_ADDR2 };
  48. uint8_t bmp_count = 0;
  49. uint8_t bmp_once = 1;
  50. bmp_sensors_t *bmp_sensors = NULL;
  51. /*********************************************************************************************\
  52. * BMP085 and BME180
  53. \*********************************************************************************************/
  54. #define BMP180_REG_CONTROL 0xF4
  55. #define BMP180_REG_RESULT 0xF6
  56. #define BMP180_TEMPERATURE 0x2E
  57. #define BMP180_PRESSURE3 0xF4 // Max. oversampling -> OSS = 3
  58. #define BMP180_AC1 0xAA
  59. #define BMP180_AC2 0xAC
  60. #define BMP180_AC3 0xAE
  61. #define BMP180_AC4 0xB0
  62. #define BMP180_AC5 0xB2
  63. #define BMP180_AC6 0xB4
  64. #define BMP180_VB1 0xB6
  65. #define BMP180_VB2 0xB8
  66. #define BMP180_MB 0xBA
  67. #define BMP180_MC 0xBC
  68. #define BMP180_MD 0xBE
  69. #define BMP180_OSS 3
  70. typedef struct {
  71. int16_t cal_ac1;
  72. int16_t cal_ac2;
  73. int16_t cal_ac3;
  74. int16_t cal_b1;
  75. int16_t cal_b2;
  76. int16_t cal_mc;
  77. int16_t cal_md;
  78. uint16_t cal_ac4;
  79. uint16_t cal_ac5;
  80. uint16_t cal_ac6;
  81. } bmp180_cal_data_t;
  82. bmp180_cal_data_t *bmp180_cal_data = NULL;
  83. boolean Bmp180Calibration(uint8_t bmp_idx)
  84. {
  85. if (!bmp180_cal_data) {
  86. bmp180_cal_data = (bmp180_cal_data_t*)malloc(BMP_MAX_SENSORS * sizeof(bmp180_cal_data_t));
  87. }
  88. if (!bmp180_cal_data) { return false; }
  89. bmp180_cal_data[bmp_idx].cal_ac1 = I2cRead16(bmp_sensors[bmp_idx].bmp_address, BMP180_AC1);
  90. bmp180_cal_data[bmp_idx].cal_ac2 = I2cRead16(bmp_sensors[bmp_idx].bmp_address, BMP180_AC2);
  91. bmp180_cal_data[bmp_idx].cal_ac3 = I2cRead16(bmp_sensors[bmp_idx].bmp_address, BMP180_AC3);
  92. bmp180_cal_data[bmp_idx].cal_ac4 = I2cRead16(bmp_sensors[bmp_idx].bmp_address, BMP180_AC4);
  93. bmp180_cal_data[bmp_idx].cal_ac5 = I2cRead16(bmp_sensors[bmp_idx].bmp_address, BMP180_AC5);
  94. bmp180_cal_data[bmp_idx].cal_ac6 = I2cRead16(bmp_sensors[bmp_idx].bmp_address, BMP180_AC6);
  95. bmp180_cal_data[bmp_idx].cal_b1 = I2cRead16(bmp_sensors[bmp_idx].bmp_address, BMP180_VB1);
  96. bmp180_cal_data[bmp_idx].cal_b2 = I2cRead16(bmp_sensors[bmp_idx].bmp_address, BMP180_VB2);
  97. bmp180_cal_data[bmp_idx].cal_mc = I2cRead16(bmp_sensors[bmp_idx].bmp_address, BMP180_MC);
  98. bmp180_cal_data[bmp_idx].cal_md = I2cRead16(bmp_sensors[bmp_idx].bmp_address, BMP180_MD);
  99. // Check for Errors in calibration data. Value never is 0x0000 or 0xFFFF
  100. if (!bmp180_cal_data[bmp_idx].cal_ac1 |
  101. !bmp180_cal_data[bmp_idx].cal_ac2 |
  102. !bmp180_cal_data[bmp_idx].cal_ac3 |
  103. !bmp180_cal_data[bmp_idx].cal_ac4 |
  104. !bmp180_cal_data[bmp_idx].cal_ac5 |
  105. !bmp180_cal_data[bmp_idx].cal_ac6 |
  106. !bmp180_cal_data[bmp_idx].cal_b1 |
  107. !bmp180_cal_data[bmp_idx].cal_b2 |
  108. !bmp180_cal_data[bmp_idx].cal_mc |
  109. !bmp180_cal_data[bmp_idx].cal_md) {
  110. return false;
  111. }
  112. if ((bmp180_cal_data[bmp_idx].cal_ac1 == (int16_t)0xFFFF) |
  113. (bmp180_cal_data[bmp_idx].cal_ac2 == (int16_t)0xFFFF) |
  114. (bmp180_cal_data[bmp_idx].cal_ac3 == (int16_t)0xFFFF) |
  115. (bmp180_cal_data[bmp_idx].cal_ac4 == 0xFFFF) |
  116. (bmp180_cal_data[bmp_idx].cal_ac5 == 0xFFFF) |
  117. (bmp180_cal_data[bmp_idx].cal_ac6 == 0xFFFF) |
  118. (bmp180_cal_data[bmp_idx].cal_b1 == (int16_t)0xFFFF) |
  119. (bmp180_cal_data[bmp_idx].cal_b2 == (int16_t)0xFFFF) |
  120. (bmp180_cal_data[bmp_idx].cal_mc == (int16_t)0xFFFF) |
  121. (bmp180_cal_data[bmp_idx].cal_md == (int16_t)0xFFFF)) {
  122. return false;
  123. }
  124. return true;
  125. }
  126. void Bmp180Read(uint8_t bmp_idx)
  127. {
  128. if (!bmp180_cal_data) { return; }
  129. I2cWrite8(bmp_sensors[bmp_idx].bmp_address, BMP180_REG_CONTROL, BMP180_TEMPERATURE);
  130. delay(5); // 5ms conversion time
  131. int ut = I2cRead16(bmp_sensors[bmp_idx].bmp_address, BMP180_REG_RESULT);
  132. int32_t xt1 = (ut - (int32_t)bmp180_cal_data[bmp_idx].cal_ac6) * ((int32_t)bmp180_cal_data[bmp_idx].cal_ac5) >> 15;
  133. int32_t xt2 = ((int32_t)bmp180_cal_data[bmp_idx].cal_mc << 11) / (xt1 + (int32_t)bmp180_cal_data[bmp_idx].cal_md);
  134. int32_t bmp180_b5 = xt1 + xt2;
  135. bmp_sensors[bmp_idx].bmp_temperature = ((bmp180_b5 + 8) >> 4) / 10.0;
  136. I2cWrite8(bmp_sensors[bmp_idx].bmp_address, BMP180_REG_CONTROL, BMP180_PRESSURE3); // Highest resolution
  137. delay(2 + (4 << BMP180_OSS)); // 26ms conversion time at ultra high resolution
  138. uint32_t up = I2cRead24(bmp_sensors[bmp_idx].bmp_address, BMP180_REG_RESULT);
  139. up >>= (8 - BMP180_OSS);
  140. int32_t b6 = bmp180_b5 - 4000;
  141. int32_t x1 = ((int32_t)bmp180_cal_data[bmp_idx].cal_b2 * ((b6 * b6) >> 12)) >> 11;
  142. int32_t x2 = ((int32_t)bmp180_cal_data[bmp_idx].cal_ac2 * b6) >> 11;
  143. int32_t x3 = x1 + x2;
  144. int32_t b3 = ((((int32_t)bmp180_cal_data[bmp_idx].cal_ac1 * 4 + x3) << BMP180_OSS) + 2) >> 2;
  145. x1 = ((int32_t)bmp180_cal_data[bmp_idx].cal_ac3 * b6) >> 13;
  146. x2 = ((int32_t)bmp180_cal_data[bmp_idx].cal_b1 * ((b6 * b6) >> 12)) >> 16;
  147. x3 = ((x1 + x2) + 2) >> 2;
  148. uint32_t b4 = ((uint32_t)bmp180_cal_data[bmp_idx].cal_ac4 * (uint32_t)(x3 + 32768)) >> 15;
  149. uint32_t b7 = ((uint32_t)up - b3) * (uint32_t)(50000UL >> BMP180_OSS);
  150. int32_t p;
  151. if (b7 < 0x80000000) {
  152. p = (b7 * 2) / b4;
  153. }
  154. else {
  155. p = (b7 / b4) * 2;
  156. }
  157. x1 = (p >> 8) * (p >> 8);
  158. x1 = (x1 * 3038) >> 16;
  159. x2 = (-7357 * p) >> 16;
  160. p += ((x1 + x2 + (int32_t)3791) >> 4);
  161. bmp_sensors[bmp_idx].bmp_pressure = (float)p / 100.0; // convert to mbar
  162. }
  163. /*********************************************************************************************\
  164. * BMP280 and BME280
  165. *
  166. * Programmer : BMP280/BME280 Datasheet and Adafruit with changes by Theo Arends
  167. \*********************************************************************************************/
  168. #define BME280_REGISTER_CONTROLHUMID 0xF2
  169. #define BME280_REGISTER_CONTROL 0xF4
  170. #define BME280_REGISTER_CONFIG 0xF5
  171. #define BME280_REGISTER_PRESSUREDATA 0xF7
  172. #define BME280_REGISTER_TEMPDATA 0xFA
  173. #define BME280_REGISTER_HUMIDDATA 0xFD
  174. #define BME280_REGISTER_DIG_T1 0x88
  175. #define BME280_REGISTER_DIG_T2 0x8A
  176. #define BME280_REGISTER_DIG_T3 0x8C
  177. #define BME280_REGISTER_DIG_P1 0x8E
  178. #define BME280_REGISTER_DIG_P2 0x90
  179. #define BME280_REGISTER_DIG_P3 0x92
  180. #define BME280_REGISTER_DIG_P4 0x94
  181. #define BME280_REGISTER_DIG_P5 0x96
  182. #define BME280_REGISTER_DIG_P6 0x98
  183. #define BME280_REGISTER_DIG_P7 0x9A
  184. #define BME280_REGISTER_DIG_P8 0x9C
  185. #define BME280_REGISTER_DIG_P9 0x9E
  186. #define BME280_REGISTER_DIG_H1 0xA1
  187. #define BME280_REGISTER_DIG_H2 0xE1
  188. #define BME280_REGISTER_DIG_H3 0xE3
  189. #define BME280_REGISTER_DIG_H4 0xE4
  190. #define BME280_REGISTER_DIG_H5 0xE5
  191. #define BME280_REGISTER_DIG_H6 0xE7
  192. typedef struct {
  193. uint16_t dig_T1;
  194. int16_t dig_T2;
  195. int16_t dig_T3;
  196. uint16_t dig_P1;
  197. int16_t dig_P2;
  198. int16_t dig_P3;
  199. int16_t dig_P4;
  200. int16_t dig_P5;
  201. int16_t dig_P6;
  202. int16_t dig_P7;
  203. int16_t dig_P8;
  204. int16_t dig_P9;
  205. int16_t dig_H2;
  206. int16_t dig_H4;
  207. int16_t dig_H5;
  208. uint8_t dig_H1;
  209. uint8_t dig_H3;
  210. int8_t dig_H6;
  211. } Bme280CalibrationData_t;
  212. Bme280CalibrationData_t *Bme280CalibrationData = NULL;
  213. boolean Bmx280Calibrate(uint8_t bmp_idx)
  214. {
  215. // if (I2cRead8(bmp_address, BMP_REGISTER_CHIPID) != BME280_CHIPID) return false;
  216. if (!Bme280CalibrationData) {
  217. Bme280CalibrationData = (Bme280CalibrationData_t*)malloc(BMP_MAX_SENSORS * sizeof(Bme280CalibrationData_t));
  218. }
  219. if (!Bme280CalibrationData) { return false; }
  220. Bme280CalibrationData[bmp_idx].dig_T1 = I2cRead16LE(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_T1);
  221. Bme280CalibrationData[bmp_idx].dig_T2 = I2cReadS16_LE(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_T2);
  222. Bme280CalibrationData[bmp_idx].dig_T3 = I2cReadS16_LE(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_T3);
  223. Bme280CalibrationData[bmp_idx].dig_P1 = I2cRead16LE(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_P1);
  224. Bme280CalibrationData[bmp_idx].dig_P2 = I2cReadS16_LE(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_P2);
  225. Bme280CalibrationData[bmp_idx].dig_P3 = I2cReadS16_LE(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_P3);
  226. Bme280CalibrationData[bmp_idx].dig_P4 = I2cReadS16_LE(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_P4);
  227. Bme280CalibrationData[bmp_idx].dig_P5 = I2cReadS16_LE(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_P5);
  228. Bme280CalibrationData[bmp_idx].dig_P6 = I2cReadS16_LE(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_P6);
  229. Bme280CalibrationData[bmp_idx].dig_P7 = I2cReadS16_LE(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_P7);
  230. Bme280CalibrationData[bmp_idx].dig_P8 = I2cReadS16_LE(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_P8);
  231. Bme280CalibrationData[bmp_idx].dig_P9 = I2cReadS16_LE(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_P9);
  232. if (BME280_CHIPID == bmp_sensors[bmp_idx].bmp_type) { // #1051
  233. Bme280CalibrationData[bmp_idx].dig_H1 = I2cRead8(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_H1);
  234. Bme280CalibrationData[bmp_idx].dig_H2 = I2cReadS16_LE(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_H2);
  235. Bme280CalibrationData[bmp_idx].dig_H3 = I2cRead8(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_H3);
  236. Bme280CalibrationData[bmp_idx].dig_H4 = (I2cRead8(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_H4) << 4) | (I2cRead8(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_H4 + 1) & 0xF);
  237. Bme280CalibrationData[bmp_idx].dig_H5 = (I2cRead8(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_H5 + 1) << 4) | (I2cRead8(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_H5) >> 4);
  238. Bme280CalibrationData[bmp_idx].dig_H6 = (int8_t)I2cRead8(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_H6);
  239. I2cWrite8(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_CONTROL, 0x00); // sleep mode since writes to config can be ignored in normal mode (Datasheet 5.4.5/6 page 27)
  240. // Set before CONTROL_meas (DS 5.4.3)
  241. I2cWrite8(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_CONTROLHUMID, 0x01); // 1x oversampling
  242. I2cWrite8(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_CONFIG, 0xA0); // 1sec standby between measurements (to limit self heating), IIR filter off
  243. I2cWrite8(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_CONTROL, 0x27); // 1x oversampling, normal mode
  244. } else {
  245. I2cWrite8(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_CONTROL, 0xB7); // 16x oversampling, normal mode (Adafruit)
  246. }
  247. return true;
  248. }
  249. void Bme280Read(uint8_t bmp_idx)
  250. {
  251. if (!Bme280CalibrationData) { return; }
  252. int32_t adc_T = I2cRead24(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_TEMPDATA);
  253. adc_T >>= 4;
  254. int32_t vart1 = ((((adc_T >> 3) - ((int32_t)Bme280CalibrationData[bmp_idx].dig_T1 << 1))) * ((int32_t)Bme280CalibrationData[bmp_idx].dig_T2)) >> 11;
  255. int32_t vart2 = (((((adc_T >> 4) - ((int32_t)Bme280CalibrationData[bmp_idx].dig_T1)) * ((adc_T >> 4) - ((int32_t)Bme280CalibrationData[bmp_idx].dig_T1))) >> 12) *
  256. ((int32_t)Bme280CalibrationData[bmp_idx].dig_T3)) >> 14;
  257. int32_t t_fine = vart1 + vart2;
  258. float T = (t_fine * 5 + 128) >> 8;
  259. bmp_sensors[bmp_idx].bmp_temperature = T / 100.0;
  260. int32_t adc_P = I2cRead24(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_PRESSUREDATA);
  261. adc_P >>= 4;
  262. int64_t var1 = ((int64_t)t_fine) - 128000;
  263. int64_t var2 = var1 * var1 * (int64_t)Bme280CalibrationData[bmp_idx].dig_P6;
  264. var2 = var2 + ((var1 * (int64_t)Bme280CalibrationData[bmp_idx].dig_P5) << 17);
  265. var2 = var2 + (((int64_t)Bme280CalibrationData[bmp_idx].dig_P4) << 35);
  266. var1 = ((var1 * var1 * (int64_t)Bme280CalibrationData[bmp_idx].dig_P3) >> 8) + ((var1 * (int64_t)Bme280CalibrationData[bmp_idx].dig_P2) << 12);
  267. var1 = (((((int64_t)1) << 47) + var1)) * ((int64_t)Bme280CalibrationData[bmp_idx].dig_P1) >> 33;
  268. if (0 == var1) {
  269. return; // avoid exception caused by division by zero
  270. }
  271. int64_t p = 1048576 - adc_P;
  272. p = (((p << 31) - var2) * 3125) / var1;
  273. var1 = (((int64_t)Bme280CalibrationData[bmp_idx].dig_P9) * (p >> 13) * (p >> 13)) >> 25;
  274. var2 = (((int64_t)Bme280CalibrationData[bmp_idx].dig_P8) * p) >> 19;
  275. p = ((p + var1 + var2) >> 8) + (((int64_t)Bme280CalibrationData[bmp_idx].dig_P7) << 4);
  276. bmp_sensors[bmp_idx].bmp_pressure = (float)p / 25600.0;
  277. if (BMP280_CHIPID == bmp_sensors[bmp_idx].bmp_type) { return; }
  278. int32_t adc_H = I2cRead16(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_HUMIDDATA);
  279. int32_t v_x1_u32r = (t_fine - ((int32_t)76800));
  280. v_x1_u32r = (((((adc_H << 14) - (((int32_t)Bme280CalibrationData[bmp_idx].dig_H4) << 20) -
  281. (((int32_t)Bme280CalibrationData[bmp_idx].dig_H5) * v_x1_u32r)) + ((int32_t)16384)) >> 15) *
  282. (((((((v_x1_u32r * ((int32_t)Bme280CalibrationData[bmp_idx].dig_H6)) >> 10) *
  283. (((v_x1_u32r * ((int32_t)Bme280CalibrationData[bmp_idx].dig_H3)) >> 11) + ((int32_t)32768))) >> 10) +
  284. ((int32_t)2097152)) * ((int32_t)Bme280CalibrationData[bmp_idx].dig_H2) + 8192) >> 14));
  285. v_x1_u32r = (v_x1_u32r - (((((v_x1_u32r >> 15) * (v_x1_u32r >> 15)) >> 7) *
  286. ((int32_t)Bme280CalibrationData[bmp_idx].dig_H1)) >> 4));
  287. v_x1_u32r = (v_x1_u32r < 0) ? 0 : v_x1_u32r;
  288. v_x1_u32r = (v_x1_u32r > 419430400) ? 419430400 : v_x1_u32r;
  289. float h = (v_x1_u32r >> 12);
  290. bmp_sensors[bmp_idx].bmp_humidity = h / 1024.0;
  291. }
  292. #ifdef USE_BME680
  293. /*********************************************************************************************\
  294. * BME680 support by Bosch https://github.com/BoschSensortec/BME680_driver
  295. \*********************************************************************************************/
  296. #include <bme680.h>
  297. struct bme680_dev *gas_sensor = NULL;
  298. static void BmeDelayMs(uint32_t ms)
  299. {
  300. delay(ms);
  301. }
  302. boolean Bme680Init(uint8_t bmp_idx)
  303. {
  304. if (!gas_sensor) {
  305. gas_sensor = (bme680_dev*)malloc(BMP_MAX_SENSORS * sizeof(bme680_dev));
  306. }
  307. if (!gas_sensor) { return false; }
  308. gas_sensor[bmp_idx].dev_id = bmp_sensors[bmp_idx].bmp_address;
  309. gas_sensor[bmp_idx].intf = BME680_I2C_INTF;
  310. gas_sensor[bmp_idx].read = &I2cReadBuffer;
  311. gas_sensor[bmp_idx].write = &I2cWriteBuffer;
  312. gas_sensor[bmp_idx].delay_ms = BmeDelayMs;
  313. /* amb_temp can be set to 25 prior to configuring the gas sensor
  314. * or by performing a few temperature readings without operating the gas sensor.
  315. */
  316. gas_sensor[bmp_idx].amb_temp = 25;
  317. int8_t rslt = BME680_OK;
  318. rslt = bme680_init(&gas_sensor[bmp_idx]);
  319. if (rslt != BME680_OK) { return false; }
  320. /* Set the temperature, pressure and humidity settings */
  321. gas_sensor[bmp_idx].tph_sett.os_hum = BME680_OS_2X;
  322. gas_sensor[bmp_idx].tph_sett.os_pres = BME680_OS_4X;
  323. gas_sensor[bmp_idx].tph_sett.os_temp = BME680_OS_8X;
  324. gas_sensor[bmp_idx].tph_sett.filter = BME680_FILTER_SIZE_3;
  325. /* Set the remaining gas sensor settings and link the heating profile */
  326. gas_sensor[bmp_idx].gas_sett.run_gas = BME680_ENABLE_GAS_MEAS;
  327. /* Create a ramp heat waveform in 3 steps */
  328. gas_sensor[bmp_idx].gas_sett.heatr_temp = 320; /* degree Celsius */
  329. gas_sensor[bmp_idx].gas_sett.heatr_dur = 150; /* milliseconds */
  330. /* Select the power mode */
  331. /* Must be set before writing the sensor configuration */
  332. gas_sensor[bmp_idx].power_mode = BME680_FORCED_MODE;
  333. /* Set the required sensor settings needed */
  334. uint8_t set_required_settings = BME680_OST_SEL | BME680_OSP_SEL | BME680_OSH_SEL | BME680_FILTER_SEL | BME680_GAS_SENSOR_SEL;
  335. /* Set the desired sensor configuration */
  336. rslt = bme680_set_sensor_settings(set_required_settings,&gas_sensor[bmp_idx]);
  337. if (rslt != BME680_OK) { return false; }
  338. bmp_sensors[bmp_idx].bme680_state = 0;
  339. return true;
  340. }
  341. void Bme680Read(uint8_t bmp_idx)
  342. {
  343. if (!gas_sensor) { return; }
  344. int8_t rslt = BME680_OK;
  345. if (BME680_CHIPID == bmp_sensors[bmp_idx].bmp_type) {
  346. if (0 == bmp_sensors[bmp_idx].bme680_state) {
  347. /* Trigger the next measurement if you would like to read data out continuously */
  348. rslt = bme680_set_sensor_mode(&gas_sensor[bmp_idx]);
  349. if (rslt != BME680_OK) { return; }
  350. /* Get the total measurement duration so as to sleep or wait till the
  351. * measurement is complete */
  352. // uint16_t meas_period;
  353. // bme680_get_profile_dur(&meas_period, &gas_sensor[bmp_idx]);
  354. // delay(meas_period); /* Delay till the measurement is ready */ // 183 mSec - we'll wait a second
  355. bmp_sensors[bmp_idx].bme680_state = 1;
  356. } else {
  357. bmp_sensors[bmp_idx].bme680_state = 0;
  358. struct bme680_field_data data;
  359. rslt = bme680_get_sensor_data(&data, &gas_sensor[bmp_idx]);
  360. if (rslt != BME680_OK) { return; }
  361. bmp_sensors[bmp_idx].bmp_temperature = data.temperature / 100.0;
  362. bmp_sensors[bmp_idx].bmp_humidity = data.humidity / 1000.0;
  363. bmp_sensors[bmp_idx].bmp_pressure = data.pressure / 100.0;
  364. /* Avoid using measurements from an unstable heating setup */
  365. if (data.status & BME680_GASM_VALID_MSK) {
  366. bmp_sensors[bmp_idx].bmp_gas_resistance = data.gas_resistance / 1000.0;
  367. } else {
  368. bmp_sensors[bmp_idx].bmp_gas_resistance = 0;
  369. }
  370. }
  371. }
  372. return;
  373. }
  374. #endif // USE_BME680
  375. /********************************************************************************************/
  376. void BmpDetect(void)
  377. {
  378. if (bmp_count) return;
  379. int bmp_sensor_size = BMP_MAX_SENSORS * sizeof(bmp_sensors_t);
  380. if (!bmp_sensors) {
  381. bmp_sensors = (bmp_sensors_t*)malloc(bmp_sensor_size);
  382. }
  383. if (!bmp_sensors) { return; }
  384. memset(bmp_sensors, 0, bmp_sensor_size); // Init defaults to 0
  385. for (byte i = 0; i < BMP_MAX_SENSORS; i++) {
  386. uint8_t bmp_type = I2cRead8(bmp_addresses[i], BMP_REGISTER_CHIPID);
  387. if (bmp_type) {
  388. bmp_sensors[bmp_count].bmp_address = bmp_addresses[i];
  389. bmp_sensors[bmp_count].bmp_type = bmp_type;
  390. bmp_sensors[bmp_count].bmp_model = 0;
  391. boolean success = false;
  392. switch (bmp_type) {
  393. case BMP180_CHIPID:
  394. success = Bmp180Calibration(bmp_count);
  395. break;
  396. case BME280_CHIPID:
  397. bmp_sensors[bmp_count].bmp_model++; // 2
  398. case BMP280_CHIPID:
  399. bmp_sensors[bmp_count].bmp_model++; // 1
  400. success = Bmx280Calibrate(bmp_count);
  401. break;
  402. #ifdef USE_BME680
  403. case BME680_CHIPID:
  404. bmp_sensors[bmp_count].bmp_model = 3; // 3
  405. success = Bme680Init(bmp_count);
  406. break;
  407. #endif // USE_BME680
  408. }
  409. if (success) {
  410. GetTextIndexed(bmp_sensors[bmp_count].bmp_name, sizeof(bmp_sensors[bmp_count].bmp_name), bmp_sensors[bmp_count].bmp_model, kBmpTypes);
  411. snprintf_P(log_data, sizeof(log_data), S_LOG_I2C_FOUND_AT, bmp_sensors[bmp_count].bmp_name, bmp_sensors[bmp_count].bmp_address);
  412. AddLog(LOG_LEVEL_DEBUG);
  413. bmp_count++;
  414. }
  415. }
  416. }
  417. }
  418. void BmpRead(void)
  419. {
  420. if (!bmp_sensors) { return; }
  421. for (byte bmp_idx = 0; bmp_idx < bmp_count; bmp_idx++) {
  422. switch (bmp_sensors[bmp_idx].bmp_type) {
  423. case BMP180_CHIPID:
  424. Bmp180Read(bmp_idx);
  425. break;
  426. case BMP280_CHIPID:
  427. case BME280_CHIPID:
  428. Bme280Read(bmp_idx);
  429. break;
  430. #ifdef USE_BME680
  431. case BME680_CHIPID:
  432. Bme680Read(bmp_idx);
  433. break;
  434. #endif // USE_BME680
  435. }
  436. }
  437. SetGlobalValues(ConvertTemp(bmp_sensors[0].bmp_temperature), bmp_sensors[0].bmp_humidity);
  438. }
  439. void BmpEverySecond(void)
  440. {
  441. if (91 == (uptime %100)) {
  442. // 1mS
  443. BmpDetect();
  444. }
  445. else {
  446. // 2mS
  447. BmpRead();
  448. }
  449. }
  450. void BmpShow(boolean json)
  451. {
  452. if (!bmp_sensors) { return; }
  453. for (byte bmp_idx = 0; bmp_idx < bmp_count; bmp_idx++) {
  454. if (bmp_sensors[bmp_idx].bmp_type) {
  455. float bmp_sealevel = 0.0;
  456. if (bmp_sensors[bmp_idx].bmp_pressure != 0.0) {
  457. bmp_sealevel = (bmp_sensors[bmp_idx].bmp_pressure / FastPrecisePow(1.0 - ((float)Settings.altitude / 44330.0), 5.255)) - 21.6;
  458. bmp_sealevel = ConvertPressure(bmp_sealevel);
  459. }
  460. float bmp_temperature = ConvertTemp(bmp_sensors[bmp_idx].bmp_temperature);
  461. float bmp_pressure = ConvertPressure(bmp_sensors[bmp_idx].bmp_pressure);
  462. char name[10];
  463. snprintf(name, sizeof(name), bmp_sensors[bmp_idx].bmp_name);
  464. if (bmp_count > 1) {
  465. snprintf_P(name, sizeof(name), PSTR("%s-%02X"), name, bmp_sensors[bmp_idx].bmp_address); // BMXXXX-XX
  466. }
  467. char temperature[33];
  468. dtostrfd(bmp_temperature, Settings.flag2.temperature_resolution, temperature);
  469. char pressure[33];
  470. dtostrfd(bmp_pressure, Settings.flag2.pressure_resolution, pressure);
  471. char sea_pressure[33];
  472. dtostrfd(bmp_sealevel, Settings.flag2.pressure_resolution, sea_pressure);
  473. char humidity[33];
  474. dtostrfd(bmp_sensors[bmp_idx].bmp_humidity, Settings.flag2.humidity_resolution, humidity);
  475. #ifdef USE_BME680
  476. char gas_resistance[33];
  477. dtostrfd(bmp_sensors[bmp_idx].bmp_gas_resistance, 2, gas_resistance);
  478. #endif // USE_BME680
  479. if (json) {
  480. char json_humidity[40];
  481. snprintf_P(json_humidity, sizeof(json_humidity), PSTR(",\"" D_JSON_HUMIDITY "\":%s"), humidity);
  482. char json_sealevel[40];
  483. snprintf_P(json_sealevel, sizeof(json_sealevel), PSTR(",\"" D_JSON_PRESSUREATSEALEVEL "\":%s"), sea_pressure);
  484. #ifdef USE_BME680
  485. char json_gas[40];
  486. snprintf_P(json_gas, sizeof(json_gas), PSTR(",\"" D_JSON_GAS "\":%s"), gas_resistance);
  487. snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"%s\":{\"" D_JSON_TEMPERATURE "\":%s%s,\"" D_JSON_PRESSURE "\":%s%s%s}"),
  488. mqtt_data,
  489. name,
  490. temperature,
  491. (bmp_sensors[bmp_idx].bmp_model >= 2) ? json_humidity : "",
  492. pressure,
  493. (Settings.altitude != 0) ? json_sealevel : "",
  494. (bmp_sensors[bmp_idx].bmp_model >= 3) ? json_gas : "");
  495. #else
  496. snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"%s\":{\"" D_JSON_TEMPERATURE "\":%s%s,\"" D_JSON_PRESSURE "\":%s%s}"),
  497. mqtt_data, name, temperature, (bmp_sensors[bmp_idx].bmp_model >= 2) ? json_humidity : "", pressure, (Settings.altitude != 0) ? json_sealevel : "");
  498. #endif // USE_BME680
  499. #ifdef USE_DOMOTICZ
  500. if ((0 == tele_period) && (0 == bmp_idx)) { // We want the same first sensor to report to Domoticz in case a read is missed
  501. DomoticzTempHumPressureSensor(temperature, humidity, pressure);
  502. #ifdef USE_BME680
  503. if (bmp_sensors[bmp_idx].bmp_model >= 3) { DomoticzSensor(DZ_AIRQUALITY, (uint32_t)bmp_sensors[bmp_idx].bmp_gas_resistance); }
  504. #endif // USE_BME680
  505. }
  506. #endif // USE_DOMOTICZ
  507. #ifdef USE_KNX
  508. if (0 == tele_period) {
  509. KnxSensor(KNX_TEMPERATURE, bmp_temperature);
  510. KnxSensor(KNX_HUMIDITY, bmp_sensors[bmp_idx].bmp_humidity);
  511. }
  512. #endif // USE_KNX
  513. #ifdef USE_WEBSERVER
  514. } else {
  515. snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_SNS_TEMP, mqtt_data, name, temperature, TempUnit());
  516. if (bmp_sensors[bmp_idx].bmp_model >= 2) {
  517. snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_SNS_HUM, mqtt_data, name, humidity);
  518. }
  519. snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_SNS_PRESSURE, mqtt_data, name, pressure, PressureUnit().c_str());
  520. if (Settings.altitude != 0) {
  521. snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_SNS_SEAPRESSURE, mqtt_data, name, sea_pressure, PressureUnit().c_str());
  522. }
  523. #ifdef USE_BME680
  524. if (bmp_sensors[bmp_idx].bmp_model >= 3) {
  525. snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s{s}%s " D_GAS "{m}%s " D_UNIT_KILOOHM "{e}"), mqtt_data, name, gas_resistance);
  526. }
  527. #endif // USE_BME680
  528. #endif // USE_WEBSERVER
  529. }
  530. }
  531. }
  532. }
  533. /*********************************************************************************************\
  534. * Interface
  535. \*********************************************************************************************/
  536. boolean Xsns09(byte function)
  537. {
  538. boolean result = false;
  539. if (i2c_flg) {
  540. switch (function) {
  541. case FUNC_INIT:
  542. BmpDetect();
  543. break;
  544. case FUNC_EVERY_SECOND:
  545. BmpEverySecond();
  546. break;
  547. case FUNC_JSON_APPEND:
  548. BmpShow(1);
  549. break;
  550. #ifdef USE_WEBSERVER
  551. case FUNC_WEB_APPEND:
  552. BmpShow(0);
  553. break;
  554. #endif // USE_WEBSERVER
  555. }
  556. }
  557. return result;
  558. }
  559. #endif // USE_BMP
  560. #endif // USE_I2C