Douchetimer (1)

algemene C code
Berichten: 143
Geregistreerd: 15 Dec 2012, 21:02
Woonplaats: Delfzijl

Douchetimer (1)

Berichtdoor FPCUser » 20 Okt 2017, 15:56

Ik gebruik de Arduino niet zoals vele hier om er concrete projecten mee te maken,.
Maar om te leren programma's te schrijven in de taal C en C++.
Wat nu volgt is het gebruik van timertjes :)


Van de zomer heb ik op campings verschillende doucheautomaten gezien.
Deze automaten geven warm water na een druk op een knop, of na het ingooien van een muntstukje, gedurende een bepaalde tijd.
Meestal 5 minuten, maar ik heb ook korter meegemaakt.
Een probleem was wel het inschatten hoeveel douchetijd je nog had.
Dit omdat een tijdsindicatie soms geheel ontbrak, en als er een was, dan was die defekt.
Streepjes op een zeven-segment display...
Bij de drukknop-automaten was de startknop gedurende een bepaalde tijd geblokkeerd.
Maar als je jezelf weer hebt aangekleed en de douche met de trekker hebt drooggemaakt, is de 'wachttijd' toch verstreken en is douche automaat weer gereed voor de volgende gast.

En dan begint het te kriebelen. Hoe zou zoiets werken? En, kan ik ook zoiets zelf programmeren voor een Arduino?
Ziehier de uitdaging.

Het moest natuurlijk zonder de delay() functie te gebruiken!
En ik heb een als startsignaal een RS-flipflop gebruikt, zodat kontaktdender niet van invloed is op het starten.

Ter verduidelijking: een RS-flipflop is te beschouwen als een zwart doosje met aan de ene kant twee ingangen, een 'set' en een 'reset',
en aan de andere zijde een uitgang.
Maak je de setingang voor een kort moment hoog dan wordt en blijft de uitgang ook hoog.
De status van de setingang (hoog of laag) heeft dan geen invloed meer op de status van de uitgang.
Maak je de resetingang eventjes hoog, dan wordt de uitgang weer laag en blijft laag.
Ook hierbij geldt dat de status van de resetingang niet meer van invloed is op de uitgang.

Om te laten zien hoeveel tijd je nog hebt om te douchen heb ik vijf leds genomen.
Bij de start branden ze alle vijf en gaat er telkens een uit na een minuut.
Ten teken dat de douchetijd bijna is afgelopen knippert de laatste LED gedurende de laatste 10 seconden.

Uiteindelijk zij het er dan 5 timertjes geworden die onafhankelijk van elkaar werken.

Het programma zal ik in het volgend bericht zetten.

Advertisement

Berichten: 143
Geregistreerd: 15 Dec 2012, 21:02
Woonplaats: Delfzijl

Re: Douchetimer (1)

Berichtdoor FPCUser » 20 Okt 2017, 16:00

Hierbij de code.


cpp code
/*
Douche timer

De douche moet, na druk op een knop of na een startsignaal van een muntautomaat,
gedurende 5 minuten warm water geven.
Na afloop v/d tijd is de startdrukknop gedurende 2 minuten geblokkeerd.
Bij start branden alle 5 stuks "minuten LED's", telkens als er een minuut is
verstreken gaat er een LED uit.
De laatste 10 seconden knippert de 1 min LED.
De "wachttijd LED" brand gedurende de... wachttijd.
De stand-by LED geeft aan dat de wachttijd is verstreken en de timer weer
startbereid is.

Uitwerking met in totaal 5 onafhankelijke timers (variaties op BlinkWithoutDelay),
3 voor de douchetijd, de wachttijd en de afschakeling van de minuten LED's,
en nog eens 2 voor de knipperfunctie.
*/


/* constanten */
const byte startPinDouche = 2; // drukknop douche
const byte standbyPin = 3; // douchetimer is weer inschakelbereid
const byte wachttijdPin = 4; // wachtijd is ingegaan
const byte klepPinDouche = 5; // waterklep douche
const unsigned long douchetijd = 300000; // 5 min douchetijd (300000 ms)
const unsigned long pauzetijd = 120000; // 2 min wachttijd (120000 ms)

