Lendo sensores NTC com o Arduino – I


Dando continuidade ao projeto do termômetro digital com o uso de sensores NTC, vamos ao desenvolvimento do firmware. Uma vez que condicionamos o sinal de temperatura aos  níveis necessários  para o interfaceamento com o conversor AD do Arduíno, o próximo passo é a elaboração do firmware que seja capaz de realizar a leitura deste sinal e a apresentação do valor lido em um terminal. Vamos elaborar um firmware com duas funcionalidades:

  • O Arduino realizará uma leitura a cada 500 ms e enviará valor lido, em formato ASCII, já na formato de graus centígrados.
  • O Arduino espera por um comando externo e retorna o valor da temperatura atual.

Utilizaremos neste experimento o Arduino UNO, mas código pode ser também utilizado em outras versões. O desenvolvimento será realizado no ambiente Linux Mint 18.1, com o editor Geany v 1.27 e a compilação /testes com o ambiente padrão de desenvolvimento Arduino 1.8.1 . Se você ainda não configurou o Geany para a operação com o Arduino, as informações para isto podem ser obtidas no endereço https://cadernodelaboratorio.com.br/2015/05/13/geany-um-editor-pau-para-toda-obra-i/.

Revisando as configurações do Geany:

No site indicado anteriormente você tem detalhado os procedimentos de configuração do Geany. Resumimos aqui os arquivos de configuração:

.main.ino

{fileheader}
 
#include  "Arduino.h"

//includes

 
//defines


//variaveis globais


 
void setup()
{
 
}
 
void  loop()
{

}
.header

{filename}
Copyright {date} {developer} <{mail}>
{version}
{gpl}
 

Especificando o firmware

 Funcionalidade desejada:

O sistema de leitura de temperatura tempNTC se destina a obtenção de valores de temperatura obtidos a partir da leitura de sensores NTC.

O sistema pode operar sob comando externo ou gerar valores em forma de “strings” ASCII a intervalos de tempo determinados.

A opção modo escravo ou gerador autônomo é realizada através da seleção de um interruptor. Interruptor fechado= Modo escravo, interruptor aberto= Modo autônomo.

A configuração da comunicação serial será de 9600,8,N,1, ou seja, 9600 bauds, 8 bits de dados, sem paridade e 1 (um) stop bit.

A temperatura será  visualizada com resolução de .1 graus.

Um indicador luminoso irá emitir 3 picadelas de 1 segundo de duração por 1 segundo de intervalo após completada a inicialização do sistema e uma piscada de 0.5 segundos a cada vez que o sistema envia uma mensagem de temperatura.

As seguintes mensagens deverão ser processadas pelo sistema. (\n corresponde ao caractere ascii 0x0a)

Comando externo                     significado        resposta
0 \n Quem é você? $0,tempNTC v0.1
1 \n Qual é a temperatura? $,xy.z, ex: $1,32.3
2,nnn Configure intervalo de operação (ms) $2,nnn
 
 Primeiro passo: Diagrama de contexto

O diagrama de contexto mostra todas as interfaces do nosso sistema. O bloco denominado “indicador visual” é implementado com leds:

Diagrama de contexto TempNTC


 Segundo passo: Detalhamento das tarefas do sistema

Precisamos agora detalhar as diferentes tarefas que irão implementar a funcionalidade desejada. Não existe uma regra fixa para este detalhamento nem em quantos níveis será detalhado. Mas uma boa prática é que cada detalhamento resulte em torno de 7 tarefas. Se ficar muito mais do que isto vale a pena agrupar várias tarefas numa só e  depois detalhar esta tarefa especificamente.

O motivo desta detalhamento é facilitar a implementação e o teste de cada tarefa individualmente. Além disto, se você for separar a implementação entre vários desenvolvedores, cada um pode saber exatamente o que deve fazer e a sua interface com os demais desenvolvedores.

Detalhamento no primeiro nível

 

 Terceiro passo: Descrevendo cada tarefa individualmente

