diff --git a/TODO b/TODO index fe80ef0..3fa4b44 100644 --- a/TODO +++ b/TODO @@ -1,4 +1,4 @@ - Generate compensation tables at startup based on low/hi range - Cleanup footswitch handling -- Display out-of-range warning on LED +- Handle start/stop: Ensure rest to all zeros when done - Detect profile selection switch diff --git a/devtimer.py b/devtimer.py index c157181..9dff7e5 100755 --- a/devtimer.py +++ b/devtimer.py @@ -10,22 +10,21 @@ # General Imports +import sys from threading import Thread from time import time, sleep # Hardware Support -from ds18b20 import * from tm1637 import * from wiringpi import wiringPiSetupGpio, pinMode, digitalRead, GPIO -import globals - - ##### # Constants ##### +DEBUG = True # Debugging switch + # GPIO Ports And Other Hardware Constants TIME_BRIGHT = 0x0a # Displays @@ -43,11 +42,34 @@ # General Constants -DEBUG = True # Debugging switch - BEEP = 15 # Beep interval CALIBRATION_OFFSET = 0.003 # Compensate for program overhead in master loop MINUS = 16 # Index of minus segment table lookup +OUTOFRANGE = False # Flag for compensating profiles when temp is out of range + +# Timing profiles + +REALTIME = 0 +PAPER = 1 +FILM = 2 + +# Range of compensated timing + +TEMP_LOW=60 +TEMP_HIGH=80 + + +##### +# Global Variables +##### + +# These get updated by the threads that read the switches and +# thermocouple. On a slow machine like the Pi Zero, we want to avoid +# unnecessary function calls, so we make these globally RW. +# So, shoot me ... + +CURRENT_PROFILE = PAPER #REALTIME +CURRENT_TEMP = 68 ##### @@ -84,7 +106,76 @@ ) ##### -# Supporting Subroutines +# Temperature Measurement Subroutines +##### + +''' + This is based on the widely available DS18B20 temperature probe. + It is a 1-wire protocol device that returns temperature directly + degrees C. The dataline must go on the Pi GPIO 4 (pin 7) which + should be pulled up to VCC with a 4.7K resistor. + + You have to do several thingsto make this work: + + 1) Enable 1-wire support in the Pi: + + edit /boot/config.txt set: dtoverlay=w1-gpio + reboot + + 2) Each 1-wire device is connected to the same pin (7) + on the Pi. It distinguishes between them by a uniq + address. You have to find that address *for your specific + device*. You do this by looking at: + + ls -l /sys/bus/w1/devices + + You'll see the ID of your device there. That needs to + be set in the line below for this code to work with + your device. +''' + +DS18B20_ID = "28-0416840ac6ff" + + +# Read the current temp returned by the probe + +def read_probe(): + + global CURRENT_TEMP + + probe = open("/sys/bus/w1/devices/%s/w1_slave" % DS18B20_ID) + temp = float(probe.readlines()[-1].split()[-1].split("=")[-1])/1000 # Parse probe output + temp = int(round((temp * 9/5) +32)) # Convert C-F and round into an integer + + # 1-wire interfaces (like the DS18B20 uses) can occasionally + # return wildly wrong results. For this reason, we throw away + # values that have changed more than 10 degrees since the last + # reading, since that's almost certainly noise. + + if abs(temp - CURRENT_TEMP) <= 10: + CURRENT_TEMP = temp + + probe.close() + + +# Update the store temperature every second or so + +def monitor_temps(): + + global CURRENT_TEMP + + while True: + update_temp = Thread(name="Update Temp", target=read_probe).start() + Thread(name="Temp", target=show_temp, args=(display_temp, CURRENT_TEMP)).start() + + if DEBUG: + sys.stdout.write("Temp: %sF\n" % CURRENT_TEMP) + + sleep(1) + + +##### +# Utility Subroutines ##### # Beep at the user at fixed intervals @@ -122,11 +213,17 @@ d3 = display_time.digit_to_segment[sec % 10] display_time.set_segments([d0, 0x80 + d1, d2, d3]) + + # Display current temperature # Negative temps are 2 digits, positive temps are 3 digits def show_temp(display_temp, temp): + global OUTOFRANGE + + display_temp.brightness=TIME_BRIGHT + if temp < 0: # The leading digit is a minus sign hun = display_time.digit_to_segment[MINUS] temp = abs(temp) @@ -137,6 +234,14 @@ one = display_time.digit_to_segment[(temp % 100) % 10] F = display_time.digit_to_segment[15] display_temp.set_segments([hun, ten, one, F]) + sleep(0.5) + + # If we're in a compensating profile and out of + # temperature range, blink the time display + + if OUTOFRANGE: + display_temp.brightness=0 + display_temp.set_segments([hun, ten, one, F]) ##### @@ -164,7 +269,7 @@ # Dim displays in film mode DIM_BY=0 - if globals.CURRENT_PROFILE == globals.FILM: + if CURRENT_PROFILE == FILM: DIM_BY = 2 display_time = TM1637(TIME_CLK, TIME_DIO, TIME_BRIGHT-DIM_BY) @@ -172,7 +277,7 @@ # Start measuring temperature - get_temps = Thread(name="Temperatures", target=monitor_temps).start() + Thread(name="Temperatures", target=monitor_temps).start() sleep(1) # Wait a bit for the 1st temp measurement to complete # Start monitoring for footswitch presses @@ -194,33 +299,28 @@ if DEBUG: last = time() - # Get last known temp + # Update the time display - current_temp = globals.CURRENT_TEMP - - # Update the displays on separate threads - - update_time = Thread(name="Timer", target=show_elapsed, args=(display_time, elapsed_time)).start() - - update_temp = Thread(name="Temp", target=show_temp, args=(display_temp, current_temp)).start() + Thread(name="Timer", target=show_elapsed, args=(display_time, elapsed_time)).start() # For temperatures in-range, look up the compensating factor - if (globals.CURRENT_PROFILE == globals.REALTIME): + OUTOFRANGE = False + if (CURRENT_PROFILE == REALTIME): compensation_factor = 1 # Realtime requires no compensation - elif globals.TEMP_LOW <= globals.CURRENT_TEMP <= globals.TEMP_HIGH: - compensation_factor = compensate[globals.CURRENT_PROFILE][current_temp-globals.TEMP_LOW] + elif TEMP_LOW <= CURRENT_TEMP <= TEMP_HIGH: + compensation_factor = compensate[CURRENT_PROFILE][CURRENT_TEMP-TEMP_LOW] # Temperature is out of range for our correction table # This implictly uses the last known compensation factor so we can keep running else: - print("Display Out-Of-Range Message Here") + OUTOFRANGE = True sleep(compensation_factor - CALIBRATION_OFFSET) elapsed_time += 1 elapsed_time %= 6000 if DEBUG: - print("Current Temp: %s Factor: %s Inter-update Time: %s" % (current_temp, compensation_factor, str(time()-last))) + print("Current Temp: %s Factor: %s Inter-update Time: %s" % (CURRENT_TEMP, compensation_factor, str(time()-last))) diff --git a/ds18b20.py b/ds18b20.py deleted file mode 100755 index 88ff4cb..0000000 --- a/ds18b20.py +++ /dev/null @@ -1,73 +0,0 @@ -#!/usr/bin/env python3 - -import globals - -import sys -from time import sleep, time -from threading import Thread - -DEBUG = False - -''' - This is based on the widely available DS18B20 temperature probe. - It is a 1-wire protocol device that returns temperature directly - degrees C. The dataline must go on the Pi GPIO 4 (pin 7) which - should be pulled up to VCC with a 4.7K resistor. - - You have to do several thingsto make this work: - - 1) Enable 1-wire support in the Pi: - - edit /boot/config.txt set: dtoverlay=w1-gpio - reboot - - 2) Each 1-wire device is connected to the same pin (7) - on the Pi. It distinguishes between them by a uniq - address. You have to find that address *for your specific - device*. You do this by looking at: - - ls -l /sys/bus/w1/devices - - You'll see the ID of your device there. That needs to - be set in the line below for this code to work with - your device. -''' - -DS18B20_ID = "28-0416840ac6ff" - - -# Read the current temp returned by the probe - -def read_probe(): - - probe = open("/sys/bus/w1/devices/%s/w1_slave" % DS18B20_ID) - temp = float(probe.readlines()[-1].split()[-1].split("=")[-1])/1000 # Parse probe output - temp = int(round((temp * 9/5) +32)) # Convert C-F and round into an integer - - # 1-wire interfaces (like the DS18B20 uses) can occasionally - # return wildly wrong results. For this reason, we throw away - # values that have changed more than 10 degrees since the last - # reading, since that's almost certainly noise. - - if abs(temp - globals.CURRENT_TEMP) <= 10: - globals.CURRENT_TEMP = temp - - probe.close() - - -# Update the store temperature every second or so - -def monitor_temps(): - - while True: - update_temp = Thread(name="Update Temp", target=read_probe).start() - sleep(1) - - if DEBUG: - sys.stdout.write("Temp: %sF\n" % globals.CURRENT_TEMP) - - -# Run this from the command line -if __name__ == "__main__": - DEBUG = True - monitor_temps() diff --git a/globals.py b/globals.py index 2f16a46..c5d022e 100644 --- a/globals.py +++ b/globals.py @@ -19,5 +19,5 @@ # unnecessary function calls, so we make these globally RW. # So, shoot me ... -CURRENT_PROFILE = REALTIME +CURRENT_PROFILE = PAPER #REALTIME CURRENT_TEMP = 68