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 }