DEV Community

Hedy
Hedy

Posted on

Temperature and humidity measurement based on AT89C52 microcontroller

Here’s a compact, field-tested way to build a temperature & humidity meter on AT89C52 (8051) with two sensor options:

1) What you’ll build

  • MCU: AT89C52 @ 11.0592 MHz (or 12 MHz)
  • Sensor (pick one):
  • Output: 1602 LCD (parallel) or UART serial to PC
  • Power: 5 V (with 3.3 V LDO if your sensor is 3.3 V)

2) Hardware at a glance
Option A — DHT11/DHT22 (single data wire)

  • DHTx VCC → 5 V, GND → GND
  • DHTx DATA → P3.7 (any GPIO) with 10 kΩ pull-up to 5 V
  • Keep data wire ≤20 cm for stability; add 100 nF near sensor

Option B — SHT21/SHT31 (I²C, more accurate)

  • SCL → P1.6, SDA → P1.7 (any GPIOs are fine)
  • 4.7 kΩ pull-ups from SCL/SDA to 3.3 V (most SHTx are 3.3 V)
  • If MCU is 5 V: either use a level shifter or power MCU I/O at 3.3 V-tolerant levels
  • Add 100 nF decoupling close to the sensor

LCD 1602 (optional, 5 V parallel)

  • RS→P2.0, RW→GND, E→P2.1, D4..D7→P2.4..P2.7, 10 kΩ contrast pot on V0
  • Or skip LCD and print via UART (P3.0 RXD / P3.1 TXD @ 9600 bps)

3) Timing & protocol
DHT11/22 (simplified)

  1. MCU pulls DATA low ≥18 ms, then release (input, pull-up).
  2. Sensor replies 80 µs low + 80 µs high.
  3. Sends 40 bits: each bit = 50 µs low, then high for 26–28 µs (0) or ~70 µs (1).
  4. Frame: Humidity_int | Humidity_dec | Temp_int | Temp_dec | Checksum (sum of previous 4 bytes, LSB 8 bits).

SHT21/SHT31 (I²C)

  • Send measure command (e.g., SHT21 Temp: 0xF3, RH: 0xF5; SHT31 single-shot: 0x2400)
  • Read 2 data bytes + CRC (optional).
  • Convert using datasheet formulas (e.g., SHT21: T(°C)= -46.85 + 175.72 * rawT / 65536 RH(%)= -6 + 125 * rawRH / 65536).

4) Minimal 8051 code (Keil C-style)
A) DHT11 driver (works for DHT22 too—just parse decimals)

#include <REG52.H>

sbit DHT = P3^7;

void delay_us(unsigned int us) { while(us--) { _nop_(); _nop_(); } } // tune per Fclk
void delay_ms(unsigned int ms){ unsigned int i; while(ms--) for(i=0;i<120;i++); } // ~1ms@11.0592MHz

bit dht_wait_level(bit level, unsigned int timeout_us){
  while(timeout_us--){
    if (DHT == level) return 1;
    delay_us(1);
  }
  return 0; // timeout
}

bit dht_read_byte(unsigned char *b){
  unsigned char i, val=0;
  for(i=0;i<8;i++){
    // wait for 50us low
    if(!dht_wait_level(0, 100)) return 0;
    // wait for high then measure high width
    if(!dht_wait_level(1, 100)) return 0;
    // after line goes high, wait ~40us then sample
    delay_us(40);
    val <<=1;
    if(DHT) val |= 1;          // high ~70us => '1', still high at 40us
    // wait for line to drop before next bit (guard)
    if(!dht_wait_level(0, 100)) return 0;
  }
  *b = val; return 1;
}

bit dht_read(int *tempC, int *humi){
  unsigned char h_int,h_dec,t_int,t_dec,chk;
  // start signal
  DHT = 0;                  // set as output low
  delay_ms(18);
  DHT = 1;                  // release (input with pull-up)
  delay_us(30);

  // sensor response: 80us low + 80us high
  if(!dht_wait_level(0, 120)) return 0;
  if(!dht_wait_level(1, 120)) return 0;

  if(!dht_read_byte(&h_int)) return 0;
  if(!dht_read_byte(&h_dec)) return 0;
  if(!dht_read_byte(&t_int)) return 0;
  if(!dht_read_byte(&t_dec)) return 0;
  if(!dht_read_byte(&chk))   return 0;

  if(((h_int + h_dec + t_int + t_dec) & 0xFF) != chk) return 0;

  *humi  = h_int; // DHT11: decimals ≈ 0; DHT22: combine int/dec if needed
  *tempC = t_int;
  return 1;
}

