Forum: Mikrocontroller und Digitale Elektronik Pflanzenbewässerung


von Benedikt K. (benek)


Lesenswert?

Hallo Foristen,
ich hätte einige Fragen zu meinem aktuellen Projekt, einer automatischen 
Pflanzenbewässerung:

1. An meinem Attiny13 sind 2 Schalter, ein Feuchtigkeitssensor (2 Nägel 
und 1MOhm Widerstand als Spannungsteiler) und eine Pumpe über ein Mosfet 
angeschlossen. Wenn der erste Taster (an PB2) gedrückt wird, wird der 
aktuelle Messwert des Feuchtigkeitssensors in die Variable "Trocken" 
geschrieben. Wenn der zweite Taster (an PB1) gedrückt wird, soll das 
selbe passieren, bloß eben in die Variable "Feucht". So habe ich die 
Maximale Trockenheit und die Maximale Feuchtigkeit.
Das Programm soll jetzt so funktionieren, dass wenn die Maximale 
Trockenheit erreicht ist, die Pumpe solange läuft bis die Maximale 
Feuchtigkeit erreicht ist. Und soweiter und so fort. Meine Frage lautet, 
ob dass der Pflanze so gut tut.

2. Ist mein Programm soweit richtig?
1
/*
2
 * Bewässerungssystem.c
3
 *
4
 * Created: 12.04.2013 17:33:44
5
 *  Author: Benedikt_2
6
 */ 
7
8
#define F_CPU 1200000 //Clockspeed 1,2 MHz
9
#include <avr/io.h>
10
#include <util/delay.h>
11
12
int Trocken = 100; //speichert die maximale Trockenheit
13
int Feucht = 0; //speichert die maximale Feuchtigkeit
14
int ADCResultVoid = 0; //speichert den Messwert der Funktion adcGet
15
int ADCResult = 0; //speichert den Messwert in der Loop Funktion
16
17
int adcGet(void){ //Funktion zur Erfassung des Messwertes
18
  ADCSRA |= (1<<ADSC); //starte Konvertierung
19
  while (ADCSRA & 0b01000000) //warte bis Konvertierung abgeschlossen
20
  {
21
    ;
22
  }
23
  ADCResultVoid = ADCH; //setzen der Variable
24
  return ADCResult; //Messwert ausgeben
25
}
26
27
int main(void)
28
{
29
  DDRB |= (1<<0); //PB0 = Output
30
  PORTB |= (1<<1) | (1<<2); //Pullups an PB1 und PB2
31
  
32
  ADMUX |= (1<<ADLAR); //8 Bit
33
  ADMUX |= (1<<MUX0) | (1<<MUX1); //ADC3
34
  ADCSRA |= (1<<ADPS1) | (1<<ADPS0); // Prescaler = 8; ADCCLock = 150kHz
35
  ADCSRA |= (1<<ADEN); //ADC Enable
36
  ADCSRA |= (1<<ADSC); //Erste Konvertierung, die dann verworfen wird
37
  
38
    while(1)
39
    {  
40
        if (!(PINB & 0b00000100)) //wurde Taster 2 an PB2 gedrückt?
41
        {
42
      Trocken = adcGet(); //Wenn ja setze Variable Trocken
43
        }
44
    
45
    if(!(PINB & 0b00000010)) //Wurde Taster 1 an PB1 gedrückt?
46
    {
47
      Feucht = adcGet(); //Wenn ja setze Variable Feucht
48
    }
49
    
50
    ADCResult  = adcGet(); //Messwert wird erfasst
51
    
52
    if (ADCResult<=Trocken && ADCResult>=Feucht) //Ist Messwert unter Maximaler Trockenheit und über Maximaler Feuchtigkeit?
53
    {
54
      PORTB |= (1<<0); //Wenn ja Pumpe für 1 ms
55
      _delay_ms(1);
56
      PORTB &= ~(1<<1);
57
    }
58
    else
59
    {
60
      PORTB &= ~(1<<0);
61
    }
62
63
    }
64
}

Vielen Dank im Vorraus,
Benedikt

von oooOooo (Gast)


Lesenswert?

Ein Eimer mit Wasser und Baumwollfäden sind besser.

'vorraus' = Augenkrebs

von Benedikt K. (benek)


Lesenswert?

oooOooo schrieb:
> Ein Eimer mit Wasser und Baumwollfäden sind besser.
>
> 'vorraus' = Augenkrebs

