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.

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