Αισθητήρες θερμοκρασίας/υγρασίας DHT11 – DHT22 (AM2302)

 

Συγγραφέας: Πορλιδάς Δημήτριος

Βιογραφικό Σημείωμα

electronics@porlidas.gr

Facebook

Linkedin


 

 

Οι αισθητήρες θερμοκρασίας/υγρασίας DHT11 και DHT22 (AM2302) χρησιμοποιούνται σε πολλές εφαρμογές λόγω της χαμηλής τιμής τους σε σχέση με την υψηλή ακρίβεια και αξιοπιστία τους, καθώς επίσης και την ευκολία σύνδεσης με μονάδες επεξεργασίας. Η σύνδεση γίνεται με μια αμφίδρομη γραμμή δεδομένων[1] και η τροφοδοσία τους είναι 3-5.5V με μέση κατανάλωση ρεύματος 0.2-1mA.

Η ανάλυσή τους είναι 1%RH (σχετική υγρασία) και 1οC (βαθμοί Κελσίου) για το DHT11 και 0.1%RH και 0.1οC αντίστοιχα για το DHT22. Κατά την επικοινωνία με τη μονάδα επεξεργασίας το DHT στέλνει ένα πακέτο 40 bit από τα οποία τα 16 είναι για την υγρασία, τα 16 επόμενα για τη θερμοκρασία και τα τελευταία 8 για έλεγχο ισοτιμίας (Πίνακας Ι).

Πίνακας Ι. Format πακέτου απάντησης του DHT.

0011 0101

0000 0000

0001 1000

0000 0000

0100 1101

Humidity High

Humidity Low

Temp. High

Temp. Low

Parity

Calculate Parity: 0011 0101 + 0000 0000 + 0001 1000 + 0000 0000 = 0100 1101

Στο DHT11 τα 16 bit της υγρασίας χωρίζονται σε δύο τμήματα των 8 bit εκ των οποίων το ψηλότερο περιέχει την πληροφορία της υγρασίας ενώ το χαμηλότερο είναι πάντα μηδέν. Το εύρος μέτρησης της υγρασίας είναι 20-90%RH. Το ίδιο ισχύει και για την πληροφορία της θερμοκρασίας όπου όμως το εύρος μέτρησης για αυτήν είναι 0-50οC. 

Στο DHT22 τα 16 bit της υγρασίας περιέχουν την πληροφορία σε δέκατα της μονάδας μέτρησης και το εύρος τιμών είναι 0 – 999 αντιστοιχώντας σε υγρασία 0 – 99.9%RH. Τα 16 bit της θερμοκρασίας περιέχουν την πληροφορία με διαφορετικό τρόπο γιατί η θερμοκρασία μπορεί να πάρει και αρνητικές τιμές. Το πρόσημο καθορίζεται από το MSB, το οποίο όταν είναι 1 η μέτρηση αντιστοιχεί σε αρνητική θερμοκρασία[2]. Τα υπόλοιπα 15 bit περιέχουν την πληροφορία σε δέκατα της μονάδας μέτρησης και το εύρος τιμών είναι 0 – 800. Η περιοχή μέτρησης της θερμοκρασίας είναι -40 – 80οC. Τα bit ισοτιμίας υπολογίζονται και στους δύο αισθητήρες με τον ίδιο τρόπο, αθροίζοντας δηλαδή τα τέσσερα 8bit μέρη σα να είναι ανεξάρτητα μεταξύ τους.

Η επικοινωνία με τη μονάδα επεξεργασίας ακολουθεί ένα συγκεκριμένο χρονοδιάγραμμα. Σε κανονική λειτουργία το DHT βρίσκεται σε κατάσταση αναμονής παρακολουθώντας το δίαυλο. Ο επεξεργαστής αιτείται επικοινωνία με τον αισθητήρα κατεβάζοντας το δίαυλο σε χαμηλή λογική στάθμη για 18ms και στη συνέχεια ανεβάζοντάς τον σε υψηλή για 40μs. Το DHT απαντά κατεβάζοντας το δίαυλο σε χαμηλή λογική στάθμη για 54μs και στη συνέχεια ανεβάζοντάς τον σε υψηλή για 80μs. Στη συνέχεια αρχίζει το DHT να αποστέλλει τα 40 bit που περιέχουν τα δεδομένα της τελευταίας μέτρησης του. Το 0 ή 1 των δεδομένων καθορίζεται από τη διάρκεια της υψηλής λογικής στάθμης στο δίαυλο. Όταν είναι 24μs αντιστοιχεί σε 0 ενώ όταν είναι 70μs αντιστοιχεί σε 1. Το ενδιάμεσο διάστημα χαμηλής λογικής στάθμης είναι πάντα 54μs. Πρώτο bit του πακέτου λαμβάνεται το MSB του Humidity High και ακολουθούν τα υπόλοιπα προς τα δεξιά όπως καταγράφονται στον Πίνακα Ι. Στο σχήμα 1 παρουσιάζεται γραφικά το χρονοδιάγραμμα της επικοινωνίας.

