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.

453 lines
16 KiB

6 years ago
  1. // -----------------------------------------------------------------------------
  2. // BME280/BMP280 Sensor over I2C
  3. // Copyright (C) 2017-2019 by Xose Pérez <xose dot perez at gmail dot com>
  4. // -----------------------------------------------------------------------------
  5. #if SENSOR_SUPPORT && BMX280_SUPPORT
  6. #pragma once
  7. #undef I2C_SUPPORT
  8. #define I2C_SUPPORT 1 // Explicitly request I2C support.
  9. #include <Arduino.h>
  10. #include "../utils.h"
  11. #include "I2CSensor.h"
  12. #define BMX280_CHIP_BMP280 0x58
  13. #define BMX280_CHIP_BME280 0x60
  14. #define BMX280_REGISTER_DIG_T1 0x88
  15. #define BMX280_REGISTER_DIG_T2 0x8A
  16. #define BMX280_REGISTER_DIG_T3 0x8C
  17. #define BMX280_REGISTER_DIG_P1 0x8E
  18. #define BMX280_REGISTER_DIG_P2 0x90
  19. #define BMX280_REGISTER_DIG_P3 0x92
  20. #define BMX280_REGISTER_DIG_P4 0x94
  21. #define BMX280_REGISTER_DIG_P5 0x96
  22. #define BMX280_REGISTER_DIG_P6 0x98
  23. #define BMX280_REGISTER_DIG_P7 0x9A
  24. #define BMX280_REGISTER_DIG_P8 0x9C
  25. #define BMX280_REGISTER_DIG_P9 0x9E
  26. #define BMX280_REGISTER_DIG_H1 0xA1
  27. #define BMX280_REGISTER_DIG_H2 0xE1
  28. #define BMX280_REGISTER_DIG_H3 0xE3
  29. #define BMX280_REGISTER_DIG_H4 0xE4
  30. #define BMX280_REGISTER_DIG_H5 0xE5
  31. #define BMX280_REGISTER_DIG_H6 0xE7
  32. #define BMX280_REGISTER_CHIPID 0xD0
  33. #define BMX280_REGISTER_VERSION 0xD1
  34. #define BMX280_REGISTER_SOFTRESET 0xE0
  35. #define BMX280_REGISTER_CAL26 0xE1
  36. #define BMX280_REGISTER_CONTROLHUMID 0xF2
  37. #define BMX280_REGISTER_CONTROL 0xF4
  38. #define BMX280_REGISTER_CONFIG 0xF5
  39. #define BMX280_REGISTER_PRESSUREDATA 0xF7
  40. #define BMX280_REGISTER_TEMPDATA 0xFA
  41. #define BMX280_REGISTER_HUMIDDATA 0xFD
  42. class BMX280Sensor : public I2CSensor {
  43. public:
  44. static unsigned char addresses[2];
  45. // ---------------------------------------------------------------------
  46. // Public
  47. // ---------------------------------------------------------------------
  48. BMX280Sensor(): I2CSensor() {
  49. _sensor_id = SENSOR_BMX280_ID;
  50. }
  51. // ---------------------------------------------------------------------
  52. // Sensor API
  53. // ---------------------------------------------------------------------
  54. // Initialization method, must be idempotent
  55. void begin() {
  56. if (!_dirty) return;
  57. _init();
  58. _dirty = !_ready;
  59. }
  60. // Descriptive name of the sensor
  61. String description() {
  62. char buffer[20];
  63. snprintf(buffer, sizeof(buffer), "%s @ I2C (0x%02X)", _chip == BMX280_CHIP_BME280 ? "BME280" : "BMP280", _address);
  64. return String(buffer);
  65. }
  66. // Type for slot # index
  67. unsigned char type(unsigned char index) {
  68. unsigned char i = 0;
  69. #if BMX280_TEMPERATURE > 0
  70. if (index == i++) return MAGNITUDE_TEMPERATURE;
  71. #endif
  72. #if BMX280_PRESSURE > 0
  73. if (index == i++) return MAGNITUDE_PRESSURE;
  74. #endif
  75. #if BMX280_HUMIDITY > 0
  76. if (_chip == BMX280_CHIP_BME280) {
  77. if (index == i) return MAGNITUDE_HUMIDITY;
  78. }
  79. #endif
  80. return MAGNITUDE_NONE;
  81. }
  82. // Number of decimals for a magnitude (or -1 for default)
  83. signed char decimals(unsigned char type) {
  84. // These numbers of decimals correspond to maximum sensor resolution settings
  85. switch (type) {
  86. case MAGNITUDE_TEMPERATURE: return 3;
  87. case MAGNITUDE_PRESSURE: return 4;
  88. case MAGNITUDE_HUMIDITY: return 2;
  89. }
  90. return -1;
  91. }
  92. // Pre-read hook (usually to populate registers with up-to-date data)
  93. virtual void pre() {
  94. if (_run_init) {
  95. i2cClearBus();
  96. _init();
  97. }
  98. if (_chip == 0) {
  99. _error = SENSOR_ERROR_UNKNOWN_ID;
  100. return;
  101. }
  102. _error = SENSOR_ERROR_OK;
  103. #if BMX280_MODE == 1
  104. _forceRead();
  105. #endif
  106. _error = _read();
  107. if (_error != SENSOR_ERROR_OK) {
  108. _run_init = true;
  109. }
  110. }
  111. // Current value for slot # index
  112. double value(unsigned char index) {
  113. unsigned char i = 0;
  114. #if BMX280_TEMPERATURE > 0
  115. if (index == i++) return _temperature;
  116. #endif
  117. #if BMX280_PRESSURE > 0
  118. if (index == i++) return _pressure / 100;
  119. #endif
  120. #if BMX280_HUMIDITY > 0
  121. if (_chip == BMX280_CHIP_BME280) {
  122. if (index == i) return _humidity;
  123. }
  124. #endif
  125. return 0;
  126. }
  127. // Load the configuration manifest
  128. static void manifest(JsonArray& sensors) {
  129. char buffer[10];
  130. JsonObject& sensor = sensors.createNestedObject();
  131. sensor["sensor_id"] = SENSOR_BMX280_ID;
  132. JsonArray& fields = sensor.createNestedArray("fields");
  133. {
  134. JsonObject& field = fields.createNestedObject();
  135. field["tag"] = UI_TAG_SELECT;
  136. field["name"] = "address";
  137. field["label"] = "Address";
  138. JsonArray& options = field.createNestedArray("options");
  139. {
  140. JsonObject& option = options.createNestedObject();
  141. option["name"] = "auto";
  142. option["value"] = 0;
  143. }
  144. for (unsigned char i=0; i< sizeof(BMX280Sensor::addresses); i++) {
  145. JsonObject& option = options.createNestedObject();
  146. snprintf(buffer, sizeof(buffer), "0x%02X", BMX280Sensor::addresses[i]);
  147. option["name"] = String(buffer);
  148. option["value"] = BMX280Sensor::addresses[i];
  149. }
  150. }
  151. };
  152. void getConfig(JsonObject& root) {
  153. root["sensor_id"] = _sensor_id;
  154. root["address"] = _address;
  155. };
  156. void setConfig(JsonObject& root) {
  157. if (root.containsKey("address")) setAddress(root["address"]);
  158. };
  159. protected:
  160. void _init() {
  161. // Make sure sensor had enough time to turn on. BMX280 requires 2ms to start up
  162. nice_delay(10);
  163. // No chip ID by default
  164. _chip = 0;
  165. // I2C auto-discover
  166. _address = _begin_i2c(_address, sizeof(BMX280Sensor::addresses), BMX280Sensor::addresses);
  167. if (_address == 0) return;
  168. // Check sensor correctly initialized
  169. _chip = i2c_read_uint8(_address, BMX280_REGISTER_CHIPID);
  170. if ((_chip != BMX280_CHIP_BME280) && (_chip != BMX280_CHIP_BMP280)) {
  171. _chip = 0;
  172. i2cReleaseLock(_address);
  173. _previous_address = 0;
  174. _error = SENSOR_ERROR_UNKNOWN_ID;
  175. // Setting _address to 0 forces auto-discover
  176. // This might be necessary at this stage if there is a
  177. // different sensor in the hardcoded address
  178. _address = 0;
  179. return;
  180. }
  181. _count = 0;
  182. #if BMX280_TEMPERATURE > 0
  183. ++_count;
  184. #endif
  185. #if BMX280_PRESSURE > 0
  186. ++_count;
  187. #endif
  188. #if BMX280_HUMIDITY > 0
  189. if (_chip == BMX280_CHIP_BME280) ++_count;
  190. #endif
  191. _readCoefficients();
  192. unsigned char data = 0;
  193. i2c_write_uint8(_address, BMX280_REGISTER_CONTROL, data);
  194. data = (BMX280_STANDBY << 0x5) & 0xE0;
  195. data |= (BMX280_FILTER << 0x02) & 0x1C;
  196. i2c_write_uint8(_address, BMX280_REGISTER_CONFIG, data);
  197. data = (BMX280_HUMIDITY) & 0x07;
  198. i2c_write_uint8(_address, BMX280_REGISTER_CONTROLHUMID, data);
  199. data = (BMX280_TEMPERATURE << 5) & 0xE0;
  200. data |= (BMX280_PRESSURE << 2) & 0x1C;
  201. data |= (BMX280_MODE) & 0x03;
  202. i2c_write_uint8(_address, BMX280_REGISTER_CONTROL, data);
  203. _measurement_delay = _measurementTime();
  204. _run_init = false;
  205. _ready = true;
  206. }
  207. void _readCoefficients() {
  208. _bmx280_calib.dig_T1 = i2c_read_uint16_le(_address, BMX280_REGISTER_DIG_T1);
  209. _bmx280_calib.dig_T2 = i2c_read_int16_le(_address, BMX280_REGISTER_DIG_T2);
  210. _bmx280_calib.dig_T3 = i2c_read_int16_le(_address, BMX280_REGISTER_DIG_T3);
  211. _bmx280_calib.dig_P1 = i2c_read_uint16_le(_address, BMX280_REGISTER_DIG_P1);
  212. _bmx280_calib.dig_P2 = i2c_read_int16_le(_address, BMX280_REGISTER_DIG_P2);
  213. _bmx280_calib.dig_P3 = i2c_read_int16_le(_address, BMX280_REGISTER_DIG_P3);
  214. _bmx280_calib.dig_P4 = i2c_read_int16_le(_address, BMX280_REGISTER_DIG_P4);
  215. _bmx280_calib.dig_P5 = i2c_read_int16_le(_address, BMX280_REGISTER_DIG_P5);
  216. _bmx280_calib.dig_P6 = i2c_read_int16_le(_address, BMX280_REGISTER_DIG_P6);
  217. _bmx280_calib.dig_P7 = i2c_read_int16_le(_address, BMX280_REGISTER_DIG_P7);
  218. _bmx280_calib.dig_P8 = i2c_read_int16_le(_address, BMX280_REGISTER_DIG_P8);
  219. _bmx280_calib.dig_P9 = i2c_read_int16_le(_address, BMX280_REGISTER_DIG_P9);
  220. _bmx280_calib.dig_H1 = i2c_read_uint8(_address, BMX280_REGISTER_DIG_H1);
  221. _bmx280_calib.dig_H2 = i2c_read_int16_le(_address, BMX280_REGISTER_DIG_H2);
  222. _bmx280_calib.dig_H3 = i2c_read_uint8(_address, BMX280_REGISTER_DIG_H3);
  223. _bmx280_calib.dig_H4 = (i2c_read_uint8(_address, BMX280_REGISTER_DIG_H4) << 4) | (i2c_read_uint8(_address, BMX280_REGISTER_DIG_H4+1) & 0xF);
  224. _bmx280_calib.dig_H5 = (i2c_read_uint8(_address, BMX280_REGISTER_DIG_H5+1) << 4) | (i2c_read_uint8(_address, BMX280_REGISTER_DIG_H5) >> 4);
  225. _bmx280_calib.dig_H6 = (int8_t) i2c_read_uint8(_address, BMX280_REGISTER_DIG_H6);
  226. }
  227. unsigned long _measurementTime() {
  228. // Measurement Time (as per BMX280 datasheet section 9.1)
  229. // T_max(ms) = 1.25
  230. // + (2.3 * T_oversampling)
  231. // + (2.3 * P_oversampling + 0.575)
  232. // + (2.4 * H_oversampling + 0.575)
  233. // ~ 9.3ms for current settings
  234. double t = 1.25;
  235. #if BMX280_TEMPERATURE > 0
  236. t += (2.3 * BMX280_TEMPERATURE);
  237. #endif
  238. #if BMX280_PRESSURE > 0
  239. t += (2.3 * BMX280_PRESSURE + 0.575);
  240. #endif
  241. #if BMX280_HUMIDITY > 0
  242. if (_chip == BMX280_CHIP_BME280) {
  243. t += (2.4 * BMX280_HUMIDITY + 0.575);
  244. }
  245. #endif
  246. return round(t + 1); // round up
  247. }
  248. void _forceRead() {
  249. // We set the sensor in "forced mode" to force a reading.
  250. // After the reading the sensor will go back to sleep mode.
  251. uint8_t value = i2c_read_uint8(_address, BMX280_REGISTER_CONTROL);
  252. value = (value & 0xFC) + 0x01;
  253. i2c_write_uint8(_address, BMX280_REGISTER_CONTROL, value);
  254. nice_delay(_measurement_delay);
  255. }
  256. unsigned char _read() {
  257. #if BMX280_TEMPERATURE > 0
  258. int32_t adc_T = i2c_read_uint16(_address, BMX280_REGISTER_TEMPDATA);
  259. if (0xFFFF == adc_T) return SENSOR_ERROR_OUT_OF_RANGE;
  260. adc_T <<= 8;
  261. adc_T |= i2c_read_uint8(_address, BMX280_REGISTER_TEMPDATA+2);
  262. adc_T >>= 4;
  263. int32_t var1t = ((((adc_T>>3) -
  264. ((int32_t)_bmx280_calib.dig_T1 <<1))) *
  265. ((int32_t)_bmx280_calib.dig_T2)) >> 11;
  266. int32_t var2t = (((((adc_T>>4) -
  267. ((int32_t)_bmx280_calib.dig_T1)) *
  268. ((adc_T>>4) - ((int32_t)_bmx280_calib.dig_T1))) >> 12) *
  269. ((int32_t)_bmx280_calib.dig_T3)) >> 14;
  270. int32_t t_fine = var1t + var2t;
  271. double T = (t_fine * 5 + 128) >> 8;
  272. _temperature = T / 100;
  273. #else
  274. int32_t t_fine = 102374; // ~20ºC
  275. #endif
  276. // -----------------------------------------------------------------
  277. #if BMX280_PRESSURE > 0
  278. int64_t var1, var2, p;
  279. int32_t adc_P = i2c_read_uint16(_address, BMX280_REGISTER_PRESSUREDATA);
  280. if (0xFFFF == adc_P) return SENSOR_ERROR_OUT_OF_RANGE;
  281. adc_P <<= 8;
  282. adc_P |= i2c_read_uint8(_address, BMX280_REGISTER_PRESSUREDATA+2);
  283. adc_P >>= 4;
  284. var1 = ((int64_t)t_fine) - 128000;
  285. var2 = var1 * var1 * (int64_t)_bmx280_calib.dig_P6;
  286. var2 = var2 + ((var1*(int64_t)_bmx280_calib.dig_P5)<<17);
  287. var2 = var2 + (((int64_t)_bmx280_calib.dig_P4)<<35);
  288. var1 = ((var1 * var1 * (int64_t)_bmx280_calib.dig_P3)>>8) +
  289. ((var1 * (int64_t)_bmx280_calib.dig_P2)<<12);
  290. var1 = (((((int64_t)1)<<47)+var1))*((int64_t)_bmx280_calib.dig_P1)>>33;
  291. if (var1 == 0) return SENSOR_ERROR_OUT_OF_RANGE; // avoid exception caused by division by zero
  292. p = 1048576 - adc_P;
  293. p = (((p<<31) - var2)*3125) / var1;
  294. var1 = (((int64_t)_bmx280_calib.dig_P9) * (p>>13) * (p>>13)) >> 25;
  295. var2 = (((int64_t)_bmx280_calib.dig_P8) * p) >> 19;
  296. p = ((p + var1 + var2) >> 8) + (((int64_t)_bmx280_calib.dig_P7)<<4);
  297. _pressure = (double) p / 256;
  298. #endif
  299. // -----------------------------------------------------------------
  300. #if BMX280_HUMIDITY > 0
  301. if (_chip == BMX280_CHIP_BME280) {
  302. int32_t adc_H = i2c_read_uint16(_address, BMX280_REGISTER_HUMIDDATA);
  303. if (0xFFFF == adc_H) return SENSOR_ERROR_OUT_OF_RANGE;
  304. int32_t v_x1_u32r;
  305. v_x1_u32r = (t_fine - ((int32_t)76800));
  306. v_x1_u32r = (((((adc_H << 14) - (((int32_t)_bmx280_calib.dig_H4) << 20) -
  307. (((int32_t)_bmx280_calib.dig_H5) * v_x1_u32r)) + ((int32_t)16384)) >> 15) *
  308. (((((((v_x1_u32r * ((int32_t)_bmx280_calib.dig_H6)) >> 10) *
  309. (((v_x1_u32r * ((int32_t)_bmx280_calib.dig_H3)) >> 11) + ((int32_t)32768))) >> 10) +
  310. ((int32_t)2097152)) * ((int32_t)_bmx280_calib.dig_H2) + 8192) >> 14));
  311. v_x1_u32r = (v_x1_u32r - (((((v_x1_u32r >> 15) * (v_x1_u32r >> 15)) >> 7) *
  312. ((int32_t)_bmx280_calib.dig_H1)) >> 4));
  313. v_x1_u32r = (v_x1_u32r < 0) ? 0 : v_x1_u32r;
  314. v_x1_u32r = (v_x1_u32r > 419430400) ? 419430400 : v_x1_u32r;
  315. double h = (v_x1_u32r >> 12);
  316. _humidity = h / 1024.0;
  317. }
  318. #endif
  319. return SENSOR_ERROR_OK;
  320. }
  321. // ---------------------------------------------------------------------
  322. unsigned char _chip;
  323. unsigned long _measurement_delay;
  324. bool _run_init = false;
  325. double _temperature = 0;
  326. double _pressure = 0;
  327. double _humidity = 0;
  328. typedef struct {
  329. uint16_t dig_T1;
  330. int16_t dig_T2;
  331. int16_t dig_T3;
  332. uint16_t dig_P1;
  333. int16_t dig_P2;
  334. int16_t dig_P3;
  335. int16_t dig_P4;
  336. int16_t dig_P5;
  337. int16_t dig_P6;
  338. int16_t dig_P7;
  339. int16_t dig_P8;
  340. int16_t dig_P9;
  341. uint8_t dig_H1;
  342. int16_t dig_H2;
  343. uint8_t dig_H3;
  344. int16_t dig_H4;
  345. int16_t dig_H5;
  346. int8_t dig_H6;
  347. } bmx280_calib_t;
  348. bmx280_calib_t _bmx280_calib;
  349. };
  350. // Static inizializations
  351. unsigned char BMX280Sensor::addresses[2] = {0x76, 0x77};
  352. #endif // SENSOR_SUPPORT && BMX280_SUPPORT