Skip to the content.

Aula 5 — Máquinas de Estados: Do Diagrama ao Código

Duração estimada: 30 minutos Bloco: 3 de 3 — Integração e preparação para UART


Objetivos

Ao final desta aula você será capaz de:


1. Conceito

O que é uma máquina de estados?

Pense em um elevador. Ele está sempre em um estado bem definido: parado, subindo ou descendo. Ele nunca está subindo e descendo ao mesmo tempo. E ele só muda de estado quando algo acontece — alguém aperta um botão, ele chega no andar.

Isso é uma máquina de estados finitos: um sistema que:

Três perguntas que definem qualquer máquina de estados:

Pergunta Nome técnico
Em que estado estou agora? Estado atual
O que pode acontecer aqui? Evento ou condição
Para onde vou se isso acontecer? Transição

Representando com um diagrama

Usamos um grafo para visualizar a máquina de estados:

Exemplo mínimo — uma lâmpada:

         [btn]               [btn]
    ─── ────────►  APAGADA  ──────►  ACESA
       ▲                               |
       |                               |
        ───────────────────────────────

Apenas dois estados, uma condição para cada sentido. Simples — mas já é uma máquina de estados completa.


O semáforo como máquina de estados

Você já conhece o semáforo. Agora vamos lê-lo como máquina de estados:

        ┌─────────────────────────────────────────┐
        │              [btn pressionado]          │
        │                                         │
        ▼                                         │
  ┌───────────┐   [2 segundos]   ┌────────────┐   │
  │  VERMELHO │                  │   AMARELO  │   │
  │  LED: ●   │                  │  LED: ●    │   │
  └───────────┘                  └────────────┘   │
        │                             ▲           │
        │   [btn pressionado]         │           │
        │                    [2 seg]  │           │
        ▼                             │           │
  ┌───────────┐ ──────────────────────┘           │
  │   VERDE   │                                   │
  │  LED: ●   │ ──────────────────────────────────┘
  └───────────┘        [btn pressionado]

Lendo o diagrama:

Estado O que faz Condição de saída Próximo estado
VERDE LED verde aceso botão pressionado AMARELO
AMARELO LED amarelo aceso 2 segundos VERMELHO
VERMELHO LED vermelho aceso botão pressionado VERDE

Dois tipos de transição aparecem aqui:


2. Do diagrama ao código

O diagrama vira código de forma direta. Cada estado vira um bloco if/elif, e cada seta vira uma atribuição estado = NOVO_ESTADO:

# constantes para os estados — facilita leitura
VERDE    = 0
AMARELO  = 1
VERMELHO = 2

estado = VERDE      # estado inicial (seta sem origem no diagrama)

while True:
    if estado == VERDE:
        # ações do estado VERDE
        # ...
        if condicao:            # rótulo da seta de saída
            estado = AMARELO    # transição — seta no diagrama

    elif estado == AMARELO:
        # ações do estado AMARELO
        # ...
        if condicao:
            estado = VERMELHO

    elif estado == VERMELHO:
        # ações do estado VERMELHO
        # ...
        if condicao:
            estado = VERDE

Regra prática: conte as setas que saem de cada estado no diagrama. Cada uma vira um if dentro do bloco elif estado == correspondente.


3. Circuito

Componente Quantidade
ESP32 DevKit 1
LED vermelho 1
LED amarelo 1
LED verde 1
Resistor 220 Ω 3
Botão (pushbutton) 1
Resistor 10 kΩ 1

Conexões:

GPIO2  ──► R220 ──► LED Verde    ──► GND
GPIO4  ──► R220 ──► LED Amarelo  ──► GND
GPIO5  ──► R220 ──► LED Vermelho ──► GND

GPIO13 ──► Botão ──► 3,3V
GPIO13 ──► R10k  ──► GND   (pull-down)
# Pico: led_verde  = Pin(0, ...)  | led_amarelo = Pin(1, ...)
# Pico: led_verm   = Pin(2, ...)  | btn         = Pin(14, ...)

4. Código

from machine import Pin
import time

# --- pinos ---
led_verde    = Pin(2,  Pin.OUT)
led_amarelo  = Pin(4,  Pin.OUT)
led_vermelho = Pin(5,  Pin.OUT)
btn          = Pin(13, Pin.IN)    # pull-down externo

