MCU & FPGA UART (Serial) Protocolos auto-sincronizáveis em sistemas embarcados

Protocolos auto-sincronizáveis em sistemas embarcados


SLIP (Serial Line Internet Protocol): simplicidade que cobra seu preço

O SLIP é, historicamente, uma das primeiras tentativas de organizar dados em um fluxo serial contínuo. Ele nasceu em um contexto onde simplicidade era mais importante que robustez, e isso aparece claramente em seu desenho. O SLIP não define campos, não define tipos, não define verificação de integridade — ele apenas delimita pacotes.

A ideia central é direta: um byte especial (0xC0, chamado END) indica o fim de um pacote. Se esse byte aparecer dentro dos dados, ele é “escapado”. Com isso, o receptor consegue identificar onde termina uma mensagem, mesmo em um fluxo contínuo.

Essa abordagem funciona, mas carrega limitações estruturais que ficam evidentes assim que o sistema cresce.


Como o SLIP funciona (byte a byte)

O protocolo define apenas três valores relevantes:

ByteNomeFunção
0xC0ENDDelimitador de fim de pacote
0xDBESCEscape
0xDCESC_ENDRepresenta 0xC0 dentro dos dados
0xDDESC_ESCRepresenta 0xDB dentro dos dados

Regras:

  • Todo pacote termina com END
  • Se END aparecer nos dados, ele vira ESC + ESC_END
  • Se ESC aparecer nos dados, ele vira ESC + ESC_ESC

Isso garante que o byte END nunca aparece dentro do payload, apenas como delimitador.


Exemplo conceitual de framing SLIP

Payload original:

41 C0 42 DB 43

Após codificação SLIP:

41 DB DC 42 DB DD 43 C0

O receptor lê bytes até encontrar 0xC0, desfaz os escapes e reconstrói o payload.


Implementação didática — Encoder SLIP em C

#include <stdint.h>
#include <stddef.h>

#define SLIP_END     0xC0
#define SLIP_ESC     0xDB
#define SLIP_ESC_END 0xDC
#define SLIP_ESC_ESC 0xDD

size_t slip_encode(const uint8_t *in, size_t len, uint8_t *out)
{
    size_t j = 0;

    for (size_t i = 0; i < len; i++) {
        if (in[i] == SLIP_END) {
            out[j++] = SLIP_ESC;
            out[j++] = SLIP_ESC_END;
        } else if (in[i] == SLIP_ESC) {
            out[j++] = SLIP_ESC;
            out[j++] = SLIP_ESC_ESC;
        } else {
            out[j++] = in[i];
        }
    }

    out[j++] = SLIP_END;  // delimitador de pacote
    return j;
}

Esse código é típico de exemplos encontrados em projetos embarcados simples. Ele é fácil de entender, fácil de portar e fácil de depurar — e exatamente por isso é tão sedutor.


Decoder SLIP (lado crítico)

int slip_decode_byte(uint8_t byte, uint8_t *out, size_t *idx)
{
    static uint8_t esc = 0;

    if (byte == SLIP_END) {
        size_t size = *idx;
        *idx = 0;
        esc = 0;
        return (int)size; // pacote completo
    }

    if (esc) {
        if (byte == SLIP_ESC_END) out[(*idx)++] = SLIP_END;
        else if (byte == SLIP_ESC_ESC) out[(*idx)++] = SLIP_ESC;
        esc = 0;
        return -1;
    }

    if (byte == SLIP_ESC) {
        esc = 1;
        return -1;
    }

    out[(*idx)++] = byte;
    return -1;
}

Note o ponto crítico: qualquer erro de byte pode invalidar todo o pacote atual, e não há mecanismo interno para detectar isso.


Onde o SLIP é uma boa escolha

Boas escolhas

  • Debug simples via UART
  • Bootloaders muito pequenos
  • Prototipação rápida
  • Sistemas com link confiável (USB CDC, por exemplo)

Nesses cenários, a previsibilidade do meio compensa a fragilidade do protocolo.


Onde o SLIP é uma má escolha (e por quê)

Más escolhas

  • Ambientes industriais
  • Links com ruído elétrico
  • Sistemas com múltiplos nós
  • Protocolos binários estruturados
  • Comunicação crítica ou contínua

O SLIP não detecta erro, não valida comprimento, não permite recuperação rápida. Um byte perdido desloca todo o fluxo até o próximo END, e o sistema sequer sabe que recebeu lixo.

Em firmware de produção, isso normalmente se manifesta como:

  • pacotes “fantasmas”
  • comandos truncados
  • estados inválidos difíceis de reproduzir

Conclusão parcial

O SLIP ensina uma lição importante: framing é melhor do que nada, mas framing frágil vira dívida técnica. Ele é didático, histórico e ainda útil em nichos, mas não escala.

Na próxima seção, entraremos no COBS, um protocolo que resolve exatamente os problemas estruturais do SLIP — mantendo simplicidade, mas com auto-sincronização forte e previsível.

Related Post