Python callback issue with GPIO on raspberry - python

I'm an absolute python newbie and this is my first raspberry project. I try to build a simple music player in which every input button loads a different album (8 albums) and 3 buttons to control playback (next, pause, last).
To load the music I use a USB drive which, as soon as it is connected automatically triggers the copy process.
The buttons are debounced with a callback function. Everything works great, except that after new music is loaded with the USB drive the buttons don't work anymore.
Most likely it is a simple programming issue which I - as a beginner - just don't see.
This is the code to work with two buttons:
#!/usr/bin/env python
import RPi.GPIO as GPIO
import os
import pyudev
from time import sleep
from mpd import MPDClient
from socket import error as SocketError
# Configure MPD connection settings
HOST = 'localhost'
PORT = '6600'
CON_ID = {'host':HOST, 'port':PORT}
#Configure Buttons
Button1 = 25
Button2 = 24
GPIO.setmode(GPIO.BCM)
GPIO.setup(Button1, GPIO.IN)
GPIO.setup(Button2, GPIO.IN)
client = MPDClient()
#Function to check if USB is connected
def checkForUSBDevice(name):
res = ""
context = pyudev.Context()
for device in context.list_devices(subsystem='block', DEVTYPE='partition'):
if device.get('ID_FS_LABEL') == name:
res = device.device_node
return res
#Function to load music from USB
def loadMusic(client, con_id, device):
os.system("mount "+device+" /music/usb")
os.system("/etc/init.d/mpd stop")
os.system("rm -r /music/mp3/*")
os.system("cp -r /music/usb/* /music/mp3/")
os.system("umount /music/usb")
os.system("rm /music/mpd/tag_cache")
os.system("/etc/init.d/mpd start")
os.system("mpc clear")
os.system("mpc ls | mpc add")
os.system("/etc/init.d/mpd restart")
#Function to connect to MPD
def mpdConnect(client, con_id):
try:
client.connect(**con_id)
except SocketError:
return False
return True
#Function to load an Album
def loadAlbum(number):
mpdConnect(client, CON_ID)
if client.status()["state"] == "play" or client.status()["state"] == "pause": client.stop()
os.system("mpc clear")
os.system("mpc ls "+str(number)+" | mpc add")
client.play()
client.disconnect()
#Callback Function
def buttonPressed(channel):
if channel == Button1:
print('Button 1 HIT')
loadAlbum(1)
elif channel == Button2:
print('Button 2 HIT')
loadAlbum(2)
def main():
GPIO.add_event_detect(Button1, GPIO.RISING, callback=buttonPressed, bouncetime=200)
GPIO.add_event_detect(Button2, GPIO.RISING, callback=buttonPressed, bouncetime=200)
# This function just creates an endless loop which does
# nothing, in order for the button detection to work
try:
flag = 0
while flag == 0:
device = checkForUSBDevice("MUSIC") # MUSIC is the name of my thumb drive
if flag == 1:
flag = 0
else:
flag = 0
if device != "":
# USB thumb drive has been inserted, new music will be copied
print('USB erkannt, Musik wird kopiert.', device)
loadMusic(client, CON_ID, device)
print('Musik wurde kopiert, USB kann entfernt werden!', device)
while checkForUSBDevice("MUSIC") == device:
sleep(1.0)
print('USB wurde entfernt.')
loadAlbum(1)
except KeyboardInterrupt:
GPIO.cleanup()
if __name__ == "__main__":
main()
Hope anyone can help me on this?
Matthias

Here is what did the trick for me. Probably it's not the best solution but it seams to do the trick.
Only the main function was changed. Changes are highlighted at the beginning of the line with a comment.
def main():
GPIO.add_event_detect(Button1, GPIO.RISING, callback=buttonPressed, bouncetime=200)
GPIO.add_event_detect(Button2, GPIO.RISING, callback=buttonPressed, bouncetime=200)
# This function just creates an endless loop which does
# nothing, in order for the button detection to work
try:
flag = 0
while flag == 0:
device = checkForUSBDevice("MUSIC") # MUSIC is the name of my thumb drive
if flag == 1:
flag = 0
else:
flag = 0
if device != "":
# USB thumb drive has been inserted, new music will be copied
print('USB erkannt, Musik wird kopiert.', device)
# Stop the callback before loading the files from the USB:
GPIO.remove_event_detect(Button1)
GPIO.remove_event_detect(Button2)
loadMusic(client, CON_ID, device)
print('Musik wurde kopiert, USB kann entfernt werden!', device)
while checkForUSBDevice("MUSIC") == device:
sleep(1.0)
print('USB wurde entfernt.')
loadAlbum(1)
# Recall the main function
main()
except KeyboardInterrupt:
GPIO.cleanup()

The reason that the buttons dont work any more is because you are still in the callback function - so it cant get triggered again. The solution is to use the callback function to simply set a flag and have the main waiting loop detect the flag and do the playing. But note that playing is probably blocking so that the main loop will also have stopped for the duration.

Related

Need to take the whole python code into a function that can be access from another python file

import RPi.GPIO as GPIO
import paho.mqtt.client as mqtt
import time
def privacyfunc():
# Pin Definitions:
led_pin_1 = 7
led_pin_2 = 21
but_pin = 18
# blink LED 2 quickly 5 times when button pressed
def blink(channel):
x=GPIO.input(18)
print("blinked")
for i in range(1):
GPIO.output(led_pin_2, GPIO.HIGH)
time.sleep(0.5)
GPIO.output(led_pin_2, GPIO.LOW)
time.sleep(0.5)
mqttBroker="mqtt.fluux.io"#connect mqtt broker
client=mqtt.Client("privacybtn") #create a client and give a name
client.connect_async(mqttBroker)#from the client -connect broker
while True:
client.publish("privacy", x)#publish this random number to the topic called temparature
print("Just published"+str(x)+"to Topc to privacy")#just print random no to topic temparature
break
def main():
# Pin Setup:
GPIO.setmode(GPIO.BOARD) # BOARD pin-numbering scheme
GPIO.setup([led_pin_1, led_pin_2], GPIO.OUT) # LED pins set as output
GPIO.setup(but_pin, GPIO.IN) # button pin set as input
# Initial state for LEDs:
GPIO.output(led_pin_1, GPIO.LOW)
GPIO.output(led_pin_2, GPIO.LOW)
GPIO.add_event_detect(but_pin, GPIO.FALLING, callback=blink, bouncetime=10)
print("Starting demo now! Press CTRL+C to exit")
try:
while True:
x=GPIO.input(18)
print(x)
# blink LED 1 slowly
GPIO.output(led_pin_1, GPIO.HIGH)
time.sleep(2)
finally:
GPIO.cleanup() # cleanup all GPIOs
if __name__ == '__main__':
main()
need to take this whole code into a function that can be accessed from another python file.plz, help me with this coding part.
In the new python file call import module where module is the name of the file. Like led_blink.py would be import led_blink
Then you can call the methods like so:
led_blink.blink(channel)
This is assuming the files are in the same folder.
Easiest way that I do this is to take a .py file and put it in the same directory you created the python script you are working with. From there you can do the following code from The_Python_File import *
Additionally note that the "The_Python_File" is the name of the .py file that contains the function you are trying to call, but you do not include .py when importing the file.

Openhab2 exec binding to external rpi using pigpio and gpiozero

Using openhab2 on machine A. Machine B is a RPi which controls a relay.
Using pigpio and gpiozero from machine a to control machine b gpio pins.
Using below script for testing. How can I rewrite this so that the on/off function in openhab will work? as of now it just loops between on and off.
help a noob please
#!/usr/bin/python
# https://gpiozero.readthedocs.io/en/stable/
# https://gpiozero.readthedocs.io/en/stable/api_output.html#outputdevice
import sys
import time
import gpiozero
relay = gpiozero.OutputDevice(18, active_high=False, initial_value=False)
def set_relay(status):
if status:
print("Setting relay: ON")
relay.on()
else:
print("Setting relay: OFF")
relay.off()
def toggle_relay():
print("toggling relay")
relay.toggle()
def main_loop():
while 1:
# then toggle the relay every second until the app closes
toggle_relay()
# wait a second
time.sleep(1)
if __name__ == "__main__":
try:
main_loop()
except KeyboardInterrupt:
# turn the relay off
set_relay(False)
print("\nExiting application\n")
# exit the application
sys.exit(0)

raspberry pi addEvent. Runtime error:Failed to add edge detection

