Douchetimer

Hi!

Voor school moet ik een douchetimer maken met behulp van Arduino. De bedoeling is dat ik dmv een watersensor meet of de douche aan of uit staat. Als de douche aanstaat gaat er een timer lopen en na 10 minuten krijg je een alarm en er gaat een rood lampje aan. Staat de douche uit of zijn de 10 minuten nog niet voorbij, een groen lampje.

Nadat de 10 minuten timer (WaterDetectionTime) gaat lopen krijg je elke minuut een beep te horen (LastTimeIDidBeep).

Niet geheel onbelangrijk om hierbij te vermelden; ik ben een beginner.

Nu heb ik de volgende code, maar ik weet niet waarom er na 10 minuten geen alarm komt. De minutenbeeps blijven doorlopen en het lampje blijft groen. Misschien kunnen jullie mij een verlossend antwoord geven.

code:




//
//    FILE: watersensor.ino
//  AUTHOR: Kimmie
// VERSION: 0.0.8
// PURPOSE: Douchegedrag veranderen
//


const int sensor = A0;
int water;
const int greenLEDPin = D3;
const int redLEDPin = D4;
const int piezoPin = D1;
unsigned long currentTime = 0;
unsigned long lastTimeIDidBeep = 0;
unsigned long WaterDetectionTime  = 0;

void setup() {

  Serial.begin(9600); //Begin serial communication
  pinMode(greenLEDPin, OUTPUT);
  pinMode(redLEDPin, OUTPUT);
  pinMode(piezoPin, OUTPUT);
}

void loop() {

  currentTime = millis();
  water = analogRead(sensor);

  if (water > 700) //douche staat aan
  {
    WaterDetectionTime = millis(); //onthoud de begintijd van waterdetectie

    if (currentTime - lastTimeIDidBeep >= 6000) //na elke minuut een beep (6 seconden voor testen)
    {
      lastTimeIDidBeep = currentTime;
      beep();
    }

    if (millis() - WaterDetectionTime >= 60000 && WaterDetectionTime  !=  0) //zijn er 10 minuten verstreken (1 minuut voor testen) én waterdetectie staat niet op 0 nadat er water is gedetecteerd? Zet rode led aan + alarm

    {
      digitalWrite(greenLEDPin, LOW);
      digitalWrite(redLEDPin, HIGH);
      alarm();
    }

  }

  else //douche staat uit
  {
    digitalWrite(greenLEDPin, HIGH);
    digitalWrite(redLEDPin, LOW);
    digitalWrite(piezoPin, LOW);
  }

}

void alarm() {

  tone(piezoPin, 698);
  delay(600);
  noTone(piezoPin);
  delay(400);

}

void beep() {
  tone(piezoPin, 100);
  delay(100);
  noTone(piezoPin);
  delay(1000);

}

Alvast bedankt!

M-i-c-h-e-l

Golden Member

if (millis() - WaterDetectionTime >= 60000 && WaterDetectionTime != 0)

Mis ik hier een paar haakjes? Dus:

if ((((millis() - WaterDetectionTime) >= 60000) && (WaterDetectionTime != 0))

Op 31 januari 2018 19:49:33 schreef M-i-c-h-e-l:
if (millis() - WaterDetectionTime >= 60000 && WaterDetectionTime != 0)

Mis ik hier een paar haakjes? Dus:

if ((((millis() - WaterDetectionTime) >= 60000) && (WaterDetectionTime != 0))

Dankje voor je reactie, ik heb de code veranderd en hij het alarm gaat nog steeds niet aan. Zie je misschien iets anders?

c code:

 if (millis() - WaterDetectionTime >= 60000 && WaterDetectionTime  !=  0)

Dat gaat dus nooit werken he, aangezien je waterdetectiontime aan het begin van de loop steeds reset met

c code:

WaterDetectionTime = millis();

Je zult de code dus iets aan moeten passen.
Ook nog een tip, binnen je beep() en alarm() functies gebruik je delay(); Dat betekend dat de code opgehouden wordt, en dus ook je loop functie blijft hangen. Als je daar geen rekening mee houdt kun je dus een afwijking krijgen. Nu zal dat in dit geval niet zo uitmaken, maar het is wel iets om over na te denken. Je kunt of zorgen dat de code er geen last van heeft, of een alternatief zoeken. 'Timer interrupt' is het sleutelwoord waarmee je daarover mogelijk meer kan vinden ;)

Spanning staat en stroom gaat!

Op 31 januari 2018 20:26:23 schreef meander:

c code:

 if (millis() - WaterDetectionTime >= 60000 && WaterDetectionTime  !=  0)

Dat gaat dus nooit werken he, aangezien je waterdetectiontime aan het begin van de loop steeds reset met

c code:

WaterDetectionTime = millis();

Je zult de code dus iets aan moeten passen.
Ook nog een tip, binnen je beep() en alarm() functies gebruik je delay(); Dat betekend dat de code opgehouden wordt, en dus ook je loop functie blijft hangen. Als je daar geen rekening mee houdt kun je dus een afwijking krijgen. Nu zal dat in dit geval niet zo uitmaken, maar het is wel iets om over na te denken. Je kunt of zorgen dat de code er geen last van heeft, of een alternatief zoeken. 'Timer interrupt' is het sleutelwoord waarmee je daarover mogelijk meer kan vinden ;)