Schön, dass du weder eine meiner Fragen beantwortet hast und meinen 
einzigen Rechtschreibfehler im Text gefunden hast.

Glückwunsch

von Dealer (Gast)


Lesenswert?

Bei so einem Aufwand kann es sich nur um Hanfpflanzen handeln.

von Markus M. (mark_m)


Lesenswert?

Ich würde in Intervallen Messen. Die Pumpe laufen lassen bzw. 
Wassermenge zuführen. Warten und wieder messen. Wenn die Erde die 
Wassermenge nicht sofort aufnimmt könnte es sonst in einem Wasserschaden 
enden.

Grüsse

von Wasserschaden (Gast)


Lesenswert?

Benedikt K. schrieb:
> Schön, dass du weder eine meiner Fragen beantwortet hast und meinen
> einzigen Rechtschreibfehler im Text gefunden hast.

Ich habe noch sieben weitere Fehler gefunden. ;-)

Benedikt K. schrieb:
> Meine Frage lautet, ob dass der Pflanze so gut tut.

Die Art und Menge der Bewässerung hängt von vielerlei Faktoren ab und 
gestaltet sich pflanzenspezifisch. Ich denke, in einem entsprechenden 
Fachforum bekommst du kompetenten Rat.

von oooOooo (Gast)


Lesenswert?

Benedikt K. schrieb:
> Meine Frage lautet,
> ob dass der Pflanze so gut tut.

NEIN! Warum? Ganz einfach.

Du bist fast am verdursten (trocken) und säufst plötzlich wie ein Loch 
Wasser in dich hinein (nass), dann hast du sicher Magenschmerzen. Das 
ist sicher angenehm, oder nicht?

Jetzt zufrieden?

von Jörg E. (jackfritt)


Lesenswert?

Adcresult is immer 0??? Und bei den anderen Problemen habe ich dann 
erstmal aufgehört.

von Benedikt K. (benek)


Lesenswert?

Jörg Esser schrieb:
> Adcresult is immer 0??? Und bei den anderen Problemen habe ich dann
> erstmal aufgehört.

Wo siehst du das denn? Der adcResult wird nur am Anfang auf Null 
gesetzt...

von Benedikt K. (benek)


Lesenswert?

oooOooo schrieb:
> Benedikt K. schrieb:
>> Meine Frage lautet,
>> ob dass der Pflanze so gut tut.
>
> NEIN! Warum? Ganz einfach.
>
> Du bist fast am verdursten (trocken) und säufst plötzlich wie ein Loch
> Wasser in dich hinein (nass), dann hast du sicher Magenschmerzen. Das
> ist sicher angenehm, oder nicht?
>
> Jetzt zufrieden?

Naja die Pflanze wird ja erst bewässert wenn ein bestimmter 
Trockenheitsgrad erreicht ist.

von Alexander R. (Gast)


Lesenswert?

Evtl. wäre es sinnvoller, einen Wert zu nehmen, bei dem das Substrat 
noch eine geringe Restfeuchte hat. Wenn dieser Wert unterschritten wird, 
kannst du dann deine Pumpe für eine bestimmte Zeit einschalten. Die 
Messung kannst du dann vier mal am Tag durchführen. So schnell wird Erde 
ja nicht trocken, wenn du überhaupt Erde benutzt.

Einen Überschwemmungsschutz solltest du dir aber noch überlegen. Bei mir 
habe ich einen normalen Überlauf. Ist allerdings auch ein Ebbe und Flut 
System, also ganz anderes Prinzip.

von Stefan (Gast)


Lesenswert?

also ich habe mir soetwas auch gebaut, mein gerät funktioniert wie 
folgt:


der benutzer steckt die sensoren in den blumentopf

dann wird die pflanze gegossen


nun wird der stecker in die steckdose gesteckt.

Nun misst der Atmega den Widerstand und merkt sich den.


einmal pro Stunde überprüft er ob der widerstand größer geworden ist, 
sollte das so sein wird die pumpe für 5 sekunden eingeschaltet, 5min 
gewartet und wieder gemessen ist der widerstand kleiner als vorgegeben 
wird wieder 1 stunde in den schlafmodus gegangen.


So funktioniert das ganze bei mir schon seit rund 1 jahr und die pflanze 
ist weder vertrocknet noch ertrunken ;)

