Arquivo mensal: Maio 2016

Projeto semaforo

Tendo em conta a dificuldade que houve na utilização dos botões (pois apresentam um funcionamento assíncrono) bem como da “variável” tempo, foi criado um módulo Python (gpio_utils.py) com classes que facilitam o uso destes elementos assim como dos LEDs:

import pigpio

class timer:
    def __init__(self, pi):
        self._pi = pi
        self.reset()
        
    def reset(self):
        self._t = self._pi.get_current_tick()
        
    def time(self):
        return self._pi.get_current_tick() - self._t

class button:
    def __init__(self, pi, pin):
        self._pi = pi
        self._pin = pin
        self._pi.set_mode(self._pin, pigpio.INPUT) 
        self._pi.callback(self._pin, pigpio.FALLING_EDGE, self._cb)
        self._h = False
    
    def _cb(self, gpio, level, tick):
        self._h = True
    
    def hitted(self):
        tmp = self._h
        self._h = False
        return tmp
    
    def state(self):
        return not self._pi.read(self._pin)

class led:
    def __init__(self, pi, pin):
        self._pi = pi
        self._pin = pin
        self._pi.set_mode(self._pin, pigpio.OUTPUT)
        self.off()
    
    def off(self):
        self._pi.write(self._pin, 0)
        
    def on(self):
        self._pi.write(self._pin, 1)

Desde logo é possível dar o primeiro passo no projeto semaforo:

import platform
import pigpio
import time
from gpio_utils import timer, button, led

if platform.uname()[4][0:3] == "arm":
    pi = pigpio.pi()
else:
    pi = pigpio.pi("10.0.0.1xx")
    
t = timer(pi)
Bp = button(pi, 23)
Bi = button(pi, 24)
Lg = led(pi, 21)
Ly = led(pi, 20)
Lr = led(pi, 16)

leave = False
while not leave:
    if Bp.hitted():
        Lg.on()
        t.reset()
    
    if Bi.hitted() or t.time() > 3000000:
        Lg.off()

    time.sleep(.05)
    leave = Bi.state() and Bp.state()

Lg.off()
Ly.off()
Lr.off()

pi.stop()

Para implementar o normal funcionamento pretendido para o semáforo, vamos usar uma abordagem já nossa conhecida de “Eletrónica Digital” que são as “máquinas de estados finitos” (FSM – Finite State Machines). Na sua versão mais simples teríamos o seguinte diagrama de transições de estados:

FSM

Optou-se por uma máquina de “Mealy” para minimizar as chamadas às funções da biblioteca pigpio que assim se fazem apenas quando das transições entre estados.

import platform
import pigpio
import time
from gpio_utils import timer, button, led

if platform.uname()[4][0:3] == "arm":
    pi = pigpio.pi()
else:
    pi = pigpio.pi("10.0.0.1xx")
    
t = timer(pi)
Bp = button(pi, 23)
Bi = button(pi, 24)
Lg = led(pi, 21)
Ly = led(pi, 20)
Lr = led(pi, 16)

# States
INI = 0
SG = 1
SY = 2
SR = 3

s = INI
leave = False
while not leave:
    if s == INI:
        ns = SG
        Lg.on()
        t.reset()
    elif s == SG:
        if t.time() > 9000000:
            ns = SY
            Lg.off()
            Ly.on()
            t.reset()
        else:
            ns = SG
    elif s == SY:
        if t.time() > 1000000:
            ns = SR
            Ly.off()
            Lr.on()
            t.reset()
        else:
            ns = SY
    elif s == SR:
        if t.time() > 5000000:
            ns = SG
            Lr.off()
            Lg.on()
            t.reset()
        else:
            ns = SR

    s = ns       

    time.sleep(.05)
    leave = Bi.state() and Bp.state()

Lg.off()
Ly.off()
Lr.off()

pi.stop()

Vamos agora adicionar a funcionalidade para antecipar o amarelo:

import platform
import pigpio
import time
from gpio_utils import timer, button, led

if platform.uname()[4][0:3] == "arm":
    pi = pigpio.pi()
else:
    pi = pigpio.pi("10.0.0.1xx")
    
t = timer(pi)
Bp = button(pi, 23)
Bi = button(pi, 24)
Lg = led(pi, 21)
Ly = led(pi, 20)
Lr = led(pi, 16)

# States
INI = 0
SG = 1
SY = 2
SR = 3

s = INI
leave = False
while not leave:
    if s == INI:
        ns = SG
        Lg.on()
        flag = False
        t.reset()
    elif s == SG:
        if Bp.hitted():
            flag = True
        if t.time() > 9000000 or flag and t.time() > 4000000:
            ns = SY
            Lg.off()
            Ly.on()
            t.reset()
        else:
            ns = SG
    elif s == SY:
        if t.time() > 1000000:
            ns = SR
            Ly.off()
            Lr.on()
            t.reset()
        else:
            ns = SY
    elif s == SR:
        if t.time() > 5000000:
            ns = SG
            Lr.off()
            Lg.on()
            flag = False
            t.reset()
        else:
            ns = SR

    s = ns       

    time.sleep(.05)
    leave = Bi.state() and Bp.state()

Lg.off()
Ly.off()
Lr.off()

pi.stop()

Finalmente é adicionado o modo intermitente:

import platform
import pigpio
import time
from gpio_utils import timer, button, led

if platform.uname()[4][0:3] == "arm":
    pi = pigpio.pi()
else:
    pi = pigpio.pi("10.0.0.1xx")
    
t = timer(pi)
Bp = button(pi, 23)
Bi = button(pi, 24)
Lg = led(pi, 21)
Ly = led(pi, 20)
Lr = led(pi, 16)

# States
INI = 0
SG = 1
SY = 2
SR = 3
INT = 4
SY1 = 5
SY0 = 6

s = INI
leave = False
while not leave:
    if s == INI:
        ns = SG
        Lg.on()
        flag = False
        t.reset()
    elif s == SG:
        if Bp.hitted():
            flag = True
        if t.time() > 9000000 or flag and t.time() > 4000000:
            ns = SY
            Lg.off()
            Ly.on()
            t.reset()
        else:
            ns = SG
    elif s == SY:
        if t.time() > 1000000:
            ns = SR
            Ly.off()
            Lr.on()
            t.reset()
        else:
            ns = SY
    elif s == SR:
        if t.time() > 5000000:
            ns = SG
            Lr.off()
            Lg.on()
            flag = False
            t.reset()
        else:
            ns = SR
            
    if s == INT:
        ns = SY1
        Ly.on()
        t.reset()
    elif s == SY1:
        if t.time() > 1000000:
            ns = SY0
            Ly.off()
            t.reset()
        else:
            ns = SY1
    elif s == SY0:
        if t.time() > 1000000:
            ns = SY1
            Ly.on()
            t.reset()
        else:
            ns = SY0
    
    if Bi.hitted():
        Lg.off()
        Ly.off()
        Lr.off()
        if s == SG or s == SY or s == SR:
            ns = INT
        if s == SY1 or s == SY0:
            ns = INI
        
    s = ns       

    time.sleep(.05)
    leave = Bi.state() and Bp.state()

Lg.off()
Ly.off()
Lr.off()

pi.stop()