Python Forum
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Miniature theater
#1
Hello World! Big Grin Dear community;

I'm creating a miniature theater to show a story in a cemetery. I intent to manipulate the puppet personages during a piece of music is playing and the lights illuminate the scenery.
Some images are down.

[Image: 3f9uTXYTUe7ZaLtq8]

I wrote the code, but I believe that it can be more simple and with less redundancy to work well. I don't know why, but some lights start "on" when I'd like to start all "off". Confused

Can someone help me to improve my code? Any help is welcome!

import RPi.GPIO as GPIO
import time
import random
import pygame

pygame.mixer.init()
pygame.mixer.music.load("/home/pi/Music/Romance para teatro.mp3")
pygame.mixer.music.play()

GPIO.setmode(GPIO.BCM)

def setup():                        #Setup GPIO iluminação, luzes de 1 a 12.
    GPIO.setup(1, GPIO.OUT)             # Luz 1 - direcional central (t. defunto).
    GPIO.setup(2, GPIO.OUT)             # Luz 2 - dir. (t. caveira).
    GPIO.setup(3, GPIO.OUT)             # Luz 3 - esq. (cova aberta).
    GPIO.setup(4, GPIO.OUT)             # Luz 4 - esq. (t. caveiro).
    GPIO.setup(5, GPIO.OUT)             # Luz 5 - dir. (t. defunto).
    GPIO.setup(6, GPIO.OUT)             # Luz 6 - esq. (esquina caveira).
    GPIO.setup(7, GPIO.OUT)             # Luz 7 - dir. (esquina caveiro).
    GPIO.setup(8, GPIO.OUT)             # Luz 8 - ribalta portão.
    GPIO.setup(9, GPIO.OUT)             # Luz 9 - esq. (t. cruz).
    GPIO.setup(10, GPIO.OUT)            # Luz 10 - int. caveiro.
    GPIO.setup(11, GPIO.OUT)            # Luz 11 - int. caveira.
    GPIO.setup(12, GPIO.OUT)            # Luz 12 - geral.

def output():
    GPIO.output(1, False)
    GPIO.output(2, False)
    GPIO.output(3, False)
    GPIO.output(4, False)
    GPIO.output(5, False)
    GPIO.output(6, False)
    GPIO.output(7, False)
    GPIO.output(8, False)
    GPIO.output(9, False)
    GPIO.output(10, False)
    GPIO.output(11, False)
    GPIO.output(12, False)
    
# set Frequece to 100Hz
    I = GPIO.PWM(1, 100)
    Z = GPIO.PWM(2, 100)
    E = GPIO.PWM(3, 100)
    A = GPIO.PWM(4, 100)
    S = GPIO.PWM(5, 100)
    G = GPIO.PWM(6, 100)
    T = GPIO.PWM(7, 100)
    B = GPIO.PWM(8, 100)
    Q = GPIO.PWM(9, 100)
    IO = GPIO.PWM(10, 100)
    II = GPIO.PWM(11, 100)
    IZ = GPIO.PWM(12, 100)

# Start PWM output, all off.
    I.start(0)
    Z.start(0)
    E.start(0)
    A.start(0)
    S.start(0)
    G.start(0)
    T.start(0)
    B.start(0)
    Q.start(0)
    IO.start(0)
    II.start(0)
    IZ.start(0)    

def action():

# 0 - 2,5 seg Black out, apenas música.
    time.sleep(2.5)             # Black out 0 - 2,5 seg.
    
# Introdução I | 2,5 - 9,8 seg, interior dos túmulos.
    GPIO.output(10,True)        # Int. Caveiro 2,5 - 3,7 seg.
    time.sleep(1.2)
    GPIO.output(10, False)
    time.sleep(0)
    GPIO.output(11, True)       # Int. Caveira 3,7 - 4,7 seg.
    time.sleep(1)
    GPIO.output(11, False)
    time.sleep(0)
    GPIO.output(10, True)       # Int. Caveiro 4,7 - 5,7 seg.
    time.sleep(1)               
    GPIO.output(10, False)
    time.sleep(0)
    GPIO.output(11, True)       # Int. Caveira 5,7 - 6,6 seg.
    time.sleep(0.9)
    GPIO.output(11, False)
    time.sleep(0)
    GPIO.output(10, True)       # Int. Caveiro 6,6 - 7,5 seg.
    time.sleep(0.9)
    GPIO.output(10, False)
    time.sleep(0)
    GPIO.output(11, True)       # Int. Caveira 7,5 - 8,6 seg.
    time.sleep(1.1)
    GPIO.output(11, False)
    time.sleep(0)
    GPIO.output(10, True)       # Int. Caveiro 8,6 - 9,4 seg.
    time.sleep(0.8)
    GPIO.output(10, False)
    time.sleep(0.4)