von Benek (Gast)


Lesenswert?

Alexander R. schrieb:
> Evtl. wäre es sinnvoller, einen Wert zu nehmen, bei dem das Substrat
> noch eine geringe Restfeuchte hat. Wenn dieser Wert unterschritten wird,
> kannst du dann deine Pumpe für eine bestimmte Zeit einschalten. Die
> Messung kannst du dann vier mal am Tag durchführen. So schnell wird Erde
> ja nicht trocken, wenn du überhaupt Erde benutzt.
>
> Einen Überschwemmungsschutz solltest du dir aber noch überlegen. Bei mir
> habe ich einen normalen Überlauf. Ist allerdings auch ein Ebbe und Flut
> System, also ganz anderes Prinzip.

Danke für deine Antwort. Kann ich für die Messungen einen Timer 
Interrupt hernehmen? Was meinst du mit Überschwemmungsschutz bzw. wie 
kann ich das implementieren?

von Benek (Gast)


Lesenswert?

Danke Stefan. Das hört sich nach einer guten Idee an. Wie hat du das
"warten" programmiert? Einfach mit delay ms?

von Jörg E. (jackfritt)


Lesenswert?

while (ADCSRA & 0b01000000) //warte bis Konvertierung abgeschlossen
  {
    ;
  }
  ADCResultVoid = ADCH; //setzen der Variable
  return ADCResult; //Messwert ausgeben

Adcresultvoid und adcresult haben nix miteinader zu tun....

von Benek (Gast)


Lesenswert?

Jörg Esser schrieb:
> while (ADCSRA & 0b01000000) //warte bis Konvertierung abgeschlossen
>   {
>     ;
>   }
>   ADCResultVoid = ADCH; //setzen der Variable
>   return ADCResult; //Messwert ausgeben
>
> Adcresultvoid und adcresult haben nix miteinader zu tun....

Stimmt. Danke.

von Alexander R. (Gast)


Lesenswert?

Wenn der Behälter für das Gießwasser mehr Wasser fasst als deine Töpfe, 
wäre mir das persönlich zu riskant, ohne einen Schutz. Wenn was schief 
läuft und die Pumpe nicht abschaltet läuft das Wasser irgendwann aus den 
Töpfen.

Zu dem Timer Interrupt: Wenn am Controller keine Uhr dran ist, würde ich 
den nehmen. Vielleicht kennt aber jemand noch eine elegantere 
Möglichkeit. Sind ja doch große Zeitabstände.

von kukuk (Gast)


Lesenswert?

Der gesunde Bauer schaut seinen Pflanzen zu,wie sie wachsen !

Die Anlage funktioniert solange du sie ueberwachen kannst....
...bei deiner ersten Abwesenheit schlaegt das Schicksal zu !

Die Naegel funktionieren 4 Wochen lang; dann passiert etwas....!

von Benedikt K. (benek)


Lesenswert?

Alexander R. schrieb:
> Zu dem Timer Interrupt: Wenn am Controller keine Uhr dran ist, würde ich
> den nehmen. Vielleicht kennt aber jemand noch eine elegantere
> Möglichkeit. Sind ja doch große Zeitabstände.

Nein eine Uhr ist nicht dran. Hat jemand eine Idee, solche großen 
Zeitabstände zu messen?

von Karl H. (kbuchegg)


Lesenswert?

Benedikt K. schrieb:
> Alexander R. schrieb:
>> Zu dem Timer Interrupt: Wenn am Controller keine Uhr dran ist, würde ich
>> den nehmen. Vielleicht kennt aber jemand noch eine elegantere
>> Möglichkeit. Sind ja doch große Zeitabstände.
>
> Nein eine Uhr ist nicht dran. Hat jemand eine Idee, solche großen
> Zeitabstände zu messen?

Ja.
Mit einem Timer eine Uhr aufbauen.
Du weisst schon: tritt ein Ereigniss alle 1/100 Sekunde auf, dann ist 
nach 100 Ereignissen 1 Sekunde vergangen. Nach 60 Sekunden 1 Minute, 
nach 60 Minuten 1 Stunde, etc. etc. Sind alles nur Variablen, die bei 
Bedarf um 1 hochgezählt werden und bei bestimmten Zählerständen die 
nächste Variable hochzählen und dann wieder auf 0 gesetzt werden.

FAQ: Timer