Σχήμα 1. Χρονοδιάγραμμα επικοινωνίας DHT με MCU.

Το DHT κάνει μια μέτρηση περίπου κάθε δύο δευτερόλεπτα και τον ενδιάμεσο χρόνο βρίσκεται σε κατάσταση χαμηλής κατανάλωσης ενέργειας (100 – 150μΑ). Κατά την αρχική τροφοδοσία με τάση πρέπει να δοθεί χρόνος εκκίνησης στον αισθητήρα τουλάχιστο 1s και μετά να γίνει αίτηση επικοινωνίας. Επίσης το χρονικό διάστημα μεταξύ δύο αιτημάτων πρέπει να είναι μεγαλύτερο του 1s. Στη συνέχεια παρουσιάζεται ένα παράδειγμα επικοινωνίας με DHT11 γραμμένο σε γλώσσα προγραμματισμού C: 

#include <avr/io.h>

#include <util/delay.h>

uint8_t dhtreg[5];

uint8_t rdf;

int16_t hum;

int16_t tmp;

/* Your code here */

int main(void)

{

/* Your code here */

while (1)

{

_delay_ms(1000);                           // 1s for DHT start up

DDRC |= (1<<0);                            // MCU Port turns to output

PORTC &= ~(1<<0);                          // MCU Request (PORT = 0)

_delay_ms(18);

PORTC |= (1<<0);                           // MCU Request (PORT = 1)

_delay_us(40);

DDRC &= ~(1<<0);                           // MCU Port turns to input

PORTC |= (1<<0);                           // Enable pull up resistor

_delay_us(20);

while (!(bit_is_set(PINC,0)));             // DHT Response PC0 = 0

_delay_us(100);                            // DHT Response PC0 = 1

for (uint8_t rgi = 0; rgi < 5; rgi++)      // Start reading DHT packet

{

for (uint8_t bti = 0; bti < 8; bti++)   // Start reading byte

{

while (!(bit_is_set(PINC,0)));       // Wait while PC0 = 0

_delay_us(40);

dhtreg[rgi] = dhtreg[rgi]<<1;        // Shift register

if (bit_is_set(PINC,0))              // Read 1

{

dhtreg[rgi] |= (1<<0);            // Load value 1

              rdf = 0;                          // Reset fault counter

              while (bit_is_set(PINC,0))        // Wait while PC0 = 1

{

rdf++;                          // Increase fault counter

_delay_us(5);

if (rdf > 100)                  // If time out

{

/* Your code here */

break;                       // Break previous “while”

}

}

}                     

}

}

hum = 0; hum = dhtreg[0];                  // DHT11 humidity

tmp = 0; tmp = dhtreg[2];                  // DHT11 temperature

/* Your code here */

}

}

Στην αρχή του κυκλικού βρόχου υπάρχει μια καθυστέρηση ώστε να δοθεί χρόνος εκκίνησης στο DHT. Στη συνέχεια το pin που είναι συνδεδεμένο στον αισθητήρα (στο παράδειγμα PC0) γίνεται έξοδος και με την επόμενη εντολή περνάει σε χαμηλή λογική στάθμη και παραμένει εκεί για 18ms. Μετά αλλάζει σε υψηλή λογική για 40μs και ακολουθούν οι εντολές που γίνεται είσοδος και ενεργοποιείται η pull up αντίσταση[3]. Με αυτήν την αλληλουχία ολοκληρώνεται η αίτηση προς DHT το οποίο απαντάει κατεβάζοντας το δίαυλο σε χαμηλή λογική. Η καθυστέρηση των 20μs υπάρχει για να προλάβει να σταθεροποιηθεί ο δίαυλος στη χαμηλή λογική (η οποία αναμένεται να διαρκέσει 54μs). Με την επόμενη εντολή το πρόγραμμα περιμένει όσο διαρκεί χαμηλή και όταν γίνει υψηλή εκτελεί μια καθυστέρηση 100μs ώστε να ολοκληρωθεί ο παλμός των 80μs. Μετά την ολοκλήρωση του παλμού αρχίζει η αποστολή του πακέτου 40bit[4]. Το πρόγραμμα περιμένει να ολοκληρωθεί η χαμηλή λογική που προηγείται του bit της πληροφορίας και στη συνέχεια εκτελεί μια ακόμα καθυστέρηση 40μs και κάνει αριστερή ολίσθηση κατά μία θέση στη μεταβλητή που θα υποδεχθεί το bit. Αν το λαμβανόμενο bit είναι 0 κλείνει ο κύκλος της εσωτερική for αφού με την παραπάνω καθυστέρηση έχει ολοκληρωθεί ο παλμός των 24μs που αντιστοιχεί σε 0 και δεν εκτελείται η εντολή if (bit_is_set(PINC,0)). Από την αριστερή ολίσθηση το LSB έχει συμπληρωθεί με 0 και έτσι δε χρειάζεται καμία άλλη παρέμβαση.