/* een Pin voor elke LED die een minuut aangeeft.
b.v. minutenLed{0] -> 5 min., ..[1] -> 4 min, enz.
*/
const byte minutenLed[] = {6, 7, 8, 9, 10};

/* variabelen - globaal */
unsigned long startDoucheTijd = 0; // merker douche
unsigned long startWachtTijd = 0; // idem


void setup()
{
pinMode(klepPinDouche, OUTPUT);
pinMode(standbyPin, OUTPUT);
pinMode(wachttijdPin, OUTPUT);
for (byte i = 0; i < 5; i++) {
pinMode(minutenLed[i], OUTPUT);
}
Serial.begin(115200); // voor test
}


void loop()
{
standby();
doucheklep(startDoucheTijd, startWachtTijd);
toonMinutenLeds();
}


/*
Douche kan gestart worden.
Wachttijd is voorbij
*/
void standby()
{
if (startDoucheTijd == 0 && startWachtTijd == 0) {
digitalWrite(standbyPin, HIGH);
} else {
digitalWrite(standbyPin, LOW);
}
}


/*
Sturing warmwaterklep t.b.v. douche

variabelen:
startDouche: merker voor de tijd waarop de douchetijd is begonnen
startPauze: merker voor de tijd waarop de wachttijd is begonnen
*/

void doucheklep(unsigned long & startDouche, unsigned long & startPauze)
{
static boolean RS1_q = false; // RS flipflop 1 - uitgang
static boolean RS1_r; // RS flipflop 1 - reset ingang
static boolean RS1_s; // RS flipflop 1 - set ingang

static boolean RS2_q = false; // idem RS flipflop 2
static boolean RS2_r;
static boolean RS2_s;

/* douchetijd */
RS1_s = digitalRead(startPinDouche);
RS1_q = (RS1_q || RS1_s) && ! RS1_r;
if (RS1_q) {
if (startDouche == 0) {
/* hier gaat douchetijd in */
startDouche = millis();
alleMinutenLedsAan();
Serial.println("start douchetijd"); // voor test
}
if ((millis() - startDouche) > douchetijd) {
/* douchetijd ia afgelopen */
RS1_r = true;
RS2_s = true;
Serial.println("stop douchetijd"); // voor test
}
}

/* wachttijd */
RS2_q = (RS2_q || RS2_s) && ! RS2_r;
if (RS2_q) {
if (startPauze == 0) {
/* nu gaat de wachttijd in */
startPauze = millis();
Serial.println("start wachttijd"); // voor ttest
}
if ((millis() - startPauze) > pauzetijd) {
/* wachttijd is voorbij */
RS2_r = true;
Serial.println("stop wachttijd"); // voor test
}
}

if (RS1_r && RS2_r) {
// douche- en wacht cyclus zijn afgelopen, reset alles
RS1_s = false;
RS1_r = false;
RS1_q = false;
RS2_s = false;
RS2_r = false;
RS2_q = false;
startDouche = 0;
startPauze = 0;
Serial.println("reset"); // voor test
}

/* sturing uitgangen */
digitalWrite(klepPinDouche, RS1_q);
digitalWrite(wachttijdPin, RS2_q);
}


/*
Bij het starten v/d douche gaan alle (5) minuten LED's aan.
Fuctie wordt aangeroepen door doucheklep()
*/
void alleMinutenLedsAan()
{
for (byte i = 0; i < 5; i++) {
digitalWrite(minutenLed[i], HIGH);
}
}


/*
Telkens gaat na een minuut (60.000 ms) er een LED uit.
Laatste 10 seconden knipperd laatste LED.
*/
void toonMinutenLeds()
{
if (startDoucheTijd != 0) {
for (byte i = 0; i < 5; i++) {
if (millis() - startDoucheTijd > (i + 1) * 60000) {
digitalWrite(minutenLed[i], LOW);
}
/* de laatste 10 sec knippert 1 min. LED */
if ((millis() - startDoucheTijd > 289000) && (millis() - startDoucheTijd < 300000)) {
digitalWrite(minutenLed[4], knipperpuls());
}
}
}
}