Passamos agora a descrever a funcionalidade de cada tarefa:

  • Tarefa 0: Conversor AD

Esta tarefa é ativada pela tarefa selecionaCmd. Ao ser ativada a tarefa realiza uma conversão AD de 10 bits e insere o resultado na variável “ultimoValorLido”. Após isto faz a variável flagAd igual a 1.

  • Tarefa 1: ConverteGraus

Esta tarefa é ativada pelo loop100. Se o flagAD estiver em 1 , lê o valor binário disponibilizado pelo AD em “ultimoValorLido” e o converte em uma string ASCII contendo o valor da temperatura em graus centígrados. Após esta conversão faz o flagAD = 0.

  • Tarefa 2: AcionaLed

Esta tarefa é ativada pelo loop100. Caso a variável pisca esteja com o valor “n” maior do que 0, emite uma piscada de “n” milisegundos

  • Tarefa 3: SelecionaCmd

Esta tarefa é ativada pelo loop100. Verifica se o modo de operação é o modo autônomo ou modo escravo. Ativa a tarefa ConversorAd em função do modo selecionado.

  • Tarefa 4: ProcessaCmd

Esta tarefa é ativada pelo lloop100. Executa o comando se existir um comando disponível. Atualiza o flag após executar o comando.

  • Tarefa 5: Timer 100ms

Esta tarefa é ativada através do loop principal do programa, e por sua vez gera o loop loop100, com uma execução a cada 100 ms.

  • Tarefa 6: leModo

Ativada pelo loop100, verifica o estado da entrada seletora de modo de operação e atualiza a variável “modoOperacao” de maneira correspondente.

  • Tarefa 7: decodificaComando

Ativada pelo loop principal do programa. Monta um comando recebido a partir da interface serial e o disponibiliza na variável “comandos”. Atualiza o flag “cmdDisp” ao terminar de montar um programa.

  • Tarefa 8: initTempNTC

Chamada na inicialização do programa. Inicializa todas as variáveis do sistema e gera 3 pulsos na saída com leds.

  • Tarefa 9: enviaMsg

Ativada pelo loop100, verifica se tem mensagem a ser enviada à serial. Em caso de existência da mensagem,  a envia e atualiza o flag flagMsg.

Implementação gradativa do firmware

Vamos agora implementar cada tarefa, escolhendo a implementação de forma a facilitar o teste individual de cada trecho de código implementado.

  • Implementação das tarefas initTempNTC (implementação parcial) , Timer100 e piscaLed

A tarefa piscaLed pode ser implementada através do seguinte fluxograma:

Tarefa piscaLed

A tarefa loop100 por sua vez pode ser executada com o fluxograma mostrado a seguir. Observe que nenhuma tarefa pode “roubar” o processador para si. Funções do tipo “delay” são terminantemente proibidas quando queremos que várias tarefas compartilhem a CPU.

Tarefa loop100

O programa que implementa as funcionalidades anteriormente descritas é o seguinte:


/*
 * tempNTC0.ino
 * Copyright 2017-07-31 tavares <tavares arroba cadernodelaboratorio.com.br>
 * 0.1
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301, USA.
 * 
 * 
 */

 
#include  "Arduino.h"

//includes

 
//defines

#define LED_PLACA    13  // definimos aqui o pino de comando do led 
#define LIGA_LED_PLACA   digitalWrite (LED_PLACA, HIGH);
#define DESLIGA_LED_PLACA    digitalWrite (LED_PLACA, LOW); 



//variaveis globais
char cDuracao;     // numero de centenas de milisegundos que deve durar cada piscada
char cPiscadas;  // 2 * numero de vezes que deve piscar
 
void setup()
{
// inicializacao de tempNTC
pinMode ( LED_PLACA,OUTPUT); // pino led como saida

// 3 piscadas de 500ms informando a inicializacao
cDuracao= 10;    
cPiscadas= 6; 


}
 
void  loop()
{
checkTimer();
}


