Feuchtigkeitssensor mit DHT11 und PIC16F1826

#7709916
Lesenswert?

Hallo Leute,

ich versuche, einen Feuchtigkeitssensor (DHT11) für das Badezimmer zu entwickeln. Wenn der Feuchtigkeitssensor einen bestimmten Wert erreicht, sollte eine LED aufleuchten, um dies anzuzeigen.

Ich hatte geplant, dafür einen PIC16F1826 zu verwenden, aber da ich ein PIC-Anfänger bin, habe ich zuerst ein Arduino Uno benutzt. Dort hat das Programm mit einer Bibliothek gut funktioniert. Aber mit dem PIC musste ich den Wert mit einer selbst konstruierten Methode messen, was ich auch getan habe. Zuerst habe ich ein Programmierset mit dem PIC18F45K22 verwendet, was ebenfalls gut funktionierte.

Aber jetzt, mit meiner eigenen Schaltung und der Verwendung des PIC16F1826 (ohne Peripheriegeräte des Programmiersets), habe ich ein Problem, den Sensorwert zu lesen. Das Merkwürdige ist, dass ich denselben Code zum Lesen der Daten verwendet habe wie beim Programmierset.

Zuerst habe ich den internen Oszillator verwendet, aber ich habe keine Daten erhalten. Dann habe ich versucht, einen Oszillator von 10 MHz anzuschließen, weil ich dachte, der interne wäre für diesen Zweck zu ungenau. Aber auch diesmal habe ich keine Daten erhalten.

Ich habe sogar versucht, den Oszillator zu testen, indem ich die LED regelmäßig blinken ließ, und das Blinken sah gut aus, soweit ich das von Auge aus beurteilen konnte.

Also bitte ich euch jetzt um Hilfe.

Ich habe einige Vermutungen, die euch vielleicht weiterhelfen:

  • Vielleicht habe ich die Konfigurationsbits durcheinandergebracht, da es das erste Mal war, dass ich dies getan habe.
  • Vielleicht gibt es auch ein Problem mit der Oszillatorschaltung.

Unten sind der Code und das Schema der Schaltung aufgeführt.

Zum besseren Verständnis des Fehlers: Ich erhalte aus meinem Code die Daten 0 für die Variable "int_RH".

Danke für eure Hilfe.

Code PIC16F1826:

1
#include <xc.h>
2
#include <stdbool.h>
3
#include <stdint.h>
4
#define _XTAL_FREQ 10000000 // external oscillator frequency of 10MHz
5
#define LOOP_DELAY 1        // Loop-Delay in ms (milliseconds)
6
#define WARN_LED LATBbits.LATB0
7
#define SENSOR PORTBbits.RB1
8
uint8_t Read_DHT ( uint8_t *data );
9
void setup ( void )
10
{
11
    OSCCON = 0b01111000; // use external oscillator 
12
    ANSELB = 0x00; // PORTB to digital
13
    TRISBbits.TRISB0 = 0; // LED as OUTPUT
14
    TRISBbits.TRISB1 = 0; // Sensor as output
15
    WARN_LED = 0;   // LED off
16
}
17
void loop ( void )
18
{
19
    uint8_t data[5] = { 0 };
20
    uint8_t result = Read_DHT( data );
21
    if ( result == 0 )
22
    {
23
        uint8_t int_RH = data[0];
24
        uint8_t dec_RH = data[1];
25
        uint8_t int_T = data[2];
26
        uint8_t dec_T = data[3];
27
        
28
        
29
        
30
        if ( int_RH > 1 )
31
        {
32
            WARN_LED = 1;
33
        }
34
        else
35
        {
36
            WARN_LED = 0;
37
        }
38
    }
39
}
40
void main ( void )
41
{
42
    setup( );
43
    while ( 1 )
44
    {
45
        loop( );
46
        __delay_ms( LOOP_DELAY );
47
    }
48
}
49
uint8_t Read_DHT ( uint8_t *data )
50
{
51
    TRISBbits.TRISB1 = 0; // Sensor as output
52
    LATBbits.LATB1 = 0;
53
    __delay_ms( 18 );
54
    LATBbits.LATB1 = 1;
55
    __delay_us( 40 );
56
    TRISBbits.TRISB1 = 1; // Sensor aas input
57
    uint16_t timeout = 10000;
58
    while ( SENSOR == 1 )
59
    {
60
        if ( --timeout == 0 )
61
        {
62
            return 0xFF;
63
        }
64
    }
65
    timeout = 10000;
66
    while ( SENSOR == 0 )
67
    {
68
        if ( --timeout == 0 )
69
        {
70
            return 0xFF;
71
        }
72
    }
73
    timeout = 10000;
74
    while ( SENSOR == 1 )
75
    {
76
        if ( --timeout == 0 )
77
        {
78
            return 0xFF;
79
        }
80
    }
81
    for ( uint8_t i = 0; i < 5; i++ )
82
    {
83
        for ( int8_t j = 7; j >= 0; j-- )
84
        {
85
            timeout = 10000;
86
            while ( SENSOR == 0 )
87
            {
88
                if ( --timeout == 0 )
89
                {
90
                    return 0xFF;
91
                }
92
            }
93
            uint8_t signal_counter = 0;
94
            timeout = 10000;
95
            while ( SENSOR == 1 )
96
            {
97
                signal_counter++;
98
                __delay_us( 1 );
99
                if ( signal_counter > 100 )
100
                {
101
                    return 0xFE;
102
                }
103
            }
104
            if ( signal_counter > 30 )
105
            {
106
                data[i] |= (1 << j);
107
            }
108
        }
109
    }
110
    if ( ( uint8_t ) (data[0] + data[1] + data[2] + data[3]) != data[4] )
111
    {
112
        return 0xFF;
113
    }
114
    return 0;
115
}

