Mais um assunto sugerido pelos
seguidores do meu canal no Youtube: protocolo CAN (Controller Area Network). É sobre
isso que vamos falar no vídeo de hoje. Importante explicar que o CAN é um
protocolo de comunicação serial simultânea. Ou seja, o sincronismo entre os
módulos conectados à rede é feito em relação ao início de cada mensagem lançada
ao barramento. Vamos, então, introduzir os conceitos básicos do
protocolo CAN e realizar uma montagem simples com dois ESP32.
Considero este protocolo
fantástico. Ele tem boa imunidade a ruídos, bastante utilizado em veículos e máquinas,
além de ser um protocolo de controle muito bom e inteligente.
No nosso circuito, os ESPs
poderão atuar tanto como Master quanto como Slave. Você poderá ter vários microcontroladores
transmitindo de forma simultânea, isso porque o CAN trata da colisão disso tudo
de forma automática. O código fonte deste projeto é super simples. Confira!
Recursos usados
·
Dois módulos ESP WROOM 32 NodeMcu
·
Dois módulos transceivers CAN da WaveShare
·
Jumper’s para conexões
·
Analisador lógico para captura
·
Três cabos USB para os ESPs e analisador
·
10 metros de par trançado para servir de
barramento
CAN (Controller Area Network)
·
Desenvolvido pela Robert Bosch GmbH, nos anos de
1980, para atender a indústria automobilística.
·
Tornou-se muito difundida com o passar dos anos
devido a sua robustez e flexibilidade de implementação, sendo introduzida em equipamentos
militares, máquinas agrícolas, na automação industrial e predial, robótica e
equipamentos médicos.
CAN - Algumas características
·
Comunicação serial de dois fios
·
Máximo de 8 bytes de informação útil por quadro,
sendo possível fragmentação.
·
Endereçamento direcionado a mensagem e, não, ao
nó.
·
Atribuição de prioridade às mensagens e
retransmissão de mensagens “em espera”
·
Capacidade eficaz de detectar e sinalizar erros.
·
Capacidade multi-mestre (todos os nós podem
pedir acesso ao barramento)
·
Capacidade multicast (uma mensagem para vários
receptores ao mesmo tempo)
·
Taxas de
transferências de até 1Mbit/s em barramento de 40 metros (redução da taxa com
aumento do comprimento do barramento).
·
Flexibilidade
de configuração e introdução de novos nós (comportando até 120 nós por
barramento).
·
Hardware
padrão, baixo custo e boa disponibilidade.
·
Protocolo
regulado: ISO 11898
Circuito utilizado
Então, aqui, eu tenho os
Transceivers, um de cada lado, ligados por um par de fios. Um deles é
responsável pelo envio e, o outro, pelo recebimento de dados.
Tensões na linha de transmissão (detecção diferencial)
No CAN o bit dominante é o
Zero.
Detecção diferencial na linha
reduz a sensibilidade a ruídos (EFI)
Padrões de CAN e formato das frames
Formato padrão com
identificador de 11 bits
Formato estendido com
identificador de 29 bits
É importante que um protocolo
já calcule o CRC e mande sinais ACK e EOF, coisas que já são feitas pelo
protocolo CAN, que garantem que a mensagem enviada não vai chegar de forma
errada. Isso porque, se der problema no CRC, ou seja, no Check Cíclico de Redundância ou Verificação Cíclica de Redundância, que é o mesmo que um dígito verificador de informação, esse será
identificado pelo CRC.
Quatro tipos de frames (quadros)
A transmissão e recepção de
dados no CAN são baseadas em quatro tipos de frames. Os tipos de frames serão
identificados pelas variações nos bits de controle ou até por mudanças nas
regras de escrita do frame para cada caso.
·
Frame de Dados: Contem os dados do emissor para
o(s) receptor(es)
·
Frame Remota: É uma solicitação de dados
partindo de um dos nós
·
Frame de Erro: É um frame enviado por qualquer
um dos nós ao identificar um erro no barramento e pode ser detectado por todos
os nós
·
Frame de sobrecarga: Serve para retardar o tráfego
no barramento devido à sobrecarga de dados ou atraso em um ou mais nós.
Circuito – detalhe das conexões
Circuito – Captura dos dados
Formas de onda obtidas para
CAN padrão com ID de 11 bits:
Dados obtidos pelo analisador
lógico:
Biblioteca Arduino – CAN
Deixo aqui as duas opções para
você instalar a Biblioteca para controlador CAN
Gerenciador de Bibliotecas da
IDE do Arduino:
Código-fonte do Transmissor
Código-fonte: Includes e Setup()
Vamos incluir a biblioteca
CAN, iniciar a serial para debug e o barramento CAN a 500 kbps.
#include <CAN.h> //Inclui a biblioteca CAN void setup() { Serial.begin(9600); //inicia a serial para debug while (!Serial); Serial.println("Transmissor CAN"); // Inicia o barramento CAN a 500 kbps if (!CAN.begin(500E3)) { Serial.println("Falha ao iniciar o controlador CAN"); //caso não seja possível iniciar o controlador while (1); } }
Código-fonte: Loop(), enviando um pacote CAN 2.0 padrão
Usando o CAN 2.0 padrão, enviamos
um pacote. O ID, de 11 bits, identifica a mensagem. O bloco de dados deve possuir
até 8 bytes. Ele inicia o pacote com ID 18 em hexadecimal. Empacota 5 bytes e
fecha a função.
void loop() { // Usando o CAN 2.0 padrão //Envia um pacote: o id tem 11 bits e identifica a mensagem (prioridade, evento) //o bloco de dados deve possuir até 8 bytes Serial.println("Enviando pacote..."); CAN.beginPacket(0x12); //id 18 em hexadecimal CAN.write('h'); //1º byte CAN.write('e'); //2º byte CAN.write('l'); //3º byte CAN.write('l'); //4º byte CAN.write('o'); //5º byte CAN.endPacket(); //encerra o pacote para envio Serial.println("Enviado."); delay(1000);
Código-fonte: Loop(), enviando um pacote CAN 2.0 estendido
Nesta etapa, o ID tem 29 bits.
Inicia o envio de 24 bits de ID e, mais uma vez, empacota 5 bytes e encerra.
//Usando CAN 2.0 Estendido //Envia um pacote: o id tem 29 bits e identifica a mensagem (prioridade, evento) //o bloco de dados deve possuir até 8 bytes Serial.println("Enviando pacote estendido..."); CAN.beginExtendedPacket(0xabcdef); //id 11259375 decimal ( abcdef em hexa) = 24 bits preenchidos até aqui CAN.write('w'); //1º byte CAN.write('o'); //2º byte CAN.write('r'); //3º byte CAN.write('l'); //4º byte CAN.write('d'); //5º byte CAN.endPacket(); //encerra o pacote para envio Serial.println("Enviado."); delay(1000); }
Código-fonte do Receptor
Código-fonte: Includes e Setup()
Novamente, vamos incluir a
biblioteca CAN, iniciar a serial para debug e o barramento CAN a 500 kbps. Caso
ocorra algum erro, será impresso este erro.
#include <CAN.h> //Inclui a biblioteca CAN void setup() { Serial.begin(9600); //inicia a serial para debug while (!Serial); Serial.println("Receptor CAN"); // Inicia o barramento CAN a 500 kbps if (!CAN.begin(500E3)) { Serial.println("Falha ao iniciar o controlador CAN"); //caso não seja possível iniciar o controlador while (1); } }
Código-fonte: Loop(), obtendo o pacote e verificando o formato
Tentamos verificar o tamanho
do pacote recebido. O método CAN.parsePacket() me retorna o tamanho deste
pacote. Então, se tivermos um pacote, verificamos se este é estendido ou não.
void loop() { // Tenta verificar o tamanho do acote recebido int packetSize = CAN.parsePacket(); if (packetSize) { // Se temos um pacote Serial.println("Recebido pacote. "); if (CAN.packetExtended()) { //verifica se o pacote é estendido Serial.println("Estendido"); }
Código-fonte: Loop(), verifica se é um pacote remoto
Aqui verificamos se o pacote
recebido é uma requisição de dados. Neste caso, não há dados.
if (CAN.packetRtr()) { //Verifica se o pacote é um pacote remoto (Requisição de dados), neste caso não há dados Serial.print("RTR "); }
Código-fonte: Loop(), comprimento do dado solicitado ou recebido
Se o pacote recebido é de
requisição, indicamos o comprimento solicitado. Obtemos, então, o DLC (Data
Length Code), que indica o comprimento dos dados. Por fim, indicamos o
comprimento recebido.
Serial.print("Pacote com id 0x"); Serial.print(CAN.packetId(), HEX); if (CAN.packetRtr()) { //se o pacote recebido é de requisição, indicamos o comprimento solicitado Serial.print(" e requsitou o comprimento "); Serial.println(CAN.packetDlc()); //obtem o DLC (Data Length Code, que indica o comprimento dos dados) } else { Serial.print(" e comprimento "); // aqui somente indica o comprimento recebido Serial.println(packetSize);
Código-fonte: Loop(), se há dados recebidos, os imprime
Imprimimos no monitor serial os
dados somente se o pacote recebido não for de requisição.
//Imprime os dados somente se o pacote recebido não foi de requisição while (CAN.available()) { Serial.print((char)CAN.read()); } Serial.println(); } Serial.println(); } }
13 Comentários
Boa tarde
ResponderExcluirO link para o arquivo em pdf não esta funcionando.
O link para pdf esta quebrado!
ResponderExcluirOlá. O link foi atualizado. Obrigado pelo aviso! Abraço
ResponderExcluirO link já foi corrigido, muito obrigado!
ResponderExcluirCaro Fernando. Primeiramente parabéns pelo artigo excelente. Gostaria de usar este mesmo metodo para fazer um simulador de ECU de carro. Voce tem ou pode indicar algum material a respeito. Quero simular por exemplo a rotacao do motor e colocar um scanner a ler na outra ponta.
ResponderExcluirGrande abraço e mais uma vez parabéns pelo seu trabalho.
Boa tarde Fernando Teria um exemplo de como trabalhar com rede can J1939 250k com ESP32 nao achei nada na internet.
ResponderExcluirPode ser um exemplo bem simples.
Estou tentando, mas fica travado em:
ResponderExcluirSerial.println("Enviando pacote...");
Testei depis os pinos 4 e 5 para acender LEDs e estão funcionando, tentei mudar pino com
CAN.setPins(4,5);
mas nada
Sigo bastante você, nos seus projetos com ESP
Obrigada
Oi Marília, tudo bem? Estou com o mesmo problema, vc conseguiu resolver. abcs.
ExcluirTambém estou com mesmo problema. Alguém achou uma solução?
ExcluirAlguém conseguiu resolver esse problema? Estou sofrendo com ele também.
ExcluirPode fazer um vídeo explicando como utilizar o ESP32 com esse Módulo CAN BUS MCP2515 TJA1050 OBDII, ele é mais encontrado nacionalmente e acho que não precisa comprar o analisador lógico. Barateia bastante os projetos. Se possível mostre uma rede com mais de 1 módulo! Obrigado vai ajudar muito!
ResponderExcluirOlá, o sw sempre dá erro de inicialização. Alguém conseguiu fazer funcionar? berenguelmarcioaugusto@gmail.com
ResponderExcluirProfessor, quando utilizamos o Can RX e TX, nos perdemos umas das UART disponíveis no ESP32 ?
ResponderExcluir