#::::::::::textpak=>adcRead_pkg.py
# function Package adcRead_pkg.py
# contains
#   various functions needed by adcRead()
#   adcRead() itself
#   adcReadInit() which must be run before adcRead
#   see test_adcRead.py for main program setup

import cmd, time, logging, smbus, RPi.GPIO as GPIO

#Source: https://smartypies.com/projects/ads1115-with-raspberrypi-and-python/ads1115runner/  #DC
#ADS1115 Datasheet: https://www.ti.com/lit/ds/symlink/ads1115.pdf?ts=1647366695082&ref_url=https%253A%252F%252Fwww.digchip.com%252F

#   Usage once:
#   define adcRead constants               # see the main program test_adcRead.py
#   BUS = adcReadInit(I2C_BUS,icNumber)    # create BUS class object
#   BUS.open(I2C_BUS)
#   . . . then repeatedly
#   value = adcRead(nChannel)              # read nChannel of the ADS1115 once
#   # no other functions need to be called
#   # the only externally callable functions are 
#         adcReadInit() 
#         adcRead()
#   # stop this program using ctrl+c       # how to stop the test    

def MUX(n):
    if n==0: m="00"
    if n==1: m="01"
    if n==2: m="10"
    if n==3: m="11"
    return m
#def end

def swap2Bytes(c):
    '''Revert Byte order for Words (2 Bytes, 16 Bit).'''
    return (c>>8 |c<<8)&0xFFFF
#def end
 
def prepareLEconf(BEconf):
    '''Prepare LittleEndian Byte pattern from BigEndian configuration string, with separators.'''
    c = int(BEconf.replace('-',''), base=2)
    return swap2Bytes(c)
#def end
 
def LEtoBE(c):
    '''Little Endian to BigEndian conversion for signed 2Byte integers (2 complement).'''
    c = swap2Bytes(c)
    if(c >= 2**15):
        c= c-2**16
    return c
#def end
 
def BEtoLE(c):
    '''BigEndian to LittleEndian conversion for signed 2 Byte integers (2 complement).'''
    if(c < 0):
        c= 2**16 + c
    return swap2Bytes(c)
#def end
 
def resetChip(RESET_ADDRESS, RESET_COMMAND):
    #BUS.write_byte(RESET_ADDRESS, RESET_COMMAND)
    return
#def end

class ADS1115Runner(cmd.Cmd):
    # this is the entry-point for  Runner.cmdloop() 
    # NB cmd.Cmd has been deprecated
    # cmd.Cmd will cause the self.cmdLoop() to return to the object's main method.
    # the ''' permits the definition of a string variable containing LFs
    #intro = '''usage: type following commands
    #     1    - one-shot measurement mode, timed
    #     2    - one-shot measurement mode, alerted through GPIO
    #     3    - continuous measurment mode, alerted through GPIO
    #     4  low high  - continuous mode, alerted when value out of range [low, high]
    #     q (quit)
    #     just hitting enter quits any mode 1-4. Enter 'y' to continue in modes 1 and 2.'''
    #prompt = 'Enter 1,2,3,4 or q >>'
    # if you enter 1 it will run do_1(), 2 . . . do_2() etc
    file = None
 
    __logfile = None
 
    def alerted(self, arg):  
        # deprecated      
        data_raw =  BUS.read_word_data(DEVICE_ADDRESS, POINTER_CONVERSION)
        print('alerted:' + str(LEtoBE(data_raw)))
        return
    #def end  alerted()
   
    def do_q(self, arg):
        exit
    #def end do_q()

    def mydo_1(self,BUS,AINn):
        RESET_ADDRESS = 0b0000000
        RESET_COMMAND = 0b00000110
        POINTER_CONVERSION = 0x0
        POINTER_CONFIGURATION  = 0x1
        POINTER_LOW_THRESHOLD  = 0x2
        POINTER_HIGH_THRESHOLD = 0x3
        DEVICE_ADDRESS = 0x48        # device address selected on the IC chip
        # replaces do_1 routine
        #print("into do_1 arg:",arg)
        #print("into do_1")
        '''One-shot, Read value from channel AINn with wait time'''
        #resetChip(RESET_ADDRESS, RESET_COMMAND)      #deprecated
        BUS.write_byte(RESET_ADDRESS, RESET_COMMAND)  #replace
       
        # compare with configuration settings from ADS1115 datasheet           #DC
        # start single conversion - AIN2/GND - 4.096V - single shot - 8SPS - X
        # - X - X - disable comparator
        # conf = prepareLEconf('1-110-001-1-000-0-0-0-11')                     #DC
        # AINn = 2 arg1 is AINn the nChannel to read
        ADSn = MUX(AINn)         #e.g. AINn=2 read AIN2  ( AIN2 to GND*)       #DC
        conf = prepareLEconf('1-1'+ADSn+'-001-1-000-0-0-0-11')                 #DC
        go = 'y'
        #while True and go in ['y','Y']:
        if True :           #only do this once
            BUS.write_word_data(DEVICE_ADDRESS, POINTER_CONFIGURATION, conf)
            # long enough to be safe that data acquisition (conversion) has completed
            # may be calculated from data rate + some extra time for safety.
            # check accuracy in any case.
            time.sleep(0.2)
            value_raw = BUS.read_word_data(DEVICE_ADDRESS, POINTER_CONVERSION)
            value = LEtoBE(value_raw)

            #print("mid do_1")

            global g_adcValue
            g_adcValue = value        # so adcRead() can see it

            #print(value)          # test_adcRead() will print it
            #enter to break, repeat with 'y'
            if False :             # deprecated
                go= input('continue: n/y')
                if (len(go)==0):
                    go='n'
            #time.sleep(sleepTime)
        #if end
        return value
    #def end mydo_1()

    def shutdown(self):
        #GPIO.cleanup()
        #BUS.close()
        pass 
    #def end shutdown()