Configurations Bits:

1
// PIC16F1826 Configuration Bit Settings
2
// 'C' source line config statements
3
// CONFIG1
4
#pragma config FOSC = HS        // Oscillator Selection (LP Oscillator, Low-power crystal connected between OSC1 and OSC2 pins)
5
#pragma config WDTE = OFF       // Watchdog Timer Enable (WDT disabled)
6
#pragma config PWRTE = OFF      // Power-up Timer Enable (PWRT disabled)
7
#pragma config MCLRE = ON      // MCLR Pin Function Select (MCLR/VPP pin function is digital input)
8
#pragma config CP = OFF         // Flash Program Memory Code Protection (Program memory code protection is disabled)
9
#pragma config CPD = OFF        // Data Memory Code Protection (Data memory code protection is disabled)
10
#pragma config BOREN = ON      // Brown-out Reset Enable (Brown-out Reset disabled)
11
#pragma config CLKOUTEN = OFF   // Clock Out Enable (CLKOUT function is disabled. I/O or oscillator function on the CLKOUT pin)
12
#pragma config IESO = OFF       // Internal/External Switchover (Internal/External Switchover mode is disabled)
13
#pragma config FCMEN = OFF      // Fail-Safe Clock Monitor Enable (Fail-Safe Clock Monitor is disabled)
14
// CONFIG2
15
#pragma config WRT = OFF        // Flash Memory Self-Write Protection (Write protection off)
16
#pragma config PLLEN = OFF      // PLL Enable (4x PLL disabled)
17
#pragma config STVREN = ON      // Stack Overflow/Underflow Reset Enable (Stack Overflow or Underflow will cause a Reset)
18
#pragma config BORV = LO        // Brown-out Reset Voltage Selection (Brown-out Reset Voltage (Vbor), low trip point selected.)
19
#pragma config DEBUG = OFF      // In-Circuit Debugger Mode (In-Circuit Debugger disabled, ICSPCLK and ICSPDAT are general purpose I/O pins)
20
#pragma config LVP = OFF        // Low-Voltage Programming Enable (High-voltage on MCLR/VPP must be used for programming)
21
// #pragma config statements should precede project file includes.
22
// Use project enums instead of #define for ON and OFF.
23
#include <xc.h>
Angehängte Dateien:
Moderator (Firma: Titel) Persönliche Seite #7709957
Lesenswert?

Dominic schrieb:

Also bitte ich euch jetzt um Hilfe.

