Skip to content
Voltar ao blog
Tutoriais

Variantes CRC-16: por que MODBUS, CCITT e XMODEM diferem

Os mesmos bytes, quatro resultados CRC-16 diferentes. Veja online como poly, init, refin, refout e xorout separam MODBUS de CCITT-FALSE e XMODEM.

13 min de leitura

Variantes CRC-16: por que MODBUS, CCITT e XMODEM diferem

CRC-16 não é um algoritmo. É uma família, e seus membros discordam entre si. Entregue os mesmos bytes ao MODBUS, ao CCITT-FALSE e ao XMODEM e você recebe de volta três números de 16 bits sem nenhuma relação.

Cinco constantes decidem qual membro você está rodando: poly, init, refin, refout e xorout. Mude uma delas e a saída muda por completo, sem nenhuma semelhança parcial para servir de aviso. Essa é a resposta inteira para “por que meu CRC não bate com o do dispositivo”: você e o dispositivo estão rodando variantes diferentes, e nenhum dos dois lados anotou qual.

O resto é detalhe, e o detalhe custa uma tarde quando você erra: o que cada parâmetro faz, como descobrir qual variante está do outro lado do cabo, em que ordem o Modbus RTU joga os dois bytes no fio, e por que um CRC segura ruído de linha e não segura um atacante.

Todos os valores deste guia saíram de um modelo CRC parametrizado rodado em Python 3.14.7 no macOS, conferidos de quatro maneiras: contra zlib.crc32 e binascii.crc_hqx da biblioteca padrão, contra os valores de verificação publicados no catálogo RevEng CRC e contra a documentação dos fabricantes de cada conjunto de parâmetros.

1. O mesmo quadro, quatro resultados CRC-16

Esta é uma requisição Modbus RTU real. Escravo 01, código de função 03 (leitura de registradores holding), endereço inicial 0x0000, quantidade 0x000A:

01 03 00 00 00 0A

Seis bytes, e quatro variantes devolvem quatro respostas:

VarianteResultadoNo fio (byte baixo primeiro)
CRC-16/MODBUS0xCDC5C5 CD
CRC-16/IBM-3740 (CCITT-FALSE)0x042828 04
CRC-16/XMODEM0x0A3838 0A
CRC-16/ARC0xD6C5C5 D6

Nada liga esses quatro valores. Não compartilham padrão de nibbles nem distância constante, e nenhum rearranjo de bytes converte um no outro. Dois deles começam com o mesmo byte no fio, o que é coincidência e não parentesco.

A consequência prática: a expressão “CRC-16” numa folha de dados quase não carrega informação. Ela diz que a saída tem 16 bits de largura e para por aí. Se você quiser olhar um resultado em outra base enquanto compara com um dispositivo que imprime em binário, o conversor de bases numéricas alterna um valor de 16 bits entre hexadecimal e binário sem que você precise pensar em preenchimento.

2. Cinco parâmetros decidem tudo

Por baixo de toda variante existe a mesma máquina: um registrador de deslocamento que engole um bit por vez e faz XOR com uma constante sempre que um 1 cai pelo topo. Os parâmetros decidem o que entra no registrador, em que direção os bits viajam, e o que ainda acontece com o resultado depois que o último byte já passou.

O motor inteiro cabe em catorze linhas de Python. É uma implementação bit a bit, lenta mas legível, e reproduz todos os valores deste artigo:

def crc(data, width, poly, init, refin, refout, xorout):
    top = 1 << (width - 1)
    mask = (1 << width) - 1
    reg = init
    for byte in data:
        if refin:
            byte = int(f"{byte:08b}"[::-1], 2)
        reg ^= byte << (width - 8)
        for _ in range(8):
            reg = ((reg << 1) ^ poly) if reg & top else (reg << 1)
            reg &= mask
    if refout:
        reg = int(f"{reg:0{width}b}"[::-1], 2)
    return reg ^ xorout

A saída do motor pode ser conferida contra a biblioteca padrão do Python. zlib.crc32 e binascii.crc_hqx dão cada um uma resposta independente, e as três batem:

zlib.crc32(b"123456789")               = 0xCBF43926   engine above = 0xCBF43926   MATCH
binascii.crc_hqx(b"123456789", 0x0000) = 0x31C3       CRC-16/XMODEM = 0x31C3      MATCH
binascii.crc_hqx(b"123456789", 0xFFFF) = 0x29B1       CCITT-FALSE   = 0x29B1      MATCH

