import time import RPi.GPIO as GPIO from timeit import default_timer as timer import logging import collections import multiprocessing class fcControl(): light_pin = 2 red_pin = 3 yellow_pin = 4 trigger_pin = 14 speed = 100 frame_advance_pct=50 #This is the CUSHION - we advance 100 minus this amt after trigger def __init__(self): GPIO.setmode(GPIO.BCM) self.light = lightControl(self.light_pin, True) self.redled = lightControl(self.red_pin) self.yellowled = lightControl(self.yellow_pin) self.motor = stepperControl() GPIO.setup(self.trigger_pin, GPIO.IN, pull_up_down=GPIO.PUD_DOWN) self.motorstate = 0 self.smart_motor = False self.smart_headroom = 25 self.triggertime = 0 self.qlen = 5 self.triggertimes = collections.deque([],self.qlen) self.phototimes = collections.deque([],self.qlen) def light_on(self): self.light.on() def light_off(self): self.light.off() def red_on(self): self.redled.on() def red_off(self): self.redled.off() def yellow_on(self): self.yellowled.on() def yellow_off(self): self.yellowled.off() def yellow_is_on(self): return GPIO.input(self.yellow_pin) def cleanup(self): logging.info("Cleaning up GPIO") self.light.off() self.redled.off() self.yellowled.off() self.motor.stop() GPIO.cleanup() def motor_wake(self): self.motor.wake() def motor_sleep(self): self.motor.sleep() def motor_fwd(self, speed=False): if (not speed): speed = self.speed self.motor.fwd(speed) self.motorstate = 1 def motor_rev(self, speed=False): if (not speed): speed = self.speed self.motor.rev(speed) self.motorstate = -1 def motor_stop(self): self.motor.stop() self.triggertimes.clear() self.phototimes.clear() if self.motorstate: self.motor.center(self.trigger_pin, self.frame_advance_pct, self.motorstate) self.motorstate = 0 self.motor.sleep() def calibrate(self): self.motor.center(self.trigger_pin, self.frame_advance_pct, 1) def end_photo(self): newtime = timer() phototime = newtime - self.triggertime self.phototimes.appendleft(phototime) class lightControl: def __init__(self, pin, reversed=False): self.pin = pin self.reversed = reversed GPIO.setup(pin, GPIO.OUT) self.off() def on(self): GPIO.output( self.pin, not(self.reversed) ) def off(self): GPIO.output( self.pin, self.reversed ) class stepperControl: pulse_freq = 1000 #stepper motor control pins dir_pin = 18 ms1_pin = 22 ms2_pin = 23 sleep_pin = 27 reset_pin = 15 pulse_pin = 17 half_pulse = .001 #for frame advance steps_per_rev = 200 #also change for MotorDriver class below def __init__(self): GPIO.setmode(GPIO.BCM) GPIO.setup(self.dir_pin, GPIO.OUT) GPIO.setup(self.pulse_pin, GPIO.OUT) GPIO.setup(self.ms1_pin, GPIO.OUT) GPIO.setup(self.ms2_pin, GPIO.OUT) GPIO.setup(self.sleep_pin, GPIO.OUT) GPIO.setup(self.reset_pin, GPIO.OUT) dir=False GPIO.output(self.dir_pin, dir) GPIO.output(self.pulse_pin, False) GPIO.output(self.ms1_pin, True) GPIO.output(self.ms2_pin, False) GPIO.output(self.sleep_pin, False) GPIO.output(self.reset_pin, True) self.p1 = GPIO.PWM(self.pulse_pin, self.pulse_freq) def wake(self): GPIO.output(self.sleep_pin, True) logging.debug("motor waking") time.sleep(.1) def sleep(self): GPIO.output(self.sleep_pin, False) logging.debug("motor sleeping") time.sleep(.1) def stop(self): self.p1.stop() GPIO.output(self.dir_pin, False) def fwd(self, speed=100): self.wake() self.p1.stop() time.sleep(.5) self.p1.ChangeFrequency(self.pulse_freq*speed/100) logging.debug(self.pulse_freq*speed/100) GPIO.output(self.dir_pin, False) logging.debug("motor starting fwd") self.p1.start(20) def rev(self, speed=100): self.wake() self.p1.stop() time.sleep(.5) self.p1.ChangeFrequency(self.pulse_freq*speed/100) GPIO.output(self.dir_pin, True) logging.debug("motor starting rev") self.p1.start(20) def fwdFrame(self, num=1): self.wake() logging.debug("fwdFrame "+str(num)) self.windFrame(num) self.sleep() def windFrame(self, num=1): pin=self.pulse_pin #directly accessing for speed hp=self.half_pulse for i in range (0,int(self.steps_per_rev*num)): GPIO.output(pin, True) #used instead of variable for speed time.sleep(hp) #again, directly entring num for speed GPIO.output(pin, False) #used instead of variable for speed time.sleep(hp) def revFrame(self, num=1): #winds back one more than necessary, then forward to properly frame logging.debug("revFrame "+str(num)) self.wake() if num==1: GPIO.output(self.dir_pin, True) self.windFrame() GPIO.output(self.dir_pin, False) else: GPIO.output(self.dir_pin, True) self.windFrame(num+1) GPIO.output(self.dir_pin, False) time.sleep(.25) self.windFrame(1) self.sleep() def center(self, trigger_pin, pct, fromState): #to center a frame in the gate and position the projector #mechanism properly relative to the photo trigger (i.e. 'pct' #distance ahead of it. Winding film backwards doesn't position #correctly, so we need to bump ahead a frame to position after #rewinding while GPIO.input(trigger_pin): #if trigger is engaging,reverse NO, FORWARD until it's not GPIO.output(self.dir_pin, False) #True) GPIO.output(self.pulse_pin, True) time.sleep(.001) GPIO.output(self.pulse_pin, False) time.sleep(.001) time.sleep(.2) while not GPIO.input(trigger_pin): #then forward until it is GPIO.output(self.dir_pin, False) GPIO.output(self.pulse_pin, True) time.sleep(.001) GPIO.output(self.pulse_pin, False) time.sleep(.001) if fromState==-1: #if we had been reversing, jump forward a frame self.windFrame(1) logging.debug("Winding 1 frame") stepsFwd=int(self.steps_per_rev*(100-pct)/100.0) GPIO.output(self.dir_pin, False) time.sleep(.01) logging.debug("Forward "+str(stepsFwd)) for i in range(0,stepsFwd): #now forward enough to leave a proper cushion GPIO.output(self.pulse_pin, True) time.sleep(.001) GPIO.output(self.pulse_pin, False) time.sleep(.001) class MotorDriver(multiprocessing.Process): #a very simple class designed to stick frame-advance in another #process during captures, so a different core can handle it and it #won't delay photography - or vice versa pulse_pin = 17 half_pulse = .0008 steps_per_rev = 200 def __init__(self, cap_event, exit_event): super(MotorDriver, self).__init__() self.cap_event=cap_event self.exit_event=exit_event logging.debug("MotorDriverInit") GPIO.setmode(GPIO.BCM) GPIO.setup(self.pulse_pin, GPIO.OUT) def run(self): logging.debug("Motor Frame Advance Process running") try: while not self.exit_event.is_set(): if self.cap_event.wait(2): self.fwdFrame(1) except KeyboardInterrupt: logging.debug("Motor Process killed via kbd") finally: logging.debug("Motor Frame Advance Process ending") def fwdFrame(self, num=1): pin=self.pulse_pin hp=self.half_pulse logging.debug("Fframe") for i in range (0,int(self.steps_per_rev*num)): GPIO.output(pin, True) #used instead of variable for speed time.sleep(hp) #again, directly entring num for speed GPIO.output(pin, False) #used instead of variable for speed time.sleep(hp) self.cap_event.clear()