I have a program that is being executed on reboot via crontab. The script and programs work as expected if I run the script from terminal manually myself, but on running the script on boot its as if my RoverDriver.py program is being killed for no reason with no errors.
#reboot sleep 15 && sh /home/pi/Desktop/Rover-Driver/launcher.sh >/home/pi/Desktop/Rover-Driver/logs/cronlog 2>&1
The shell script is:
cd /
cd /home/pi/Desktop/Rover-Driver
sudo python3 bluetooth.py
cd /
The bluetooth.py program checks to see if our bluetooth wireless controller is connected to the pi, if so start the driver program
import pexpect
import time
attempts = 0
while attempts < 30:
child = pexpect.spawn('bluetoothctl')
child.sendline('paired-devices')
output = child.read_nonblocking(size = 200, timeout = -1).decode('utf-8')
target = 'Xbox Wireless Controller'
if output.find(target > -1:
print("Target Found")
print(pexpect.run('python RoverDriver.py'))
break
else:
print("No target found")
attempts = attempts + 1
time.sleep(1)
The RoverDiver.py is:
import pyfirmata
import pygame
import time
from pygame.locals import *
pygame.init()
# controller setup
joysticks = []
for i in range(pygame.joystick.get_count()):
joysticks.append(pygame.joystick.Joystick(i))
joysticks[-1].init()
controller = joysticks[0]
board = pyfirmata.ArduinoMega('/dev/ttyACM0')
# setting pins
onboard_led = board.get_pin('d:13:o')
l_motor_pin = board.get_pin('d:11:p')
r_motor_pin = board.get_pin('d:12:p')
motor_toggle_pin = board.get_pin('d:24:o')
# constantly updates statuses for example if reading analog input from potentiometer
it = pyfirmata.util.Iterator(board)
it.start()
motor_toggle_pin.write(0)
l_motor_pin.write(.49804)
r_motor_pin.write(.49804)
print("Have not entered while loop yet")
while(1):
pygame.event.pump()
# puts rover in "stop" state if no input, avoids rover from "running away"
motor_toggle_pin.write(0)
l_motor_pin.write(.49804)
r_motor_pin.write(.49804)
if(controller.get_button(11)):
print("Exiting...")
exit()
# divider ensure only half power given unless "A" button pressed down
divider = 0.125
reverse = False
right_power = controller.get_axis(4)
left_power = controller.get_axis(5)
# "A" button pressed, give full power
if(controller.get_button(0)):
divider = 0.25
right_power = divider * (right_power + 1)
left_power = divider * (left_power + 1)
# Bumpers are pressed, go reverse. Must be doing both to avoid breaking bot
if(controller.get_button(6) and controller.get_button(7)):
left_power = 0.5 - left_power
right_power = 0.5 - right_power
reverse = True
else:
left_power = 0.5 + left_power
right_power = 0.5 + right_power
# send motors their values
motor_toggle_pin.write(1)
l_motor_pin.write(left_power)
r_motor_pin.write(right_power)
print(f"L Power:{left_power} |R Power:{right_power}")
# avoid cpu overloading
time.sleep(.05)
# after exiting while loop, "turn off" motors
motor_toggle_pin.write(0)
l_motor_pin.write(.49804)
r_motor_pin.write(.49804)
print("End.")
I am expecting a rather large blob of text in the logs (because of the print statement of power levels), or at least the before/after print statements of the while loop however all i get is this:
Target Found.
b'pygame 1.9.4post1\r\nHello from the pygame community. https://www.pygame.org/contribute.html\r\n'
I know that its formatted that way because I am not decoding it. I am hypothesizing it has something to do with pygame but not sure what?
EDIT: To anyone who comes across this post. I switched my controls completely and no longer even rely on pygame. I switched to connecting to the pi via sockets and sending command strings over. This still needed to be setup on boot so I tried using crontab (unsuccessfully again) and instead found out about systemd which works beautifully.
Related
I am designing a new time/score keeper for an air hockey table using a PyBoard as a base. My plan is to use a TM1627 (4x7seg) for time display, rotary encoder w/ button to set the time, IR and a couple 7segs for scoring, IR reflector sensors for goallines, and a relay to control the fan.
I'm getting hung up trying to separate the clock into its own thread while focusing on reading the sensors. Figured I could use uasyncio to split everything up nicely, but I can't figure out where to put the directives to spin off a thread for the clock and eventually the sensors.
On execution right now, it appears the rotary encoder is assigned the default value, no timer is started, the encoder doesn't set the time, and the program returns control to REPL rather quickly.
Prior to trying to async everything, I had the rotary encoder and timer working well. Now, not so much.
from rotary_irq_pyb import RotaryIRQ
from machine import Pin
import tm1637
import utime
import uasyncio
async def countdown(cntr):
# just init min/sec to any int > 0
min = sec = 99
enableColon = True
while True:
# update the 4x7seg with the time remaining
min = abs(int((cntr - utime.time()) / 60))
sec = (cntr - utime.time()) % 60
#print(str(), str(sec), sep=':' )
enableColon = not enableColon # alternately blink the colon
tm.numbers(min, sec, colon = enableColon)
if(min + sec == 0): # once both reach zero, break
break
await uasyncio.sleep(500)
X1 = pyb.Pin.board.X1
X2 = pyb.Pin.board.X2
Y1 = pyb.Pin.board.Y1
Y2 = pyb.Pin.board.Y2
button = pyb.Pin(pyb.Pin.board.X3, pyb.Pin.IN)
r = RotaryIRQ(pin_num_clk=X1,
pin_num_dt=X2,
min_val=3,
max_val=10,
reverse=False,
range_mode=RotaryIRQ.RANGE_BOUNDED)
tm = tm1637.TM1637(clk = Y1, dio = Y2)
val_old = val_new = 0
while True:
val_new = r.value()
if(val_old != val_new):
val_old = val_new
print(str(val_new))
if(button.value()): # save value as minutes
loop = uasyncio.get_event_loop()
endTime = utime.time() + (60 * val_new)
loop.create_task(countdown(endTime))
r.close() # Turn off Rotary Encoder
break
#loop = uasyncio.get_event_loop()
#loop.create_task(countdown(et))
#loop.run_until_complete(countdown(et))
I'm sure it's something simple, but this is the first non-CLI python script I've done, so I'm sure there are a bunch of silly mistakes. Any assistance would be appreciated.
I have raspberry pi 3 and trying to create smart green house model. This model should open window if temperature is too hight.
I am new writing codes in Python, found several examples: 1. for temperature sensor and 2. for servo motor to rotating.
Could anyone help me with servo motor? I would like to move servo for example to 30° if temperature is 20°C, if it is 21°C move 40° servo and so on.
I have Python code:
import sys
import Adafruit_DHT
import time
import wiringpi
sensor_args = { '11': Adafruit_DHT.DHT11,
'22': Adafruit_DHT.DHT22,
'2302': Adafruit_DHT.AM2302 }
if len(sys.argv) == 3 and sys.argv[1] in sensor_args:
sensor = sensor_args[sys.argv[1]]
pin = sys.argv[2]
else:
print('usage: sudo ./Adafruit_DHT.py [11|22|2302] GPIOpin#')
print('example: sudo ./Adafruit_DHT.py 2302 4 - Read from an AM2302
connected to GPIO #4')
sys.exit(1)
humidity, temperature = Adafruit_DHT.read_retry(sensor, pin)
if humidity is not None and temperature is not None:
print('Temp={0:0.1f}* Humidity={1:0.1f}%'.format(temperature, humidity))
else:
print('Failed to get reading. Try again!')
sys.exit(1)
temp=temperature
text_file = open("output.txt", "w")
text_file.write("%s" %(temp))
text_file.close()
wiringpi.wiringPiSetupGpio()
wiringpi.pinMode(18,wiringpi.GPIO.PWM_OUTPUT)
wiringpi.pwmSetMode(wiringpi.GPIO.PWM_MODE_MS)
wiringpi.pwmSetClock(192)
wiringpi.pwmSetRange(2000)
delay_period = 0.01
while temp==20.0:
for pulse in range(50,250,1):
wiringpi.pwmWrite(18,50)
time.sleep(delay_period)
for pulse in range(250,50,-1):
wiringpi.pwmWrite(18,pulse)
time.sleep(delay_period)
Part about servo motor ir example I found on the internet. I need to replace "while" to "if". I tried by myself, but rotor all the time spin to the same angle.
Can anyone help me with this little part of code?
Second question, how can I run this command in terminal "sudo python servo.py 11 17" on raspberry pi automatically in every 10 mminutes and when raspberry pi is turned on?
Thanks for your help!
The pulse is how you are controlling the position of the servo in your code:
wiringpi.pwmWrite(18,pulse)
See this example:
https://learn.adafruit.com/adafruits-raspberry-pi-lesson-8-using-a-servo-motor?view=all
Depending on your servo, a pulse value of 100 move the servo all the way left (closed in this example), and 200 all the way to the right (open in this example). You need to find these values by reading the datasheet or by experimentation. Once you have these, here is how you would set the position of the servo:
min_servo_val = 100
max_servo_val = 200
wiringpi.pwmWrite(18, min_servo_val) # Move all the way left
time.sleep(1) # Wait a second
wiringpi.pwmWrite(18, max_servo_val) # Move all the way right
Now you can either write a function that translates temperature to a servo position between min_servo_val and max_servo_val, or use a simple if statement. Here is an example of a function to translate temp into a pulse (servo position):
def get_servo_position(temp):
min_servo_val = 100 # Pulse value at which window is all the way closed closed
max_servo_val = 200 # Pulse value at which window is all the way open
full_closed_temp = 20.0 # Temperature at which window is completely closed
full_open_temp = 26.0 # Temperature at which window is completely open
if temp <= full_closed_temp:
return min_servo_val
elif temp >= full_open_temp:
return max_servo_val
else:
return ((temp - full_closed_temp) / (full_open_temp - full_closed_temp)) * (max_servo_val - min_servo_val) + min_servo_val
Now you can do:
print get_servo_position(19) # 100 all the way closed
print get_servo_position(22) # 133.3, or 33% of the way between min_servo_val and max_servo_val
print get_servo_position(25) # 183.3, or 83% of the way between min_servo_val and max_servo_val
print get_servo_position(27) # 200 all the way open
Now you need a loop that checks the temperature every ten minutes and adjusts the servo position. Something like this:
while True:
humidity, temp = Adafruit_DHT.read_retry(sensor, pin) # Get temperature.
position = get_servo_position(temp) # Get the servo position
wiringpi.pwmWrite(18,position) # Move to the position
time.sleep(10*60) # Wait 10 minutes
Putting it all together, your script should look something like this:
import sys
import Adafruit_DHT
import time
import wiringpi
dht_pin = 17 # GPIO conencted to DHT
servo_pin = 18 # GPIO connected to servo
dht_sensor = Adafruit_DHT.DHT11 # Put your sensor here, or set it from command line args
min_servo_val = 100 # Pulse value at which window is all the way closed closed
max_servo_val = 200 # Pulse value at which window is all the way open
full_closed_temp = 20.0 # Temperature at which window is completely closed
full_open_temp = 26.0 # Temperature at which window is completely open
def get_servo_position(temp):
if temp <= full_closed_temp:
return min_servo_val
elif temp >= full_open_temp:
return max_servo_val
else:
return ((temp - full_closed_temp) / (full_open_temp - full_closed_temp)) * (max_servo_val - min_servo_val) + min_servo_val
def main_loop():
while True:
humidity, temp = Adafruit_DHT.read_retry(dht_sensor, dht_pin) # Get temperature.
position = get_servo_position(temp) # Get the servo position
wiringpi.pwmWrite(18,position) # Move to the position
time.sleep(10*60) # Wait 10 minutes
if __name__ == '__main__':
# If you need to get command line arguments, do it below, otherwise just set the pins and other settings at the top of this script.
# For example...
# dht_pin = sys.argv[1]
# servo_pin = sys.argv[2]
# Set up servo
wiringpi.wiringPiSetupGpio()
wiringpi.pinMode(18,wiringpi.GPIO.PWM_OUTPUT)
wiringpi.pwmSetMode(wiringpi.GPIO.PWM_MODE_MS)
wiringpi.pwmSetClock(192)
wiringpi.pwmSetRange(2000)
# Enter main loop
main_loop()
Note that I left out command line arg parsing. If you need those you can add them right after if __name__ == '__main__':
Finally, making a script run on startup is a well covered topic:
Start shell script on Raspberry Pi startup
First of all, I'm a newby in python. Had to take a course of it in college and got hooked by its efficiency.
I have this sticky problem where the Windows 7 prompt becomes unresponsive after using a curses window. In Windows 10 it works well. Note that I'm using the Win7 terminal with its default settings. In my code I create a curses window to show 2 simultaneous progress bars, each for a file download. I implemented this by passing the curses window to a FileDownload class (one class instance for each download) that handles its progress bar inside this window. Oddly, in Windows 7 when the downloads are done and the control returns to the prompt, it becomes unresponsive to the keyboard. I worked around this by invoking curses.endwin() after using the window, but this causes the prompt to display all the way down the screen buffer, what hides the curses window.
Here is my code. Any ideas are greatly appreciated. Thanks!
# Skeleton version for simulations.
# Downloads 2 files simultaneously and shows a progress bar for each.
# Each file download is a FileDownload object that interacts with a
# common curses window passed as an argument.
import requests, math, threading, curses, datetime
class FileDownload:
def __init__(self, y_pos, window, url):
# Y position of the progress bar in the download queue window.
self.__bar_pos = int(y_pos)
self.__progress_window = window
self.__download_url = url
# Status of the file download object.
self.__status = "queued"
t = threading.Thread(target=self.__file_downloader)
t.start()
# Downloads selected file and handles its progress bar.
def __file_downloader(self):
file = requests.get(self.__download_url, stream=True)
self.__status = "downloading"
self.__progress_window.addstr(self.__bar_pos + 1, 1, "0%" + " " * 60 + "100%")
size = int(file.headers.get('content-length'))
win_prompt = "Downloading " + format(size, ",d") + " Bytes:"
self.__progress_window.addstr(self.__bar_pos, 1, win_prompt)
file_name = str(datetime.datetime.now().strftime("%Y-%m-%d_%H.%M.%d"))
dump = open(file_name, "wb")
# Progress bar length.
bar_space = 58
# Same as an index.
current_iteration = 0
# Beginning position of the progress bar.
progress_position = 4
# How many iterations will be needed (in chunks of 1 MB).
iterations = math.ceil(size / 1024 ** 2)
# Downloads the file in 1MB chunks.
for block in file.iter_content(1024 ** 2):
dump.write(block)
# Progress bar controller.
current_iteration += 1
step = math.floor(bar_space / iterations)
if current_iteration > 1:
progress_position += step
if current_iteration == iterations:
step = bar_space - step * (current_iteration - 1)
# Updates the progress bar.
self.__progress_window.addstr(self.__bar_pos + 1, progress_position,
"#" * step)
dump.close()
self.__status = "downloaded"
# Returns the current status of the file download ("queued", "downloading" or
# "downloaded").
def get_status(self):
return self.__status
# Instantiates each file download.
def files_downloader():
# Creates curses window.
curses.initscr()
win = curses.newwin(8, 70)
win.border(0)
win.immedok(True)
# Download URLs.
urls = ["http://ipv4.download.thinkbroadband.com/10MB.zip",
"http://ipv4.download.thinkbroadband.com/5MB.zip"]
downloads_dct = {}
for n in range(len(urls)):
# Progress bar position in the window for the file.
y_pos = n * 4 + 1
downloads_dct[n + 1] = FileDownload(y_pos, win, urls[n])
# Waits for all files to be downloaded before passing control of the terminal
# to the user.
all_downloaded = False
while not all_downloaded:
all_downloaded = True
for key, file_download in downloads_dct.items():
if file_download.get_status() != "downloaded":
all_downloaded = False
# Prevents the prompt from returning inside the curses window.
win.addstr(7, 1, "-")
# This solves the unresponsive prompt issue but hides the curses window if the screen buffer
# is higher than the window size.
# curses.endwin()
while input("\nEnter to continue: ") == "":
files_downloader()
Perhaps you're using cygwin (and ncurses): ncurses (like any other curses implementation) changes the terminal I/O mode when it is running. The changes that you probably are seeing is that
input characters are not echoed
you have to type controlJ to end an input line, rather than just Enter
output is not flushed automatically at the end of each line
It makes those changes to allow it to read single characters and also to use the terminal more efficiently.
To change back to the terminal's normal I/O mode, you would use the endwin function. The reset_shell_mode function also would be useful.
Further reading:
endwin (ncurses manual)
reset_shell_mode (ncurses manual)
I am using the Pyosc library to send code from python to Sonic Pi on a Raspberry Pi.
The following code sends my code to Sonic Pi.
code = SOME SONIC PI CODE
oscmsg = OSC.OSCMessage()
oscmsg.setAddress("/run-code")
oscmsg.append(code)
c.send(oscmsg)
How do I terminate this code and send new code? What is the address/command for terminating the current code that is playing?
It's all written on the Sonic Pi github wiki page.
Therefore the address/command you're looking for is /stop-all-jobs.
This might answer even more questions.
# do this first:
# pip install pyosc
import OSC
import time
def sendCmd(cmd,par = None):
oscmsg = OSC.OSCMessage()
oscmsg.append('MY_PYTHON_GUI')
oscmsg.setAddress(cmd)
if par:
oscmsg.append(par)
c.send(oscmsg)
c = OSC.OSCClient()
c.connect(('127.0.0.1', 4557)) # connect to SuperCollider
#change these:
MYPATH = "/Users/you/path/"
# currentSong = MYSONG + ".txt"
# song = open( currentSong, 'r').read()
# MYSONG = "cloudbeat"
song = """
# music by Pit Noack
# http://www.maschinennah.de/sonic-pi-codeschnipsel-compus-loop/
use_bpm 80
live_loop :compus do
with_fx :ixi_techno, phase: [0.125, 0.25, 0.5, 1, 2].choose do
sample :loop_compus, beat_stretch: (ring 4, 8)[tick], amp: 4
sleep (ring 4, 8)[look]
end
end
"""
sendCmd("/stop-all-jobs")
sendCmd("/start-recording")
sendCmd("/run-code", song)
# the recording time should be calculated
time.sleep(3)
sendCmd("/stop-all-jobs")
# wait for reverb tail, etc
time.sleep(1)
sendCmd("/stop-recording")
time.sleep(0.1)
# sendCmd("/save-recording", MYPATH + MYSONG + ".wav")
sendCmd("/save-recording", MYPATH + "song.wav")
While running program through the terminal we can stop the program by pressing 'Ctrl+c' and it will show the message as 'KeyboardInterrupt' . So, is there any way to do the sane thing by clicking the push-button in PyQt.
If your program is running a loop, you can call processEvents periodically to allow the gui time to update (which should allow you to click a button to close the application):
count = 0
while True:
count += 1
if not count % 50:
QtGui.qApp.processEvents()
# do stuff...
In my script to interrupt an infinite loop I also used QtGui.qApp.processEvents() and it worked out fine. The infinite loop writes to and reads data from a serial port and the user can interrupt the loop with a push button (1.condition).
def Move_Right(self):
# move the slide right
cmdPack = struct.pack(cmdStruct, Address, Rotate_Right, 0, Motor5, Speed5)
dataByte = bytearray(cmdPack)
checksumInt = sum(dataByte[:]) % 256
msgPack = struct.pack(msgStruct, Address, Rotate_Right, 0, Motor5, Speed5, checksumInt)
ser0.flushOutput() # Clear output buffer
ser0.write(msgPack)
# read the switch status
cmdPack = struct.pack(cmdStruct, Address, Command.GAP, 10, Motor5, 0)
dataByte = bytearray(cmdPack)
checksumInt = sum(dataByte[:]) % 256
msgPack = struct.pack(msgStruct, Address, Command.GAP, 10, Motor5, 0, checksumInt)
ser0.flushOutput() # Clear output buffer
# check the switch status with an infinite write/read loop with two break out conditions
while True:
QtGui.qApp.processEvents() # 1. condition: interrupt with push button
ser0.write(msgPack)
reply = ser0.read(9)
answer = struct.unpack('>BBBBlB', reply)
value = answer[4]
command = answer[3]
if (command == 6) and (value == 1): # 2. condition: interrupt with limit switch
print 'end of line'
Stop_Motor5()
break