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.
Related
I want to make let's say 5 subprocesses to work in exact same time. Problem is cmd window that appears for a split second so I am unable to see anything. How can I pause it or make anything else to have that window on sight after finished code?
I am opening it with such approach:
client = subprocess.Popen(f'python file.py {arg1} {arg2}', creationflags=CREATE_NEW_CONSOLE)
You could do it by creating Windows batch files that executed your Python script and then pause before ending.
import atexit
import msvcrt
import os
import subprocess
import sys
from tempfile import NamedTemporaryFile
import time
import textwrap
def create_batchfile(proc_no, py_script_filename, *script_args):
"""Create a batch file to execute a Python script and pass it the specified
arguments then pause and wait for a key to be pressed before allowing the
console window to close.
"""
with NamedTemporaryFile('w', delete=False, newline='', suffix='.bat',
) as batchfile:
cmd = (f'"{sys.executable}" "{py_script_filename}" ' +
' '.join(f'"{sarg}"' for sarg in script_args))
print(f'{cmd}') # Display subprocess being executed. (optional)
batchfile.write(textwrap.dedent(f"""\
#echo off
title Subprocess #{proc_no}
{cmd}
echo {py_script_filename} has finished execution
pause
"""))
return batchfile.name
def clean_up(filenames):
"""Remove list of files."""
for filename in filenames:
os.remove(filename)
temp_files = [] # List of files to delete when script finishes.
py_script_filename = 'my_file.py'
atexit.register(clean_up, temp_files)
for i in range(1, 4):
batch_filename = create_batchfile(i, 'my_file.py', i, 'arg 2')
temp_files.append(batch_filename)
subprocess.Popen(batch_filename, creationflags=subprocess.CREATE_NEW_CONSOLE)
print('Press any key to quit: ', end='', flush=True)
while True: # Wait for keypress.
if msvcrt.kbhit():
ch = msvcrt.getch()
if ch in b'\x00\xe0': # Arrow or function key prefix?
ch = msvcrt.getch() # Second call returns the actual key code.
break
time.sleep(0.1)
My project is to make a program that you can run while you are playing games or other programs in the background.
When you press a certain key, your notepad should open and also close after you press the same key again.
I have managed to open notepad with subprocess and that works fine but I have no idea to make it open only when a certain key is pressed.
Thanks for any help!
EDIT:
What I tried already:
import subprocess
import keyboard
if keyboard.is_pressed('k'):
subprocess.Popen('C:\\Windows\\System32\\notepad.exe')
input()
here it just doesn't detect any keyboard input, the input() at the end makes the program not close instantly
import subprocess
import keyboard
keyboard.add_hotkey('ctrl+k', print,args=("hello", "test"))
input()
Here if I press "ctrl+k it" will print hello test that means the hotkey works fine. When I switch this part "print,args=("hello", "test")" to "subprocess.Popen('C:\Windows\System32\notepad.exe')"(it should open the program instead of printing hello test) the notepad opens instantly after I run the program and when I press "ctrl+k" I get a big error.
A more complex, but still working example could be the following. With this code your program will be always listening the keyboard, not only when you are focused on the input, so may be mre practical in your case
from pynput import keyboard
import subprocess
import threading
class MyException(Exception): pass
class Listening:
"""Is allways waiting for the keyboard input"""
def __init__(self):
self.notepad_open = False # to know the state
with keyboard.Listener(
on_press=self.on_press) as listener:
try:
listener.join()
except:
pass
def on_press(self, key):
try:
if key.char == "k":
if not self.notepad_open:
self.subprocess = \
subprocess.Popen('C:\\Windows\\System32\\notepad.exe')
self.notepad_open = True # update state
else:
self.subprocess.kill()
self.notepad_open = False # update state
except: # special key was pressed
pass
thread = threading.Thread(target=lambda: Listening())
thread.start()
The problem is that you check for the key 'k' only once at the beginning. If you want the program to correctly work then you should try this:
import time
import subprocess
import keyboard
while True:
if keyboard.is_pressed('k'):
subprocess.Popen('C:\\Windows\\System32\\notepad.exe')
time.sleep(5)
-I used the time so that you can only open the program once 5 seconds(If you're curious, see what happens without it)-
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'm new to python and the RaspberryPi. For my RaspberryPi 3 I created two scripts. The first (bash script) detects the push of a button and executes a python3 script when the button is pushed.
The python3 script checks some API, saves the data to a dictionary and then asks for input. When the input is equal to the data action #1 is executed and when not action #2. Input comes from a usb barcode scanner.
Everything works perfect when I run the scripts manually either in desktop or cli mode of the RaspberryPi.
Now I wanted to autostart the bash script while starting the RaspberryPi in cli mode. I did this by adding the script to my rc.local file. This worked too, but when I press the button and the python script comes to the point where it asks for the input I directly get the following error:
Traceback (most recent call last):
File "*my python script*", line 36, in <module>
scan = input()
EOFError: EOF when reading a line
I really don't know why, because this doesn't happen when I run the bash script manually and I already searched for similar questions, but the given solutions didn't worked for me or the questions didn't include something like my problem.
Thank you!
Here are my two scripts:
bash script
#!/bin/bash
# Define pin as input
echo "23" > /sys/class/gpio/export
echo "in" > /sys/class/gpio/gpio23/direction
# Read value of pin
previous=$(cat /sys/class/gpio/gpio23/value)
# Loop
while true
do
# Read value of input
pin=$(cat /sys/class/gpio/gpio23/value)
# If input changes from 1 to 0
if [ $pin -gt $previous ]
then
# Start the script
python3 /home/pi/okey/get_api.py
else
# Sleep
sleep 2.5
fi
# Current value becomes old value for the next time
previous=$pin
done
python3 script
# -*- coding: utf-8 -*-
import requests
import json
from nested_lookup import nested_lookup
import time
import RPi.GPIO as GPIO
from Adafruit_CharLCD import Adafruit_CharLCD
# Set pin numbers
#GPIO.setmode(GPIO.BOARD)
# Set pin 11, 12 & 13 as output
GPIO.setup(17, GPIO.OUT)
GPIO.setup(18, GPIO.OUT)
GPIO.setup(27, GPIO.OUT)
# Set LCD & clear
lcd = Adafruit_CharLCD(rs=26, en=19, d4=13, d5=6, d6=5, d7=11, cols=16, lines=2)
lcd.clear()
# Get API JSON data from URL
lcd.message("Abfrage der\nPaketnummern...")
api = requests.get('my api')
# Serialize data into a python dictionary
data = api.json()
# Extract tracking numbers and create python list
trackingnumbers = nested_lookup('content', data)
# Get input
GPIO.output(27, GPIO.HIGH)
lcd.clear()
lcd.message("Warten auf\nEingabe...")
scan = input()
GPIO.output(27, GPIO.LOW)
# Check if input equals a number in the list
if scan in trackingnumbers:
lcd.clear()
lcd.message("Nummer vorhanden!")
GPIO.output(18, GPIO.HIGH)
time.sleep(2.5)
GPIO.output(18, GPIO.LOW)
else:
lcd.clear()
lcd.message("Nummer nicht\nvorhanden.")
GPIO.output(17, GPIO.HIGH)
time.sleep(2.5)
GPIO.output(17, GPIO.LOW)
# Clear LCD
lcd.clear()
# Clean GPIO resources
GPIO.cleanup()
Here is script (/shutdown.py). It monitors button press and if button is pressed more than 3 seconds, it runs poweroff command.
#!/usr/bin/python
# Import the modules to send commands to the system and access GPIO pins
from subprocess import call
from time import sleep
import RPi.GPIO as gpio
import time
# Define a function to keep script running
def loop():
raw_input()
# Define a function to run when an interrupt is called
def shutdown(pin):
button_press_timer = 0
while True:
if (gpio.input(17) == False) : # while button is still pressed down
button_press_timer += 1 # keep counting until button is released
if button_press_timer == 3:
#print "powering off"
call('poweroff', shell=True)
sleep(1)
else: # button is released, figure out for how long
#print "Poga atlaista. nospiesta bija " + str(button_press_timer) + " sekundes"
#button_press_timer = 0
return
# sleep(1) # 1 sec delay so we can count seconds
# print "powering off"
gpio.setmode(gpio.BCM) # Use BCM GPIO numbers
gpio.setup(17, gpio.IN, pull_up_down=gpio.PUD_UP) # Set up GPIO 17 as an input
gpio.add_event_detect(17, gpio.FALLING, callback=shutdown, bouncetime=200) # Set up an interrupt to look for button presses
loop() # Run the loop function to keep script running
If I run script from console like /shutdown.py all is fine. Button press is detected and system shutdown is initialed. But if i add that script to /etc/rc.local (/shutdown.py &), then it fails at startup with this error:
Traceback (most recent call last):
File "/shutdown.py", line 35, in <module>
loop() # Run the loop function to keep script running
File "/shutdown.py", line 11, in loop
raw_input()
EOFError: EOF when reading a line
If I comment out loop() line, then there is no error and script does not run in background. I just start and exit and button press not detected. So, how i can run that script at startup and keep running in background?
EDIT
I am not python guru and i think that loop() is python internal function. Now i seen that it is defined function which calls raw_input(). That script I found and modified to fit my needs.
Thanks.
What you really need is a Python daemon which runs in the background. The raw_input method you are trying to use looks like an ugly hack to me.
Have a look at python-daemon package, which is meant exactly for your use case and is quite simple to use. There is also an updated fork with Python 3 support.
After installing python-daemon, add this line to the beginning of your script
import daemon
Then substitute the loop() call at the end of your script with this code:
with daemon.DaemonContext():
while True:
time.sleep(10)
This code is untested, but you get the idea.