Dankje! Het klinkt heel logisch, dus ik dacht hij moet pas gereset worden nadat er 10 minuten voorbij zijn. Dus deed ik het volgende:

code:



    if (((millis() - WaterDetectionTime) >= 60000) && (WaterDetectionTime != 0)) //zijn er 10 minuten verstreken (1 minuut voor testen) én waterdetectie staat niet op 0 nadat er water is gedetecteerd? Zet rode led aan + alarm

    {
      WaterDetectionTime = millis();
      digitalWrite(greenLEDPin, LOW);
      digitalWrite(redLEDPin, HIGH);
      alarm();
    }

Dit werkte niet. Toen dacht ik, hij moet gereset worden als er juist geen water meer gedetecteerd wordt. Toen deed ik dit:

code:



  else //douche staat uit
  {
    WaterDetectionTime = millis();
    digitalWrite(greenLEDPin, HIGH);
    digitalWrite(redLEDPin, LOW);
    digitalWrite(piezoPin, LOW);
  }

En dat doet hij ook niet. Ik zie het niet..

In dit soort gevallen moet je echt terug naar de basis. Geen enkele 'if' of zelfs 1+1=2 moet je vertrouwen..

In jouw geval: je zet elke keer een waterdetectiontime en dat moet je maar 1 keer doen:

If ( WaterdetectionTime == 0 ) {

WaterdetectionTime = currentTime;

}

En onder 'douche staat uit' zet je:

WaterdetectionTime == 0;

Zoek anders eens op de programmeermethode 'state machine'. Dat maakt het namelijk een stuk overzichtelijker.

Wat je daarbij doet is je ontwerp opdelen in een aantal statussen. In jouw geval kan dit dus bijv zijn: rust (geen water gedetecteerd), douchen (wel water, maar < 10 minuten) en alarm.

Per status bedenk je wat er moet gebeuren, en vooral wat er moet gebeuren om naar een andere status te gaan. een voorbeeldje:

c code:




int status = 0; //0=status A, 1=status B, 2=status C, 3=status D

void loop() {
   switch (status) {
      case 0:
         statusA();
         break;
      case 1:
         statusB();
         break;
      case 2:
         statusC();
         break;
      case 3:
         statusD();
         break;
   }
}

void statusA() {

   //doe wat status A moet doen


   //controleer of we naar een andere status moeten
   if(conditie){
     status = 1;   //ga naar B
   }else if(andere conditie){
     status = 2;   //ga naar C
   }

   //indien beide condities niet waar zijn, blijf dus gewoon in status A.
   //merk op dat we in dit voorbeeld dus niet van status A direct naar D kunnen

}

// de andere statusfuncties zijn natuurlijk soortgelijk opgebouwd

Spanning staat en stroom gaat!

Bedankt voor je hulp, maar hier snap ik al helemaal niks van;). Of ik ben er nu al zo lang mee bezig dat ik me niet meer kan focussen en het gewoon niet meer zie.

Door het antwoord van @K7Jz werkt mijn timer nu wel, alleen als er geen water meer gedetecteerd is blijft de timer doorlopen, hij stopt niet. Dus als ik de sensor vervolgens weer in het water doe gaat het alarm meteen af. Als ik alleen dit nog op kan lossen ben ik heel erg blij.

Hensz

Golden Member

State machine is misschien wel een beetje overkill hier.

Je doet alles in één loop, dat maakt het onoverzichtelijk en onhandig. Maak er 2.

Eerste loop is waarin je wacht tot de douche aan gaat. Gebeurt dat, dan kun je WaterDetectionTime en lastTimeIDidBeep instellen. Beter dan in je huidige code want volgens mij geeft ie daar ook meteen een beep direct als je de douche aanzet.

Loopt het water, dan ga je in de tweede loop, daarin wacht je tot de douche weer uit gaat. In die tijd beep je elke minuut en verander je alleen lastTimeIDidBeep. WaterDetectionTime check je alleen, de waarde ervan verandert niet! LEDs worden binnen deze loop aan- en uitgezet als je over de 10 min heen gaat.

Stopt het water, dan verlaat je loop 2 en ga je terug naar loop 1. Je zou daar een overkoepelende lus voor kunnen/moeten gebruiken.