von Karl H. (kbuchegg)


Lesenswert?

schreib solche Dinge

>        if (!(PINB & 0b00000100)) //wurde Taster 2 an PB2 gedrückt?

so

  if( !(PINB & (1<<PB2) )

dann sieht man gleich viel besser, dass es hier um PB2 geht.
Und wenn du dem ganzen dann noch per #define ein paar schöne Namen 
verpasst, dann kannst du dir auch den Kommentar sparen

#define MIN_TASTER  PB2


...

  if( !(PINB & (1<< MIN_TASTER ) )



jetzt brauchst du den ganzen Kommentar nicht mehr und siehst trotzdem im 
Programmtext mehr als dein ganzer Kommentar je ausgesagt hat. Denn jetzt 
sieht man im Code selber, dass es hier um den Taster geht, mit dem du 
deinen minimalen Wert feststellst.

So, wirds dann noch mal eine Spur schöner und besser lesbar

#define IS_PRESSED(x)     (!(PINB & (1<<(x))))

...

  if( IS_PRESSED( MIN_TASTER ) )


Herz was willst du mehr? Das kann sogar jemand lesen und verstehen, der 
noch nie programmiert hat.



Hier dasselbe
>  ADCSRA |= (1<<ADSC); //starte Konvertierung
>  while (ADCSRA & 0b01000000) //warte bis Konvertierung abgeschlossen

zum Bit setzen hast du wunderbar den Bitnamen benutzt. Da gibt es nichts 
daran auszusetzen. Aber warum hast du ihn nicht für die Abfrage benutzt?

  ADCSRA |= (1<<ADSC); //starte Konvertierung
  while (ADCSRA & (1<<ADSC)) //warte bis Konvertierung abgeschlossen

jetzt könnte man sogar sehen, dass zwischen diesen beiden Dingen ein 
Zusammenhang besteht. Du setzt das ADSC Bit per Programm, woraufhin der 
ADC zu arbeiten anfängt, und wenn der ADC fertig ist, nimmt er das Bit 
wieder zurück auf 0.

von dolf (Gast)


Lesenswert?

Dealer schrieb:
> Bei so einem Aufwand kann es sich nur um Hanfpflanzen handeln.

nö das zeugs ist n unkraut das wächst auch ohne aufwändige 
bewässerung;-)).

von Karl H. (kbuchegg)


Lesenswert?

Karl Heinz Buchegger schrieb:

> Ja.
> Mit einem Timer eine Uhr aufbauen.
> Du weisst schon: tritt ein Ereigniss alle 1/100 Sekunde auf, dann ist
> nach 100 Ereignissen 1 Sekunde vergangen. Nach 60 Sekunden 1 Minute,
> nach 60 Minuten 1 Stunde, etc. etc. Sind alles nur Variablen, die bei
> Bedarf um 1 hochgezählt werden und bei bestimmten Zählerständen die
> nächste Variable hochzählen und dann wieder auf 0 gesetzt werden.
>
> FAQ: Timer

PS. Um Zeiten abzustoppen, bewährt es sich ganz gut, wenn man dann 
diesen Teil umgekehrt macht, also von einer Zahl runterzählt.

zb. wenn das deine normale Uhr ist
1
volatile uint8_t Seconds, Minutes, Hours;
2
3
ISR( ... )
4
{
5
  count++;
6
7
  if( count == .... )     // je nachdem wie oft dann die ISR wirklich
8
                          // aufgerufen wird -> Timereinstellung
9
  {
10
    count = 0;
11
    Seconds++;
12
13
    if( Seconds == 60 )
14
    {
15
      Seconds = 0;
16
      Minutes++;
17
18
      if( Minutes == 60 )
19
      {
20
        Minutes = 0;
21
        Hours++;
22
23
        if( Hours == 24 )
24
          Hours = 0;
25
      }
26
    }
27
  }
28
}

und du willst mit Sekundenauflösung was stoppen, dann führst du dir eine 
weitere Variable ein und zählst sie in der ISR runter, wenn sie grösser 
als 0 ist
1
volatile uint8_t Seconds, Minutes, Hours;
2
volatile int16_t Waittime = -1; 
3
4
ISR( ... )
5
{
6
  count++;
7
8
  if( count == .... )     // je nachdem wie oft dann die ISR wirklich
9
                          // aufgerufen wird -> Timereinstellung
10
  {
11
    count = 0;
12
    Seconds++;
13
14
    if( Waittime > 0 )
15
      Waittime--;
16
17
    if( Seconds == 60 )
18
    {
19
      Seconds = 0;
20
      Minutes++;
21
22
      if( Minutes == 60 )
23
      {
24
        Minutes = 0;
25
        Hours++;
26
27
        if( Hours == 24 )
28
          Hours = 0;
29
      }
30
    }
31
  }
32
}

