raspberry pi python pwm continue after program done - python

I have a robot I am working on and am controlling it with PWM. The way I am controlling it is with a script that sets the PWM and then exits. I need it to set the PWM and then keep running. The reason I need it to exit is because I am just calling this script through an ssh connection each time the x and y values change. Normal digital outputs continue after the program exits but PWM doesn't with the way I have it setup.
Here is my code so far. It contains a lot of print statements to try to help me figure out what the problem was.
#filename: setMotors.py
import RPi.GPIO as GPIO
from sys import argv
from time import sleep
MOTOR_EN_1_PIN = 14
MOTOR_A_1_PIN = 15
MOTOR_B_1_PIN = 18
MOTOR_EN_2_PIN = 23
MOTOR_A_2_PIN = 24
MOTOR_B_2_PIN = 25
def mixXY(x, y):
"""
mixes x and y from a joystick to values for a 2 motor drive system
input: x (int or float), y (int or float)
output: (leftMotor (float), rightMotor (float)) tuple
"""
leftMotor = y + x
rightMotor = y - x
return (leftMotor, rightMotor)
def setMotorPWMS(leftMotor, rightMotor):
#left motor
if leftMotor == 0:
print("left motor 0")
GPIO.output(MOTOR_EN_1_PIN, 0)
motor1A.stop()
motor1B.stop()
elif leftMotor < 0:
print("left motor < 0")
GPIO.output(MOTOR_EN_1_PIN, 1)
motor1A.stop()
motor1B.ChangeDutyCycle(abs(leftMotor))
else:
print("left motor else")
GPIO.output(MOTOR_EN_1_PIN, 1)
motor1A.ChangeDutyCycle(leftMotor)
motor1B.stop()
#right motor
if rightMotor == 0:
print("right motor 0")
GPIO.output(MOTOR_EN_2_PIN, 0)
motor2A.stop()
motor2B.stop()
elif rightMotor < 0:
print("right motor < 0")
GPIO.output(MOTOR_EN_2_PIN, 1)
motor2A.stop()
motor2B.ChangeDutyCycle(abs(rightMotor))
else:
print("right motor else")
GPIO.output(MOTOR_EN_2_PIN, 1)
motor2A.ChangeDutyCycle(rightMotor)
motor2B.stop()
GPIO.setwarnings(False)
#setup
GPIO.setmode(GPIO.BCM)
GPIO.setup(MOTOR_EN_1_PIN, GPIO.OUT)
GPIO.setup(MOTOR_A_1_PIN, GPIO.OUT)
GPIO.setup(MOTOR_B_1_PIN, GPIO.OUT)
GPIO.setup(MOTOR_EN_2_PIN, GPIO.OUT)
GPIO.setup(MOTOR_A_2_PIN, GPIO.OUT)
GPIO.setup(MOTOR_B_2_PIN, GPIO.OUT)
motor1A = GPIO.PWM(MOTOR_A_1_PIN, 50)
motor1B = GPIO.PWM(MOTOR_B_1_PIN, 50)
motor2A = GPIO.PWM(MOTOR_A_2_PIN, 50)
motor2B = GPIO.PWM(MOTOR_B_2_PIN, 50)
motor1A.start(0)
motor1B.start(0)
motor2A.start(0)
motor2B.start(0)
if len(argv) <= 2:
print("Need to call with x and y from commandline")
else:
motorPWM = mixXY(int(argv[1]), int(argv[2]))
leftMotorPWM = motorPWM[0]
rightMotorPWM = motorPWM[1]
print("left motor:",leftMotorPWM)
print("right motor:", rightMotorPWM)
setMotorPWMS(leftMotorPWM, rightMotorPWM)
sleep(5)
print("done")
The way it would be called is with sudo python setMotors.py x y. Is there a way of keeping the PWM going after the program exits or a better way of doing this?

The RaspberryPi doesn't support hardware PWM, so it's emulated using a software loop. Basically, it sets the GPIO, sleeps a little, resets the GPIO, sleeps a little and loops. This is done in a separated thread which is killed when the program ends.
Thus, you have to find a way to keep your program alive in the background. If you look at the official example on use of PWM, you'll notice the endless loop that keeps the program alive until manually killed.
You should also add something like
try:
while 1:
time.sleep(0.5)
except KeyboardInterrupt:
pass
p.stop()
GPIO.cleanup()
to the end of your program, or something better constructed using the signal module.
Then, leave the process in the background before destroying the console
sudo python setMotors.py x y &
You could also let your program get daemonized.

