gobot.io/x/gobot/v2@v2.1.0/drivers/i2c/bmp388_driver.go (about) 1 package i2c 2 3 import ( 4 "bytes" 5 "encoding/binary" 6 "fmt" 7 "log" 8 "math" 9 ) 10 11 const bmp388Debug = false 12 13 // the default address is applicable for SDO to VDD, for SDO to GND it will be 0x76 14 const bmp388DefaultAddress = 0x77 15 16 // BMP388Accuracy accuracy type 17 type BMP388Accuracy uint8 18 type BMP388IIRFilter uint8 19 20 const ( 21 bmp388ChipID = 0x50 22 23 bmp388RegChipID = 0x00 24 bmp388RegStatus = 0x03 25 bmp388RegPressureData = 0x04 // XLSB, 0x05 LSByte, 0x06 MSByte 26 bmp388RegTempData = 0x07 // XLSB, 0x08 LSByte, 0x09 MSByte 27 bmp388RegPWRCTRL = 0x1B // enable/disable pressure and temperature measurement, mode 28 bmp388RegOSR = 0x1C // Oversampling Rates 29 bmp388RegODR = 0x1D // Output Data Rates 30 bmp388RegConf = 0x1F // config filter for IIR coefficients 31 bmp388RegCalib00 = 0x31 32 bmp388RegCMD = 0x7E 33 34 // bits 0, 1 of control register 35 bmp388PWRCTRLPressEnableBit = 0x01 36 bmp388PWRCTRLTempEnableBit = 0x02 37 38 // bits 4, 5 of control register (will be shifted on write) 39 bmp388PWRCTRLSleep = 0x00 40 bmp388PWRCTRLForced = 0x01 // same as 0x02 41 bmp388PWRCTRLNormal = 0x03 42 43 // bits 1, 2 ,3 of config filter IIR filter coefficients (will be shifted on write) 44 bmp388ConfFilterCoef0 BMP388IIRFilter = 0 // bypass-mode 45 bmp388ConfFilterCoef1 BMP388IIRFilter = 1 46 bmp388ConfFilterCoef3 BMP388IIRFilter = 2 47 bmp388ConfFilterCoef7 BMP388IIRFilter = 3 48 bmp388ConfFilterCoef15 BMP388IIRFilter = 4 49 bmp388ConfFilterCoef31 BMP388IIRFilter = 5 50 bmp388ConfFilterCoef63 BMP388IIRFilter = 6 51 bmp388ConfFilterCoef127 BMP388IIRFilter = 7 52 53 // oversampling rate, a single value is used (could be different for pressure and temperature) 54 BMP388AccuracyUltraLow BMP388Accuracy = 0 // x1 sample 55 BMP388AccuracyLow BMP388Accuracy = 1 // x2 samples 56 BMP388AccuracyStandard BMP388Accuracy = 2 // x4 samples 57 BMP388AccuracyHigh BMP388Accuracy = 3 // x8 samples 58 BMP388AccuracyUltraHigh BMP388Accuracy = 4 // x16 samples 59 BMP388AccuracyHighest BMP388Accuracy = 5 // x32 samples 60 61 bmp388CMDReserved = 0x00 // reserved, no command 62 bmp388CMDExtModeEnMiddle = 0x34 63 bmp388CMDFifoFlush = 0xB0 // clears all data in the FIFO, does not change FIFO_CONFIG registers 64 bmp388CMDSoftReset = 0xB6 // triggers a reset, all user configuration settings are overwritten with their default state 65 66 bmp388SeaLevelPressure = 1013.25 67 ) 68 69 type bmp388CalibrationCoefficients struct { 70 t1 float32 71 t2 float32 72 t3 float32 73 p1 float32 74 p2 float32 75 p3 float32 76 p4 float32 77 p5 float32 78 p6 float32 79 p7 float32 80 p8 float32 81 p9 float32 82 p10 float32 83 p11 float32 84 } 85 86 // BMP388Driver is a driver for the BMP388 temperature/pressure sensor 87 type BMP388Driver struct { 88 *Driver 89 calCoeffs *bmp388CalibrationCoefficients 90 ctrlPwrMode uint8 91 confFilter BMP388IIRFilter 92 } 93 94 // NewBMP388Driver creates a new driver with specified i2c interface. 95 // Params: 96 // c Connector - the Adaptor to use with this Driver 97 // 98 // Optional params: 99 // i2c.WithBus(int): bus to use with this driver 100 // i2c.WithAddress(int): address to use with this driver 101 // 102 func NewBMP388Driver(c Connector, options ...func(Config)) *BMP388Driver { 103 d := &BMP388Driver{ 104 Driver: NewDriver(c, "BMP388", bmp388DefaultAddress), 105 calCoeffs: &bmp388CalibrationCoefficients{}, 106 ctrlPwrMode: bmp388PWRCTRLForced, 107 confFilter: bmp388ConfFilterCoef0, 108 } 109 d.afterStart = d.initialization 110 111 for _, option := range options { 112 option(d) 113 } 114 115 // TODO: expose commands to API 116 return d 117 } 118 119 // WithBMP388IIRFilter option sets count of IIR filter coefficients. 120 // Valid settings are of type "BMP388IIRFilter" 121 func WithBMP388IIRFilter(val BMP388IIRFilter) func(Config) { 122 return func(c Config) { 123 if d, ok := c.(*BMP388Driver); ok { 124 d.confFilter = val 125 } else if bmp388Debug { 126 log.Printf("Trying to set IIR filter for non-BMP388Driver %v", c) 127 } 128 } 129 } 130 131 // Temperature returns the current temperature, in celsius degrees. 132 func (d *BMP388Driver) Temperature(accuracy BMP388Accuracy) (temp float32, err error) { 133 d.mutex.Lock() 134 defer d.mutex.Unlock() 135 136 var rawT int32 137 138 mode := uint8(d.ctrlPwrMode)<<4 | bmp388PWRCTRLPressEnableBit | bmp388PWRCTRLTempEnableBit 139 if err = d.connection.WriteByteData(bmp388RegPWRCTRL, mode); err != nil { 140 return 0, err 141 } 142 143 // Set Accuracy for temperature 144 if err = d.connection.WriteByteData(bmp388RegOSR, uint8(accuracy<<3)); err != nil { 145 return 0, err 146 } 147 148 if rawT, err = d.rawTemp(); err != nil { 149 return 0.0, err 150 } 151 temp = d.calculateTemp(rawT) 152 return 153 } 154 155 // Pressure returns the current barometric pressure, in Pa 156 func (d *BMP388Driver) Pressure(accuracy BMP388Accuracy) (press float32, err error) { 157 d.mutex.Lock() 158 defer d.mutex.Unlock() 159 160 var rawT, rawP int32 161 162 mode := uint8(d.ctrlPwrMode)<<4 | bmp388PWRCTRLPressEnableBit | bmp388PWRCTRLTempEnableBit 163 if err = d.connection.WriteByteData(bmp388RegPWRCTRL, mode); err != nil { 164 return 0, err 165 } 166 167 // Set Standard Accuracy for pressure 168 if err = d.connection.WriteByteData(bmp388RegOSR, uint8(accuracy)); err != nil { 169 return 0, err 170 } 171 172 if rawT, err = d.rawTemp(); err != nil { 173 return 0.0, err 174 } 175 176 if rawP, err = d.rawPressure(); err != nil { 177 return 0.0, err 178 } 179 tLin := d.calculateTemp(rawT) 180 return d.calculatePress(rawP, float64(tLin)), nil 181 } 182 183 // Altitude returns the current altitude in meters based on the 184 // current barometric pressure and estimated pressure at sea level. 185 // https://www.weather.gov/media/epz/wxcalc/pressureAltitude.pdf 186 func (d *BMP388Driver) Altitude(accuracy BMP388Accuracy) (alt float32, err error) { 187 atmP, _ := d.Pressure(accuracy) 188 atmP /= 100.0 189 alt = float32(44307.0 * (1.0 - math.Pow(float64(atmP/bmp388SeaLevelPressure), 0.190284))) 190 191 return 192 } 193 194 // initialization reads the calibration coefficients. 195 func (d *BMP388Driver) initialization() (err error) { 196 var chipID uint8 197 if chipID, err = d.connection.ReadByteData(bmp388RegChipID); err != nil { 198 return err 199 } 200 201 if bmp388ChipID != chipID { 202 return fmt.Errorf("Incorrect BMP388 chip ID '0%x' Expected 0x%x", chipID, bmp388ChipID) 203 } 204 205 var ( 206 t1 uint16 207 t2 uint16 208 t3 int8 209 p1 int16 210 p2 int16 211 p3 int8 212 p4 int8 213 p5 uint16 214 p6 uint16 215 p7 int8 216 p8 int8 217 p9 int16 218 p10 int8 219 p11 int8 220 ) 221 222 coefficients := make([]byte, 24) 223 if err = d.connection.ReadBlockData(bmp388RegCalib00, coefficients); err != nil { 224 return err 225 } 226 buf := bytes.NewBuffer(coefficients) 227 228 binary.Read(buf, binary.LittleEndian, &t1) 229 binary.Read(buf, binary.LittleEndian, &t2) 230 binary.Read(buf, binary.LittleEndian, &t3) 231 binary.Read(buf, binary.LittleEndian, &p1) 232 binary.Read(buf, binary.LittleEndian, &p2) 233 binary.Read(buf, binary.LittleEndian, &p3) 234 binary.Read(buf, binary.LittleEndian, &p4) 235 binary.Read(buf, binary.LittleEndian, &p5) 236 binary.Read(buf, binary.LittleEndian, &p6) 237 binary.Read(buf, binary.LittleEndian, &p7) 238 binary.Read(buf, binary.LittleEndian, &p8) 239 binary.Read(buf, binary.LittleEndian, &p9) 240 binary.Read(buf, binary.LittleEndian, &p10) 241 binary.Read(buf, binary.LittleEndian, &p11) 242 243 d.calCoeffs.t1 = float32(float64(t1) / math.Pow(2, -8)) 244 d.calCoeffs.t2 = float32(float64(t2) / math.Pow(2, 30)) 245 d.calCoeffs.t3 = float32(float64(t3) / math.Pow(2, 48)) 246 d.calCoeffs.p1 = float32((float64(p1) - math.Pow(2, 14)) / math.Pow(2, 20)) 247 d.calCoeffs.p2 = float32((float64(p2) - math.Pow(2, 14)) / math.Pow(2, 29)) 248 d.calCoeffs.p3 = float32(float64(p3) / math.Pow(2, 32)) 249 d.calCoeffs.p4 = float32(float64(p4) / math.Pow(2, 37)) 250 d.calCoeffs.p5 = float32(float64(p5) / math.Pow(2, -3)) 251 d.calCoeffs.p6 = float32(float64(p6) / math.Pow(2, 6)) 252 d.calCoeffs.p7 = float32(float64(p7) / math.Pow(2, 8)) 253 d.calCoeffs.p8 = float32(float64(p8) / math.Pow(2, 15)) 254 d.calCoeffs.p9 = float32(float64(p9) / math.Pow(2, 48)) 255 d.calCoeffs.p10 = float32(float64(p10) / math.Pow(2, 48)) 256 d.calCoeffs.p11 = float32(float64(p11) / math.Pow(2, 65)) 257 258 if err = d.connection.WriteByteData(bmp388RegCMD, bmp388CMDSoftReset); err != nil { 259 return err 260 } 261 262 if err = d.connection.WriteByteData(bmp388RegConf, uint8(d.confFilter)<<1); err != nil { 263 return err 264 } 265 266 return nil 267 } 268 269 func (d *BMP388Driver) rawTemp() (temp int32, err error) { 270 var tp0, tp1, tp2 byte 271 272 data := make([]byte, 3) 273 if err = d.connection.ReadBlockData(bmp388RegTempData, data); err != nil { 274 return 0, err 275 } 276 buf := bytes.NewBuffer(data) 277 278 binary.Read(buf, binary.LittleEndian, &tp0) // XLSB 279 binary.Read(buf, binary.LittleEndian, &tp1) // LSB 280 binary.Read(buf, binary.LittleEndian, &tp2) // MSB 281 282 temp = ((int32(tp2) << 16) | (int32(tp1) << 8) | int32(tp0)) 283 return 284 } 285 286 func (d *BMP388Driver) rawPressure() (press int32, err error) { 287 var tp0, tp1, tp2 byte 288 289 data := make([]byte, 3) 290 if err = d.connection.ReadBlockData(bmp388RegPressureData, data); err != nil { 291 return 0, err 292 } 293 buf := bytes.NewBuffer(data) 294 295 binary.Read(buf, binary.LittleEndian, &tp0) // XLSB 296 binary.Read(buf, binary.LittleEndian, &tp1) // LSB 297 binary.Read(buf, binary.LittleEndian, &tp2) // MSB 298 299 press = ((int32(tp2) << 16) | (int32(tp1) << 8) | int32(tp0)) 300 301 return 302 } 303 304 func (d *BMP388Driver) calculateTemp(rawTemp int32) float32 { 305 // datasheet, sec 9.2 Temperature compensation 306 pd1 := float32(rawTemp) - d.calCoeffs.t1 307 pd2 := pd1 * d.calCoeffs.t2 308 309 temperatureComp := pd2 + (pd1*pd1)*d.calCoeffs.t3 310 311 return temperatureComp 312 } 313 314 func (d *BMP388Driver) calculatePress(rawPress int32, tLin float64) float32 { 315 pd1 := float64(d.calCoeffs.p6) * tLin 316 pd2 := float64(d.calCoeffs.p7) * math.Pow(tLin, 2) 317 pd3 := float64(d.calCoeffs.p8) * math.Pow(tLin, 3) 318 po1 := float64(d.calCoeffs.p5) + pd1 + pd2 + pd3 319 320 pd1 = float64(d.calCoeffs.p2) * tLin 321 pd2 = float64(d.calCoeffs.p3) * math.Pow(tLin, 2) 322 pd3 = float64(d.calCoeffs.p4) * math.Pow(tLin, 3) 323 po2 := float64(rawPress) * (float64(d.calCoeffs.p1) + pd1 + pd2 + pd3) 324 325 pd1 = math.Pow(float64(rawPress), 2) 326 pd2 = float64(d.calCoeffs.p9) + float64(d.calCoeffs.p10)*tLin 327 pd3 = pd1 * pd2 328 pd4 := pd3 + math.Pow(float64(rawPress), 3)*float64(d.calCoeffs.p11) 329 330 pressure := po1 + po2 + pd4 331 332 return float32(pressure) 333 }