# bme280.py – MicroPython driver for BME280 sensor
import time
from ustruct import unpack, unpack_from
from machine import I2C

class BME280:
    def __init__(self, i2c, addr=0x76):
        self.i2c = i2c
        self.addr = addr
        self.dig = {}
        self._read_coefficients()
        self._write_register(0xF4, 0x27)  # Normal mode, oversampling 1x
        self._write_register(0xF5, 0xA0)  # Standby 1000ms, filter off

    def _read_coefficients(self):
        # Read calibration registers
        data = self._read_register(0x88, 24)
        self.dig["T1"] = unpack("<H", data[0:2])[0]
        self.dig["T2"] = unpack("<h", data[2:4])[0]
        self.dig["T3"] = unpack("<h", data[4:6])[0]
        self.dig["P1"] = unpack("<H", data[6:8])[0]
        self.dig["P2"] = unpack("<h", data[8:10])[0]
        self.dig["P3"] = unpack("<h", data[10:12])[0]
        self.dig["P4"] = unpack("<h", data[12:14])[0]
        self.dig["P5"] = unpack("<h", data[14:16])[0]
        self.dig["P6"] = unpack("<h", data[16:18])[0]
        self.dig["P7"] = unpack("<h", data[18:20])[0]
        self.dig["P8"] = unpack("<h", data[20:22])[0]
        self.dig["P9"] = unpack("<h", data[22:24])[0]

    def _read_register(self, reg, length=1):
        return self.i2c.readfrom_mem(self.addr, reg, length)

    def _write_register(self, reg, value):
        self.i2c.writeto_mem(self.addr, reg, bytes([value]))

    def compensate_temperature(self, adc_T):
        var1 = (adc_T / 16384.0 - self.dig["T1"] / 1024.0) * self.dig["T2"]
        var2 = (adc_T / 131072.0 - self.dig["T1"] / 8192.0) * (adc_T / 131072.0 - self.dig["T1"] / 8192.0) * self.dig["T3"]
        t_fine = var1 + var2
        T = t_fine / 5120.0
        return T, t_fine

    def compensate_pressure(self, adc_P, t_fine):
        var1 = t_fine / 2.0 - 64000.0
        var2 = var1 * var1 * self.dig["P6"] / 32768.0
        var2 = var2 + var1 * self.dig["P5"] * 2.0
        var2 = var2 / 4.0 + self.dig["P4"] * 65536.0
        var1 = (self.dig["P3"] * var1 * var1 / 524288.0 + self.dig["P2"] * var1) / 524288.0
        var1 = (1.0 + var1 / 32768.0) * self.dig["P1"]
        if var1 == 0:
            return 0
        p = 1048576.0 - adc_P
        p = (p - var2 / 4096.0) * 6250.0 / var1
        var1 = self.dig["P9"] * p * p / 2147483648.0
        var2 = p * self.dig["P8"] / 32768.0
        p = p + (var1 + var2 + self.dig["P7"]) / 16.0
        return p / 100.0  # pressure in hPa

    def read(self):
        # Read raw data
        data = self._read_register(0xF7, 8)
        adc_P = (data[0] << 12) | (data[1] << 4) | (data[2] >> 4)
        adc_T = (data[3] << 12) | (data[4] << 4) | (data[5] >> 4)

        T, t_fine = self.compensate_temperature(adc_T)
        P = self.compensate_pressure(adc_P, t_fine)
        return T, P   # temperature in °C, pressure in hPa