Related

Using If statement in python comparing time to a value

I am trying to write a simple code in python which working on the Raspberry pi.
This code is working but I want to make a better code when I turb on LED in different time.
such as 5minutes later turn on the first led and another 5minutes later second led ....
The problem is when I changing the dlay time such as 5minutes or 10minutes, I have to do hard coding and it seems not a good way to do.
I also tried to adding time to timeset but could not find out the solution.
import RPi.GPIO as GPIO
from datetime import datetime
from datetime import timedelta
import time
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)
GPIO.setup(24, GPIO.OUT)
GPIO.setup(6, GPIO.OUT)
GPIO.setup(5, GPIO.OUT)
try:
while True:
timeset = datetime.now().strftime("%H:%M:%S")
print(timeset)
time.sleep(5)
if timeset > "19:11":
GPIO.output(24, True)
print('Please take a medicine')
if timeset > "19:12":
GPIO.output(6, True)
time.sleep(10)
GPIO.output(24, False)
GPIO.output(6, False)
time.sleep(0.30)
#if timeset =="00:23:30":
# timeset = datetime.now() + timedelta(minutes=1)
finally:
print("clean up")
GPIO.cleanup()`
https://discuss.tryton.org/t/typeerror-unsupported-operand-type-s-for-int-and-datetime-timedelta/4966
I have looked this and tried applying on my code but does not work.
You could go with a basic state machine.
Keep just sleep periods. No need anymore to deal with dates.
Example :
import time
i = 0
state = 0
while True:
time.sleep(5)
state = i%3
if state == 0:
print("light on led A")
elif state == 1:
print("light on led B")
elif state == 2:
print("light on led C")
i += 1
i increments forever
At each loop iteration thanks to the modulo function, state changes value in a cyclic way : 0, 1, 2, 0, 1, 2, ...
For each state value it's up to you to do whatever you decide with the RPi leds.

How to control a dc motor using python in raspberry pi with an L298N driver

I m trying to control the speed and to turn off/ON a small motor using python in raspberry pi. The Driver I m using is L298N. Everything works except the motor does not stop when I click "s". It also turn on even before I run the code. I tried to make it stop by setting the duty cycle to zero which works for me, but I trying to find out why it does not turn off when I set both GPIO to LOW to stop it. When I type "s" it stop for half a sec or less then continue to run. This is the code I m using. I have a picture of the wiring as well.
import RPi.GPIO as GPIO
from time import sleep
in1 = 24
in2 = 23
en = 25
temp1=1
GPIO.setmode(GPIO.BCM)
GPIO.setup(in1,GPIO.OUT)
GPIO.setup(in2,GPIO.OUT)
GPIO.setup(en,GPIO.OUT)
GPIO.output(in1,GPIO.LOW)
GPIO.output(in2,GPIO.LOW)
p=GPIO.PWM(en,1000)
p.start(25)
print("\n")
print("The default speed & direction of motor is LOW & Forward.....")
print("r-run s-stop f-forward b-backward l-low m-medium h-high e-exit")
print("\n")
while(1):
x=input()
if x=='r':
print("run")
if(temp1==1):
GPIO.output(in1,GPIO.HIGH)
GPIO.output(in2,GPIO.LOW)
print("forward")
x='z'
else:
GPIO.output(in1,GPIO.LOW)
GPIO.output(in2,GPIO.HIGH)
print("backward")
x='z'
elif x=='s':
print("stop")
GPIO.output(in1,GPIO.LOW)
GPIO.output(in2,GPIO.LOW)
x='z'
elif x=='f':
print("forward")
GPIO.output(in1,GPIO.HIGH)
GPIO.output(in2,GPIO.LOW)
temp1=1
x='z'
elif x=='b':
print("backward")
GPIO.output(in1,GPIO.LOW)
GPIO.output(in2,GPIO.HIGH)
temp1=0
x='z'
elif x=='l':
print("low")
p.ChangeDutyCycle(25)
x='z'
elif x=='m':
print("medium")
p.ChangeDutyCycle(50)
x='z'
elif x=='h':
print("high")
p.ChangeDutyCycle(75)
x='z'
elif x=='e':
GPIO.cleanup()
break
else:
print("<<< wrong data >>>")
print("please enter the defined data to continue.....")

Ending a thread that contains an infinite loop

I'm trying to use python to turn on some RGB lights after motion is detected. My code is below but I can't get the thread to end and switch off the LEDS. All that happening is I get stuck after keyboard interrupt.
Essentially when I run the code it works to detect movement and switch the lights on but then when I try to end the program with a keyboard interupt exception either the program hangs and doesn't stop or it stops but the LEDs don't switch off and stay lit. I've looked at various pages about how to stop a thread running but none of it has helped. Below are the articles I've looked at. I think my issue is that the thread running the lighting pattern loop isn't stopping so when I try to turn each LED off its turned back on immediately but I'm not sure.
I can't get the thread to stop any way I've tried. Any help would be much appreciated.
How to stop a looping thread in Python?
https://www.oreilly.com/library/view/python-cookbook/0596001673/ch06s03.html
https://www.geeksforgeeks.org/python-different-ways-to-kill-a-thread/
Threading an Infinite Loop
Stopping a python thread running an Infinite Loop
Threading infinite loop in python
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Wed Jan 1 10:10:25 2020
#author: thomas
"""
import RPi.GPIO as GPIO
import time
import subprocess
import argparse
from neopixel import *
import datetime as dt
from threading import Thread
from threading import enumerate
from threading import Event as EV
import sys
global stop
GPIO.setmode(GPIO.BCM)
# LED strip configuration:
LED_COUNT = 288 # Number of LED pixels.
LED_PIN = 18 # GPIO pin connected to the pixels (must support PWM!).
LED_FREQ_HZ = 800000 # LED signal frequency in hertz (usually 800khz)
LED_DMA = 10 # DMA channel to use for generating signal (try 10)
LED_BRIGHTNESS = 255 # Set to 0 for darkest and 255 for brightest
LED_INVERT = False # True to invert the signal (when using NPN transistor level shift)
LED_CHANNEL = 0
LED_STRIP = ws.SK6812_STRIP_RGBW
#LED_STRIP = ws.SK6812W_STRIP
# Define functions which animate LEDs in various ways.
def wheel(pos):
"""Generate rainbow colors across 0-255 positions."""
if pos < 85:
return Color(pos * 3, 255 - pos * 3, 0)
elif pos < 170:
pos -= 85
return Color(255 - pos * 3, 0, pos * 3)
else:
pos -= 170
return Color(0, pos * 3, 255 - pos * 3)
def colorWipe(strip, color, wait_ms=20):
"""Wipe color across display a pixel at a time."""
for i in range(strip.numPixels()):
strip.setPixelColor(i, color)
strip.show()
time.sleep(wait_ms/1000.0)
def rainbowCycle(strip, wait_ms=20, iterations=5):
"""Draw rainbow that uniformly distributes itself across all pixels."""
while not stop:
for j in range(256*iterations):
for i in range(strip.numPixels()):
strip.setPixelColor(i, wheel(((i * 256 // strip.numPixels()) + j) & 255))
strip.show()
time.sleep(wait_ms/1000.0)
class RainbowLights(Thread):
def __init__(self, name="RainbowLights"):
self._stopevent = EV()
self.sleeppreiod = 1.0
Thread.__init__(self, name=name)
def run(self):
while not self._stopevent.isSet():
rainbowCycle(strip)
self._stopevent.wait(self._sleepperiod)
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('-c', '--clear', action='store_true', help='clear the display on exit')
args = parser.parse_args()
# Create NeoPixel object with appropriate configuration.
strip = Adafruit_NeoPixel(LED_COUNT, LED_PIN, LED_FREQ_HZ, LED_DMA, LED_INVERT, LED_BRIGHTNESS, LED_CHANNEL, LED_STRIP)
# Intialize the library (must be called once before other functions).
strip.begin()
print ('Press Ctrl-C to quit.')
if not args.clear:
print('Use "-c" argument to clear LEDs on exit')
GPIO.setup(22, GPIO.IN) #PIR
rta = "none"
time1 = "none"
stop = False
RainbowLights = RainbowLights()
try:
time.sleep(2) # to stabilize sensor
while True:
trigger = GPIO.input(22)
if trigger == 1:
print("Motion Detected...")
if rta == "running":
print("Lights Already On")
else:
RainbowLights.start()
rta = "running"
time1 = dt.datetime.now()
time.sleep(5) #to avoid multiple detection
elif trigger == 0:
print("No Motion...")
print("Lights turned on at " + str(time1))
time.sleep(0.1) #loop delay, should be less than detection delay
except KeyboardInterrupt:
RainbowLights._stopevent.set()
stop = True
time.sleep(5)
if args.clear:
colorWipe(strip, Color(0,0,0), 10)
GPIO.cleanup()
Reduced to a minimal example so it can run without hardware. Comments added for fixes. The main problem was stop was only checked after 256*iterations*wait_ms/1000.0 or 25.6 seconds, so stopping appeared unresponsive. A few other bugs as well. Global stop is sufficient to end the thread so the Event wasn't needed.
import time
import argparse
from threading import Thread
import sys
global stop
def rainbowCycle(strip, wait_ms=20, iterations=5):
"""Draw rainbow that uniformly distributes itself across all pixels."""
while not stop:
for j in range(256*iterations):
if stop: break # add stop check here for responsiveness.
print(f'rainbowCycle')
time.sleep(wait_ms/1000.0)
print('stopping...')
class RainbowLights(Thread):
def __init__(self, name="RainbowLights"):
print('__init__')
Thread.__init__(self, name=name)
def run(self):
print('run')
rainbowCycle(strip)
print('stopped')
if __name__ == '__main__':
strip = None
print('Press Ctrl-C to quit.')
rta = "none"
stop = False
rainbow_lights = RainbowLights() # renamed variable so it doesn't replace the class.
try:
time.sleep(2) # to stabilize sensor
while True:
trigger = 1 # pretend some motion
if trigger == 1:
print("Motion Detected...")
if rta == "running":
print("Lights Already On")
else:
rainbow_lights.start()
rta = "running"
time.sleep(5) #to avoid multiple detection
elif trigger == 0:
print("No Motion...")
time.sleep(0.1) #loop delay, should be less than detection delay
except KeyboardInterrupt:
print('KeyboardInterrupt')
print('stop')
stop = True
Press Ctrl-C to quit.
__init__
Motion Detected...
run
rainbowCycle
rainbowCycle
rainbowCycle
rainbowCycle
rainbowCycle
rainbowCycle
rainbowCycle
rainbowCycle
rainbowCycle
rainbowCycle
rainbowCycle
rainbowCycle
KeyboardInterrupt
stop
stopping...
stopped

Thread synchronization between a slow and a fast processing thread in Python

I'm working on automation of a vision based machine driven by stepper motor. Structure of my program is as followed.
import ImageProcessing
import Mechanices
import cv2
def rotate_motor(steps,direction):
global g_motor_ploc
DIR = 20 # Direction GPIO Pin
STEP = 21 # Step GPIO Pin
delay = .00055
GPIO.setmode(GPIO.BCM)
GPIO.setup(DIR, GPIO.OUT)
GPIO.setup(STEP, GPIO.OUT)
GPIO.output(DIR, direction)
for x in range(steps):
GPIO.output(STEP, GPIO.HIGH)
sleep(delay)
GPIO.output(STEP, GPIO.LOW)
sleep(delay)
GPIO.cleanup()
def findImage():
####################################
< Very Fast Image Processing Code >
####################################
return position_of_object
def calculate_steps(position):
####################################
< Very Fast Steps and Direction Calculation Code >
####################################
return steps_required,direction_of_rotation
def main():
while True:
position=findImage()
steps, direction = calculate_steps
rotate_motor(steps,directions) # Very Slow Process
if __name__ == "__main__":
main()
Despite very fast processing of images, whole program has to wait for rotate_motor to complete before processing next frame. In mean time if object moves backward it fails to see the change unless previously calculated position is achieved.
I'm looking forward to run rotate_motor in a separate thread and update the number_of_steps and direction required for motion. So that even if previously calculated position is not achieved, it should know how many more steps are required or change of direction is required.
So I modified the program as followed:
import multiprocessing as mp
import cv2
(r, g_w) = mp.Pipe(True)
g_motor_ploc = 0
def rotate_motor(steps,direction):
global g_motor_ploc
DIR = 20 # Direction GPIO Pin
STEP = 21 # Step GPIO Pin
delay = .00055
GPIO.setmode(GPIO.BCM)
GPIO.setup(DIR, GPIO.OUT)
GPIO.setup(STEP, GPIO.OUT)
GPIO.output(DIR, direction)
for x in range(steps):
GPIO.output(STEP, GPIO.HIGH)
sleep(delay)
GPIO.output(STEP, GPIO.LOW)
sleep(delay)
if direction==1:
g_motor_ploc -= 1
else:
g_motor_ploc += 1
GPIO.cleanup()
def findImage():
####################################
< Very Fast Image Processing Code >
####################################
return position_of_object
def calculate_steps(position):
####################################
< Very Fast Steps and Direction Calculation Code >
####################################
return steps_required,direction_of_rotation ## (Clockwise, Counter Clockwise)
def linear_motion(r):
global g_motor_ploc
while True:
loc = r.recv()
steps = loc - g_motor_ploc
if steps < 0:
direction = 1
else:
direction = 0
rotate_motor(abs(steps),direction)
def main():
while True:
position=findImage()
steps = calculate_steps
g_w.send(steps)
if __name__ == "__main__":
motor_proc = mp.Process(target=linear_motion, args=(r,))
motor_proc.daemon = True
motor_proc.start()
main()
Using the mp.Pipe() it interrupts the linear_motion() as soon as new position is send to the pipe. However this process if so fast that linear_motion never reach to the rotate_motor part.
Any suggestions, I'm confused.

Combining two python codes for raspberry pi with a servo and ldr sensor

I have Python code running on my raspberry pi 2b and a light sensor, which measures the amount of time it takes for the capacitor of the light sensor to charge and send the pin high:
import RPi.GPIO as GPIO
import time
GPIO.setmode(GPIO.BOARD)
pin_to_circuit = 7
def rc_time (pin_to_circuit):
count = 0
#Output on the pin for
GPIO.setup(pin_to_circuit, GPIO.OUT)
GPIO.output(pin_to_circuit, GPIO.LOW)
time.sleep(0.1)
#Change the pin back to input
GPIO.setup(pin_to_circuit, GPIO.IN)
#Count until the pin goes high
while (GPIO.input(pin_to_circuit) == GPIO.LOW):
count += 1
if count > 1000000:
return True
else:
return count
#Catch when script is interrupted, cleanup correctly
try:
# Main loop
while True:
print rc_time(pin_to_circuit)
except KeyboardInterrupt:
pass
finally:
GPIO.cleanup()
I want when the count goes higher than 1000000, a MG90S, that I have also connected to the pi and a 4AA battery pack, moves about 90 degrees.
The code I was trying to integrate to move the servo:
import RPi.GPIO as GPIO
import time
GPIO.setmode(GPIO.BOARD)
GPIO.setup(12, GPIO.OUT)
p.ChangeDutyCycle(7.5) # turn towards 90 degree
time.sleep(1) # sleep 1 second
p.stop()
GPIO.cleanup()
I want to combine these two Python codes. I tried for a bit, but I have almost no Python experience.
The code is now:
import RPi.GPIO as GPIO
import time
GPIO.setmode(GPIO.BOARD)
pin_to_circuit = 7
def move_90_degrees():
GPIO.setmode(GPIO.BOARD)
GPIO.setup(12, GPIO.OUT)
p = GPIO.PWM(12, 50)
p.start(7.5)
p.ChangeDutyCycle(7.5) # turn towards 90 degree
time.sleep(1) # sleep 1 second
p.stop()
def rc_time (pin_to_circuit):
count = 0
#Output on the pin for
GPIO.setup(pin_to_circuit, GPIO.OUT)
GPIO.output(pin_to_circuit, GPIO.LOW)
time.sleep(0.1)
#Change the pin back to input
GPIO.setup(pin_to_circuit, GPIO.IN)
#Count until the pin goes high
while (GPIO.input(pin_to_circuit) == GPIO.LOW):
count += 1
if count > 1000000:
return True
move_90_degrees()
else:
return count
#Catch when script is interrupted, cleanup correctly
try:
# Main loop
while True:
print rc_time(pin_to_circuit)
except KeyboardInterrupt:
pass
finally:
GPIO.cleanup()
The code does print True when the count exceeds 1000000, but the servo still doesn't move. The servo code on its own does however work correctly. (I forgot a bit of the servo code, that's why p wasn't defined, sorry.)
You could just integrate the code block you are using to move the MG90S into a function, insert it before or below your def rc_time (pin_to_circuit): (but first you have to define p inside, its not really clear what p refers to):
New Function from second code block:
def move_90_degrees():
p = '???'
GPIO.setup(12, GPIO.OUT)
p.ChangeDutyCycle(7.5) # turn towards 90 degree
time.sleep(1) # sleep 1 second
p.stop()
After defining this function, call it inside your first block like:
if count > 1000000:
move_90_degrees()
return True
else:
return count
That should work.
I've made a mistake in the code. I changed the order of the function call inside of the
if count > 1000000:
return True
move_90_degrees()
else:
return count
block to :
if count > 1000000:
move_90_degrees()
return True
else:
return count
otherwise, the code returns before the function call is executed. Is it working now?

Categories