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