diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..95865c8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +Script/__pycache__ +.lgd-nfy0 +rtDb.json diff --git a/Script/common.py b/Script/common.py new file mode 100644 index 0000000..06f3460 --- /dev/null +++ b/Script/common.py @@ -0,0 +1,23 @@ +CONFIG_YAML_FILE = "config.yaml" +RT_DB_FILE = "rtDb.json" + +INPUT = 0 +OUTPUT = 1 + +PULL_UP = 0 +PULL_DOWN = 1 + +HIGH = 1 +LOW = 0 + +LCD_MODULE_NO_OF_ROW = 4 +LCD_MODULE_NO_OF_COLUMN = 20 + +POSITION_LEFT = -1 +POSITION_MIDDLE = 0 +POSITION_RIGHT = 1 + +ROW_NO_0 = 0 +ROW_NO_1 = 1 +ROW_NO_2 = 2 +ROW_NO_3 = 3 diff --git a/Script/lcd20x4.py b/Script/lcd20x4.py new file mode 100644 index 0000000..cbcbab2 --- /dev/null +++ b/Script/lcd20x4.py @@ -0,0 +1,66 @@ +from RPLCD.i2c import CharLCD + +class Lcd20x4: + def __init__(self, i2cAddress: int, column: int, row: int): + self.i2cAddress = i2cAddress + self.port = 0x01 + self.columns = column + self.rows = row + self.dotSize = 8 + + self.init() + + + def init(self): + self._lcd = CharLCD( + i2c_expander = 'PCF8574', + address = self.i2cAddress, + port = self.port, + cols = self.columns, + rows = self.rows, + dotsize = self.dotSize) + self.clean() + + def clean(self): + self._lcd.clear() + + + def setLcdCursor(self, row: int, column: int): + self._lcd.cursor_pos = (row, column) + + + def write(self, + text : str, + row : int = -1, + column : int = -1, + padding : bool = False, + center : bool = False): + + # if only row is passrd but no column value + if row != -1 and column == -1: + column = 0 + + # padding is only allowed when center option is false + if padding and not center: + pad = self.columns - len(text) + text = text + (" " * pad) + + # center text + if center: + pad = self.columns - len(text) + leftPad = pad // 2 + rightPad = pad - leftPad + text = (" " * leftPad) + text + (" " * rightPad) + + if row != -1: + self.setLcdCursor(row, column) + + self._lcd.write_string(text) + + + def turnOffBacklight(self): + self._lcd.backlight_enabled = False + + + def turnOnBacklight(self): + self._lcd.backlight_enabled = True diff --git a/Script/main.py b/Script/main.py new file mode 100644 index 0000000..c463902 --- /dev/null +++ b/Script/main.py @@ -0,0 +1,219 @@ +from datetime import datetime +import time + +from lcd20x4 import Lcd20x4 +from rpigpio import RpiGpio + +from utility import * +from common import * +from pinDescription import * + + +# global hardware module library initilization +lcd = Lcd20x4(LCD_MODULE_ADDRESS, LCD_MODULE_NO_OF_COLUMN, LCD_MODULE_NO_OF_ROW) +gpio = RpiGpio() + + +# global variables +cycleIndexPos = 0 +cycleCounter = 0 + +previousPosition = None +currentPosition = None + +ResetBtnReading = None +buttonState = None + +powerDownTrigger = False +ResetTrigger = False +isEepromReadNeed = True +cycleIndexPosMiddle = False + +lastButtonState = HIGH +debounceDelay = 5000 +lastDebounceTime = 0 + + +# Introduction banner print +def startIntroPrint(): + lcd.write("SPRING LOADED", row = ROW_NO_0, center = True) + lcd.write("SWITCH TESTER", row = ROW_NO_1, center = True) + appVersion = getAppVersion() + lcd.write(f"Version: {appVersion}", row = ROW_NO_3, padding = True) + time.sleep(3) + lcd.clean() + time.sleep(0.5) + lcd.turnOffBacklight() + time.sleep(1) + lcd.turnOnBacklight() + time.sleep(1) + + +# This is the gpio initlization +def gpioInilitization(): + gpio.pinMode(ON_BOARD_LED_PIN, OUTPUT) + + gpio.pinMode(MICRO_SWITCH_S1_NC_PIN, INPUT) + gpio.pinMode(MICRO_SWITCH_S1_NO_PIN, INPUT) + gpio.pinMode(MICRO_SWITCH_S2_NC_PIN, INPUT) + gpio.pinMode(MICRO_SWITCH_S2_NO_PIN, INPUT) + + gpio.pinMode(POWER_LINE_DETECT_PIN, INPUT) + gpio.pinMode(RESET_PUSH_SWITCH_PIN, INPUT) + + gpio.pinMode(ON_BOARD_LED_PIN, OUTPUT) + + +# Time print function +def printCurrentTime(row: int): + lcd.write(datetime.now().strftime('%I:%M:%S %p %d/%m/%y'), row = ROW_NO_0, padding = True) + # time.sleep(0.1) + + +# Print the count value +def printCount(row: int, count: int): + lcd.write(f"Count: {count}", row = row, padding = True) + + +# This is the void setup() function just like Arduino code +def setup(): + global cycleIndexPos + global previousPosition + global cycleCounter + + startIntroPrint() + gpioInilitization() + createRtDbFile() + + if gpio.digitalRead(MICRO_SWITCH_S1_NO_PIN) and gpio.digitalRead(MICRO_SWITCH_S2_NC_PIN): + previousPosition = POSITION_LEFT + cycleIndexPos = POSITION_LEFT + + if gpio.digitalRead(MICRO_SWITCH_S1_NC_PIN) and gpio.digitalRead(MICRO_SWITCH_S2_NC_PIN): + previousPosition = POSITION_MIDDLE + cycleIndexPos = POSITION_MIDDLE + + if gpio.digitalRead(MICRO_SWITCH_S1_NC_PIN) and gpio.digitalRead(MICRO_SWITCH_S2_NO_PIN): + previousPosition = POSITION_RIGHT + cycleIndexPos = POSITION_RIGHT + + ########## TEST BEGIN ############### + if gpio.digitalRead(MICRO_SWITCH_S1_NO_PIN) and gpio.digitalRead(MICRO_SWITCH_S2_NC_PIN) and\ + gpio.digitalRead(MICRO_SWITCH_S1_NC_PIN) and gpio.digitalRead(MICRO_SWITCH_S2_NC_PIN) and\ + gpio.digitalRead(MICRO_SWITCH_S1_NC_PIN) and gpio.digitalRead(MICRO_SWITCH_S2_NO_PIN): + previousPosition = POSITION_MIDDLE + cycleIndexPos = POSITION_MIDDLE + print("Probably No switch found; All pin are HIGH ... !!") + ########## TEST END## ############### + + cycleCounter = getCycleCount() + printCount(row = ROW_NO_2, count = cycleCounter) + + gpio.digitalWrite(ON_BOARD_LED_PIN, LOW) + + +# This is the void loop() function just like Arduino code +def loop(): + global powerDownTrigger + global isEepromReadNeed + global previousPosition + global cycleIndexPos + global currentPosition + global cycleCounter + global cycleIndexPosMiddle + global ResetBtnReading + global lastButtonState + global debounceDelay + global buttonState + global ResetTrigger + global lastDebounceTime + + while True: + if not powerDownTrigger: + printCurrentTime(ROW_NO_0) + + # If power down condition happend + if gpio.digitalRead(POWER_LINE_DETECT_PIN) == LOW: + lcd.turnOffBacklight() + lcd.clean() + powerDownTrigger = True + + if not isEepromReadNeed: + setCycleCount(cycleCounter) + isEepromReadNeed = True + continue + else: + lcd.turnOnBacklight() + powerDownTrigger = False + if isEepromReadNeed: + cycleCounter = getCycleCount() + lcd.write(" "*LCD_MODULE_NO_OF_COLUMN, row = ROW_NO_3) + isEepromReadNeed = False + + # Reading the gpio pin for the switchs + if gpio.digitalRead(MICRO_SWITCH_S1_NO_PIN) and gpio.digitalRead(MICRO_SWITCH_S2_NC_PIN): + currentPosition = POSITION_LEFT + lcd.write("Position Left ", row = ROW_NO_1) + + if gpio.digitalRead(MICRO_SWITCH_S1_NC_PIN) and gpio.digitalRead(MICRO_SWITCH_S2_NC_PIN): + currentPosition = POSITION_MIDDLE + lcd.write("Position Middle", row = ROW_NO_1) + + if gpio.digitalRead(MICRO_SWITCH_S1_NC_PIN) and gpio.digitalRead(MICRO_SWITCH_S2_NO_PIN): + currentPosition = POSITION_RIGHT + lcd.write("Position Right ", row = ROW_NO_1) + + # According to the position, update the count number + if currentPosition != previousPosition: + if currentPosition == POSITION_LEFT or POSITION_LEFT == POSITION_RIGHT: + if cycleIndexPos == currentPosition: + cycleCounter += 1 + elif currentPosition == POSITION_MIDDLE: + if cycleIndexPosMiddle and (cycleIndexPos == currentPosition): + cycleCounter += 1 + cycleIndexPosMiddle = not cycleIndexPosMiddle + else: + pass + + # Update the count in the LCD + previousPosition = currentPosition + printCount(row = ROW_NO_2, count = cycleCounter) + + # If reach the full count number then turn on the Finish LED + if cycleCounter >= 7500000: + gpio.digitalWrite(ON_BOARD_LED_PIN, HIGH) + cycleCounter = 0 + + # Reset button debounce logic + ResetBtnReading = gpio.digitalRead(RESET_PUSH_SWITCH_PIN) + + if ResetBtnReading != lastButtonState: + lastDebounceTime = time.time() + + if ((time.time() - lastDebounceTime) * 1000) > debounceDelay: + if ResetBtnReading != buttonState: + buttonState = ResetBtnReading + + if buttonState == LOW: + lcd.write("Count Reseting... ", row = ROW_NO_3) + ResetTrigger = True + + lastButtonState = ResetBtnReading + + # If reset button pressed then clean the old stuffs + if ResetTrigger: + cycleCounter = 0 + setCycleCount(cycleCounter) + ResetTrigger = False + time.sleep(2) + lcd.write(" "*LCD_MODULE_NO_OF_COLUMN, row = ROW_NO_3) + + +# Entry point main function +def main(): + setup() + loop() + + +if __name__ == "__main__": + main() diff --git a/Script/pinDescription.py b/Script/pinDescription.py new file mode 100644 index 0000000..2b476c4 --- /dev/null +++ b/Script/pinDescription.py @@ -0,0 +1,12 @@ +MICRO_SWITCH_S1_NO_PIN = 17 +MICRO_SWITCH_S1_NC_PIN = 27 +MICRO_SWITCH_S2_NO_PIN = 22 +MICRO_SWITCH_S2_NC_PIN = 18 + +RESET_PUSH_SWITCH_PIN = 23 + +ON_BOARD_LED_PIN = 24 + +POWER_LINE_DETECT_PIN = 25 + +LCD_MODULE_ADDRESS = 0x27 diff --git a/Script/rpigpio.py b/Script/rpigpio.py new file mode 100644 index 0000000..7af747f --- /dev/null +++ b/Script/rpigpio.py @@ -0,0 +1,60 @@ +import RPi.GPIO as GPIO +from common import * + +class RpiGpio: + def __init__(self): + self.init() + + + def init(self): + GPIO.setmode(GPIO.BCM) + + + def deinit(self): + GPIO.cleanup() + + + def pinMode(self, pin: int, state: bool, pullUpDown: bool = None): + _state = None + _pullUpDown = None + + # Mapping with INPUT OUTPUT macro with GPIO.IN GPIO.OUT + if state == INPUT: + _state = GPIO.IN + elif state == OUTPUT: + _state = GPIO.OUT + else: + raise Exception("Wrong pin status!!") + + # Mapping with PULL_UP PULL_DOWN macro with GPIO.PUD_UP GPIO.PUD_DOWN + if pullUpDown == PULL_UP: + _pullUpDown = GPIO.PUD_UP + elif pullUpDown == PULL_DOWN: + _pullUpDown = GPIO.PUD_DOWN + else: + _pullUpDown = GPIO.PUD_OFF + + if pullUpDown != None: + GPIO.setup(pin, _state, pull_up_down = _pullUpDown) + else: + GPIO.setup(pin, _state) + + + def digitalRead(self, pin: int): + state = GPIO.input(pin) + return state + + + def digitalWrite(self, pin: int, state: bool): + _state = None + + # Mapping with INPUT OUTPUT macro with GPIO.IN GPIO.OUT + if state == INPUT: + _state = GPIO.IN + elif state == OUTPUT: + _state = GPIO.OUT + else: + raise Exception("Wrong pin status!!") + + GPIO.output(pin, _state) + diff --git a/Script/utility.py b/Script/utility.py new file mode 100644 index 0000000..ebb5bb1 --- /dev/null +++ b/Script/utility.py @@ -0,0 +1,54 @@ +import os +import yaml +import json + +from common import * + + +def getYamlData(yamlFile): + if not os.path.exists(yamlFile): + raise Exception(f"ERROR: {yamlFile} File not Found!!") + + try: + with open(yamlFile) as file: + config = yaml.load(file, Loader = yaml.FullLoader) + return config + except Exception as e: + print(f"Not able to read {yamlFile} file") + + +def setYamlData(yamlFile, data): + try: + with open(yamlFile, "w") as file: + json.dump(data, file, indent = 4) + except Exception as e: + print(f"Not able to create {yamlFile}!!!") + + +def getAppVersion(): + configData = getYamlData(CONFIG_YAML_FILE) + return configData.get("appVersion") + + +def createRtDbFile(): + if not os.path.exists(RT_DB_FILE): + blankData = { + "cycleCount": 0 + } + + setYamlData(RT_DB_FILE, blankData) + + +def getCycleCount(): + rtData = getYamlData(RT_DB_FILE) + cycleCount = rtData.get("cycleCount") + if cycleCount == None: + cycleCount = 0 + + return cycleCount + + +def setCycleCount(count: int): + rtData = getYamlData(RT_DB_FILE) + rtData["cycleCount"] = count + setYamlData(RT_DB_FILE, rtData) diff --git a/config.yaml b/config.yaml new file mode 100644 index 0000000..9ed9415 --- /dev/null +++ b/config.yaml @@ -0,0 +1 @@ +appVersion: 1.0.0.1000