Operácie

Kombinovaný snímač teploty a vlhkosti DHT11: Rozdiel medzi revíziami

Zo stránky SensorWiki

StudentMIPS (diskusia | príspevky)
Bez shrnutí editace
StudentMIPS (diskusia | príspevky)
Bez shrnutí editace
 
(14 medziľahlých úprav od rovnakého používateľa nie je zobrazených.)
Riadok 1: Riadok 1:
Záverečný projekt predmetu MIPS / LS2025 - '''Aleh Sobaleu'''
Záverečný projekt predmetu MIPS / LS2025 - '''Aleh Sobaleu'''
https://arduinoposlovensky.sk/projekty/dht11-a-dht22/




Riadok 14: Riadok 11:
* [https://docs.arduino.cc/resources/datasheets/A000066-datasheet.pdf Dokumentácia k doske Acrob]
* [https://docs.arduino.cc/resources/datasheets/A000066-datasheet.pdf Dokumentácia k doske Acrob]
* [https://arduinoposlovensky.sk/projekty/dht11-a-dht22/ Dokumentácia k senzoru teploty a vlhkosti DHT11]
* [https://arduinoposlovensky.sk/projekty/dht11-a-dht22/ Dokumentácia k senzoru teploty a vlhkosti DHT11]
* [https://www.lcd-module.de/eng/pdf/zubehoer/st7036.pdf Dokumentácia k ST7036 LCD Controller]




Riadok 56: Riadok 54:
[[Súbor:Lcd.jpg|400px|thumb|center|LCD displej s ovládačom ST7036]]
[[Súbor:Lcd.jpg|400px|thumb|center|LCD displej s ovládačom ST7036]]


3. Schéma zapojenia
3. Schéma zapojenia:


Celé zapojenie som realizoval na breadboarde, pričom som sa snažil čo najefektívnejšie využiť dostupné piny mikrokontroléra ATmega328P, aby nedochádzalo ku konfliktom medzi perifériami.
Celé zariadenie bolo zapojené na breadboarde pomocou prepojovacích vodičov. Použil som mikrokontrolér ATmega328P, ktorý riadi čítanie dát zo senzora DHT11 a ich následné zobrazenie na LCD displeji. Komponenty boli zvolené tak, aby spolu komunikovali spoľahlivo bez rušenia a aby sa dali pripojiť priamo k pinom mikrokontroléra bez potreby zložitej logiky.


Základné princípy zapojenia:
Základné zapojenie komponentov:
Senzor DHT11 je pripojený na pin PD5 (Arduino D5). Tento pin slúži ako digitálny vstup aj výstup – najskôr sa vysiela štartovací signál a následne sa na tom istom pine prijímajú údaje.


LCD displej využíva SPI komunikáciu, kde sú použité tieto vývody:
1. Senzor DHT11


PB3 (MOSI) Data (SI)
VCC senzora 5V na doske


PB5 (SCK) Hodiny
GND senzora GND na doske


PD2 RS (register select)
DATA senzora pin PD5 (Arduino D5)


PD4 → CS (chip select)
Senzor DHT11 komunikuje cez digitálny jednovodičový protokol. Mikrokontrolér najprv vyšle signál na prebudiť senzor a potom čaká na odpoveď a číta 5 bajtov – vlhkosť, teplotu a kontrolný súčet.


PD6 → BKLT (podsvietenie)
2. LCD displej (ST7036)


Displej je napájaný z 5V a uzemnený (GND). Pri inicializácii sa zapne podsvietenie a aktivuje sa režim zobrazovania.
LCD displej komunikuje cez SPI.  


UART (sériová komunikácia) slúži na výstup údajov do sériového monitora. Mikrokontrolér posiela údaje, ktoré sú následne čitateľné v aplikácii ako napr. PuTTY.
Použité piny:


[[Súbor:GeminiAI-image2.jpg|400px|thumb|center|Schéma zapojenia.]]
PB3 (MOSI) → SI (dáta do displeja)
 
PB5 (SCK) → hodiny (clock)
 
PD2 → RS (výber dátového alebo príkazového režimu)
 
PD4 → CS (výber displeja)
 
PD6 → podsvietenie (BKLT)
 
VCC → 5V
 
GND → GND
 
Displej zobrazuje na 1. riadku teplotu v °C a na 2. riadku vlhkosť v %.
 
UART výstup je pripojený k USB-UART prevodníku a slúži na monitorovanie výstupu cez terminál (PuTTY).
 
[[Súbor:Schema_DHT11.png|400px|thumb|center|Schéma zapojenia vytvorená v programe EasyEDA.]]




=== Algoritmus a program ===
=== Algoritmus a program ===


Algoritmus programu využíva toto a toto, základné funkcie sú takéto a voláma ich tuto...  
Použité knižnice:
Výpis kódu je nižšie...
 
dht11.h a dht11.c – vlastná knižnica, ktorú som upravil pre čítanie dát zo senzora DHT11 cez digitálny pin. Obsahuje základnú komunikáciu, spracovanie bitov a kontrolu správnosti pomocou kontrolného súčtu.
 
lcd.h a lcd.c – knižnica pre prácu s LCD displejom, ktorú sme dostali ako základ na cvičení. Rozšíril som ju o vlastné funkcie lcd_puts, lcd_setCursor, lcd_clearline a lcd_print_num.
 
uart.h a uart.c – jednoduchá knižnica na výpis textu cez sériový port. Umožňuje používať štandardné funkcie ako printf() a uart_puts().




<tabs>
<tabs>
<tab name="AVR C-code"><syntaxhighlight  lang="c++" style="background: LightYellow;">
<tab name="AVR C-code"><syntaxhighlight  lang="c++" style="background: LightYellow;">
#include "lcd.h"            // Knižnica pre prácu s LCD displejom
#include <avr/io.h>          // Základné definície pre AVR
#include <util/delay.h>      // Funkcia pre oneskorenie
#include <stdlib.h>
#include <stdio.h>
#include "uart.h"            // Knižnica pre sériovú komunikáciu
#include "dht11.h"          // Knižnica pre DHT11 senzor
#define LED1 PB5            // Interná LED na doske (napr. Arduino UNO)
#define LED2 PC2            // Externá LED (voliteľná)
#define SW_A PC3            // Tlačidlo (nepoužíva sa v tomto kóde)
FILE mystdout = FDEV_SETUP_STREAM(uart_putc, NULL, _FDEV_SETUP_WRITE); // Pripojenie výstupu printf k UARTu
// Pomocná funkcia na zobrazenie čísla na LCD ako text
void lcd_print_num(uint8_t num) {
char buffer[5];
itoa(num, buffer, 10);    // Konverzia čísla na string
lcd_puts(buffer);          // Zobrazenie na LCD
}
int main(void) {
uint8_t temp, hum;        // Premenné pre teplotu a vlhkosť
// Nastavenie pinov ako výstupy pre LEDky
DDRB |= (1<<LED1);
DDRC |= (1<<LED2);
uart_init();              // Inicializácia sériového portu
stdout = &mystdout;        // Prepojenie printf na UART
printf("DHT11 + LCD init\n");
lcd_init();                // Inicializácia LCD
lcd_bklt(1);              // Zapnutie podsvietenia
lcd_command(0x0C);        // Zapnutie displeja, vypnutie kurzora
lcd_clear();              // Vyčistenie obrazovky
while (1) {
  // Pokus o načítanie dát zo senzora
  if (dht11_read(&temp, &hum) == 0) {
  // Ak úspešné čítanie — spracovanie teploty
  char send[16];
  sprintf(send,"Teplota: %d %C",temp); // Formátovaný výstup
  lcd_setCursor(0, 0);                // Riadok 1, stĺpec 0
  lcd_puts(send);
  lcd_putc(0xDF);                      // Znak ° (stupne)
  lcd_putc('C');                      // Celsia
  // Spracovanie vlhkosti
  char message[16];
  sprintf(message,"Vlhkost: %d %%",hum);
  lcd_setCursor(1, 0);                // Riadok 2, stĺpec 0
  lcd_puts(message);
  // Výpis aj do terminálu
  printf("T: %d C, H: %d %%\n", temp, hum);
  } else {
  // Ak čítanie zlyhalo — chyba
  lcd_setCursor(0, 0);
  lcd_puts("Chyba citania ");          // Hlásenie na LCD
  lcd_setCursor(1, 0);
  lcd_puts(" ");                      // Vymazanie druhého riadku
  printf("Read error: %d\n", dht11_read(&temp, &hum));
  }
  _delay_ms(2000);                      // Požadovaná pauza medzi čítaniami
}
}
</syntaxhighlight ></tab>
<tab name="uart.h"><syntaxhighlight  lang="c++" style="background: LightYellow;">
#define LED PB5  // interná LED na doske (napr. Arduino UNO)
/* Užitočné makrá pre prácu s tlačidlami alebo pinmi */
// Test, či je bit nastavený
// bit_is_set(PINB, SW1)
// Test, či je bit nulový (tlačidlo stlačené pri pull-up odpore)
// bit_is_clear(PINB, SW1)
/* Preddefinované slučky pre čakanie na udalosť */
// Čakanie, kým sa tlačidlo pustí
// loop_until_bit_is_set(PINB, SW1);
// Čakanie, kým sa tlačidlo stlačí
// loop_until_bit_is_clear(PINB, SW1);
#define set_bit(ADDRESS,BIT) (ADDRESS |= (1<<BIT))      // Nastaví bit v registri
#define clear_bit(ADDRESS,BIT) (ADDRESS &= ~(1<<BIT))    // Vynuluje bit v registri
#ifndef UART_H_
#define UART_H_
#include <stdio.h>
/* Výpočet predvoľby pre BAUD rate na základe frekvencie CPU a požadovanej rýchlosti.
* Vzorec je prevzatý z datasheetu pre ATmega328P.
*/
#define BAUD_PRESCALE  (((F_CPU / (BAUDRATE * 16UL))) - 1)
/* Inicializuje potrebný hardvér (voliteľné, ak sa používa) */
void hw_init(void);
/* Inicializuje UART (nastaví rýchlosť, formát rámca a povolí prenos) */
void uart_init(void);
/*
* Funkcia pre výpis znakov cez UART, kompatibilná s printf().
* Vďaka tomu je možné napísať:
*    printf("Text %d\n", hodnota);
* a výstup sa odošle cez UART.
*/
int uart_putc(char c, FILE *stream);
/* Jednoduchý výpis reťazca cez UART (bez formátovania) */
void uart_puts(const char *s);
/* Prijme jeden znak z UART (blokuje, kým nepríde znak) */
char uart_getc(void);
/* Jednoduchá funkcia delay (ak sa nepoužíva _delay_ms()) */
void delay(int delay);
#endif /* UART_H_ */
</syntaxhighlight ></tab>
<tab name="uart.c"><syntaxhighlight  lang="c++" style="background: LightYellow;">
/* ************************************************************************* */
/* FileName:            uart.c                                              */
/* ************************************************************************* */
#include <avr/io.h>
#include <avr/io.h>
#include <util/delay.h>
#include "uart.h"
void hw_init( void )
{
  DDRB |= (1<<LED);    // PORTB.5 kde je LED ma byt OUTPUT
  /* sem si mozete dopisat svoje vlastne inicializacne prikazy */
}
void uart_init( void )
{
//  for different BAUD rate change the project settings, or uncomment
//  following two lines:
// #undef  BAUD          // avoid compiler warning
//  #define BAUD 115200
  #include <util/setbaud.h>  // requires defined BAUD
 
  UBRR0H = UBRRH_VALUE;
  UBRR0L = UBRRL_VALUE;
  #if USE_2X                // defined in setbaud.h
    UCSR0A |= (1 << U2X0);
  #else
    UCSR0A &= ~(1 << U2X0);
  #endif
    UCSR0C = _BV(UCSZ01) | _BV(UCSZ00); /* 8-bit data */
    UCSR0B = _BV(RXEN0) | _BV(TXEN0);  /* Enable RX and TX */
}
int uart_putc( char c, FILE *stream )
{
  if (c == '\n')
      uart_putc('\r',stream);
 
  loop_until_bit_is_set(UCSR0A, UDRE0); /* Wait until data register empty. */
  UDR0 = c;
  return 0;
}
void uart_puts(const char *s)
{
  /* toto je vasa uloha */
}
char uart_getc(void)
{
    loop_until_bit_is_set(UCSR0A, RXC0); /* Wait until data exists. */
    return UDR0;
}


int main(void)
void delay(int delay)     // vlastna funkcia pre dlhsie casy
{
{
   unsigned int measuredValue;
   for (int i=1; i<=delay; i++)
  _delay_ms(1);
}
 
</syntaxhighlight ></tab>
<tab name="lcd.h"><syntaxhighlight  lang="c++" style="background: LightYellow;">
/*
*  lcd.h  Library for MISA @ FEI STU
*
*  Created: 15. 4. 2025 20:48:48  - added char definition support, removed ext. dependencies
*  Author: Richard Balogh based on gitHub code for EA*DOGM163
*/
 
#ifndef LCD_H_
#define LCD_H_
 
#include <stdlib.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
 
#define CHARACTER_BUFFER_BASE_ADDRESS 0x80
#define CHARACTERS_PER_ROW 16
 
#define DISPLAY_RS_PORT PORTD
#define DISPLAY_RS_DDR DDRD
#define DISPLAY_RS_PIN 2
#define DISPLAY_CSB_PORT PORTD
#define DISPLAY_CSB_DDR DDRD
#define DISPLAY_CSB_PIN 4
 
#define DISPLAY_BKLT_PORT PORTD
#define DISPLAY_BKLT_DDR DDRD
#define DISPLAY_BKLT_PIN 6
 
#define DISPLAY_RS_low  (DISPLAY_RS_PORT &= ~(1<<DISPLAY_RS_PIN))
#define DISPLAY_RS_high  (DISPLAY_RS_PORT |= (1<<DISPLAY_RS_PIN))
 
#define DISPLAY_CSB_low  (DISPLAY_CSB_PORT &= ~(1<<DISPLAY_CSB_PIN))
#define DISPLAY_CSB_high (DISPLAY_CSB_PORT |= (1<<DISPLAY_CSB_PIN))
 
#define DISPLAY_BKLT_low  (DISPLAY_BKLT_PORT &= ~(1<<DISPLAY_BKLT_PIN))
#define DISPLAY_BKLT_high  (DISPLAY_BKLT_PORT |= (1<<DISPLAY_BKLT_PIN))
 
 
//instructions (see the ST7036 instruction set for further information)
#define INSTRUCTION_CLEAR_DISPLAY    0b00000001
#define INSTRUCTION_FUNCTION_SET_INIT_0  0b00110011
#define INSTRUCTION_FUNCTION_SET_INIT_1  0b00110010
#define INSTRUCTION_FUNCTION_SET_INIT_2  0b00101001
#define INSTRUCTION_INSTRUCTION_SET_0    0b00101000
#define INSTRUCTION_INSTRUCTION_SET_1 0b00101001
#define INSTRUCTION_BIAS_SET 0b00010101
#define INSTRUCTION_POWER_CONTROL 0b01010011
#define INSTRUCTION_FOLLOWER_CONTROL 0b01101100
#define INSTRUCTION_CONTRAST_SET 0b01111111
#define INSTRUCTION_DISPLAY_ON 0b00001100
#define INSTRUCTION_ENTRY_MODE 0b00000110
 
void lcd_init(void);
void lcd_write( char data );
void lcd_data( char data );   
void lcd_command( char instruction );
 
void lcd_putc( char znak );
void lcd_puts( char *string );
 
void lcd_clear( void );
void lcd_clearline( unsigned char zeile );
 
void lcd_setCursor(char row, char col);
void lcd_bklt( char OnOff);


  while (1)
void def_znak(unsigned char *ZnakArray, unsigned char Address);
  {
 
     /* relax  *
#endif /* LCD_H_ */
  }
 
</syntaxhighlight ></tab>
<tab name="lcd.c"><syntaxhighlight  lang="c++" style="background: LightYellow;">
#include "lcd.h"
 
/* Funkcia zapise jeden bajt po SPI zbernici do zariadenia */
void lcd_write(char data) {
    signed char index = 8;
    char c_data;
 
    DISPLAY_CSB_PORT &= ~(1 << DISPLAY_CSB_PIN); // Chip-Select do log.0
    c_data = data;
 
    do {
        _delay_us(6);
        if (c_data & 0x80)
            PORTB |= (1 << 3); // MOSI
        else
            PORTB &= ~(1 << 3);
 
        _delay_us(5);
        PORTB &= ~(1 << 5); // CLK low
        _delay_us(6);
        PORTB |= (1 << 5); // CLK high
 
        c_data <<= 1;
        index--;
    } while (index > 0);
 
    _delay_ms(2);
    DISPLAY_CSB_PORT |= (1 << DISPLAY_CSB_PIN); // CS high
}
 
void lcd_command(char instruction) {
     DISPLAY_RS_PORT &= ~(1 << DISPLAY_RS_PIN);
    _delay_us(1);
    lcd_write(instruction);
}
 
void lcd_data(char data) {
    DISPLAY_RS_PORT |= (1 << DISPLAY_RS_PIN);
    _delay_us(7);
    lcd_write(data);
}
 
void lcd_init(void) {
    DDRB |= (1 << PB3) | (1 << PB5); // MOSI + SCK
    DISPLAY_RS_DDR |= (1 << DISPLAY_RS_PIN);
    DISPLAY_CSB_DDR |= (1 << DISPLAY_CSB_PIN);
    DISPLAY_BKLT_DDR |= (1 << DISPLAY_BKLT_PIN);
 
    PORTB |= (1 << PB5);
    DISPLAY_CSB_PORT |= (1 << DISPLAY_CSB_PIN);
    DISPLAY_RS_PORT &= ~(1 << DISPLAY_RS_PIN);
 
    _delay_ms(50);
 
    lcd_command(0x39);
    _delay_us(50);
    lcd_command(0x1D);
    _delay_us(50);
    lcd_command(0x50);
    _delay_us(50);
    lcd_command(0x6C);
    _delay_ms(500);
    lcd_command(0x7C);
    _delay_us(50);
    lcd_command(0x38);
    _delay_us(50);
    lcd_command(0x0F);
    _delay_us(50);
    lcd_command(0x01);
    _delay_ms(400);
    lcd_command(0x06);
    _delay_us(50);
}
 
void lcd_putc(char znak) {
    lcd_data(znak);
}
 
void lcd_puts(char *string) {
    while (*string) {
        lcd_data(*string++);
    }
}
 
void lcd_setCursor(char row, char col) {
    char address;
 
    switch (row) {
        case 0: address = 0x00 + col; break;
        case 1: address = 0x10 + col; break;
        case 2: address = 0x20 + col; break;
        default: address = 0x00 + col; break;
    }
 
    lcd_command(0x80 | address);
}
 
void lcd_clearline(unsigned char riadok) {
    lcd_setCursor(riadok, 0);
    for (unsigned char i = 0; i < 16; i++) {
        lcd_data(' ');
    }
}
 
void lcd_clear(void) {
    lcd_clearline(0);
    lcd_clearline(1);
    lcd_clearline(2);
}
 
void lcd_bklt(char OnOff) {
    if (OnOff)
        DISPLAY_BKLT_PORT |= (1 << DISPLAY_BKLT_PIN);
    else
        DISPLAY_BKLT_PORT &= ~(1 << DISPLAY_BKLT_PIN);
}


  return(0);
void def_znak(unsigned char *ZnakArray, unsigned char Address) {
    lcd_command(0x40 | (Address << 3));
    for (unsigned char i = 0; i < 8; i++) {
        lcd_data(*(ZnakArray + i));
    }
    lcd_command(0x80);
}
}


</syntaxhighlight ></tab>
</syntaxhighlight ></tab>
<tab name="filename.h"><syntaxhighlight  lang="c++" style="background: LightYellow;">
<tab name="dht11.h"><syntaxhighlight  lang="c++" style="background: LightYellow;">
#ifndef DHT11_H_
#define DHT11_H_
 
#include <avr/io.h>
#include <avr/io.h>
#include <util/delay.h>
#define DHT11_DDR DDRD
#define DHT11_PORT PORTD
#define DHT11_PIN PIND
#define DHT11_INPUTPIN PD5
uint8_t dht11_read(uint8_t* temperature, uint8_t* humidity);
#endif
</syntaxhighlight ></tab>
<tab name="dht11.c"><syntaxhighlight  lang="c++" style="background: LightYellow;">
#include "dht11.h"
uint8_t dht11_read(uint8_t* temperature, uint8_t* humidity) {
    uint8_t bits[5] = {0};
    uint8_t i, j = 0;
    DHT11_DDR |= (1 << DHT11_INPUTPIN);
    DHT11_PORT &= ~(1 << DHT11_INPUTPIN);
    _delay_ms(18);
    DHT11_PORT |= (1 << DHT11_INPUTPIN);
    _delay_us(40);
    DHT11_DDR &= ~(1 << DHT11_INPUTPIN);
  // if ((DHT11_PIN & (1 << DHT11_INPUTPIN))) return 1;
    _delay_us(80);
  // if (!(DHT11_PIN & (1 << DHT11_INPUTPIN))) return 2;
    _delay_us(80);
    for (j = 0; j < 5; j++) {
        for (i = 0; i < 8; i++) {
            while (!(DHT11_PIN & (1 << DHT11_INPUTPIN)));
            _delay_us(30);
            if (DHT11_PIN & (1 << DHT11_INPUTPIN)) bits[j] |= (1 << (7 - i));
            while (DHT11_PIN & (1 << DHT11_INPUTPIN));
        }
    }
    if ((uint8_t)(bits[0] + bits[1] + bits[2] + bits[3]) != bits[4])
        return 3;
    *humidity = bits[0];
    *temperature = bits[2];
    return 0;
}


void adc_init(void);                                  // A/D converter initialization


unsigned int adc_read(char a_pin);
</syntaxhighlight ></tab>
</syntaxhighlight ></tab>
</tabs>
</tabs>


Pridajte sem aj zbalený kompletný projekt, napríklad takto (použite jednoznačné pomenovanie, nemôžeme mať na serveri 10x ''zdrojaky.zip'':  
Kompletný projekt nájdete tu:  


Zdrojový kód: [[Médiá:projektMenoPriezvisko.zip|zdrojaky.zip]]
Zdrojový kód: [[Médiá:ProjektAlehSobaleu.zip|ProjektAlehSobaleu.zip]]


=== Overenie ===
=== Overenie ===


Ako ste overili funkciu, napríklad... Na používanie našej aplikácie stačia dve tlačítka a postup používania je opísaný v sekcii popis riešenia.  
Správnosť zapojenia a funkčnosť programu som overoval priamo počas vývoja. Aby som otestoval, či senzor DHT11 správne reaguje na zmeny prostredia, rozhodol som sa použiť jednoduchú a okamžite účinnú metódu – fúkaním ústami na senzor.
Na konci uvádzame fotku hotového zariadenia.  
 
Teplý a vlhký vzduch z dychu spôsobil, že senzor zaznamenal nárast teploty aj relatívnej vlhkosti. Tieto zmeny boli následne zobrazené na LCD displeji, čím som si overil, že čítanie údajov prebieha korektne a výstup je spoľahlivý.
 
Hodnoty sa obnovovali každé dve sekundy, takže reakcia bola viditeľná veľmi rýchlo. Údaje sa zároveň zobrazovali aj v sériovom termináli cez UART, čo uľahčilo ladenie a sledovanie výsledkov.  


[[Súbor:GeminiAI-image1.jpg|400px|thumb|center|Aplikácia.]]
[[Súbor:FinalDHT11.jpg|400px|thumb|center|Fotka hotového zariadenia]]


'''Video:'''
'''Video:'''
<center><youtube>D0UnqGm_miA</youtube></center>
<center><youtube>JLDHHC5HRKo</youtube></center>





Aktuálna revízia z 22:42, 21. máj 2025

Záverečný projekt predmetu MIPS / LS2025 - Aleh Sobaleu


Zadanie

Cieľom tohto projektu bolo navrhnúť jednoduchý systém na meranie teploty a vlhkosti pomocou senzora DHT11, ktorý komunikuje s mikrokontrolérom ATmega328P. Získané údaje sa zobrazujú na LCD displeji, pričom celý proces prebieha v reálnom čase bez potreby ďalšieho zásahu používateľa.

Vývojová doska ACROB.

Literatúra:


Analýza a opis riešenia

Projekt som postavil na základnej doske s mikrokontrolérom ATmega328P, pričom k nej boli pripojené dva hlavné komponenty: DHT11 senzor a LCD displej. Celé zapojenie som najskôr testoval na breadboarde a až po overení funkčnosti som ho zafixoval.

Použité súčiastky:

1. DHT11 – senzor teploty a vlhkosti Tento senzor meria aktuálnu teplotu (v stupňoch Celzia) a relatívnu vlhkosť vzduchu v percentách. Má 3 piny:

VCC – napájanie (5V)

GND – zem

DATA – dátový výstup

Senzor komunikuje cez digitálny jednovodičový protokol, takže na prenos údajov je použitý iba jeden pin mikrokontroléra – v mojom prípade to bol PD5 (Arduino pin D5).

Senzor DHT11 – pohľad zhora

2. LCD displej (EA DOGM163 / ST7036) Použil som trojriadkový LCD displej s ovládačom ST7036, ktorý komunikuje cez SPI zbernicu. Pripojenie vyzerá nasledovne:

SI → PB3

SCK → PB5

RS → PD2

CS → PD4

BKLT → PD6

+5V a GND napájanie

LCD displej slúži na prehľadné zobrazenie aktuálnej teploty a vlhkosti. Dáta sa obnovujú každé dve sekundy.

LCD displej s ovládačom ST7036

3. Schéma zapojenia:

Celé zariadenie bolo zapojené na breadboarde pomocou prepojovacích vodičov. Použil som mikrokontrolér ATmega328P, ktorý riadi čítanie dát zo senzora DHT11 a ich následné zobrazenie na LCD displeji. Komponenty boli zvolené tak, aby spolu komunikovali spoľahlivo bez rušenia a aby sa dali pripojiť priamo k pinom mikrokontroléra bez potreby zložitej logiky.

Základné zapojenie komponentov:

1. Senzor DHT11

VCC senzora → 5V na doske

GND senzora → GND na doske

DATA senzora → pin PD5 (Arduino D5)

Senzor DHT11 komunikuje cez digitálny jednovodičový protokol. Mikrokontrolér najprv vyšle signál na prebudiť senzor a potom čaká na odpoveď a číta 5 bajtov – vlhkosť, teplotu a kontrolný súčet.

2. LCD displej (ST7036)

LCD displej komunikuje cez SPI.

Použité piny:

PB3 (MOSI) → SI (dáta do displeja)

PB5 (SCK) → hodiny (clock)

PD2 → RS (výber dátového alebo príkazového režimu)

PD4 → CS (výber displeja)

PD6 → podsvietenie (BKLT)

VCC → 5V

GND → GND

Displej zobrazuje na 1. riadku teplotu v °C a na 2. riadku vlhkosť v %.

UART výstup je pripojený k USB-UART prevodníku a slúži na monitorovanie výstupu cez terminál (PuTTY).

Schéma zapojenia vytvorená v programe EasyEDA.


Algoritmus a program

Použité knižnice:

dht11.h a dht11.c – vlastná knižnica, ktorú som upravil pre čítanie dát zo senzora DHT11 cez digitálny pin. Obsahuje základnú komunikáciu, spracovanie bitov a kontrolu správnosti pomocou kontrolného súčtu.

lcd.h a lcd.c – knižnica pre prácu s LCD displejom, ktorú sme dostali ako základ na cvičení. Rozšíril som ju o vlastné funkcie lcd_puts, lcd_setCursor, lcd_clearline a lcd_print_num.

uart.h a uart.c – jednoduchá knižnica na výpis textu cez sériový port. Umožňuje používať štandardné funkcie ako printf() a uart_puts().


#include "lcd.h"             // Knižnica pre prácu s LCD displejom
#include <avr/io.h>          // Základné definície pre AVR
#include <util/delay.h>      // Funkcia pre oneskorenie
#include <stdlib.h>
#include <stdio.h>

#include "uart.h"            // Knižnica pre sériovú komunikáciu
#include "dht11.h"           // Knižnica pre DHT11 senzor

#define LED1 PB5             // Interná LED na doske (napr. Arduino UNO)
#define LED2 PC2             // Externá LED (voliteľná)
#define SW_A PC3             // Tlačidlo (nepoužíva sa v tomto kóde)

FILE mystdout = FDEV_SETUP_STREAM(uart_putc, NULL, _FDEV_SETUP_WRITE); // Pripojenie výstupu printf k UARTu

// Pomocná funkcia na zobrazenie čísla na LCD ako text
void lcd_print_num(uint8_t num) {
 char buffer[5];
 itoa(num, buffer, 10);     // Konverzia čísla na string
 lcd_puts(buffer);          // Zobrazenie na LCD
}

int main(void) {
 uint8_t temp, hum;         // Premenné pre teplotu a vlhkosť

 // Nastavenie pinov ako výstupy pre LEDky
 DDRB |= (1<<LED1);
 DDRC |= (1<<LED2);

 uart_init();               // Inicializácia sériového portu
 stdout = &mystdout;        // Prepojenie printf na UART
 printf("DHT11 + LCD init\n");

 lcd_init();                // Inicializácia LCD
 lcd_bklt(1);               // Zapnutie podsvietenia
 lcd_command(0x0C);         // Zapnutie displeja, vypnutie kurzora
 lcd_clear();               // Vyčistenie obrazovky

 while (1) {
  // Pokus o načítanie dát zo senzora
  if (dht11_read(&temp, &hum) == 0) {
   // Ak úspešné čítanie — spracovanie teploty
   char send[16];
   sprintf(send,"Teplota: %d %C",temp); // Formátovaný výstup
   lcd_setCursor(0, 0);                 // Riadok 1, stĺpec 0
   lcd_puts(send);
   lcd_putc(0xDF);                      // Znak ° (stupne)
   lcd_putc('C');                       // Celsia

   // Spracovanie vlhkosti
   char message[16];
   sprintf(message,"Vlhkost: %d %%",hum);
   lcd_setCursor(1, 0);                 // Riadok 2, stĺpec 0
   lcd_puts(message);

   // Výpis aj do terminálu
   printf("T: %d C, H: %d %%\n", temp, hum);
  } else {
   // Ak čítanie zlyhalo — chyba
   lcd_setCursor(0, 0);
   lcd_puts("Chyba citania ");          // Hlásenie na LCD
   lcd_setCursor(1, 0);
   lcd_puts(" ");                       // Vymazanie druhého riadku
   printf("Read error: %d\n", dht11_read(&temp, &hum));
  }

  _delay_ms(2000);                      // Požadovaná pauza medzi čítaniami
 }
}
#define LED PB5  // interná LED na doske (napr. Arduino UNO)

/* Užitočné makrá pre prácu s tlačidlami alebo pinmi */

// Test, či je bit nastavený
// bit_is_set(PINB, SW1)

// Test, či je bit nulový (tlačidlo stlačené pri pull-up odpore)
// bit_is_clear(PINB, SW1)

/* Preddefinované slučky pre čakanie na udalosť */

// Čakanie, kým sa tlačidlo pustí
// loop_until_bit_is_set(PINB, SW1);

// Čakanie, kým sa tlačidlo stlačí
// loop_until_bit_is_clear(PINB, SW1);

#define set_bit(ADDRESS,BIT) (ADDRESS |= (1<<BIT))       // Nastaví bit v registri
#define clear_bit(ADDRESS,BIT) (ADDRESS &= ~(1<<BIT))    // Vynuluje bit v registri

#ifndef UART_H_
#define UART_H_

#include <stdio.h>

/* Výpočet predvoľby pre BAUD rate na základe frekvencie CPU a požadovanej rýchlosti.
 * Vzorec je prevzatý z datasheetu pre ATmega328P.
 */
#define BAUD_PRESCALE  (((F_CPU / (BAUDRATE * 16UL))) - 1)

/* Inicializuje potrebný hardvér (voliteľné, ak sa používa) */
void hw_init(void);

/* Inicializuje UART (nastaví rýchlosť, formát rámca a povolí prenos) */
void uart_init(void);

/*
 * Funkcia pre výpis znakov cez UART, kompatibilná s printf().
 * Vďaka tomu je možné napísať:
 *     printf("Text %d\n", hodnota);
 * a výstup sa odošle cez UART.
 */
int uart_putc(char c, FILE *stream);

/* Jednoduchý výpis reťazca cez UART (bez formátovania) */
void uart_puts(const char *s);

/* Prijme jeden znak z UART (blokuje, kým nepríde znak) */
char uart_getc(void);

/* Jednoduchá funkcia delay (ak sa nepoužíva _delay_ms()) */
void delay(int delay);

#endif /* UART_H_ */
/* ************************************************************************* */
/* FileName:             uart.c                                              */
/* ************************************************************************* */

#include <avr/io.h>
#include <util/delay.h>
#include "uart.h"

void hw_init( void )
{ 
  DDRB |= (1<<LED);    // PORTB.5 kde je LED ma byt OUTPUT
  /* sem si mozete dopisat svoje vlastne inicializacne prikazy */ 	
}

void uart_init( void ) 
{
//  for different BAUD rate change the project settings, or uncomment 
//  following two lines:	
//	#undef  BAUD           // avoid compiler warning
//  #define BAUD 115200
	
   #include <util/setbaud.h>  // requires defined BAUD
   
   UBRR0H = UBRRH_VALUE;
   UBRR0L = UBRRL_VALUE;
   #if USE_2X                 // defined in setbaud.h 
    UCSR0A |= (1 << U2X0);
   #else
    UCSR0A &= ~(1 << U2X0);
   #endif


    UCSR0C = _BV(UCSZ01) | _BV(UCSZ00); /* 8-bit data */
    UCSR0B = _BV(RXEN0) | _BV(TXEN0);   /* Enable RX and TX */	
}


int uart_putc( char c, FILE *stream )
{
   if (c == '\n') 
      uart_putc('\r',stream);
   
   loop_until_bit_is_set(UCSR0A, UDRE0); /* Wait until data register empty. */
   UDR0 = c;
   return 0;
}


void uart_puts(const char *s)
{
  /* toto je vasa uloha */
}

char uart_getc(void) 
{
    loop_until_bit_is_set(UCSR0A, RXC0); /* Wait until data exists. */
    return UDR0;
}

void delay(int delay)      // vlastna funkcia pre dlhsie casy 
{
  for (int i=1; i<=delay; i++)
  _delay_ms(1);
}
/*
 *  lcd.h  Library for MISA @ FEI STU
 *
 *  Created: 15. 4. 2025 20:48:48  - added char definition support, removed ext. dependencies
 *   Author: Richard Balogh based on gitHub code for EA*DOGM163
 */ 

#ifndef LCD_H_
#define LCD_H_

#include <stdlib.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>

#define CHARACTER_BUFFER_BASE_ADDRESS 0x80
#define CHARACTERS_PER_ROW 16

#define DISPLAY_RS_PORT PORTD
#define DISPLAY_RS_DDR DDRD
#define DISPLAY_RS_PIN 2
#define DISPLAY_CSB_PORT PORTD
#define DISPLAY_CSB_DDR DDRD
#define DISPLAY_CSB_PIN 4

#define DISPLAY_BKLT_PORT PORTD
#define DISPLAY_BKLT_DDR DDRD
#define DISPLAY_BKLT_PIN 6

#define DISPLAY_RS_low   (DISPLAY_RS_PORT &= ~(1<<DISPLAY_RS_PIN))
#define DISPLAY_RS_high  (DISPLAY_RS_PORT |= (1<<DISPLAY_RS_PIN))

#define DISPLAY_CSB_low  (DISPLAY_CSB_PORT &= ~(1<<DISPLAY_CSB_PIN))
#define DISPLAY_CSB_high (DISPLAY_CSB_PORT |= (1<<DISPLAY_CSB_PIN))

#define DISPLAY_BKLT_low   (DISPLAY_BKLT_PORT &= ~(1<<DISPLAY_BKLT_PIN))
#define DISPLAY_BKLT_high  (DISPLAY_BKLT_PORT |= (1<<DISPLAY_BKLT_PIN))


//instructions (see the ST7036 instruction set for further information)
#define INSTRUCTION_CLEAR_DISPLAY    		0b00000001
#define INSTRUCTION_FUNCTION_SET_INIT_0  	0b00110011
#define INSTRUCTION_FUNCTION_SET_INIT_1  	0b00110010
#define INSTRUCTION_FUNCTION_SET_INIT_2  	0b00101001
#define INSTRUCTION_INSTRUCTION_SET_0    	0b00101000
#define INSTRUCTION_INSTRUCTION_SET_1 		0b00101001
#define INSTRUCTION_BIAS_SET				0b00010101
#define INSTRUCTION_POWER_CONTROL 			0b01010011
#define INSTRUCTION_FOLLOWER_CONTROL		0b01101100
#define INSTRUCTION_CONTRAST_SET			0b01111111
#define INSTRUCTION_DISPLAY_ON				0b00001100
#define INSTRUCTION_ENTRY_MODE 				0b00000110

void lcd_init(void);
void lcd_write( char data );
void lcd_data( char data );     
void lcd_command( char instruction );

void lcd_putc( char znak );
void lcd_puts( char *string );

void lcd_clear( void );
void lcd_clearline( unsigned char zeile );

void lcd_setCursor(char row, char col);
void lcd_bklt( char OnOff);

void def_znak(unsigned char *ZnakArray, unsigned char Address);

#endif /* LCD_H_ */
#include "lcd.h"

/* Funkcia zapise jeden bajt po SPI zbernici do zariadenia */
void lcd_write(char data) {
    signed char index = 8;
    char c_data;

    DISPLAY_CSB_PORT &= ~(1 << DISPLAY_CSB_PIN); // Chip-Select do log.0
    c_data = data;

    do {
        _delay_us(6);
        if (c_data & 0x80)
            PORTB |= (1 << 3); // MOSI
        else
            PORTB &= ~(1 << 3);

        _delay_us(5);
        PORTB &= ~(1 << 5); // CLK low
        _delay_us(6);
        PORTB |= (1 << 5); // CLK high

        c_data <<= 1;
        index--;
    } while (index > 0);

    _delay_ms(2);
    DISPLAY_CSB_PORT |= (1 << DISPLAY_CSB_PIN); // CS high
}

void lcd_command(char instruction) {
    DISPLAY_RS_PORT &= ~(1 << DISPLAY_RS_PIN);
    _delay_us(1);
    lcd_write(instruction);
}

void lcd_data(char data) {
    DISPLAY_RS_PORT |= (1 << DISPLAY_RS_PIN);
    _delay_us(7);
    lcd_write(data);
}

void lcd_init(void) {
    DDRB |= (1 << PB3) | (1 << PB5); // MOSI + SCK
    DISPLAY_RS_DDR |= (1 << DISPLAY_RS_PIN);
    DISPLAY_CSB_DDR |= (1 << DISPLAY_CSB_PIN);
    DISPLAY_BKLT_DDR |= (1 << DISPLAY_BKLT_PIN);

    PORTB |= (1 << PB5);
    DISPLAY_CSB_PORT |= (1 << DISPLAY_CSB_PIN);
    DISPLAY_RS_PORT &= ~(1 << DISPLAY_RS_PIN);

    _delay_ms(50);

    lcd_command(0x39);
    _delay_us(50);
    lcd_command(0x1D);
    _delay_us(50);
    lcd_command(0x50);
    _delay_us(50);
    lcd_command(0x6C);
    _delay_ms(500);
    lcd_command(0x7C);
    _delay_us(50);
    lcd_command(0x38);
    _delay_us(50);
    lcd_command(0x0F);
    _delay_us(50);
    lcd_command(0x01);
    _delay_ms(400);
    lcd_command(0x06);
    _delay_us(50);
}

void lcd_putc(char znak) {
    lcd_data(znak);
}

void lcd_puts(char *string) {
    while (*string) {
        lcd_data(*string++);
    }
}

void lcd_setCursor(char row, char col) {
    char address;

    switch (row) {
        case 0: address = 0x00 + col; break;
        case 1: address = 0x10 + col; break;
        case 2: address = 0x20 + col; break;
        default: address = 0x00 + col; break;
    }

    lcd_command(0x80 | address);
}

void lcd_clearline(unsigned char riadok) {
    lcd_setCursor(riadok, 0);
    for (unsigned char i = 0; i < 16; i++) {
        lcd_data(' ');
    }
}

void lcd_clear(void) {
    lcd_clearline(0);
    lcd_clearline(1);
    lcd_clearline(2);
}

void lcd_bklt(char OnOff) {
    if (OnOff)
        DISPLAY_BKLT_PORT |= (1 << DISPLAY_BKLT_PIN);
    else
        DISPLAY_BKLT_PORT &= ~(1 << DISPLAY_BKLT_PIN);
}

void def_znak(unsigned char *ZnakArray, unsigned char Address) {
    lcd_command(0x40 | (Address << 3));
    for (unsigned char i = 0; i < 8; i++) {
        lcd_data(*(ZnakArray + i));
    }
    lcd_command(0x80);
}
#ifndef DHT11_H_
#define DHT11_H_

#include <avr/io.h>
#include <util/delay.h>

#define DHT11_DDR DDRD
#define DHT11_PORT PORTD
#define DHT11_PIN PIND
#define DHT11_INPUTPIN PD5

uint8_t dht11_read(uint8_t* temperature, uint8_t* humidity);

#endif
#include "dht11.h"

uint8_t dht11_read(uint8_t* temperature, uint8_t* humidity) {
    uint8_t bits[5] = {0};
    uint8_t i, j = 0;

    DHT11_DDR |= (1 << DHT11_INPUTPIN);
    DHT11_PORT &= ~(1 << DHT11_INPUTPIN);
    _delay_ms(18);
    DHT11_PORT |= (1 << DHT11_INPUTPIN);
    _delay_us(40);
    DHT11_DDR &= ~(1 << DHT11_INPUTPIN);

   // if ((DHT11_PIN & (1 << DHT11_INPUTPIN))) return 1;
    _delay_us(80);
   // if (!(DHT11_PIN & (1 << DHT11_INPUTPIN))) return 2;
    _delay_us(80);

    for (j = 0; j < 5; j++) {
        for (i = 0; i < 8; i++) {
            while (!(DHT11_PIN & (1 << DHT11_INPUTPIN))); 
            _delay_us(30);
            if (DHT11_PIN & (1 << DHT11_INPUTPIN)) bits[j] |= (1 << (7 - i));
            while (DHT11_PIN & (1 << DHT11_INPUTPIN)); 
        }
    }

    if ((uint8_t)(bits[0] + bits[1] + bits[2] + bits[3]) != bits[4])
        return 3;

    *humidity = bits[0];
    *temperature = bits[2];
    return 0;
}

Kompletný projekt nájdete tu:

Zdrojový kód: ProjektAlehSobaleu.zip

Overenie

Správnosť zapojenia a funkčnosť programu som overoval priamo počas vývoja. Aby som otestoval, či senzor DHT11 správne reaguje na zmeny prostredia, rozhodol som sa použiť jednoduchú a okamžite účinnú metódu – fúkaním ústami na senzor.

Teplý a vlhký vzduch z dychu spôsobil, že senzor zaznamenal nárast teploty aj relatívnej vlhkosti. Tieto zmeny boli následne zobrazené na LCD displeji, čím som si overil, že čítanie údajov prebieha korektne a výstup je spoľahlivý.

Hodnoty sa obnovovali každé dve sekundy, takže reakcia bola viditeľná veľmi rýchlo. Údaje sa zároveň zobrazovali aj v sériovom termináli cez UART, čo uľahčilo ladenie a sledovanie výsledkov.

Fotka hotového zariadenia

Video:



Kľúčové slová 'Category', ktoré sú na konci stránky nemeňte.