quarta-feira, janeiro 16, 2019

Sensor de Temperatura (e Umidade) HDC1080 (ou compatível)

Tantos sensores e tão pouco tempo... Acho que este vai ser o último sensor de temperatura que vou examinar por algum tempo. Eu comprei a plaquinha na China, mas tem também em lojas brasileiras e no Mercado Livre. O verso da placa lista nada menos que cinco possíveis componentes, que imagino serem compatíveis. Usei como base para este post o datasheet do HDC1080, que você pode baixar do site da Texas Instruments.



O HDC1080 é um sensor de umidade e temperatura bastante preciso (precisão de 0,2C na temperatura) com interface I2C (com endereço fixo, portanto não dá para ter mais de um sensor no mesmo barramento I2C). Embora o datasheet fale que pode operar a 5V, a placa indica 3.3V.

O primeiro teste foi ligando ao Arduino. Por simplificação, liguei direto SCL e SDA (veja aqui como ligar da forma correta). Bem, a própria Texas faz isso....

As ligações ficam assim:
    HDC1080  Arduino UNO
    GND      GND
    3.3V     3.3V
    SDA      SDA (A4)
    SCL      SCL (A5)


O código abaixo se limita a fazer o dump de alguns registradores, disparar uma leitura de temperatura e umidade, aguardar a sua execução e ler o resultado bruto:
//
// Teste do sensor de temperatura e humidade HDC1080
//

#include <Wire.h>

// Endereço I2C do sensor
#define SENSOR_ADDR  0x40

// Registradores do sensor
#define HDC1080_TEMP  0x00
#define HDC1080_UMID  0x01
#define HDC1080_CONF  0x02
#define HDC1080_ID0   0xFB
#define HDC1080_ID1   0xFC
#define HDC1080_ID2   0xFD
#define HDC1080_MFG   0xFE
#define HDC1080_DEV   0xFF

// Rotinas locais
static void read_tempumid(uint16_t *pTemp, uint16_t *pUmid);
static uint16_t read_reg(uint8_t reg);
static void write_reg(uint8_t reg, uint16_t val);

// Iniciação
void setup() {
  Serial.begin (9600);
  Wire.begin();

  // Espera terminar o reset
  while (read_reg(HDC1080_CONF) & 0x8000) {
    delay (100);
  }

  // Mostra informações do chip
  Serial.print("Fabricante: ");
  Serial.println(read_reg(HDC1080_MFG), HEX);
  Serial.print("    Modelo: ");
  Serial.println(read_reg(HDC1080_DEV), HEX);

  // Garante a configuração desejada
  write_reg(HDC1080_CONF, 0x1000);
}

// Laco principal
void loop() {
  uint16_t temp, umid;

  // Faz a leitura
  read_tempumid (&temp, &umid);
  
  // Mostra resultado bruto
  Serial.println();
  Serial.println(temp);
  Serial.println(umid);

  // Dá um tempo
  delay(10000);
}

// Dispara uma leitura de temperatura e umidade e
// pega o resultado.
static void read_tempumid(uint16_t *pTemp, uint16_t *pUmid) {
  
  // Esta escrita dispara a leitura
  Wire.beginTransmission(SENSOR_ADDR);
  Wire.write (HDC1080_TEMP);
  Wire.endTransmission();

  // Aguarda fazer a leitura
  delay(50);

  // Lê o resultado
  if (Wire.requestFrom(SENSOR_ADDR, 4) == 4) {
    *pTemp = Wire.read() << 8;
    *pTemp |= Wire.read();
    *pUmid = Wire.read() << 8;
    *pUmid |= Wire.read();
  } else {
    // algo deu errado
    *pTemp = *pUmid = 0;
  }
}

// Lê um registrador
// Não usar para ler temperatura e umidade!
static uint16_t read_reg(uint8_t reg) {
  uint16_t val;
  
  Wire.beginTransmission(SENSOR_ADDR);
  Wire.write (reg);
  Wire.endTransmission();

  if (Wire.requestFrom(SENSOR_ADDR, 2) == 2) {
    val = Wire.read() << 8;
    val |= Wire.read();
  } else {
    val = 0;  // algo deu errado
  }
  return val;
}

// Escreve em um registrador
static void write_reg(uint8_t reg, uint16_t val) {
  Wire.beginTransmission(SENSOR_ADDR);
  Wire.write (reg);
  Wire.write (val >> 8);
  Wire.write (val & 0xFF);
  Wire.endTransmission();
  
}
Algumas observações:
  • Os registradores do HDC1080 são de 16 bits, com o byte mais significativo sendo transferido primeiro.
  • Os valores lidos dos registradores de fabricante e modelo correspondem ao HDC1080
  • Estou usando o modo onde são feitas as leituras consecutivas de temperatura e umidade. Quando é selecionado o registrador de temperatura ou umidade, as leituras são disparadas. Em seguida é necessário aguardar o fim da leitura e depois ler os quatro bytes de resposta.
  • Por este motivo, não dá certo tentar ler normalmente os registradores de temperatura e umidade.
A conversão do valor do registrador na temperatura é simples:
// Calcula e mostra a temperatura em celsius
  uint32_t aux;
  aux = temp * 1650L;
  aux = (aux >> 16) - 400L;
  temp = (uint16_t) aux;
  Serial.print(temp/10);
  Serial.print(",");
  Serial.println(temp%10);

  // Calcula e mostra a umidade em %
  aux = umid * 100L;
  aux = aux >> 16;
  umid = (uint16_t) aux;
  Serial.println(umid);
Cabe destacar que eu preferi evitar o uso de ponto flutuante e fiz as contas com inteiros. A temperatura é calculada com uma casa decimal e a umidade só a parte inteira. Não me preocupei com arredondamento, pois as precisões são de 0,2C e 2%.

O uso deste sensor com o Raspberry Pi fica para o próximo post.

Nenhum comentário: