How can keep a timed count of user input (from a button)? - python

I'm using a Raspberry Pi 2 and a breadboard to make a morse code interpreter. The circuit is a simple button that lights up an LED when pressed. How could I use functions to count how long the button is held down?

You haven't given sufficient details of your circuit to pin this down to your particular use case, so I'll suggest the following (on which I'll base my code):
Connect the LED (with series resistor) to GPIO 12
Connect a 10k resistor between GPIO 11 and the 3.3V rail (acts as a pull-up)
Connect the push-button between GPIO 11 and Ground (0V).
I'd then write the code to monitor the pin and log the time for each press into a list. You can read out the values in the same order which allows you to process the times and interpret the code from them.
import RPi.GPIO as GPIO
import time
# set up an empty list for the key presses
presses = []
# set up the GPIO pins to register the presses and control the LED
GPIO.setmode(GPIO.BOARD)
GPIO.setup(12, GPIO.OUT)
GPIO.setup(11, GPIO.IN)
# turn LED off initially
GPIO.output(12, False)
while True:
# wait for input to go 'low'
input_value = GPIO.input(11)
if not input_value:
# log start time and light the LED
start_time = time.time()
GPIO.output(12, True)
# wait for input to go high again
while not input_value:
input_value = GPIO.input(11)
else:
# log the time the button is un-pressed, and extinguish LED
end_time = time.time()
GPIO.output(12, False)
# store the press times in the list, as a dictionary object
presses.append({'start':start_time, 'end':end_time})
You'll finish up with a list that looks something like this:
[{start:123234234.34534, end:123234240.23482}, {start:123234289.96841, end:123234333.12345}]
The numbers you see here are in units of seconds (this is your system time, which is measured in seconds since the epoch).
You can then use a for loop to run through each item in the list, subtract each end time from the start time, and of course subtract the start of the next press from the end of the previous one, so that you can assemble characters and words based on the gaps between presses. Have fun :-)

Related

GPIO event detect not giving output when button pressed

The following python script is supposed to wait for a button press, print a button press message, and then exit.
However, when I press the button nothing is printed. Then when I press the enter key, the script prints "Button push detected" then stops.
How might I fix this code so that 'Button push detected' is printed when I push the button?
I followed a tutorial to make this code:
#Button input detection
#by
#Start date: 11th February 2021
#End date: 11th February 2021
#Importing GPIO andtime libraries as per usual with a python script making use of RPi GPIO
import RPi.GPIO as GPIO
import time
#Callback function to print 'Button push detected' when called
def button_callback(channel):
print("Button push detected")
#Disabling annoying GPIO warnings and setting GPIO mode to board
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BOARD)
GPIO.setup(11, GPIO.IN, pull_up_down=GPIO.PUD_UP)
#GPIO event detector that detects a rising edge on pin 11
GPIO.add_event_detect(11,GPIO.FALLING,callback=button_callback)
#Command that does not stop until someone presses enter
quit = input("Press enter to quit")
#Clean up the pins used
GPIO.cleanup()
The problem is that your script needs a loop that waits until the button is pressed so that when the button press is detected, your script will be able to react to the event.
Right now your script sets the event detection but then sits waiting for input and as soon as the input is made, your script exits.
See this Raspberry Pi forum post, https://www.raspberrypi.org/forums/viewtopic.php?t=201747
which has a program for lighting an LED on and off with button presses.
However it looks to me like you would probably need to make a change to the call back function to raise an exception rather than light the LED if you want the script to end when you press the button.
See below the modified program with some additional annotation along with the LED light/unlight commented out.
However this modified program uses a busy loop that just runs continuously until the button is pressed. Using a busy loop, while it works for simple programs such as yours, is generally not a good idea.
An alternative is to use the GPIO.wait_for_edge() function that will pause the script until the button is pressed. See Raspberry Pi StackExchange - Pausing code execution till a button is pressed which removes the busy loop.
First the busy loop version.
import time
import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BCM)
btn_input = 4; # button to monitor for button presses.
LED_output = 17; # LED to light or not depending on button presses.
# GPIO btn_input set up as input.
GPIO.setup(btn_input, GPIO.IN)
# GPIO.setup(LED_output, GPIO.OUT)
# handle the button event
def buttonEventHandler_rising (pin):
# turn LED on
# GPIO.output(LED_output,True)
raise Exception('button pressed')
def buttonEventHandler_falling (pin):
# turn LED off
# GPIO.output(LED_output,False)
raise Exception('button released')
# set up the event handlers so that when there is a button press event, we
# the specified call back is invoked.
# for your purposes you may only want to detect the falling
# indicating that the button was released.
# GPIO.add_event_detect(btn_input, GPIO.RISING, callback=buttonEventHandler_rising)
GPIO.add_event_detect(btn_input, GPIO.FALLING, callback=buttonEventHandler_falling)
# we have now set our even handlers and we now need to wait until
# the event we have registered for actually happens.
# This is an infinite loop that waits for an exception to happen and
# and when the exception happens, the except of the try is triggered
# and then execution continues after the except statement.
try:
while True : pass
except:
GPIO.cleanup()
See as well the explanations as to what is happening in these forum posts
https://www.raspberrypi.org/forums/viewtopic.php?t=128510
https://www.raspberrypi.org/forums/viewtopic.php?t=141520
And this article about exceptions in python. https://realpython.com/python-exceptions/

Moisture detection not resetting

I have a friend setting up an irrigation system at his house. He is telling me it won't detect moisture after the first run-through, regardless of whether there is moisture or not. In his own words:
"I’m trying to figure out how to make my program restarts the detection process. Like say my sensor is in soil and it detects water then after a certain period I need it to run again. My problems is that when I run it again and it’s sitting in a cup of water, it’s doesn’t detect the water. I read something about maybe checking the state of the sensor but I couldn’t get anywhere.
Also would I would the delay function to continuously run it in intervals."
He sent me his code today, and I would like to help him, although I am new to Python. Here is the code.
#!/usr/bin/python
import RPi.GPIO as GPIO
import time
#GPIO SETUP
channel = 20
GPIO.setmode(GPIO.BCM)
GPIO.setup(channel, GPIO.IN)
def callback(channel):
if GPIO.input(channel)==0:
print (" Water Detected!")
else:
print ( 'NO Water Detected!')
GPIO.add_event_detect(channel, GPIO.FALLING, bouncetime=300) # let us know when the pin goes HIGH or LOW
GPIO.add_event_callback(channel, callback) # assign function to GPIO PIN, Run function on change
# infinite loop
while True:
time.sleep(1)
import RPi.GPIO as GPIO # Import Raspberry Pi GPIO library
from time import sleep # Import the sleep function from the time module
GPIO.setwarnings(False) # Ignore warning for now
GPIO.setmode(GPIO.BOARD) # Use physical pin numbering
GPIO.setup(3, GPIO.OUT, initial=GPIO.LOW) # Set pin 8 to be an outputpin and set initial value to low (off)
while True: # Run forever
GPIO.output(3, GPIO.HIGH) # Turn on
sleep(1) # Sleep for 1 second
GPIO.output(3, GPIO.LOW) # Turn off
sleep(1) # Sleep for 1 second

Raspberry pi servo doesn't stop

So I'm trying to use a servo (Doman s0306d) with the pi camera, tried running this script I found to test the motor, it starts running but doesn't stop unless I manually unplug it from the breadboard.
import RPi.GPIO as IO # calling for header file for GPIO’s of PI
import time # calling for time to provide delays in program
IO.setwarnings(False) # do not show any warnings
IO.setmode (IO.BCM) # programming the GPIO by BCM pin numbers. (like PIN29 as‘GPIO5’)
IO.setup(19,IO.OUT) # initialize GPIO19 as an output
p = IO.PWM(19,50) # GPIO19 as PWM output, with 50Hz frequency
p.start(7.5) # generate PWM signal with 7.5% duty cycle
time.sleep(4)
for x in range(0,5): # execute loop forever
p.ChangeDutyCycle(7.5) # change duty cycle for getting the servo position to 90º
time.sleep(1) # sleep for 1 second
p.ChangeDutyCycle(12.5) # change duty cycle for getting the servo position to 180º
time.sleep(1) # sleep for 1 second
p.ChangeDutyCycle(2.5) # change duty cycle for getting the servo position to 0º
time.sleep(1) # sleep for 1 second
p.ChangeDutyCycle(0)
p.stop()
IO.cleanup()
Any ideas ? Thank you.
[EDIT] The servo you are using is a "continuous" servo - so you need to give it the zero speed or "STOP" setting pulse width of 1500us (according to the website http://www.domanrchobby.com/content/?150.html). I haven't used PWM on the pi but if the percentages are of the 50Hz pulse rate (20ms interval) then that should be your 7.5% centre value. You need to make sure the servo gets that pulse before your code exits.
[Original] You are setting the duty cycle to 0 when you exit, which will probably mean the servo doesn't get any pulses. Some servos will stop sometime after they don't get pulses, but some servos (particularly digital servos, but not all) will continue to try achieve the setting from the last pulse they received. Suggest you leave the setting at the mid range 7.5 which you know the servo can achieve and delay for a little while before cleaning up.

Lighting program - GPIO on/off on raspberry pi runtimewarning

Hello i have been trying to work this one out by myself for over a week now but i think its time to ask the question.
This is my first program with python and i intend to control my aquarium with various functions.
The first function i am programming is a lighting schedule (shortened the code a bit for this post)
The code executes okay but the GPIO pin 2 isnt turning on and off properly. and im also getting a "runtimewarning this channel is already in use"
Can i please have a bit of guidance, im sure its something simple and noob :)
here is my code
#Lighting Program
import RPi.GPIO as GPIO
import datetime
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
#Declare Lighting On/Off Times
on_time_Monday = 600
off_time_Monday = 2330
rest of the days here
#Find out what day of the week it is
day = datetime.datetime.now()
day_of_week = day.isoweekday()
#find out what time it is
Current_time = datetime.datetime.strftime(datetime.datetime.now(),'%H%M')
#convert the time to an int so it can be compared
Current_time_INT = int(Current_time)
#Schedule on / off
if (day_of_week == 1) and (Current_time_INT > on_time_Monday and Current_time_INT < off_time_Monday) :
Light_on_off = 'on'
Using 'elif' for tues wed etc etc
else :
Light_on_off = 'off'
Now enable the outputs
#CALL OUTPUTS ON / OFF
GPIO.setup(2, GPIO.OUT)
if Light_on_off == 'on':
GPIO.output(2, GPIO.HIGH)
else:
GPIO.output(2, GPIO.LOW)
GPIO.cleanup()
In my experience "runtimewarning this channel is already in use" comes up when you have previously setup the GPIO pin, but haven't called
GPIO.cleanup().
I see that in your code, but I would suspect that for some reason it is not actually running.
Sometimes it is helpful to test out a GPIO pin in the terminal by running the python interpreter. It is especially useful to test if you have wired your circuit correctly.
>>>import RPi.GPIO as GPIO
>>>GPIO.setmode(GPIO.BCM)
>>>GPIO.setwarnings(False)
>>>GPIO.setup(2, GPIO.OUT)
>>>GPIO.output(2, GPIO.HIGH)
>>>GPIO.input(2)
True
>>>GPIO.output(2, GPIO.LOW)
>>>GPIO.input(2)
False
>>>GPIO.cleanup()
If your circuit is wired correctly you should be able to turn your light on and off this way. When you call GPIO.input(2) the interpreter responds with the current state of the pin. This enables you to make sure the pin is working even without a working external circuit. Once you know that it is working you can move on from there.
One simple way to flip a light switch on and off would be to use cron jobs. I have used this method successfully in the past.
You can write two short scripts, one that turns the light on, and one that turns it off.
Example: lightOn.py (replace 'HIGH' with 'LOW' for lightOff.py)
#!/usr/bin/env python
import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
GPIO.setup(2, GPIO.OUT)
GPIO.output(2, GPIO.HIGH)
GPIO.cleanup()
Now you can create a crontab to automatically run the script at the times you want.
For example:
0 6 * * * /home/pi/lightOn.py
0 18 * * * /home/pi/lightOff.py
Would turn the light on everyday at 6AM and off at 6PM

How to generate sound signal through a GPIO pin on BeagleBone

I am looking for pointers/tips on how to generate a synthesized sound signal on the BeagleBone akin to watch the tone() function would return on a Arduinos. Ultimately, I'd like to connect a piezo or a speaker on a GPIO pin and hear a sound wave out of it. Any pointers?
This is how I managed to solve this question on the Beaglebone using Python and PyBBIO:
#!/usr/bin/python
# Circuit:
# * A Piezo is connected to pin 12 on header P8. - GPIO1_12
# * A LED is connected to pin 14 on header P8. - GPIO0_26
# * A button is connected to pin 45 on header P8. - GPIO2_6
# Use a pull-down resistor (around 10K ohms) between pin 45 and ground.
# 3.3v for the other side of the button can be taken from pins 3 or 4
# on header P9. Warning: Do not allow 5V to go into the GPIO pins.
# * GND - pin 1 or 2, header P9.
def setup(): # this function will run once, on startup
pinMode(PIEZO, OUTPUT) # set up pin 12 on header P8 as an output - Piezo
pinMode(LED, OUTPUT) # set up pin 14 on header P8 as an output - LED
pinMode(BUTTON, INPUT) # set up pin 45 on header P8 as an input - Button
def loop(): # this function will run repeatedly, until user hits CTRL+C
if (digitalRead(BUTTON) == HIGH):
# was the button pressed? (is 3.3v making it HIGH?) then do:
buzz()
delay(10) # don't "peg" the processor checking pin
def delay(j): #need to overwrite delay() otherwise, it's too slow
for k in range(1,j):
pass
def buzz(): #this is what makes the piezo buzz - a series of pulses
# the shorter the delay between HIGH and LOW, the higher the pitch
limit = 500 # change this value as needed;
# consider using a potentiometer to set the value
for j in range(1, limit):
digitalWrite(PIEZO, HIGH)
delay(j)
digitalWrite(PIEZO, LOW)
delay(j)
if j==limit/2:
digitalWrite(LED, HIGH)
digitalWrite(LED, LOW) # turn it off
run(setup, loop)
Check out this page. From userland (e.g. python) you can use set a pin to high or low by writing to the correct sysfs file in /sys/class/gpio.
The GPIO pins of the AM3359 are low-voltage and with insufficient driver strength to directly drive any kind of transducer. You would need to build a small circuit with a op-amp, transistor or FET to do this.
Once you've done this, you'd simply set up a timer loop to change the state of the GPIO line at the required frequency.
By far the quickest and easiest way of getting audio from this board is with a USB Audio interface.

Categories

Resources