Enter fullscreen mode Exit fullscreen mode

B) Bit-banged I²C + SHT21 read (higher accuracy)

sbit SDA = P1^7;
sbit SCL = P1^6;

void i2c_delay(){ _nop_(); _nop_(); _nop_(); }
void i2c_start(){ SDA=1; SCL=1; i2c_delay(); SDA=0; i2c_delay(); SCL=0; }
void i2c_stop(){  SDA=0; SCL=1; i2c_delay(); SDA=1; i2c_delay(); }
bit  i2c_ack(){   bit a; SDA=1; SCL=1; i2c_delay(); a=SDA; SCL=0; return (a==0); }
void i2c_write(unsigned char d){
  char i; for(i=0;i<8;i++){ SDA=(d&0x80); SCL=1; i2c_delay(); SCL=0; d<<=1; }
  i2c_ack();
}
unsigned char i2c_read(bit ack){
  char i; unsigned char d=0;
  SDA=1; for(i=0;i<8;i++){ d<<=1; SCL=1; i2c_delay(); if(SDA) d|=1; SCL=0; }
  SDA = ack ? 0 : 1; SCL=1; i2c_delay(); SCL=0; SDA=1; return d;
}

#define SHT21_ADDR 0x40
float sht21_read_temp(){
  i2c_start();
  i2c_write((SHT21_ADDR<<1)|0); // write
  i2c_write(0xF3);              // trigger temp
  i2c_stop();

  // crude wait (no clock stretching handling here)
  { unsigned int i; for(i=0;i<30000;i++); }

  i2c_start();
  i2c_write((SHT21_ADDR<<1)|1); // read
  unsigned char msb = i2c_read(1);
  unsigned char lsb = i2c_read(1);
  (void)i2c_read(0);            // CRC byte (ignored here)
  i2c_stop();

  unsigned int raw = ((unsigned int)msb<<8) | (lsb & 0xFC);
  return -46.85 + 175.72 * ((float)raw / 65536.0);
}
float sht21_read_rh(){
  i2c_start();
  i2c_write((SHT21_ADDR<<1)|0);
  i2c_write(0xF5);              // trigger RH
  i2c_stop();
  { unsigned int i; for(i=0;i<30000;i++); }

  i2c_start();
  i2c_write((SHT21_ADDR<<1)|1);
  unsigned char msb = i2c_read(1);
  unsigned char lsb = i2c_read(1);
  (void)i2c_read(0);
  i2c_stop();

  unsigned int raw = ((unsigned int)msb<<8) | (lsb & 0xFC);
  return -6.0 + 125.0 * ((float)raw / 65536.0);
}
Enter fullscreen mode Exit fullscreen mode

Notes:
• Adjust delays to your clock so I²C is ~100 kHz.
• For production, handle clock stretching, CRC, and error paths.

5) Display / Logging

UART (quickest):

Init UART0 at 9600 bps and printf("T=%dC RH=%d%%\r\n", tC, RH); each second.

LCD 1602:

Init in 4-bit mode; show two lines like T=25C and RH=53%.

Sampling: 1–2 Hz is fine for room sensing; average 4–8 samples to smooth noise.

*6) Accuracy, calibration, and protection
*

  • DHT11: ±2 °C / ±5%RH (rough). DHT22/AM2302: better. SHT21/SHT31: ±0.3–0.4 °C / ±2%RH (recommended).
  • Add a breather slot in the enclosure; keep sensor away from regulators and LCD backlights (heat!).
  • If long cables: add TVS diode and series 100 Ω on data lines.
  • Power: 5 V rail with 100 nF near MCU; for I²C sensors at 3.3 V, use an LDO + common ground.

7) BOM (example)

  • AT89C52, 11.0592 MHz crystal + 2×22 pF
  • DHT22 or SHT31 + 4.7 kΩ pull-ups
  • 1602 LCD (optional) + 10 kΩ pot
  • 5 V supply, LDO 3.3 V (if SHTxx)
  • 100 nF decoupling caps, 10 kΩ pull-up (DHT), headers, TVS (optional)

TL;DR

  • Want fast & simple? Use DHT22, one data pin, parse 40 bits.
  • Want accuracy & stability? Use SHT21/SHT31 over bit-banged I²C and apply datasheet formulas.
  • Show results via UART or 1602 LCD.

Top comments (0)