#!/usr/bin/env python3 # devtimer.py - Temperature Controlled Photographic Darkroom Timer # Targeted for RaspberryPi # Copyright (c) 2018 TundraWare Inc. # Permission Hereby Granted For Unrestricted Personal Or Commercial Use from threading import Thread from time import time, sleep from tm1637 import * from wiringpi import wiringPiSetupGpio ##### # Constants ##### # Display BRIGHTNESS0 = 0x09 CLK0 = 21 DIO0 = 20 # General Constants DEBUG = True # Debugging switch BEEP = 15 # Beep interval CALIBRATION_OFFSET = 0.0022 # Compensate for program overhead in master loop NORMAL_TEMP = 68 # Reference temperature in degrees F ##### # Lookup Table For Compensating Factors ##### ''' Each paper|film-developer combo has an entry here, expressed as: +- correction/degree 68F is considered "normal". Temperatures below this will cause the timer to run slower. Temperatures above it, will cause the timer to run faster. This creates a "virtual second" that reflect time, material, and developer selected. WARNING: It takes about 250ms to update the display on a Pi Zero. So, if the "virtual second" falls at or below this, the code will be attempting to do updates faster than the display can handle. So ... the total compensation cannot reduce the virtual second to less than about 0.300 to be on the safe side. ''' compensate = ( 0.000, # Normal 0.010, # Small temperature dependency 0.040, # Medium temperature dependency 0.100, # Large temperature dependency ) # Beep at the user at fixed intervals def beep(): print("Beep!") # Get currently selected media/developer profile def get_profile(): return 0 # Return the current temperature in degrees F def get_temp(): return 72 # Update the display with elapsed time def show_elapsed(display0, elapsed): min = elapsed // 60 sec = elapsed % 60 d0 = display0.digit_to_segment[min // 10] d1 = display0.digit_to_segment[min % 10] d2 = display0.digit_to_segment[sec // 10] d3 = display0.digit_to_segment[sec % 10] display0.set_segments([d0, 0x80 + d1, d2, d3]) # Program entry point ''' Notice that the actual updating of the display gets run on its own thread. That's because - on a Pi Zero, at least - it takes over 250ms to do this. We don't want that time added to our timing loop, so we send it off on a parallel thread, and initiate timing for the next round in this thread. ''' if __name__ == "__main__": # Setup the hardware wiringPiSetupGpio() display0 = TM1637(CLK0, DIO0, BRIGHTNESS0) # Start timing, using the selected profile and measured temperature elapsed_time = 0 while True: # Beep periodically if not elapsed_time % BEEP: beep() if DEBUG: last = time() update_thread = Thread(None, show_elapsed, None, (display0, elapsed_time)) sleep(1.000 + ((NORMAL_TEMP-get_temp()) * compensate[get_profile()]) - CALIBRATION_OFFSET) elapsed_time += 1 elapsed_time %= 6000 update_thread = Thread(None, show_elapsed, None, (display0, elapsed_time)) if DEBUG: print("Inter-update Time: %s" % str(time()-last)) update_thread.start()