// ----------------------------------------------------------------------------- // BME280/BMP280 Sensor over I2C // Uses SparkFun BME280 library // Copyright (C) 2017 by Xose Pérez // ----------------------------------------------------------------------------- #pragma once #include "Arduino.h" #include "BaseSensor.h" #include #define BMX280_NAME "BME280 / BMP280" #define BMX280_KEY "bme280" #define BMX280_CHIP_BMP280 0x58 #define BMX280_CHIP_BME280 0x60 class BMX280Sensor : public BaseSensor { public: static unsigned char addresses[2]; // --------------------------------------------------------------------- // Public // --------------------------------------------------------------------- void setAddress(unsigned char address) { if (_address == address) return; _address = address; _dirty = true; } // --------------------------------------------------------------------- unsigned char getAddress() { return _address; } // --------------------------------------------------------------------- // Sensor API // --------------------------------------------------------------------- // Initialization method, must be idempotent void begin() { if (!_dirty) return; _dirty = false; _chip = 0; // I2C auto-discover _address = lock_i2c(_address, sizeof(BMX280Sensor::addresses), BMX280Sensor::addresses); if (_address == 0) return; // Init init(); } // Descriptive name of the sensor String description() { char buffer[20]; snprintf(buffer, sizeof(buffer), "%s @ I2C (0x%02X)", _chip == BMX280_CHIP_BME280 ? "BME280" : "BMP280", _address); return String(buffer); } // Descriptive name of the slot # index String slot(unsigned char index) { return description(); } // Type for slot # index magnitude_t type(unsigned char index) { if (index < _count) { _error = SENSOR_ERROR_OK; unsigned char i = 0; #if BMX280_TEMPERATURE > 0 if (index == i++) return MAGNITUDE_TEMPERATURE; #endif #if BMX280_PRESSURE > 0 if (index == i++) return MAGNITUDE_PRESSURE; #endif #if BMX280_HUMIDITY > 0 if (_chip == BMX280_CHIP_BME280) { if (index == i) return MAGNITUDE_HUMIDITY; } #endif } _error = SENSOR_ERROR_OUT_OF_RANGE; return MAGNITUDE_NONE; } // Pre-read hook (usually to populate registers with up-to-date data) virtual void pre() { if (_chip == 0) { _error = SENSOR_ERROR_UNKNOWN_ID; return; } #if BMX280_MODE == 1 forceRead(); #endif } // Current value for slot # index double value(unsigned char index) { if (index < _count) { _error = SENSOR_ERROR_OK; unsigned char i = 0; #if BMX280_TEMPERATURE > 0 if (index == i++) return bme->readTempC(); #endif #if BMX280_PRESSURE > 0 if (index == i++) return bme->readFloatPressure() / 100; #endif #if BMX280_HUMIDITY > 0 if (_chip == BMX280_CHIP_BME280) { if (index == i) return bme->readFloatHumidity(); } #endif } _error = SENSOR_ERROR_OUT_OF_RANGE; return 0; } // Load the configuration manifest static void manifest(JsonObject& sensors) { char buffer[10]; JsonObject& sensor = sensors.createNestedObject(BMX280_KEY); sensor["name"] = BMX280_NAME; JsonArray& fields = sensor.createNestedArray("fields"); { JsonObject& field = fields.createNestedObject(); field["tag"] = UI_TAG_SELECT; field["name"] = "address"; field["label"] = "Address"; JsonArray& options = field.createNestedArray("options"); { JsonObject& option = options.createNestedObject(); option["name"] = "auto"; option["value"] = 0; } for (unsigned char i=0; i< sizeof(BMX280Sensor::addresses); i++) { JsonObject& option = options.createNestedObject(); snprintf(buffer, sizeof(buffer), "0x%02X", BMX280Sensor::addresses[i]); option["name"] = String(buffer); option["value"] = BMX280Sensor::addresses[i]; } } }; void getConfig(JsonObject& root) { root["key"] = BMX280_KEY; root["address"] = getAddress(); }; void setConfig(JsonObject& root) { if (root.containsKey("address")) setAddress(root["address"]); }; protected: void init() { // Destroy previous instance if any if (bme) delete bme; bme = new BME280(); bme->settings.commInterface = I2C_MODE; bme->settings.I2CAddress = _address; bme->settings.runMode = BMX280_MODE; bme->settings.tStandby = 0; bme->settings.filter = 0; bme->settings.tempOverSample = BMX280_TEMPERATURE; bme->settings.pressOverSample = BMX280_PRESSURE; bme->settings.humidOverSample = BMX280_HUMIDITY; // Fix when not measuring temperature, t_fine should have a sensible value if (BMX280_TEMPERATURE == 0) bme->t_fine = 100000; // aprox 20ºC // Make sure sensor had enough time to turn on. BMX280 requires 2ms to start up delay(10); // Check sensor correctly initialized _chip = bme->begin(); if ((_chip != BMX280_CHIP_BME280) && (_chip != BMX280_CHIP_BMP280)) { _chip = 0; i2cReleaseLock(_address); _error = SENSOR_ERROR_UNKNOWN_ID; } #if BMX280_TEMPERATURE > 0 ++_count; #endif #if BMX280_PRESSURE > 0 ++_count; #endif #if BMX280_HUMIDITY > 0 if (_chip == BMX280_CHIP_BME280) ++_count; #endif _measurement_delay = measurementTime(); } unsigned long measurementTime() { // Measurement Time (as per BMX280 datasheet section 9.1) // T_max(ms) = 1.25 // + (2.3 * T_oversampling) // + (2.3 * P_oversampling + 0.575) // + (2.4 * H_oversampling + 0.575) // ~ 9.3ms for current settings double t = 1.25; #if BMX280_TEMPERATURE > 0 t += (2.3 * BMX280_TEMPERATURE); #endif #if BMX280_PRESSURE > 0 t += (2.3 * BMX280_PRESSURE + 0.575); #endif #if BMX280_HUMIDITY > 0 if (_chip == BMX280_CHIP_BME280) { t += (2.4 * BMX280_HUMIDITY + 0.575); } #endif return round(t + 1); // round up } void forceRead() { // We set the sensor in "forced mode" to force a reading. // After the reading the sensor will go back to sleep mode. uint8_t value = bme->readRegister(BME280_CTRL_MEAS_REG); value = (value & 0xFC) + 0x01; bme->writeRegister(BME280_CTRL_MEAS_REG, value); delay(_measurement_delay); } // --------------------------------------------------------------------- BME280 * bme; unsigned char _chip; unsigned long _measurement_delay; }; // Static inizializations unsigned char BMX280Sensor::addresses[2] = {0x76, 0x77};