/*
knipperpuls 1 s ritme.
Fuctie wordt aangeroepen door toonMinutenLeds()
*/
bool knipperpuls()
{
static unsigned long pauzeStatus;
static unsigned long pulsStatus;

if (pulsStatus == 0) {
if (pauzeStatus == 1)
pauzeStatus = millis();
else if (millis() - pauzeStatus > 500) {
pauzeStatus = 0;
pulsStatus = 1;
}
return (false);
}

if (pauzeStatus == 0) {
if (pulsStatus == 1)
pulsStatus = millis();
else if (millis() - pulsStatus > 500) {
pulsStatus = 0;
pauzeStatus = 1;
}
return (true);
}
}

Gebruikers-avatar
Berichten: 1728
Geregistreerd: 06 Aug 2016, 01:03

Re: Douchetimer (1)

Berichtdoor Koepel » 22 Okt 2017, 02:28

En nu hetzelfde, maar dan met een finite state machine ;)

Berichten: 143
Geregistreerd: 15 Dec 2012, 21:02
Woonplaats: Delfzijl

Re: Douchetimer (1)

Berichtdoor FPCUser » 22 Okt 2017, 13:04

Je geeft me dus een nieuwe uitdaging.

Ik zal me er eens in gaan verdiepen :)
Maar garanderen....

Gebruikers-avatar
Berichten: 4827
Geregistreerd: 13 Mei 2013, 20:57
Woonplaats: Heemskerk

Re: Douchetimer (1)

Berichtdoor nicoverduin » 22 Okt 2017, 13:39

Eerst plaatje maken.... de rest volgt vanzelf:)
Docent HBO Software Engineering, Embedded ontwikkelaar & elektronicus
http://www.verelec.nl

Berichten: 143
Geregistreerd: 15 Dec 2012, 21:02
Woonplaats: Delfzijl

Re: Douchetimer (1)

Berichtdoor FPCUser » 22 Okt 2017, 14:39

Umbrello (UML modeller) ?

Gebruikers-avatar
Berichten: 4827
Geregistreerd: 13 Mei 2013, 20:57
Woonplaats: Heemskerk

Re: Douchetimer (1)

Berichtdoor nicoverduin » 22 Okt 2017, 18:25

FPCUser schreef:Umbrello (UML modeller) ?

Ik werk zelf met softwareideasmodeler. Daarmee kan ik c++ bouwen en genereren en ook reverse genereren met behoud van de code en documentatie
Docent HBO Software Engineering, Embedded ontwikkelaar & elektronicus
http://www.verelec.nl

Berichten: 143
Geregistreerd: 15 Dec 2012, 21:02
Woonplaats: Delfzijl

Re: Douchetimer (1)

Berichtdoor FPCUser » 01 Nov 2017, 12:35

Het is er eindelijk van gekomen: de douche timer volgens het 'finite state machine' principe.
Na een zoektocht op het internet denk ik dat bedoeld word dat het programma verloop wordt
opgesplitst in een aantal toestanden waarin het zich kan bevinden.

Ik kom dan bij mijn douchetimer tot 5 toestanden (state), genummerd van 0 t/m/ 4 :-)
State 0 is de uitgangssituatie, systeem is in rust en standBy-LED brand.

Bij een druk op de startknop gaat het over naar state 1.
Hiebij gaat de warmwaterklep open, en de douchetimer wordt gestart.
StanBy-LED gaat uit.

Na afloop van de tijd gaat het over naar state 2.
Warmwaterklep wordt weer gesloten.
Er wordt doorgeschakeld naar state 3.

Bij state 3 wordt de wachttimer gestart en gaat de wacht-LED branden.

Na afloop van de wachttimer wordt overgegaan naar state 4.
Hierbij gaat de wacht-LED weer uit en wordt weer teruggeschakeld naar state 0.

Kortom:
state 0 = rust
state 1 = startdouchetijd
state 2 = einde douchetijd
state 3 = start wachttijd
state 4 = einde wachttijd

