class Actuator: # Class attributes that can be accessed using ActuatorControl.X (not actuatorcontrol.X) MAX_NUM_OF_SERVOS = 0 # Circular servos MAX_NUM_OF_MOTORS = 12 # Circular stepper or brushless DC motors MAX_NUM_OF_LINEAR_ACT = 5 # Linear actuators N_A = 0 # Not Applicable # Circular & linear actuator direction CONSTANTS CCW = -1 # Counter-Clockwise CW = 1 # Clockwise LINEAR_IN = CCW # Towardsbase of linear actuator LINEAR_OUT = CW # Away from base of linear SERVO_SLACK = 0.2 # Positional accuaracy slack for servo so that control system does not go crazy FORWARD = 1 BACKWARD = -1 # Pin value CONSTANTS LOW = 0 HIGH = 1 # Wire value CONTSTANTS # Raspberry Pi 4 Pin Layout https://pinout.xyz/pinout/pin1_3v3_power NO_PIN = -1 #TODO This constant may not be needed :) NO_WIRE = 0 VCC_3_3V = 1 VCC_3_3V_NAME = "BOARD1" # 3.3 Volts @ upto 0.050 Amps = 0.165 Watts https://pinout.xyz/pinout/pin1_3v3_power VCC_5V = 2 VCC_5V_NAME = "BOARD2" # 5 Volts @ upto ~1.5 Amps (Power Adapter - Pi usgae) = 7.5 Watts https://pinout.xyz/pinout/pin2_5v_power GND = "BOARD6&9&14&20&25&30&34&39" # Digital Ground (0 Volts) https://pinout.xyz/pinout/ground # Negative to NOT confuse it with Pi BOARD 12 https://pinout.xyz/pinout/pin12_gpio18 HIGH_PWR_5V = 5 # 5.00 Volts @ upto 5.0 Amps = 25.0 Watts to power Pi, force / load cell sensor and servos HIGH_PWR_12V = 12 # 12.0 Volts @ upto 5.0 Amps = 70.0 Watts to power linear actuators HIGH_PWR_36V = 36 # TODO (30 or 36) Volts @ upto 5 Amps = 150 Watts to power Stepper Motors # wires are on the actuator side of hardwrae schematic. While pins are on the CPU side, but often have similar names wires = [NO_WIRE, NO_WIRE, NO_WIRE, NO_WIRE, NO_WIRE, NO_WIRE, NO_WIRE] def __init__(self, aType, actuatorID, pins, partNumber, direction): """ Constructor to initialize an Actutator object, which can be an AngularServo(), Motor(), or Relay() Key arguments: self - Newly created object aType - Single String character to select type of actuator to create (S=Servo, M=Motor, R=Relay) actuatorID - Interger CONSTANT defined in Driver.py to enable quick array searches pins - Array to document wires / pins being used by Raspberry Pi to control an actuator partNumber - Vendor part number string variable (e.g. Seamuing MG996R) direction - Set counter-clockwise (CCW) / Linear IN or clockwise (CW) / Linear OUT as the forward direction Return value: Newly created Actuator() object """ currentProgramFilename = os.path.basename(__file__) self.DebugObject = Debug(True, currentProgramFilename) self.actuatorID = actuatorID self.actuatorType = aType numOfWires = len(pins) wires = np.empty(numOfWires, dtype=object) # TODO wires = ndarray((len(pins),),int) OR wires = [None] * len(pins) # Create an array on same length as pins[?, ?, ?] for i in range(numOfWires): #TODO REMOVE print("PIN: " + repr(i)) self.wires[i] = pins[i] self.partNumber = partNumber self.forwardDirection = direction # The last wire in array is the PWM control pin tempServoObject = Servo(pins[0]) #TODO REMOVE BECAUSE TO SIMPLE AN OBJECT #tempServoObject = gpiozero.Servo(pins[0]) #TODO REMOVE BECAUSE TO SIMPLE AN OBJECT tempAngularServoObject = AngularSevo(wires[len(wires)-1]) # The last two wires in array are the INPUT control pins tempMotorObject = Motor(wires[len(wires)-2], wires[len(wires)-1]) # The last wire in array is the relay control pin tempRelayObject = OutputDevice(wires[len(wires)-1]) # https://gist.github.com/johnwargo/ea5edc8516b24e0658784ae116628277 # https://gpiozero.readthedocs.io/en/stable/api_output.html # https://stackoverflow.com/questions/14301967/bare-asterisk-in-function-arguments/14302007#14302007 if(aType == "S"): self.actuatorObject = tempAngularServoObject #TODO If above DOES NOT WORK: self.actuatorType = Servo(wires[0], initial_value=0, min_pulse_width=1/1000, max_pulse_width=2/1000, frame_width=20/1000, pin_factory=None) elif(aType == "M"): self.actuatorObject = tempMotorObject #TODO If above DOES NOT WORK: self.actuatorType = Motor(wires[0], wires[1], pwm=true, pin_factory=None) elif(aType == "R"): self.actuatorObject = tempRelayObject #TODO If above DOES NOT WORK: self.actuatorObject = gpiozero.OutputDevice(wired[0], active_high=False, initial_value=False) else: self.DebugObject.Dprint("INVALID Actutator Type in __init__ method, please use S, M, or R string as first parameter to Actuator() Object") def Run(self, duration, newPosition, speed, direction): #TODO https://www.google.com/search?q=pass+object+to+python+function&rlz=1C1GCEA_enUS892US892&oq=pass+object+to+python+function&aqs=chrome..69i57.5686j0j7&sourceid=chrome&ie=UTF-8 #TODO https://stackoverflow.com/questions/20725699/how-do-i-pass-instance-of-an-object-as-an-argument-in-a-function-in-python """ Run an actuator for a given number of milliseconds to a given position at percentage of max speed in FORWARD or BACKWARDS direction Key arguments: duration - Time actuator is in motion, for Servo() objects this can be used to control speed of movement newPosition - New position between -1 and 1 that actuator should move to speed - Speed at which actuator moves at, for Servo() objects this parameter is NOT used direction - Set counter-clockwise (CCW or LINEAR_IN) or clockwise (CW or LINEAR_OUT) as the forward direction Return Value: NOTHING """ print("Actuator.py Run() function started!") if(type == "S"): currentPosition = self.actuatorObject.value() if(currentPosition < (newPosition - Actuator.SERVO_SLACK)): self.actuatorObject.max() #TODO THIS MAY NOT STOP AND GO ALL THE WAY TO MAX POS elif(currentPosition > (newPosition - Actuator.SERVO_SLACK)): self.actuatorObject.min() #TODO THIS MAY NOT STOP AND GO ALL THE WAY TO MIN POS else: # NEAR to new position DO NOTHING self.actuatotObject.dettach() elif(type == "M"): #TODO Write motor control code Motor.enable() #TODO CHANGE TO self.acutatorObject currentPosition = actuatorObject.value while(currentPosition != newPosition): if(actuatorObject.forwardDirection == Actuator.CW): Motor.forward(speed) else: Motor.reverse(speed) currentPosition = self.actuatorObject.value sleep(duration) #TODO signal.pause(duration) Motor.disable() elif(type == "R"): relay.on() sleep(duration) #TODO signal.pause(duration) relay.off() else: self.DebugObect.Dprint("INVALID Actutator Type sent to Run method, please use S, M, R as first parameter to Actuator() Object") self.DebugObject.Dprint("Run function completed!") def setAngularPosition(self, newAngle): """ Set the rotational position of a AngularServo() or Motor() object Key arguments: newAngle - Rotational angle to set actuator to, more exact for Servo() objects then Motor() object Return value: NOTHING """ if(self.actuatorType == "S"): self.angle = newAngle elif(self.actuatorType == "M"): self.DebugObject.Dprint("THIS CODE IS GOING TO BE HARD") #TODO Possible global variable with dead recoking needed elif(self.actuatorType == "R"): self.Debug.Dprint("Relays do not have rotational positions. Are you sure you called the correct object?") else: self.DebugObject.Dprint("INVALID Actutator Type sent to SetAngularPosition method, please use S, M, R as first parameter to Actuator() Object") def getPosition(self): """ Read the linear or rotational positon on an actuator Return value: The position of actuator, with value between -1.0 and 1.0 inclusively """ if(self.actuatorType == "S"): print("TODO") #TODO return self.value def isActive(self): """ Determine if actuator is moving Return value: TRUE if actuator is powered on and moving, FALSE otherwise """ return self.actuatorOjbect.isActive def setAngle(self, angle): print("TODO")
from gpiozero import PingServer, pi_info # Useful pin status tools and math tools from gpiozero.tools import all_values, negated, sin_values # Useful for controlling devices based on date and time from gpiozero import TimeOfDay # Allow control of output devices such as Motors, Servos, LEDs, and Relays from gpiozero import Motor, Servo, LED, Energenie, OutputDevice except ImportError: #TODO DO LOW LEVEL PIN CONTROL THAT WORKS EVER WHERE? http://wiringpi.com/the-gpio-utility/ currentProgramFilename = os.path.basename(__file__) ImportDebugObject = Debug(True, currentProgramFilename) ImportDebugObject.Dprint("WARNING: You are running code on Mac or PC (NOT a Raspberry Pi 4), thus hardware control is not possible.") class Actuator: # Class attributes that can be accessed using ActuatorControl.X (not actuatorcontrol.X) MAX_NUM_OF_SERVOS = 0 # Circular servos MAX_NUM_OF_MOTORS = 12 # Circular stepper or brushless DC motors MAX_NUM_OF_LINEAR_ACT = 5 # Linear actuators N_A = 0 # Not Applicable # Circular & linear actuator direction CONSTANTS CCW = -1 # Counter-Clockwise CW = 1 # Clockwise LINEAR_IN = CCW # Towardsbase of linear actuator LINEAR_OUT = CW # Away from base of linear
class CocoDrink: # Drink Name CONSTANTS NONE = "NONE" NO_DRINK = 0 COCONUT = 1 MAX_DRINK_NAME = COCONUT # Coconut sizing CONSTANTS SIZE_102MM = 102 SIZE_88MM = 88 # Addon Name CONSTANTS #TODO CONVERT INTERGERS TO STRINGS??? IMMUNITY_BOOST = 1 DAILY_VITAMINS = 2 ENERGY_BOOST = 3 PINA_COLADA = 4 PINEAPPLE_FLAVOR = 5 ORANGE_FLAVOR = 6 MAX_ADD_ON_NAME = ORANGE_FLAVOR # Extra add-ons for v2021.1 CBD = 7 ENERGY_BOOST = 8 ORGINAL_RED_BULL = 9 RUM = 10 # Add-On level CONSTANTS MAX_ADD_ON_LEVEL = 5 MIN_ADD_ON_LEVEL = 0 # User GUI.py program flow selection CONSTANTS TAPPED = 0 TOPPED_OFF = 1 #TODO REMOVE - LIKELY NOT GOING TO BE SUPPORTED IN v2020.1 FLAVOR = 2 HEALTH_ADDITIVE = 3 # LASER branding PNG filename CONSTANTS RESORT_WORLD_LOGO = "ResortWorldLogoV0.png" COCOTAPS_LOGO = "CocoTapsLogoV0.png" WYNN_HOTEL_LOGO = "WynnHotelLogoV0.png" RED_BULL_LOGO = "RedBullLogoV0.png" BACARDI_LOGO = "BacardiLogoV0.png" ROYAL_CARRIBBEAN_LOGO = "RoyalCarribbeanLogoV0.png" def __init__(self, drinkNameID, addOnFlavor, addOnFlavorLevel, addOnHealthAdditive, addOnHealthAdditiveLevel, tapOrCutFlow, brandingArt): """ Constructor to initialize an CocoDrink() object, which stores add-ons and artwork Key arguments: self -- Newly created CocoDrink object drinkNameID -- CONSTANT product name of drink addOnFlavor -- CONSTANT product name of flavoring being added to base drink (e.g. IMMUNITY_BOOST, PINA_COLADA, etc) addOnFlavorLevel -- The unit step amount of flavor to be added to a drink from 0 to MAX_ADD_ON_LEVEL Each unit is a different but constant volume (in milliLiters) for each flavor addOnHealthAdditive -- CONSTANT product name of health additive being added to base liquid addOnHealthAdditiveLevel -- The unit step amount of additive to be added to a drink from 0 to MAX_ADD_ON_LEVEL Each discrete unit is a differnt but constant volume (in milliLiters) for each additive tapOrCutFlow -- Variable used as conditional in program flow to determin if coconut is topped off or tapped brandingArt -- PNG image to LASER brand onto the side of the coconut (max size is 200 MB or ? x ? pixels / ? x ? inches) Return value: New CocoDrink() object """ currentProgramFilename = os.path.basename(__file__) self.DebugObject = Debug(True, currentProgramFilename) self.drinkNameID = drinkNameID self.addOnFlavor = addOnFlavor self.addOnFlavorLevel = addOnFlavorLevel self.addOnHealthAdditive = addOnHealthAdditive self.addOnHealthAdditiveLevel = addOnHealthAdditiveLevel self.tapOrCutFlow = tapOrCutFlow #TODO REMOVE - LIKELY NOT GOING TO BE SUPPORTED IN v2020.1 self.brandingArt = brandingArt if (addOnFlavorLevel > CocoDrink.MAX_ADD_ON_LEVEL or addOnHealthAdditiveLevel > CocoDrink.MAX_ADD_ON_LEVEL): self.DebugObject.Dprint( "OBJECT CREATION ERROR: You created a CocoDrink() object with add-on level greater then " + MAX_ADD_ON_LEVEL) __exit__() # Destructor / memory clean up if (addOnFlavorLevel < 0 or addOnHealthAdditiveLevel < 0): self.DebugObject.Dprint( "OBJECT CREATION ERROR: You created a CocoDrink() object with add-on level less then " + MIN_ADD_ON_LEVEL) __exit__() # Destructor / memory clean up if (CocoDrink.NO_DRINK > drinkNameID or drinkNameID > CocoDrink.MAX_DRINK_NAME): self.DebugObject.Dprint( "OBJECT CREATION ERROR: You created a CocoDrink() object with a drink name that doesn't exist. Why did you do that genius?" ) __exit__() # Destructor / memory clean up def __enter__(self): """ The 'with' statement clarifies code that previously would use try...finally blocks to ensure that clean-up code is executed. https://docs.python.org/2.5/whatsnew/pep-343.html https://stackoverflow.com/questions/1984325/explaining-pythons-enter-and-exit Key arguments: NONE Return value: NOTHING """ print("in __enter__") return self def __exit__(self, exception_type, exception_value, traceback): """ Memory cleanup Key arguments: exception_type -- exception_value -- traceback -- Return value: NOTHING """ self.DebugObject.Dprint("Cleaning up CocoDrink() object in __exit__ ") def GetAddOn(self, lType): """ Determine the type of add-on the user selected from GUI.py and branch code floW Key arguments: lType -- Type of liquid add-on to inject into a coconut Return value: addOn -- CocoDrink CONSTANT based on user selection """ if (lType == CocoDrink.FLAVOR): addOn = self.GetFlavorType() elif (lType == CocoDrink.HEALTH_ADDITIVE): addOn = self.GetHealthAdditiveType() #TODO OR (self) return addOn def GetFlavorType(self): """ Get product name of flavoring Key arguments: NONE Return value: Integer GLOBAL CONSTANT from CocoDrink.py """ return self.addOnFlavor def GetHealthAdditiveType(self): """ Get product name of additive fluid Key arguments: NONE Return value: return Integer GLOBAL CONSTANT from CocoDrink.py """ return self.addOnHealthAdditive def GetLaserArtType(self): """ Get filename of logo LASER branding into coconut Key arguments: NONE Return value: return String GLOBAL CONSTANT from CocoDrink.py """ return self.brandingArt def GetFlavorLevel(self): """ Get amount / level of flavor fluid in a coco drink in User Interface units Key arguments: NONE Return value: return Integer value between 0 and MAX_ADD_ON_LEVEL inclusively """ return self.addOnFlavorLevel def GetHealthAdditiveLevel(self): """ Get amount / level of health additive fluid in a Cocodrink in User Interface units Key arguments: NONE Return value: return Integer value between 0 and MAX_ADD_ON_LEVEL inclusively """ return self.addOnHealthAdditiveLevel def ConvertLevelToVolume(self, addOnUnits): """ Since each fluid has a different flavor or health effect strength per mL this function defined what the User Interface units represent Key arguments: addOnUnits -- Integer variable, between and 0 and MAX_ADD_ON_LEVEL (inclusive) Reture value: volume -- Interger variable, in units of mL for pumps to dispense """ self.addOnFlavor = addOnFlavor self.addOnFlavorLevel = addOnFlavorLevel self.addOnHealthAdditive = addOnHealthAdditive self.addOnHealthAdditiveLevel = addOnHealthAdditiveLevel if (self.addOnHealthAdditive == IMMUNITY_BOOST): oneUnitToMilliLiterFactor = 15 elif (self.addOnHealthAdditive == DAILY_VITAMINS): oneUnitToMilliLiterFactor = 13.3 elif (self.addOnHealthAdditive == ENERGY_BOOST): oneUnitToMilliLiterFactor = 13.3 elif (self.addOnFlavor == PINA_COLADA): oneUnitToMilloiiterFactor = 12 elif (self.addOnFlavor == PINEAPPLE_FLAVOR): oneUnitToMilliLiterFactor = 19 elif (self.addOnFlavor == ORANGE_FLAVOR): oneUnitToMilliLiterFactor = 3 else: self.DebugObject.Dprint("ERROR CODE (0x" + GLOBAL_CONSTANT_USAGE_ERROR + ") Verfiy that this Tapomatic unit has " + self.addOnHealthAdditice + " & " + self.addOneFlavor + " stocked.") volume = addOnUnits * oneUnitToMilliLeterFactor return volume def ConvertVolumeToPumpRunTime(volume, cocoPartNumber): """ Since motor models can vary in dispense volume / sec this function adjusts run time Key argument: volume -- Interger variable, of desired volume to be dispensed cocoPartNumber -- String variable, which is internal CocoTaps data in the format (XXX-YYYY-ZZ)) Return value: seconds - Float variable, that pump should run in order to dispense the correct volume of liquid """ thiccBoiFactor = 1 # Set to default value if (pumpPartNumber == "202-0006-A"): oneMilloLiterToSecondFactor = 2.2 else: this.DebugObject.Dprint( "ERROR CODE (0x" + PUMP_CONFIGURATION_ERROR + ") Verify that you are using the correct pump part nuumber, as defined in Actuator.py and CocoDrink.py" ) if (self.drinkName == PINA_COLADA): thiccBoiFactor = 1.75 # This flavoring is extra thick, pump longer to get correct volume seconds = volume * oneMilloLiterToSecondFactor * thiccBoiFactor return seconds def UnitTest(): print("START CocoDrink.py UnitTest()") TestCocoDrinkObject = CocoDrink(CocoDrink.COCONUT, CocoDrink.NONE, 0, CocoDrink.DAILY_VITAMINS, 5, CocoDrink.TAPPED, CocoDrink.COCOTAPS_LOGO) print("AddOn flavor ID # is: ", TestCocoDrinkObject.GetAddOn(CocoDrink.FLAVOR)) print("Flavor LEVEL (in User Interface units) selected was: ", TestCocoDrinkObject.GetFlavorLevel()) print("AddOn health additive ID # is: ", TestCocoDrinkObject.GetAddOn(CocoDrink.HEALTH_ADDITIVE)) print("Health additive LEVEL (in Use Interface units) selected was: ", TestCocoDrinkObject.GetHealthAdditiveLevel()) print("END CocoDrink.py UnitTest()")
import os try: # Robotic Beverage Technologies code for controlling many different types of motors, servos, and relays from Actuator import * # Robotic Beverage Technologies code for controlling the physical movement and power output of a LASER from LASER import * except ImportError: #TODO DO LOW LEVEL PIN CONTROL THAT WORKS EVER WHERE? http://wiringpi.com/the-gpio-utility/ currentProgramFilename = os.path.basename(__file__) TempDebugObject = Debug( True, "Try/Catch ImportError in " + currentProgramFilename) TempDebugObject.Dprint( "WARNING - You are running code on Mac or PC (NOT a Raspberry Pi 4), thus hardware control is not possible." ) class CocoDrink: # Drink Name CONSTANTS NONE = "NONE" NO_DRINK = 0 COCONUT = 1 MAX_DRINK_NAME = COCONUT # Coconut sizing CONSTANTS SIZE_102MM = 102 SIZE_88MM = 88
class LASER: # Preset LASER power level CONSTANTS (units are Watts) HIGH_POWER = 10.00 STANDARD_POWER = 5.00 LOW_POWER = 2.50 MAX_POWER_LEVEL = HIGH_POWER DEFAULT_LASER_CONSTANT = 0.05264472 #TODO Adjust this until LASER branding looks good # Global class variable laserConstant = -1 laserConfig = -1 def __init__(self, gpioFirePin, supplierPartNumber, cocoPartNumber, powerLevel, maxPowerLevel, brandingArt): """ Create a LASER object with power settings, part numbers, and image data to used when fired via GPIO pin Key arguments: gpioFirePin -- 5V GPIO pin used to control a LASER or a 12V relay connected to a LASER supplierPartNumber -- External supplier part number (i.e. PA-07-12-5V) cocoPartNumber -- Internal part number (i.e XXX-YYYYY-Z)) linked to one supplier part number powerLevel -- Power in Wats to intialize a LASER module first fire to maxPowerLevel -- Max power in Watts that LASER can support in continous operation (> 30 seconds) brandingArt -- Black & White PNG image to brand / burn into an object Return value: New LASER() object """ currentProgramFilename = os.path.basename(__file__) self.DebugObject = Debug(True, currentProgramFilename) #self.gpioFirePin = gpiozero.OutputDevice(gpioFirePin) self.supplierPartNumber = supplierPartNumber if (len(cocoPartNumber) != 10): self.DebugObject.Dprint( "Invalid part number format, please verify part number looks like XXX-YYYYY-Z" ) # List of current valid internal LASER part numbers if (cocoPartNumber == "205-0003-A" or cocoPartNumber == "???-????-?"): self.cocoPartNumber = cocoPartNumber else: self.DebugObject.Dprint("Invalid part number format") if (0 > powerLevel or powerLevel > LASER.MAX_POWER_LEVEL): # Check for valid power level and default to 10 Watts if invalid Debug.Dprint( DebugOject, "Invalid power. I'm setting the LASER power to " + repr(self.maxPowerLevel / 2) + " Watts") self.powerLevel = self.MAX_POWER_LEVEL / 2 else: self.powerLevel = powerLevel self.brandingArt = LASER.__WarpImage( CocoDrink.COCOTAPS_LOGO, CocoDrink.SIZE_102MM) # Initialize to standard CocoTaps logo LASER.__ConfigureLaserForNewImage(self.brandingArt) def LoadImage(fileName): """ Key arguments: filename -- PNG file to load into memory Return value: bwImg -- Black & White PNG image """ return bwImg def __WarpImage(fileName, coconutSize): """ Load a PNG image on the local harddrive into RAM Wrap a straight / square image so that after LASER branding on coconut its straight again Key arguments: fileName -- PNG file to load into memory (TODO max size in ? x ? pixels / ?? MB) coconutSize -- Horizontal diameter of coconut in millimeters Return value: newImage -- A new image that has been warpped to to display correctly after LASER branding """ # https://docs.opencv.org/2.4/doc/tutorials/core/mat_the_basic_image_container/mat_the_basic_image_container.html # https://pythonprogramming.net/loading-images-python-opencv-tutorial/ fileNameLength = len(fileName) periodIndex = fileNameLength - 3 fileExtension = fileName[periodIndex:] if (fileExtension.lower() == "png"): path = "static/images/" + fileName originalImage = cv2.imread(path) # Convert to gray scale first to apply better thresholding which will create a better black and white image grayImg = cv2.cvtColor(originalImage, cv2.COLOR_BGR2GRAY) # Anything above 127 on a scale from 0 to 255 is WHITE (thresh, bwImg) = cv2.threshold(grayImg, 127, 255, cv2.THRESH_BINARY) else: self.DebugObject.Dprint( "Please pass a .png file to the LoadImage() function in LASER.py code" ) imgWidth = originalImage.size imgHeight = originalImage.size for xPixel in range(imgWidth): print("xPixel = ", xPixel) for yPixel in range(imgHeight): print("yPixel = ", yPixel) print(" of ", imgHeight) #TODO rgbColor = originalImage.at<Vec3b>(xPixel,yPixel) # Split image into three part vertically and horizonatlly ### TODO SyntaxError: can't assign to comparison # warppedImg.at<Vec3b>(xPixel,yPixel) = rgbColor if (xPixel < (imgWidth / 5)): xPixel = xPixel + 8 # Skip EIGHT pixels since ends warps more at ends elif ((imgWidth / 5) <= xPixel and xPixel < (imgWidth * 2 / 5)): xPixel = xPixel + 4 # Skip EIGHT pixels since ends wraps more at ends elif ((imgWidth * 2 / 5) <= xPixel and xPixel < (imgWidth * 3 / 5)): xPixel = xPixel + 0 # Skip EIGHT pixels since ends wraps more at ends elif ((imgWidth * 3 / 5) <= xPixel and xPixel < (imgWidth * 4 / 5)): xPixel = xPixel + 4 # Skip EIGHT pixels since ends wraps more at ends elif ((imgWidth * 4 / 5) <= xPixel and xPixel < (imgWidth)): xPixel = xPixel + 8 # Skip EIGHT pixels since ends wraps more at ends def __ConfigureLaserForNewImage(img): """ PRIVATE FUNCATION (See __) Calculate pixel dwell duration based on LASER power level and image size Key arguments: img -- PNG file to load into memory Return value: pixelBurnDuration -- Time in seconds that LASER should dwell on coconut pixel """ numOfPixels = LASER.__GetNumOfPixels(img) moistureLevel = GetCoconutMoistureLevel() if (0 < self.powerLevel or self.powerLevel <= LOW_POWER): laserConstant = DEFAULT_LASER_CONSTANT * 0.5 elif (LOW < self.powerLevel or self.powerLevel < STANDARD_POWER): laserConstant = DEFAULT_LASER_CONSTANT * 1.0 elif (self.powerLevel >= STANDARD_POWER): laserConstant = DEFAULT_LASER_CONSTANT * 1.5 else: self.DebugObject.Lprint( "ERROR: Invalid power level choosen in ConfigureLaserForNewImage() function" ) pixelBurnDuration = laserConstant * moistureLevel / 100.0 * numOfPixels / 1000000 return pixelBurnDuration def StopLaser(self): """ Toogle GPIO pin connected to high power relay LOW to turn OFF a LASER Key arguments: NONE Return value: NOTHING """ self.gpioFirePin.off() #gpiozero.off(self.gpioFirePin) def BurnImage(self, laserConfig): """ Toogle GPIO pin possibly connected to a high power relay HIGH to turn ON a LASER Puts CPU to sleep so NOT a threadable function yet Key arguments: laserConfig -- TODO REMOVE? Return value: NOTHING """ pixelBurnDuration = self.__ConfigureLaserForNewImage dutyCycle = self.powerLevel / self.maxPowerLevel imageBurnComplete = False frequency = 100 # Desired LASER pulse in Hz while (not imageBurnComplete): # laserConstant is a class variable highTime = 1 / frequency * dutyCycle * laserConstant sleep(highTime) # Sleep upto 10 ms and keep LASER ON self.gpioFirePin.on() sleep(0.010 - highTime) # Sleep 10 ms minus time is HIGH self.gpioFirePin.off() imageBurnComplete = MoveLaserStepperMotor(pixelDwellDuration, frequency) def MoveLaserStepperMotor(self, frequency, motorID): """ Return value: NOTHING """ for pixelNum in range(0, GetNumOfPixels(filename) - 1): sleep(pixelDwellDuration + 1 / frequency) #TODO if(pixelNum = ) def SetPowerLevel(self, watts, cocoPartNumber): """ Set the power level based on LASER part number being used Key arguments: watts -- Power in Watts to set LASER output to cocoPartNumber -- Internal XXX-YYYYY-Z part number linked to a vendor part number """ if (cocoPartNumber == "205-00003-A"): if (0 > watts or watts > 10): self.DebugObject.Dprint( self.DebugObject, "The 400067260113 LASER must have power level between or equal to 0.1 and 10 Watts" ) else: self.powerLevel = watts else: self.DebugObject.Dprint( "This LASER supplier part number is not supported in LASER.py code base" ) def __GetNumOfPixels(inputImage): """ Calculate the total number of (pixels / 1,000,000) that is in an image file Key argument: inputImage Return value: totalNumOfPixels -- Total number of megapixels (million pixels) in an image """ #img = LoadLImage(self.brandingArt #TODO DOES LoadImage RETURN a img variable) img = cv2.imread(inputImage) imgWidth = img.width imgHeight = img.height totalNumOfPixels = imgWidth * imgHeight return totalNumOfPixels def GetCoconutMoistureLevel(self): """ Moisture level from 0 to 100 corresponing to % humidity Key arguments: NONE Return value: moisturePercentage -- An float from 0.0 to 100.0 """ #TODO Moisture sensor in fridge print("TODO I2C sensor") moisturePercentage = 100 return moisturePercentage def CompileImageAndStoreToEMMC(): """ Key arguments: NONE Return value: NOTHING """ LASER.__WarpImage()
#import numpy as np #TODO REMOVE SINCE IM NOT USE ARRAY ANY MORE #import cv2 as cv #TODO REMOVE SINCE MAKING SIMPLER IS DUMB! import cv2 try: # Allow control of output devices such as LEDs and Relays from gpiozero import LED, OutputDevice #from gpiozero import Energenize #TODO REMOVE SINCE TOO SPECIFIC??? except ImportError: #TODO DO LOW LEVEL PIN CONTROL THAT WORKS EVER WHERE? http://wiringpi.com/the-gpio-utility/ currentProgramFilename = os.path.basename(__file__) TempDebugObject = Debug( True, "Try/Catch ImportError in " + currentProgramFilename) TempDebugObject.Dprint( "WARNING - You are running code on Mac or PC (NOT a Raspberry Pi 4), thus hardware control is not possible." ) class LASER: # Preset LASER power level CONSTANTS (units are Watts) HIGH_POWER = 10.00 STANDARD_POWER = 5.00 LOW_POWER = 2.50 MAX_POWER_LEVEL = HIGH_POWER DEFAULT_LASER_CONSTANT = 0.05264472 #TODO Adjust this until LASER branding looks good # Global class variable laserConstant = -1 laserConfig = -1