hoothoot
Posts: 8
Joined: Tue Jun 21, 2016 2:35 am

Calling functions from a list sequentially with a sensor

Tue Jun 21, 2016 3:01 am

Hello RPi community!

This is my first RPi AND my first Python project! I've become stuck with a few problems that seem easy to do, but are difficult for me because I'm not sure what I should be looking up.

The project is this: I have a list of 10 functions that each play an mp4 file. A CO2 sensor will call each function when the sensor output value goes below a certain number (let's say 700). It needs to play the videos in order. Example: blow into the sensor, output value goes down, video 1 plays. Blow into the sensor again, video 2 plays, etc. Once video 10 plays, it resets so the next video played will be video 1.

So basically I need Python to always be reading the sensor output, and to play the video ONCE when the value goes below 700. For example, when you blow into the sensor, the value dips from around 800 to around 600. It seems that just saying "if value < 700, play video" it will play the video for every returned value that's less than 700. That means it'll attempt to play the video 100 times or more!

Note: I have not been able to successfully make something happen when the sensor value goes below 700, even print a string. Here's an example of something that didn't work. What am I doing wrong here?

Code: Select all

import serial

ser = serial.Serial('/dev/ttyACM0', 9600)

while True:
        line = ser.readline()

        
if line < 700:
        print "Readout is less than 600."
else:
        pass        
        
ser.close()
Let me share my setup of the whole project. Again, I'm very new to Python, and in my newbie mind it seems like the best way to do this would be to move through a list of functions that call each video. Please correct me if I'm wrong!

Thank you for reading this far, any guidance is appreciated!

Code: Select all

import os
import serial

ser = serial.Serial('/dev/ttyACM0', 9600)

while True:
        line = ser.readline()

# -------------functions for black image to display when no one is blowing into the sensor-------------

def blank():
        os.system("sudo fbi -a --noverbose -T 1 blk.jpg")

def endblank():
        os.system("sudo killall fbi")

# -------------functions for videos-------------

def vid1():
	os.system("xterm -fullscreen -fg black -bg black -e omxplayer -o hdmi -r /home/pi/Desktop/01.mp4")

def vid2():
	os.system("xterm -fullscreen -fg black -bg black -e omxplayer -o hdmi -r /home/pi/Desktop/02.mp4")

def vid3():
	os.system("xterm -fullscreen -fg black -bg black -e omxplayer -o hdmi -r /home/pi/Desktop/03.mp4")

def vid4():
	os.system("xterm -fullscreen -fg black -bg black -e omxplayer -o hdmi -r /home/pi/Desktop/04.mp4")

def vid5():
	os.system("xterm -fullscreen -fg black -bg black -e omxplayer -o hdmi -r /home/pi/Desktop/05.mp4")

def vid6():
	os.system("xterm -fullscreen -fg black -bg black -e omxplayer -o hdmi -r /home/pi/Desktop/06.mp4")

def vid7():
	os.system("xterm -fullscreen -fg black -bg black -e omxplayer -o hdmi -r /home/pi/Desktop/07.mp4")

def vid8():
	os.system("xterm -fullscreen -fg black -bg black -e omxplayer -o hdmi -r /home/pi/Desktop/08.mp4")

def vid9():
	os.system("xterm -fullscreen -fg black -bg black -e omxplayer -o hdmi -r /home/pi/Desktop/09.mp4")

def vid10():
	os.system("xterm -fullscreen -fg black -bg black -e omxplayer -o hdmi -r /home/pi/Desktop/10.mp4")


# -------------list of videos-------------

video = [vid1, vid2, vid3, vid4, vid5, vid6, vid7, vid8, vid9, vid10]

# -----------------------------------------

if line < 600:
        endblank()
# Here's where I need help! 

else:
        blank()


User avatar
paddyg
Posts: 2555
Joined: Sat Jan 28, 2012 11:57 am
Location: UK

Re: Calling functions from a list sequentially with a sensor

Tue Jun 21, 2016 9:45 pm

you can have a list of functions in python and just 'run' them, however that's not a good idea here. A good basic rule of programming is that when you write nearly the same bit of code over and over you need to re-think what you are doing (google wet v. dry programming). So you could just have one function:

Code: Select all

def play_vid(num):
  os.system("xterm -fullscreen -fg black -bg black -e omxplayer -o hdmi -r /home/pi/Desktop/{:02d}.mp4".format(num))
....
if line < 700 and play_finished:
  play_vid(i)
  i = i + 1
  if i > num_videos:
    i = 1
Or if your videos had more complicated names that were strings then you could use a list and pass the name to your function, incrementing your pointer each time (but list index goes from 0 to n-1). However you still need to have a system of checking when the video has finished. I would use subprocess.Popen which has poll() method. So something like (not checked this but you can probably work out what I'm trying to do)

Code: Select all

num_videos = 10
i = 1
p = None
def play_vid(num):
  global p
  p = subprocess.Popen(["xterm", "-fullscreen", "-fg", "black", "-bg", "black", "-e", "omxplayer", "-o", "hdmi", "-r", "/home/pi/Desktop/{:02d}.mp4".format(num)])

if line < 700 and p is not None and p.poll() is not None:
  play_vid(i)
  i = i + 1
  if i > num_videos:
    i = 1

EDIT. For anybody finding this thread from a google search: Following this message the OP contacted me with a PM to ask some details, the conclusion of which is the following code (which I understand does what they want)

Code: Select all

import serial
import subprocess

# -------------function for videos-------------
def play_vid(num):
        global p
        p = subprocess.Popen(["xterm", "-fullscreen", "-fg", "black", "-bg", "black", "-e", "omxplayer", "-o", "hdmi", "-r", "/home/pi/Desktop/{:02d}.mp4".format(num)])

# -------------sensor setup-------------
ser = serial.Serial('/dev/ttyACM0', 9600)
# -----------------------------------------
num_videos = 10
i = 1
p = None

while True:
        ser.readline()
        line = int(ser.readline())
        print line # for debugging
        if line < 600 and (p is None or p.poll() is not None):
                play_vid(i)
                i = i + 1
                if i > num_videos:
                        i = 1
ser.close()
Last edited by paddyg on Sun Jul 03, 2016 10:24 am, edited 1 time in total.
also https://groups.google.com/forum/?hl=en-GB&fromgroups=#!forum/pi3d

hoothoot
Posts: 8
Joined: Tue Jun 21, 2016 2:35 am

Re: Calling functions from a list sequentially with a sensor

Wed Jun 22, 2016 5:09 pm

Wow, this is such an awesome solution, thank you! MUCH appreciated!

stderr
Posts: 2178
Joined: Sat Dec 01, 2012 11:29 pm

Re: Calling functions from a list sequentially with a sensor

Wed Jun 22, 2016 5:25 pm

hoothoot wrote:Note: I have not been able to successfully make something happen when the sensor value goes below 700, even print a string. Here's an example of something that didn't work. What am I doing wrong here?

Code: Select all

import serial
ser = serial.Serial('/dev/ttyACM0', 9600)
while True:
    line = ser.readline()
    if line < 700:
        print "Readout is less than 600."
    else:
        pass        
Regarding the above attempts to see if line is less than 700, you are not printing anything when it isn't less than 700 and you aren't printing whatever it actually is. If there is trouble, I would look at the actual value of line. I can imagine all sorts of potential trouble in situations like this including line not even being number, although that might not be true here.

hoothoot
Posts: 8
Joined: Tue Jun 21, 2016 2:35 am

Re: Calling functions from a list sequentially with a sensor

Wed Jun 22, 2016 9:31 pm

stderr wrote:
hoothoot wrote:Note: I have not been able to successfully make something happen when the sensor value goes below 700, even print a string. Here's an example of something that didn't work. What am I doing wrong here?

Code: Select all

import serial
ser = serial.Serial('/dev/ttyACM0', 9600)
while True:
    line = ser.readline()
    if line < 700:
        print "Readout is less than 600."
    else:
        pass        
Regarding the above attempts to see if line is less than 700, you are not printing anything when it isn't less than 700 and you aren't printing whatever it actually is. If there is trouble, I would look at the actual value of line. I can imagine all sorts of potential trouble in situations like this including line not even being number, although that might not be true here.
Do you mind explaining this a little more? Is it necessary to print something when when line is equal to or more than 700? The actual value, as printed in Terminal, hovers around 800 (this is a CO2 sensor, so the output value wavers a bit). When I'm looking at it in IDLE, it's (for example) 800\r\n, making it a string. I added a line to convert the value to a string, and still no luck.

Knowing this, do you have any suggestions for how to change it?

scotty101
Posts: 3958
Joined: Fri Jun 08, 2012 6:03 pm

Re: Calling functions from a list sequentially with a sensor

Thu Jun 23, 2016 1:21 pm

Can you post what the variable 'line' contains after the serial port read.

I think that your comparison isn't working as the data that
a. isn't a number
b. contains extra characters
Electronic and Computer Engineer
Pi Interests: Home Automation, IOT, Python and Tkinter

User avatar
paddyg
Posts: 2555
Joined: Sat Jan 28, 2012 11:57 am
Location: UK

Re: Calling functions from a list sequentially with a sensor

Thu Jun 23, 2016 4:04 pm

often just doing

Code: Select all

    line = int(ser.readline())
will strip out all the unwanted white space characters as well as convert to integer.
also https://groups.google.com/forum/?hl=en-GB&fromgroups=#!forum/pi3d

stderr
Posts: 2178
Joined: Sat Dec 01, 2012 11:29 pm

Re: Calling functions from a list sequentially with a sensor

Thu Jun 23, 2016 4:52 pm

hoothoot wrote:[Note: I have not been able to successfully make something happen when the sensor value goes below 700, even print a string. Here's an example of something that didn't work. What am I doing wrong here?

Code: Select all

import serial
ser = serial.Serial('/dev/ttyACM0', 9600)
while True:
    line = ser.readline()
    if line < 700:
        print "Readout is less than 600."
    else:
        pass        
Is it necessary to print something when when line is equal to or more than 700?
I just meant that if you are debugging something, it would be likely helpful to know that the number you are getting is sane. Even after that, tests to make sure the values are sane make sense since the sensor could fail and you need your system to know that at least in obvious cases like the numbers are impossible.
making it a string. I added a line to convert the value to a string, and still no luck.
That's the other thing, python is pretty fluid with data types, I'd look at exactly what I was getting off the serial input, which is a string, right? Even if python just converts it, will it work if the string includes stuff that isn't a number? Is the greater than working correctly? (I see others are wondering about this too and actually providing potential solutions)

Return to “Python”