From f9472e4492c508545ece8604df6887ffc82c1b3d Mon Sep 17 00:00:00 2001 From: Jan Date: Tue, 24 Sep 2019 16:35:33 +0200 Subject: [PATCH] 1st commit --- button.py | 62 +++++++ clocky.py | 513 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ tm1637.py | 256 +++++++++++++++++++++++++++ 3 files changed, 831 insertions(+) create mode 100644 button.py create mode 100644 clocky.py create mode 100644 tm1637.py diff --git a/button.py b/button.py new file mode 100644 index 0000000..052d300 --- /dev/null +++ b/button.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python2.7 + +import time +import RPi.GPIO as GPIO +from time import sleep # this lets us have a time delay (see line 12) + +GPIO.setmode(GPIO.BCM) # set up BCM GPIO numbering +#GPIO.setup(20, GPIO.IN) # set GPIO25 as input (button) +#GPIO.setup(20, GPIO.IN, pull_up_down = GPIO.PUD_UP) + +class Button: + 'Handle button stuff' + starttime=0 + ispushed=0 + waspushed=0 + presstime=0 + ignore=0 + + def __init__(self, gpio): + print "initialize button on gpio %d"%gpio + self.gpio = gpio + GPIO.setup(self.gpio, GPIO.IN, pull_up_down = GPIO.PUD_UP) + GPIO.add_event_detect(self.gpio, GPIO.BOTH, callback=self.my_callback) + self.presstime=0 + self.starttime=0 + self.presstime=0 + self.waslong=0 + + def my_callback(self,channel): + if not GPIO.input(channel): # button is pressed +# print "[press] on %d"%self.gpio + self.starttime=time.time() + self.ispushed=1 + else: # button released + self.presstime=time.time()-self.starttime +# print "[release] on %d / for=%f sconds"%(self.gpio,self.presstime) + if self.ispushed: + self.waspushed=1 + self.ispushed=0 + + def WasPressed(self): + if self.waspushed: + self.waspushed=0 + return 1 + elif self.ispushed: + self.presstime=time.time()-self.starttime + if self.presstime > 2: + self.ispushed=0 + self.waspushed=1 + self.waslong=1 + + else: + return 0 + + def WasLong(self): + if self.presstime > 2: + self.waslong=0 + self.ignore=1 + return 1 + else: + return 0 + diff --git a/clocky.py b/clocky.py new file mode 100644 index 0000000..1185818 --- /dev/null +++ b/clocky.py @@ -0,0 +1,513 @@ +#!/usr/bin/python + +import pickle +import os.path +import time +import sys +import keyboard +import tm1637 +import pygame +from time import sleep,strftime +import RPi.GPIO as GPIO +import button + +settings= {} +looptype=0 + +o_configfilename = "clocky.conf" +o_defaultalarmtime = 60*7+45 # hh*60+mm +o_showalarmtime=5 # time that the alarmtime shows after short press +o_showtracktime=2 # note that this is per track, so total time is 10 times longer +o_editdelaytime=3 # time it takes to qualify as longpress +o_showtimeout=6 # timeout to exit edit modus +o_snoozetime = 9 # time to snooze +o_timetomaxvol = 150# seconds it takes to go from 0% to 100% volume +o_maxplaytime = 900 # seconds it to play +o_soundpreviewtime=5 +o_defaultbrightness=0 + +o_loopdelay=0.25 +track=0 + +GPIO.setmode(GPIO.BCM) # set up BCM GPIO numbering +GPIO.setup(21, GPIO.IN, pull_up_down = GPIO.PUD_UP) +GPIO.setup(20, GPIO.IN, pull_up_down = GPIO.PUD_UP) + + +def showclock(tstr): + "Show da clock" +# sys.stdout.write( "%s [%d]\r" % (tstr,looptype)) +# sys.stdout.flush() + + d=0 + if tstr[2]==':': + display.ShowDoublepoint(True) + else: + display.ShowDoublepoint(False) + + if tstr[0]==' ': + display.Show1(0,0x7f) + else: + display.Show1(0,int(tstr[0])) + if tstr[1]==' ': + display.Show1(1,0x7f) + elif tstr[1]=='=': + display.Show1(1,16) + else: + display.Show1(1,int(tstr[1])) + if tstr[3]==' ': + display.Show1(2,0x7f) + else: + display.Show1(2,int(tstr[3])) + if tstr[4]==' ': + display.Show1(3,0x7f) + else: + display.Show1(3,int(tstr[4])) + + +def writesettings(): + "write settings to conf file" + with open(o_configfilename, 'wb') as handle: + pickle.dump(settings, handle) + handle.close() + +def readmp3list(): + c=0 + mp3s=[] + dirs=os.listdir('.') + + for ent in os.listdir('.'): + if ent.lower().endswith(".mp3") and os.access(ent,os.R_OK): + mp3s.append(ent) + mp3s.sort() + for m in mp3s: + c+=1 + if c>99: break + + if c: + return(mp3s) + else: + return(0) + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + +looptype=0 +colon=0 +count=0 +digit=1 +showtime=0 +playtime=0 +soundpreview=0 +alarmoff=0 +alarmstarted=0 +tvol=0 +volume=0.0 +brightness=0.0 +snooze=0 + +print "\n +--------+\n | Clocky |\n | v0.1 |\n +--------+\n" + +display=tm1637.TM1637(CLK=14, DIO=4, brightness=1.0) +display.Clear() +display.SetBrightnessRaw(o_defaultbrightness) + +print "" +print "reading mp3s from directory:" +allmp3s=readmp3list() +if allmp3s==0: + print "ERROR: No mp3's found!" + display.ShowDoublepoint(False) + display.Show1(0,0x7f); + display.Show1(1,14); + display.Show1(2,17); + display.Show1(3,17); + GPIO.cleanup() + sys.exit() + +c=0 +for m in allmp3s: + print " %2d : %s"%(c+1,m) + c+=1 + +print "" +but1=button.Button(21) +but2=button.Button(20) +print "" + +if not os.path.isfile(o_configfilename): + print "creating new configfile \""+o_configfilename+"\":" + + settings = {'track0':1, 'track1':2, 'track2':3, 'track3':4, 'track4':5, 'track5':6, 'track6':7, 'track7':8, 'track8':9, 'track9':10 } + settings['alarmtime']=o_defaultalarmtime; + settings['starttrack']=0; + + with open(o_configfilename, 'wb') as handle: + pickle.dump(settings, handle) + handle.close() + +else: + print "reading from previous configfile \""+o_configfilename+"\":" + + with open(o_configfilename, 'rb') as handle: + settings = pickle.load(handle) + handle.close() + +a_mins=settings['alarmtime']%60 +a_hour=(settings['alarmtime']-a_mins)/60 +print " Alarmtime: %02d:%02d - Starttrack: %d"%(a_hour,a_mins,settings['starttrack']) + +for i in range(0,10): + print " track %d: %02d"%(i,settings["track%d"%i]), + if settings["track%d"%i] and (settings["track%d"%i]-1 < len(allmp3s)): + print " => "+allmp3s[settings["track%d"%i]-1] + else: + print "" + settings["track%d"%i]=0 +writesettings() + + +print "" + +print strftime("Starting: %d %b %Y %H:%M:%S") +sys.stdout.flush() +#------------------------------------------------------------------ +while quit!=True: + count+=1; + sleep(o_loopdelay) + + mytime=time.localtime(time.time()) + currenttime=mytime[3]*60 + mytime[4]; + +# settings['alarmtime']= currenttime + if currenttime == settings['alarmtime']: + if not alarmoff: + looptype=5 + else: + alarmoff=0 + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + if looptype==0: # normal time display + + if but1.WasPressed(): + looptype=1 + if but1.WasLong(): + looptype=2 + + if but2.WasPressed(): + looptype=3 + track=0 + if but2.WasLong(): + looptype=4 + + if colon: + tim="%02d:%02d" % (mytime[3] , mytime[4]) + else: + tim="%02d %02d" % (mytime[3] , mytime[4]) + showclock(tim) + if count % 4 == 0: + colon=1-colon + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + if looptype==1: # show alarmtime + a_mins=settings['alarmtime']%60 + a_hour=(settings['alarmtime']-a_mins)/60 + + if colon: + tim="%02d:%02d" % (a_hour, a_mins) + else: + tim="%02d %02d" % (a_hour, a_mins) + showclock(tim) + colon=1-colon + if showtime > o_showalarmtime: + if but1.WasPressed(): # drop intermediate presses + {} + if but2.WasPressed(): + {} + showtime=0 + looptype=0 + else: + showtime+=o_loopdelay + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + if looptype==2: # edit alarmtime + + delta=0 + sm=settings['alarmtime']%10 + dm=((settings['alarmtime']-sm)/10)%6 + sh=(settings['alarmtime']-dm)/60%10 + dh=(settings['alarmtime']-sh)/600 + + if colon: + tim="%0d%0d:%0d%0d" % (dh,sh,dm,sm) + else: + if but2.WasPressed(): + delta=1 + showtime=0 + + if digit==1: + if delta: + dh+=delta + if dh>2: + dh=0 + if dh==2 and sh>3: + sh=3 + tim=" %0d:%0d%0d" % ( sh,dm,sm) + elif digit==2: + if delta: + sh+=delta + if sh>9: + sh=0 + if dh==2 and sh>3: + sh=0 + tim="%0d :%0d%0d" % (dh, dm,sm) + elif digit==3: + if delta: + dm+=delta + if dm>5: + dm=0 + tim="%0d%0d: %0d" % (dh,sh, sm) + elif digit==4: + if delta: + sm+=delta + if sm>9: + sm=0 + tim="%0d%0d:%0d " % (dh,sh,dm ) + delta=0 + + showclock(tim) + colon=1-colon + + if showtime > o_showtimeout: + showtime=0 + looptype=0 + writesettings() + sys.stdout.write(strftime("%d %b %Y %H:%M:%S : ")) + print "set alarmtime = %d%d:%d%d"%(dh,sh,dm,sm) + sys.stdout.flush() + else: + showtime+=o_loopdelay + + settings['alarmtime']=dh*600+sh*60+dm*10+sm + + if but1.WasPressed(): + showtime=0 + digit+=1 + if digit>4: + digit=1 + + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + if looptype==3: # show tracks + + if but1.WasPressed(): + pygame.mixer.music.set_volume(0) + pygame.mixer.music.stop() + if pygame.mixer.get_init(): + pygame.mixer.quit() + soundpreview=0 + looptype=1 + if but1.WasLong(): + looptype=2 + + if but2.WasPressed(): + if settings["track%d"%track]>0: + print (allmp3s[settings["track%d"%track]-1]) + sys.stdout.flush() + if not pygame.mixer.get_init(): + pygame.mixer.init() + pygame.mixer.music.load(allmp3s[settings["track%d"%track]-1]) + pygame.mixer.music.set_volume(1) + pygame.mixer.music.play() + soundpreview=1 + + tr=track + tl=settings["track%d"%tr]%10 + th=(settings["track%d"%tr]-tl)/10 + + if colon: + tim="%d=:%d%d" % (tr,th,tl) + else: + tim="%d %d%d" % (tr,th,tl) + + tr+=1; + if tr>9: + looptype=0 + showclock(tim) + colon=1-colon + + if soundpreview: + if pygame.mixer.music.get_pos() >o_soundpreviewtime*1000: + pygame.mixer.music.set_volume(0) + pygame.mixer.music.stop() + soundpreview=0 + showtime=0 + track+=1; + if track>9: + looptype=0 + + else: + if showtime > o_showtracktime: + showtime=0 + track+=1; + if track>9: + looptype=0 + + else: + showtime+=o_loopdelay + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + if looptype==4: # edit track + + delta=0 + tr=track + tl=settings["track%d"%tr]%10 + th=(settings["track%d"%tr]-tl)/10 + + if colon: + tim="%d=:%d%d" % (tr,th,tl) + else: + if but2.WasPressed(): + delta=1 + showtime=0 + + if digit==1: + if delta: + tr+=delta + if tr>9: + tr=0 + delta=0 + tl=settings["track%d"%tr]%10 + th=(settings["track%d"%tr]-tl)/10 + tim=" =:%0d%0d" % ( th,tl) + elif digit==2: + if delta: + th+=delta + if th>9: + th=0 + delta=0 + tim="%d=: %d" % (tr, tl) + elif digit==3: + if delta: + tl+=delta + if tl>9: + tl=0 + delta=0 + tim="%d=:%d " % (tr,th ) + delta=0 + + showclock(tim) + colon=1-colon + + settings["track%d"%tr]=th*10+tl + track=tr + + if showtime > o_showtimeout: + showtime=0 + looptype=0 + writesettings() + + else: + showtime+=o_loopdelay + + + if but1.WasPressed(): + digit+=1 + if digit>3: + digit=1 + showtime=0 + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + if looptype==5: # Alarm! +#o_timetomaxvol = 60 # seconds it takes to go from 0% to 100% volume + + if colon: + display.SetBrightnessRaw(0) + tim="%02d:%02d" % (mytime[3] , mytime[4]) + else: + display.SetBrightnessRaw(brightness) + tim="%02d:%02d" % (mytime[3] , mytime[4]) + showclock(tim) + if count % 2 == 0: + colon=1-colon + + if not alarmstarted: + sys.stdout.write(strftime("%d %b %Y %H:%M:%S : ")) + track=settings['starttrack']; + print "[%d] - %02d %s"%(track,settings["track%d"%track],(allmp3s[settings["track%d"%track]-1])) + sys.stdout.flush() + if not pygame.mixer.get_init(): + pygame.mixer.init() + pygame.mixer.music.load(allmp3s[settings["track%d"%track]-1]) + pygame.mixer.music.set_volume(volume) + pygame.mixer.music.play() + track+=1 + if track > 9: + track=0 + if settings["track%d"%track]==0: + track=0 + settings['starttrack']=track; + writesettings() + playtime=0 + alarmstarted=1 + else: + if playtime < o_timetomaxvol: + brightness=(playtime / o_timetomaxvol)*6 + volume = float(int((playtime*100) / o_timetomaxvol))/100 +# print "------> %d / %d => %f - %f"%(playtime,o_timetomaxvol,tvol,volume) + else: + volume=1 + + if pygame.mixer.get_init(): + pygame.mixer.music.set_volume(volume) + + playtime+=o_loopdelay + + if not pygame.mixer.music.get_busy(): + track=settings['starttrack'] + sys.stdout.write(strftime("%d %b %Y %H:%M:%S : ")) + print "[%d] - %02d %s"%(track,settings["track%d"%track],(allmp3s[settings["track%d"%track]-1])) + sys.stdout.flush() + pygame.mixer.music.load(allmp3s[settings["track%d"%track]-1]) + pygame.mixer.music.play() + track+=1 + if track > 9: + track=0 + if settings["track%d"%track]==0: + track=0 + settings['starttrack']=track; + writesettings() + + if playtime > o_maxplaytime: + looptype=0 + playtime=0 + pygame.mixer.music.set_volume(0) + pygame.mixer.music.stop() + if pygame.mixer.get_init(): + pygame.mixer.quit() + display.SetBrightnessRaw(o_defaultbrightness) + + if but1.WasPressed(): + if but1.WasLong(): # Finished + pygame.mixer.music.set_volume(0) + pygame.mixer.music.stop() + if pygame.mixer.get_init(): + pygame.mixer.get_init() + display.SetBrightnessRaw(o_defaultbrightness) + playtime=0 + if but1.WasPressed(): # drop intermediate presses + {} + if but2.WasPressed(): + {} + looptype=0 + alarmoff=1 + + +print "\nQuit!" +display.Clear() +display.cleanup() diff --git a/tm1637.py b/tm1637.py new file mode 100644 index 0000000..3de1807 --- /dev/null +++ b/tm1637.py @@ -0,0 +1,256 @@ +import math +import RPi.GPIO as IO +import threading +from time import sleep, localtime +# from tqdm import tqdm + +# IO.setwarnings(False) +IO.setmode(IO.BCM) + +HexDigits = [0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, + 0x07, 0x7f, 0x6f, 0x77, 0x7c, 0x39, 0x5e, 0x79, 0x71, 0x49, 0x50 ] + +# 0 1 2 3 4 5 6 +# 7 8 9 A B C D E F = r +# +# 0 +# --- +# | | +# 5 | 7 | 1 +# --- +# | | +# 4 | | 2 +# --- +# 3 +# + +ADDR_AUTO = 0x40 +ADDR_FIXED = 0x44 +STARTADDR = 0xC0 +# DEBUG = False + + +class TM1637: + __doublePoint = False + __Clkpin = 0 + __Datapin = 0 + __brightness = 1.0 # default to max brightness + __currentData = [0, 0, 0, 0] + + def __init__(self, CLK, DIO, brightness): + self.__Clkpin = CLK + self.__Datapin = DIO + self.__brightness = brightness + IO.setup(self.__Clkpin, IO.OUT) + IO.setup(self.__Datapin, IO.OUT) + + def cleanup(self): + """Stop updating clock, turn off display, and cleanup GPIO""" + self.StopClock() + self.Clear() + IO.cleanup() + + def Clear(self): + b = self.__brightness + point = self.__doublePoint + self.__brightness = 0 + self.__doublePoint = False + data = [0x7F, 0x7F, 0x7F, 0x7F] + self.Show(data) + # Restore previous settings: + self.__brightness = b + self.__doublePoint = point + + def ShowInt(self, i): + s = str(i) + self.Clear() + for i in range(0, len(s)): + self.Show1(i, int(s[i])) + + def Show(self, data): + for i in range(0, 4): + self.__currentData[i] = data[i] + + self.start() + self.writeByte(ADDR_AUTO) + self.br() + self.writeByte(STARTADDR) + for i in range(0, 4): + self.writeByte(self.coding(data[i])) + self.br() + self.writeByte(0x88 + int(self.__brightness)) + self.stop() + + def Show1(self, DigitNumber, data): + """show one Digit (number 0...3)""" + if(DigitNumber < 0 or DigitNumber > 3): + return # error + + self.__currentData[DigitNumber] = data + + self.start() + self.writeByte(ADDR_FIXED) + self.br() + self.writeByte(STARTADDR | DigitNumber) + self.writeByte(self.coding(data)) + self.br() + self.writeByte(0x88 + int(self.__brightness)) + self.stop() + + def SetBrightness(self, percent): + """Accepts percent brightness from 0 - 1""" + max_brightness = 7.0 + brightness = math.ceil(max_brightness * percent) + if (brightness < 0): + brightness = 0 + if(self.__brightness != brightness): + self.__brightness = brightness + self.Show(self.__currentData) + + def SetBrightnessRaw(self, brightness): + """Accepts raw brightness from 0 - 7""" + if(self.__brightness != brightness): + self.__brightness = brightness + self.Show(self.__currentData) + + def ShowDoublepoint(self, on): + """Show or hide double point divider""" + if(self.__doublePoint != on): + self.__doublePoint = on + self.Show(self.__currentData) + + def writeByte(self, data): + for i in range(0, 8): + IO.output(self.__Clkpin, IO.LOW) + if(data & 0x01): + IO.output(self.__Datapin, IO.HIGH) + else: + IO.output(self.__Datapin, IO.LOW) + data = data >> 1 + IO.output(self.__Clkpin, IO.HIGH) + + # wait for ACK + IO.output(self.__Clkpin, IO.LOW) + IO.output(self.__Datapin, IO.HIGH) + IO.output(self.__Clkpin, IO.HIGH) + IO.setup(self.__Datapin, IO.IN) + + while(IO.input(self.__Datapin)): + sleep(0.001) + if(IO.input(self.__Datapin)): + IO.setup(self.__Datapin, IO.OUT) + IO.output(self.__Datapin, IO.LOW) + IO.setup(self.__Datapin, IO.IN) + IO.setup(self.__Datapin, IO.OUT) + + def start(self): + """send start signal to TM1637""" + IO.output(self.__Clkpin, IO.HIGH) + IO.output(self.__Datapin, IO.HIGH) + IO.output(self.__Datapin, IO.LOW) + IO.output(self.__Clkpin, IO.LOW) + + def stop(self): + IO.output(self.__Clkpin, IO.LOW) + IO.output(self.__Datapin, IO.LOW) + IO.output(self.__Clkpin, IO.HIGH) + IO.output(self.__Datapin, IO.HIGH) + + def br(self): + """terse break""" + self.stop() + self.start() + + def coding(self, data): + if(self.__doublePoint): + pointData = 0x80 + else: + pointData = 0 + + if(data == 0x7F): + data = 0 + else: + data = HexDigits[data] + pointData + return data + + def clock(self, military_time): + """Clock script modified from: + https://github.com/johnlr/raspberrypi-tm1637""" + self.ShowDoublepoint(True) + while (not self.__stop_event.is_set()): + t = localtime() + hour = t.tm_hour + if not military_time: + hour = 12 if (t.tm_hour % 12) == 0 else t.tm_hour % 12 + d0 = hour // 10 if hour // 10 else 0 + d1 = hour % 10 + d2 = t.tm_min // 10 + d3 = t.tm_min % 10 + digits = [d0, d1, d2, d3] + self.Show(digits) + # # Optional visual feedback of running alarm: + # print digits + # for i in tqdm(range(60 - t.tm_sec)): + for i in range(60 - t.tm_sec): + if (not self.__stop_event.is_set()): + sleep(1) + + def StartClock(self, military_time=True): + # Stop event based on: http://stackoverflow.com/a/6524542/3219667 + self.__stop_event = threading.Event() + self.__clock_thread = threading.Thread( + target=self.clock, args=(military_time,)) + self.__clock_thread.start() + + def StopClock(self): + try: + print 'Attempting to stop live clock' + self.__stop_event.set() + except: + print 'No clock to close' + + +if __name__ == "__main__": + """Confirm the display operation""" + display = TM1637(CLK=26, DIO=16, brightness=1.0) + + display.Clear() + + digits = [1, 2, 3, 4] + display.Show(digits) + print "1234 - Working? (Press Key)" + scrap = raw_input() + + print "Updating one digit at a time:" + display.Clear() + display.Show1(1, 3) + sleep(0.5) + display.Show1(2, 2) + sleep(0.5) + display.Show1(3, 1) + sleep(0.5) + display.Show1(0, 4) + print "4321 - (Press Key)" + scrap = raw_input() + + print "Add double point\n" + display.ShowDoublepoint(True) + sleep(0.2) + print "Brightness Off" + display.SetBrightness(0) + sleep(0.5) + print "Full Brightness" + display.SetBrightness(1) + sleep(0.5) + print "30% Brightness" + display.SetBrightness(0.3) + sleep(0.3) + + + display.ShowDoublepoint(False) + display.Show1(0,0x7f); + display.Show1(1,14); + display.Show1(2,17); + display.Show1(3,17); + + # See clock.py for how to use the clock functions!