gobot.io/x/gobot@v1.16.0/drivers/i2c/bmp388_driver.go (about) 1 package i2c 2 3 import ( 4 "bytes" 5 "encoding/binary" 6 "fmt" 7 "math" 8 9 "gobot.io/x/gobot" 10 ) 11 12 const ( 13 bmp388ChipID = 0x50 14 15 bmp388RegisterChipID = 0x00 16 bmp388RegisterStatus = 0x03 17 bmp388RegisterConfig = 0x1F 18 bmp388RegisterPressureData = 0x04 19 bmp388RegisterTempData = 0x07 20 bmp388RegisterCalib00 = 0x31 21 bmp388RegisterCMD = 0x7E 22 // CMD : 0x00 nop (reserved. No command.) 23 // : 0x34 extmode_en_middle 24 // : 0xB0 fifo_flush (Clears all data in the FIFO, does not change FIFO_CONFIG registers) 25 // : 0xB6 softreset (Triggers a reset, all user configuration settings are overwritten with their default state) 26 bmp388RegisterODR = 0x1D // Output Data Rates 27 bmp388RegisterOSR = 0x1C // Oversampling Rates 28 29 bmp388RegisterPWRCTRL = 0x1B 30 bmp388PWRCTRLSleep = 0 31 bmp388PWRCTRLForced = 1 32 bmp388PWRCTRLNormal = 3 33 34 bmp388SeaLevelPressure = 1013.25 35 36 // IIR filter coefficients 37 bmp388IIRFIlterCoef0 = 0 // bypass-mode 38 bmp388IIRFIlterCoef1 = 1 39 bmp388IIRFIlterCoef3 = 2 40 bmp388IIRFIlterCoef7 = 3 41 bmp388IIRFIlterCoef15 = 4 42 bmp388IIRFIlterCoef31 = 5 43 bmp388IIRFIlterCoef63 = 6 44 bmp388IIRFIlterCoef127 = 7 45 ) 46 47 // BMP388Accuracy accuracy type 48 type BMP388Accuracy uint8 49 50 // BMP388Accuracy accuracy modes 51 const ( 52 BMP388AccuracyUltraLow BMP388Accuracy = 0 // x1 sample 53 BMP388AccuracyLow BMP388Accuracy = 1 // x2 samples 54 BMP388AccuracyStandard BMP388Accuracy = 2 // x4 samples 55 BMP388AccuracyHigh BMP388Accuracy = 3 // x8 samples 56 BMP388AccuracyUltraHigh BMP388Accuracy = 4 // x16 samples 57 BMP388AccuracyHighest BMP388Accuracy = 5 // x32 samples 58 ) 59 60 type bmp388CalibrationCoefficients struct { 61 t1 float32 62 t2 float32 63 t3 float32 64 p1 float32 65 p2 float32 66 p3 float32 67 p4 float32 68 p5 float32 69 p6 float32 70 p7 float32 71 p8 float32 72 p9 float32 73 p10 float32 74 p11 float32 75 } 76 77 // BMP388Driver is a driver for the BMP388 temperature/pressure sensor 78 type BMP388Driver struct { 79 name string 80 connector Connector 81 connection Connection 82 Config 83 84 tpc *bmp388CalibrationCoefficients 85 } 86 87 // NewBMP388Driver creates a new driver with specified i2c interface. 88 // Params: 89 // conn Connector - the Adaptor to use with this Driver 90 // 91 // Optional params: 92 // i2c.WithBus(int): bus to use with this driver 93 // i2c.WithAddress(int): address to use with this driver 94 // 95 func NewBMP388Driver(c Connector, options ...func(Config)) *BMP388Driver { 96 b := &BMP388Driver{ 97 name: gobot.DefaultName("BMP388"), 98 connector: c, 99 Config: NewConfig(), 100 tpc: &bmp388CalibrationCoefficients{}, 101 } 102 103 for _, option := range options { 104 option(b) 105 } 106 107 // TODO: expose commands to API 108 return b 109 } 110 111 // Name returns the name of the device. 112 func (d *BMP388Driver) Name() string { 113 return d.name 114 } 115 116 // SetName sets the name of the device. 117 func (d *BMP388Driver) SetName(n string) { 118 d.name = n 119 } 120 121 // Connection returns the connection of the device. 122 func (d *BMP388Driver) Connection() gobot.Connection { 123 return d.connector.(gobot.Connection) 124 } 125 126 // Start initializes the BMP388 and loads the calibration coefficients. 127 func (d *BMP388Driver) Start() (err error) { 128 var chipID uint8 129 130 bus := d.GetBusOrDefault(d.connector.GetDefaultBus()) 131 address := d.GetAddressOrDefault(bmp180Address) 132 133 if d.connection, err = d.connector.GetConnection(address, bus); err != nil { 134 return err 135 } 136 137 if chipID, err = d.connection.ReadByteData(bmp388RegisterChipID); err != nil { 138 return err 139 } 140 141 if bmp388ChipID != chipID { 142 return fmt.Errorf("Incorrect BMP388 chip ID '0%x' Expected 0x%x", chipID, bmp388ChipID) 143 } 144 145 if err := d.initialization(); err != nil { 146 return err 147 } 148 149 return nil 150 } 151 152 // Halt halts the device. 153 func (d *BMP388Driver) Halt() (err error) { 154 return nil 155 } 156 157 // Temperature returns the current temperature, in celsius degrees. 158 func (d *BMP388Driver) Temperature(accuracy BMP388Accuracy) (temp float32, err error) { 159 var rawT int32 160 161 // Enable Pressure and Temperature measurement, set FORCED operating mode 162 var mode byte = (bmp388PWRCTRLForced << 4) | 3 // 1100|1|1 == mode|T|P 163 if err = d.connection.WriteByteData(bmp388RegisterPWRCTRL, mode); err != nil { 164 return 0, err 165 } 166 167 // Set Accuracy for temperature 168 if err = d.connection.WriteByteData(bmp388RegisterOSR, uint8(accuracy<<3)); err != nil { 169 return 0, err 170 } 171 172 if rawT, err = d.rawTemp(); err != nil { 173 return 0.0, err 174 } 175 temp = d.calculateTemp(rawT) 176 return 177 } 178 179 // Pressure returns the current barometric pressure, in Pa 180 func (d *BMP388Driver) Pressure(accuracy BMP388Accuracy) (press float32, err error) { 181 var rawT, rawP int32 182 183 // Enable Pressure and Temperature measurement, set FORCED operating mode 184 var mode byte = (bmp388PWRCTRLForced << 4) | 3 // 1100|1|1 == mode|T|P 185 if err = d.connection.WriteByteData(bmp388RegisterPWRCTRL, mode); err != nil { 186 return 0, err 187 } 188 189 // Set Standard Accuracy for pressure 190 if err = d.connection.WriteByteData(bmp388RegisterOSR, uint8(accuracy)); err != nil { 191 return 0, err 192 } 193 194 if rawT, err = d.rawTemp(); err != nil { 195 return 0.0, err 196 } 197 198 if rawP, err = d.rawPressure(); err != nil { 199 return 0.0, err 200 } 201 tLin := d.calculateTemp(rawT) 202 return d.calculatePress(rawP, float64(tLin)), nil 203 } 204 205 // Altitude returns the current altitude in meters based on the 206 // current barometric pressure and estimated pressure at sea level. 207 // https://www.weather.gov/media/epz/wxcalc/pressureAltitude.pdf 208 func (d *BMP388Driver) Altitude(accuracy BMP388Accuracy) (alt float32, err error) { 209 atmP, _ := d.Pressure(accuracy) 210 atmP /= 100.0 211 alt = float32(44307.0 * (1.0 - math.Pow(float64(atmP/bmp388SeaLevelPressure), 0.190284))) 212 213 return 214 } 215 216 // initialization reads the calibration coefficients. 217 func (d *BMP388Driver) initialization() (err error) { 218 var ( 219 coefficients []byte 220 t1 uint16 221 t2 uint16 222 t3 int8 223 p1 int16 224 p2 int16 225 p3 int8 226 p4 int8 227 p5 uint16 228 p6 uint16 229 p7 int8 230 p8 int8 231 p9 int16 232 p10 int8 233 p11 int8 234 ) 235 236 if coefficients, err = d.read(bmp388RegisterCalib00, 24); err != nil { 237 return err 238 } 239 buf := bytes.NewBuffer(coefficients) 240 241 binary.Read(buf, binary.LittleEndian, &t1) 242 binary.Read(buf, binary.LittleEndian, &t2) 243 binary.Read(buf, binary.LittleEndian, &t3) 244 binary.Read(buf, binary.LittleEndian, &p1) 245 binary.Read(buf, binary.LittleEndian, &p2) 246 binary.Read(buf, binary.LittleEndian, &p3) 247 binary.Read(buf, binary.LittleEndian, &p4) 248 binary.Read(buf, binary.LittleEndian, &p5) 249 binary.Read(buf, binary.LittleEndian, &p6) 250 binary.Read(buf, binary.LittleEndian, &p7) 251 binary.Read(buf, binary.LittleEndian, &p8) 252 binary.Read(buf, binary.LittleEndian, &p9) 253 binary.Read(buf, binary.LittleEndian, &p10) 254 binary.Read(buf, binary.LittleEndian, &p11) 255 256 d.tpc.t1 = float32(float64(t1) / math.Pow(2, -8)) 257 d.tpc.t2 = float32(float64(t2) / math.Pow(2, 30)) 258 d.tpc.t3 = float32(float64(t3) / math.Pow(2, 48)) 259 d.tpc.p1 = float32((float64(p1) - math.Pow(2, 14)) / math.Pow(2, 20)) 260 d.tpc.p2 = float32((float64(p2) - math.Pow(2, 14)) / math.Pow(2, 29)) 261 d.tpc.p3 = float32(float64(p3) / math.Pow(2, 32)) 262 d.tpc.p4 = float32(float64(p4) / math.Pow(2, 37)) 263 d.tpc.p5 = float32(float64(p5) / math.Pow(2, -3)) 264 d.tpc.p6 = float32(float64(p6) / math.Pow(2, 6)) 265 d.tpc.p7 = float32(float64(p7) / math.Pow(2, 8)) 266 d.tpc.p8 = float32(float64(p8) / math.Pow(2, 15)) 267 d.tpc.p9 = float32(float64(p9) / math.Pow(2, 48)) 268 d.tpc.p10 = float32(float64(p10) / math.Pow(2, 48)) 269 d.tpc.p11 = float32(float64(p11) / math.Pow(2, 65)) 270 271 // Perform a power on reset. All user configuration settings are overwritten 272 // with their default state. 273 if err = d.connection.WriteByteData(bmp388RegisterCMD, 0xB6); err != nil { 274 return err 275 } 276 277 // set IIR filter to off 278 if err = d.connection.WriteByteData(bmp388RegisterConfig, bmp388IIRFIlterCoef0<<1); err != nil { 279 return err 280 } 281 282 return nil 283 } 284 285 func (d *BMP388Driver) rawTemp() (temp int32, err error) { 286 var data []byte 287 var tp0, tp1, tp2 byte 288 289 if data, err = d.read(bmp388RegisterTempData, 3); err != nil { 290 return 0, err 291 } 292 buf := bytes.NewBuffer(data) 293 294 binary.Read(buf, binary.LittleEndian, &tp0) // XLSB 295 binary.Read(buf, binary.LittleEndian, &tp1) // LSB 296 binary.Read(buf, binary.LittleEndian, &tp2) // MSB 297 298 temp = ((int32(tp2) << 16) | (int32(tp1) << 8) | int32(tp0)) 299 return 300 } 301 302 func (d *BMP388Driver) rawPressure() (press int32, err error) { 303 var data []byte 304 var tp0, tp1, tp2 byte 305 306 if data, err = d.read(bmp388RegisterPressureData, 3); err != nil { 307 return 0, err 308 } 309 buf := bytes.NewBuffer(data) 310 311 binary.Read(buf, binary.LittleEndian, &tp0) // XLSB 312 binary.Read(buf, binary.LittleEndian, &tp1) // LSB 313 binary.Read(buf, binary.LittleEndian, &tp2) // MSB 314 315 press = ((int32(tp2) << 16) | (int32(tp1) << 8) | int32(tp0)) 316 317 return 318 } 319 320 func (d *BMP388Driver) calculateTemp(rawTemp int32) float32 { 321 // datasheet, sec 9.2 Temperature compensation 322 pd1 := float32(rawTemp) - d.tpc.t1 323 pd2 := pd1 * d.tpc.t2 324 325 temperatureComp := pd2 + (pd1*pd1)*d.tpc.t3 326 327 return temperatureComp 328 } 329 330 func (d *BMP388Driver) calculatePress(rawPress int32, tLin float64) float32 { 331 pd1 := float64(d.tpc.p6) * tLin 332 pd2 := float64(d.tpc.p7) * math.Pow(tLin, 2) 333 pd3 := float64(d.tpc.p8) * math.Pow(tLin, 3) 334 po1 := float64(d.tpc.p5) + pd1 + pd2 + pd3 335 336 pd1 = float64(d.tpc.p2) * tLin 337 pd2 = float64(d.tpc.p3) * math.Pow(tLin, 2) 338 pd3 = float64(d.tpc.p4) * math.Pow(tLin, 3) 339 po2 := float64(rawPress) * (float64(d.tpc.p1) + pd1 + pd2 + pd3) 340 341 pd1 = math.Pow(float64(rawPress), 2) 342 pd2 = float64(d.tpc.p9) + float64(d.tpc.p10)*tLin 343 pd3 = pd1 * pd2 344 pd4 := pd3 + math.Pow(float64(rawPress), 3)*float64(d.tpc.p11) 345 346 pressure := po1 + po2 + pd4 347 348 return float32(pressure) 349 } 350 351 func (d *BMP388Driver) read(address byte, n int) ([]byte, error) { 352 if _, err := d.connection.Write([]byte{address}); err != nil { 353 return nil, err 354 } 355 buf := make([]byte, n) 356 bytesRead, err := d.connection.Read(buf) 357 if bytesRead != n || err != nil { 358 return nil, err 359 } 360 return buf, nil 361 }