Αν το λαμβανόμενο bit είναι 1 εκτελείται η εντολή if (bit_is_set(PINC,0)), αφού με την καθυστέρηση των 40μs δεν έχει προλάβει να ολοκληρωθεί ο παλμός των 70μs που αντιστοιχεί σε 1. Στο μπλοκ της εντολής τοποθετείται το LSB της μεταβλητής και το πρόγραμμα περιμένει την ολοκλήρωση του παλμού ώστε να κλείσει ο κύκλος της for. Αν υπάρξει μια αστοχία στη συνδεσμολογία του αισθητήρα, το πρόγραμμα θα μείνει για πάντα σε αυτό το σημείο, εξ αιτίας της pull up αντίστασης του μικροελεγκτή. Για αυτό το λόγο η μεταβλητή rdf αυξάνει συνεχώς το περιεχόμενό της μέσα στην while και όταν ξεπεράσει ένα όριο που έχουμε επιλέξει ως time out κάνει break[5] σε αυτήν, ώστε να κλείσει αναγκαστικά ο κύκλος της for.  Όταν ολοκληρωθούν και οι δύο for έχουν ληφθεί τα 40bit μπορεί να γίνει η επεξεργασία τους.

Αν ο αισθητήρας είναι DHT-11 οι μεταβλητές hum και tmp  μπορούν να είναι 8bit ή να μη χρησιμοποιηθούν καθόλου, αφού η πληροφορίες της υγρασίας και της θερμοκρασίας θα βρίσκονται ήδη, από τη ρουτίνα ανάγνωσης, στις dhtreg[0] και dhtreg[2] αντίστοιχα. Εναλλακτικά μπορούν να παραμείνουν ως 16bit ώστε να έχουμε τη δυνατότητα να χρησιμοποιήσουμε στη συνέχεια τη συνάρτηση[6] itoa. Σε αυτήν την περίπτωση καλό είναι να μηδενίζουν πριν την ανάθεση της νέας τιμής.       

Αν ο αισθητήρας είναι DHT-22 οι μεταβλητές hum και tmp είναι απαραίτητες και πρέπει να είναι 16bit. Οι τελευταίες δύο σειρές πρέπει να τροποποιηθούν ως εξής:

hum = 0;                                 // DHT22

hum = dhtreg[0]<<8;                      // DHT22

hum |= dhtreg[1];                        // DHT22

             

tmp = 0;                                 // DHT22

tmp = dhtreg[2]<<8;                      // DHT22

tmp |= dhtreg[3];                        // DHT22            

Με τις παραπάνω εντολές φορτώνονται οι 8bit μεταβλητές του πίνακα dhtreg στα αντίστοιχα τμήματα των 16bit μεταβλητών hum και tmp. Η μετατροπή τους στη συνέχεια σε χαρακτήρες ASCII για απεικόνιση ή αποστολή μπορεί να γίνει με τη συνάρτηση itoa. Θα πρέπει να ληφθεί όμως υπόψη ότι οι μεταβλητές hum και tmp περιέχουν την πληροφορία σε δέκατα της μονάδας μέτρησης και έτσι χρειάζεται να γίνει διαχείριση της υποδιαστολής με λογισμικό κατά την απεικόνιση. Επίσης θα πρέπει να γίνει ανάλογη διαχείριση του αρνητικού πρόσημου για τη θερμοκρασία αφού για να παράγει η itoa αρνητικό πρόσημο θα πρέπει ο αρνητικός αριθμός να έχει τη μορφή του συμπληρώματος ως προς 2, κάτι που δεν ισχύει για τις αρνητικές τιμές του DHT. Μια βολική προσέγγιση είναι να καθαρίσουμε το bit του πρόσημου, ώστε να εκτελεστεί η itoa σε θετικό αριθμό και να εμφανίσουμε το πρόσημο με ανάλογη εντολή πριν την απεικόνιση των υπόλοιπων ψηφίων. Στη συνέχεια παρουσιάζεται (χωρίς σχολιασμό) ένα παράδειγμα απεικόνισης για το DHT22 γραμμένο σε γλώσσα προγραμματισμού C[7]:     

char humasc[6];

char tmpasc[6];

 

SeCu (L1R1); _delay_ms (5);       // Send cursor to 1st line 1st rank

WrDa('H');                        // Display H

WrDa('=');                        // Display =

itoa (hum, humasc, 10);           // Binary integer to decimal ASCII string

if (hum<10)                       // For values lower than 1.0

WrDa ('0');                       // Display 0 before decimal point

for (uint8_t idht = 0; idht < ((unsigned)strlen(humasc)-1); idht++)

WrDa (humasc[idht]);              // Send ASCII string to LCD

WrDa('.');                        // Display . for decimal point

WrDa (humasc[((unsigned)strlen(humasc)-1)]);    // Display last digit (decimal)

WrDa('%');                        // Display %

WrDa('R');                        // Display R

WrDa('H');                         // Display H

for (uint8_t idht = (unsigned)strlen(humasc); idht < 6; idht++)     

WrDa(' ');                        // Complete with blanks

             

SeCu (L2R1); _delay_ms (5);       // Send cursor to 2nd line 1st rank

 

WrDa('T');                        // Display T

WrDa('=');                        // Display =

if (bit_is_set(dhtreg[2],7))      // If negative temperature

{

WrDa('-');                      // Display -

tmp &= ~(1<<15);                // Clear negative bit

}            

itoa (tmp, tmpasc, 10);           // Binary integer to decimal ASCII string

if (tmp<10)                       // For values lower than 1.0

WrDa ('0');                     // Display 0

for (uint8_t idht = 0; idht < ((unsigned)strlen(tmpasc)-1); idht++) 

WrDa (tmpasc[idht]);              // Send ASCII string to LCD

WrDa('.');                        // Display .

WrDa (tmpasc[((unsigned)strlen(tmpasc)-1)]);    // Display last digit (decimal)

WrDa(223);                        // Display degree symbol o

WrDa('C');                        // Display C

for (uint8_t idht = (unsigned)strlen(tmpasc); idht < 6; idht++)     

WrDa(' ');                        // Complete with blanks

 

©2017 Πορλιδάς Δημήτριος

Download files:  Atmel Studio 7.0 prj   .c files


[1] Με τάση τροφοδοσίας 5V και γραμμή σύνδεσης με καλώδιο έως 20m χρειάζεται μια αντίσταση pull up της τάξεως των 5kΩ. Όταν η τάση τροφοδοσίας είναι 3.5V το καλώδιο της γραμμής σύνδεσης δεν πρέπει να ξεπερνάει τα 20cm.

[2] Παράδειγμα αρνητικής θερμοκρασίας: 1 000 0001 0011 1011 = 1 000(Bin) 13Β(Hex) = 1 000(Bin) 315(Dec) = -31.5οC. Προσοχή: Δε γίνεται χρήση αρνητικών αριθμών με τη μορφή του συμπληρώματος ως προς 2 για αρνητικές θερμοκρασίες.

[3] Η ενεργοποίηση της pull up είναι απαραίτητη για το ενδεχόμενο αστοχίας της συνδεσμολογίας με τον αισθητήρα. Η εξωτερική αντίσταση pull up που είναι συνδεδεμένη στο δίαυλο βρίσκεται κοντά στο DHT και στην περίπτωση που υπάρχουν συνδεσμολογίες προς την πλακέτα του μικροελεγκτή με επαφές, υπάρχει πιθανότητα να αστοχήσει κάποια από αυτές. Η εσωτερική pull up θα εξασφαλίσει υψηλή λογική τότε ώστε με την κατάλληλη ρουτίνα να διαχειριστούμε την αστοχία.

[4] Είναι βολικό να γίνει καταχώρηση σε πέντε 8bit μεταβλητές και για αυτό το λόγο συντάσσονται δύο εντολές for, η μία μέσα στην άλλη, ώστε με την εσωτερική να αλλάζουν τα bit και με την εξωτερική οι μεταβλητές.

[5] Μαζί με τη break μπορεί να εκτελεστεί και κάποια ρουτίνα ένδειξης σφάλματος σύνδεσης.

[6] Πληροφορίες για τη χρήση της συνάρτησης itoa θα βρείτε στο κεφάλαιο «Χρήσιμες ρουτίνες (tips)» http://porlidas.gr/AVR/CHAPT_A.pdf   

[7] Το παράδειγμα περιέχει ρουτίνες από τον κώδικα που παρουσιάζεται στο κεφάλαιο «Οθόνη LCD 2x16 (4x20)» http://porlidas.gr/AVR/CHAPT_11.pdf και πρέπει να χρησιμοποιηθούν σε συνδυασμό.

 

Σας ευχαριστώ για την υποστήριξή σας ώστε να γίνει η ιστοσελίδα μου καλύτερη.

© 2017 Πορλιδάς Δημήτριος