Mirror of espurna firmware for wireless switches and more
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

236 lines
7.6 KiB

  1. // -----------------------------------------------------------------------------
  2. // BMP085/BMP180 Sensor over I2C
  3. // Copyright (C) 2019 by Xose Pérez <xose dot perez at gmail dot com>
  4. // -----------------------------------------------------------------------------
  5. #if SENSOR_SUPPORT && BMP180_SUPPORT
  6. #pragma once
  7. #include "I2CSensor.h"
  8. #include "../utils.h"
  9. #define BMP180_CHIP_ID 0x55
  10. #define BMP180_REGISTER_CHIPID 0xD0
  11. #define BMP180_REGISTER_CAL_AC1 0xAA
  12. #define BMP180_REGISTER_CAL_AC2 0xAC
  13. #define BMP180_REGISTER_CAL_AC3 0xAE
  14. #define BMP180_REGISTER_CAL_AC4 0xB0
  15. #define BMP180_REGISTER_CAL_AC5 0xB2
  16. #define BMP180_REGISTER_CAL_AC6 0xB4
  17. #define BMP180_REGISTER_CAL_B1 0xB6
  18. #define BMP180_REGISTER_CAL_B2 0xB8
  19. #define BMP180_REGISTER_CAL_MB 0xBA
  20. #define BMP180_REGISTER_CAL_MC 0xBC
  21. #define BMP180_REGISTER_CAL_MD 0xBE
  22. #define BMP180_REGISTER_VERSION 0xD1
  23. #define BMP180_REGISTER_SOFTRESET 0xE0
  24. #define BMP180_REGISTER_CONTROL 0xF4
  25. #define BMP180_REGISTER_TEMPDATA 0xF6
  26. #define BMP180_REGISTER_PRESSUREDATA 0xF6
  27. #define BMP180_REGISTER_READTEMPCMD 0x2E
  28. #define BMP180_REGISTER_READPRESSURECMD 0x34
  29. class BMP180Sensor : public I2CSensor<> {
  30. public:
  31. // ---------------------------------------------------------------------
  32. // Sensor API
  33. // ---------------------------------------------------------------------
  34. unsigned char id() const override {
  35. return SENSOR_BMP180_ID;
  36. }
  37. unsigned char count() const override {
  38. return 2;
  39. }
  40. // Initialization method, must be idempotent
  41. void begin() override {
  42. if (!_dirty) return;
  43. _init();
  44. _dirty = !_ready;
  45. }
  46. // Descriptive name of the sensor
  47. String description() const override {
  48. char buffer[20];
  49. snprintf_P(buffer, sizeof(buffer),
  50. PSTR("BMP180 @ I2C (0x%02X)"), lockedAddress());
  51. return String(buffer);
  52. }
  53. // Type for slot # index
  54. unsigned char type(unsigned char index) const override {
  55. if (index == 0) return MAGNITUDE_TEMPERATURE;
  56. if (index == 1) return MAGNITUDE_PRESSURE;
  57. return MAGNITUDE_NONE;
  58. }
  59. // Pre-read hook (usually to populate registers with up-to-date data)
  60. void pre() override {
  61. if (_run_init) {
  62. i2cClearBus();
  63. _init();
  64. }
  65. if (_chip == 0) {
  66. resetUnknown();
  67. return;
  68. }
  69. _error = SENSOR_ERROR_OK;
  70. _error = _read(lockedAddress());
  71. if (_error != SENSOR_ERROR_OK) {
  72. _run_init = true;
  73. }
  74. }
  75. // Current value for slot # index
  76. double value(unsigned char index) override {
  77. if (index == 0) return _temperature;
  78. if (index == 1) return _pressure / 100;
  79. return 0;
  80. }
  81. protected:
  82. void _init() {
  83. // Make sure sensor had enough time to turn on. BMP180 requires 2ms to start up
  84. espurna::time::blockingDelay(espurna::duration::Milliseconds(10));
  85. // I2C auto-discover
  86. static constexpr uint8_t addresses[] {0x77};
  87. auto address = findAndLock(addresses);
  88. if (address == 0) {
  89. return;
  90. }
  91. // Check sensor correctly initialized
  92. _chip = i2c_read_uint8(address, BMP180_REGISTER_CHIPID);
  93. if (_chip != BMP180_CHIP_ID) {
  94. _chip = 0;
  95. resetUnknown();
  96. return;
  97. }
  98. _readCoefficients(address);
  99. _run_init = false;
  100. _ready = true;
  101. }
  102. void _readCoefficients(uint8_t address) {
  103. _bmp180_calib = bmp180_calib_t{
  104. .ac1 = i2c_read_int16(address, BMP180_REGISTER_CAL_AC1),
  105. .ac2 = i2c_read_int16(address, BMP180_REGISTER_CAL_AC2),
  106. .ac3 = i2c_read_int16(address, BMP180_REGISTER_CAL_AC3),
  107. .ac4 = i2c_read_uint16(address, BMP180_REGISTER_CAL_AC4),
  108. .ac5 = i2c_read_uint16(address, BMP180_REGISTER_CAL_AC5),
  109. .ac6 = i2c_read_uint16(address, BMP180_REGISTER_CAL_AC6),
  110. .b1 = i2c_read_int16(address, BMP180_REGISTER_CAL_B1),
  111. .b2 = i2c_read_int16(address, BMP180_REGISTER_CAL_B2),
  112. .mb = i2c_read_int16(address, BMP180_REGISTER_CAL_MB),
  113. .mc = i2c_read_int16(address, BMP180_REGISTER_CAL_MC),
  114. .md = i2c_read_int16(address, BMP180_REGISTER_CAL_MD),
  115. };
  116. }
  117. // Compute B5 coefficient used in temperature & pressure calcs.
  118. // Based on Adafruit_BMP085_Unified library
  119. long _computeB5(unsigned long t) {
  120. const long X1 = (t - (long)_bmp180_calib.ac6) * ((long)_bmp180_calib.ac5) >> 15;
  121. const long X2 = ((long)_bmp180_calib.mc << 11) / (X1+(long)_bmp180_calib.md);
  122. return X1 + X2;
  123. }
  124. unsigned char _read(uint8_t address) {
  125. // Read raw temperature
  126. i2c_write_uint8(address, BMP180_REGISTER_CONTROL, BMP180_REGISTER_READTEMPCMD);
  127. espurna::time::blockingDelay(espurna::duration::Milliseconds(5));
  128. unsigned long t = i2c_read_uint16(address, BMP180_REGISTER_TEMPDATA);
  129. // Compute B5 coeficient
  130. long b5 = _computeB5(t);
  131. // Final temperature
  132. _temperature = ((double) ((b5 + 8) >> 4)) / 10.0;
  133. // Read raw pressure
  134. i2c_write_uint8(address, BMP180_REGISTER_CONTROL, BMP180_REGISTER_READPRESSURECMD + (_mode << 6));
  135. espurna::time::blockingDelay(espurna::duration::Milliseconds(26));
  136. unsigned long p1 = i2c_read_uint16(address, BMP180_REGISTER_PRESSUREDATA);
  137. unsigned long p2 = i2c_read_uint8(address, BMP180_REGISTER_PRESSUREDATA+2);
  138. long p = ((p1 << 8) + p2) >> (8 - _mode);
  139. // Pressure compensation
  140. long b6 = b5 - 4000;
  141. long x1 = (_bmp180_calib.b2 * ((b6 * b6) >> 12)) >> 11;
  142. long x2 = (_bmp180_calib.ac2 * b6) >> 11;
  143. long x3 = x1 + x2;
  144. long b3 = (((((int32_t) _bmp180_calib.ac1) * 4 + x3) << _mode) + 2) >> 2;
  145. x1 = (_bmp180_calib.ac3 * b6) >> 13;
  146. x2 = (_bmp180_calib.b1 * ((b6 * b6) >> 12)) >> 16;
  147. x3 = ((x1 + x2) + 2) >> 2;
  148. unsigned long b4 = (_bmp180_calib.ac4 * (uint32_t) (x3 + 32768)) >> 15;
  149. unsigned long b7 = ((uint32_t) (p - b3) * (50000 >> _mode));
  150. if (b7 < 0x80000000) {
  151. p = (b7 << 1) / b4;
  152. } else {
  153. p = (b7 / b4) << 1;
  154. }
  155. x1 = (p >> 8) * (p >> 8);
  156. x1 = (x1 * 3038) >> 16;
  157. x2 = (-7357 * p) >> 16;
  158. _pressure = p + ((x1 + x2 + 3791) >> 4);
  159. return SENSOR_ERROR_OK;
  160. }
  161. // ---------------------------------------------------------------------
  162. unsigned char _chip;
  163. bool _run_init = false;
  164. double _temperature = 0;
  165. double _pressure = 0;
  166. unsigned int _mode = BMP180_MODE;
  167. struct bmp180_calib_t {
  168. int16_t ac1;
  169. int16_t ac2;
  170. int16_t ac3;
  171. uint16_t ac4;
  172. uint16_t ac5;
  173. uint16_t ac6;
  174. int16_t b1;
  175. int16_t b2;
  176. int16_t mb;
  177. int16_t mc;
  178. int16_t md;
  179. };
  180. bmp180_calib_t _bmp180_calib;
  181. };
  182. #endif // SENSOR_SUPPORT && BMP180_SUPPORT