# Christian Fiot 2020
# Digital IO MCP23017

# Usage:
# from MCP23017 import MCP23017
#
# DIO = MCP23017(0x20, 0x0000, traceDebug=True)  # adr, iodir
# DIO.begin()  # return True if chip is found
# DIO.readPort()  # return the state of the 16 io
# DIO.writePort(data)  # write the 16 io,  PortA low byte,  PortB High side
#



class MCP23017():

    def __init__(self, iicadr=0x020, dir=0xFFFF, sda = 'P22', scl = 'P21', traceDebug=False):
        self.trace = traceDebug
        self.adr = iicadr
        self.iodir = dir # default = xFFFF all 16 io are input
        self.data = bytearray(2)

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

    # Detect and init IO
    def begin(self):
        try:
            # 3.5.6 and 3.6 IOcon register
            # Bank b7=1  PORTA $00-0A  PORTB $10-1A
            # Bank b7=0   IODIRA mapped 00h, IODIRB is mapped to next address
            # Mirror INT b6=0 ,  SEQOP enabled b5=0
            # SLW b4=0 , b3=0 not used, ODR b2=1, INTPOL b1=0, b0=0
            # 0000 0100   =   0x04
            self.i2c1.writeto_mem(self.adr, 0x05, bytes([0b00000100]) )

            # IODir   bn=1 input    bn=0 output   write dir PortA=LB  and portB=HB
            self.data[0] = ( self.iodir & 0x0FF)  # PortA
            self.data[1] = ( self.iodir & 0x0FF00) >> 8 # PortB
            self.i2c1.writeto_mem(self.adr, 0x00, self.data)  # Write to $00 and $01
            return True

        except:
            print('MCP23017 Adr:' + str(self.adr) + ' not found')
            print('Begin I2C1 scan ...')
            print(self.i2c1.scan())
            return False


    def getWord(self, high, low):
        return ((high & 0xFF) << 8) + (low & 0xFF)


    def readPort(self):
        self.i2c1.readfrom_mem_into(self.adr, 0x012, self.data) # read to $12 and $13
        gpio = self.getWord(self.data[1], self.data[0])
        if self.trace :
            print('GPIO read: 0x{0:04X}'.format(gpio) )
        return gpio

    def writePort(self, gpio):
        self.data[0] = ( gpio & 0x0FF)  # PortA
        self.data[1] = ( gpio & 0x0FF00) >> 8 # PortB
        self.i2c1.writeto_mem(self.adr, 0x012, self.data)  # Write to $12 and $13