Was hast du damit erreicht.
Im Normalfall tut sich nichts mit dieser Variablen. Sie ist -1 und wenn 
das so ist, dann macht auch der Code in der ISR nichts damit.

Aber.
Im Hauptprogramm kannst du jetzt, wenn die Bedingungen richtig sind, 
diese Variable auf einen Wert setzen
1
   if( irgendwas liegt vor )
2
   {
3
     cli();
4
     Waittime = 300;
5
     sei();
6
   }

und in dem Moment beginnt die 'Wartezeit' zu ticken. Durch die ISR wird 
die Variable im Sekundentakt wieder runtergezählt. Nach 1 Sekunde 299, 
nach 2 Sekunden 298, etc. etc. bis die Variable nach 300 Sekunden wieder 
bei 0 angelangt ist. Und das kann man abfragen!
1
   if( irgendwas liegt vor )
2
   {
3
     cli();
4
     Waittime = 300;
5
     sei();
6
   }
7
8
   cli();
9
   NochZuWarten = Wartezeit;
10
   sei();
11
12
   if( NochZuWarten == 0 )
13
   {
14
      // Ha! Die 300 Sekunden sind um. Mach was!
15
16
      ....
17
18
      // und schalte die Wartezeit wieder auf inaktiv, damit bei
19
      // der nächsten Abfrage die 0 nicht nochmal auftauchen und
20
      // den Schaltvorgang erneut auslösen
21
      cli();
22
      Wartezeit = -1;
23
      sei();
24
   }
25
26
   ....

was mit einer derartigen Variablen geht, geht auch mit 2 oder 10 oder 
100.
Und wenn der Teil 'Mach was' kurz genug ist, dann braucht man das auch 
nicht so kompliziert in die Hauptschleife auslagern, wo man jedesmal den 
Interrupt abschalten muss, wenn man mit dieser Variablen arbeitet, 
sondern man legt den Teil einfach gleich in die ISR.

von Benedikt K. (benek)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Ja.
> Mit einem Timer eine Uhr aufbauen.
> Du weisst schon: tritt ein Ereigniss alle 1/100 Sekunde auf, dann ist
> nach 100 Ereignissen 1 Sekunde vergangen. Nach 60 Sekunden 1 Minute,
> nach 60 Minuten 1 Stunde, etc. etc. Sind alles nur Variablen, die bei
> Bedarf um 1 hochgezählt werden und bei bestimmten Zählerständen die
> nächste Variable hochzählen und dann wieder auf 0 gesetzt werden.
>
> FAQ: Timer

Danke für die Erklärung und den Link! Sehr Hilfreich.

MfG Benedikt

von Alex (Gast)


Lesenswert?

Das mit den Nägeln als Feuchtigkeitssensor wird so nicht gehen. Es baut 
sich bald ein elektrochemisches Potential auf, deine Messwerte sind bald 
unbrauchbar und ein Nagel rostet.
Du müsstest mit dem µC eine Rechteckspannung (> 10Hz) ausgeben und den 
Nagel über einen entsprechend großen Kondensator anschließen, damit er 
Wechselspannung abbekommt. Den Messeingang schließt du vor dem 
Kondensator an. Natürlich ist dadurch die Auswertung etwas 
komplizierter, aber machbar.

von Stefan (Gast)


Lesenswert?

DIe Wartenfunktion habe ich einfach mit ein Timer gemacht.


Zum Schutz vor überschwämmung ist die schaltung auch so das er nur die 
wasserpumpe einschaltet wenn das signal plausibel ist.

z.b wenn der widerstand unendlich ist ( wenn die messonden aus den 
behälter gefallen sind ) oder wenn der widerstand fast 0 ( wenn die 
sonden sich ausversehen berühren ) oder wenn der wert nach den 5 
sekunden pumpen sich garnicht ändert dann wird immer gestoppt bis 
schaltung resetet wird.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.