quarta-feira, julho 17, 2013

Sensor de Temperatura DS18B20 ligado ao Arduino - parte 2

Existe uma biblioteca específica para o DS18S20, a "Dallas Temperature Control Library", descrita aqui. Neste post vamos fazer algumas experiências com ela.


Instalação

Esta biblioteca não inclui a comunicação 1-wire, para isto ela usa a OneWire que vimos no post anterior. A instalação é semelhante à da OneWire,  basta expandir o zip no subdiretório libraries do diretório onde são gravados os sketches.  Precisei renomear o subdiretório, pois a IDE não aceita espaços e caracteres alfanuméricos.

Primeiro Teste

Mantendo a montagem do post anterior, abri o exemplo Tester, alterei a porta para 10 e executei. A saída está abaixo:

Dallas Temperature IC Control Library Demo
Locating devices...Found 3 devices.
Parasite power is: ON
Found device 0 with address: 289C7BB504000041
Setting resolution to 9
Resolution actually set to: 9
Found device 1 with address: 281D25DC040000A8
Setting resolution to 9
Resolution actually set to: 9
Found device 2 with address: 28274CB6040000E3
Setting resolution to 9
Resolution actually set to: 9
Requesting temperatures...DONE
Temperature for device: 0
Temp C: 21.00 Temp F: 69.80
Temperature for device: 1
Temp C: 21.50 Temp F: 70.70
Temperature for device: 2
Temp C: 21.50 Temp F: 70.70


Como a saída mostra, através da biblioteca conseguimos descobrir quantos dispositivos estão conectados, se eles são alimentados pela linha de dados e quais os seus endereços. Podemos também controlar a resolução (número de bits do ADC) e disparar a leitura em vários dispositivos com uma única chamada.

O Uso da Biblioteca

Vamos examinar o código do exemplo (simplificado abaixo):

#include <OneWire.h>
#include <DallasTemperature.h>

#define ONE_WIRE_BUS 10
#define TEMPERATURE_PRECISION 9

// Setup a oneWire instance to communicate with any OneWire devices
OneWire oneWire(ONE_WIRE_BUS);

// Pass our oneWire reference to Dallas Temperature. 
DallasTemperature sensors(&oneWire);

int numberOfDevices; // Number of temperature devices found
DeviceAddress tempDeviceAddress;

void setup(void)
{
  // start serial port
  Serial.begin(9600);
  Serial.println("Dallas Temperature IC Control Library Demo");

  // Start up the library
  sensors.begin();
  
  // Grab a count of devices on the wire
  numberOfDevices = sensors.getDeviceCount();
  
  // locate devices on the bus
  Serial.print("Locating devices...");
  
  Serial.print("Found ");
  Serial.print(numberOfDevices, DEC);
  Serial.println(" devices.");

  // report parasite power requirements
  Serial.print("Parasite power is: "); 
  if (sensors.isParasitePowerMode()) Serial.println("ON");
  else Serial.println("OFF");
  
  // Loop through each device, print out address
  for(int i=0;i<numberOfDevices; i++)
  {
    // Search the wire for address
    if(sensors.getAddress(tempDeviceAddress, i))
 {
  Serial.print("Found device ");
  Serial.print(i, DEC);
  Serial.print(" with address: ");
  printAddress(tempDeviceAddress);
  Serial.println();
  
  Serial.print("Setting resolution to ");
  Serial.println(TEMPERATURE_PRECISION, DEC);
  
  // set the resolution to TEMPERATURE_PRECISION bit (Each Dallas/Maxim device is capable of several different resolutions)
  sensors.setResolution(tempDeviceAddress, TEMPERATURE_PRECISION);
  
   Serial.print("Resolution actually set to: ");
  Serial.print(sensors.getResolution(tempDeviceAddress), DEC); 
  Serial.println();
 }else{
  Serial.print("Found ghost device at ");
  Serial.print(i, DEC);
  Serial.print(" but could not detect address. Check power and cabling");
 }
  }

}

// function to print the temperature for a device
void printTemperature(DeviceAddress deviceAddress)
{
  float tempC = sensors.getTempC(deviceAddress);
  Serial.print("Temp C: ");
  Serial.print(tempC);
  Serial.print(" Temp F: ");
  Serial.println(DallasTemperature::toFahrenheit(tempC)); // Converts tempC to Fahrenheit
}

