Ik vermoed dat je op een 'finite state machine' gaat uitkomen. In een FSM heb je een aantal stappen (states) en je gaat van stap naar stap afhankelijk van condities. Hieronder een uitgewerkt voorbeeld (niet perfect maar doet waarschijnlijk wat je wilt).
Het programma maakt gebruik van een millis() gebaseerde benadering om de delay() kwijt te raken. Het eenvoudge voorbeeld is dat je een eitje wilt koken. Je gooit het eitje in een pan met kokend water (vergelijk met het hoog maken van een pin) en kijkt op de klok wat de tijd is; vervolgens kijk je af en toe op de klok om te zien of de (bv) drie minuten verstreken zijn en als dat het geval is haal je je eitje uit het water (vergelijk met het laag maken van een pin).
Eerst moet je de stappen bepalen; ik kwam op het volgende uit, je kunt dit aan het begin van je sketch toevoegen.
enum STATES
{
WAIT_BUTTON, // wacht tot knop wordt ingedrukt
DEBOUNCE, // ontdender of kijk of knop is los gelaten
CHECK_TEMPERATURE, // kijk of actie moet worden ondernomen gebaseerd op temperatuur
LEDG, // doe actie voor groene led
LEDR, // doe actie voor rode led
};
Een enum is een type (net zoals int, char etc) met een aantal gedefinieerde waardes (die je zelf bepaalt). Je kunt dan later een variabele van dat type deklareren (zie de sm() functie hieronder,
Vervolgens kun je de basis opzetten voor de FSM
void sm()
{
// finite statemachine stap
static STATES currentState = WAIT_BUTTON;
// lees knop en temperatuur
buttonread = digitalRead(buttonpin);
temp = analogRead(temppin);
switch (currentState)
{
case WAIT_BUTTON:
break;
case DEBOUNCE:
break;
case CHECK_TEMPERATURE:
break;
case LEDG:
break;
case LEDR:
break;
}
}
Je kunt nu de stappen invullen; in WAIT_BUTTON kijk je of de knop ingedrukt is
case WAIT_BUTTON:
ledG_executed = false;
ledR_executed = false;
// als de knop ingedrukt is
if (buttonread == LOW)
{
// onthoud de tijd dat de knop ingedrukt werd
startTime = millis();
// ga naar de volgende stap
currentState = DEBOUNCE;
Serial.println("Naar DEBOUNCE");
}
break;
Je hebt een variabele startTime nodig om bij te houden wanneer de knop werd ingedrukt. En twee variabelen die aangeven of LEDG / LEDR uitgevoerd zijn.
In DEBOUNCE kijk je of de knop dendert of losgeaten is of dat de knop stabiel is voor een gegeven tijd. Afhankelijk daarvan ga je terug naar WAIT_BUTTON of ga je verder naar CHECK_TEMPERATURE.
case DEBOUNCE:
// als de knop losgelaten werd of denderde
if (buttonread == HIGH)
{
// terug naar vorige stap
currentState = WAIT_BUTTON;
Serial.println("Terug naar WAIT_BUTTON");
}
else
{
// als de knop stabiel is voor 100 ms
if (millis() - startTime >= 100)
{
// naar de volgende stap
currentState = CHECK_TEMPERATURE;
Serial.println("Naar CHECK_TEMPERATURE");
}
}
break;
In CHECK_TEMPERATURE kun je nu bepalen wat uitgevoerd moet worden gabaseerd op temperatuur en of het al uitgevoerd was.
case CHECK_TEMPERATURE:
// onthoud de start tijd voor gebruik met de led
startTime = millis();
// volgende stap afhankelijk van temperatuur
if (temp >= 100)
{
// indien de groene led niet gedaan was
if (ledG_executed == false)
{
currentState = LEDG;
Serial.println("Naar LEDG");
// houd bij wat uitgevoerd was
ledG_executed = true;
ledR_executed = false;
}
}
else
{
// indien de rode led niet gedaan was
if (ledR_executed == false)
{
currentState = LEDR;
Serial.println("Naar LEDR");
// houd bij wat uitgevoerd was
ledR_executed = true;
ledG_executed = false;
}
}
break;
Eerst onthoud je de start tijd voor de tijd dat de puls moet duren; dezelfde variabele wordt gebruikt als voor de ontdendering. Vervolgens vergelijk je de temperatuur en verandert de stap als de code voor de temperatuur nog niet iutgevoerd was.
De laatste twee stappen zijn voor de respectievelijke temperatuur acties.
case LEDG:
// ter zekerheid ledR gwn uit doen
digitalWrite(ledR, LOW);
// ledG aan
digitalWrite(ledG, HIGH);
// als de gegeven tijd verlopen is
if (millis() - startTime >= 350)
{
// ledG uitt
digitalWrite(ledG, LOW);
// terug naar debounce
currentState = DEBOUNCE;
Serial.println("LEDG compleet, terug naar DEBOUNCE");
}
break;
case LEDR:
// ter zekerheid ledG gwn uit doen
digitalWrite(ledG, LOW);
// ledR aan
digitalWrite(ledR, HIGH);
// als de gegeven tijd verlopen is
if (millis() - startTime >= 350)
{
// ledR uit
digitalWrite(ledR, LOW);
// terug naar debounce
currentState = DEBOUNCE;
Serial.println("LEDR compleet, terug naar DEBOUNCE");
}
break;
Dit gedeelte zal terug gaan naar de DEBOUNCE stap
- Als de knop in de tussentijd los gelaten was zal de DEBOUNCE stap terug gaan naar de WAIT_BUTTON stap.
- Als de knop nog steeds ingedrukt is, zal
if (millis() - startTime >= 100)
naar true evalueren en het programma gaat weer naar de CHECK_TEMPERATURE stap.
De extra variabelen die in de sm() functie gebruikt worden zijn als volgst gedeklareerd in de sm() functie zelf
void sm()
{
// finite statemachine stap
static STATES currentState = WAIT_BUTTON;
// variabele om start tijd te onthouden
static uint32_t startTime;
// variabelen om te onthouden of LEDG en LED uitgevoerd zijn
static bool ledG_executed = false;
static bool ledR_executed = false;
}
Alle variabelen zijn static
- Dit houdt in dat de inhoud niet verloren gaat als de loop() functie eindigt (hetzelfde effect als wanneer je ze als globale variabelen zou deklarerern).
- Ze zijn lokaal voor de functie; dit maakt dat de functie volledig op zichzelf kan staan (met uitzondering van de variabelen die al in je originele program gebruikt zijn) en dat er geen risico is dat een andere functie ze kan veranderen.
Je roept sm() aan vanuit loop()
void loop()
{
sm();
}
Kijk of je dit begrijpt en aan de gang kunt krijgen.
Er zit een foutje in je originele programma; de regel temp * 0.048828125;
doet helemaal niets, je gooit het resultaat of de berekening weg.