diff --git a/code/espurna/config/prototypes.h b/code/espurna/config/prototypes.h index 3ddaf53e..ee7a97a8 100644 --- a/code/espurna/config/prototypes.h +++ b/code/espurna/config/prototypes.h @@ -66,6 +66,13 @@ unsigned char i2cFindAndLock(size_t size, unsigned char * addresses); bool i2cGetLock(unsigned char address); bool i2cReleaseLock(unsigned char address); +void i2c_write_uint8(uint8_t address, uint8_t reg, uint8_t value); +uint8_t i2c_read_uint8(uint8_t address, uint8_t reg); +uint16_t i2c_read_uint16(uint8_t address, uint8_t reg); +uint16_t i2c_read_uint16_le(uint8_t address, uint8_t reg); +int16_t i2c_read_int16(uint8_t address, uint8_t reg); +int16_t i2c_read_int16_le(uint8_t address, uint8_t reg); + // ----------------------------------------------------------------------------- // GPIO // ----------------------------------------------------------------------------- diff --git a/code/espurna/config/sensors.h b/code/espurna/config/sensors.h index 5844f2e9..1b798bf8 100644 --- a/code/espurna/config/sensors.h +++ b/code/espurna/config/sensors.h @@ -141,7 +141,11 @@ #define BMX280_ADDRESS 0x00 // 0x00 means auto #endif -#define BMX280_MODE 1 // 1 for forced mode, 3 for normal mode +#define BMX280_MODE 1 // 0 for sleep mode, 1 or 2 for forced mode, 3 for normal mode +#define BMX280_STANDBY 0 // 0 for 0.5ms, 1 for 62.5ms, 2 for 125ms + // 3 for 250ms, 4 for 500ms, 5 for 1000ms + // 6 for 10ms, 7 for 20ms +#define BMX280_FILTER 0 // 0 for OFF, 1 for 2 values, 2 for 4 values, 3 for 8 values and 4 for 16 values #define BMX280_TEMPERATURE 1 // Oversampling for temperature (set to 0 to disable magnitude) #define BMX280_HUMIDITY 1 // Oversampling for humidity (set to 0 to disable magnitude, only for BME280) #define BMX280_PRESSURE 1 // Oversampling for pressure (set to 0 to disable magnitude) diff --git a/code/espurna/i2c.ino b/code/espurna/i2c.ino index 9c396fe9..2e7a237b 100644 --- a/code/espurna/i2c.ino +++ b/code/espurna/i2c.ino @@ -104,6 +104,52 @@ int _i2cClearbus(int sda, int scl) { } +// --------------------------------------------------------------------- +// I2C API +// --------------------------------------------------------------------- + +void i2c_write_uint8(uint8_t address, uint8_t reg, uint8_t value) { + Wire.beginTransmission((uint8_t) address); + Wire.write((uint8_t) reg); + Wire.write((uint8_t) value); + Wire.endTransmission(); +} + +uint8_t i2c_read_uint8(uint8_t address, uint8_t reg) { + uint8_t value; + Wire.beginTransmission((uint8_t) address); + Wire.write((uint8_t) reg); + Wire.endTransmission(); + Wire.requestFrom((uint8_t)address, (uint8_t) 1); + value = Wire.read(); + Wire.endTransmission(); + return value; +}; + +uint16_t i2c_read_uint16(uint8_t address, uint8_t reg) { + uint16_t value; + Wire.beginTransmission((uint8_t) address); + Wire.write((uint8_t) reg); + Wire.endTransmission(); + Wire.requestFrom((uint8_t) address, (uint8_t) 2); + value = (Wire.read() << 8) | Wire.read(); + Wire.endTransmission(); + return value; +}; + +uint16_t i2c_read_uint16_le(uint8_t address, uint8_t reg) { + uint16_t temp = i2c_read_uint16(address, reg); + return (temp >> 8) | (temp << 8); +}; + +int16_t i2c_read_int16(uint8_t address, uint8_t reg) { + return (int16_t) i2c_read_uint16(address, reg); +}; + +int16_t i2c_read_int16_le(uint8_t address, uint8_t reg) { + return (int16_t) i2c_read_uint16_le(address, reg); +}; + // ----------------------------------------------------------------------------- // Utils // ----------------------------------------------------------------------------- diff --git a/code/espurna/sensors/BMX280Sensor.h b/code/espurna/sensors/BMX280Sensor.h index d34bcb8e..2d80d2ae 100644 --- a/code/espurna/sensors/BMX280Sensor.h +++ b/code/espurna/sensors/BMX280Sensor.h @@ -1,6 +1,5 @@ // ----------------------------------------------------------------------------- // BME280/BMP280 Sensor over I2C -// Uses SparkFun BME280 library // Copyright (C) 2017-2018 by Xose Pérez // ----------------------------------------------------------------------------- @@ -10,10 +9,43 @@ #include "Arduino.h" #include "I2CSensor.h" -#include -#define BMX280_CHIP_BMP280 0x58 -#define BMX280_CHIP_BME280 0x60 +#define BMX280_CHIP_BMP280 0x58 +#define BMX280_CHIP_BME280 0x60 + +#define BMX280_REGISTER_DIG_T1 0x88 +#define BMX280_REGISTER_DIG_T2 0x8A +#define BMX280_REGISTER_DIG_T3 0x8C + +#define BMX280_REGISTER_DIG_P1 0x8E +#define BMX280_REGISTER_DIG_P2 0x90 +#define BMX280_REGISTER_DIG_P3 0x92 +#define BMX280_REGISTER_DIG_P4 0x94 +#define BMX280_REGISTER_DIG_P5 0x96 +#define BMX280_REGISTER_DIG_P6 0x98 +#define BMX280_REGISTER_DIG_P7 0x9A +#define BMX280_REGISTER_DIG_P8 0x9C +#define BMX280_REGISTER_DIG_P9 0x9E + +#define BMX280_REGISTER_DIG_H1 0xA1 +#define BMX280_REGISTER_DIG_H2 0xE1 +#define BMX280_REGISTER_DIG_H3 0xE3 +#define BMX280_REGISTER_DIG_H4 0xE4 +#define BMX280_REGISTER_DIG_H5 0xE5 +#define BMX280_REGISTER_DIG_H6 0xE7 + +#define BMX280_REGISTER_CHIPID 0xD0 +#define BMX280_REGISTER_VERSION 0xD1 +#define BMX280_REGISTER_SOFTRESET 0xE0 + +#define BMX280_REGISTER_CAL26 0xE1 + +#define BMX280_REGISTER_CONTROLHUMID 0xF2 +#define BMX280_REGISTER_CONTROL 0xF4 +#define BMX280_REGISTER_CONFIG 0xF5 +#define BMX280_REGISTER_PRESSUREDATA 0xF7 +#define BMX280_REGISTER_TEMPDATA 0xFA +#define BMX280_REGISTER_HUMIDDATA 0xFD class BMX280Sensor : public I2CSensor { @@ -27,11 +59,6 @@ class BMX280Sensor : public I2CSensor { BMX280Sensor(): I2CSensor() { _sensor_id = SENSOR_BMX280_ID; - _bme = new BME280(); - } - - ~BMX280Sensor() { - delete _bme; } // --------------------------------------------------------------------- @@ -91,8 +118,9 @@ class BMX280Sensor : public I2CSensor { } #if BMX280_MODE == 1 - forceRead(); + _forceRead(); #endif + _read(); } @@ -103,19 +131,20 @@ class BMX280Sensor : public I2CSensor { _error = SENSOR_ERROR_OK; unsigned char i = 0; #if BMX280_TEMPERATURE > 0 - if (index == i++) return _bme->readTempC(); + if (index == i++) return _temperature; #endif #if BMX280_PRESSURE > 0 - if (index == i++) return _bme->readFloatPressure() / 100; + if (index == i++) return _pressure / 100; #endif #if BMX280_HUMIDITY > 0 if (_chip == BMX280_CHIP_BME280) { - if (index == i) return _bme->readFloatHumidity(); + if (index == i) return _humidity; } #endif } _error = SENSOR_ERROR_OUT_OF_RANGE; return 0; + } // Load the configuration manifest @@ -161,23 +190,11 @@ class BMX280Sensor : public I2CSensor { void _init() { - _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(); + _chip = i2c_read_uint8(_address, BMX280_REGISTER_CHIPID); if ((_chip != BMX280_CHIP_BME280) && (_chip != BMX280_CHIP_BMP280)) { _chip = 0; i2cReleaseLock(_address); @@ -194,11 +211,51 @@ class BMX280Sensor : public I2CSensor { if (_chip == BMX280_CHIP_BME280) ++_count; #endif - _measurement_delay = measurementTime(); + _readCoefficients(); + + unsigned char data = 0; + i2c_write_uint8(_address, BMX280_REGISTER_CONTROL, data); + + data = (BMX280_STANDBY << 0x5) & 0xE0; + data |= (BMX280_FILTER << 0x02) & 0x1C; + i2c_write_uint8(_address, BMX280_REGISTER_CONFIG, data); + + data = (BMX280_HUMIDITY) & 0x07; + i2c_write_uint8(_address, BMX280_REGISTER_CONTROLHUMID, data); + + data = (BMX280_TEMPERATURE << 5) & 0xE0; + data |= (BMX280_PRESSURE << 2) & 0x1C; + data |= (BMX280_MODE) & 0x03; + i2c_write_uint8(_address, BMX280_REGISTER_CONTROL, data); + _measurement_delay = _measurementTime(); + + } + + void _readCoefficients() { + _bmx280_calib.dig_T1 = i2c_read_uint16_le(_address, BMX280_REGISTER_DIG_T1); + _bmx280_calib.dig_T2 = i2c_read_int16_le(_address, BMX280_REGISTER_DIG_T2); + _bmx280_calib.dig_T3 = i2c_read_int16_le(_address, BMX280_REGISTER_DIG_T3); + + _bmx280_calib.dig_P1 = i2c_read_uint16_le(_address, BMX280_REGISTER_DIG_P1); + _bmx280_calib.dig_P2 = i2c_read_int16_le(_address, BMX280_REGISTER_DIG_P2); + _bmx280_calib.dig_P3 = i2c_read_int16_le(_address, BMX280_REGISTER_DIG_P3); + _bmx280_calib.dig_P4 = i2c_read_int16_le(_address, BMX280_REGISTER_DIG_P4); + _bmx280_calib.dig_P5 = i2c_read_int16_le(_address, BMX280_REGISTER_DIG_P5); + _bmx280_calib.dig_P6 = i2c_read_int16_le(_address, BMX280_REGISTER_DIG_P6); + _bmx280_calib.dig_P7 = i2c_read_int16_le(_address, BMX280_REGISTER_DIG_P7); + _bmx280_calib.dig_P8 = i2c_read_int16_le(_address, BMX280_REGISTER_DIG_P8); + _bmx280_calib.dig_P9 = i2c_read_int16_le(_address, BMX280_REGISTER_DIG_P9); + + _bmx280_calib.dig_H1 = i2c_read_uint8(_address, BMX280_REGISTER_DIG_H1); + _bmx280_calib.dig_H2 = i2c_read_int16_le(_address, BMX280_REGISTER_DIG_H2); + _bmx280_calib.dig_H3 = i2c_read_uint8(_address, BMX280_REGISTER_DIG_H3); + _bmx280_calib.dig_H4 = (i2c_read_uint8(_address, BMX280_REGISTER_DIG_H4) << 4) | (i2c_read_uint8(_address, BMX280_REGISTER_DIG_H4+1) & 0xF); + _bmx280_calib.dig_H5 = (i2c_read_uint8(_address, BMX280_REGISTER_DIG_H5+1) << 4) | (i2c_read_uint8(_address, BMX280_REGISTER_DIG_H5) >> 4); + _bmx280_calib.dig_H6 = (int8_t) i2c_read_uint8(_address, BMX280_REGISTER_DIG_H6); } - unsigned long measurementTime() { + unsigned long _measurementTime() { // Measurement Time (as per BMX280 datasheet section 9.1) // T_max(ms) = 1.25 @@ -224,23 +281,135 @@ class BMX280Sensor : public I2CSensor { } - void forceRead() { + 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); + uint8_t value = i2c_read_uint8(_address, BMX280_REGISTER_CONTROL); value = (value & 0xFC) + 0x01; - _bme->writeRegister(BME280_CTRL_MEAS_REG, value); + i2c_write_uint8(_address, BMX280_REGISTER_CONTROL, value); delay(_measurement_delay); } + void _read() { + + #if BMX280_TEMPERATURE > 0 + int32_t adc_T = i2c_read_uint16(_address, BMX280_REGISTER_TEMPDATA); + adc_T <<= 8; + adc_T |= i2c_read_uint8(_address, BMX280_REGISTER_TEMPDATA+2); + adc_T >>= 4; + + int32_t var1t = ((((adc_T>>3) - + ((int32_t)_bmx280_calib.dig_T1 <<1))) * + ((int32_t)_bmx280_calib.dig_T2)) >> 11; + + int32_t var2t = (((((adc_T>>4) - + ((int32_t)_bmx280_calib.dig_T1)) * + ((adc_T>>4) - ((int32_t)_bmx280_calib.dig_T1))) >> 12) * + ((int32_t)_bmx280_calib.dig_T3)) >> 14; + + int32_t t_fine = var1t + var2t; + + double T = (t_fine * 5 + 128) >> 8; + _temperature = T / 100; + #else + int32_t t_fine = 102374; // ~20ºC + #endif + + // ----------------------------------------------------------------- + + #if BMX280_PRESSURE > 0 + int64_t var1, var2, p; + + int32_t adc_P = i2c_read_uint16(_address, BMX280_REGISTER_PRESSUREDATA); + adc_P <<= 8; + adc_P |= i2c_read_uint8(_address, BMX280_REGISTER_PRESSUREDATA+2); + adc_P >>= 4; + + var1 = ((int64_t)t_fine) - 128000; + var2 = var1 * var1 * (int64_t)_bmx280_calib.dig_P6; + var2 = var2 + ((var1*(int64_t)_bmx280_calib.dig_P5)<<17); + var2 = var2 + (((int64_t)_bmx280_calib.dig_P4)<<35); + var1 = ((var1 * var1 * (int64_t)_bmx280_calib.dig_P3)>>8) + + ((var1 * (int64_t)_bmx280_calib.dig_P2)<<12); + var1 = (((((int64_t)1)<<47)+var1))*((int64_t)_bmx280_calib.dig_P1)>>33; + if (var1 == 0) return; // avoid exception caused by division by zero + + p = 1048576 - adc_P; + p = (((p<<31) - var2)*3125) / var1; + var1 = (((int64_t)_bmx280_calib.dig_P9) * (p>>13) * (p>>13)) >> 25; + var2 = (((int64_t)_bmx280_calib.dig_P8) * p) >> 19; + + p = ((p + var1 + var2) >> 8) + (((int64_t)_bmx280_calib.dig_P7)<<4); + _pressure = (double) p / 256; + #endif + + // ----------------------------------------------------------------- + + #if BMX280_HUMIDITY > 0 + if (_chip == BMX280_CHIP_BME280) { + + int32_t adc_H = i2c_read_uint16(_address, BMX280_REGISTER_HUMIDDATA); + + int32_t v_x1_u32r; + + v_x1_u32r = (t_fine - ((int32_t)76800)); + + v_x1_u32r = (((((adc_H << 14) - (((int32_t)_bmx280_calib.dig_H4) << 20) - + (((int32_t)_bmx280_calib.dig_H5) * v_x1_u32r)) + ((int32_t)16384)) >> 15) * + (((((((v_x1_u32r * ((int32_t)_bmx280_calib.dig_H6)) >> 10) * + (((v_x1_u32r * ((int32_t)_bmx280_calib.dig_H3)) >> 11) + ((int32_t)32768))) >> 10) + + ((int32_t)2097152)) * ((int32_t)_bmx280_calib.dig_H2) + 8192) >> 14)); + + v_x1_u32r = (v_x1_u32r - (((((v_x1_u32r >> 15) * (v_x1_u32r >> 15)) >> 7) * + ((int32_t)_bmx280_calib.dig_H1)) >> 4)); + + v_x1_u32r = (v_x1_u32r < 0) ? 0 : v_x1_u32r; + v_x1_u32r = (v_x1_u32r > 419430400) ? 419430400 : v_x1_u32r; + double h = (v_x1_u32r >> 12); + _humidity = h / 1024.0; + + } + #endif + + } + // --------------------------------------------------------------------- - BME280 * _bme = NULL; unsigned char _chip; unsigned long _measurement_delay; + double _temperature = 0; + double _pressure = 0; + double _humidity = 0; + + typedef struct { + + uint16_t dig_T1; + int16_t dig_T2; + int16_t dig_T3; + + uint16_t dig_P1; + int16_t dig_P2; + int16_t dig_P3; + int16_t dig_P4; + int16_t dig_P5; + int16_t dig_P6; + int16_t dig_P7; + int16_t dig_P8; + int16_t dig_P9; + + uint8_t dig_H1; + int16_t dig_H2; + uint8_t dig_H3; + int16_t dig_H4; + int16_t dig_H5; + int8_t dig_H6; + + } bmx280_calib_t; + + bmx280_calib_t _bmx280_calib; }; diff --git a/code/platformio.ini b/code/platformio.ini index ffe91680..8299021a 100644 --- a/code/platformio.ini +++ b/code/platformio.ini @@ -22,7 +22,6 @@ lib_deps = OneWire Brzo I2C https://github.com/krosk93/espsoftwareserial#a770677 - SparkFun BME280 PMS Library https://github.com/madpilot/mDNSResolver#4cfcda1 https://bitbucket.org/xoseperez/justwifi.git#1.1.6