Je zou ook de LEDjes nog uit kunnen zetten, na een minuut bijv. Dat zou een derde lus binnen de opper-lus opleveren en begint het iets meer op een eenvoudige state-machine te lijken. ;-)

Als je afwijkende timers gebruikt is het fijn essentieel als je dat expliciet in de code vermeld. Ik zou denken dan de functie millis() een aantal milliseconden oplevert. Bij jou gaan er daarvan kennelijk 6000 in een minuut, dat maakt ze 10x zo lang als normaal. Voor iemand die je code voor het eerst ziet is dat ff schrikken.

Don't Panic!

Dank voor je reactie! Met een overkoepelende lus bedoel je for loop? Hoe verwerk ik die in mijn code?

Hensz

Golden Member

In grote trekken wordt het zoiets:
Details mag je zelf toevoegen en mijn fouten eruit halen. ;-)

c code:

void loop()
{

  currentTime = millis();
  water = analogRead(sensor);

  repeat
     water = analogRead(sensor);
  until (water > 700) //douche staat aan
  
  WaterDetectionTime = millis(); //onthoud de begintijd van waterdetectie
  Groene led aan, rode uit
  While (water > 700) //douche staat aan
     {
      if (currentTime - lastTimeIDidBeep >= 6000)
       {
         lastTimeIDidBeep = currentTime;
          beep();
        }

      if (millis() - WaterDetectionTime >= 60000 && WaterDetectionTime  !=  0) 
       {
        Groene led uit, rode aan
        alarm();
       }
     water = analogRead(sensor);
    }

  While (water < 700) //douche staat uit 
  {
    Wacht tot de minuut om is
    water = analogRead(sensor);
  }
   Zet alles uit
}

Don't Panic!

Ik zeg: Programma is wel ongeveer goed. (en ik heb alleen dat ding in de openingspost zitten lezen.)

Probleem is dat je "int" gebruikt. Bij de AVR is dat een 16-bit getal. Dat krijg je dus nooit meer dan 65000... Alles wat met tijd en millis temaken heeft moet een long worden.

[Bericht gewijzigd door rew op donderdag 1 februari 2018 00:40:09 (13%)

four NANDS do make a NOR . Kijk ook eens in onze shop: http://www.bitwizard.nl/shop/
M-i-c-h-e-l

Golden Member

Op 1 februari 2018 00:39:49 schreef rew:
Ik zeg: Programma is wel ongeveer goed. (en ik heb alleen dat ding in de openingspost zitten lezen.)

Probleem is dat je "int" gebruikt. Bij de AVR is dat een 16-bit getal. Dat krijg je dus nooit meer dan 65000... Alles wat met tijd en millis temaken heeft moet een long worden.

De tijd-variabelen zijn unsigned long.
Alleen de variabele water is een int. Die hoeft slechts een ADC-waarde op te slaan.

@Kimmie
Wat ik nog wel 's gebruik om fouten te achterhalen, is één of meerdere ledjes of een buzzer op een extra uitgang aansluiten en deze gebruiken om bepaalde waarden zichtbaar te maken.

Hensz

Golden Member

Hoe bepaal je of het water stroomt? Omdat er nu met de waarde 700 vergeleken wordt lijkt het alsof je een stroomsensor oid gebruikt. Is dat ook zo?

Don't Panic!
Arco

Special Member

Ze zijn er ook compleet ('ShowerSmart'). Grappige is dat het geen externe voeding nodig heeft, er zit een 'mini-turbine' in de waterstroom... ;)

http://watersmarttechnology.com/wp-content/themes/water/images/uploads/watersmart1.jpg

Arco - "Simplicity is a prerequisite for reliability" - hard-, firm-, en software ontwikkeling: www.arcovox.com

Oplossing voor je probleem helemaal onderaan dit gewauwel :)

Op 31 januari 2018 22:55:02 schreef kimmieg:
Bedankt voor je hulp, maar hier snap ik al helemaal niks van;). Of ik ben er nu al zo lang mee bezig dat ik me niet meer kan focussen en het gewoon niet meer zie.

State machines werden in 1958 nog wel eens gebruikt dacht ik ;)
En code waar je op 4 plaatsen je sensor uitleest lijkt me ook niet de manier, sorry Hensz ;)

Coden is vooral puzzelen. Hoe kan je het netter maken, eenvoudiger, liever 2 regels dan 12. Dingen niet dubbel doen.

Ik zie bijvoorbeeld dat je currentTime = millis() gebruikt maar verderop niet currentTime gebruikt, dat werkt verwarrend en dat kan leiden tot fouten.

De alarm() routine kan bijvoorbeeld ook meteen de ledjes doen, dat maakt de code wat overzichtelijker. Meer dan 10 - 20 regels in een if statement ben ik ook niet zo'n fan van. Ik doe dan liever zoiets:

c code:


  if (water > 700) { 
    douche_aan_routine(); 
  } 
  else  { douche_uit_routine(); }

Je ziet dat zelfs zonder commentaar je deze code snel overziet. Tijdens testen kan je best tijdelijk even zoiets doen:

c code:


  totaalteller=totaalteller+1; 
  if (water > 700) { 
    douche_aan_routine(); 
    teller = teller + 1; 
  } 
  else  { 
    douche_uit_routine(); teller = teller - 1 ; 
  }
  print "De douche staat:".teller/totaalteller" van de tijd aan"; 

En als dat werkt dat ruim je dat weer op in de subroutines douche_aan en douche_uit.

Door het antwoord van @K7Jz werkt mijn timer nu wel, alleen als er geen water meer gedetecteerd is blijft de timer doorlopen, hij stopt niet. Dus als ik de sensor vervolgens weer in het water doe gaat het alarm meteen af. Als ik alleen dit nog op kan lossen ben ik heel erg blij.

Post even je code nu, want het wordt wel een flinke mentale puzzel om alle stukjes bij elkaar te voegen zonder dat het huiswerk wordt.

Ik denk dat ik hem al zie, mijn laatste regel was verkeerd:
Onder douche uit moet je dit zetten, met 1 '=' teken:

WaterdetectionTime == 0; // niet dit want dit is een vergelijking maar:
WaterdetectionTime = 0;

Hensz

Golden Member

Op 1 februari 2018 14:32:05 schreef K7Jz:En code waar je op 4 plaatsen je sensor uitleest lijkt me ook niet de manier, sorry Hensz ;)

Is te reduceren tot 3, maar je wilt dan ook op 3 verschillende momenten weten of er water stroomt.
Wat is het bezwaar en hoe wil je dat anders doen dan?

Don't Panic!

Op 1 februari 2018 23:48:15 schreef Hensz:
[...]
Is te reduceren tot 3, maar je wilt dan ook op 3 verschillende momenten weten of er water stroomt.
Wat is het bezwaar en hoe wil je dat anders doen dan?

Ach het is misschien ook als de voorkeur voor elektrisch of accoustisch, wat je wil :)

Ik denk dat een groot doel met coden het zo simpel en robuust mogelijk te houden. Ik heb wel eens de neiging om liever dit te doen:

code:


A = 1 ; 
...
IF BLA=2 THEN A=2; 

Terwijl je veel vaker dit ziet:

code:

 
IF BLA=2 THEN A=2 ELSE A=1; 

Beide gaat goed, totdat je 'A' een keer te vroeg gebruikt.

Ook tijdens het coderen is het handiger om dingen op 1 plek te regelen. In jouw code lees je 4 keer de sensor in en vergelijkt ook de waarde 4 keer met 700.

Ik vind de flow van TS wel aardig:

code:

if (millis() - WaterDetectionTime >= 60000 && WaterDetectionTime  !=  0) //zijn er 10 minuten verstreken (1 minuut voor testen) én waterdetectie staat niet op 0 nadat er water is gedetecteerd? Zet rode led aan + alarm

De tweede vergelijking is overbodig, >=60000 kan geen 0 zijn.

Je zou ook een teller kunnen maken bij de 'beep', 10 piepjes is alarm.

code:


int beepTeller = 0 ; // aan het begin

....

    if (currentTime - lastTimeIDidBeep >= 6000) //na elke minuut een beep (6 seconden voor testen)
    {
      lastTimeIDidBeep = currentTime;
      beep();
      if ( beepTeller++ >= 10 ) { alarm(); } 
       
    }

.... 
beepTeller = 0 ; // dit onder 'douche uit' zetten. 

....
Hensz

Golden Member

Op 2 februari 2018 10:18:21 schreef K7Jz:
[...]

Ook tijdens het coderen is het handiger om dingen op 1 plek te regelen. In jouw code lees je 4 keer de sensor in en vergelijkt ook de waarde 4 keer met 700.

Leg dan eens uit hoe je dat in het onderhavige geval doet?

Op 2 februari 2018 10:18:21 schreef K7Jz:
[...]

Ook tijdens het coderen is het handiger om dingen op 1 plek te regelen. In jouw code lees je 4 keer de sensor in en vergelijkt ook de waarde 4 keer met 700.

Ik vind de flow van TS wel aardig:

code:

if (millis() - WaterDetectionTime >= 60000 && WaterDetectionTime  !=  0)

De tweede vergelijking is overbodig, >=60000 kan geen 0 zijn.

Hij vergelijkt ook niet met 60.000, maar kijkt of WaterDetectionTime 0 is of niet. Dat is écht wat anders!

Don't Panic!