package bmp280 import ( "time" "tinygo.org/x/drivers" "tinygo.org/x/drivers/internal/legacy" ) // OversamplingMode is the oversampling ratio of the temperature or pressure measurement. type Oversampling uint // Mode is the Power Mode. type Mode uint // Standby is the inactive period between the reads when the sensor is in normal power mode. type Standby uint // Filter unwanted changes in measurement caused by external (environmental) or internal changes (IC). type Filter uint // Device wraps an I2C connection to a BMP280 device. type Device struct { bus drivers.I2C Address uint16 buf [6]byte cali calibrationCoefficients Temperature Oversampling Pressure Oversampling Mode Mode Standby Standby Filter Filter } type calibrationCoefficients struct { // Temperature compensation t1 uint16 t2 int16 t3 int16 // Pressure compensation p1 uint16 p2 int16 p3 int16 p4 int16 p5 int16 p6 int16 p7 int16 p8 int16 p9 int16 } // New creates a new BMP280 connection. The I2C bus must already be // configured. // // This function only creates the Device object, it does not initialize the device. // You must call Configure() first in order to use the device itself. func New(bus drivers.I2C) Device { return Device{ bus: bus, Address: Address, } } // Connected returns whether a BMP280 has been found. // It does a "who am I" request and checks the response. func (d *Device) Connected() bool { data := make([]byte, 1) legacy.ReadRegister(d.bus, uint8(d.Address), REG_ID, data) return data[0] == CHIP_ID } // Reset preforms complete power-on-reset procedure. // It is required to call Configure afterwards. func (d *Device) Reset() { legacy.WriteRegister(d.bus, uint8(d.Address), REG_RESET, []byte{CMD_RESET}) } // Configure sets up the device for communication and // read the calibration coefficients. func (d *Device) Configure(standby Standby, filter Filter, temp Oversampling, pres Oversampling, mode Mode) { d.Standby = standby d.Filter = filter d.Temperature = temp d.Pressure = pres d.Mode = mode // Write the configuration (standby, filter, spi 3 wire) config := uint(d.Standby<<5) | uint(d.Filter<<2) | 0x00 legacy.WriteRegister(d.bus, uint8(d.Address), REG_CONFIG, []byte{byte(config)}) // Write the control (temperature oversampling, pressure oversampling, config = uint(d.Temperature<<5) | uint(d.Pressure<<2) | uint(d.Mode) legacy.WriteRegister(d.bus, uint8(d.Address), REG_CTRL_MEAS, []byte{byte(config)}) // Read Calibration data data := make([]byte, 24) err := legacy.ReadRegister(d.bus, uint8(d.Address), REG_CALI, data) if err != nil { return } // Datasheet: 3.11.2 Trimming parameter readout d.cali.t1 = readUintLE(data[0], data[1]) d.cali.t2 = readIntLE(data[2], data[3]) d.cali.t3 = readIntLE(data[4], data[5]) d.cali.p1 = readUintLE(data[6], data[7]) d.cali.p2 = readIntLE(data[8], data[9]) d.cali.p3 = readIntLE(data[10], data[11]) d.cali.p4 = readIntLE(data[12], data[13]) d.cali.p5 = readIntLE(data[14], data[15]) d.cali.p6 = readIntLE(data[16], data[17]) d.cali.p7 = readIntLE(data[18], data[19]) d.cali.p8 = readIntLE(data[20], data[21]) d.cali.p9 = readIntLE(data[22], data[23]) } // PrintCali prints the Calibration information. func (d *Device) PrintCali() { println("T1:", d.cali.t1) println("T2:", d.cali.t2) println("T3:", d.cali.t3) println("P1:", d.cali.p1) println("P2:", d.cali.p2) println("P3:", d.cali.p3) println("P4:", d.cali.p4) println("P5:", d.cali.p5) println("P6:", d.cali.p6) println("P7:", d.cali.p7) println("P8:", d.cali.p8) println("P9:", d.cali.p9, "\n") } // ReadTemperature returns the temperature in celsius milli degrees (°C/1000). func (d *Device) ReadTemperature() (temperature int32, err error) { data := d.buf[:3] if err = d.readData(REG_TEMP, data); err != nil { return } rawTemp := convert3Bytes(data[0], data[1], data[2]) // Datasheet: 8.2 Compensation formula in 32 bit fixed point // Temperature compensation var1 := ((rawTemp >> 3) - int32(d.cali.t1<<1)) * int32(d.cali.t2) >> 11 var2 := (((rawTemp >> 4) - int32(d.cali.t1)) * ((rawTemp >> 4) - int32(d.cali.t1)) >> 12) * int32(d.cali.t3) >> 14 tFine := var1 + var2 // Convert from degrees to milli degrees by multiplying by 10. // Will output 30250 milli degrees celsius for 30.25 degrees celsius temperature = 10 * ((tFine*5 + 128) >> 8) return } // ReadPressure returns the pressure in milli pascals (mPa). func (d *Device) ReadPressure() (pressure int32, err error) { // First 3 bytes are Pressure, last 3 bytes are Temperature data := d.buf[:6] if err = d.readData(REG_PRES, data); err != nil { return } rawTemp := convert3Bytes(data[3], data[4], data[5]) // Datasheet: 8.2 Compensation formula in 32 bit fixed point // Calculate tFine (temperature), used for the Pressure compensation var1 := ((rawTemp >> 3) - int32(d.cali.t1<<1)) * int32(d.cali.t2) >> 11 var2 := (((rawTemp >> 4) - int32(d.cali.t1)) * ((rawTemp >> 4) - int32(d.cali.t1)) >> 12) * int32(d.cali.t3) >> 14 tFine := var1 + var2 rawPres := convert3Bytes(data[0], data[1], data[2]) // Datasheet: 8.2 Compensation formula in 32 bit fixed point // Pressure compensation var1 = (tFine >> 1) - 64000 var2 = (((var1 >> 2) * (var1 >> 2)) >> 11) * int32(d.cali.p6) var2 = var2 + ((var1 * int32(d.cali.p5)) << 1) var2 = (var2 >> 2) + (int32(d.cali.p4) << 16) var1 = (((int32(d.cali.p3) * (((var1 >> 2) * (var1 >> 2)) >> 13)) >> 3) + ((int32(d.cali.p2) * var1) >> 1)) >> 18 var1 = ((32768 + var1) * int32(d.cali.p1)) >> 15 if var1 == 0 { return 0, nil } p := uint32(((1048576 - rawPres) - (var2 >> 12)) * 3125) if p < 0x80000000 { p = (p << 1) / uint32(var1) } else { p = (p / uint32(var1)) * 2 } var1 = (int32(d.cali.p9) * int32(((p>>3)*(p>>3))>>13)) >> 12 var2 = (int32(p>>2) * int32(d.cali.p8)) >> 13 return 1000 * (int32(p) + ((var1 + var2 + int32(d.cali.p7)) >> 4)), nil } // readData reads n number of bytes of the specified register func (d *Device) readData(register int, data []byte) error { // If not in normal mode, set the mode to FORCED mode, to prevent incorrect measurements // After the measurement in FORCED mode, the sensor will return to SLEEP mode if d.Mode != MODE_NORMAL { config := uint(d.Temperature<<5) | uint(d.Pressure<<2) | uint(MODE_FORCED) legacy.WriteRegister(d.bus, uint8(d.Address), REG_CTRL_MEAS, []byte{byte(config)}) } // Check STATUS register, wait if data is not available yet status := make([]byte, 1) for legacy.ReadRegister(d.bus, uint8(d.Address), uint8(REG_STATUS), status[0:]); status[0] != 4 && status[0] != 0; legacy.ReadRegister(d.bus, uint8(d.Address), uint8(REG_STATUS), status[0:]) { time.Sleep(time.Millisecond) } // Read the requested register return legacy.ReadRegister(d.bus, uint8(d.Address), uint8(register), data[:]) } // convert3Bytes converts three bytes to int32 func convert3Bytes(msb byte, b1 byte, lsb byte) int32 { return int32(((((uint32(msb) << 8) | uint32(b1)) << 8) | uint32(lsb)) >> 4) } // readUint converts two bytes to uint16 func readUint(msb byte, lsb byte) uint16 { return (uint16(msb) << 8) | uint16(lsb) } // readUintLE converts two little endian bytes to uint16 func readUintLE(msb byte, lsb byte) uint16 { temp := readUint(msb, lsb) return (temp >> 8) | (temp << 8) } // readIntLE converts two little endian bytes to int16 func readIntLE(msb byte, lsb byte) int16 { return int16(readUintLE(msb, lsb)) }