I am writing python code on raspberry pi 3. I am registering an event on input channel 21, to check moisture detection. I am getting this error Runtime error:Failed to add edge detection.
My code is:
import RPi.GPIO as GPIO
import sys,os
import time
import datetime
channel = 21
led_output = 18
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
GPIO.setup(channel, GPIO.IN)
GPIO.setup(led_output, GPIO.OUT)
def callback(channel):
filehandle = open("output.txt", "w") or die ("Could not write out")
if GPIO.input(channel) == 1:
print ("Water Detected!")
filehandle.write("1")
GPIO.output(led_output, GPIO.LOW)
else:
print ("Water Not Detected!")
filehandle.write("0")
GPIO.output(led_output, GPIO.HIGH)
filehandle.close()
GPIO.add_event_detect(channel, GPIO.BOTH, bouncetime=300)
GPIO.add_event_callback(channel, callback)
print(GPIO.input(channel))
while True:
time.sleep(5)
When I reboot the Raspberry and run your code it works perfect.
Only after killing the process or CTRL-C keyboard interrupting and running it again the problem/error occurs. I think this has to do with the fact that you exit the program without cleaning up properly...
I got it working in case you exit the running the program with CTRL-C with the code below in which I included a GPIO.cleanup()
However...this unfortunately it does not cover the situation in which you simply kill the running programm...In that case you still need to reboot.
So there is room for improvement.
Please re-insert your own file management commands again.
import RPi.GPIO as GPIO
import sys,os
import time
import datetime
channel = 21
led_output = 18
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
GPIO.setup(channel, GPIO.IN)
GPIO.setup(led_output, GPIO.OUT)
def callback(channel):
if GPIO.input(channel) == 1:
print ("Water Detected!")
GPIO.output(led_output, GPIO.LOW)
else:
print ("Water Not Detected!")
GPIO.output(led_output, GPIO.HIGH)
GPIO.add_event_detect(channel, GPIO.BOTH, bouncetime=300)
GPIO.add_event_callback(channel, callback)
print(GPIO.input(channel))
try:
while True:
#main loop here with some (dummy) code
eg_set_a_dummy_variable = 0
except KeyboardInterrupt:
# here you put any code you want to run before the program
# exits when you press CTRL+C
print ("Program interrupted by CTRL-C")
except:
# this catches ALL other exceptions including errors.
# You won't get any error messages for debugging
# so only use it once your code is working
print ("Other error or exception occurred!")
finally:
# this ensures a clean exit and prevents your
# error "Runtime error:Failed to add edge detection"
# in case you run your code for the second time after a break
GPIO.cleanup()
# credits to:
# https://raspi.tv/2013/rpi-gpio-basics-3-how-to-exit-gpio-programs-cleanly-avoid-warnings-and-protect-your-pi
It is not very clean solution, but you can call GPIO.cleanup() at the start of your script too for case when your process was killed before and GPIO.cleanup() was not called.

PIR sensor with Raspberry Pi

My project is to take the reading from the PIR sensor and play a song when the person is in front of the sensor but I cannot figure the logic behind this code that I found online and tried modifying it.
What I need to do is:
How do I loop this, omxp.poll() doesn't work :(
Edit: Now It stops but is there a way to loop the process and is there a way to make the script memory efficient
Here is the code:(updated)
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#from subprocess import Popen
from omxplayer import OMXPlayer
import RPi.GPIO as GPIO
import time
import subprocess
GPIO.setmode(GPIO.BCM)
PIR_PIN = 7
GPIO.setup(PIR_PIN, GPIO.IN)
song = OMXPlayer('/home/pi/5Seconds.mp3')
try:
print ("Pir Module Test (CTRL+C to exit)")
time.sleep(2)
print("Ready")
active = False
while True:
time.sleep(2)
if GPIO.input(PIR_PIN):
time.sleep(1)
print("Motion detected")
if not active:
active = True
print("Music started")
song.play()
time.sleep(10)
elif active:
print("No motion detected, stop the music")
song.pause()
song.can_control(song)
active = False
if active and song.poll() != None: # detect completion to allow another start
print("Music finished")
active = False
except KeyboardInterrupt:
print ("Quit")
GPIO.cleanup()
Based on your original code, try the following, I have made a few minor changes to the way your script works:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from subprocess import Popen
import RPi.GPIO as GPIO
import time
GPIO.setmode(GPIO.BCM)
PIR_PIN = 7
GPIO.setup(PIR_PIN, GPIO.IN)
song_path = '/home/pi/Hillsong.mp3'
try:
print ("Pir Module Test (CTRL+C to exit)")
time.sleep(2)
print("Ready")
active = False
while True:
if GPIO.input(PIR_PIN):
print("Motion detected")
if not active:
active = True
print("Music started")
omxp = Popen(['omxplayer', song_path])
elif active:
print("No motion detected, stop the music")
omxp.terminate()
active = False
if active and omxp.poll() != None: # detect completion to allow another start
print("Music finished")
active = False
time.sleep(5)
except KeyboardInterrupt:
print ("Quit")
GPIO.cleanup()
Note:
while True means loop forever, as such the time.sleep(10) following it would never be executed.
while False will never execute what is inside it, so omxp.terminate() would never be executed.
Use a variable active to indicate if the player is running to avoid multiple starts.
I do not have a Pi to hand so it has not been tested.

