I am new to python with the Raspberry Pi. Below is a example program to use the GPIO. My problem is that in my conditional I cannot see to have any variables span to other statements in the block
The entire program is included the example of the problem code is as follows:
Turn on GPIO pin 23
print uptime
NameError: global name 'uptime' is not defined
Any suggestions?
Thank in advance
**************************** python code *****************************************
!/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.setwarnings(False)
GPIO.setmode(GPIO.BCM) # set up BCM GPIO numbering
GPIO.setup(17, GPIO.IN) # set GPIO17 as input (button)
GPIO.setup(23, GPIO.OUT) ## Setup GPIO Pin 23 to OUT
GPIO.output(23,False) ## Turn off GPIO pin 23
Define a threaded callback function to run in another thread when events are detected
def my_callback(channel):
global start_time, uptime
if GPIO.input(17): # if port 17 == 1
end_time = time.time()
#print (end_time)
print "Rising edge detected on 17"
uptime = end_time - start_time
#uptime = int(uptime)
print uptime
GPIO.output(23,False)
## Turn off GPIO pin 2
elif (GPIO.input(17)== 0):
start_time = time.time()
#print (start_time)
print "Falling edge detected on 17"
GPIO.output(23,True)
## Turn on GPIO pin 23
print uptime
when a changing edge is detected on port 17, regardless of whatever
else is happening in the program, the function my_callback will be run
GPIO.add_event_detect(17, GPIO.BOTH, callback=my_callback)
raw_input("Press Enter when ready\n>")
try:
print "When pressed, you'll see: Rising Edge detected on 17"
print "When released, you'll see: Falling Edge detected on 17"
sleep(10) # wait 30 seconds
print "Time's up. Finished!"
print uptime
finally: # this block will run no matter how the try block exits
GPIO.cleanup() # clean up after yourself
If, the first time my_callback gets called, GPIO.input(17) is 1, then you'll set uptime and everything will be fine.
But what happens if the first time it's called, GPIO.input(17) is 0? Then you won't set uptime. And, because this is the first time you've ever called this function, and you don't have any other code that sets uptime, it hasn't been set yet. But you try to print it anyway. So you get a NameError.
You could fix this by, e.g., setting it to an initial value at the start of the problem—e.g.:
def my_callback(channel):
# your existing code
uptime = 0
Of course I'm assuming some reasonable indentation for your code. As pasted, your code is just a mess of IndentationError exceptions and won't do anything, and it's quite possible that your real code has other bugs caused by code not being indented under the right flow control statement.
And, since it looks like you may be mixing tabs and spaces, making indentation errors hard to spot: Get yourself a better text editor, which automatically converts tabs into spaces, auto-indents for you, and/or shows tabs in some visible way. Or at least run your code with the -tt flag to catch tabs as errors so you can fix them.
As a side note, your elif is definitely wasteful, and possibly even broken.
If the if GPIO.input(17): part didn't trigger, then you know the result is 0 (or something else falsey, but since input only returns numbers, it can only be 0). So, why test it again for 0 when it can't be anything else?
But meanwhile, you're not just re-testing a value here unnecessarily, you're actually going out and reading the value again off the hardware. So, if the edge changes from 1 to 0 and back to 1 quickly enough, your first read may return 0, then your second read may return 1, meaning neither of your branches fires. (Plus, by reading, you may have also absorbed the edge trigger so you don't get another callback, although I'm not sure about that.)
You can solve all of these problems by just using else: instead of trying to write an elif that exactly captures the opposite sense of the if.
Related
I am trying to control OMXplayer during playback of a video using a Python script. I am new to Dbus on Python, and am hopefully just missing something simple.
Eventually I want to do this with a motion sensor using the GPIO pins, but first I am just trying to get control over the player. What I need to do is loop a section of the video once a certain time passes, and then jump forward to the 'second' section when the passage is interrupted. I am testing this using a key entry.
My problem is that the 'if' statement within the code does not loop unless there is an interruption from a key being inputted, or any other signal. Any key will cause an interruption, but I want the time-specific 'if' statement in the loop to trigger without any input being entered. I hope what I am saying is clear in the below code, but if not please ask any question and I'll be happy to try to clarify.
I am using Will Price's OMXplayer wrapper: https://github.com/willprice/python-omxplayer-wrapper/
My hardware is a Raspberry Pi 3B+
I have looked at similar questions including those below, but have not found the answer:
How to open and close omxplayer (Python/Raspberry Pi) while playing video?
https://www.raspberrypi.org/forums/viewtopic.php?p=533160
I have cleaned out unnecessary parts of the code so this is a basic functioning version.
from omxplayer.player import OMXPlayer #runs from the popcornmix omxplayer wrapper at https://github.com/popcornmix/omxplayerhttps://github.com/popcornmix/omxplayer and https://python-omxplayer-wrapper.readthedocs.io/en/latest/)
from pathlib import Path
import time
import RPi.GPIO as GPIO #for taking signal from GPIO
import subprocess
import logging
logging.basicConfig(level=logging.INFO)
VIDEO_PATH = Path("VIDEO.m4v")
player_log = logging.getLogger("Player 1")
player = OMXPlayer(VIDEO_PATH, dbus_name='org.mpris.MediaPlayer2.omxplayer1')
player.playEvent += lambda _: player_log.info("Play")
player.pauseEvent += lambda _: player_log.info("Pause")
player.stopEvent += lambda _: player_log.info("Stop")
positionEvent = 12
flyin = 3
flyaway = 15
'''
THE ERROR OCCURS IN THE BELOW FUNCTION!!!
the following function does not run each time
the 'while' loop below is playing, but does run
when there is a key pressed as an 'input'
I want to add a chck so that each time a certain
amount of time passes, the video jumps back
'''
def checktime():
currtime = player.position()
print("current time is " + str(currtime))
if(currtime > 3):
print("currtime is greater than 3")
try:
player.play()
print (" Ready")
while True:
'''
Below is the call for the function 'checktime'
This runs once when the video starts, and only
runs again when a key is entered.
I want this to run on a continuous loop
and call each time the video loops
'''
checktime()
key = input()
if key == 'd': #works perfectly
currtime = player.position()
player.seek(flyin-currtime)
print("player position is" + str(player.position()))
if key == 'a': #works perfectly
currtime = player.position()
player.seek(flyaway-currtime)
if key == 's': #works perfectly
player.play()
'''
in addition to the key inputs above, entering any key
and pressing return will run the checktime()
function. I understand that this is receiving an
input, therefore 'waking' the program up,
but I don't understand why the loop is not working
continuously
'''
# Wait for 10 milliseconds
time.sleep(.01)
except KeyboardInterrupt:
print (" Quit")
# Reset GPIO settings
GPIO.cleanup()
The video is controlled using the 's', 'a' and 'd' keys as inputs, and this works perfectly. However, the 'checktime()' function does not call each time the while loop goes around. My guess is that this is because once the video is playing, it is not looking for anything from the Python program until it receives another input.
I am not sure if I am using DBus correctly. Ideally, I wanted to use the seek_absolute function of omxplayer-wrapper, but I haven't been able to understand this. I have been ploughing ahead on this project and have gotten a better understanding since posting an earlier question (see here: OMXplayer-wrapper and Python - jump to a specific point in video) but I am a bit stumped here.
If anyone can help I would appreciate any suggestions!
I wrote a simple code to test interrupts in Python:
import RPi.GPIO as GPIO
brakepin = 32
brake_value = 0
GPIO.setmode(GPIO.BCM)
GPIO.setup(brakepin, GPIO.IN, pull_up_down = GPIO.PUD_UP) #Brake
def brake_isr(channel):
global brake_value
print ("Entered interrupt")
brake_value = GPIO.input(brakepin)
GPIO.add_event_detect(brakepin, GPIO.RISING, callback = brake_isr)
while True:
print("Brake_value: %d" % (brake_value))
time.sleep(0.025)
I am using interrupts to capture when the brakes are pressed on a scooter and when they are not. Ofcourse, 1 indicates the brakes are pressed and 0 indicates they are not. I have three questions:
In the line:
GPIO.setup(brakepin, GPIO.IN, pull_up_down = GPIO.PUD_UP) what should I put the value of pull_up_down? UP or DOWN. Actually both are working the same way, there is no difference. The brake's default value is 0 (when not pressed) because it is connected to ground.
In the line: GPIO.add_event_detect(brakepin, GPIO.RISING, callback = brake_isr) should I put GPIO.RISING or FALLING? Since I want to detect brake events (i.e. when a brake is being pressed and when it is not) I think I should use RISING only.
One very weird thing is happening:
When I press the brake I get output as:
Brake_value: 1 -> This is as expected. Great!
When I let go off the brake it still remains 1, i.e.:
Brake_value: 1 -> This should be 0
Why is the brake value not coming back to 0 when I let go of the brake
Edit:
I modified my interrupt code block to this:
def brake_isr(channel):
print ("Entered interrupt")
global brake_value
while GPIO.input(brakepin) == 1:
brake_value = GPIO.input(brakepin)
brake_value = 0
Well this solved the problem that I was facing with Question 3. However another weird thing is happening with the output (which was happening even before):
Brake_value: 0
.
.
.
When I press the brakes:
Entered interrupt
Entered interrupt
Entered interrupt
Brake_value: 1
Brake_value: 1
.
.
.
When I let go of the brakes:
Entered interrupt
Entered interrupt
Brake_value: 0
Brake_value: 0
.
.
.
Why is the code entering the interrupt block several times when I press the brakes (as can be seen with the multiple print statement) and why is it entering the interrupt when I let go off the brakes? This is happening for time.sleep(1) or time.sleep(0.025), deosnt matter what the delay is.
It looks like you have an interrupt set to detect rising edges, so when the value changes from 0 to 1, but you have nothing setup to check falling edges which would be when you release the brakes. So it looks to me like you have an interrupt triggered when you push the brakes, set Brake_value = 1... but then you never setup any way to swap it back to 0 so it just stays as a 1 in your stored variable. if you were to measure the physical voltage at the GPIO pin it would change as you pressed the brake, causing the rising edge which triggers your interrupt, however within that interrupt you're just setting Brake_value = GPIOvalue which during the interrupt will always be 1.
Basically the problem is that you're trying to track the status of your GPIO pin, but instead of just reading it's value and printing that, you are setting an internal variable to be equal to the value of the GPIO pin... except you set it high on your interrupt event and then never change it back.
There are a few ways to fix this, you could set another interrupt event to occur on a falling edge for the same pin, which would make it trigger when you make a change from 1 to 0 (when you release the brakes). Additionally you could get rid of brake_value and just replace everywhere you access it with an actual read to the GPIO pin. You could also poll the GPIO pin after the 0 -> 1 interrupt occurs, essentially just having a
while(GPIO == 1) in your interrupt code block which would exit as soon as the pin is set back to 0. There are probably a few other ways to do this but these are what immediately come to mind.
The above essentially answers your questions 3, question 1 is probably a datasheet question, the microcontrollers i've used needed specific values for the pullups to make the GPIO pins do different things, but I think the pi is much more forgiving and if you're not having any issues with either way you set it I wouldn't worry about it... and for question 2 rising edge will trigger an interrupt when the value at that pin goes from 0 to 1, while falling edge triggers the event when going from 1 to 0, so if you have it normally grounded, and it goes high when you pull the brakes, what you want is a rising edge. However if you normally have it tied to vcc and it goes to ground when you pull the brakes, what you want is a falling edge.
As suggested by #Ryan, this is what I did:
def brake_isr(channel):
print ("Entered interrupt")
global brake_value
while GPIO.input(brakepin) == 1:
brake_value = GPIO.input(brakepin)
brake_value = 0
This solves question 3, but it will be great if someone can answer the edit I posted in my question
Concerning your new issue, you might want to look into "switch debounce". This is something that can happen when you push a button and the simple answer is that cheap buttons/switches can result in multiple REALLY fast 0->1->0->1 changes for each press/release... which is why you are getting print outs when you release the button as well. Your sleeps are only pausing the print out of the current brake value, while the interrupt itself triggers it's own print out which is why the sleep duration has no effect on this message. The software fix is to have your interrupt only trigger a certain amount of time after a rising edge is detected if and only if there is no following falling edge (basically if we go from 0->1, make sure it stays at 1 for .5 seconds or something before you actually trigger the event), and then do the same thing for releasing the brake(when we go 1->0, wait 0.5 seconds to make sure it stays at 0). THAT SAID, a software solution isn't what you really want here. Switch Debouncing can be done very easily with two nand gates and a few resistors. https://electrosome.com/switch-debouncing/ has a better explanation with a few really good pictures and if you have the physical components it's the way to go, but if you don't have the actual parts you can cobble together a software solution. The software solution to this problem is sub ideal because it will introduce lag between pushing the brakes and it registering with the arduino, which in some cases you don't really care about but generally for brakes you want them to be very responsive.
I have a Raspberry Pi running an LCD display. I found the Python script below which displays the time on the display, but I notice there's a delay of almost a second between the Pi's time and the LCD.
I tried removing the last line of the code (sleep(1)) and that removed the delay but the LCD flickers a lot.
Can anyone please explain the function of that last line, and whether there's a way to display the time on the LCD without a delay?
Thanks.
#!/usr/bin/python
import RPi.GPIO as GPIO
from Adafruit_CharLCD import Adafruit_CharLCD
from subprocess import *
from time import sleep, strftime
from datetime import datetime
lcd = Adafruit_CharLCD()
lcd.begin(16,1)
GPIO.setup(18, 0)
GPIO.output(18, 1)
while 1:
lcd.clear()
lcd.message(datetime.now().strftime('%b %d %H:%M:%S\n'))
sleep(1)
The sleep(1) instructs the program to "sleep" for 1 second. This is actually very significant because that means that the while loop (which is always true) will then only run once every second. When you take away the sleep(1) line, that means the while loop never takes a break and continues running as fast as the computer can go infinitely. One thing you might try is just reducing the sleep(1) to some smaller value. For example, you can specify sleep(0.1)
By reducing the amount of time it sleeps, that means the clock will update more frequently. Right now with sleep(1) the clock updates once every second, which makes perfect sense. However, you might increase accuracy by having the clock update 10 times every second with sleep(0.1). Let me know if this all makes sense.
The flickering is likely caused by the fact that the program is clearing and repopulating the display so many times a second.
Edit: Documentation suggests that sending decimal values to the sleep() function is valid
Edit 2: An extra bit about how to only refresh the display if right at the turn of a second
from datetime import datetime
from time import sleep, mktime
dti = mktime(datetime.now().timetuple())
while 1:
ndti = mktime(datetime.now().timetuple())
if dti < ndti:
dti = ndti
lcd.clear()
lcd.message(datetime.now().strftime('%b %d %H:%M:%S\n'))
sleep(0.95)
else:
sleep(0.01)
In essence, here's how it works:
When starting the program, create a datetime in integer form (our var dti). By "integer form" I mean add up all the seconds from some arbitrary start date (e.g. 1970-01-01 00:00:00) and use that as a reference for time. For example, today (2016-08-18 00:00:00) might be something like 1471478400 seconds since 1970. Once we have that value, we start our loop.
At the beginning of the loop, we always create a new datetime integer (ndti) to track where we are since the last time we ran the loop. With this information handy, we hop into the if conditional. if our new datetime integer (ndti) has changed fully by one second compared to our old datetime integer (dti) then that means, obviously, one second has passed. Since that is the case, we will now set our reference datetime integer to the datetime now (dti = ndti). Then we display our message (lcd.clear() and lcd.message()). After that we will sleep the program for just under 1 whole second (sleep(0.95)). Since some computers can possibly sleep more than the exact allotted time, this gives us .05 seconds of cushion to be inaccurate. In the event that we are running through the if conditional and a second has not passed yet, we would sleep the program for a short time and continue repeating until a second has actually passed (sleep(0.01)).
If everything goes exactly as planned, then for each second our program should be refreshing the lcd screen only once, and it should also be sleeping for roughly 95% of that second, so that we aren't spinning our wheels for no reason the whole time. Another part to note is that since our else clause tells the program to sleep for 0.01 seconds, that means that, in most cases, our clock can only ever be inaccurate by a margin of 0.01 seconds, which is quite good. This is low enough to be mostly undetectable by humans. Let me know if all this makes sense.
I tested this out via command line (replacing the lcd stuff with simple print statements) and it seemed to line up exactly with another time tracking resource (http://time.is/)
Try it out and see if it works for you.
IT is best way to print date and time on lcd
firstly download or git clone rplcd library for lcd add in upper side of code
import RPLCD
textDate = strftime("%d %A %Y", gmtime())
textTime = strftime("%H:%M:%S", gmtime())
lcd_string(textDate,LCD_LINE_1)
lcd_string(textTime,LCD_LINE_2)
I'm detecting a touch throuth a ttp223b touch sensor via python on a raspberry pi. It works pretty good but i need to wait for a second after a touch detection to prevent more than one execution so i just added a "time.sleep(1)".
The problem is that i also get multiple outputs, they are just time offset to 1 second, it seems that the routine is triggering multiple times at once.
import time
import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BOARD)
GPIO.setup(3, GPIO.IN)
while 1:
if GPIO.input(3) == GPIO.HIGH:
print "touched :3"
time.sleep(1)
Any suggestions how i could solve the issue?
add a sentinal
last_update = 0
while 1:
if time.time() - last_update > 1.5 : #1 and a half seconds
if GPIO.input(3) == GPIO.HIGH:
print "touched :3"
this will allow the GPIO to continue flushing so you dont pick up the old press (at least I think im not entirely sure how GPIO buffers work here)
I am trying to have a script which I trigger via cron to turn a pin low and high based on a temperature, however.. I have having a couple of problems.
1 - When the script starts and it setsup the GPIO pin, it will either pull the pin high or low (depending on paramater) - there doesn't appear to be a way to tell it not to change the current state of the pin.
This is a problem because if the relay is high and the default state is low then the relay will be set low and then could change to high again very quickly after - doing this every minute is pretty hard on what the pin is controlling (same applies if the default state is high).
2 - When the script exits it cleans up the GPIO pins and changes the state of my pin. Ideally if the script turns the pin high then when it exits I want the pin to remain high. If I remove the cleanup function then the next time the script runs it says that the pin was already in use (problem?).
So the script which runs every minute looks like this.
#!/usr/bin/python
import RPi.GPIO as GPIO
import time
import random
from temp import Temp # custom object to handle the temp sensor.
def main():
random.seed()
GPIO.setmode(GPIO.BCM)
PUMP_ON_TEMP = 38
PUMP_OFF_TEMP = 30
GPIO_PIN = 18
try:
t = Temp('28-00000168c492')
GPIO.setup(GPIO_PIN, GPIO.OUT)
current_temp = t.getTemp()
print current_temp
if current_temp > PUMP_ON_TEMP:
GPIO.output(GPIO_PIN, 1)
print "Turning the pump on! %s" % current_temp
if current_temp < PUMP_OFF_TEMP:
GPIO.output(GPIO_PIN, 0)
print "Turning the pump off! %s" % current_temp
except KeyboardInterrupt:
pass
finally:
GPIO.cleanup()
if __name__ == '__main__':
main()
This run every minute via cron. I don't want to use a loop.
I have tried to read the pin as an input first to get current hight/low state but that throws an error saying the pin needs to be setup first....