gobot.io/x/gobot@v1.16.0/drivers/i2c/bme280_driver.go (about)

     1  package i2c
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/binary"
     6  	"errors"
     7  )
     8  
     9  const bme280RegisterControlHumidity = 0xF2
    10  const bme280RegisterHumidityMSB = 0xFD
    11  const bme280RegisterCalibDigH1 = 0xa1
    12  const bme280RegisterCalibDigH2LSB = 0xe1
    13  
    14  type bmeHumidityCalibrationCoefficients struct {
    15  	h1 uint8
    16  	h2 int16
    17  	h3 uint8
    18  	h4 int16
    19  	h5 int16
    20  	h6 int8
    21  }
    22  
    23  // BME280Driver is a driver for the BME280 temperature/humidity sensor.
    24  // It implements all of the same functions as the BMP280Driver, but also
    25  // adds the Humidity() function by reading the BME280's humidity sensor.
    26  // For details on the BMP280Driver please see:
    27  // 	https://godoc.org/gobot.io/x/gobot/drivers/i2c#BMP280Driver
    28  //
    29  type BME280Driver struct {
    30  	*BMP280Driver
    31  	hc *bmeHumidityCalibrationCoefficients
    32  }
    33  
    34  // NewBME280Driver creates a new driver with specified i2c interface.
    35  // Params:
    36  //		conn Connector - the Adaptor to use with this Driver
    37  //
    38  // Optional params:
    39  //		i2c.WithBus(int):	bus to use with this driver
    40  //		i2c.WithAddress(int):	address to use with this driver
    41  //
    42  func NewBME280Driver(c Connector, options ...func(Config)) *BME280Driver {
    43  	b := &BME280Driver{
    44  		BMP280Driver: NewBMP280Driver(c),
    45  		hc:           &bmeHumidityCalibrationCoefficients{},
    46  	}
    47  
    48  	for _, option := range options {
    49  		option(b)
    50  	}
    51  
    52  	// TODO: expose commands to API
    53  	return b
    54  }
    55  
    56  // Start initializes the BME280 and loads the calibration coefficients.
    57  func (d *BME280Driver) Start() (err error) {
    58  	bus := d.GetBusOrDefault(d.connector.GetDefaultBus())
    59  	address := d.GetAddressOrDefault(bmp180Address)
    60  
    61  	if d.connection, err = d.connector.GetConnection(address, bus); err != nil {
    62  		return err
    63  	}
    64  
    65  	if err := d.initialization(); err != nil {
    66  		return err
    67  	}
    68  
    69  	if err := d.initHumidity(); err != nil {
    70  		return err
    71  	}
    72  
    73  	return nil
    74  }
    75  
    76  // Humidity returns the current humidity in percentage of relative humidity
    77  func (d *BME280Driver) Humidity() (humidity float32, err error) {
    78  	var rawH uint32
    79  	if rawH, err = d.rawHumidity(); err != nil {
    80  		return 0.0, err
    81  	}
    82  	humidity = d.calculateHumidity(rawH)
    83  	return
    84  }
    85  
    86  // read the humidity calibration coefficients.
    87  func (d *BME280Driver) initHumidity() (err error) {
    88  	var coefficients []byte
    89  	if coefficients, err = d.read(bme280RegisterCalibDigH1, 1); err != nil {
    90  		return err
    91  	}
    92  	buf := bytes.NewBuffer(coefficients)
    93  	binary.Read(buf, binary.BigEndian, &d.hc.h1)
    94  
    95  	if coefficients, err = d.read(bme280RegisterCalibDigH2LSB, 7); err != nil {
    96  		return err
    97  	}
    98  	buf = bytes.NewBuffer(coefficients)
    99  
   100  	// H4 and H5 laid out strangely on the bme280
   101  	var addrE4 byte
   102  	var addrE5 byte
   103  	var addrE6 byte
   104  
   105  	binary.Read(buf, binary.LittleEndian, &d.hc.h2) // E1 ...
   106  	binary.Read(buf, binary.BigEndian, &d.hc.h3)    // E3
   107  	binary.Read(buf, binary.BigEndian, &addrE4)     // E4
   108  	binary.Read(buf, binary.BigEndian, &addrE5)     // E5
   109  	binary.Read(buf, binary.BigEndian, &addrE6)     // E6
   110  	binary.Read(buf, binary.BigEndian, &d.hc.h6)    // ... E7
   111  
   112  	d.hc.h4 = 0 + (int16(addrE4) << 4) | (int16(addrE5 & 0x0F))
   113  	d.hc.h5 = 0 + (int16(addrE6) << 4) | (int16(addrE5) >> 4)
   114  
   115  	d.connection.WriteByteData(bme280RegisterControlHumidity, 0x3F)
   116  
   117  	// The 'ctrl_hum' register sets the humidity data acquisition options of
   118  	// the device. Changes to this register only become effective after a write
   119  	// operation to 'ctrl_meas'. Read the current value in, then write it back
   120  	var cmr uint8
   121  	cmr, err = d.connection.ReadByteData(bmp280RegisterControl)
   122  	if err == nil {
   123  		err = d.connection.WriteByteData(bmp280RegisterControl, cmr)
   124  	}
   125  	return err
   126  }
   127  
   128  func (d *BME280Driver) rawHumidity() (uint32, error) {
   129  	ret, err := d.read(bme280RegisterHumidityMSB, 2)
   130  	if err != nil {
   131  		return 0, err
   132  	}
   133  	if ret[0] == 0x80 && ret[1] == 0x00 {
   134  		return 0, errors.New("Humidity disabled")
   135  	}
   136  	buf := bytes.NewBuffer(ret)
   137  	var rawH uint16
   138  	binary.Read(buf, binary.BigEndian, &rawH)
   139  	return uint32(rawH), nil
   140  }
   141  
   142  // Adapted from https://github.com/BoschSensortec/BME280_driver/blob/master/bme280.c
   143  // function bme280_compensate_humidity_double(s32 v_uncom_humidity_s32)
   144  func (d *BME280Driver) calculateHumidity(rawH uint32) float32 {
   145  	var rawT int32
   146  	var err error
   147  	var h float32
   148  
   149  	rawT, err = d.rawTemp()
   150  	if err != nil {
   151  		return 0
   152  	}
   153  
   154  	_, tFine := d.calculateTemp(rawT)
   155  	h = float32(tFine) - 76800
   156  
   157  	if h == 0 {
   158  		return 0 // TODO err is 'invalid data' from Bosch - include errors or not?
   159  	}
   160  
   161  	x := float32(rawH) - (float32(d.hc.h4)*64.0 +
   162  		(float32(d.hc.h5) / 16384.0 * h))
   163  
   164  	y := float32(d.hc.h2) / 65536.0 *
   165  		(1.0 + float32(d.hc.h6)/67108864.0*h*
   166  			(1.0+float32(d.hc.h3)/67108864.0*h))
   167  
   168  	h = x * y
   169  	h = h * (1 - float32(d.hc.h1)*h/524288)
   170  	return h
   171  }