cpp code
/*
Douche timer
Versie 2 (finite state machine)

De douche moet, na druk op een knop of na een startsignaal van een muntautomaat,
gedurende 5 minuten warm water geven.
Na afloop v/d tijd is de startdrukknop gedurende 2 minuten geblokkeerd.
Bij start branden alle 5 stuks "minuten LED's", telkens als er een minuut is
verstreken gaat er een LED uit.
De laatste 10 seconden knippert de 1 min LED.
De "wachttijd LED" brand gedurende de... wachttijd.
De stand-by LED geeft aan dat de wachttijd is verstreken en de timer weer
startbereid is.
*/


const byte startPinDouche = 2; // drukknop douche
const byte standbyPin = 3; // douchetimer is (weer) inschakelbereid
const byte wachttijdPin = 4; // wachtijd is ingegaan
const byte klepPinDouche = 5; // waterklep douche
const unsigned long douchetijd = 300000; // 5 min douchetijd (300000 ms)
const unsigned long pauzetijd = 120000; // 2 min wachttijd (120000 ms)

unsigned long startDoucheTijd = 0; // merker douche
unsigned long startWachtTijd = 0; // idem
static unsigned int state; // status finite state machine

/* een Pin voor elke LED die een minuut aangeeft.
b.v. minutenLed{0] -> 5 min., ..[1] -> 4 min, enz.
*/
const byte minutenLed[] = {6, 7, 8, 9, 10};


void setup()
{
pinMode(klepPinDouche, OUTPUT);
pinMode(standbyPin, OUTPUT);
pinMode(wachttijdPin, OUTPUT);
for (byte i = 0; i < 5; i++) {
pinMode(minutenLed[i], OUTPUT);
}
state = 0;
}


void loop()
{
// finite state machine:
switch (state) {
case 0:
/* rust positie (standBy) */
digitalWrite(standbyPin, HIGH);
if (digitalRead(startPinDouche)) {
state = 1;
}
break;
case 1:
/* start douchetijd */
digitalWrite(standbyPin, LOW);
digitalWrite(klepPinDouche, HIGH);
toonMinutenLeds();
if (startDoucheTijd == 0) {
/* douchetimer start */
startDoucheTijd = millis();
alleMinutenLedsAan();
}
if ((millis() - startDoucheTijd) > douchetijd) {
/* douchetijd afgelopen */
state = 2;
}
break;
case 2:
/* einde douchetijd */
digitalWrite(klepPinDouche, LOW);
startDoucheTijd = 0;
state = 3;
break;
case 3:
/* start wachttijd */
digitalWrite(wachttijdPin, HIGH);
if (startWachtTijd == 0) {
/* wachttimer start */
startWachtTijd = millis();
}
if ((millis() - startWachtTijd) > pauzetijd) {
/* wachttijd is voorbij */
state = 4;
}
break;
case 4:
/* einde wachttijd */
digitalWrite(wachttijdPin, LOW);
startWachtTijd = 0;
state = 0;
break;
} // end switch
}


/*
Bij het starten v/d douche gaan alle (5) minuten LED's aan.
Fuctie wordt gestart bij case 1
*/
void alleMinutenLedsAan()
{
for (byte i = 0; i < 5; i++) {
digitalWrite(minutenLed[i], HIGH);
}
}


/*
Telkens gaat na een minuut (60.000 ms) er een LED uit.
Laatste 10 seconden knipperd laatste LED.
*/
void toonMinutenLeds()
{
for (byte i = 0; i < 5; i++) {
if (millis() - startDoucheTijd > (i + 1) * 60000) {
digitalWrite(minutenLed[i], LOW);
}
/* de laatste 10 sec knippert 1 min. LED */
if ((millis() - startDoucheTijd > 289000) && (millis() - startDoucheTijd < 300000)) {
digitalWrite(minutenLed[4], knipperpuls());
}
}
}


/*
knipperpuls 1 s ritme.
Fuctie wordt aangeroepen door toonMinutenLeds()
*/
bool knipperpuls()
{
static unsigned long pauzeStatus;
static unsigned long pulsStatus;

if (pulsStatus == 0) {
if (pauzeStatus == 1)
pauzeStatus = millis();
else if (millis() - pauzeStatus > 500) {
pauzeStatus = 0;
pulsStatus = 1;
}
return (false);
}

if (pauzeStatus == 0) {
if (pulsStatus == 1)
pulsStatus = millis();
else if (millis() - pulsStatus > 500) {
pulsStatus = 0;
pauzeStatus = 1;
}
return (true);
}
}