Hast du ein Oszilloskop zur Hand, mit dem du einfach mal messen kannst, ob auf dem Interface das Protokoll sowohl pegelmäßig als auch vom Timing her so läuft, wie der Sensor es laut Datenblatt braucht?

Das Debuggen serieller Schnittstellen ohne Oszilloskop (oder wenigstens so einem 10€-Saleae-Logikanalyzer-Nachbau) ist wie "Autofahren nach Gehör": wenn es scheppert hats irgendein Problem gegeben.

BTW: diesen Schaltplan hättest du besser als PNG-Screenshot hier angehängt.

#7709962
Lesenswert?

Hi Lothar

Habe leider selber keines, aber kann vielleicht eins von einem Freund ausleihen.

Sollte ich dann lieber den Ablauf der Sensorabfrage überprüfen oder reicht es auch, z.B. ein 1 Hz Signal zu überprüfen.

Zudem wie viel Toleranz wäre für die Signale maximal erlaubt?

Ach ja und Danke für den Tipp mit dem PNG!

Moderator (Firma: Titel) Persönliche Seite #7709968
Lesenswert?

Dominic schrieb:

Sollte ich dann lieber den Ablauf der Sensorabfrage überprüfen

Der Sensor erwartet ein Signal, das so aussieht, wie es im Datenblatt des Sensors steht. Das ist es also, was du überprüfen musst.

Zudem wie viel Toleranz wäre für die Signale maximal erlaubt?

Genau das sollte im Datenblatt des Sensors stehen. Dort gibt es eine Sammlung von Datenblättern zu diesem Sensor:

Allerdings sind die chinesischen Datenblätter oft völliger Schrott. Und so offenbar auch bei diesem Bauteil, wo das Timing nicht vernünftig mit min/typ/max Werten und Schaltpegeln spezifiziert ist. Und das, was irgendwie "spezifiziert" ist, steht verstreut in irgendwelchen Bildchen. Grauenhaft.

Letztlich hilft dann nur, möglichst nah am "Soll"-Bildchen zu bleiben.

Beitrag #7709973 wurde von einem Moderator gelöscht.
#7710411
Lesenswert?

Also ich habe den Ablauf mal mit dem Oszilloskop gemessen. Am Anfang sendet der PIC für 18 us ein 0V/Low-Signal, was er auch machen soll.

Darauf folgt ein Signal, welches zuerst auf 5V/High geht. Nach kurzer Zeit jedoch fällt dieses auf ca. 1V herab (siehe Bild)

Es sieht mir sehr danach aus, dass dies der eigentliche Fehler ist.

Nichtsdestotrotz folgen darauf allerdings noch High und Low Signale des Sensors, wobei diese nicht ganz ankommen.

Ich konnte leider noch nicht mehr Messungen aufstellen, werde aber versuchen noch mehr Messungen durchzuführen.

Angehängte Dateien:
(Firma: www.pic-microcontroller.de) #7710443
Lesenswert?

Hier mal ein Code Schnipsel für einen anderen Controller. Vielleicht hilft es dir weiter. Hier werden Temperatur und Feuchte ausgelesen.

1
unsigned short  Check, Temp, RH, Sum, Ch ;
2
void StartSignal(){
3
 TRISIO.F2 = 0;    //Configure TRISIO.F2 as output
4
 GPIO.F2 = 0;    //GP2 sends 0 to the sensor
5
 delay_ms(18);
6
 GPIO.F2 = 1;    //GP2 sends 1 to the sensor
7
 delay_us(30);
8
 TRISIO.F2 = 1;  //Configure TRISIO.F2 as input
9
  }
10
 //////////////////////////////
11
 void CheckResponse(){
12
 Check = 0;
13
 delay_us(40);
14
 if (GPIO.F2 == 0){
15
 delay_us(80);
16
 if (GPIO.F2 == 1)   Check = 1;   delay_us(40);}
17
 }
18
 //////////////////////////////
