I own a 3d printer which is connectet to a raspberry. To controll (like shut on and off via relais) the printer remotely, I made a small python script.
One possibility to controll it is to use the Telegram Bot (telepot) which seems to work properly.
The other method to shut the printer on and off is a hardware switch. For secutity reasons, I want the printer to shut off only if the switch is pressed for 3 seconds.
The problem is, the script crashes sometimes (most at after a long uptime).
I assume that it's my while loop at the end, but I don't really understand why it only crashes after a variable amount of time and how to improve that.
That's the code so far:
#!/usr/bin/python
import time
import subprocess
import os
import datetime
import telepot
import urllib
import RPi.GPIO as GPIO
chat_id_auth = XXXXXXXXXXX
# Warnungen ausschalten
GPIO.setwarnings(False)
# Pin Nummern verwenden
GPIO.setmode(GPIO.BOARD)
# Pin 11 als Input
GPIO.setup(29, GPIO.IN)
GPIO.setup(11, GPIO.OUT)
GPIO.output(11, 0) #on
def handle(msg):
chat_id = msg['chat']['id']
command = msg['text']
print 'Got command: %s' % command
if chat_id == chat_id_auth:
if command == '/status':
bot.sendMessage(chat_id, 'online')
elif command == '/picture':
bashCommand = 'wget --output-document snapshot.jpg http://127.0.0.1:8080/?action=snapshot'
subprocess.check_output(bashCommand, shell=True)
print ('downloaded photo')
bot.sendPhoto(chat_id, open('snapshot.jpg','rb'), caption='Printer Status')
print ('sent photo')
bashCommand = 'rm snapshot.jpg'
subprocess.check_output(bashCommand, shell=True)
print ('removed photo')
elif command == '/ip':
bashCommand = 'ifconfig eth0 | grep \'inet addr:\' | cut -d: -f2 | awk \'{ print $1}\''
output = subprocess.check_output(bashCommand, shell=True)
bot.sendMessage(chat_id, output)
print(output)
elif command == '/on':
#bashCommand = 'echo \'0\' > /sys/class/gpio/gpio17/value'
#output = subprocess.check_output(bashCommand, shell=True)
GPIO.output(11, 0) #on
bot.sendMessage(chat_id, 'Drucker wird eingeschaltet..')
#print(output)
elif command == '/off':
#bashCommand = 'echo \'1\' > /sys/class/gpio/gpio17/value'
#output = subprocess.check_output(bashCommand, shell=True)
GPIO.output(11, 1) #off
bot.sendMessage(chat_id, 'Drucker wird ausgeschaltet..')
#print(output)
else:
bot.sendMessage(chat_id, 'You are not authorized.')
bot = telepot.Bot('XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX')
bot.message_loop(handle)
print 'I am listening ...'
global n
n=0
while True:
if GPIO.input(29):
if GPIO.input(11) == 0:
if n >= 300: #Taster 3s halten zum Ausschalten
GPIO.output(11, 1) #off
n=0
time.sleep(2)
elif GPIO.input(11) == 1:
GPIO.output(11, 0) #on
n=0
time.sleep(2)
time.sleep(0.01)
n+=1
else:
n=0
print n
GPIO.cleanup()
I cannot see a clear reason for a crash but the while loop at the end seems to be very busy waiting when nothing is done (no button pressed). From what I understand it then prints 0 and loops without any delay, so it will print zeros as fast as it can and keeping the Raspi busy as much as possible. Maybe that load is the indirect reason for any crash you experience (but that is just wild guessing).
Anyway, I propose to improve on that massively busy waiting. Even if it does not address the crashes, it would improve your code. To achieve this, simply add a time.sleep(0.1) in the else: branch to loop at most ten times per second (and not as fast as the CPU can).
In any case you should debug the situation. Find out why there is a crash.
Log your output into some file. (./script.py >& script.log)
try/except your exceptions and print debugging stuff in case you catch some.
Whoever starts it should monitor its exit code and log it.
Unix signals received result in an exit code of 128 + the signal number.
Type kill -l on the command line to see a list of all signals and their numbers.
Related
I have a little problem with understand what is going on with my code.
I have a lot of lines in my code so i decided to simulate my problem with shorter version.
Im using raspberry pi 4, flirone on rapsbian.
import sys
import os
import time
import subprocess
global shellscript
#global pid
def subprocess_settings():
shellscript = subprocess.Popen(["/home/pi/Desktop/init_flir.sh"], close_fds=True)
#shellscript = subprocess.Popen(["/home/pi/Desktop/init_flir.sh", "&> /dev/null"], stdin=None, stdout=None, close_fds=True)
#pid = shellscript.pid
try:
x = 1
while True:
if x == 0:
sys.exit()
elif x == 1:
print("Yas, x=1")
time.sleep(2)
subprocess_settings()
time.sleep(5)
print("Driver test is in background mode")
time.sleep(2)
print("Close process")
subprocess.Popen.kill(shellscript)
x=0
except KeyboardInterrupt:
subprocess.Popen.kill(shellscript)
and my .sh file:
#!/bin/bash
cd /home/pi/github/flirone-v4l2
sudo modprobe v4l2loopback exclusive_caps=0,0 video_nr=1,2,3
sudo ./flirone ~/github/flirone-v4l2/palettes/Iron2.raw
I want to run my .sh file. It has to work in background. I need to terminate this with 2 ways, first one from keyboard and second inside my elif.
At the end i need to hide terminal output from .sh file.
What im doing wrong??
Is this is a problem with global variables?
How to fix this to work properly?
Thanks for any help.
This solution works properly, as i wanted. In flirone driver i runned ./flirone and opened 1 process from subprocess.Popen and 2 additional processes in flirone code. I checked this running command sudo ./flirone ~/github/flirone-v4l2/palettes/Iron2.raw and ps aux | grep flirone . I used pidof flirone command to check main ID. When im killing this process everything works as should be and other processes ends too.
import sys
import os
import signal
import time
import subprocess
def subprocess_kill():
pidof = subprocess.check_output("pidof flirone", shell=True)
time.sleep(1)
subprocess.check_output("sudo kill -9 %s " % (pidof), shell=True)
try:
x = 1
while True:
if x == 0:
sys.exit()
elif x == 1:
print("x=1")
time.sleep(2)
os.chdir("/home/pi/github/flirone-v4l2")
shellscript = subprocess.Popen("sudo modprobe v4l2loopback exclusive_caps=0,0 video_nr=1,2,3", shell=True)
shellscript2 = subprocess.Popen("sudo ./flirone ~/github/flirone-v4l2/palettes/Iron2.raw", shell=True)
time.sleep(5)
print("Driver test is in background mode")
time.sleep(2)
print("Close process")
subprocess_kill()
x=0
except KeyboardInterrupt:
print("hey")
When i deleted killing method from except KeyboardInterrupt processes ends too.
If you want to hide output of subprocess u can use:
FNULL = open(os.devnull, "w")
shellscript2 = subprocess.Popen("sudo ./flirone ~/github/flirone-v4l2/palettes/Iron2.raw", shell=True, stdout=FNULL)
More: How to hide output of subprocess in Python 2.7
Subprocess python 2.7 lib
I have a python script in my Raspberry Pi that is connected to a rain gauge. When the rain gauge detects rain, the script shows 0.2 and write it to file. This is the code:
#!/usr/bin/env python3
import time
import RPi.GPIO as GPIO
BUTTON_GPIO = 16
if __name__ == '__main__':
GPIO.setmode(GPIO.BCM)
GPIO.setup(BUTTON_GPIO, GPIO.IN, pull_up_down=GPIO.PUD_UP)
pressed = False
while True:
# button is pressed when pin is LOW
if not GPIO.input(BUTTON_GPIO):
if not pressed:
print("0.2")
pressed = True
# button not pressed (or released)
else:
pressed = False
time.sleep(0.1)
My idea is to use a code like that to save the total amount of rain. When the python script show 0.2 > write it to file.
python3 rain.py >> rain.txt
The code creates a file but doesn't write anything until execution is finished by Ctrl + C.
I need to execute it on boot. I have tried to add it to crontab and rc.local but doesn't work.
I tried to execute it with sudo and pi. The permissions are 755.
Thank you!
try this
import time
import RPi.GPIO as GPIO
BUTTON_GPIO = 16
if __name__ == '__main__':
outputfile=open("/var/log/rain.txt","a",0)
GPIO.setmode(GPIO.BCM)
GPIO.setup(BUTTON_GPIO, GPIO.IN, pull_up_down=GPIO.PUD_UP)
pressed = False
while True:
# button is pressed when pin is LOW
if not GPIO.input(BUTTON_GPIO):
if not pressed:
openputfile.write("0.2\n")
pressed = True
# button not pressed (or released)
else:
pressed = False
time.sleep(0.1)
open a file in append mode with non buffered write.
Then when an event occurs, write to that file.
Do not use shell redirect as it will (in this case) buffer all the program output until exit and then write to a file. Of course, the exit never happens as you have a "while True" with no break
Indeed, this construct command >> file takes the whole of stdout and flushes into the file. It's done only when command execution is over. You must write to the file as soon as your intermediate result is ready:
#!/usr/bin/env python3
import sys
import time
import RPi.GPIO as GPIO
BUTTON_GPIO = 16
if __name__ == '__main__':
GPIO.setmode(GPIO.BCM)
GPIO.setup(BUTTON_GPIO, GPIO.IN, pull_up_down=GPIO.PUD_UP)
pressed = False
# command line arguments
if len(sys.argv) > 1: ## file name was passed
fname = sys.argv[1]
else: ## standard output
fname = None
## this will clear the file with name `fname`
## exchange 'w' for 'a' to keep older data into it
outfile = open(fname, 'w')
outfile.close()
try:
while True:
# button is pressed when pin is LOW
if not GPIO.input(BUTTON_GPIO):
if not pressed:
if fname is None: ## default print
print("0.2")
else:
outfile = open(fname, 'a')
print("0.2", file=outfile)
outfile.close()
pressed = True
# button not pressed (or released)
else:
pressed = False
time.sleep(0.1)
except (Exception, KeyboardInterrupt):
outfile.close()
In this approach you should run python3 rain.py rain.txt and everything will be fine. The try except pattern ensures the file will be properly closed when execution is interrupted by errors or keyboard events.
Notice the file keyword argument in call to print. It selects an open file object to write printed stuff. It defaults to sys.stdout.
I'm trying to make a python script that starts mdk3 when a switch is flipped on (LOW) and kills it when it's switched off (HIGH). However, the mdk3 command always starts when the script is started, regardless of the switch's position. Starting the script with the switch in the ON position and then switching it OFF while it's running kills the command, as expected. However, it does not begin running again when switching it back ON. Interestingly, the text set to print functions exactly as would be expected. My code is as follows:
import RPi.GPIO as GPIO
import time
import subprocess
import os
import signal
FSU = 'sudo mdk3 mon0 d'
pro = 'subprocess.Popen(FSU, stdout=subprocess.PIPE, shell=True, preexec_fn=os.setsid)'
# tell the GPIO module that we want to use the chip's numbering scheme
GPIO.setmode(GPIO.BCM)
# Set up GPIO16 as an input with internal pull-up resistor to hold it HIGH until it is pulled down to GND by the connected switch
GPIO.setup(16, GPIO.IN, pull_up_down=GPIO.PUD_UP)
running = False
while True:
if GPIO.input(16) == GPIO.LOW:
if running == False:
print('Button was pushed!')
pro
running = True
time.sleep(0.1)
elif running == True:
print('The process is running.')
time.sleep(0.1)
elif GPIO.input(16) == GPIO.HIGH and running == True:
os.killpg(os.getpgid(pro.pid), signal.SIGTERM)
print('Process killed.')
running = False
time.sleep(0.1)
elif running == False:
print('The process is not running.')
time.sleep(0.1)
else:
print('Critical error.')
time.sleep(0.1)
The reason I'm using my own loop to poll the GPIO pins instead of the event detection built-in in the RPi.GPIO library is because it has caused me nothing but problems and doing it myself seemed simpler. Any help would be appreciated.
Edit: I'm not sure why I didn't think of putting that call into quotes. Now it doesn't run on script start, but it only runs once. As in: I start the script with the switch OFF, and it doesn't run (as expected). I switch it ON and it runs. I switch it back OFF and it successfully kills the script. However, switching it back ON doesn't restart the script. Sorry if this is a bad explanation.
subprocess.Popen() starts the process as soon as it is called and also returns the process.
So, you can simply start the process again in the loop when it needs to be running by calling the same function again.
Slightly modifying your code:
import RPi.GPIO as GPIO
import time
import subprocess
import os
import signal
proc = subprocess.Popen(FSU, stdout=subprocess.PIPE, shell=True, preexec_fn=os.setsid)
FSU = 'sudo mdk3 mon0 d'
# tell the GPIO module that we want to use the chip's numbering scheme
GPIO.setmode(GPIO.BCM)
# Set up GPIO16 as an input with internal pull-up resistor to hold it HIGH until it is pulled down to GND by the connected switch
GPIO.setup(16, GPIO.IN, pull_up_down=GPIO.PUD_UP)
running = False
while True:
if GPIO.input(16) == GPIO.LOW:
if running == False:
print('Button was pushed!')
# declare the proc variabe again and also start the process
proc = subprocess.Popen(FSU, stdout=subprocess.PIPE, shell=True, preexec_fn=os.setsid)
running = True
time.sleep(0.1)
elif running == True:
print('The process is running.')
time.sleep(0.1)
elif GPIO.input(16) == GPIO.HIGH and running == True:
os.killpg(os.getpgid(pro.pid), signal.SIGTERM)
print('Process killed.')
running = False
time.sleep(0.1)
elif running == False:
print('The process is not running.')
time.sleep(0.1)
else:
print('Critical error.')
time.sleep(0.1)
More on subprocess
I have this panic button, and a script that responds to the button press.
if I send the process to the background, using the command $sudo python3 ./Panicbutton.py & I get the command line back. when I press the panic button, the script responds, and prints the message corresponding to the number of times the button was pressed. What I want it to do is go back to the background, whilst it waits for more button presses. pressing ctrl+c returns me to the command line, but I don’t want to need to press ctrl+c each time after the button is pressed. is there a way to return the script to the background while it awaits further key presses? How can I send the ctrl+c command to the terminal from the script? I don't want to terminate the process, I just want the command line prompt to return after the message is printed.
I am using Linux, and python3.4. Please assist.
heres the script:
# USB Panic Button interface code
# Copyright 2010 Ken Shirriff
# http://arcfn.com
import usb.core
x = 0
comment0 = """ PanicButton - interface to USB Panic Button This code requires PyUSB."""
class PanicButton:
def __init__(self):
# Device is: ID 1130:0202 Tenx Technology, Inc.
self.dev = usb.core.find(idVendor=0x1130, idProduct=0x0202)
if not self.dev:
raise ValueError("Panic Button not found")
try:
self.dev.detach_kernel_driver(0) # Get rid of hidraw
except Exception as e:
pass # already unregistered
def read(self):
comment1 = """ Read the USB port. Return 1 if pressed and released, 0 otherwise."""
#Magic numbers are from http://search.cpan.org/~bkendi/Device-USB-PanicButton-0.04/lib/Device/USB/PanicButton.pm
return self.dev.ctrl_transfer(bmRequestType=0xA1, bRequest=1, wValue=0x300, data_or_wLength=8, timeout=500)[0]
if __name__ == "__main__":
import time
button = PanicButton()
while 1:
if button.read():
global x
x = x + 1
if x < 5:
print(x)
elif x == 5:
print("Baby just screem if you want some more!")
elif x == 6:
print("Like AH!")
elif x == 7:
print("push it, push it.")
elif x == 8:
print("watch me work it.")
elif x == 9:
print("I'm PERFECT")
else:
x = 0
print("")
time.sleep(.5)
The script is always in the background, and does not go to the foreground at any point. It just looks that way because the script's output has the purely cosmetic effect of appearing to mess up the prompt.
Instead of pressing Ctrl-C, you can just write ls and press enter. You'll see that the shell works just fine and that your script is not in the foreground.
Bash can't really redraw the prompt because it doesn't control which other programs write to the screen. Consider finding a less invasive way of reporting button presses such as:
printing a bel character to make the terminal play a sound
changing the xterm's title
using tmux or screen to have a window or status bar message
using xmessage or similar to pop up a dialog box
having your bash prompt include messages from the script every time it redraws
I am just starting to learn python and using it with my raspberry pi and GPIO. I am trying to program this so it runs this line "echo p >> /home/pi/.config/pianobar/ctl" which would pause the song that is playing. I got it to print it but it doesnt actually pause the song.
#!/usr/bin/python
import os
import time
import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BOARD)
GPIO.setup(11, GPIO.IN)
while True:
mybutton = GPIO.input(11)
if mybutton == False:
print "pause"
os.system("echo 'echo 'p' >> /home/pi/.config/pianobar/ctl'")
time.sleep(.2)
and the output is: echo p >> /home/pi/.config/pianobar/ctl
pause
but nothing actually happens.
This line:
os.system("echo 'echo 'p' >> /home/pi/.config/pianobar/ctl'")
has two echos, and the >> operator is quoted, so all it's doing is printing something to standard output. Try:
os.system("echo 'p' >> /home/pi/.config/pianobar/ctl")