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:
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()