# Christian Fiot 2020
# ADC 18bit  with PGA    4 channels   VIn max 2V
# power requirement 135uA
#
#
#
# Usage:
# from MCP342x import MCP3424
#
# adc6814 = MCP3424(0x69, traceDebug=True)
# adc6814.read_18(channel=0)
# adc6814.read_16(channel=0)

# 2.3.2
import ustruct
import time
from trap import *
from machine import I2C

class MCP3424:
    def __init__(self, iicadr=0x68, i2c=None, sda='P22', scl='P21', traceDebug=False, sdAccess=False):
        self.adr = iicadr
        self.gain = 0   # x0=1, x1=2, x2=4, x3=8
        self.smpersec = 3  # x0=240, x1=60, x2=15, x3=3.75  SamplePerSec
        self.channel = 0  # x0=1, x1=2, x2=3, x3=4 Channel
        self.trace = traceDebug
        self.sdAccess = sdAccess

        # 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:
            self.i2c1 = i2c
        else:
            # from machine import I2C
            self.i2c1 = I2C(0, mode=I2C.MASTER, pins=(sda, scl))

    def begin(self):
        try:
            self.read_config()
            print('Detected MCP3424 - ADC  Adr:' + str(self.adr) )
            return True

        except Exception as e:
            scratch = 'MCP3424 - ADC (Adr:' + str(self.adr) + ') not found - '
            file_errLog(0, scratch + str(e), self.sdAccess)
            return False

    def read_config(self):
        data = self.i2c1.readfrom(self.adr, 1)
        U1 = ustruct.unpack('@B', data)[0]
        if self.trace:
            print('config read: 0x{0:04X}'.format(U1))
        return U1

    def read_16(self, channel=0, gain=0):  # with 16bit resolution it takes 66ms to acquire a value
        self.gain = gain # x0=1, x1=2, x2=4, x3=8
        self.channel = channel  # x0=1, x1=2, x2=3, x3=4 Channel
        self.smpersec = 2   # x0=240, x1=60, x2=15, x3=3.75  SamplePerSec
        b4 = 0  # conversion mode
        b7 = 1  # start conversion
        register = (b7 << 7) | ((self.channel & 3) << 5) | (b4 << 4) | ((self.smpersec & 3) << 2) | (self.gain & 3)
        data = ustruct.pack('@B', register)
        self.i2c1.writeto(self.adr, data)
        if self.trace:
            print("Register: {}".format(data))

        time.sleep_ms(70)
        data = self.i2c1.readfrom(self.adr, 2)
        adc = (data[0] << 8) + data[1]
        if self.trace:
            print("ADC: H{} L{} - {}".format(data[1],data[0], adc))
        return adc  # 16bit  +32767 \ -32768

    def read_18(self, channel=0, gain=0):  # with 18bit resolution it takes 260ms to acquire a value
        self.gain = gain  # x0=1, x1=2, x2=4, x3=8
        self.channel = channel  # x0=1, x1=2, x2=3, x3=4 Channel
        self.smpersec = 3   # x0=240, x1=60, x2=15, x3=3.75  SamplePerSec
        b4 = 0  # conversion mode
        b7 = 1  # start conversion
        register = (b7 << 7) | ((self.channel & 3) << 5) | (b4 << 4) | ((self.smpersec & 3) << 2) | (self.gain & 3)
        data = ustruct.pack('@B', register)
        self.i2c1.writeto(self.adr, data)
        if self.trace:
            print("Register: {}".format(data))

        time.sleep_ms(300)
        data = self.i2c1.readfrom(self.adr, 3)
        adc = ((data[0] & 3) << 16) + (data[1] << 8) + data[2]
        if self.trace:
            print("ADC: {} {} {} - {}".format(data[2], data[1], data[0], adc))
        return adc # 18bit +131071 \ -131072


    def get_voltage(self, adc):
        adcspan = 2047
        if self.smpersec == 1: adcspan = 8191
        if self.smpersec == 2: adcspan = 32767
        if self.smpersec == 3: adcspan = 131071
        resolution  = 2.048 / adcspan  # VRef = 2.048V
        gain = 1
        if self.gain == 1:  gain = 2 # x0=1, x1=2, x2=4, x3=8
        if self.gain == 2:  gain = 4
        if self.gain == 3:  gain = 8
        vsense = (adc * resolution) / gain
        if self.trace:
            print("Volt: {}".format( vsense))
        return vsense


    # FOR 6814 ONLY ....

    # VSense = 3.3V * RLoad / ( RLoad + RSense)      Voltage divider
    # RSense = RLoad * VSense / ( 3.3V - VSense)     Resolve RSense
    # RSense = R0    @ no gas         Save the R0  unto VRAM
    # RSense = RS    @ with gas
    def get_rsense(self, rload, adc):
        vsense = self.get_voltage(adc)
        rsense = rload * vsense / ( 3.3 - vsense)
        return rsense

    # RS will be lower than R0  ...   as RS decerase RED increase
    def read_RED(self): # return value of R for Reduction gas
        adc = self.read_16(0,0)   # for speed        read_18(0,0)
        REDsense = self.get_rsense( 1000000, adc)  # 1000k   0.3V to 1.98V
        return REDsense

    # RS will be lower than R0  ...   as RS decerase RED increase
    def read_OX(self): # return value of R for Oxidation gas
        adc =  self.read_16(1,0)   # read_18(1,0)
        OXsense = self.get_rsense( 15000, adc)  # 15k   0.167V to 1.88V
        return OXsense

    def read_NH3(self): # return value of R for Ammonia gas
        adc = self.read_16(2,0)  # read_18(2,0)
        NH3sense = self.get_rsense( 1000000, adc)  # 1000k     0.032V to 1.98V
        return NH3sense
