I have a python program running a clock and thermometer (raspberry pi + fourletterphat), see program below. I change between showing time or temperature by clicking a single button (gpio16).
What I need help with:
I want to pause the program during night, 21:00 - 06:00, and clear the display because the light is annoying. With the current code I get the display to clear at time for night to start but it does not start again.
If, during above period, the button is clicked I want the program to run for 10 seconds and then stop/clear display. I simply have no idea how to do this, Not even where start.
Is there an elegant way to do this, preferably by just adding something to the existing program. See below.
I have tried various ways of either clearing the display during night time and/or pausing the program until button is hit (but only during the night time, i want the program running during day to show temperature or time).
I have found many versions on finding it time.now is within my range for night but they seem not to be compatible with starting the program as described in point 2 above. (e.g. if time.now < night_end or time.now >= night_start:)
in code below function bright() sets the brightness AND turns off the display at night start 20:00.
Function night() is my feeble start on the restart display at night time but I have not gotten further.
#! /usr/bin/env python3
#Tempclock working with fourletterphat from pimoroni. One switch to change between temp and clock.
#sets brightness at startup but not continuously.
import glob
import time
import datetime
import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BCM)
GPIO.setup(16, GPIO.IN, pull_up_down=GPIO.PUD_DOWN )
input_state = GPIO.input(16)
import fourletterphat as flp
# Find 1s temp sensor
base_dir = '/sys/bus/w1/devices/'
device_folder = glob.glob(base_dir + '28*')[0]
device_file = device_folder + '/w1_slave'
# times for setting brightness of display:
# d2 and d3 time dim and brighten display
d2 = 18
d3 = 7
# d4 and d5 time to turn off display for nigh
d4 = 20
d5 = 6
butt = 1
# Set brightness
def bright():
todays_date = datetime.datetime.now()
hn = (todays_date.hour)
if hn < d5 and hn > d4:
flp.clear()
flp.show()
elif hn < d2 and hn > d3:
flp.set_brightness(12)
else:
flp.set_brightness(0)
# Define nighttime display off
def night():
todays_date = datetime.datetime.now()
hn = (todays_date.hour)
if hn < d5 or hn => d4:
flp.set_brightness(5)
# define temp and time reading
def read_temp_raw():
f = open(device_file, 'r')
lines = f.readlines()
f.close()
return lines
def read_temp():
lines = read_temp_raw()
while lines[0].strip()[-3:] != 'YES':
time.sleep(0.2)
lines = read_temp_raw()
equals_pos = lines[1].find('t=')
if equals_pos != -1:
temp_string = lines[1][equals_pos+2:]
temp_c = float(temp_string) / 1000.0
temp_f = temp_c * 9.0 / 5.0 + 32.0
return temp_c, temp_f
def display_temp():
# temp = read_temp()[1] # F
temp = read_temp()[0] # C
flp.print_float(temp, decimal_digits=1, justify_right=True)
flp.show()
def display_time():
now = datetime.datetime.now()
str_time = time.strftime("%H%M")
flp.print_number_str(str_time)
flp.show()
# Display time or temp button on gpio pin 16 push button counter "butt" and set
# brightness bright() according to time of day. Function night() turns on display for 10sec if at night, then turns it off.
while True:
bright()
if GPIO.input(16) == 0:
butt = butt + 1
night()
if butt > 2:
butt = 1
if butt == 1:
display_time()
flp.show()
elif butt == 2:
display_temp()
flp.show()
time.sleep(0.2)
I'll start by assuming that your code to display time and read temperature is working correctly. Also, since I don't have a RaspberryPi with me, I'm focusing on how to organise your logic on the parts that I think you are having difficulty. My code to specifically turn the display on or off may be wrong, but I guess you figured that part out already. Since your question is about an elegant way to do this, the solution is obviously opinion-based. What I am proposing here is a simple but sound way to do it.
The first thing to notice is that you want two modes of operation. I'll call them day_mode and night_mode. The behaviour you expect is quite different for each mode:
day_mode: display is always on, when button 16 is pressed, the display should change from temperature to time. It should be active from 6:00 until 21:00
night_mode: display is normally off. When button 16 is pressed, the display should turn on and display something (you didn't specify, but I'll assume it is the temperature). It should be active when day_mode is not active.
When thinking about modes, it usually helps to separate three things:
What you need to do when you enter that mode of operation (for example, when you enter day_mode you should turn on the display and show the clock, when you enter night_mode you should turn off the display)
What is the behaviour you want during the mode. In your case, it is what we discussed above.
Any clean-up action needed when you exit the mode. Here none is needed, so we will skip this part.
So, let's begin! From your original code, I'll keep everything but the functions night, bright and the final loop. The first thing to do is create a function that tells in which mode we should be:
import datetime as dt
def which_mode():
current_hour = dt.datetime.now().time().hour
# In your code you seemed to need special behaviour between 6 and 7 and 18 and 21,
# but since it was not in your question, I'm not including it here
if 6 <= current_hour <= 21:
return 'day_mode'
else:
return 'night_mode'
To represent the modes, I'll use functions. If your problem were more complex, classes with specific enter, exit and during methods and a full blown state machine implementation would be needed. Since your problem is small, let's use a small solution. Each mode is a function, and the fact that you are entering the mode is informed by a parameter is_entering. The function will also receive a signal indicating that the button was pressed (a rising edge, but change according to your implementation). Before that, we will create a function that displays time or temperature based on a parameter:
def display_what(what):
if what == 0: # Time
display_time()
else: # Temperature
display_temp()
display_info = 0 # 0 means time, 1 means temperature
def day_mode(is_entering=False, button_pressed=False):
global display_info # Not very elegant, but gets the job done
if is_entering:
# Here the display is turned on. I got the values from your `bright` function
flp.set_brightness(12)
flp.clear()
# This is the `during` logic. Note that it will be run right after `entering` the first time
if button_pressed:
display_info = (display_info + 1) % 2 # Cycles between 0 and 1
display_what(display_info) # Update the display accordingly
For the night_mode function, we will need to keep a global variable to record when the button was last pressed. By convention, when it is smaller than 0, it means we are not counting the time (so we do not keep "clearing the display" all the time).
time_of_last_press = -1.0
def night_mode(is_entering=False, button_pressed=False):
if is_entering:
# Setup the dark display
flp.clear()
flp.set_brightness(0)
if button_pressed:
# Record when the button was pressed and display something
time_of_last_press = time.time()
flp.set_brightness(4) # I understood from your code that at night the display is less bright
display_what(1) # Temperature? You can use a similar logic from day_mode to toogle the displays
elif ((time.time() - time_of_last_press) > 10.0) and (time_of_last_press >= 0):
flp.clear()
flp.set_brightness(0)
time_of_last_press = -1.0
Finally, the main loop becomes relatively simple: check what should be the next mode. If it is different from the current one, set is_entering as True. Then call the function for the current mode.
# Setup the GPIO to detect rising edges.
GPIO.add_event_detect(16, GPIO.RISING)
current_mode = None # In the first run, this forces the `entering` code of the correct mode to run
while True:
button_pressed = GPIO.event_detected(16)
next_mode = which_mode()
changed = next_mode != current_mode
current_mode = next_mode
if current_mode == 'day_mode':
day_mode(changed, button_pressed)
else: # There are only two modes, but could be an elif if there were more modes
night_mode(changed, button_pressed)
time.sleep(0.2)
You may have noticed that there is some repetition: for example, in night_mode, the code to clear the display is the same code used when entering it, so you could actually call night_mode(True) to run that code again. Most importantly, there are two ifs that are similar: the one in which_mode and the one in the main loop. This can be fixed (and the code made more generic) if we change which_mode to return the function representing the mode, instead of a string. This may be a little more difficult to read if you are not used to functions returning functions, but it is a very flexible way of doing this sort of state machine:
# Must be defined after the functions `day_mode` and `night_mode` are defined
def which_mode():
current_hour = dt.datetime.now().time().hour
# In your code you seemed to need special behaviour between 6 and 7 and 18 and 21,
# but since it was not in your question, I'm not including it here
if 6 <= current_hour <= 21:
return day_mode # Note that this is not a string. It is a function!
else:
return night_mode
# Setup the GPIO to detect rising edges.
GPIO.add_event_detect(16, GPIO.RISING)
current_mode = None # In the first run, this forces the `entering` code of the correct mode to run
while True:
button_pressed = GPIO.event_detected(16)
next_mode = which_mode()
changed = next_mode != current_mode
current_mode = next_mode
current_mode(changed, button_pressed) # Now current_mode is a reference to one of the two mode functions, so we just need to call it.
time.sleep(0.2)
I am trying to make a alarm like app where I want a list of things to happen when its time. But I am facing a bug. Time keeps waiting for previous time instead of going to next time in list.
t1 = dt.time(hour=17,minute=8)
t2 = dt.time(hour=18,minute=48)
timetable = [t1, t2]
for elt in timetable:
i_time = elt
#i_minute = i.minute
while True:
if i_time == dt.datetime.now().time():
#if i_hour == dt.datetime.now().hour and i_minute == dt.datetime.now().minute:
#current_time = tk.Label(text = dt.datetime.now())
#current_time.pack()
#playsound('media/ClassAlarm.mp3')
print("Its time")
break
The function works fine when it comes to t1 but if t1 is passed and current time is higher that t1 it should go to t2 and ring alarm. But it keeps waiting for t1 which will happen next day. it doesn't read t2 unless t1 is processed.
Ex. Current time 1:30 while t1 is 1:25 and t2 is 1:35. It doesn't ring at t2 but keeps waiting for t1 to happen again which has already happened.
I have tried to execute for loop in different way
for elt in timetable:
time = dt.datetime.now().time()
if time - elt < 0:
break
while(True):
if time == elt:
print("you did it")
I have also tried any() method. Which isn't helping exactly as well
current_hour = dt.datetime.now().hour
current_min = dt.datetime.now().minute
alarm = any(i.hour == current_hour and i.minute == current_min for i in timetable)
print(alarm)
I have tried posting question previously but wasn't able to explain properly. Hope this helps
Using == Operator To Compare Time Is Risky, Logically It Should Work But Somehow It's Better To Use <= Operator Which Eventually Compare If Your Time Is Greater Than The One Recorded In List! This Is Lot Safer Than Equality Which Trigger Only Once And Has No Guarantee To Work For A Split
-->Note: I Believe Those Function Generated Timestamp Of Different Format, Although They Represent Time And Are Useful But Since They Are In Different Format You Ain't Getting Equality Operator To Work (Bcz Even for Same Time And Date, Your Timestamp Gonna Be Different Although They Represent Same). To confirm this behavior you can write print variables of t1 and datetime.now and see if they are same.
Regarding Your Second Question You Can Have if/else statements to check for which time has been occured and most last time which has just been crossed, or you can run loop in reverse and check for timer (assuming late timers are in end of loop)
Sample Code:
for elt in timetable.reverse():
i_time = elt
while True:
if i_time <= dt.datetime.now().time():
print("Its time")
break
I'm trying to create a simulation where there are two printers and I find the average wait time for each. I'm using a class for the printer and task in my program. Basically, I'm adding the wait time to each of each simulation to a list and calculating the average time. My issue is that I'm getting a division by 0 error so nothing is being appended. When I try it with 1 printer (Which is the same thing essentially) I have no issues. Here is the code I have for the second printer. I'm using a queue for this.
if printers == 2:
for currentSecond in range(numSeconds):
if newPrintTask():
task = Task(currentSecond,minSize,maxSize)
printQueue.enqueue(task)
if (not labPrinter1.busy()) and (not labPrinter2.busy()) and \
(not printQueue.is_empty()):
nexttask = printQueue.dequeue()
waitingtimes.append(nexttask.waitTime(currentSecond))
labPrinter1.startNext(nexttask)
elif (not labPrinter1.busy()) and (labPrinter2.busy()) and \
(not printQueue.is_empty()):
nexttask = printQueue.dequeue()
waitingtimes.append(nexttask.waitTime(currentSecond))
labPrinter1.startNext(nexttask)
elif (not labPrinter2.busy()) and (labPrinter1.busy()) and \
(not printQueue.is_empty()):
nexttask = printQueue.dequeue()
waitingtimes.append(nexttask.waitTime(currentSecond))
labPrinter2.startNext(nexttask)
labPrinter1.tick()
labPrinter2.tick()
averageWait = sum(waitingtimes)/len(waitingtimes)
outfile.write("Average Wait %6.2f secs %3d tasks remaining." \
%(averageWait,printQueue.size()))
Any assistance would be great!
Edit: I should mention that this happens no matter the values. I could have a page range of 99-100 and a PPM of 1 yet I still get divided by 0.
I think your problem stems from an empty waitingtimes on the first iteration or so. If there is no print job in the queue, and there has never been a waiting time inserted, you are going to reach the bottom of the loop with waitingtimes==[] (empty), and then do:
sum(waitingtimes) / len(waitingtimes)
Which will be
sum([]) / len([])
Which is
0 / 0
The easiest way to deal with this would just be to check for it, or catch it:
if not waitingtimes:
averageWait = 0
else:
averageWait = sum(waitingtimes)/len(waitingtimes)
Or:
try:
averageWait = sum(waitingtimes)/len(waitingtimes)
except ZeroDivisionError:
averageWait = 0
I'm working on a game that is sort of like a simple Galaga-esk game. Although, they only move once in a while (something else I can't get working, but that's something different). So, since they don't move on their own, I want a way to add a timer to the game, but I can't seem to find anything on this. Here's what I have for my timer and the surrounding code to reset for each level so far. Will something like this work where I have it?
from livewires import games, color, random, time
start = time.time()
for items in enemies:
items.destroy()
if Enemy_amount > 0:
display_balloons()
elif Enemy_amount == 0 and (time.time() - start <= 120):
start = time.time()
level += 1
Enemy_amount = load_enemies()
#Just telling the code to load the next group of enemies.
The value of start is going to be a number like 1398354442, which is the number of seconds that have passed since January 1, 1970.
If you want to figure out the elapsed time between two events, you'll need to subtract. Perhaps something like this?
elif Enemy_amount == 0 and (time.time() - start <= 120):
start = time.time() # update start variable to the current time
I am using a raspberry pi, a pi face and a python script to monitor several home sensors. I want to add the sensing wire from the smoke detectors to that list but I need a bit of help with the if statement.
I'm unsure how to tell the if statement to check how long the input has detected the signal. Under 4 seconds disregard (low battery chirp), over 4 seconds (smoke detected) alert me..
Basically I need help writing the if statement below.
if piface.digital_read(0)==0 >= 4 seconds:
# do x
else:
# do y
Do I need a loop and can it be as easy as what I have above? (Coded correctly of course!)
Something like this (untested pseudo-code):
counter = 0
while True: #your main loop
smoke = digital_read() #assume 0 = no alarm, 1 = alarm
if smoke:
counter += 1
else:
counter = 0
if counter >= 4: #there was smoke for the last 4 seconds
call_the_fire_brigade()
time.sleep(1) #wait one second
I guess you probably need some loop.
Well I think a good solution to this would be spawn a separate thread for each detector and then use a blocking for the number with a loop.. like
count = 0
while count < 4:
if piface.digital_read(0) == 0:
count += 1
else: count = 0
sleep(4000)
# ... rest of code ...