# Christian Fiot 2020
# Usage:
# from BMP280 import BMP280
#
# Bar = BMP280(0x77, traceDebug=False)
# 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/bmp280-python/blob/master/library/bmp280/__init__.py
# Credit  https://github.com/ButchAnton/LoPy_BME280/blob/master/bme280.py


# REG_CHIPID = 0xD0   #   = 0x058
# 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 BMP280():

    def __init__(self, iicadr=0x077, 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

        # Section 3.12  These coefficient are used to test  compensate  _temperature()  _pressure()
        self.CoeffData = 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


        # 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
        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

    # 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()

            # 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]))

            # 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]))
            print('Detected BMP280 - Temp/Pressure Sensor Adr:' + str(self.adr) )

        except Exception as e: # 2.2.9b
            scratch = 'BMP280 - Temp/Pressure Sensor (Adr:' + str(self.adr) + ') not found - '   # 2.2.9b print('BMP280 - Temp/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 == 0x058


    # 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)


    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(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

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

        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)))

        # return uTuPData
        # return temp, pres
        return self.temperature, self.pressure # Temp in C, Pressure in Pa


""" For debug
def readU16(self, register, little_endian=True):
    data = bytearray(2)
    data = self.i2c1.readfrom_mem(self.adr, register, 2)
    result = int.from_bytes(data, "little")
    return result

def readS16(self, register, little_endian=True):
    data = bytearray(2)
    data = self.i2c1.readfrom_mem(self.adr, register, 2)
    result = int.from_bytes(data, "little")
    if result > 32767:
        result -= 65536
    print('SAdr {:2X}: {}'.format (register, result) )
    return result

def test(self):
    self.dig_T1 = self.readU16(0x88)
    self.dig_T2 = self.readS16(0x8A)

def test(self):
    did = self.i2c1.readfrom_mem(self.adr, 0xD0,1)
    print("Read device ID:" + str(did[0]))
"""
