Entre os vários sensores de digital disponíveis, acabei comprando um baseado no módulo FPM10A, cujo datasheet você pode ver aqui. Neste post vamos ver como é a comunicação com ele e fazer um primeiro teste.
A ligação do módulo a outros dispositivos é feito por comunicação serial assíncrona. Segundo o datasheet, a alimentação do módulo pode ser 3,6 e 6V e ele pode ser ligado diretamente a microcontroladores operando a 3,3 ou 5V. Entretanto, o meu módulo vem com uma indicação de 3,3V na placa...
A velocidade padrão da comunicação é 57600, podendo ser programada para múltiplos de 9600bps. O formato dos dados é 8N1 (8 bits de dados, sem paridade, um stop bit). Após ser ligado o módulo precisa de 0,5 segundo para iniciação e só começa a tratar a comunicação após isso.
Os dados trafegam em pacotes com o seguinte formato:
O módulo suporta 23 comandos diferentes:
Os detalhes sobre cada comando, e suas respostas, estão no datasheet. Para um primeiro teste vamos usar o comando ReadSysPara (o formato da resposta está errado no datasheet):
O fato da comunicação ser serial assíncrona nos trás uma pequena dificuldade ao conectar ao Arduino Uno: o ATmega328 possui somente uma serial, que é usada para comunicar com o PC via USB. Isto significa que, usando o Arduino Uno, quando formos falar com o módulo vamos precisar desconectar do PC, o que dificulta a depuração. Neste meu primeiro teste eu quero ver no PC os dados que estão sendo trocados entre o Arduino e o PC. Uma opção seria usar a biblioteca SoftwareSerial, mas a considero bastante precária. Uma solução melhor é usar uma placa com mais de uma serial. Eu resolvi usar o Seeeduino XIAO, mas poderia ser um Arduino DUE ou um Arduino Mega (se você não tiver medo de ligar um sinal de 5V no sensor).
O código ficou assim:
/** * Primeiro teste de comunicação com o sensor de * Digitais FPM10 * * Daniel Quadros, out/20 */ // Algumas constantes const uint16_t START = 0xEF01; const byte CMD_READSYSPARAM = 0x0F; // Estrutura do cabeçalho dos pacotes typedef struct { byte startHi; byte startLo; byte addr3; byte addr2; byte addr1; byte addr0; byte tipo; byte tamHi; byte tamLo; } HEADER; HEADER header; // Estrutura dos comandos typedef struct { byte cmd; } READSYSPARAM; // Estrutura da resposta typedef struct { HEADER header; byte codigo; union { byte dados[256]; struct { byte statusreg[2]; byte verify[2]; byte libsize[2]; byte seclevel[2]; byte addr[4]; byte datasize[2]; byte baud[2]; } sysparam; } d; } RESPOSTA; RESPOSTA resp; void setup() { Serial.begin(115200); while (!Serial) ; Serial1.begin(9600*6); while (!Serial1) ; delay (500); Serial.println("Pronto"); READSYSPARAM cmd; cmd.cmd = CMD_READSYSPARAM; enviaCmd ((byte *) &cmd, sizeof(cmd)); Serial.println("Resposta:"); if (recebeResp()) { Serial.println("Sucesso"); Serial.print("Memoria para "); uint16_t mem = (resp.d.sysparam.libsize[0] << 8) + resp.d.sysparam.libsize[1]; Serial.print(mem); Serial.println(" digitais"); } else { Serial.println("Falhou"); } } void loop() { } // Envia um comando para o sensor void enviaCmd (byte *cmd, int tam) { // monta o cabeçalho e envia header.startHi = START >> 8; header.startLo = START & 0xFF; header.addr3 = 0xFF; // endereço default header.addr2 = 0xFF; header.addr1 = 0xFF; header.addr0 = 0xFF; header.tipo = 0x01; // comando header.tamHi = (tam+2) >> 8; header.tamLo = (tam+2) & 0xFF; Serial1.write((byte *) &header, sizeof(header)); // calcula o checksum uint16_t chksum = header.tipo + header.tamHi + header.tamLo; for (int i = 0; i < tam; i++) { chksum += cmd[i]; } // envia o comando Serial1.write(cmd, tam); // envia o checksum Serial1.write(chksum >> 8); Serial1.write(chksum & 0xFF); } // Recebe uma resposta do sensor bool recebeResp() { enum { START1, START2, ADDR, TAG, LEN1, LEN2, DADOS, CHECK1, CHECK2 } estado = START1; uint16_t checksum; byte *p = (byte *) &resp; int tam; int i; bool ok = false; while (true) { int c = Serial1.read(); if (c == -1) { continue; } Serial.print (c, HEX); Serial.print (" "); *p++ = c; switch (estado) { case START1: if (c == (START >> 8)) { estado = START2; } else { p = (byte *) &resp; // ignora o byte lido } break; case START2: if (c == (START & 0xFF)) { estado = ADDR; i = 0; } else { estado = START1; p = (byte *) &resp; // ignora os bytes lidos } break; case ADDR: if (++i == 4) { estado = TAG; } break; case TAG: checksum = c; estado = LEN1; break; case LEN1: checksum += c; tam = c << 8; estado = LEN2; break; case LEN2: checksum += c; tam += c - 2; // desconta o checksum estado = DADOS; i = 0; break; case DADOS: checksum += c; if (++i == tam) { estado = CHECK1; } break; case CHECK1: ok = c == (checksum >> 8); estado = CHECK2; break; case CHECK2: Serial.println (); return ok && (c == (checksum & 0xFF)); } } }
É algo mais ou menos típico do que costumo escrever para protocolos e deve servir como base para a implementação de mais comandos.
No próximo post vamos começar a ver os comandos relativos a digitais.
01/11/20: Corrigido código
Nenhum comentário:
Postar um comentário