tinygo.org/x/drivers@v0.27.1-0.20240509133757-7dbca2a54349/bmp388/bmp388.go (about) 1 package bmp388 2 3 import ( 4 "errors" 5 6 "tinygo.org/x/drivers" 7 "tinygo.org/x/drivers/internal/legacy" 8 ) 9 10 var ( 11 errConfigWrite = errors.New("bmp388: failed to configure sensor, check connection") 12 errConfig = errors.New("bmp388: there is a problem with the configuration, try reducing ODR") 13 errCaliRead = errors.New("bmp388: failed to read calibration coefficient register") 14 errSoftReset = errors.New("bmp388: failed to perform a soft reset") 15 errNotConnected = errors.New("bmp388: not connected") 16 ) 17 18 type Oversampling byte 19 type Mode byte 20 type OutputDataRate byte 21 type FilterCoefficient byte 22 23 // Config contains settings for filtering, sampling, and modes of operation 24 type Config struct { 25 Pressure Oversampling 26 Temperature Oversampling 27 Mode Mode 28 ODR OutputDataRate 29 IIR FilterCoefficient 30 } 31 32 // Device wraps the I2C connection and configuration values for the BMP388 33 type Device struct { 34 bus drivers.I2C 35 Address uint8 36 cali calibrationCoefficients 37 Config Config 38 } 39 40 type calibrationCoefficients struct { 41 // Temperature compensation 42 t1 uint16 43 t2 uint16 44 t3 int8 45 46 // Pressure compensation 47 p1 int16 48 p2 int16 49 p3 int8 50 p4 int8 51 p5 uint16 52 p6 uint16 53 p7 int8 54 p8 int8 55 p9 int16 56 p10 int8 57 p11 int8 58 } 59 60 // New returns a bmp388 struct with the default I2C address. Configure must also be called after instanting 61 func New(bus drivers.I2C) Device { 62 return Device{ 63 bus: bus, 64 Address: Address, 65 } 66 } 67 68 // Configure can enable settings on the BMP388 and reads the calibration coefficients 69 func (d *Device) Configure(config Config) (err error) { 70 d.Config = config 71 72 if d.Config == (Config{}) { 73 d.Config.Mode = Normal 74 } 75 76 // Turning on the pressure and temperature sensors and setting the measurement mode 77 err = d.writeRegister(RegPwrCtrl, PwrPress|PwrTemp|byte(d.Config.Mode)) 78 79 // Configure the oversampling, output data rate, and iir filter coefficient settings 80 err = d.writeRegister(RegOSR, byte(d.Config.Pressure|d.Config.Temperature<<3)) 81 err = d.writeRegister(RegODR, byte(d.Config.ODR)) 82 err = d.writeRegister(RegIIR, byte(d.Config.IIR<<1)) 83 84 if err != nil { 85 return errConfigWrite 86 } 87 88 // Check if there is a problem with the given configuration 89 if d.configurationError() { 90 return errConfig 91 } 92 93 // Reading the builtin calibration coefficients and parsing them per the datasheet. The compensation formula given 94 // in the datasheet is implemented in floating point 95 buffer, err := d.readRegister(RegCali, 21) 96 if err != nil { 97 return errCaliRead 98 } 99 100 d.cali.t1 = uint16(buffer[1])<<8 | uint16(buffer[0]) 101 d.cali.t2 = uint16(buffer[3])<<8 | uint16(buffer[2]) 102 d.cali.t3 = int8(buffer[4]) 103 104 d.cali.p1 = int16(buffer[6])<<8 | int16(buffer[5]) 105 d.cali.p2 = int16(buffer[8])<<8 | int16(buffer[7]) 106 d.cali.p3 = int8(buffer[9]) 107 d.cali.p4 = int8(buffer[10]) 108 d.cali.p5 = uint16(buffer[12])<<8 | uint16(buffer[11]) 109 d.cali.p6 = uint16(buffer[14])<<8 | uint16(buffer[13]) 110 d.cali.p7 = int8(buffer[15]) 111 d.cali.p8 = int8(buffer[16]) 112 d.cali.p9 = int16(buffer[18])<<8 | int16(buffer[17]) 113 d.cali.p10 = int8(buffer[19]) 114 d.cali.p11 = int8(buffer[20]) 115 116 return nil 117 } 118 119 // Read the temperature registers and compute a compensation value for the temperature and pressure compensation 120 // calculations. This is not the temperature itself. 121 func (d *Device) tlinCompensate() (int64, error) { 122 rawTemp, err := d.readSensorData(RegTemp) 123 if err != nil { 124 return 0, err 125 } 126 127 // pulled from C driver: https://github.com/BoschSensortec/BMP3-Sensor-API/blob/master/bmp3.c 128 partialData1 := rawTemp - (256 * int64(d.cali.t1)) 129 partialData2 := int64(d.cali.t2) * partialData1 130 partialData3 := (partialData1 * partialData1) 131 partialData4 := partialData3 * int64(d.cali.t3) 132 partialData5 := (partialData2 * 262144) + partialData4 133 return partialData5 / 4294967296, nil 134 135 } 136 137 // ReadTemperature returns the temperature in centicelsius, i.e 2426 / 100 = 24.26 C 138 func (d *Device) ReadTemperature() (int32, error) { 139 140 tlin, err := d.tlinCompensate() 141 if err != nil { 142 return 0, err 143 } 144 145 temp := (tlin * 25) / 16384 146 return int32(temp), nil 147 } 148 149 // ReadPressure returns the pressure in centipascals, i.e 10132520 / 100 = 101325.20 Pa 150 func (d *Device) ReadPressure() (int32, error) { 151 152 tlin, err := d.tlinCompensate() 153 if err != nil { 154 return 0, err 155 } 156 rawPress, err := d.readSensorData(RegPress) 157 if err != nil { 158 return 0, err 159 } 160 161 // code pulled from bmp388 C driver: https://github.com/BoschSensortec/BMP3-Sensor-API/blob/master/bmp3.c 162 partialData1 := tlin * tlin 163 partialData2 := partialData1 / 64 164 partialData3 := (partialData2 * tlin) / 256 165 partialData4 := (int64(d.cali.p8) * partialData3) / 32 166 partialData5 := (int64(d.cali.p7) * partialData1) * 16 167 partialData6 := (int64(d.cali.p6) * tlin) * 4194304 168 offset := (int64(d.cali.p5) * 140737488355328) + partialData4 + partialData5 + partialData6 169 partialData2 = (int64(d.cali.p4) * partialData3) / 32 170 partialData4 = (int64(d.cali.p3) * partialData1) * 4 171 partialData5 = (int64(d.cali.p2) - 16384) * tlin * 2097152 172 sensitivity := ((int64(d.cali.p1) - 16384) * 70368744177664) + partialData2 + partialData4 + partialData5 173 partialData1 = (sensitivity / 16777216) * rawPress 174 partialData2 = int64(d.cali.p10) * tlin 175 partialData3 = partialData2 + (65536 * int64(d.cali.p9)) 176 partialData4 = (partialData3 * rawPress) / 8192 177 178 // dividing by 10 followed by multiplying by 10 179 // To avoid overflow caused by (pressure * partial_data4) 180 partialData5 = (rawPress * (partialData4 / 10)) / 512 181 partialData5 = partialData5 * 10 182 partialData6 = (int64)(uint64(rawPress) * uint64(rawPress)) 183 partialData2 = (int64(d.cali.p11) * partialData6) / 65536 184 partialData3 = (partialData2 * rawPress) / 128 185 partialData4 = (offset / 4) + partialData1 + partialData5 + partialData3 186 compPress := ((uint64(partialData4) * 25) / uint64(1099511627776)) 187 return int32(compPress), nil 188 } 189 190 // SoftReset commands the BMP388 to reset of all user configuration settings 191 func (d *Device) SoftReset() error { 192 err := d.writeRegister(RegCmd, SoftReset) 193 if err != nil { 194 return errSoftReset 195 } 196 return nil 197 } 198 199 // Connected tries to reach the bmp388 and check its chip id register. Returns true if it was able to successfully 200 // communicate over i2c and returns the correct value 201 func (d *Device) Connected() bool { 202 data, err := d.readRegister(RegChipId, 1) 203 return err == nil && data[0] == ChipId // returns true if i2c comm was good and response equals 0x50 204 } 205 206 // SetMode changes the run mode of the sensor, NORMAL is the one to use for most cases. Use FORCED if you plan to take 207 // measurements infrequently and want to conserve power. SLEEP will of course put the sensor to sleep 208 func (d *Device) SetMode(mode Mode) error { 209 d.Config.Mode = mode 210 return d.writeRegister(RegPwrCtrl, PwrPress|PwrTemp|byte(d.Config.Mode)) 211 } 212 213 func (d *Device) readSensorData(register byte) (data int64, err error) { 214 215 if !d.Connected() { 216 return 0, errNotConnected 217 } 218 219 // put the sensor back into forced mode to get a reading, the sensor goes back to sleep after taking one read in 220 // forced mode 221 if d.Config.Mode != Normal { 222 err = d.SetMode(Forced) 223 if err != nil { 224 return 225 } 226 } 227 228 bytes, err := d.readRegister(register, 3) 229 if err != nil { 230 return 231 } 232 data = int64(bytes[2])<<16 | int64(bytes[1])<<8 | int64(bytes[0]) 233 return 234 } 235 236 // configurationError checks the register error for the configuration error bit. The bit is cleared on read by the bmp. 237 func (d *Device) configurationError() bool { 238 data, err := d.readRegister(RegErr, 1) 239 return err == nil && (data[0]&0x04) != 0 240 } 241 242 func (d *Device) readRegister(register byte, len int) (data []byte, err error) { 243 data = make([]byte, len) 244 err = legacy.ReadRegister(d.bus, d.Address, register, data) 245 return 246 } 247 248 func (d *Device) writeRegister(register byte, data byte) error { 249 return legacy.WriteRegister(d.bus, d.Address, register, []byte{data}) 250 }