5 — Desenhando pipelines completos de eventos no Zephyr
Do evento físico ao processamento industrial robusto
Agora que já entendemos interrupções, soft timers e workqueues isoladamente, é hora de unir tudo em algo que realmente aparece em firmware de produção:
um pipeline de eventos bem definido, onde cada etapa tem responsabilidade clara, latência previsível e possibilidade de evolução sem quebrar o sistema.
Aqui vamos tratar o pipeline como arquitetura, não como truque de API.
5.1 O modelo mental correto: firmware como fluxo de eventos
Em firmware industrial, nada “roda em loop” por acaso.
Tudo acontece porque algo aconteceu:
- Um sinal físico mudou (GPIO, ADC, encoder)
- Um tempo expirou (timeout, periodicidade)
- Um pacote chegou (UART, SPI, Ethernet)
- Um estado venceu (watchdog lógico)
Esses eventos entram no sistema e percorrem estágios bem definidos.
Modelo genérico:
[ Fonte de Evento ]
↓
[ Normalização / Debounce / Timeout ]
↓
[ Processamento ]
↓
[ Decisão ]
↓
[ Ação / Comunicação ]
No Zephyr, cada estágio mapeia naturalmente para uma primitiva.
5.2 Pipeline canônico no Zephyr
Vamos definir um pipeline realista para um sensor industrial IoT:
- Evento físico: pulso em GPIO
- Filtro temporal: debounce por timer
- Processamento: cálculo e validação
- Publicação: envio para rede ou fila
Mapeamento:
| Estágio | Primitiva Zephyr |
|---|---|
| Detecção | Interrupção |
| Normalização temporal | k_timer |
| Processamento | k_work |
| Comunicação | k_work (ou thread dedicada) |
5.3 Implementação passo a passo
1️⃣ Interrupção: entrada do pipeline
static void gpio_isr(const struct device *dev,
struct gpio_callback *cb,
uint32_t pins)
{
/* Evento bruto */
k_timer_start(&debounce_timer, K_MSEC(10), K_NO_WAIT);
}
Aqui:
- A ISR não decide nada
- Apenas inicia o próximo estágio
2️⃣ Soft Timer: debounce e normalização
static void debounce_timer_handler(struct k_timer *timer)
{
/* Evento validado no tempo */
k_work_submit_to_queue(&sensor_workq, &sensor_work);
}
K_TIMER_DEFINE(debounce_timer, debounce_timer_handler, NULL);
O timer:
- Remove ruído temporal
- Impede rajadas de eventos
- Dá previsibilidade
3️⃣ Workqueue: processamento real
static void sensor_work_handler(struct k_work *work)
{
int value = read_sensor();
if (value_is_valid(value)) {
enqueue_publish(value);
}
}
K_WORK_DEFINE(sensor_work, sensor_work_handler);
Aqui acontece:
- Leitura de drivers
- Validação
- Conversão
- Decisão
Tudo fora de ISR, tudo seguro.
4️⃣ Workqueue de comunicação (opcional, mas comum)
Em sistemas maiores, processar e comunicar são responsabilidades diferentes.
static void publish_work_handler(struct k_work *work)
{
send_over_mqtt();
}
K_WORK_DEFINE(publish_work, publish_work_handler);
Isso evita que latência de rede contamine o pipeline principal.
5.4 Por que esse pipeline é robusto
Esse desenho oferece:
- Isolamento de falhas
Se a comunicação falhar, o processamento não trava. - Controle de latência
Cada estágio tem custo conhecido. - Escalabilidade
Novos sensores entram sem mudar a arquitetura. - Observabilidade
É fácil instrumentar cada estágio. - Testabilidade
Cada etapa pode ser testada isoladamente.
Isso é exatamente o tipo de arquitetura exigida em:
- Automação industrial
- Equipamentos médicos
- Gateways IoT
- Sistemas críticos conectados
5.5 Anti-padrões comuns (e perigosos)
❌ ISR chamando driver complexo
❌ k_sleep() como debounce
❌ Tudo rodando na system workqueue
❌ Pipeline implícito em while(1)
❌ Dependência temporal “mágica”
Esses erros funcionam em demos…
e falham silenciosamente em produção.
5.6 Regra de ouro do pipeline no Zephyr
Eventos fluem.
Tempo valida.
Workqueues processam.
Threads coordenam.
Quando você passa a pensar assim, o Zephyr deixa de ser “mais um RTOS” e vira uma plataforma de firmware industrial de verdade.