gobot.io/x/gobot/v2@v2.1.0/drivers/i2c/bmp280_driver.go (about) 1 package i2c 2 3 import ( 4 "bytes" 5 "encoding/binary" 6 "log" 7 "math" 8 ) 9 10 const bmp280Debug = true 11 12 // the default address is applicable for SDO to VDD, for SDO to GND it will be 0x76 13 // this is also true for bme280 (which using this address as well) 14 const bmp280DefaultAddress = 0x77 15 16 type BMP280PressureOversampling uint8 17 type BMP280TemperatureOversampling uint8 18 type BMP280IIRFilter uint8 19 20 const ( 21 bmp280RegCalib00 = 0x88 // 12 x 16 bit calibration data (T1..T3, P1..P9) 22 bmp280RegCtrl = 0xF4 // data acquisition options (oversampling of temperature and pressure, power mode) 23 bmp280RegConf = 0xF5 // rate, IIR-filter and interface options (SPI) 24 bmp280RegPressureData = 0xF7 25 bmp280RegTempData = 0xFA 26 27 // bits 0, 1 of control register 28 bmp280CtrlPwrSleepMode = 0x00 29 bmp280CtrlPwrForcedMode = 0x01 30 bmp280CtrlPwrForcedMode2 = 0x02 // same function as 0x01 31 bmp280CtrlPwrNormalMode = 0x03 32 33 // bits 2, 3, 4 of control register (will be shifted on write) 34 BMP280CtrlPressNoMeasurement BMP280PressureOversampling = 0x00 // no measurement (value will be 0x08 0x00 0x00) 35 BMP280CtrlPressOversampling1 BMP280PressureOversampling = 0x01 // resolution 16 bit 36 BMP280CtrlPressOversampling2 BMP280PressureOversampling = 0x02 // resolution 17 bit 37 BMP280CtrlPressOversampling4 BMP280PressureOversampling = 0x03 // resolution 18 bit 38 BMP280CtrlPressOversampling8 BMP280PressureOversampling = 0x04 // resolution 19 bit 39 BMP280CtrlPressOversampling16 BMP280PressureOversampling = 0x05 // resolution 20 bit (same as 0x06, 0x07) 40 41 // bits 5, 6, 7 of control register (will be shifted on write) 42 BMP280CtrlTempNoMeasurement BMP280TemperatureOversampling = 0x00 // no measurement (value will be 0x08 0x00 0x00) 43 BMP280CtrlTempOversampling1 BMP280TemperatureOversampling = 0x01 // resolution 16 bit 44 BMP280CtrlTempOversampling2 BMP280TemperatureOversampling = 0x02 // resolution 17 bit 45 BMP280CtrlTempOversampling4 BMP280TemperatureOversampling = 0x03 // resolution 18 bit 46 BMP280CtrlTempOversampling8 BMP280TemperatureOversampling = 0x04 // resolution 19 bit 47 BMP280CtrlTempOversampling16 BMP280TemperatureOversampling = 0x05 // resolution 20 bit 48 49 // bit 0 of config register 50 bmp280ConfSPIBit = 0x01 // if set, SPI is used 51 52 // bits 2, 3, 4 of config register (bit 1 is unused, will be shifted on write) 53 bmp280ConfStandBy0005 = 0x00 // 0.5 ms 54 bmp280ConfStandBy0625 = 0x01 // 62.5 ms 55 bmp280ConfStandBy0125 = 0x02 // 125 ms 56 bmp280ConfStandBy0250 = 0x03 // 250 ms 57 bmp280ConfStandBy0500 = 0x04 // 500 ms 58 bmp280ConfStandBy1000 = 0x05 // 1000 ms 59 bmp280ConfStandBy2000 = 0x06 // 2000 ms 60 bmp280ConfStandBy4000 = 0x07 // 4000 ms 61 62 // bits 5, 6, 7 of config register 63 BMP280ConfFilterOff BMP280IIRFilter = 0x00 64 BMP280ConfFilter2 BMP280IIRFilter = 0x01 65 BMP280ConfFilter4 BMP280IIRFilter = 0x02 66 BMP280ConfFilter8 BMP280IIRFilter = 0x03 67 BMP280ConfFilter16 BMP280IIRFilter = 0x04 68 69 bmp280SeaLevelPressure = 1013.25 70 ) 71 72 type bmp280CalibrationCoefficients struct { 73 t1 uint16 74 t2 int16 75 t3 int16 76 p1 uint16 77 p2 int16 78 p3 int16 79 p4 int16 80 p5 int16 81 p6 int16 82 p7 int16 83 p8 int16 84 p9 int16 85 } 86 87 // BMP280Driver is a driver for the BMP280 temperature/pressure sensor 88 type BMP280Driver struct { 89 *Driver 90 calCoeffs *bmp280CalibrationCoefficients 91 ctrlPwrMode uint8 92 ctrlPressOversamp BMP280PressureOversampling 93 ctrlTempOversamp BMP280TemperatureOversampling 94 confFilter BMP280IIRFilter 95 } 96 97 // NewBMP280Driver creates a new driver with specified i2c interface. 98 // Params: 99 // c Connector - the Adaptor to use with this Driver 100 // 101 // Optional params: 102 // i2c.WithBus(int): bus to use with this driver 103 // i2c.WithAddress(int): address to use with this driver 104 // 105 func NewBMP280Driver(c Connector, options ...func(Config)) *BMP280Driver { 106 d := &BMP280Driver{ 107 Driver: NewDriver(c, "BMP280", bmp280DefaultAddress), 108 calCoeffs: &bmp280CalibrationCoefficients{}, 109 ctrlPwrMode: bmp280CtrlPwrNormalMode, 110 ctrlPressOversamp: BMP280CtrlPressOversampling16, 111 ctrlTempOversamp: BMP280CtrlTempOversampling1, 112 confFilter: BMP280ConfFilterOff, 113 } 114 d.afterStart = d.initialization 115 116 for _, option := range options { 117 option(d) 118 } 119 120 // TODO: expose commands to API 121 return d 122 } 123 124 // WithBMP280PressureOversampling option sets the oversampling for pressure. 125 // Valid settings are of type "BMP280PressureOversampling" 126 func WithBMP280PressureOversampling(val BMP280PressureOversampling) func(Config) { 127 return func(c Config) { 128 if d, ok := c.(*BMP280Driver); ok { 129 d.ctrlPressOversamp = val 130 } else if bmp280Debug { 131 log.Printf("Trying to set pressure oversampling for non-BMP280Driver %v", c) 132 } 133 } 134 } 135 136 // WithBMP280TemperatureOversampling option sets oversampling for temperature. 137 // Valid settings are of type "BMP280TemperatureOversampling" 138 func WithBMP280TemperatureOversampling(val BMP280TemperatureOversampling) func(Config) { 139 return func(c Config) { 140 if d, ok := c.(*BMP280Driver); ok { 141 d.ctrlTempOversamp = val 142 } else if bmp280Debug { 143 log.Printf("Trying to set temperature oversampling for non-BMP280Driver %v", c) 144 } 145 } 146 } 147 148 // WithBMP280IIRFilter option sets the count of IIR filter coefficients. 149 // Valid settings are of type "BMP280IIRFilter" 150 func WithBMP280IIRFilter(val BMP280IIRFilter) func(Config) { 151 return func(c Config) { 152 if d, ok := c.(*BMP280Driver); ok { 153 d.confFilter = val 154 } else if bmp280Debug { 155 log.Printf("Trying to set IIR filter for non-BMP280Driver %v", c) 156 } 157 } 158 } 159 160 // Temperature returns the current temperature, in celsius degrees. 161 func (d *BMP280Driver) Temperature() (temp float32, err error) { 162 d.mutex.Lock() 163 defer d.mutex.Unlock() 164 165 var rawT int32 166 if rawT, err = d.rawTemp(); err != nil { 167 return 0.0, err 168 } 169 temp, _ = d.calculateTemp(rawT) 170 return 171 } 172 173 // Pressure returns the current barometric pressure, in Pa 174 func (d *BMP280Driver) Pressure() (press float32, err error) { 175 d.mutex.Lock() 176 defer d.mutex.Unlock() 177 178 var rawT, rawP int32 179 if rawT, err = d.rawTemp(); err != nil { 180 return 0.0, err 181 } 182 183 if rawP, err = d.rawPressure(); err != nil { 184 return 0.0, err 185 } 186 _, tFine := d.calculateTemp(rawT) 187 return d.calculatePress(rawP, tFine), nil 188 } 189 190 // Altitude returns the current altitude in meters based on the 191 // current barometric pressure and estimated pressure at sea level. 192 // Calculation is based on code from Adafruit BME280 library 193 // https://github.com/adafruit/Adafruit_BME280_Library 194 func (d *BMP280Driver) Altitude() (alt float32, err error) { 195 atmP, _ := d.Pressure() 196 atmP /= 100.0 197 alt = float32(44330.0 * (1.0 - math.Pow(float64(atmP/bmp280SeaLevelPressure), 0.1903))) 198 199 return 200 } 201 202 // initialization reads the calibration coefficients. 203 func (d *BMP280Driver) initialization() (err error) { 204 coefficients := make([]byte, 24) 205 if err = d.connection.ReadBlockData(bmp280RegCalib00, coefficients); err != nil { 206 return err 207 } 208 buf := bytes.NewBuffer(coefficients) 209 binary.Read(buf, binary.LittleEndian, &d.calCoeffs.t1) 210 binary.Read(buf, binary.LittleEndian, &d.calCoeffs.t2) 211 binary.Read(buf, binary.LittleEndian, &d.calCoeffs.t3) 212 binary.Read(buf, binary.LittleEndian, &d.calCoeffs.p1) 213 binary.Read(buf, binary.LittleEndian, &d.calCoeffs.p2) 214 binary.Read(buf, binary.LittleEndian, &d.calCoeffs.p3) 215 binary.Read(buf, binary.LittleEndian, &d.calCoeffs.p4) 216 binary.Read(buf, binary.LittleEndian, &d.calCoeffs.p5) 217 binary.Read(buf, binary.LittleEndian, &d.calCoeffs.p6) 218 binary.Read(buf, binary.LittleEndian, &d.calCoeffs.p7) 219 binary.Read(buf, binary.LittleEndian, &d.calCoeffs.p8) 220 binary.Read(buf, binary.LittleEndian, &d.calCoeffs.p9) 221 222 ctrlReg := uint8(d.ctrlPwrMode) | uint8(d.ctrlPressOversamp)<<2 | uint8(d.ctrlTempOversamp)<<5 223 d.connection.WriteByteData(bmp280RegCtrl, ctrlReg) 224 225 confReg := uint8(bmp280ConfStandBy0005)<<2 | uint8(d.confFilter)<<5 226 d.connection.WriteByteData(bmp280RegConf, confReg & ^uint8(bmp280ConfSPIBit)) 227 228 return nil 229 } 230 231 func (d *BMP280Driver) rawTemp() (temp int32, err error) { 232 var tp0, tp1, tp2 byte 233 234 data := make([]byte, 3) 235 if err = d.connection.ReadBlockData(bmp280RegTempData, data); err != nil { 236 return 0, err 237 } 238 buf := bytes.NewBuffer(data) 239 binary.Read(buf, binary.LittleEndian, &tp0) 240 binary.Read(buf, binary.LittleEndian, &tp1) 241 binary.Read(buf, binary.LittleEndian, &tp2) 242 243 temp = ((int32(tp2) >> 4) | (int32(tp1) << 4) | (int32(tp0) << 12)) 244 245 return 246 } 247 248 func (d *BMP280Driver) rawPressure() (press int32, err error) { 249 var tp0, tp1, tp2 byte 250 251 data := make([]byte, 3) 252 if err = d.connection.ReadBlockData(bmp280RegPressureData, data); err != nil { 253 return 0, err 254 } 255 buf := bytes.NewBuffer(data) 256 binary.Read(buf, binary.LittleEndian, &tp0) 257 binary.Read(buf, binary.LittleEndian, &tp1) 258 binary.Read(buf, binary.LittleEndian, &tp2) 259 260 press = ((int32(tp2) >> 4) | (int32(tp1) << 4) | (int32(tp0) << 12)) 261 262 return 263 } 264 265 func (d *BMP280Driver) calculateTemp(rawTemp int32) (float32, int32) { 266 tcvar1 := ((float32(rawTemp) / 16384.0) - (float32(d.calCoeffs.t1) / 1024.0)) * float32(d.calCoeffs.t2) 267 tcvar2 := (((float32(rawTemp) / 131072.0) - (float32(d.calCoeffs.t1) / 8192.0)) * ((float32(rawTemp) / 131072.0) - float32(d.calCoeffs.t1)/8192.0)) * float32(d.calCoeffs.t3) 268 temperatureComp := (tcvar1 + tcvar2) / 5120.0 269 270 tFine := int32(tcvar1 + tcvar2) 271 return temperatureComp, tFine 272 } 273 274 func (d *BMP280Driver) calculatePress(rawPress int32, tFine int32) float32 { 275 var var1, var2, p int64 276 277 var1 = int64(tFine) - 128000 278 var2 = var1 * var1 * int64(d.calCoeffs.p6) 279 var2 = var2 + ((var1 * int64(d.calCoeffs.p5)) << 17) 280 var2 = var2 + (int64(d.calCoeffs.p4) << 35) 281 var1 = (var1 * var1 * int64(d.calCoeffs.p3) >> 8) + 282 ((var1 * int64(d.calCoeffs.p2)) << 12) 283 var1 = ((int64(1) << 47) + var1) * (int64(d.calCoeffs.p1)) >> 33 284 285 if var1 == 0 { 286 return 0 // avoid exception caused by division by zero 287 } 288 p = 1048576 - int64(rawPress) 289 p = (((p << 31) - var2) * 3125) / var1 290 var1 = (int64(d.calCoeffs.p9) * (p >> 13) * (p >> 13)) >> 25 291 var2 = (int64(d.calCoeffs.p8) * p) >> 19 292 293 p = ((p + var1 + var2) >> 8) + (int64(d.calCoeffs.p7) << 4) 294 return float32(p) / 256 295 }