19
 char ReadData(){
20
 char i, j;
21
 for(j = 0; j < 8; j++){
22
 while(!GPIO.F2); //Wait until GP2 goes HIGH
23
 delay_us(30);
24
 if(GPIO.F2 == 0)
25
       i&= ~(1 << (7 - j));  //Clear bit (7-b)
26
 else {i|= (1 << (7 - j));  //Set bit (7-b)
27
 while(GPIO.F2);}  //Wait until GP2 goes LOW
28
 }
29
 return i;
30
 }
31
 //////////////////////////////
32
 void main() {
33
  Serial_Lcd_Init();                 // LCD display initialization
34
  Serial_Lcd_Cmd(_LCD_CURSOR_OFF);        // cursor off
35
  Serial_Lcd_Cmd(_LCD_CLEAR);             // clear LCD
36
  while(1){
37
  StartSignal();
38
  CheckResponse();
39
  if(Check == 1){
40
  RH = ReadData();
41
  ReadData();
42
  Temp = ReadData();
43
  ReadData();
44
  Sum = ReadData();
45
  if(Sum == (RH + Temp)){
46
  Serial_Lcd_Out(1, 6, "T:  C");
47
  Serial_Lcd_Out(2, 6, "H:  %");
48
  Serial_LCD_Chr(1, 8, 48 + ((Temp / 10) % 10));
49
  Serial_LCD_Chr(1, 9, 48 + (Temp % 10));
50
  Serial_LCD_Chr(2, 8, 48 + ((RH / 10) % 10));
51
  Serial_LCD_Chr(2, 9, 48 + (RH % 10));
52
  }
53
  else{
54
  Serial_Lcd_Cmd(_LCD_CLEAR);
55
  Serial_Lcd_Out(1, 6, "Error");}
56
    }
57
  else {
58
  Serial_Lcd_Cmd(_LCD_CLEAR);
59
  Serial_Lcd_Out(1, 6, "Error");
60
  }
61
  delay_ms(1000);
62
  }
63
 }

Mod: die [c] Tags das nächste Mal bitte selber um den Quelltext machen

#7710468
Lesenswert?

Dominic schrieb:

Darauf folgt ein Signal, welches zuerst auf 5V/High geht. Nach kurzer Zeit jedoch fällt dieses auf ca. 1V herab (siehe Bild)

Lt. DB des Sensors sollen da 5,1k als PullUp gegen Ub gehen, Du hast aber 1k. Vielleicht ist der Strom einfach etwas zu hoch für den OC-Ausgang, so daß er da ein sattes Volt hat ... Sollte aber trotzdem noch kein Problem sein, da der PIC das immer noch als L zählen dürfte. Ansonste ist es immer Mist, nur einen kleinen Ausschnitt des Oszillograms zu zeigen. Zeig doch mal die ganze Sequence beginnend mit dem Startsignal (zumindest noch einige relevante Pulse nach dem Start, solange das sinnvoll auf den Oszischirm paßt)

#7710648
Lesenswert?

Ich möchte etwas grundsätzliches zu den DHTxx-Sensoren sagen: das sind nur Schätzeisen, wenn die länger höhere Rh sehen, dann bleiben die auf 100%. Man kann die zwar mit dem Fön regenerieren, das hält aber nicht lange.

Ich habe daher alle DHTxx rausgeworfen und durch SHT31 bzw. jetzt SHT41 ersetzt, die wollen allerdings I2C aber das ist ja auch kein Problem.

#7711191
Lesenswert?

dumme Zwischenfrage, warum DHT11 wo doch der DHT22 genauer ist und man am den genauso lernen kann.

Hab ihn nur genommen, weil ich ihn schon hatte, aber Danke für die Info

Zurück aber zum wesentlichen: Ich habe erneut Messungen durchgeführt in "Bild 1" ist der gesamte Ablauf des "Korrekten Signals" zu sehen.

In "Bild 2" sind zeitlichen Abläufe, welche vom PIC an den DHT11 gehen noch einmal klar dargestellt.

Das zweite Signal vom PIC (das High Signal) wird aber etwas falsch ausgegeben, siehe "Bild 3".

In den Bildern 4, 5 und 6 sind nun die drei nächsten Signale zu sehen. Wobei soweit ich es verstanden habe, die ersten zwei als Start-Signal vom DHT11 aus gesendet werden.