/*
 tarefa CheckTimer
 gera o loop loop100 a cada 100 ms
 chamada atraves do loop principal do programa
*/
 
void checkTimer()
{
unsigned long tempoAtual;  
static  unsigned long tempoAnterior100ms;
 
tempoAtual= millis();   //obtem o tempo atual
 
if((tempoAtual - tempoAnterior100ms) >= 100L)
    {
    tempoAnterior100ms= tempoAtual;
    loop100();
    }

}
 


void loop100(void)
{
acionaLed();	
	
	
}	



void acionaLed()
{
static char cEstadoLed;
static char cIntervaloAtual;

if (cPiscadas >= 0 )
	{
	if (cIntervaloAtual <= 0)
		{
		cIntervaloAtual = 	cDuracao;
		-- cPiscadas;
		cEstadoLed= !cEstadoLed;
		}
	
	if (cEstadoLed == 0)
		{
		LIGA_LED_PLACA
		}
	else
		{
		DESLIGA_LED_PLACA
		}
		
	-- cIntervaloAtual;		
			
	}
	
	
}	

Transfira o programa para o Arduino e veja como o led pisca três vezes ao iniciar, exatamente como esta expresso nos diagramas e na especificação.

Passamos agora a implementação e teste da tarefa enviaMsg. O teste da tarefa será realizado através do envio de uma mensagem na inicialização, identificando a versão do firmware. Após compilar e transferir o programa a seguir monitore a saída com um emulador de terminais configurado para 9600,8,N,1 e veja o resultado. A mensagem identificando a versão do software deverá aparecer na tela do terminal.



/*
 * tempNTC1.ino
 * Copyright 2017-07-31 tavares <tavares arroba cadernodelaboratorio.com.br>
 * 0.1
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301, USA.
 * 
 * 
 */

 
#include  "Arduino.h"

//includes

 
//defines

#define LED_PLACA    13  // definimos aqui o pino de comando do led 
#define LIGA_LED_PLACA   digitalWrite (LED_PLACA, HIGH);
#define DESLIGA_LED_PLACA    digitalWrite (LED_PLACA, LOW); 



//variaveis globais
char cDuracao;     // numero de centenas de milisegundos que deve durar cada piscada
char cPiscadas;  // 2 * numero de vezes que deve piscar

char chBufferSaida[32];   //buffer de mensagens de saída
char flagMsg;             // em zero, buffer vazio, em 1 tem mensagem
 
void setup()
{
// inicializacao de tempNTC
pinMode ( LED_PLACA,OUTPUT); // pino led como saida

//inicializacao da interface serial
Serial.begin(9600, SERIAL_8N1); //serial iniciada em 9600,8,n,1

// 3 piscadas de 500ms informando a inicializacao
cDuracao= 10;    
cPiscadas= 6; 

//mensagem inicial

strcpy(chBufferSaida,"$0,tempNTC v 1.0");
flagMsg=1;


}
 
void  loop()
{
checkTimer();
}


/*
 tarefa CheckTimer
 gera o loop loop100 a cada 100 ms
 chamada atraves do loop principal do programa
*/
 
void checkTimer()
{
unsigned long tempoAtual;  
static  unsigned long tempoAnterior100ms;
 
tempoAtual= millis();   //obtem o tempo atual
 
if((tempoAtual - tempoAnterior100ms) >= 100L)
    {
    tempoAnterior100ms= tempoAtual;
    loop100();
    }

}
 


void loop100(void)
{
acionaLed();	
enviaMsg();	
	
}	



void acionaLed()
{
static char cEstadoLed;
static char cIntervaloAtual;

if (cPiscadas >= 0 )
	{
	if (cIntervaloAtual <= 0)
		{
		cIntervaloAtual = 	cDuracao;
		-- cPiscadas;
		cEstadoLed= !cEstadoLed;
		}
	
	if (cEstadoLed == 0)
		{
		LIGA_LED_PLACA
		}
	else
		{
		DESLIGA_LED_PLACA
		}
		
	-- cIntervaloAtual;		
			
	}
	
}	
	