Raspberry Pi crash during python program (OR how is it possible to kill Linux???)

I build a keypad activated sensor with a Raspberry Pi and Python. Everything seems to go well but after a few minutes and several keypad entries, the Pi has a total crash and switches off immediately - no error messages pop up.
The script will continously wait for a keypad entry and if the code is correct, switch on a sensor, if it's not you have to try again. If the sensor is activated you have to put in the correct numbers to avoid an alarm being triggered after 30s)
Could anyone point me in the direction of what might be the problem? Here are the things I've tried so far without success
1) Exchange Pi to new Pi 2
2) Different OS, both NOOBS and Raspbian Wheezy
3) Different sensor (accelerometer vs IR sensor)
4) Disconnect monitor, keyboard and use SSH connection via Cygwin
5) Get log file until crash - log file was empty after reboot
python bad_script &> full_log.txt
6) Different log file command: causes instant crash and is also empty after reboot:
python script.py >> /logdir/script.py.log 2>&1
The question is: how am I able to crash Linux? If it's a memory problem, isn't there a prevention in linux to stop processed before they appear?
Here is the full script I am running:
import sys
from time import sleep
import threading
import signal
from matrix_keypad import RPi_GPIO1
import RPi.GPIO as GPIO
import smbus
import time
passcode = [1,2,3,4] # this is the correct code you have to enter
kp = RPi_GPIO1.keypad(columnCount = 3)
alarm_active = threading.Event() # shared variable used to control the sensor monitoring thread
alarm_active.clear() # Clear the alarm initially
monitor_thread = None # Global variable used to store the monitoring thread
#Set up all the pins correctio
GPIO.setmode(GPIO.BCM)
PINIR=7
GPIO.setup(7, GPIO.IN) # infrad-sensor
#Now activate the kaypad and listen for keypad inputs
def digit():
r = None
while r == None:
r = kp.getKey()
return r
def get_keycode():
# Function to loop around reading 4 keypresses in a row
# Compare against chosen code
# If match, switch the alarm state
entered = []
while len(entered) < 4:
key = digit()
sleep(0.5)
print key
entered.append( key )
if entered == passcode:
entered = []
print "Code correct"
switch_alarm_state()
else:
# Just clear the keypad buffer if the wrong code went in
# Could say "BAD CODE!" here, or even force immediate alarm perhaps
print "Wrong Code - Try again"
GPIO.output(27, True) # let red LED blink as indicator that code was wrong
time.sleep(1)
GPIO.output(27, False)
entered = []
def switch_alarm_state():
# Function to control the state of the alarm
# If the alarm should be on, run a thread monitoring the sensor
# If the alarm should be off, make sure the thread is stopped
global monitor_thread
if alarm_active.is_set():
# If the alarm is currently set, stop it
print "Alarm was abolished"
GPIO.output(17, False) #switch green LED off
alarm_active.clear() # Signals to the monitor thread that we wish to shut it down
monitor_thread.join() # Wait until the monitor thread has stopped
else:
# Alarm is not currently set, so start the sensor monitor thread going
print "Alarm was activated"
GPIO.output(17, True)
monitor_thread = threading.Thread( target=sensor_monitor )
alarm_active.set()
monitor_thread.start()
def sensor_monitor():
# Function to be run in a separate thread, monitoring the sensor
alarm_timer = None # Variable to hold a timer object that will fire the alarm
while alarm_active.is_set():
#print xrota
if GPIO.input(PINIR):
print "Alarm has been triggered"
if alarm_timer is None:
alarm_timer = threading.Timer( 30.0, fire_alarm )
alarm_timer.start()
sleep(0.5)
# The alarm must have been deactivated to get here
# Stop any outstanding alarms and shutdown
if alarm_timer is not None:
alarm_timer.cancel()
return
def fire_alarm():
# Here you implement the actions to be carried out by an alarm
print "Alarm is send to server"
msg = "Alarm"
publish.single("alarm/demo",msg, hostname="52.17.194.125")
def shutdown_handler( signum, frame ):
# Shut down the child thread nicely
alarm_active.clear()
monitor_thread.join()
if __name__ == '__main__': # This is the Python way to check you are being directly run, and not just imported by another script
signal.signal( signal.SIGINT, shutdown_handler ) # If you stop the script with ctrl+C, make sure it shuts down properly
signal.signal( signal.SIGTERM, shutdown_handler )
try:
while True:
get_keycode()
except KeyboardInterrupt:
GPIO.cleanup()
print "Quit program"

Categories