void loop(void)
{ 
  // call sensors.requestTemperatures() to issue a global temperature 
  // request to all devices on the bus
  Serial.print("Requesting temperatures...");
  sensors.requestTemperatures(); // Send the command to get temperatures
  Serial.println("DONE");
  
  
  // Loop through each device, print out temperature data
  for(int i=0;i<numberOfDevices; i++)
  {
    // Search the wire for address
    if(sensors.getAddress(tempDeviceAddress, i))
 {
  // Output the device ID
  Serial.print("Temperature for device: ");
  Serial.println(i,DEC);
  
  // It responds almost immediately. Let's print out the data
  printTemperature(tempDeviceAddress); // Use a simple function to print out the data
 } 
 //else ghost device! Check your power requirements and cabling
 
  }
}

Ok, não parece muito complicado. Os métodos possuem nomes e parâmetros simples, mesmo sem olhar a documentação dá para entender o código.

Abrindo o Capô

Uma olhada no código fonte da biblioteca trouxe algumas surpresas. A biblioteca não mantém nenhuma informação sobre a rede 1-wire (exceto o pino onde ela está ligado). Em quase todo acesso ela enumera os dispositivos conectados, o que parece desnecessário. Poderia ser útil caso dispositivos sejam conectados ou desconectados durante a execução, porém aí esbarramos em um outro problema. As funções usadas no exemplo se referem aos dispositivos pela sua ordem de enumeração, o que mudaria neste caso.

O disparo da leitura "simultaneamente" também pode apresentar problemas. Caso esteja sendo usada a alimentação pelo pino de dados, a biblioteca espera um tempo fixo (conforme a precisão programada). Se a alimentação é independente, é testado se a conversão terminou em apenas um dos sensores. Portanto, teremos problemas se os sensores estiverem configurados com precisões diferentes ou apresentarem tempos de resposta diferente.

Concluindo
Esta biblioteca é bem prática para projetos rápidos, desde que todos os sensores estejam configurados iguais e tenham desempenho semelhante e não haja alteração dinâmica na rede.

Se quisermos algo mais sofisticado ou seguro, vamos precisar escrever a nossa própria biblioteca. É o que pretendo tentar no futuro.

8 comentários:

janainajocs disse...

Bom dia, estou tentando utilizar o DS18B20 já montado testei diversos programas inclusive este que você apresentou e obtenho apenas temperaturas negativas -2032 ºC que totalmente incompatível já que a temperatura deveria estar na faixa de 20ºC. Já testei diversas montagem e programas e todos me dão uma temperatura negativa totalmente fora da faixa. Você tem alguma ideia do que pode ser?
Aguardo retorno.

Daniel Quadros disse...

O programa localizou o DS18B20 e mostrou o endereço? Você tentou o programa da parte 1 e ele identificou corretamente o modelo do chip? Se sim para as duas coisas, eu desconfiaria que o DS18B20 está pifado (você testou com mais de um?).

Unknown disse...

Como posso fazer para o sensor analisar durante 1 minuto se ouve alteração >=6 graus para mais nesse 1 minuto e fazer determinado comando que eu vou criar. É possível isso?

Daniel Quadros disse...

Rafael: Quem fará isto é o Arduino, não o sensor. Por exemplo, o seu programa pode ler a temperatura, dar um delay de 1 minuto, ler de novo e comparar as duas temperaturas. Isto pode ser suficiente se a sua temperatura está sempre subindo, mas se ela estiver oscilando pode ser melhor você usar uma média móvel e ficar monitorando continuamente.

Unknown disse...

Como eu faço para salvar o dado de 1 minuto para comparar depois?

Daniel Quadros disse...

Rafael: float tempC = sensors.getTempC(deviceAddress); salva na variável tempC a temperatura informada pelo dispositivo de endereço deviceAddress. Se você não conhece a linguagem C e a programação do Arduino, recomendo você dar uma estudada nelas primeiro.

LUCIO disse...

Fiz uma ligação em “modo normal” de vários sensores (mais especificamente 4 sensores) DS18B20. Coloquei-os medindo a mesma posição no quarto, porém mostraram valores diferentes entre si. A diferença não é muito grande (em torno de meio grau celsius). Gostaria de saber se isso tem algo a ver com o resistor utilizado (4,7 kohm) e se eu poderia melhorar a precisão da medida usando resistores diferentes para cada sensor.

Daniel Quadros disse...

Lúcio, o resistor está relacionado à comunicação e não afeta a precisão. Segundo o datasheet, a precisão no pior caso é justamente esta que você observou +/- 0,5 C. Além disso, se você estiver usando a resolução de 9 bits o bit menos significativo da leitura corresponde também a 0,5C. Você pode experimentar aumentar a resolução para 10, 11 ou 12 bits. Isto vai aumentar o tempo de leitura e não vai melhorar a precisão em si, mas o valor lido será mais fiel. Por exemplo, imagine que o valor lido está oscilando entre 20,9 e 21,1 (devido à precisão). Trabalhando com 9 bits a leitura só poderá oscilar entre 20,5 e 21, pois não tem como reportar um valor intermediário.