poly: dois campos, 0x8005 e 0x1021

O polinômio é a constante que volta ao registrador por XOR. Ele é escrito na forma “normal”, com o bit mais alto implícito, então 0x8005 significa x^16 + x^15 + x^2 + 1 e 0x1021 significa x^16 + x^12 + x^5 + 1.

Quase todo CRC-16 que você encontra usa um desses dois. 0x8005 cobre ARC, MODBUS e USB. 0x1021 cobre todo o emaranhado CCITT mais XMODEM, KERMIT e as variantes de RFID. É por isso que o polinômio sozinho nunca identifica uma variante.

O laço de deslocamento e XOR é uma divisão polinomial longa sobre GF(2). Cada bit da mensagem é um passo da divisão, o polinômio é o divisor e o registrador guarda o resto corrente.

init: o valor inicial do registrador

Dois valores dominam, 0x0000 e 0xFFFF. A diferença pesa mais do que parece. Comece em zero e um byte zero deixa o registrador em zero, de modo que 00 00 01 e 01 produzem CRCs idênticos. Uma mensagem que ganha ou perde zeros à esquerda no caminho passa na verificação. Começar em 0xFFFF elimina esse ponto cego, e é por isso que Modbus, CCITT-FALSE e USB usam esse valor.

refin e refout: ordem dos bits, não ordem dos bytes

refin inverte os oito bits de cada byte de entrada antes que ele chegue ao registrador. refout inverte o registrador final antes do último XOR. Hardware que desloca os dados para fora com o bit menos significativo primeiro ganha essas reflexões de graça, e por isso as variantes refletidas costumam ser as que nasceram em protocolos seriais.

Esse é o par de parâmetros que mais se confunde com endianness. É outra coisa, em outro nível, e a seção 6 separa os dois.

xorout: o XOR final

O último passo, aplicado ao registrador depois de refout. Normalmente 0x0000 ou 0xFFFF no CRC-16, e 0xFFFFFFFF em toda a família CRC-32. É o parâmetro mais barato de errar, porque uma divergência aqui parece exatamente igual a uma divergência em qualquer outro ponto.

width: 8, 16 ou 32 bits

A largura define o teto de detecção. Um CRC de largura n pega todo erro em rajada de até n bits e deixa passar uma corrupção aleatória com probabilidade em torno de 2^-n. Isso dá 1 em 65.536 para o CRC-16 e 1 em 4,3 bilhões para o CRC-32.

Partindo do CRC-16/XMODEM e mudando exatamente um parâmetro por vez, contra a entrada de sondagem padrão 123456789:

baseline CRC-16/XMODEM                        = 0x31C3
init 0x0000 -> 0xFFFF   (becomes CCITT-FALSE) = 0x29B1
refin/refout -> true    (becomes KERMIT)      = 0x2189
xorout 0x0000 -> 0xFFFF                       = 0xCE3C
poly 0x1021 -> 0x8005                         = 0xFEE8

Um sinalizador invertido e a saída já não tem nada a ver com a anterior. CRC não tem noção de “perto”. Dois resultados ou batem, ou não carregam informação nenhuma sobre quão distantes estavam as entradas, e é por isso que ficar encarando uma divergência nunca localiza a causa. O laço interno é só XOR e deslocamentos; o guia completo de operações bit a bit cobre esses operadores em si, e este artigo já os pressupõe.

3. “CCITT” é um nome ruim

O catálogo CRC do RevEng lista, em setembro de 2026, 31 definições distintas de CRC de 16 bits, e três delas carregam o rótulo CCITT. Essas três já foram chamadas de CCITT por aí, e nenhuma delas concorda com as outras. Olhe as quatro linhas abaixo. Mesmo polinômio, mesma entrada, quatro resultados sem relação:

Nome comumNome no catálogocheckinitrefin/refoutxorout
CCITT-FALSECRC-16/IBM-37400x29B10xFFFFfalse0x0000
XMODEMCRC-16/XMODEM0x31C30x0000false0x0000
o CCITT “de verdade”CRC-16/KERMIT0x21890x0000true0x0000
CRC-16/MCRF4XX0x6F910xFFFFtrue0x0000

A história é curta e não ajuda. O catálogo lista o CRC-16/KERMIT com os apelidos CRC-CCITT e CRC-16/CCITT-TRUE, e essa variante é refletida. Uma implementação não refletida com init 0xFFFF também circulou bastante sob o nome CCITT, e por isso o catálogo registra “CCITT-FALSE” como apelido do CRC-16/IBM-3740. O XMODEM fica no meio dos dois: mesmo polinômio, sem reflexão, init zero.

Ou seja, quando uma folha de dados diz CCITT, ela te informou que o polinômio é 0x1021 e mais nada. Você continua com quatro candidatos, e escolher errado devolve um valor tão plausível quanto o certo.

O hábito que vale cultivar: cite parâmetros, não nomes. Escrever poly=0x1021, init=0xFFFF, refin=false, refout=false, xorout=0x0000 no seu documento de protocolo ocupa uma linha e elimina a ambiguidade de vez. Escrever “CRC-16/CCITT” não.

4. Como identificar qual variante de CRC-16 você tem em mãos

O catálogo resolve isso com uma impressão digital. Toda entrada publica um valor de verificação (check): o CRC dos nove bytes ASCII 123456789. Passe essa string pela implementação que está na sua frente e depois procure o resultado.

Variantecheckpolyinitrefinrefoutxorout
CRC-16/ARC (IBM/LHA)0xBB3D0x80050x0000truetrue0x0000
CRC-16/MODBUS0x4B370x80050xFFFFtruetrue0x0000
CRC-16/USB0xB4C80x80050xFFFFtruetrue0xFFFF
CRC-16/IBM-3740 (CCITT-FALSE)0x29B10x10210xFFFFfalsefalse0x0000
CRC-16/XMODEM0x31C30x10210x0000falsefalse0x0000
CRC-16/KERMIT (o CCITT de verdade)0x21890x10210x0000truetrue0x0000
CRC-16/GENIBUS0xD64E0x10210xFFFFfalsefalse0xFFFF
CRC-16/MCRF4XX0x6F910x10210xFFFFtruetrue0x0000
CRC-32/ISO-HDLC (zip, PNG, zlib)0xCBF439260x04C11DB70xFFFFFFFFtruetrue0xFFFFFFFF
CRC-32/BZIP20xFC8919180x04C11DB70xFFFFFFFFfalsefalse0xFFFFFFFF
CRC-32C (Castagnoli, iSCSI)0xE30692830x1EDC6F410xFFFFFFFFtruetrue0xFFFFFFFF
CRC-8/SMBUS0xF40x070x00falsefalse0x00
CRC-8/MAXIM-DOW (1-Wire)0xA10x310x00truetrue0x00

Um detalhe afunda um número surpreendente dessas comparações: 123456789 significa os nove bytes 31 32 33 34 35 36 37 38 39, não o número 123456789 e não uma string terminada em nulo. Se a sua linguagem entrega à função uma string larga ou acrescenta um terminador, você está calculando o hash de outra entrada e nenhuma linha vai bater. Cargas úteis não ASCII trazem uma segunda armadilha: o mesmo texto codificado em UTF-8 e em Windows-1252 são duas sequências de bytes diferentes e, portanto, dois CRCs diferentes. A tabela ASCII e conversor mostra o byte de cada caractere, o que leva dez segundos e descarta a dúvida.

Se o valor de verificação não bate com nenhuma linha, descarte primeiro as causas banais antes de suspeitar de um polinômio próprio:

  1. Ordem dos bytes: você pode estar lendo o resultado invertido do fio.
  2. Campos cobertos: o remetente inclui ou exclui um byte de endereço, um byte de comprimento ou o próprio campo CRC.
  3. Um init do fabricante que não é nem 0x0000 nem 0xFFFF; isso acontece em protocolos proprietários de medidores e costuma ficar enterrado numa nota de rodapé.

Descobrindo os parâmetros por força bruta

Quando o fabricante não conta e você não consegue ler o firmware dele, peça só uma coisa: um quadro capturado junto com o CRC que o dispositivo produziu para ele. Depois é só enumerar. Dois polinômios, dois valores de init, refin e refout independentes e dois valores de xorout dão 32 combinações, ou seja, nada:

target = 0x4B37                    # o valor que a implementação deles retornou
for poly in (0x8005, 0x1021):
    for init in (0x0000, 0xFFFF):
        for refin in (False, True):
            for refout in (False, True):
                for xorout in (0x0000, 0xFFFF):
                    if crc(b"123456789", 16, poly, init, refin, refout, xorout) == target:
                        print(f"poly=0x{poly:04X} init=0x{init:04X} "
                              f"refin={refin} refout={refout} xorout=0x{xorout:04X}")
poly=0x8005 init=0xFFFF refin=True refout=True xorout=0x0000

Um acerto, e ele é o CRC-16/MODBUS. Alimente o laço com um quadro real em vez de 123456789 e a mesma varredura de 32 vias funciona para qualquer mensagem cujo CRC correto você conheça. Se várias combinações sobreviverem a uma amostra, rode um segundo quadro e cruze os resultados.

5. Modbus RTU na prática

O Modbus RTU usa o CRC-16/MODBUS: polinômio 0x8005, init 0xFFFF, refletido na entrada e na saída, sem XOR final. Para a nossa requisição de seis bytes o CRC é 0xCDC5, e o quadro completo fica assim:

01 03 00 00 00 0A C5 CD

A ordem no fio é o inverso de como você escreve o valor

O valor é 0xCDC5. Os bytes anexados ao quadro são C5 CD. O Modbus especifica o byte baixo do CRC primeiro, o contrário da ordem em que você lê o número hexadecimal, e isso pega as pessoas todas as vezes. Todos os outros campos de vários bytes no mesmo quadro, inclusive aquela contagem de registradores 00 0A, são big-endian. O CRC é a exceção.

Daí sai uma consequência útil. Calcule o CRC-16/MODBUS sobre o quadro completo, com os bytes do CRC incluídos, e o resultado é:

0x0000

Essa é a rotina de validação inteira do receptor. Não é preciso separar os dois bytes finais, trocá-los de ordem e comparar com um valor calculado localmente. Rode o CRC sobre tudo o que chegou e verifique se dá zero. Menos passos significa menos lugares onde inverter os bytes errado.

Por que a documentação do Modbus mostra 0xA001

Abra quase qualquer implementação de Modbus e a constante no código-fonte é 0xA001, não 0x8005. As duas estão certas. 0xA001 é 0x8005 com seus 16 bits invertidos, e pertence à forma refletida do algoritmo, na qual o registrador desloca para a direita em vez de para a esquerda e os bytes de entrada dispensam a inversão individual. As duas implementações produzem saída idêntica; só as tripas mudam. Ver 0xA001 no código é um sinal confiável de que você está diante de um CRC-16 refletido, e não de um polinômio diferente.

Um parêntese para quem estiver lendo uma captura. O Modbus ASCII é um enquadramento à parte, que carrega cada byte como dois caracteres hexadecimais entre um 3A (:) inicial e um 0D 0A final, e usa um LRC em vez de um CRC. Se o seu trace mostra 3A 30 31 30 33 onde você esperava 01 03, você está em modo ASCII e nenhuma variante de CRC vai bater. A tabela ASCII mapeia esses bytes de volta para caracteres.

O mesmo algoritmo em C

O firmware Modbus quase nunca usa o modelo em Python acima. Ele emprega diretamente a forma refletida: deslocar à direita e aplicar XOR com 0xA001.

uint16_t crc16_modbus(const uint8_t *buf, size_t len) {
    uint16_t crc = 0xFFFF;
    for (size_t i = 0; i < len; i++) {
        crc ^= buf[i];
        for (int b = 0; b < 8; b++)
            crc = (crc & 1) ? (crc >> 1) ^ 0xA001 : crc >> 1;
    }
    return crc;
}

Com o quadro 01 03 00 00 00 0A, esta função devolve 0xCDC5, o mesmo valor que o motor em Python produziu. A string de verificação 1234567890x4B37. Ambos foram executados no Apple clang 21 e coincidem bit a bit com a tabela da seção 4.

6. refin/refout não é endianness

A confusão sai cara justamente porque um quadro Modbus carrega as duas coisas ao mesmo tempo, e quem mexe na errada troca um sintoma por outro sem chegar mais perto.

Endianness trata de bytes dentro de um valor de vários bytes: se 0xCDC5 é armazenado ou transmitido como CD C5 ou como C5 CD. Nada se move dentro de um byte. Esse é o nível que o guia big-endian versus little-endian cobre por inteiro, e este artigo não vai repetir.

Reflexão fica um nível abaixo, nos bits dentro de um único byte. refin inverte os oito bits de cada byte antes que o registrador o veja: o bit 0 vira o bit 7. A posição do byte na mensagem nunca muda. Endianness não enxerga isso, e a reflexão não enxerga endianness.

Num quadro Modbus os dois acontecem, de forma independente:

  • Dentro do algoritmo, refin e refout são true, então os bits são invertidos dentro dos bytes.
  • No fio, o CRC pronto é anexado com o byte baixo primeiro, o que é uma decisão de ordem de bytes tomada pelo protocolo.

Desligar a reflexão para “consertar” um problema de ordem de bytes produz uma variante completamente diferente, e trocar os bytes finais do quadro para compensar uma reflexão errada produz um valor errado de um jeito novo. Diagnostique um de cada vez: acerte primeiro o valor de verificação só com o algoritmo, depois cuide do layout do quadro.

7. CRC-32 ou CRC-16: qual você deve usar?

O CRC-32 tem o mesmo problema do CRC-16, só que de forma menos visível, porque uma variante domina tão completamente que a maioria dos desenvolvedores nunca fica sabendo que existem outras.

Variantecheckpolyrefin/refout
CRC-32/ISO-HDLC0xCBF439260x04C11DB7true
CRC-32/BZIP20xFC8919180x04C11DB7false
CRC-32C (Castagnoli)0xE30692830x1EDC6F41true

O ISO-HDLC é o que zlib.crc32 calcula, e ele está em toda parte no lado web da pilha. O zip guarda um por entrada no diretório central, e todo chunk de PNG termina com um cobrindo o tipo e os dados do chunk. A sequência de verificação de quadro da Ethernet usa o mesmo conjunto de parâmetros, no fim de cada quadro que o seu servidor envia. O BZIP2 é o mesmo polinômio com as reflexões desligadas, o que produz um valor que não tem nada em comum com o do ISO-HDLC.

O CRC-16 também aparece na mesma vizinhança: o Redis Cluster roteia uma chave para um dos seus 16.384 slots com CRC16(key) mod 16384, e é por isso que chaves com a mesma hash tag caem no mesmo nó.

Por que o CRC-32C ganhou na loteria do hardware

O CRC-32C usa um polinômio diferente, com detecção de erros melhor nos blocos curtos que os protocolos de armazenamento e de rede realmente enviam. Isso lhe rendeu o iSCSI, os checksums de metadados do ext4, o SCTP e o Btrfs. Depois a Intel o colocou no silício: a instrução crc32 do SSE4.2 calcula CRC-32C diretamente, o que o transforma numa verificação de integridade praticamente gratuita no x86 moderno. Se você está escolhendo um CRC para código novo hoje e não tem restrição de compatibilidade, é este que deve pegar.

Nenhum deles é um hash para verificar um download. Quando você quer um checksum para confirmar que um arquivo chegou inteiro, o gerador de hash MD5 é a ferramenta mais familiar, e MD5 versus SHA-256 explica em qual digest confiar para cada coisa. A divisão é essa: um CRC responde “isto foi corrompido?”, um hash criptográfico responde “isto é exatamente o conteúdo que eu espero?”.

8. Um CRC não impede adulteração

Aqui estão duas mensagens JSON com sentidos opostos e o mesmo CRC-32:

message A = b'{"to":"alice","amount":100}  H\xf6\xc0E'
message B = b'{"to":"mallory","amount":999}\xb2\xb2\xc2\xc2'
CRC-32(A) = 0x12345678
CRC-32(B) = 0x12345678

SHA-256 sobre essas mesmas duas:

A -> a83b90f5e3f21dd32039e0e693a8b15864eda83e258c115deeee50a60c09ae23
B -> f9319ee0507d719c7260535dff33df721ddd9c0e1857690f3d8e02f92601b742

A colisão em si não é a parte interessante; interessante é como ela foi obtida. Ninguém quebrou nada por força bruta aqui: os bytes finais saíram de uma conta. O CRC é uma função linear, e anexar quatro bytes a uma mensagem mapeia na saída do CRC-32 como uma bijeção, de modo que, para qualquer mensagem e qualquer valor alvo, existe exatamente um sufixo de quatro bytes que leva até lá. Achá-lo é aritmética.

A demonstração, anexando os quatro bytes resolvidos 46 C9 6E 0B a hello world:

CRC-32(b'hello world' + 46 C9 6E 0B) = 0xDEADBEEF   target 0xDEADBEEF   MATCH

Ou seja, um atacante capaz de modificar o seu payload também consegue ajustar o seu CRC, em tempo constante, sem precisar procurar nada. Prefixar a mensagem com um segredo não salva: para uma edição que preserve o comprimento, a correção aplicada ao CRC depende apenas dos bits que mudaram, então dá para calculá-la sem jamais conhecer o segredo. Esse é o formato do ataque que quebrou o WEP.

O CRC defende contra ruído de transmissão, que é aleatório e não quer nada. Contra um adversário, que escolhe onde bater, ele não defende. Quando o requisito é autenticidade e não integridade, você precisa de uma construção com chave: o gerador de HMAC produz uma sobre qualquer payload e chave, e por que a verificação de assinatura de webhook falha percorre as partes que derrubam as pessoas na hora de ligar isso a um endpoint de verdade.

9. Perguntas frequentes

CRC-16/CCITT é a mesma coisa que CRC-16/CCITT-FALSE?

Não, CRC-16/CCITT-FALSE e CRC-16/CCITT são variantes diferentes. O CCITT-FALSE é o CRC-16/IBM-3740: init 0xFFFF, sem reflexão, valor de verificação 0x29B1. A variante que se costuma querer dizer com um CCITT sem sobrenome é o CRC-16/KERMIT: init 0x0000, refletido, check 0x2189. Os dois compartilham o polinômio 0x1021 e não concordam em mais nada.

Por que uma calculadora online discorda do meu dispositivo?

Parâmetros diferentes, quase sempre. A ferramenta assume uma variante por padrão e o dispositivo implementa outra. Passe os bytes ASCII 123456789 pelos dois, compare os dois valores de verificação com a tabela da seção 4 e o parâmetro divergente costuma se entregar em menos de um minuto.

Por que o Modbus coloca o byte baixo do CRC primeiro?

Porque a especificação manda, e esse é o único campo do quadro que se comporta assim. Endereços e contagens de registradores são big-endian; o CRC final não é. Calcule o CRC-16/MODBUS sobre o quadro inteiro, incluindo esses dois bytes, e verifique se dá 0x0000, em vez de reordená-los por conta própria.

O que exatamente refin e refout invertem?

Bits dentro de um byte, nunca bytes dentro de uma mensagem. refin inverte os oito bits de cada byte de entrada antes do processamento; refout inverte o registrador final antes do último XOR. Os dois são independentes de endianness, que governa como um valor de vários bytes é disposto no fio.

Devo usar CRC-16 ou CRC-32?

O CRC-16 deixa passar uma corrupção aleatória mais ou menos uma vez a cada 65.536; o CRC-32, uma vez a cada 4,3 bilhões. Para quadros seriais curtos, de algumas dezenas de bytes, o CRC-16 dá conta e normalmente já é exigido pelo protocolo de qualquer forma. Para arquivos, quadros de rede e qualquer coisa acima de alguns kilobytes, use CRC-32.

Dá para usar um CRC como assinatura de API?

Não, um CRC não pode servir como assinatura de API. O CRC é linear, então quem alterar o payload consegue recalcular um valor que bate, e anexar quatro bytes escolhidos alcança qualquer alvo de CRC-32 que você indicar. Assinaturas precisam de chave secreta e de uma construção não linear. Use HMAC com SHA-256.

Dá para recuperar os dados originais a partir de um valor de CRC?

Não, os dados originais não podem ser recuperados a partir de um valor CRC. Um CRC-16 comprime qualquer entrada em 16 bits, então infinitas mensagens compartilham cada valor. Mesmo assim, o sentido inverso continua útil para atacantes: dado um valor alvo, você consegue construir uma mensagem que o produz, e é exatamente por isso que um CRC não autentica nada.

O documento do fabricante só diz “CRC-16”. Como fixo os parâmetros?

Peça um quadro capturado mais o CRC que o dispositivo calculou para ele e rode a varredura de 32 combinações da seção 4 contra esse par. Se sobrar mais de um conjunto de parâmetros, repita com um segundo quadro e fique com a interseção. Duas amostras quase sempre resolvem.

Tags: crc checksum modbus embedded data-integrity

Artigos relacionados

Ver todos os artigos