Gebruikers-avatar
Berichten: 1728
Geregistreerd: 06 Aug 2016, 01:03

Re: Douchetimer (1)

Berichtdoor Koepel » 01 Nov 2017, 13:31

Wow, dude! gozer! dat is awesome :!: :D
Je hebt het perfect begrepen.

Voor de cijfers van de state kun je een 'enum' gebruiken. Een 'enum' is een reeks van #define met opeenvolgende nummers.
Dan krijgen de states een naam, en dat maakt het gemakkelijker te lezen.

cpp code
enum
{
RUST,
STARTDOUCHETIJD,
} state;

switch(state)
{
case RUST:
break;
case STARTDOUCHETIJD:
break;
}


Je start de millis timer bij "case 1", en je kijkt of die is afgelopen bij "case 1".
Ik doe dat anders. Zodra bij "case 0" de state wordt gewijzigd naar de volgende, dan start ik meteen daar de millis timer. Je zou ook een extra state kunnen toevoegen die de millis timer start en bij de volgens state kijkt of die is afgelopen.

Er zit nog een rare kronkel in de code. De functie "toonMinutenLeds()" gebruikt 'startDoucheTijd', en in de finite state machine gebruik je ook 'startDoucheTijd'. Er zijn meerdere manieren om het knipperen en de tijd in de finite state machine te verwerken (er binnen of er buiten). Dat zou beter gescheiden kunnen worden.

Berichten: 143
Geregistreerd: 15 Dec 2012, 21:02
Woonplaats: Delfzijl

Re: Douchetimer (1)

Berichtdoor FPCUser » 23 Nov 2017, 20:49

Het eerste deel van Koepels reactie was duidelijk en ik heb daarom voor de toestanden enumeratie toegepast.
Als ik heb goed heb begrepen in het tweede deel van z'n reactie vond hij dat het gebruik van een globale variabele
in twee verschillende functies niet netjes.

Ik heb daarom in de functie toonMinutenLeds() een lokale variabele gemaakt om de begintijd in op te bergen.
Toen konden ook de twee globale variabelen startDoucheTijd en startWachtTijd lokale variabelen worden in de loop() functie.
Ook state kon lokaal worden zodat er geen globale variabelen meer worden gebruikt in de functies.

Omdat er tussen begin douchetijd en einde douchetijd nog een toestand zit, n.l. dat de tijd loopt is hiervoor nog een switch case gemaakt.
En dat ook natuurlijk voor de wachttijd.

Verder zijn de twee functies alleMinutenLedsAan() en toonMinutenLeds() samengevoegd omdat ze beide betrekking hebben
op het weergeven van de minuten LED's. Ook is deze functie aangepast door meer constanten en een switch constructie te gebruiken.

Tenslotte zijn er nog een paar types aangepast en zijn de namen v/d constanten hoofdletters geworden.

cpp code
/*
Douche timer
Versie 2b (finite state machine)
Board Arduibo Uno

De douche moet, na druk op een knop of na een startsignaal van een muntautomaat,
gedurende 5 minuten warm water geven.
Na afloop v/d tijd is de startdrukknop gedurende 2 minuten geblokkeerd.
Bij start branden alle 5 stuks "minuten LED's", telkens als er een minuut is
verstreken gaat er een LED uit.
De laatste 10 seconden knippert de 1 min LED.
De "wachttijd LED" brand gedurende de... wachttijd.
De stand-by LED geeft aan dat de wachttijd is verstreken en de timer weer
startbereid is.
*/

const byte START_PIN = 2; // drukknop douche
const byte STANDBY_PIN = 3; // douchetimer is (weer) inschakelbereid
const byte WACHTTIJD_PIN = 4; // wachtijd is ingegaan
const byte KLEP_PIN = 5; // waterklep douche
const unsigned long DOUCHETIJD = 300000; // 5 min douchetijd (300000 ms)
const unsigned long WACHTTIJD = 120000; // 2 min wachttijd (120000 ms)

/*
Een Pin voor elke LED die een minuut douchetijd aangeeft.
b.v. MINUTENLED{0] -> 5 min., ..[1] -> 4 min, enz.
*/
const byte MINUTENLED[] = {6, 7, 8, 9, 10};

