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.

213 lines
6.5 KiB

  1. // -----------------------------------------------------------------------------
  2. // BME280/BMP280 Sensor over I2C
  3. // Uses SparkFun BME280 library
  4. // Copyright (C) 2017 by Xose Pérez <xose dot perez at gmail dot com>
  5. // -----------------------------------------------------------------------------
  6. #pragma once
  7. #include "Arduino.h"
  8. #include "BaseSensor.h"
  9. #include <SparkFunBME280.h>
  10. #define BMX280_CHIP_BMP280 0x58
  11. #define BMX280_CHIP_BME280 0x60
  12. class BMX280Sensor : public BaseSensor {
  13. public:
  14. // ---------------------------------------------------------------------
  15. // Public
  16. // ---------------------------------------------------------------------
  17. void setAddress(unsigned char address) {
  18. if (_address != address) _dirty = true;
  19. _address = address;
  20. }
  21. // ---------------------------------------------------------------------
  22. // Sensor API
  23. // ---------------------------------------------------------------------
  24. // Initialization method, must be idempotent
  25. void begin() {
  26. if (!_dirty) return;
  27. _dirty = false;
  28. // Discover
  29. if (_address == 0) {
  30. unsigned char addresses[] = {0x76, 0x77};
  31. _address = i2cFindFirst(2, addresses);
  32. }
  33. if (_address == 0) {
  34. _error = SENSOR_ERROR_UNKNOWN_ID;
  35. _chip = 0;
  36. return;
  37. }
  38. // Init
  39. init();
  40. }
  41. // Descriptive name of the sensor
  42. String name() {
  43. char buffer[20];
  44. snprintf(buffer, sizeof(buffer), "%s @ I2C (0x%02X)", _chip == BMX280_CHIP_BME280 ? "BME280" : "BMP280", _address);
  45. return String(buffer);
  46. }
  47. // Descriptive name of the slot # index
  48. String slot(unsigned char index) {
  49. return name();
  50. }
  51. // Type for slot # index
  52. magnitude_t type(unsigned char index) {
  53. if (index < _count) {
  54. _error = SENSOR_ERROR_OK;
  55. unsigned char i = 0;
  56. #if BMX280_TEMPERATURE > 0
  57. if (index == i++) return MAGNITUDE_TEMPERATURE;
  58. #endif
  59. #if BMX280_PRESSURE > 0
  60. if (index == i++) return MAGNITUDE_PRESSURE;
  61. #endif
  62. #if BMX280_HUMIDITY > 0
  63. if (_chip == BMX280_CHIP_BME280) {
  64. if (index == i) return MAGNITUDE_HUMIDITY;
  65. }
  66. #endif
  67. }
  68. _error = SENSOR_ERROR_OUT_OF_RANGE;
  69. return MAGNITUDE_NONE;
  70. }
  71. // Pre-read hook (usually to populate registers with up-to-date data)
  72. virtual void pre() {
  73. if (_chip == 0) {
  74. _error = SENSOR_ERROR_UNKNOWN_ID;
  75. return;
  76. }
  77. #if BMX280_MODE == 1
  78. forceRead();
  79. #endif
  80. }
  81. // Current value for slot # index
  82. double value(unsigned char index) {
  83. if (index < _count) {
  84. _error = SENSOR_ERROR_OK;
  85. unsigned char i = 0;
  86. #if BMX280_TEMPERATURE > 0
  87. if (index == i++) return bme->readTempC();
  88. #endif
  89. #if BMX280_PRESSURE > 0
  90. if (index == i++) return bme->readFloatPressure() / 100;
  91. #endif
  92. #if BMX280_HUMIDITY > 0
  93. if (_chip == BMX280_CHIP_BME280) {
  94. if (index == i) return bme->readFloatHumidity();
  95. }
  96. #endif
  97. }
  98. _error = SENSOR_ERROR_OUT_OF_RANGE;
  99. return 0;
  100. }
  101. protected:
  102. void init() {
  103. // Destroy previous instance if any
  104. if (bme) delete bme;
  105. bme = new BME280();
  106. bme->settings.commInterface = I2C_MODE;
  107. bme->settings.I2CAddress = _address;
  108. bme->settings.runMode = BMX280_MODE;
  109. bme->settings.tStandby = 0;
  110. bme->settings.filter = 0;
  111. bme->settings.tempOverSample = BMX280_TEMPERATURE;
  112. bme->settings.pressOverSample = BMX280_PRESSURE;
  113. bme->settings.humidOverSample = BMX280_HUMIDITY;
  114. // Fix when not measuring temperature, t_fine should have a sensible value
  115. if (BMX280_TEMPERATURE == 0) bme->t_fine = 100000; // aprox 20ºC
  116. // Make sure sensor had enough time to turn on. BMX280 requires 2ms to start up
  117. delay(10);
  118. // Check sensor correctly initialized
  119. _chip = bme->begin();
  120. if ((_chip != BMX280_CHIP_BME280) && (_chip != BMX280_CHIP_BMP280)) {
  121. _chip = 0;
  122. _error = SENSOR_ERROR_UNKNOWN_ID;
  123. }
  124. #if BMX280_TEMPERATURE > 0
  125. ++_count;
  126. #endif
  127. #if BMX280_PRESSURE > 0
  128. ++_count;
  129. #endif
  130. #if BMX280_HUMIDITY > 0
  131. if (_chip == BMX280_CHIP_BME280) ++_count;
  132. #endif
  133. _measurement_delay = measurementTime();
  134. }
  135. unsigned long measurementTime() {
  136. // Measurement Time (as per BMX280 datasheet section 9.1)
  137. // T_max(ms) = 1.25
  138. // + (2.3 * T_oversampling)
  139. // + (2.3 * P_oversampling + 0.575)
  140. // + (2.4 * H_oversampling + 0.575)
  141. // ~ 9.3ms for current settings
  142. double t = 1.25;
  143. #if BMX280_TEMPERATURE > 0
  144. t += (2.3 * BMX280_TEMPERATURE);
  145. #endif
  146. #if BMX280_PRESSURE > 0
  147. t += (2.3 * BMX280_PRESSURE + 0.575);
  148. #endif
  149. #if BMX280_HUMIDITY > 0
  150. if (_chip == BMX280_CHIP_BME280) {
  151. t += (2.4 * BMX280_HUMIDITY + 0.575);
  152. }
  153. #endif
  154. return round(t + 1); // round up
  155. }
  156. void forceRead() {
  157. // We set the sensor in "forced mode" to force a reading.
  158. // After the reading the sensor will go back to sleep mode.
  159. uint8_t value = bme->readRegister(BME280_CTRL_MEAS_REG);
  160. value = (value & 0xFC) + 0x01;
  161. bme->writeRegister(BME280_CTRL_MEAS_REG, value);
  162. delay(_measurement_delay);
  163. }
  164. // ---------------------------------------------------------------------
  165. BME280 * bme;
  166. unsigned char _chip;
  167. unsigned char _address = 0;
  168. unsigned long _measurement_delay;
  169. };