# Pico: led_verde = Pin(0, Pin.OUT) | led_amarelo = Pin(1, Pin.OUT)
# Pico: led_vermelho = Pin(2, Pin.OUT) | btn = Pin(14, Pin.IN)

# --- constantes de estado ---
VERDE    = 0
AMARELO  = 1
VERMELHO = 2

# --- funções auxiliares ---
def apagar_todos():
    """Apaga todos os LEDs — chamada antes de acender o do estado atual."""
    led_verde.value(0)
    led_amarelo.value(0)
    led_vermelho.value(0)

def aplicar_leds(estado):
    """Acende apenas o LED correspondente ao estado atual."""
    apagar_todos()
    if estado == VERDE:
        led_verde.value(1)
    elif estado == AMARELO:
        led_amarelo.value(1)
    elif estado == VERMELHO:
        led_vermelho.value(1)

def btn_pressionado():
    """Retorna True se o botão está pressionado.
    O sleep de 20ms evita leituras falsas por bouncing."""
    if btn.value():
        time.sleep_ms(20)       # debounce
        return btn.value()      # confirma após estabilizar
    return False

# --- máquina de estados ---
estado = VERDE      # estado inicial
aplicar_leds(estado)
print("Semáforo iniciado — estado: VERDE")

while True:

    # ── VERDE ──────────────────────────────────────────────────────
    if estado == VERDE:
        # transição por evento: botão avança para AMARELO
        if btn_pressionado():
            estado = AMARELO
            aplicar_leds(estado)
            print("Transição: VERDE → AMARELO")
            time.sleep_ms(200)      # aguarda soltar o botão

    # ── AMARELO ────────────────────────────────────────────────────
    elif estado == AMARELO:
        # transição por tempo: 2 segundos avançam para VERMELHO
        time.sleep(2)
        estado = VERMELHO
        aplicar_leds(estado)
        print("Transição: AMARELO → VERMELHO  (timeout 2s)")

    # ── VERMELHO ───────────────────────────────────────────────────
    elif estado == VERMELHO:
        # transição por evento: botão volta para VERDE
        if btn_pressionado():
            estado = VERDE
            aplicar_leds(estado)
            print("Transição: VERMELHO → VERDE")
            time.sleep_ms(200)      # aguarda soltar o botão

    time.sleep_ms(50)   # cadência do loop principal

5. Circuito Wokwi — diagram.json

Cole o conteúdo abaixo no arquivo diagram.json do seu projeto Wokwi (sempre confira se está correto):