void enviaMsg()
{
if (flagMsg==1)
	{
	Serial.println(chBufferSaida);
	flagMsg=0;
	}
}	

Para o teste, basta compilar, transferir o programa e interligar o emulador de terminais da própria IDE Arduino. A mensagem irá aparecer na tela, conforme mostrado na figura a seguir:

Saída na interface após inicialização

A próxima tarefa a ser implementada é a tarefa “leModo”. A leitura do modo é realizada lendo-se o nível na entrada D12. Com a entrada em nível lógico “1” o modo assumido é o modo “autônomo”, no qual o o software envia de forma periódica o valor da temperatura para a saída. Com a entrada em nível “0” o modo é o “escravo”, onde o software só envia os dados a partir da recepção de um comando de solicitação.

.tempNTC2.c
/*
 * tempNTC2.ino
 * Copyright 2017-07-31 tavares <tavares arroba cadernodelaboratorio.com.br>
 * 0.1
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301, USA.
 * 
 * 
 */

 
#include  "Arduino.h"

//includes

 
//defines

#define LED_PLACA    13  // definimos aqui o pino de comando do led 
#define SEL_MODE     12  // pino que define o modo de operacao
#define LIGA_LED_PLACA   digitalWrite (LED_PLACA, HIGH);
#define DESLIGA_LED_PLACA    digitalWrite (LED_PLACA, LOW); 



//variaveis globais
char cDuracao;     // numero de centenas de milisegundos que deve durar cada piscada
char cPiscadas;  // 2 * numero de vezes que deve piscar

char chBufferSaida[32];   //buffer de mensagens de saída
char flagMsg;             // em zero, buffer vazio, em 1 tem mensagem

char chModo;  // modo de operacao  1-> autonomo   0-> escravo
 
void setup()
{
// inicializacao de tempNTC
pinMode ( LED_PLACA,OUTPUT); // pino led como saida

pinMode(SEL_MODE,INPUT_PULLUP);

//inicializacao da interface serial
Serial.begin(9600, SERIAL_8N1); //serial iniciada em 9600,8,n,1

// 3 piscadas de 500ms informando a inicializacao
cDuracao= 10;    
cPiscadas= 6; 

//mensagem inicial

strcpy(chBufferSaida,"$0,tempNTC v 1.0");
flagMsg=1;


}
 
void  loop()
{
checkTimer();
}


/*
 tarefa CheckTimer
 gera o loop loop100 a cada 100 ms
 chamada atraves do loop principal do programa
*/
 
void checkTimer()
{
unsigned long tempoAtual;  
static  unsigned long tempoAnterior100ms;
 
tempoAtual= millis();   //obtem o tempo atual
 
if((tempoAtual - tempoAnterior100ms) >= 100L)
    {
    tempoAnterior100ms= tempoAtual;
    loop100();
    }

}
 


void loop100(void)
{
acionaLed();	
enviaMsg();	
leModo();	
}	



void acionaLed()
{
static char cEstadoLed;
static char cIntervaloAtual;

if (cPiscadas >= 0 )
	{
	if (cIntervaloAtual <= 0)
		{
		cIntervaloAtual = 	cDuracao;
		-- cPiscadas;
		cEstadoLed= !cEstadoLed;
		}
	
	if (cEstadoLed == 0)
		{
		LIGA_LED_PLACA
		}
	else
		{
		DESLIGA_LED_PLACA
		}
		
	-- cIntervaloAtual;		
			
	}
	
}	
	

void enviaMsg()
{
if (flagMsg==1)
	{
	Serial.println(chBufferSaida);
	flagMsg=0;
	}
}	
	


void leModo()
{
chModo= (char) digitalRead(SEL_MODE);
}
 

Basta por hoje. No próximo artigo vamos dar continuidade ao desenvolvimento do firmware. Até lá!

Deixe um comentário