#class end ADS1115Runner()

def adcRead(BUS,nChannel=0) :
    # nChannel is the number of the analog input to read i.e. 0 = AIN0

    # adcRead returns value 0 to ~11000 as the actual reading

    #by D@CC on 202CMar16 DC

    #must be preceded by 
    #    BUS.adcReadInit(I2C_BUS,icNumber)
    #    BUS.open(I2C_BUS)

    #Source: https://smartypies.com/projects/ads1115-with-raspberrypi-and-python/ads1115runner/  #DC
    #ADS1115 Datasheet: https://www.ti.com/lit/ds/symlink/ads1115.pdf?ts=1647366695082&ref_url=https%253A%252F%252Fwww.digchip.com%252F

    #Jumper S of KY-18 photo-cell to AIN2
    # invert the KY-18 3v3 and GND to measure the opposite [ light (not dark) ]
    # terminal strip is L-R  on the ADS1115 for RPi from SeeedStudios
    #  3V3,AIN3,AIN2,AIN1,AIN0,GND
    # Uses 3 objects:BUS.write_byte(RESET_ADDRESS, RESET_COMMAND)  #replace
    #   Runner.      for ADS1115 ADC
    #   BUS.         for ADS11#Jumper S of KY-18 photo-cell to AIN2  #DC

    #   GPIO.        for ALERTPIN  class is not used  #DC

    try:
        # I (D@CC) do not know anything about the logging
        logging.basicConfig(
            level=logging.DEBUG,
            #format='%(name)-12s: %(levelname)-8s %(message)s')
            format='%(message)s')
        logger = logging.getLogger('ADS1115Runner')
        #print("logger:",logger)
        #AINn=2                          # AINn=2 to read AIN2
        Runner = ADS1115Runner()    # create object Runner
        #print("before .cmdloop")
        Runner.mydo_1(BUS,nChannel)     # nChannel is 2 to read AIN2
        #Runner.cmdloop()            # will run the main Runner.method (cmd.Cmd)
                                    # which prompts for a response & invokes do_r(arg)
                                    # for a response of "r arg"
        #print("after .cmdloop")
  
        global g_adcValue
        returnValue = g_adcValue     # from mydo_1
        return returnValue  
    finally:
        Runner.shutdown()               #release this object
        #BUS.close()                     #release this object
#if end adcRead()

def adcReadInit(I2C_BUS=1,icNumber="ADS1115") :
    # arg1 is the BUS number used by the IC chip
    # arg2 is the number of the IC chip used on the Seeed ADS1115 4 channel RPi board
    # usage of global variables
    # global g_adcValue

    if icNumber=="ADS1115" :
        BUS=smbus.SMBus(I2C_BUS)
        #returns the BUS object , (usually BUS=smbus.SMBus(I2C_BUS) )
        # Open I2C device
        # I2C_BUS = 1
        # DEVICE_ADDRESS = 0x48

        return BUS
    else:
        print('ERROR025: ic supported is "ADS1115"')
        exit
#def end adcReadInit()

#   9 constants included in adcRead_Pkg.py
    RESET_ADDRESS = 0b0000000
    RESET_COMMAND = 0b00000110
    POINTER_CONVERSION = 0x0
    POINTER_CONFIGURATION  = 0x1
    POINTER_LOW_THRESHOLD  = 0x2
    POINTER_HIGH_THRESHOLD = 0x3
    DEVICE_ADDRESS = 0x48        # device address selected on the IC chip
    icNumber = "ADS1115"         # IC chip used
    I2C_BUS = 1                  # BUS number used by the IC chip

# end of adcRead_pkg.py