{
  "version": 1,
  "author": "RMB - Mini Curso Embarcados — Aula 5",
  "editor": "wokwi",
  "parts": [
    { "type": "wokwi-esp32-devkit-v1", "id": "esp", "top": 0, "left": 0, "attrs": {} },
    { "type": "wokwi-led", "id": "led_v", "top": 6, "left": 253.4, "attrs": { "color": "green" } },
    {
      "type": "wokwi-led",
      "id": "led_a",
      "top": 6,
      "left": 291.8,
      "attrs": { "color": "yellow" }
    },
    { "type": "wokwi-led", "id": "led_r", "top": 6, "left": 330.2, "attrs": { "color": "red" } },
    {
      "type": "wokwi-resistor",
      "id": "rv",
      "top": 89.8,
      "left": 248.75,
      "rotate": 270,
      "attrs": { "value": "220" }
    },
    {
      "type": "wokwi-resistor",
      "id": "ra",
      "top": 99.4,
      "left": 287.15,
      "rotate": 270,
      "attrs": { "value": "220" }
    },
    {
      "type": "wokwi-resistor",
      "id": "rr",
      "top": 118.6,
      "left": 325.55,
      "rotate": 270,
      "attrs": { "value": "220" }
    },
    { "type": "wokwi-pushbutton", "id": "btn", "top": 179, "left": 230.4, "attrs": {} },
    {
      "type": "wokwi-resistor",
      "id": "rpd",
      "top": 147.4,
      "left": 143.15,
      "rotate": 270,
      "attrs": { "value": "10000" }
    }
  ],
  "connections": [
    [ "esp:2", "rv:1", "green", [] ],
    [ "rv:2", "led_v:A", "green", [] ],
    [ "led_v:K", "esp:GND.1", "black", [] ],
    [ "esp:4", "ra:1", "yellow", [] ],
    [ "ra:2", "led_a:A", "yellow", [] ],
    [ "led_a:K", "esp:GND.1", "black", [] ],
    [ "esp:5", "rr:1", "red", [] ],
    [ "rr:2", "led_r:A", "red", [] ],
    [ "led_r:K", "esp:GND.1", "black", [] ],
    [ "esp:13", "btn:1.l", "blue", [] ],
    [ "btn:2.l", "esp:3V3", "red", [ "h-96", "v-39.2", "h-33.1" ] ],
    [ "btn:1.l", "rpd:1", "blue", [] ],
    [ "rpd:2", "esp:GND.1", "black", [ "v-8.4", "h-19.2", "v33.8" ] ],
    [ "rv:1", "esp:D2", "green", [ "v19.2", "h-28.8", "v-67.2", "h-115.2", "v53.6" ] ],
    [ "ra:1", "esp:D4", "gold", [ "v19.2", "h-76.8", "v-48", "h-115.2", "v14.4" ] ],
    [ "rr:1", "esp:D5", "red", [ "v9.6", "h-124.8", "v-76.8", "v4.9" ] ],
    [ "esp:GND.2", "led_r:C", "black", [ "h-24.2", "v-168.2", "h355.2", "v86.4", "h9.6" ] ],
    [ "led_a:C", "esp:GND.2", "black", [ "v19.2", "h-9.2", "v-86.4", "h-316.8", "v19.2" ] ],
    [ "led_v:C", "esp:GND.2", "black", [ "v19.2", "h-9.2", "v-86.4", "h-278.4", "v163.2", "v5" ] ],
    [ "esp:D13", "rpd:1", "blue", [ "h-53", "v110.1", "h220.8" ] ]
  ],
  "dependencies": {}
}

6. Experimento

Execute o código e observe o comportamento do semáforo.

a) Desenhe abaixo o diagrama de estados do semáforo conforme o comportamento que você observou — sem olhar para o diagrama da seção 1:

  Estado inicial: __________

  (  ________  ) ──[ condição: __________ ]──► (  ________  )
                                                      │
                                          [ condição: __________ ]
                                                      │
                                                      ▼
                                               (  ________  )
                                                      │
                                          [ condição: __________ ]
                                                      │
                                                      └──────────────► ...

b) No estado AMARELO o código usa time.sleep(2) e então muda de estado. Por que isso é diferente de como o estado VERDE e VERMELHO funcionam?



c) Retire o time.sleep_ms(20) dentro de btn_pressionado() e teste. O que acontece? Por que o debounce é necessário?



d) O código usa apagar_todos() antes de acender o LED do novo estado. O que aconteceria se você não fizesse isso?



7. Desafio

Desafio principal: adicione um quarto estado PISCA entre AMARELO e VERMELHO. Nesse estado o LED amarelo pisca 3 vezes antes de avançar para VERMELHO:

VERDE ──[btn]──► AMARELO ──[2s]──► PISCA ──[3 piscadas]──► VERMELHO ──[btn]──► VERDE
PISCA = 3   # nova constante de estado

# dentro do while True, adicione:
elif estado == PISCA:
    for _ in range(3):              # pisca 3 vezes
        led_amarelo.value(1)
        time.sleep(_____)
        led_amarelo.value(0)
        time.sleep(_____)
    estado = _________              # qual o próximo estado?
    aplicar_leds(estado)

Desafio bônus: modifique o estado VERMELHO para que, se o botão for mantido pressionado por mais de 3 segundos, o semáforo entre em modo EMERGENCIA — todos os LEDs piscam juntos indefinidamente até um reset:

EMERGENCIA = 4

elif estado == EMERGENCIA:
    apagar_todos()
    time.sleep(0.3)
    # acende todos simultaneamente
    led_verde.value(1)
    led_amarelo.value(1)
    led_vermelho.value(1)
    time.sleep(0.3)
    # este estado não tem saída — só reset resolve

Resumo da aula


*← Aula 4 Próxima → Aula 6: Flags na Prática — Máquina de Lavar*