# Christian Fiot Dec 2020
# Digital IO PCA9536   4bits
# 4bit IO  - Pycom Socket Vb, switch power to grove J4 J8
#
# Usage:
# from PCA9536 import PCA9536
#
# DIO = PCA9536(0x0000, traceDebug=True)  # iodir
# DIO.begin()  # return True if chip is found
# DIO.readPort()  # return the state of the 4 io
# DIO.writePort(data)  # write the 4 io
#
# P0(Power shield)=1
# P1(PM)=1   dust sensor
# P2=0/1 1/0 Alive
# P3=1 Enable DogRelay


import utime # 2.2.0b
import ustruct  # 2.2.0b
from trap import * # 2.2.9b
from machine import I2C # 2.2.18


class PCA9536():  # 2.09

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

        self.iodir = dir # default = xFFFF all 4 io are input
        self.data = bytearray(1)
        self.out = bytearray(1) # 2.2.18

        # I2C bus declaration is extenal
        # PCA is connected on I2C bus #1   SDA-P22   SCL-P21
        #
        #
        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))

    # Detect and init IO
    def begin(self):
        try:
            # Set config reg - IO Dir   bn=1 input    bn=0 output
            self.data[0] = ( self.iodir & 0x0FF)
            self.i2c1.writeto_mem(self.adr, 0x03, self.data)  # Write to $03
            self.writePort(0x0F) # 2.2.21  0x03) # 2.2.18   Set P0(Power shield)=1 &  P1(PM)=1
            print('Detected DIO PCA9536 Adr:' + str(self.adr) + ' - WS power ON')
            return True

        except Exception as e: # 2.2.9b
            scratch =  'PCA9536 Adr:' + str(self.adr) + ' not found - '  # 2.2.9b    print('PCA9536 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())
            return False


    def readPort(self):
        self.i2c1.readfrom_mem_into(self.adr, 0x00, self.data) # read Inputs
        if self.trace:
            print('GPIO read: 0x{0:04X}'.format(self.data) )
        return self.data


    def writePort(self, gpio):
        self.data[0] = ( gpio & 0x0FF)  # PortA
        self.out[0] = ( gpio & 0x0FF) # 2.2.18
        self.i2c1.writeto_mem(self.adr, 0x01, self.data)  # Write to output pins


    # 2.2.18  The following are to control DogRelay
    def setLive(self): # 2.2.18  set Live pin and, reset disable pin
        self.data[0] = self.out[0] | 0x04   # 2.2.21 b2 0100           b3,b2  1100
        self.out[0] = self.data[0]
        self.i2c1.writeto_mem(self.adr, 0x01, self.data)

    def clrLive(self): # 2.2.18
        self.data[0] = self.out[0] & 0x0B   # b2 1011
        self.out[0] = self.data[0]
        self.i2c1.writeto_mem(self.adr, 0x01, self.data)

    def setDogRate(self, rate): # 2.2.21  0= 14sec,  1= 0.5sec
        if rate == 0: # 14sec
            self.data[0] = self.out[0] & 0x07   # b3 0111
        else: # 500ms
            self.data[0] = self.out[0] | 0x08   # b3  1000
        self.out[0] = self.data[0]
        self.i2c1.writeto_mem(self.adr, 0x01, self.data)

    # 2.2.21
    def sleepPM(self):
        self.data[0] = self.out[0] & 0x0D   # b1 1101
        self.out[0] = self.data[0]
        self.i2c1.writeto_mem(self.adr, 0x01, self.data)

    def wakeupPM(self):
        self.data[0] = self.out[0] | 0x02   # b1  0010
        self.out[0] = self.data[0]
        self.i2c1.writeto_mem(self.adr, 0x01, self.data)

    def shieldON(self):
        self.data[0] = self.out[0] | 0x01   # b1  0001
        self.out[0] = self.data[0]
        self.i2c1.writeto_mem(self.adr, 0x01, self.data)

    def shieldOFF(self):
        self.data[0] = self.out[0] & 0x0E   # b1 1110
        self.out[0] = self.data[0]
        self.i2c1.writeto_mem(self.adr, 0x01, self.data)











class PCF8574():  # 2.2.0b   TI adr = 0x038   instead of  Philipps 0x020

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

        self.iodir = dir # default = xFFFF all 4 io are input
        self.data = bytearray(1)
        self.reference = reference # 2.2.3b

        # PCF is connected on I2C bus #1   SDA-P22   SCL-P21
        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))

    # Detect and init IO
    def begin(self):
        try:
            self.i2c1.writeto(self.adr, self.iodir) # 0xFF)  # Set all pin to Inputs
            print('Detected PCF8574 ' + self.reference + ' - IO  Adr:' + str(self.adr))
            return True

        except Exception as e: # 2.2.9b
            scratch = 'PCF8574 ' + self.reference +  ' - IO  Adr:' + str(self.adr) + ' not found - '  # 2.2.9b  print('PCF8574 ' + self.reference +  ' - IO  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())
            return False

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

    def writePort(self, gpio): # 2.3.1
        self.data[0] = ( gpio) # | self.iodir)
        self.i2c1.writeto(self.adr, self.data)





# PI4MSD5V9540BUEX   I2C  switch
class PI4M():  # 2.2.0b

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

        self.data = bytearray(1)

        # I2C bus #1   SDA-P22   SCL-P21
        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))

    # Detect and init IO
    def begin(self):
        try:
            data = self.i2c1.readfrom(self.adr, 1)  #  self.i2c1.readfrom_into(self.adr, self.data)
            U1 = ustruct.unpack('@B', data)[0]
            if self.trace:
                print('PI4M read: 0x{0:04X}'.format(U1) )
            print('Detected wsCommSw PI4M Adr:' + str(self.adr) )
            return True

        except Exception as e: # 2.2.9b
            scratch = 'PI4M Adr:' + str(self.adr) + ' not found - '  # 2.2.9b  print('PI4M 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())
            return False

    def channel(self, gpio):
        self.data[0] = ( gpio & 0x07)
        self.i2c1.writeto_mem(self.adr, 0x01, self.data)
        if self.trace:
            # 2.2.18 print('Begin I2C WeatherShield scan ...')
            # 2.2.18 print(self.i2c1.scan())
            pass
        else:
            utime.sleep_ms(250)
