Eis mais um sketch da série ctrl+C, ctrl+V. Com pequenas modificações construímos um termômetro de infravermelho que além de indicar a temperatura ambiente, exibe a temperatura de um objeto que estiver na frente do sensor MLX90614, fabicado pela Melix e comprado na sparkfun.com.
Para quem sempre quis medir a temperatura alheia sem ter que encostar na pessoa, eis a solução. Com uns R$50,00 dá para comprar esse tipo de termômetro em farmácias, mas não tem graça nenhuma. Nada melhor do que o "faça você mesmo". Outra vantagem é que podemos programar o Arduino para lidar com os dados obtidos e assim desenvolver outras aplicações, como por exemplo um sensor de aproximação. Também é possível fazer o imageamento térmico de um ambiente,detectar fontes de calor, como por exemplo "pontos quentes" em instalações elétricas isso tudo de forma automática.
Figura 1 - Sensor (foi obtida em http://www.sparkfun.com/products/9570).
O que iremos aprender nesse exemplo:
- Detalhes do funcionamento do sensor MLX90614;
- Protocolo de comunicação TWI (quase o mesmo que o I2C);
- Uso da instrução IF no Arduino (wiring).
O sketch original foi obtido em http://interface.khm.de/index.php/lab/experiments/infrared-thermometer-mlx90614/ e o autor é Martin Nawrath. As modificações feitas pela Solucionática estão destacadas em vermelho:
created 2010
by Martin Nawrath
Modified 19july2012
by Solucionática
This example code is in the public domain.
#include <i2cmaster.h>
void setup(){
Serial.begin(9600);
Serial.println("Setup...");
pinMode(13, OUTPUT);
i2c_init(); //Initialise the i2c bus
PORTC = (1 << PORTC4) | (1 << PORTC5);//enable pullups
}
void loop(){
int dev = 0x00<<1;
int data_low = 0;
int data_high = 0;
int pec = 0;
int amb_low = 0;
int amb_high = 0;
i2c_start_wait(dev+I2C_WRITE);
i2c_write(0x07);
// read
i2c_rep_start(dev+I2C_READ);
data_low = i2c_readAck(); //Read 1 byte and then send ack
data_high = i2c_readAck(); //Read 1 byte and then send ack
pec = i2c_readNak();
i2c_stop();
//This converts high and low bytes together and processes temperature, MSB is a error bit and is ignored for temps
double tempFactor = 0.02; // 0.02 degrees per LSB (measurement resolution of the MLX90614)
double tempData = 0x0000; // zero out the data
int frac; // data past the decimal point
// This masks off the error bit of the high byte, then moves it left 8 bits and adds the low byte.
tempData = (double)(((data_high & 0x007F) << 8) + data_low);
tempData = (tempData * tempFactor)-0.01;
float celcius = tempData - 273.15;
float fahrenheit = (celcius*1.8) + 32;
i2c_start_wait(dev+I2C_WRITE);
i2c_write(0x06);
// read
i2c_rep_start(dev+I2C_READ);
amb_low = i2c_readAck(); //Read 1 byte and then send ack
amb_high = i2c_readAck(); //Read 1 byte and then send ack
pec = i2c_readNak();
i2c_stop();
//This converts high and low bytes together and processes temperature, MSB is a error bit and is ignored for temps
double tempFactorAmb = 0.02; // 0.02 degrees per LSB (measurement resolution of the MLX90614)
double tempAmb = 0x0000; // zero out the data
int fracAmb; // data past the decimal point
// This masks off the error bit of the high byte, then moves it left 8 bits and adds the low byte.
tempAmb = (double)(((amb_high & 0x007F) << 8) + amb_low);
tempAmb = (tempAmb * tempFactorAmb)-0.01;
float celciusAmb = tempAmb - 273.15;
float fahrenheitAmb = (celciusAmb*1.8) + 32;
if ((celcius-celciusAmb)>1.0) digitalWrite(13, HIGH); //This is the proximity detector. If the object temperature is one degree higher than the room temperature, the led attached //on pin 13 lights on, else (note the "else" statement above) the same led turn off (Solucionática).
else digitalWrite(13, LOW);
Serial.print("Celcius: ");
Serial.println(celcius);
Serial.print("Ambiente: ");
Serial.println(celciusAmb);
Serial.print("Fahrenheit: ");
Serial.println(fahrenheit);
delay(1000); // wait a second before printing again
}
Notem que mantive os comentários e dessa vez não vou cansá-los explicando linha por linha. O principal desse projeto é a correta pinagem do sensor, os resistores de pull-up nas linhas SLC e SLA (ver tópico protocolo I2C, que não existe ainda, então googla lá !), a correta identificação do modelo do sensor e sua tensão de alimentação e os pinos do Arduino correspondentes ao protocolo I2C. No caso do MEGA 2560 os pino são identificados como SCL e SDA (Figura 3), logo é só ligar nos pinos correspondentes no sensor.
Figura 2 - Pinagem Figura 3 (Detalhe do SDA e SCL) Figura 4 - Circuito
Obs: Figura 2 obtida em www.datasheetdir.com http://circuits.datasheetdir.com/101/MLX90614-pinout.jpg
Figura 4 obtida em http://www.jdzj.com/diangong/article/2010-11-24/22949-1.htm
Para identificar os pinos o sensor tem que ser "visto" por cima e a lingueta indica à sua esquerda o pino 1 (SCL/Vz) e no sentido anti-horário os demais pinos (2 - PWM/SDA, 3 - VDD e 4- VSS). No circuito notem a presença de R1 e R2, ambos de 4K7 (4700 ohms) e do capacitor de 100nF (ou 0,1micro Farad). Importante: a alimentação é de 3,3V (o VDD, que significa tensão de dreno). O gnd, ou terra, é o VSS (tensão de supridouro).
Agora, mais um pouco sobre o sensor. Essa obra prima da engenharia é na verdade a soma de dois circuitos integrados desenvolvidos pela Melexis e que cooperam entre si e foram encapsulados em um invólucro metálico com uma janela de cristal, por onde a radiação infravermelha excita uma (ou duas, depende do modelo) "termopilhas" de infravermelho, gerando uma tensão elétrica que varia de acordo com a temperatura (a "cor") emanada pelo objeto. Se nas aulas de Física seu professor ou professora explicou a Lei de Planck (black body radiation), agora é a hora de entender como é possível determinar a temperatura de um objeto ou mesmo a dos corpos celestes , ou então descobrir o tipo de vegetação plantada em uma determinada região, questão muito usada em Sensoriameto Remoto. Os satélites meteorológicos usam sensores semelhantes ao nosso para gerar imagens e detectar focos de incêndio, por exemplo.
(Lei de Planck, Wikipedia)
Em resumo: o sensor mede a radiação infravermelha emitida pelo objeto, faz um Processamento Digital do Sinal (DSP) e armazena o resultado em determinados endereços de uma memória ram como mostra a tabela abaixo.
Vamos dar uma olhada em apenas um fragmento do sketch, para ver como aproveitamos as leituras muito rápidas da temperatura de um objeto e acrescentamos um alarme de aproximação,que nada mais é do que um led que acende quando alguem ou um objeto com temperatura maior que um grau Celsius da temperatura ambiente se aproxima uns vinte centímetros do sensor.
float celciusAmb = tempAmb - 273.15;
float fahrenheitAmb = (celciusAmb*1.8) + 32;
if ((celcius-celciusAmb)>1.0) digitalWrite(13, HIGH); //This is the proximity detector. If the object temperature is one degree higher than the room temperature, the led attached //on pin 13 lights on, else (note the "else" statement below) the same led turns off.
else digitalWrite(13, LOW);
Explicando: criamos as variáveis tempAmb,celciusAmb e fahrnheitAmb para armazenarem e calcularem o valor da temperatura ambiente guardada na posição de memória 006h. A instrução condicional (IF) só vai executar o comando digitalWrite(13, HIGH), ou seja ligar o led, se a diferença entre temperatura do objeto e a temperatura ambiente formaior que 1 grau. Pronto. Qualquer dúvida é só perguntar. Para finalizar, o sensor leva um tempinho (menos de 0,5 s) para "ler" a temperatura do objeto.
Outra ideia: que tal usar um led RGB para mostrar a temperatura do objeto em tons de azul até o vermelho dependendo se o objeto está quente ou frio? Um grande amigo vai gostar muito desse projeto...
Acrescenta esse trecho no lugar do detetor de proximidade. O que faz: se tá frio (<25°) - pelo menos aqui no Nordeste isso é frio! vai acender o azul mais forte (128+ celcius*2) do que o vermelho (128-celcius*2), deve resultar em uma cor violeta clara, acho eu. Se estiver quente (>=25.2), faz o contrário. Vê se não aponta o sensor para algo com menos de 64° negativos, pois aí a expressão da cor vermelha vai dar negativo e vai dar erro...nada de mais, basta acrescentar outro if..else para essa condição.
float celciusAmb = tempAmb - 273.15;
float fahrenheitAmb = (celciusAmb*1.8) + 32;
if (celcius<25.0) {
analogWrite(9, (map(celcius,0,25,512,128));
}
else if (celcius>=25.2) {
analogWrite(10, (celcius,25,50,128,512));
}
Explicando: a variável celcius é a medida da temperatura do objeto. A função map() condiciona a faixa de valores que celcius pode assumir: entre 512 a 128, caso 0<celcius<25; ou 128 a 512, caso 25<celcius<50. Note que quanto mais frio (<25) maior o valor que a função analogWrite () vai "escrever" no pino 9 do Arduino, o qual está ligado no terminal referente ao azul do led RGB, pois notem que quando celcius = 0 analogWrite=512 e quando celcius= 25, analogWrite=128. Por outro lado, quando a temperatura do objeto é maior que 25°, a função analogWrite() vai aumentar, proporcionalmente, entre 128 até 512, quando celcius variar entre 25° e 50°. O pino 10 do Arduino está ligado ao terminal referente ao vermelho do led RGB. Com esse arranjo conseguimos a variação gradual de cores do vermelho ao azul em função da temperatura do objeto.