enum {
RUST,
BEGINDOUCHETIJD,
DOUCHETIJDLOOPT,
EINDEDOUCHETIJD,
TOONDOUCHETIJD,
BEGINWACHTTIJD,
WACHTTIJDLOOPT,
EINDEWACHTTIJD
};


void setup()
{
pinMode(KLEP_PIN, OUTPUT);
pinMode(STANDBY_PIN, OUTPUT);
pinMode(WACHTTIJD_PIN, OUTPUT);
for (byte i = 0; i < 5; i++) {
pinMode(MINUTENLED[i], OUTPUT);
}
}


/*
De hoofdlus (is in dit geval de finite state machine)
*/
void loop()
{
static unsigned long startDoucheTijd;
static unsigned long startWachtTijd;
static byte state = RUST;

switch (state) {
case RUST:
digitalWrite(STANDBY_PIN, HIGH);
if (digitalRead(START_PIN)) {
state = BEGINDOUCHETIJD;
}
break;

case BEGINDOUCHETIJD:
digitalWrite(STANDBY_PIN, LOW);
digitalWrite(KLEP_PIN, HIGH);
startDoucheTijd = millis();
toonMinutenLeds(BEGINDOUCHETIJD);
state = DOUCHETIJDLOOPT;
break;

case DOUCHETIJDLOOPT:
toonMinutenLeds(TOONDOUCHETIJD);
if ((millis() - startDoucheTijd) > DOUCHETIJD) {
state = EINDEDOUCHETIJD;
}
break;

case EINDEDOUCHETIJD:
digitalWrite(KLEP_PIN, LOW);
state = BEGINWACHTTIJD;
break;

case BEGINWACHTTIJD:
digitalWrite(WACHTTIJD_PIN, HIGH);
startWachtTijd = millis();
state = WACHTTIJDLOOPT;
break;

case WACHTTIJDLOOPT:
if ((millis() - startWachtTijd) > WACHTTIJD) {
state = EINDEWACHTTIJD;
}
break;

case EINDEWACHTTIJD:
digitalWrite(WACHTTIJD_PIN, LOW);
state = RUST;
break;
} // end switch
}


/*
Bij het starten v/d douche gaan alle (5) minuten LED's aan.
Telkens gaat na een minuut (60.000 ms) er een LED uit.
Laatste 10 seconden knipperd laatste LED.
Fuctie wordt aangeroepen bij case STARTDOUCHETIJD en DOUCHETIJDLOOPT
*/
void toonMinutenLeds(byte optie)
{
static unsigned long starttijd;

switch (optie) {
case BEGINDOUCHETIJD:
starttijd = millis();
for (byte i = 0; i < 5; i++) {
digitalWrite(MINUTENLED[i], HIGH);
}
break;

case TOONDOUCHETIJD:
for (byte i = 0; i < 5; i++) {
if (millis() - starttijd > (i + 1) * 60000) {
digitalWrite(MINUTENLED[i], LOW);
}
/* de laatste 10 sec knippert 1 min. LED */
if ((millis() - starttijd > (DOUCHETIJD - 11000)) &&
(millis() - starttijd < DOUCHETIJD)) {
digitalWrite(MINUTENLED[4], knipperpuls());
}
}
break;
}
}


/*
knipperpuls 1 s ritme.
Fuctie wordt aangeroepen door toonMINUTENLEDs()
*/
bool knipperpuls()
{
static unsigned long pauzeStatus;
static unsigned long pulsStatus;

if (pulsStatus == 0) {
if (pauzeStatus == 1) {
pauzeStatus = millis();
} else if (millis() - pauzeStatus > 500) {
pauzeStatus = 0;
pulsStatus = 1;
}
return (false);
}

if (pauzeStatus == 0) {
if (pulsStatus == 1) {
pulsStatus = millis();
} else if (millis() - pulsStatus > 500) {
pulsStatus = 0;
pauzeStatus = 1;
}
return (true);
}
}

Volgende

Terug naar C code

Wie is er online?

Gebruikers in dit forum: Geen geregistreerde gebruikers en 1 gast