Nun ist mir aber noch etwas weiteres aufgefallen, nämlich tritt so eine "korrekte Signalübertragung" gar nicht immer auf. Denn wie im "Bild 7" zu sehen ist, kommen manchmal auch nur Signale, welche nur für 18 ms auf low gehen und dann wieder auf high bleiben. (Für präzisere Zeitanzeige siehe "Bild 8")

Ich habe deshalb einmal den Code noch so umgeschrieben, dass wenn einmal eine "Korrekte Messung stattgefunden hat. Was geprüft wird indem ein Wert höher als 1 registriert wurde, dass der Code dann in einer while hängen bleibt und die LED durchgehend leuchtet. Jedoch aber ohne Erfolg die LED zum Leuchten zu bringen.

Hat jemand einen Lösungsansatz?

Angehängte Dateien:
#7711214
Lesenswert?

Dominic schrieb:

Nun ist mir aber noch etwas weiteres aufgefallen, nämlich tritt so eine "korrekte Signalübertragung" gar nicht immer auf. Denn wie im "Bild 7" zu sehen ist, kommen manchmal auch nur Signale, welche nur für 18 ms auf low gehen und dann wieder auf high bleiben. (Für präzisere Zeitanzeige siehe "Bild 8")

Du scheinst offensichtlich immer exakt 18ms als Startsignal zu verwenden. Das ist Blödsinn, denn der DHT will >18ms haben, und nicht exakt 18ms, so daß es auch gar keine Not gibt, die 18ms genau einhalten zu wollen. Damit hängst Du immer an der Start-Detektionsgrenze des DHT, so daß er offensichtlich gerne auch etliche Startsignale übersieht, was Du dann wohl auch im Bild 7 siehst. Zumal der DHT es mit dem Timing der Pulse selber nicht so genau nimmt, sondern offensichtlich leicht "nachgeht". Sieht man ja an deinen Messungen, die immer leicht über den DB-Angaben liegen. Also solltest Du beim Startsignal auch entsprechend mindestens leicht drüber liegen. Nimm einfach >25ms Start, und gut ...

Moderator (Firma: Titel) Persönliche Seite #7711221
Lesenswert?

Helmut -. schrieb:

st 36MB für relativ unsinnige Bilder?

(Kamera)fotos von irgendwas bitte immer(!) als JPEG posten. Auch wenn sie "Screenshots" ersetzen. Und richtige Screenshots von digital erzeugten Bildern (Coumputer- und Bedienoberflächen) dann besser als PNG. Man sollte das Werkzeug namens "Bildformat" schon kennen und wissen welches Bildformat das richtige Tool für welchen Zweck ist.

BTW: mein Rigol 4-Kanal-Oszi hat einen USB-Slot für genau solche Screenshots. Tolle Sache.

#7711822
Lesenswert?

Ich habe jetzt einmal den ersten Delay auf 25 ms geändert, jedoch ohne Erfolg.

Deshalb habe ich den Code einmal etwas umgeändert, so dass ich während des Debuggen sehen kann bei welcher Stelle er die Funktion "Read_DHT" verlässt (siehe Code unten)

Im ersten Durchlauf beim Debuggen bekomme ich den Rückgabewert 0xFA.

Ab dann bekomme ich nach jedem Durchlauf den Rückgabewert 0x00, was eigentlich gut ist und bedeutet, dass das Messen des Sensors erfolgreich war. Jedoch ist in jedem der fünf Variablen des Arrays data[0]...data[4] 0x00 hinterlegt.

Ich habe darauf noch das zweite Delay von ursprünglich 40 us auf 30 us geändert, da im DB steht dieses Delay soll zwischen 20 us - 40 us liegen. Aber auch dies lieferte mir die gleichen Ergebnisse.

Hat jemand eine Idee woran das liegen könnte.

1
#include <xc.h>
2
#include <stdbool.h>
3
#include <stdint.h>
4

5
#define _XTAL_FREQ 10000000 // external oscillator frequency of 10MHz
6

7
#define LOOP_DELAY 1        // Loop-Delay in ms (milliseconds)
8

9

