Sensor DHT11 DHT21 struktura i dekodowanie odebranych bajtów.
W poprzednim artykule przedstawiłem różnice w funkcjonalności rodziny sensorów (DHT11, DHT21) do pomiaru wartości temperatury i wilgotności względnej otoczenia. Stwierdziłem wtedy: Rodzina sensorów jest obfita w modele, które generalnie różnią się dokładnością pomiarów i ich powtarzalnością. DHT11, SHT11, DHT22, AM2301, AM2302, AM2303.
Teraz zajmę się przedstawieniem, i podaniem różnic, w sposób praktyczny jak przetworzyć odebrane bajty i wyłuskać z nich informację o wartościach zmierzonych czynników środowiskowych. Więc na „warsztat” trafia temat: sensor DHT11 DHT21 odczyt danych. Opis będzie nawiązywał do informacji z załączonych not katalogowych o nazwach: DHT11-1.pdf i AM2301.pdf. A więc do dzieła 😉
Z tego co jest istotne dla prawidłowego odczytania wartości pomiarów, to zakres i rozdzielczość generowanych wyników oraz ich precyzja. Dla nas ma znaczenie rozdzielczość z tytułu kodowania danych, na różny sposób (po mimo identycznie zorganizowanych bajtów z wysyłanymi danymi), pomiędzy poszczególnymi modelami sensorów.
No więc jak jest wiadomo, każdy sensor z poszczególnej rodziny „wypluwa” 40 bitów, z których powstaje 5 słów bajtowych. Licząc od części najbardziej znaczącej, każdy produkuje dwa bajty z wilgotnością, kolejnie dwa bajty z temperaturą i bajt z sumą kontrolną. I tu kończą się zgodności.
DHT11
ponieważ ma rozdzielczość 1 nie generuje części dziesiętnej (ułamkowej), która zawsze jest równa zero, zarówno przy temperaturze jak i wilgotności. Więc autor programu, który zamieściłem zignorował więc przetwarzanie tych bajtów, dla uproszczenia kodu.
Na potwierdzenie mojej teorii w nocie wyczytamy:
Data consists of decimal and integral parts. A complete data transmission is 40bit, and the
sensor sends higher data bit first.
Data format: 8bit integral RH data + 8bit decimal RH data + 8bit integral T data + 8bit decimal T
data + 8bit check sum. If the data transmission is right, the check-sum should be the last 8bit of
„8bit integral RH data + 8bit decimal RH data + 8bit integral T data + 8bit decimal T data”
Czyli dla podkreślenia: sensor wysyła najpierw część całkowitą, a potem dziesiętną w tym przypadku zawsze równą zero.
Jak to zrobić w programie?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 |
$regfile = "m8def.dat" $crystal = 8000000 'konfiguracja wyświetlacza LCD Config Lcd = 16 * 2 Config Lcdpin = Pin,Rs=Portc.5,E=Portc.4,Db4=Portc.3,Db5=Portc.2,Db6=Portc.1,Db7=Portc.0 Cursor Off Cls 'deklaracja procedury Declare Sub Get_th(t As Byte , H As Byte) 'Config Serialin = Buffered , Size = 128 'konfiguracja hardware UART 'Config Serialout = Buffered , Size = 128 'konfiguracja hardware UART 'czujnik podłączony do PortD.6 Dht_put Alias Portd.6 Dht_get Alias Pind.6 Dht_io_set Alias Ddrd.6 Dim T As Byte 'zmienna do przechowywania temperatury Dim H As Byte 'zmienna do przechowywania wilgotności Dim Crc As Byte 'zmienna do przechowywania bajtów parzystości Dim Mybyte As Byte Dim Sensor_data As String * 40 'tutaj będą zapisywać się dane z czujnika Dim Tmp_str8 As String * 8 Dim Count As Byte 'zmienna do obliczania odebranych bitów 'Enable Interrupts Set Dht_io_set Set Dht_put Lcd "DHT 11" Lowerline Lcd " sensor" Do Waitms 1500 Call Get_th(t , H) Cls Lcd "TMP: " ; T ; "C" Lowerline Lcd "PHP: " ; H ; "%" Loop Sub Get_th(t As Byte , H As Byte) Count = 0 Sensor_data = "" Set Dht_io_set 'robimy wyjście z PD.6 Reset Dht_put 'ustawić magistralę w stan LOW Waitms 25 'odczekać co najmniej 18ms Set Dht_put 'zwolnić magistrale Waitus 40 Reset Dht_io_set 'robimy wejście z PD.6 Waitus 40 'odczekaj 40 ms If Dht_get = 1 Then 'jeśli magistrala jest w stanie wysokim H = 1 'oznacza że czujnik nie odpowiedział Exit Sub 'kończymy podprogram End If 'jeśli czujnik odpowiedział i wyciągnął magistrale do zera kontynuujemy pracę Waitus 80 'czekamy jeszcze 80ms If Dht_get = 0 Then 'jeśli na linii jest nadal 0 H = 2 'oznacza czujnik zwariował Exit Sub 'kończymy podprogram End If 'jeśli wszystko pójdzie dobrze i czujnik zareagował prawidłowo, możemy kontynuować While Dht_get = 1 : Wend 'czekamy, aż magistrala ma stan wysoki Do 'zaczynamy przyjmować 40-bitowe dane While Dht_get = 0 : Wend 'czekamy na ustawienie magistrali w stan 0 Waitus 30 'po wystąpieniu stanu 0 czekać 30 ms If Dht_get = 1 Then 'jeśli na szynie 1 Sensor_data = Sensor_data + "1" 'zapisujemy do zmiennej tą wartość While Dht_get = 1 : Wend 'i czekamy aż nadajnik wyśle kolejny bit Else 'w przeciwnym razie, jeśli tam jest 0 Sensor_data = Sensor_data + "0" 'zapisujemy je do zmiennej End If Incr Count 'zwiększamy licznik Loop Until Count = 40 'Powtarzaj tę czynność dopóki licznik osiągnie liczbę 40 Set Dht_io_set Set Dht_put 'zaczynamy rozkładać otrzymane dane Tmp_str8 = Left(sensor_data , 8) 'bierzemy lewe 8 znaków zmiennej H = Binval(tmp_str8) 'jest cała część wilgotności Tmp_str8 = Mid(sensor_data , 17 , 8) 'wyjmujemy 8 znaków ze środka T = Binval(tmp_str8) 'to cała część temperatury Tmp_str8 = Right(sensor_data , 8) 'prawe 8 znaków Crc = Binval(tmp_str8) 'dane testowe 'test sumy kontrolnej Mybyte = T + H 'dodajemy wartości temperatury i wilgotności If Mybyte <> Crc Then 'jeśli suma kontrolna się nie zgadza H = 3 'Oznacza to, że dane nie są poprawne End If End Sub 'powrót do głównej pętli programu |
Kod zaczerpnięty z internetu, nie jestem jego autorem.
DRD to rejestr odpowiedzialny za konfigurację pinów IO, zapisy równoznaczne z np Config Pind.1 = Output/Input.
W tym programie pin do komunikacji z DHT raz jest wejściem a razy wyjściem, i stąd to.
DHT21 (DHT22, AM2301, AM2302, AM2303)
ten ma rozdzielczość jedną dziesiątą i trochę inaczej dane są upychane w tych 16 bitach, co można łatwo obejrzeć w nocie.
Dla przykładu temperatura 23,4 stopnie Celsjusza kodowana jest jako liczba 234, i trzeba sobie wstawić w odpowiednie miejsce przecinek. Dokładnie to widać w programie zamieszczonym poniżej. Dlatego dla zapisu tak dużej liczby potrzeba nierozłącznych dwóch bajtów (zakres temperatury do 80,0°C, wilgotności do 99,9%RH).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 |
'######################## DHT-22 Humidity & Temperature Sensor#################### ' DHT-22 ' ' |-----o-- Vcc ' | | ' | - ' | 10k ' | - ' | | ' |-----o-- Data ~~~ PC0 (?C) ' | ' |-------- NC ' | ' |-------- GND ' ' Timings: Bit = 0 ~ 28 ?s / Bit = 1 ~ 70 ?s ' Min_time = 28 ?s < TCNT0 Value (ticks) < 70 ?s (in ticks) ' every time > Min_time >>> Bit = 1 ' every time < Min_time >>> Bit = 0 ' ' Min_time ~ 20 (ticks) for 3.6864 MHz ' ' ~ ticks for Bit = 0: 26 ?s * $crystal (in MHz) / Prescale = 460800*0,000026=11,96 dla mojej częstotliwoąsi ==26 ' ~ ticks for Bit = 1: 70 ?s * $crystal (in MHz) / Prescale = 460800*0,000070=32,25 dla mojej częstotliwości ==70 ' zmieniłem Min_time z 20 na 48 ' '######################## DHT-22 Humidity & Temperature Sensor#################### $regfile = "m8def.dat" '$crystal = 3686400 $crystal = 8000000 'taktowanie '$baud = 115200 'Liczba określająca szybkość pracy łącza RS 232 w bitach na sekundę (bod) 'stała określająca rozmiar wybranego stosu '$hwstack = 64 '$swstack = 64 '$framesize = 64 '##################################### Config Pind.1 = Input Config Timer0 = Timer , Prescale = 8 Config Lcd = 16 * 2 'konfiguracja typu wyświetlacza 'LCD Config Lcdpin = Pin , Db4 = Portd.5 , Db5 = Portd.6 , Db6 = Portd.7 , Db7 = Portd.4 , E = Portd.3 , Rs = Portd.2 'konfiguracja linii 'mikrokontrolera, do których 'dołączono wyświetlacz 'LCD '##################################### 'Const Min_time = 20 Const Min_time = 48 'this has to be changed according to your frequency settings in $crystal Dim Count As Byte Dim Signaltime(43) As Byte Dim Humidityw As Word Dim Temperaturew As Word Dim Humsens_chksum As Byte Dim Humiditys As String * 16 Dim Temperatures As String * 16 '##################################### Declare Sub Read_timings Declare Sub Humtemp_values Declare Function Compare_chksum(byval Hsens_humidity As Word , Byval Hsens_temperature As Word , Byval Hsens_chksum As Byte) As Byte '##################################### Main loop Do Call Humtemp_values ' If Compare_chksum(humidityw , Temperaturew , Humsens_chksum) = 1 Then Humiditys = Str(humidityw) Temperatures = Str(temperaturew) Cls Lcd "wilg: " ; Format(humiditys , "0.0") ; " %" Lowerline Lcd "Temp: " ; Format(temperatures , "0.0") ; " deg" Waitms 100 'Else ' Lcd "Read Error" 'End If Loop End '############################################################# Read timings ' measure time (in ticks) for signal = high (Start / Stop TIMER0) ' 1st two measurements do not contain sensor values Sub Read_timings Wait 3 Count = 1 Config Pind.1 = Output : Portc.0 = 0 ' request data Waitms 20 ' wait 20 ms Config Pind.1 = Input ' wait for data, receive data While Count < 43 'collect 42 timings / signals Bitwait Pind.1 , Set 'signal goes high > start timer Start Timer0 Bitwait Pind.1 , Reset 'signal goes low > stop timer Stop Timer0 Signaltime(count) = Tcnt0 'store number of ticks per signal in Signaltime byte Tcnt0 = 0 Incr Count Wend End Sub '############################################################# Humidity and temperature values ' transform timings into bits (40 timings > 40 Bits) ' every time (in ticks) > Min_time >>> Bit = 1 ' every time (in ticks) < Min_time >>> Bit = 0 ' first word contains humidity ' second word contains temperature ' last byte contains checksum Sub Humtemp_values Local X As Byte Humidityw = 0 Temperaturew = 0 Humsens_chksum = 0 Call Read_timings For Count = 3 To 42 ' skip first two Select Case Count Case 3 To 18 X = 18 - Count If Signaltime(count) > Min_time Then Toggle Humidityw.x Case 19 To 34 X = 34 - Count If Signaltime(count) > Min_time Then Toggle Temperaturew.x Case 35 To 42 X = 42 - Count If Signaltime(count) > Min_time Then Toggle Humsens_chksum.x End Select Next End Sub '############################################################# Calculate Checksum and compare with trasnmitted value Function Compare_chksum(byval Hsens_humidity As Word , Byval Hsens_temperature As Word , Byval Hsens_chksum As Byte) As Byte Local Chksum As Byte Chksum = Low(hsens_humidity ) + High(hsens_humidity ) Chksum = Chksum + Low(hsens_temperature) Chksum = Chksum + High(hsens_temperature) If Chksum = Hsens_chksum Then Compare_chksum = 1 Else Compare_chksum = 0 End If End Function |
Nie jestem autorem tego kodu, a służy on jedynie za pomoc dydaktyczną w moich rozpatrywaniach ;-D
To co jest jeszcze istotne, to sposób „kodowania” temperatury ujemnej w sensorze, który ją umie mierzyć. Przedstawiony program nie zawiera jej obsługi. Znacznik „mrozów” to bit nr 24 (najstarszy bit temperatury), gdy jest ustawiony oznacza, że jako wynik dostaliśmy wartość ujemną. Należało by badać jego wystąpienie i przedsięwziąć odpowiednią akcję.
Warto jeszcze zwróć uwagę jak ciekawie autor tego kodu ominął dzielenie, przy wyświetlaniu wyniku, które jest mordęgą dla 8-śmio bitowego µC. Po prostu formatuje string-a przy wyświetlaniu 😉
1 |
Lcd "wilg: " ; Format(humiditys , "0.0") ; " %" |
jak cyferki są niepotrzebne do obliczeń itp to jest to bardzo sprytne rozwiązanie.
Jak dla mnie pierwszy program mniej elegancki na instrukcji waitus, jest bardziej interesujący do wykorzystania w małym µC, pochłania mniej zasobów µC, i generuje mniejszą binarkę. Natomiast program z funkcjami i procedurami jest bardziej „edukacyjny” ale ma dla mnie jedną niedogodność – bitwait. W przypadku problemów „z łącznością” program może pójść w maliny i warto by tu zastosować watchdog-a, aby temu zapobiec. Co zeżre oczywiście następne bajty pamięci flash. W przypadku chwilowego rozłączenia sensor „wysypie” swoje bity, a µC będzie czekał w nieskończoność na wystąpienie stosownego bitu.
Aby pierwszy przykład kodu dostosować do obsługi drugiej rodziny sensorów trzeba by zając się na nowo zdekodowaniem danych temperatury i wilgotności z ciągu odczytanych bitów. Kolejnie wyłuskać i analizować znacznik mrozów, co nie powinno nikomu przysporzyć żadnych problemów.
Prezentacja układu z pomiarem temperatury za pośrednictwem przedstawionej metody.
Nota katalogowa
AM2301.pdf (4954 pobrania )
Nota katalogowa
AM2302.pdf (4466 pobrań )
Nota katalogowa
AM2303.pdf (4275 pobrań )
Nota katalogowa
DHT11.pdf (5121 pobrań )
Nota katalogowa
DHT11-1.pdf (4454 pobrania )
Nota katalogowa
DHT22.pdf (4420 pobrań )
Może wygodniej i bezpieczniej jest używać Pulsein, Pulseout, niż bitwait czy timer ów? 😆