# Introdução II | 9,8 - 20,4 seg, focos nos túmulos e luz geral.
    GPIO.output(4, True)        # T. Caveiro 9,8 seg.
    time.sleep(0.9)
    GPIO.output(4, False)
    time.sleep(0)
    GPIO.output(2, True)        # T. Caveira 11,7 seg.
    time.sleep(1.7)
    GPIO.output(2, False)
    time.sleep(0)
    GPIO.output(7, True)        # Cova aberta 13,4 seg.
    time.sleep(1.9)               
    GPIO.output(7, False)
    time.sleep(0)
    GPIO.output(9, True)        # T. cruz 15,3 seg.
    time.sleep(2.0)
    GPIO.output(9, False)
    time.sleep(0)
    GPIO.output(12, True)       # Geral 17,3 seg.
    time.sleep(4.1)
    GPIO.output(12, False)
    time.sleep(0)

# Encontro das caveiras | 20,4 - 45,6 seg, saída das caveiras.
    GPIO.output(2, True)       # Foco nos túmulos dos dois aos 20,4 seg.
    GPIO.output(4, True)
    time.sleep(6.6)
    GPIO.output(2, False)
    GPIO.output(4, False)
    time.sleep(0)
    GPIO.output(12, True)      # Geral 27,0 seg.
    time.sleep(12.8)
    GPIO.output(12, False)
    time.sleep(0)
    GPIO.output(8, True)       # Ribalta 39,8 seg.
    time.sleep(5.8)
    GPIO.output(8, False)
    time.sleep(0)

# Juras de amor até coruja | 45,6 - 95,1 seg.
    GPIO.output(1, True)       # Foco na lousa fria aos 45,6 seg.
    GPIO.output(5, True)
    GPIO.output(6, True)
    time.sleep(25)
    GPIO.output(1, False)
    GPIO.output(5, False)
    GPIO.output(6, False)
    time.sleep(0)
    GPIO.output(8, True)       # Foco na coruja aos 70,6 seg.
    time.sleep(5.5)
    GPIO.output(1, True)       # Foco na lousa fria aos 76,1 seg + anterior.
    GPIO.output(5, True)
    GPIO.output(6, True)
    time.sleep(6.8)
    GPIO.output(1, False)      # Apaga lousa fria 82,9 seg, mantém coruja.
    GPIO.output(5, False)
    GPIO.output(6, False)
    time.sleep(0)
    GPIO.output(12, True)      # Luz geral aos 82,9 seg + coruja.
    time.sleep(5.5)
    GPIO.output(12, False)
    time.sleep(5.7)
    GPIO.output(8, False)      # Apaga coruja aos 94,1 seg e blackout por 1 seg.
    time.sleep(1)    

# Surge o defunto | 95,1 - 170,9 seg.
    GPIO.output(1, True)       # Foco na lousa fria aos 95,1 seg.
    GPIO.output(5, True)
    GPIO.output(6, True)
    time.sleep(9.2)
    GPIO.output(1, False)
    GPIO.output(5, False)
    GPIO.output(6, False)
    time.sleep(0)
    GPIO.output(12, True)      # Luz geral aos 104,2 seg.
    time.sleep(9)
    GPIO.output(12, False)
    time.sleep(0)
    GPIO.output(3, True)       # Foco na cova aberta aos 113,3 seg.
    GPIO.output(7, True)
    time.sleep(9.3)
    GPIO.output(3, False)      
    GPIO.output(7, False)
    time.sleep(0.4)
    GPIO.output(12, True)      # Luz geral aos 123,0 seg.
    time.sleep(28.6)
    GPIO.output(12, False)
    time.sleep(0)
    GPIO.output(2, True)       # Túmulo caveira aos 151,6 seg.
    time.sleep(6.7)
    GPIO.output(2, False)      # Blackout aos 158,3 seg.
    time.sleep(3.4)
    GPIO.output(11, True)      # Luz interna túmulo da caveira aos 158,3 seg.
    time.sleep(6.9)
    GPIO.output(11, False)
    time.sleep(1.9)

def loop():
    try:
        while True:
            action()
    except KeyboardInterrupt:
        pass
    finally:
        pygame.mixer.music.stop()       #Stop music.
        GPIO.cleanup()

setup()

loop()
Reply
#2
I would use a parser and a specific format that describes the entire action.
The following is a sketch, but is almost completed, and probably will be helpful for you.

import RPi.GPIO as GPIO
import time
import random
import pygame
  
pygame.mixer.init()
pygame.mixer.music.load("/home/pi/Music/Romance para teatro.mp3")
  
 
class Theater:
    freq = 100
     
    actors = {
            'defunto': 1,   # put comments here directional central etc...
            'caveira': 2, 
             
            # ... define the same way all 12 items/switches?!
            'int_caveira': 11
            'geral':   12
             
            }
     
    takt_executors = {
            'mplay': '_mplay_exec',
            'mstop': '_mstop_exec',
            'o': '_o_exec',
            's': '_s_exec'
            }
 
 
    def __init__(self):
         
        self.iface = GPIO
        self.iface.setmode(self.iface.BCM)
         
        # do setup
        for actor, num in Theater.actors.items():
            self.iface.setup(num)
         
         
    def output(self): 
        """ Docstring needs be here. 
             
        I didn't see that you use this function?!
        It seems to be never called in your code.
         
        """
        # is this allowed, or you need to split
        # it into 2 separated for-loops?
        for actor, num in Theater.actors.items():
            self.iface.output(num, False)
            self.iface.PWM(num, Theater.freq).start(0)
             
    def load_episode(self, episode):
        """ Docstring needs be here. """
        self.episode = episode
 
    def play(self, mode=None):
        """ Docstring needs be here. """
 
        if self.episode is None:
            # I don't know, where it will be printed in case of GPIO?!
            print("No episode was loaded. Load an episode first")
        try:
            if mode == 'always':
                while True:
                    self._play()
            else: # you can define more modes using elif statement...
                self._play()  # play only once
        except KeyboardInterrupt:
            pass
        finally:
            pygame.mixer.music.stop()       #Stop music.
            GPIO.cleanup()
     
     
    def _parse(self):
        if self.episode is None:
            return []
 
        result = []
        for token in map(str.strip, self.episode.split('\n')):
            if token == 'mplay':
                result.append(("mplay", None, None))
            elif token == 'mstop':
                result.append(("mstop", None, None))
            elif token.startswith('s'):
                try:
                    result.append(('s', float(token[1:]), None))
                except (ValueError, IndexError):
                    pass
            elif token.startswith('o'):
                try:
                    result.append(('o', token[2:-2],
                                   True if token[-1] == 't' else False))
                except (ValueError, IndexError):
                    pass
        return result
     
     
    def _mplay_exec(self, *args):
        pygame.mixer.music.play()
     
    def _mstop_exec(self, *args):
        pygame.mixer.music.stop()
     
    def _o_exec(self, *args):
        channel = Theater.actors.get(args[1])
        if ch is not None:
            self.iface.output(channel, args[-1])
            
    def _s_exec(self, *args):
        time.sleep(args[1])
     
    def _nothing_exec(self, *args):
        pass
         
     def _play(self):
        for takt in self._parse():
            exec_name = self.takt_executors.get(takt[0], '_nothing_exec')
            getattr(self, exec_name)(*takt)
             
             
     
     
 
# --------------------- Structure of episode description ---------------
# This is your action, it is written as a string, so it can be saved
# to a file, read and played again. 
episode = """
            mplay
            s2.5
            o_caveira_t
            s1.2
            o_caveira_f
            s0

             
            # complete full action; syntax is self-explanatory, i hope
            o_int_caveira_f
            s1.9
            mstop
"""
# mstop, mplay -- stop  and play music respectively    
             
theater = Theater()
theater.load_episode(episode)
theater.play(mode='always')
I am sorry about too few comments ( I am tired), but I hope the code is self-explanatory
and almost working...

====
UPD: I updated the code. It is not tested, however. The main idea is to use human understandable commands
that describe the theatrical performance. If you look at episode variable, it is just a string.
Each new line is a new command. Awesome invention, I think. Hope that helps.
Reply


Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020