10
#define WARN_LED LATBbits.LATB0
11
#define SENSOR PORTBbits.RB1
12

13

14

15
uint8_t Read_DHT ( uint8_t *data );
16

17
void setup ( void )
18
{
19
    OSCCON = 0b01111000; // use external oscillator 
20

21
    ANSELB = 0x00; // PORTB to digital
22

23
    TRISBbits.TRISB0 = 0; // LED as OUTPUT
24

25
    TRISBbits.TRISB1 = 0; // Sensor as output
26

27
    WARN_LED = 0;   // LED off
28

29
}
30

31
void loop ( void )
32
{
33

34
    uint8_t data[5] = { 0 };
35

36
    uint8_t result = Read_DHT( data ); // Breakpoint is here
37
    if ( result != 0 )
38
    {
39
        uint8_t int_RH = data[0];
40
        uint8_t dec_RH = data[1];
41
        uint8_t int_T = data[2];
42
        uint8_t dec_T = data[3];
43

44

45
        if ( int_RH > 1 )
46
        {
47
            WARN_LED = 1;
48
        }
49
        else
50
        {
51
            WARN_LED = 0;
52
        }
53
    }
54
}
55

56
void main ( void )
57
{
58
    setup( );
59

60
    while ( 1 )
61
    {
62
        loop( );
63
        __delay_ms( LOOP_DELAY );
64

65
    }
66
}
67

68
uint8_t Read_DHT ( uint8_t *data )
69
{
70

71

72
    TRISBbits.TRISB1 = 0; // Sensor as output
73
    LATBbits.LATB1 = 0;
74
    __delay_ms( 25 );
75

76
    LATBbits.LATB1 = 1;
77
    __delay_us( 30 );
78

79
    TRISBbits.TRISB1 = 1; // Sensor a   n s input
80

81
    uint16_t timeout = 10000;
82
    while ( SENSOR == 1 )
83
    {
84
        if ( --timeout == 0 )
85
        {
86
            return 0xFA;
87
        }
88
    }
89

90
    timeout = 10000;
91
    while ( SENSOR == 0 )
92
    {
93
        if ( --timeout == 0 )
94
        {
95
            return 0xFB;
96
        }
97
    }
98

99
    timeout = 10000;
100
    while ( SENSOR == 1 )
101
    {
102
        if ( --timeout == 0 )
103
        {
104
            return 0xFC;
105
        }
106
    }
107

108
    for ( uint8_t i = 0; i < 5; i++ )
109
    {
110
        for ( int8_t j = 7; j >= 0; j-- )
111
        {
112
            timeout = 10000;
113
            while ( SENSOR == 0 )
114
            {
115
                if ( --timeout == 0 )
116
                {
117
                    return 0xFD;
118
                }
119
            }
120
            uint8_t signal_counter = 0;
121
            timeout = 10000;
122
            while ( SENSOR == 1 )
123
            {
124
                signal_counter++;
125
                __delay_us( 1 );
126
                if ( signal_counter > 100 )
127
                {
128
                    return 0xFE;
129
                }
130
            }
131

132
            if ( signal_counter > 30 )
133
            {
134
                data[i] |= (1 << j);
135
            }
136
        }
137
    }
138

139
    if ( ( uint8_t ) (data[0] + data[1] + data[2] + data[3]) != data[4] )
140
    {
141
        return 0xFF;
142
    }
143

144
    return 0;
145
}
#7711866
Lesenswert?

Hallo, dein spezieller Code, viiile Leerzeilen, x-mal 10000 "timeout", dazu die "Messungen" mit 36 Mbyte .png Bildern (Fotos von einem Scope welches echte Screenshots kann, --- nur noch abpausen auf Butterbrotpapier wäre besser ---), alles recht sehr viel eigen... Ohh ohhhhh ohhhhhhhhhhh!

Kaum mehr auszuhalten...

Daher habe ich jetzt zu später Stunde im WWW ein besseres Beispiel gesucht und hoffentlich auch gefunden. Bitte das auf deinen uC anpassen und probieren.

https://brainly.com/question/40429058

Und nur noch regelkonforme Meldungen hier posten.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren