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.

261 lines
8.3 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_NAME "BME280 / BMP280"
  11. #define BMX280_KEY "bme280"
  12. #define BMX280_CHIP_BMP280 0x58
  13. #define BMX280_CHIP_BME280 0x60
  14. class BMX280Sensor : public BaseSensor {
  15. public:
  16. static unsigned char addresses[2];
  17. // ---------------------------------------------------------------------
  18. // Public
  19. // ---------------------------------------------------------------------
  20. void setAddress(unsigned char address) {
  21. if (_address == address) return;
  22. _address = address;
  23. _dirty = true;
  24. }
  25. // ---------------------------------------------------------------------
  26. unsigned char getAddress() {
  27. return _address;
  28. }
  29. // ---------------------------------------------------------------------
  30. // Sensor API
  31. // ---------------------------------------------------------------------
  32. // Initialization method, must be idempotent
  33. void begin() {
  34. if (!_dirty) return;
  35. _dirty = false;
  36. _chip = 0;
  37. // I2C auto-discover
  38. _address = lock_i2c(_address, sizeof(BMX280Sensor::addresses), BMX280Sensor::addresses);
  39. if (_address == 0) return;
  40. // Init
  41. init();
  42. }
  43. // Descriptive name of the sensor
  44. String description() {
  45. char buffer[20];
  46. snprintf(buffer, sizeof(buffer), "%s @ I2C (0x%02X)", _chip == BMX280_CHIP_BME280 ? "BME280" : "BMP280", _address);
  47. return String(buffer);
  48. }
  49. // Descriptive name of the slot # index
  50. String slot(unsigned char index) {
  51. return description();
  52. }
  53. // Type for slot # index
  54. magnitude_t type(unsigned char index) {
  55. if (index < _count) {
  56. _error = SENSOR_ERROR_OK;
  57. unsigned char i = 0;
  58. #if BMX280_TEMPERATURE > 0
  59. if (index == i++) return MAGNITUDE_TEMPERATURE;
  60. #endif
  61. #if BMX280_PRESSURE > 0
  62. if (index == i++) return MAGNITUDE_PRESSURE;
  63. #endif
  64. #if BMX280_HUMIDITY > 0
  65. if (_chip == BMX280_CHIP_BME280) {
  66. if (index == i) return MAGNITUDE_HUMIDITY;
  67. }
  68. #endif
  69. }
  70. _error = SENSOR_ERROR_OUT_OF_RANGE;
  71. return MAGNITUDE_NONE;
  72. }
  73. // Pre-read hook (usually to populate registers with up-to-date data)
  74. virtual void pre() {
  75. if (_chip == 0) {
  76. _error = SENSOR_ERROR_UNKNOWN_ID;
  77. return;
  78. }
  79. #if BMX280_MODE == 1
  80. forceRead();
  81. #endif
  82. }
  83. // Current value for slot # index
  84. double value(unsigned char index) {
  85. if (index < _count) {
  86. _error = SENSOR_ERROR_OK;
  87. unsigned char i = 0;
  88. #if BMX280_TEMPERATURE > 0
  89. if (index == i++) return bme->readTempC();
  90. #endif
  91. #if BMX280_PRESSURE > 0
  92. if (index == i++) return bme->readFloatPressure() / 100;
  93. #endif
  94. #if BMX280_HUMIDITY > 0
  95. if (_chip == BMX280_CHIP_BME280) {
  96. if (index == i) return bme->readFloatHumidity();
  97. }
  98. #endif
  99. }
  100. _error = SENSOR_ERROR_OUT_OF_RANGE;
  101. return 0;
  102. }
  103. // Load the configuration manifest
  104. static void manifest(JsonObject& sensors) {
  105. char buffer[10];
  106. JsonObject& sensor = sensors.createNestedObject(BMX280_KEY);
  107. sensor["name"] = BMX280_NAME;
  108. JsonArray& fields = sensor.createNestedArray("fields");
  109. {
  110. JsonObject& field = fields.createNestedObject();
  111. field["tag"] = UI_TAG_SELECT;
  112. field["name"] = "address";
  113. field["label"] = "Address";
  114. JsonArray& options = field.createNestedArray("options");
  115. {
  116. JsonObject& option = options.createNestedObject();
  117. option["name"] = "auto";
  118. option["value"] = 0;
  119. }
  120. for (unsigned char i=0; i< sizeof(BMX280Sensor::addresses); i++) {
  121. JsonObject& option = options.createNestedObject();
  122. snprintf(buffer, sizeof(buffer), "0x%02X", BMX280Sensor::addresses[i]);
  123. option["name"] = String(buffer);
  124. option["value"] = BMX280Sensor::addresses[i];
  125. }
  126. }
  127. };
  128. void getConfig(JsonObject& root) {
  129. root["key"] = BMX280_KEY;
  130. root["address"] = getAddress();
  131. };
  132. void setConfig(JsonObject& root) {
  133. if (root.containsKey("address")) setAddress(root["address"]);
  134. };
  135. protected:
  136. void init() {
  137. // Destroy previous instance if any
  138. if (bme) delete bme;
  139. bme = new BME280();
  140. bme->settings.commInterface = I2C_MODE;
  141. bme->settings.I2CAddress = _address;
  142. bme->settings.runMode = BMX280_MODE;
  143. bme->settings.tStandby = 0;
  144. bme->settings.filter = 0;
  145. bme->settings.tempOverSample = BMX280_TEMPERATURE;
  146. bme->settings.pressOverSample = BMX280_PRESSURE;
  147. bme->settings.humidOverSample = BMX280_HUMIDITY;
  148. // Fix when not measuring temperature, t_fine should have a sensible value
  149. if (BMX280_TEMPERATURE == 0) bme->t_fine = 100000; // aprox 20ºC
  150. // Make sure sensor had enough time to turn on. BMX280 requires 2ms to start up
  151. delay(10);
  152. // Check sensor correctly initialized
  153. _chip = bme->begin();
  154. if ((_chip != BMX280_CHIP_BME280) && (_chip != BMX280_CHIP_BMP280)) {
  155. _chip = 0;
  156. i2cReleaseLock(_address);
  157. _error = SENSOR_ERROR_UNKNOWN_ID;
  158. }
  159. #if BMX280_TEMPERATURE > 0
  160. ++_count;
  161. #endif
  162. #if BMX280_PRESSURE > 0
  163. ++_count;
  164. #endif
  165. #if BMX280_HUMIDITY > 0
  166. if (_chip == BMX280_CHIP_BME280) ++_count;
  167. #endif
  168. _measurement_delay = measurementTime();
  169. }
  170. unsigned long measurementTime() {
  171. // Measurement Time (as per BMX280 datasheet section 9.1)
  172. // T_max(ms) = 1.25
  173. // + (2.3 * T_oversampling)
  174. // + (2.3 * P_oversampling + 0.575)
  175. // + (2.4 * H_oversampling + 0.575)
  176. // ~ 9.3ms for current settings
  177. double t = 1.25;
  178. #if BMX280_TEMPERATURE > 0
  179. t += (2.3 * BMX280_TEMPERATURE);
  180. #endif
  181. #if BMX280_PRESSURE > 0
  182. t += (2.3 * BMX280_PRESSURE + 0.575);
  183. #endif
  184. #if BMX280_HUMIDITY > 0
  185. if (_chip == BMX280_CHIP_BME280) {
  186. t += (2.4 * BMX280_HUMIDITY + 0.575);
  187. }
  188. #endif
  189. return round(t + 1); // round up
  190. }
  191. void forceRead() {
  192. // We set the sensor in "forced mode" to force a reading.
  193. // After the reading the sensor will go back to sleep mode.
  194. uint8_t value = bme->readRegister(BME280_CTRL_MEAS_REG);
  195. value = (value & 0xFC) + 0x01;
  196. bme->writeRegister(BME280_CTRL_MEAS_REG, value);
  197. delay(_measurement_delay);
  198. }
  199. // ---------------------------------------------------------------------
  200. BME280 * bme;
  201. unsigned char _chip;
  202. unsigned long _measurement_delay;
  203. };
  204. // Static inizializations
  205. unsigned char BMX280Sensor::addresses[2] = {0x76, 0x77};