I have the following code working very nicely, but it is WAY too slow - something like 3/4 of a second latency. This is running on a Raspberry Pi 3+ under Raspbian Stretch with chromium browser as the client and apache2 as the server, both on the same Pi. There is a 5" monitor, but no keyboard or mouse attached. The unit has four momentary switches attached to GPIO pins, and each of the buttons controls a cursor. Two of the buttons move the cursor image up and down, and the other two control the text on top of the cursor image. After each button press, I issue a keypress (F5, in the code below) to the browser using pynput.keyboard. I am sure the reason this is too slow is at the moment I am refreshing the entire screen after updating control values in files in /run/thermostat. A better solution would be to emulate UpArrow and DnArrow to have the highlight image moved or the text updated, as the case may be. Sending the appropriate control code is easy, but how do I move the image or change the text without doing a full page refresh? (Note, this would also allow me to use the keyboard from an external browser to do the same thing.)
Snippet of external Python script that monitors the button status:
import RPi.GPIO as GPIO
import time, os
from pins import pinsarrow
from pynput.keyboard import Key, Controller
keyboard = Controller()
...
def my_callback4(channel):
Exists = os.path.exists('/run/thermostat/Cursor') # Check to see Cursor position is set
if Exists:
Fsize = os.path.getsize('/run/thermostat/Cursor') # Check for a valid file
if Fsize == 0:
Write_Cursor(0)
Cursor = Read_Cursor()
if Cursor > 0:
Cursor = Cursor - 1
Write_Cursor(Cursor) # Write the new Cursor position
##################################################
keyboard.press(Key.f5) #
keyboard.release(Key.f5) #
##################################################
else:
Write_Cursor(0) # File does not exist. Write the Cursor file.
GPIO.add_event_detect(pinsarrow[0], GPIO.RISING, callback=my_callback1, bouncetime=100)
GPIO.add_event_detect(pinsarrow[1], GPIO.RISING, callback=my_callback2, bouncetime=100)
GPIO.add_event_detect(pinsarrow[2], GPIO.RISING, callback=my_callback3, bouncetime=100)
GPIO.add_event_detect(pinsarrow[3], GPIO.RISING, callback=my_callback4, bouncetime=100)
Snippet of Python code that creates the web page:
Exists = os.path.exists('/run/thermostat/Cursor') # Check to see the Cursor position has been set
if Exists:
Fsize = os.path.getsize('/run/thermostat/Cursor')
if Fsize > 0:
with open("/run/thermostat/Cursor","r") as f: # Get the Cursor position
Cursor = int(f.read())
else:
write_Cursor() # No value in Cursor file
else:
write_Cursor() # Cursor file does not exist
RunStatus = ["Heating On", "Cooling On", "Fan On", "Idle"]
print('Content-type: text/html\n')
print("\n")
with open("/usr/lib/cgi-bin/index.txt","r") as f:
x = f.read()
print(x)
######################################################################
## Places the cursor image at a position specified by variable Cursor#
######################################################################
print('<img src="/images/Thermostat-Cursor.png" width="100" height="29" style="position: absolute; left: 15px; top: ',65 + 30 * Cursor,'px;">', sep='')
############################################################
## Check for cursor position and set color text accordingly#
############################################################
bColor = ["0", "0", "0", "0", "0", "0", "0"]
for x in range(0, 7):
if Cursor == x:
bColor[x] = "black"
else:
bColor[x] = "rgb(200,200,200)"
## Print each <div> with appropriate color
print('<div style="color:',bColor[0],'" class="Scale">',ClassValues[0][LineValue[0]],'</div>', sep="")
print('<div style="color:',bColor[1],'" class="Clock">',ClassValues[1][LineValue[1]],'</div>', sep="")
print('<div style="color:',bColor[2],'" class="Barometer">',ClassValues[2][LineValue[2]],'</div>', sep="")
print('<div style="color:',bColor[3],'" class="Server">',ClassValues[3][LineValue[3]],'</div>', sep="")
print('<div style="color:',bColor[4],'" class="Control">',ClassValues[4][LineValue[4]],'</div>', sep="")
if LineValue[4] < 2:
print('<div style="color:',bColor[5],'" class="Hot">Cool: ',ClassValues[5],'</div>', sep="")
if LineValue[4] == 0 or LineValue[4] == 2:
print('<div style="color:',bColor[6],'" class="Cold">Heat: ',ClassValues[6],'</div>', sep="")
Related
I'm trying to set up a system where my start-screen video loops until 1 of 2 buttons is pressed (GPIO buttons).
Then, the playback changes to either a video with subtitles or no-subtitles.
Once that has finished its play-through, it reverts back to the splash screen video.
I have additional tickers in here just to count the number of play-throughs per day for analytics. My Test device also only has 1 button hooked up which is why GPIO 18 is never used. Implementation will be identical to GPIO 17's, so once one is working the other won't be hard to match up.
Problem
When I launch the script, the media played is not always splash. The script also closes the window at the end of playback, and opens a new one to play the media. I believe this may be due to not establishing an xwindow (for raspberry pi).
Any advice?
#Vars
GPIO.setmode(GPIO.BCM)
GPIO.setup(17,GPIO.IN)
GPIO.setup(18,GPIO.IN)
update = True #Update to false to exit
def Main():
# Setup logs
print(date.today())
# Media Paths
path = "/home/pi/Videos/"
nosubs = path+"Content-NoSubs.mp4"
subs = path+"Content-Subtitles.mp4"
splash = path+"StartScreen.mp4"
Instance = vlc.Instance("-f")
playlist = set([splash,subs,nosubs])
url = [str(splash),str(subs),str(nosubs)] #Yes, this looks pretty redundant. Hopefully it's not.
#Setup the player
player = Instance.media_list_player_new()
Media = Instance.media_new(url[1])
Media_list = Instance.media_list_new(playlist)
Media.get_mrl()
player.set_media_list(Media_list)
playerState = {'State.NothingSpecial',
'State.Opening',
'State.Buffering',
'State.Playing',
'State.Paused',
'State.Stopped',
'State.Ended',
'State.Error'}
subsPlayed = 0
nosubsPlayed = 0
active = 0
playingMedia = 0
while update:
input = GPIO.input(17)
state = str(player.get_state())
if(state == playerState[0]):
player.play_item_at_index(0)
player.set_playback_mode(2)
if(state == playerState[7]):
player.play_item_at_index(0)
playingMedia = 0
if input == 1 and playingMedia == 0:
playingMedia = 1
player.play_item_at_index(1)
active +=1
nosubsPlayed +=1
print(playingMedia)
with open(str(date.today()))+'.txt','w' as file:
file.write("Active Views: " + active)
file.write("SubsPlayed: " + subsPlayed)
file.write("No Subs Played: " + nosubsPlayed)
Main()
So I figured out the solution, but not the problem's origin.
# Make my media paths into vlc.Media Objects
nosubs = vlc.Media(path+"Content-NoSubs.mp4")
subs = vlc.Media(path+"Content-Subtitles.mp4")
splash = vlc.Media(path+"SplashScreen.mp4")
#Setup the player
player = Instance.media_list_player_new()
Media_list = Instance.media_list_new()
Media_list.add_media(splash)
Media_list.add_media(subs)
Media_list.add_media(nosubs)
player.set_media_list(Media_list)
Media_list.lock()
Setting up each of the media by name in my list helps by switching the play function from play_item_at_index(int) to play_item(media)
Still not sure why it was kind of randomizing. My guess was that it changed the position of media in the list based on play through.
My next step will be to adjust this to work off of media_player and embedding playback into a tkinter window.
Im gonna try and explain to the best of my efforts.
Script im running is below.
I want to include something that will make the pir sensor not do anything between the hours of 24:00 and 05:00. But the button should work at those times regardless.
Also i want to be able to send different colors at certain times of the day.
So that if its between 8pm and 11 pm it will give this code to the lights: {"on":true,"bri":255,"sat":80,"hue":357}
There will be 4 colors in total. I have tried defining the command called with command(): and the colors but im at a standstill here.
Can anyone help me with this? I do really hope i made myself clear here, but fire away if anything is unclear.
import sys
sys.path.append("/home/pi/.local/lib/python2.7/site-packages")
from phue import Bridge
import RPi.GPIO as GPIO
import time
import datetime
print 'Waiting for network...'
time.sleep(30)
print 'The wait is over. It\'s showtime!'
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)
GPIO.setup(4, GPIO.IN) #Read output from PIR motion sensor
GPIO.setup(18, GPIO.IN, pull_up_down=GPIO.PUD_UP) #Read output from button.
b=Bridge('192.168.1.47')
try:
b.connect()
except ImportError:
print "Import error occurred!"
print "Connected to Hue bridge!"
lightson=b.get_light(2, "on")
if lightson: print "Lights are already on."
print 'Entering infinite loop...'
light_on_delay = 15 # time in min for light to stay on when button pressed
button_pressed = 0
while True:
# check for button press
input_state = GPIO.input(18)
if input_state == False:
print('Button Pressed')
end = (light_on_delay * 1) + time.time()
button_pressed = 1
command = {"on" : True, "bri" : 255, "sat" : 0, "hue" : 0}
b.set_group(2, command)
lightson=True
print('Lights are on for 15 minutes')
# check if button has been pressed if it has check to see if time is up
if button_pressed == 1:
if time.time() > end:
button_pressed = 0
else:
i=GPIO.input(4)
timestamp=datetime.datetime.now().time()
if (timestamp < offstarttime and timestamp > offendtime):
if i==0: #When output from motion sensor is LOW
print ('No movement detected - Turning lights off')
b.set_group(2, 'on', False)
lightson=False
print ('Lights are off')
time.sleep(0.1)
else: #When output from motion sensor is HIGH
print ('Movement detected - Turning lights on')
command = {"on" : True, "bri" : 255, "sat" : 0, "hue" : 0}
b.set_group(2, command)
lightson=True
print ('Lights are on.')
time.sleep(5)
# added delay to prevent program using 100% cpu time.
time.sleep(0.5)
You can add a time check using datetime module at the start of each iteration to conditionally set your command dictionary and run your PIR code if between certain hours. The button logic code should be run outside of the if blocks to make sure it always works
from datetime import datetime
while True:
now = datetime.now()
# Check to see if it is 5am or later
if now.hour >= 5:
# PIR sensor code here
print("PIR sensor should work now")
# Check to see if between 8pm and 11pm
if now.hour >= 20 and now.hour <= 23:
# Configure command dictionary for specific hours
command = {"on": True,"bri": 255,"sat": 80,"hue": 357}
else:
# Configure command dictionary for regular hours
command = {"on": False}
# Rest of your code including button logic
I'm new to python and am attempting to control solenoid valves using a relay, RPi, PIR sensor and python script. I found a script posted online I used as a base and modified it a bit to switch my relay. Everything seems to be working as expected overall, but I've noticed the following:
As soon as I run the script in terminal, the relay switches are on. After first trigger , they will stay off until the following trigger. Why are they intially on?
In my code I'm using GPIO.output(<variable>,False) to switch on and GPIO.output(<variable>,True) to turn off which seems backward. Shouldn't the False argument turn the switch off and True turn on?
Code below. Any pointers or insight would be appreciated! Thanks!
# Import required Python libraries
import RPi.GPIO as GPIO
import time
import random
# Use BCM GPIO references
# instead of physical pin numbers
GPIO.setmode(GPIO.BCM)
# Define GPIO to use on Pi
pir = 17
arm_one = 23
arm_two = 24
print "PIR Module Test (CTRL-C to exit)"
# Set pin as input
GPIO.setup(pir,GPIO.IN)
GPIO.setup(arm_one,GPIO.OUT)
GPIO.setup(arm_two,GPIO.OUT)
current_state = 0
previous_state = 0
def getRandomNum():
random_sleep=random.uniform(0,1)
return random_sleep;
try:
print "Waiting for PIR to settle ..."
# Loop until PIR output is 0
while GPIO.input(pir)==1:
current_state = 0
print " Ready"
# Loop until users quits with CTRL-C
while True:
# Read PIR state
current_state = GPIO.input(pir)
if current_state==1 and previous_state==0:
# PIR is triggered
print " Motion detected!"
for i in range(5):
GPIO.output(arm_one,False)
GPIO.output(arm_two,False)
time.sleep(1)
GPIO.output(arm_one,True)
time.sleep(getRandomNum())
GPIO.output(arm_two,True)
time.sleep(getRandomNum())
GPIO.output(arm_one,False)
time.sleep(getRandomNum())
GPIO.output(arm_one,True)
GPIO.output(arm_two,False)
time.sleep(getRandomNum())
GPIO.output(arm_two,True)
# Record previous state
previous_state=1
elif current_state==0 and previous_state==1:
# PIR has returned to ready state
print " Ready"
GPIO.output(arm_one,True)
GPIO.output(arm_two,True)
previous_state=0
# Wait for 10 milliseconds
time.sleep(0.01)
except KeyboardInterrupt:
print " Quit"
# Reset GPIO settings
GPIO.cleanup()
I am new to Raspberry pi and python and am having a bit of trouble with some code
I wish to push a button and have the gpio pin that corresponds to that button trigger a relay to turn on for a given amount of time then turn off. I have it working with the code below but by using the 'time.sleep(my-variable)' it holds up the raspberry pi for the duration of the time and i am unable to do anything else.
What i am after is the ability to push one button and get the relay to act for say 10 seconds and within those 10 seconds be able to press another button to fire another relay and do the same thing without tying up the pi
my code below first checks if input_state_LHS is equel to false, then clears the LCD display, writes text to the LCD on one line then on the next line it write the value of my variable(LHS_feedtime) then fires the relay with the time on the next line time.sleep, this is the bit i wish to be rid of but am unable to figure out the code to do it.
if input_state_LHS == False:
## calls the LCD_Clear function which has to be in the same folder as this file
mylcd.lcd_clear()
mylcd.lcd_display_string("LHS Feedtime",1,2)
mylcd.lcd_display_string(str(round(LHS_feedtime, 2)) + " sec" , 2,5)
GPIO.output(27, GPIO.input(12) )
time.sleep(LHS_feedtime)
mylcd.lcd_clear()
mylcd.lcd_display_string("Flatson Feeding", 1)
mylcd.lcd_display_string("Systems", 2,4)
GPIO.output(27, GPIO.input(12) )
menuitem = 0
thanks for the help
The functionality you need is in the Python standard library class threading.Timer. When you start a timer it launches another thread, which consists of a time delay followed by a call to a function that you specify. In contrast to time.sleep() which stops your main thread at that point, with a Timer your main thread will keep going.
Here is roughly what you want:
from threading import Timer
def turn_off_lcd():
mylcd.lcd_clear()
mylcd.lcd_display_string("Flatson Feeding", 1)
mylcd.lcd_display_string("Systems", 2,4)
GPIO.output(27, GPIO.input(12) )
if input_state_LHS == False:
## calls the LCD_Clear function which has to be in the same folder as this file
mylcd.lcd_clear()
mylcd.lcd_display_string("LHS Feedtime",1,2)
mylcd.lcd_display_string(str(round(LHS_feedtime, 2)) + " sec" , 2,5)
GPIO.output(27, GPIO.input(12) )
t = Timer(LHS_feedtime, turn_off_led)
t.start()
menuitem = 0
Here you go, this will constantly loop the code until the 10 seconds have passed. But it will constantly print "10 seconds havent passed" (you can remove this line.) As you will notice, this code does not use the time.sleep() and so will not hold the script up.
import time
#Get the initial system time
timebuttonpressed = time.strftime("%H%M%S")
elapsedtime = time.strftime("%H%M%S")
while True:
elapsedtime = time.strftime("%H%M%S")
if input_state_LHS == False:
#Get the new system time, but only set timesample2
timebuttonpressed = time.strftime("%H%M%S")
## calls the LCD_Clear function which has to be in the same folder as this file
mylcd.lcd_clear()
mylcd.lcd_display_string("LHS Feedtime",1,2)
mylcd.lcd_display_string(str(round(LHS_feedtime, 2)) + " sec" , 2,5)
GPIO.output(27, GPIO.input(12) )
#Check if 10 seconds have passed
if((int(elapsedtime) - int(timebuttonpressed)) == 10):
timebuttonpressed = time.strftime("%H%M%S")
mylcd.lcd_clear()
mylcd.lcd_display_string("Flatson Feeding", 1)
mylcd.lcd_display_string("Systems", 2,4)
GPIO.output(27, GPIO.input(12) )
menuitem = 0
print("10 seconds havent passed...")
I have been working on a project using Python to read values from an arduino and then control video cameras. The Arduino controls two ultrasonic sensors and reports distance in cm. The python script then reads the distances from the Arduino using ser.readline(). When the script reads values outside the range everything works fine. However if it goes into the loop for the distance inside the required range it works correctly once and then reads old values from the Arduino instead of current "live" values which causes it to continue the record loop instead of exiting the loop. What can I do to get rid of the old values in the buffer and only read the most current value? I have found several methods and tested them but so far no luck.
Here is the code I am using (i know its not well written but its my first try using python and writing code outside of matlab)
import sys, time
import serial
import cv
import os
from time import strftime
#Create window for Camera 0
cv.NamedWindow("Camera 0", cv.CV_WINDOW_AUTOSIZE)
capture0 = cv.CreateCameraCapture(2)
cv.ResizeWindow("Camera 1", 640, 480)
cv.MoveWindow("Camera 0", 0, 0)
#Create window for Camera 1
cv.NamedWindow("Camera 1", cv.CV_WINDOW_AUTOSIZE)
capture1 = cv.CreateCameraCapture(1)
cv.MoveWindow("Camera 1", 150, 150)
#Initialize connection to Arduino
arduino = serial.Serial('COM12', 9600)
connected = False
#Confirm that Arduino is connected and software is able to read inputs
while not connected:
serin = arduino.readline()
connected = True
f = 'Sensor Connected'
print f
'''#Dummy variables for testing
value1 = 145
value2 = 30'''
#Initialize video record on as false (0)
vid = 0
#Initialize counter
counter_vid = 0
counter = 0
Accel = 1
def Camera0():
frame0=cv.QueryFrame(capture0)
cv.WriteFrame(writer0,frame0)
cv.ShowImage("Camera 0", frame0)
def Camera1():
frame1=cv.QueryFrame(capture1)
cv.WriteFrame(writer1,frame1)
cv.ShowImage("Camera 1", frame1)
while True:
status = arduino.readline()
value1=int((status[6:10]))-1000
value2=int((status[17:21]))-1000
print(value1)
print(value2)
if value1>180 and value2>180 and vid==0:
vid = 0
elif value1>180 and value2>180 and vid==1:
vid = 0
elif value1<180 and vid==0 or value2<180 and vid==0:
filename0 = strftime("OUTPUT\%Y_%m_%d %H_%M_%S") + " camera0.avi"
writer0=cv.CreateVideoWriter(filename0, 1, 15.0, (640,480), is_color=1)
filename1 = strftime("OUTPUT\%Y_%m_%d %H_%M_%S") + " camera1.avi"
writer1=cv.CreateVideoWriter(filename1, 1, 15.0, (640,480), is_color=1)
vid=1
while counter_vid<25 and vid==1:
Camera0()
Camera1()
counter_vid += 1
print(counter_vid)
cv.WaitKey(10)
else:
while counter_vid<25 and vid==1:
Camera0()
Camera1()
counter_vid += 1
print(counter_vid)
cv.WaitKey(10)
cv.WaitKey(25)
counter_vid = 0
counter += 1
print('End of Loop Counter')
print(counter)
You're right about the buffer filling up. You need a way to always get the most recent value out of the buffer.
I would suggest replacing this:
status = arduino.readline()
with this:
status = getLatestStatus()
and then further up towards the top, by your camera functions:
def getLatestStatus():
while arduino.inWaiting() > 0:
status = arduino.readline()
return status
This function getLatestStatus will go through the entire buffer every time it is called and only return the latest status, disregarding all the statuses returned in the meantime.
Your other option is to modify the "firmware" for your arduino to return a distance sensor value every time it receives a command, (say "M\n") so that way you don't have to worry about buffer problems. That's what I did for an arduino-powered ultrasonic distance device and I felt it was cleaner than the "read through the entire buffer" solution. It will introduce a bit more latency into your distance measurement though.