# Christian Fiot Nov 2020
# temperature, humidity, pressure sensors
# power requirement 3.6uA max
#
# from BME280   from  BMP280
# same functionality with added humidity sensor
# 2.00
#
# Usage:
# from BME280 import BME280
#
# Bar = BME280(0x76, traceDebug=True)
# Bar.begin() # return True if chip found
# Bar.read()  # return T in C   and P in Pa
# Bar.Convert_C2F(n)
# Bar.Convert_Pa2PSI(n)
#
# Credit  https://github.com/pimoroni/bme280-python/blob/master/library/bme280/__init__.py
# Credit  https://github.com/ButchAnton/LoPy_BME280/blob/master/bme280.py


# REG_CHIPID = 0xD0   #   = 0x060
# REG_SOFTRESET = 0xE0
# REG_STATUS = 0xF3
# REG_CONTROL = 0xF4
# REG_CONFIG = 0xF5
# REG_PRESSURE_DATA = 0xF7  # F7[2]  F8[1]   F9[0]
# REG_TEMP_DATA = 0xFA  # FA[2]   FB[1]  FC[0]

from trap import * # 2.2.9b
from machine import I2C # 2.2.18


class BME280():

    def __init__(self, iicadr=0x076, i2c=None, sda='P22', scl='P21', traceDebug=False, sdAccess=False): # 2.2.9b
        self.trace = traceDebug
        self.adr = iicadr
        self.sdAccess = sdAccess # 2.2.9b

        # 2.2.21
        self.temperature = 0
        self.temperature_fine = 0
        self.tempDAC = 0  # DAC equivalent
        self.pressure = 0
        self.presDAC = 0
        self.humidity = 0
        self.humDAC = 0

        # Section 3.12  These coefficient are used to test  compensate  _temperature()  _pressure()  pg #23 BMP280
        self.CoeffData = bytearray(26)   # 2.00  bytearray(12*2)
        self.dig_t1 = 27504  # U 0x88 0x89
        self.dig_t2 = 26435  # S 0x8A 0x8B
        self.dig_t3 = -1000  # S 0x8C 0x8D
        self.dig_p1 = 36477  # U 0x8E 0x8F
        self.dig_p2 = -10685  # S 0x90 0x91
        self.dig_p3 = 3024  # S 0x92 0x93
        self.dig_p4 = 2855  # S 0x94 0x95
        self.dig_p5 = 140  # S 0x96 0x97
        self.dig_p6 = -7  # S 0x98 0x99
        self.dig_p7 = 15500  # S 0x9A 0x9B
        self.dig_p8 = -14600  # S 0x9C 0x9D
        self.dig_p9 = 6000  # S 0x9E 0x9F
        self.dig_h1 = 75 # 2.00   U 0x0A1

        # 2.00
        self.CoeffHData = bytearray(7)
        self.dig_h2 = 376 # S 0x0E1 0x0E2
        self.dig_h3 = 0 # U 0x0E3
        self.dig_h4 = 286 # S 0x0E4 0x0E5
        self.dig_h5 = 50 # 0x0E5 0x0E6
        self.dig_h6 = 30 # 0x0E7

        # I2C bus declaration is extenal
        # MCP are connected on I2C bus #1   SDA-P22   SCL-P21
        #                      I2C bus #2   SDA-P18   SCL-P17
        #
        if i2c is not None: # 2.2.18
            self.i2c1 = i2c
        else:
            # from machine import I2C
            self.i2c1 = I2C(0, mode=I2C.MASTER, pins=(sda, scl))


    # fetch the calibration coefficients from CoeffData buffer
    def getCoeffU16(self, pointer):
        result = (self.CoeffData[pointer+1]  << 8) | self.CoeffData[pointer]
        if self.trace :
            print('Upointer {:2}: {}'.format (pointer, result) )
        return result

    def getCoeffS16(self, pointer):
        result = (self.CoeffData[pointer+1] << 8) | self.CoeffData[pointer]
        if result > 32767:
            result -= 65536
        if self.trace :
            print('Spointer {:2}: {}'.format (pointer, result) )
        return result

    # Section 3.12  These coefficient are used to test  compensate  _temperature()  _pressure()
    # read calibration data, used to format temperature and pressure
    # carreful to the bytes order  see page#23 of the datasheet
    def getCalibCoeff(self):
        self.i2c1.readfrom_mem_into(self.adr, 0x88, self.CoeffData)
        # return  self.CoeffData
        if self.trace:
            print("CoeffData: $") # + str(self.CoeffData) )
            for i in self.CoeffData:
                print(" %X," % i, end = "")
            print("")
        self.dig_t1 = self.getCoeffU16(0)  # U 0x88 0x89
        self.dig_t2 = self.getCoeffS16(2)  # S 0x8A 0x8B
        self.dig_t3 = self.getCoeffS16(4)  # S 0x8C 0x8D
        self.dig_p1 = self.getCoeffU16(6)  # U 0x8E 0x8F
        self.dig_p2 = self.getCoeffS16(8)  # S 0x90 0x91
        self.dig_p3 = self.getCoeffS16(10)   # S 0x92 0x93
        self.dig_p4 = self.getCoeffS16(12)   # S 0x94 0x95
        self.dig_p5 = self.getCoeffS16(14)   # S 0x96 0x97
        self.dig_p6 = self.getCoeffS16(16)   # S 0x98 0x99
        self.dig_p7 = self.getCoeffS16(18)   # S 0x9A 0x9B
        self.dig_p8 = self.getCoeffS16(20)   # S 0x9C 0x9D
        self.dig_p9 = self.getCoeffS16(22)   # S 0x9E 0x9F
        self.dig_h1 = self.getCoeffU16(24) >> 8  # 2.00 keep $A1 only

        # 2.00
        self.i2c1.readfrom_mem_into(self.adr, 0xE1, self.CoeffHData)
        if self.trace:
            print("CoeffHData: $") # + str(self.CoeffHData) )
            for i in self.CoeffHData:
                print(" %X," % i, end = "")
            print("")
        self.dig_h2 = self.CoeffHData[0] | ( self.CoeffHData[1] << 8)
        self.dig_h3 = self.CoeffHData[2]
        self.dig_h4 = (self.CoeffHData[4] & 0x0F) | (self.CoeffHData[3] << 4)
        self.dig_h5 = ((self.CoeffHData[4] & 0x0F0) >> 4) | (self.CoeffHData[5] << 4 )
        self.dig_h6 = self.CoeffHData[6]
        if self.trace:
            print("dig_h2: $%X %d" % (self.dig_h2, self.dig_h2))
            print("dig_h3: $%X %d" % (self.dig_h3, self.dig_h3))
            print("dig_h4: $%X %d" % (self.dig_h4, self.dig_h4))
            print("dig_h5: $%X %d" % (self.dig_h5, self.dig_h5))
            print("dig_h6: $%X %d" % (self.dig_h6, self.dig_h6))


    # Detect and init sensor
    def begin(self):
        int = 0 # 2.2.9b
        try:
            did = self.i2c1.readfrom_mem(self.adr, 0xD0,1)
            int = did[0]

            if self.trace :
                print("Read device ID:" + str(did[0]))
                print("Fetch calibration coefficients:")

            # read calibration coefficient to correct raw data
            self.getCalibCoeff()

            # 2.00 Write to humidity ctrl
            # ovsmp = 2   osrs_h[2:0]= 010
            self.i2c1.writeto_mem(self.adr, 0xF2, bytes([0b010]))

            # 4.3.4  Write to REG_CONTROL
            # pressure ovsmp = 2  reso 1.31 Pa     osrs_p[7:5]= 010
            # temperature ovsmp = 2  reso 0.0025 C   osrs_t[4:2]= 010
            # Power mode normal b[1:0]= 11
            self.i2c1.writeto_mem(self.adr, 0xF4, bytes([0b01001011]))

            # configurate the sensor
            # 4.3.5 Write to REG_CONFIG
            # Stanby 250ms  t_sb[7:5]= 011
            # IIR filter 4   filter[4:2]= 010
            # interface I2C  [1:0]= 00
            self.i2c1.writeto_mem(self.adr, 0xF5, bytes([0b01101000]))
            print('Detected BME280 - Temp/Humidity/Pressure Sensor Adr:' + str(self.adr) )

        except Exception as e: # 2.2.9b
            scratch =  'BME280 - Temp/Humidity/Pressure Sensor (Adr:' + str(self.adr) + ') not found - '  # 2.2.9b  print('BME280 - Temp/Humidity/Pressure Sensor (Adr:' + str(self.adr) + ') not found.')
            file_errLog(0, scratch + str(e), self.sdAccess)  # 2.2.9b
            # 2.2.18 print('Begin I2C1 scan ...')
            # 2.2.18 print(self.i2c1.scan())
            int = 0

        finally:
            return int == 0x060



    # Section 3.12   checked, 519888 => 25.08248 C
    def compensate_temperature(self, raw_temperature):
        var1 = (raw_temperature / 16384.0 - self.dig_t1 / 1024.0) * self.dig_t2
        var2 = raw_temperature / 131072.0 - self.dig_t1 / 8192.0
        var2 = var2 * var2 * self.dig_t3
        self.temperature_fine = (var1 + var2)
        self.temperature = self.temperature_fine / 5120.0
        # if (self.temperature != 0):
        #    self.temperature = round(self.temperature, 2)


    # Section 3.12  checked, 415148 => 100653.3 Pa
    def compensate_pressure(self, raw_pressure):
        var1 = self.temperature_fine / 2.0 - 64000.0
        var2 = var1 * var1 * self.dig_p6 / 32768.0
        var2 = var2 + var1 * self.dig_p5 * 2
        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
        pressure = 1048576.0 - raw_pressure
        pressure = (pressure - var2 / 4096.0) * 6250.0 / var1
        var1 = self.dig_p9 * pressure * pressure / 2147483648.0
        var2 = pressure * self.dig_p8 / 32768.0
        self.pressure = pressure + (var1 + var2 + self.dig_p7) / 16.0
        # self.pressure = round(self.pressure + 0.001, 2)

    # 2.00
    # check 30281 => 68.66996648709039
    def compensate_humidity(self, raw_humidity):
        var1 = self.temperature_fine - 76800.0
        var2 = self.dig_h4 * 64.0 + (self.dig_h5 / 16384.0) * var1
        var3 = raw_humidity - var2
        var4 = self.dig_h2 / 65536.0
        var5 = 1.0 + (self.dig_h3 / 67108864.0) * var1
        var6 = 1.0 + (self.dig_h6 / 67108864.0) * var1 * var5
        var6 = var3 * var4 * (var5 * var6)
        self.humidity = var6 * (1.0 - self.dig_h1 * var6 / 524288.0)


    def Convert_C2F(self, DataC):
        return (DataC * 9/5) + 32

    def Convert_Pa2PSI(self, DataPa):
        return DataPa / 6895

    def Convert_Pa2ATM(self, DataPa):  # 1.15
        return DataPa / 101325

    # see datasheet section 3.9 Data readout
    def read(self):
        uTuPData = bytearray(6+2) # 2.00  3*2)
        self.i2c1.readfrom_mem_into(self.adr, 0xF7, uTuPData)
        pres = ( (uTuPData[0] << 16) | (uTuPData[1]  << 8) | uTuPData[2] ) >> 4
        temp = ( (uTuPData[3] << 16) | (uTuPData[4]  << 8) | uTuPData[5] ) >> 4
        hum = (uTuPData[6] << 8) | (uTuPData[7]) # 2.00

        # 2.2.21  to detect sensor reset ...
        self.tempDAC = temp
        self.presDAC = pres
        self.humDAC = hum

        # Must compute temperature first then pressure
        self.compensate_temperature(temp)
        self.compensate_pressure(pres)
        self.compensate_humidity(hum) # 2.00

        if self.trace :
            print("Temp C: " + str(self.temperature) + " F: " + str(self.Convert_C2F(self.temperature)))
            print("Pres Pa: " + str(self.pressure) + " PSI: " + str(self.Convert_Pa2PSI(self.pressure)))
            print("Hum  RH: " + str(self.humidity) ) # 2.00

        # return uTuPData
        # return temp, pres
        return self.temperature, self.pressure, self.humidity  # Temp in C, Pressure in Pa, Humidity %RH
