Fork of the espurna firmware for `mhsw` switches
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.

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