Forum: Mikrocontroller und Digitale Elektronik Millis als Lösung, oder geht es auch anders?


von Xaver M. (priapos)


Lesenswert?

Moingsen Gemeinde.

Ich habe mir eine kleine Steuerung für Fenster gebaut, Arduino Nano, 
Temp-Sensor, Relaisboard und Linearmotor mit Endschalter.
Das Ganze funktioniert daher will ich es funktioneller machen.

Die Fenster werden in 3 Schritten geöffnet, das der Linearmotor 
insgesamt 10 sec lang läuft, erst 2 sec, steigt die Temp weiter nochmal 
4 sec und bei weiter steigender Temp noch einmal 4, dann ist das Fenster 
komplett geöffnet und der Motor geht an den Endschalter und aus.

Diese Funktionalität habe ich bisher wie Folgt gelöst:
1
 
2
if ((zaehler == 0) && (temp >= t1) || (humidity > 90))             // zaehler = 0; fenster geschlossen
3
            {
4
              digitalWrite(ledGelb, HIGH);
5
              digitalWrite(ledGruen, HIGH);
6
              digitalWrite(relaisPin5, HIGH);
7
              digitalWrite(relaisPin9, HIGH);  //pin8 bestromen, fenster auf über relais, led gruen
8
              delay(2000);                     //teilöffnung des fensters (1/5 der laufzeit von 10 sec
9
              digitalWrite(relaisPin9, LOW);   // pin9 strom abschalten, ruhezustand
10
              digitalWrite(relaisPin8, HIGH);
11
              delay(2000);
12
              digitalWrite(relaisPin8, LOW);
13
              digitalWrite(relaisPin7, HIGH);
14
              delay(2000);
15
              digitalWrite(relaisPin7, LOW);
16
              digitalWrite(relaisPin5, LOW);
17
              digitalWrite(ledGelb, LOW);
18
              digitalWrite(ledGruen, LOW);
19
              delay(15);
20
              zaehler++;
21
            }

Der zaehler fungiert als Status analog zur Fensteröffnung und wird 
nach jeder Teilöffnung mit hochgezählt, beim Schließen entsprechend 
runter. D.h. zaehler == 0 Fenster sind geschlossen, bei zaehler == 1 
ist die obige Teilschleife abgearbeitet. Die LED´s sind Status-LED´s um 
zu sehen, da passiert was.
Ich will das jetzt umstellen, vom delay auf millis, weis  nur eben nicht 
so recht um das wie dahinter.
Relevant als Hintergrund dürfte sein, das die Motoren nicht alle auf 
einmal laufen können, wegen der max Stromlast und daher nacheinander 
angesteuert werden.

Vielleicht hat ja jemand von euch einen Vorschlag.

: Verschoben durch Moderator
von Andre (Gast)


Lesenswert?

Mach das doch mit einer statemachine und den Millisekunden. Oder auf 
Deutsch, einer Schrittkette.
Sinngemäß:
Schritt 1 Motor an
Wenn Zeit abgelaufen, weiterschalten
Schritt 2 Motor stoppen
usw.

von Xaver M. (priapos)


Lesenswert?

Jepp, klingt genau nach dem was mir entgegen kommt, super, danke. Ich 
guck mir mal ein paar Beispiele an.

von Stefan F. (Gast)


Lesenswert?

Xaver M. schrieb:
> Ich will das jetzt umstellen, vom delay auf millis, weis  nur eben nicht
> so recht um das wie dahinter.

Dort steht wie es geht. 
http://stefanfrings.de/multithreading_arduino/index.html

von Xaver M. (priapos)


Lesenswert?

Stefan ⛄ F. schrieb:
> Xaver M. schrieb:
>> Ich will das jetzt umstellen, vom delay auf millis, weis  nur eben nicht
>> so recht um das wie dahinter.
>
> Dort steht wie es geht.
> http://stefanfrings.de/multithreading_arduino/index.html

Hervorragendes Beispiel, danke, das Beispiel passt sehr gut, bis auf das 
"gleichzeitig" bei mir nicht geht, weil ich nicht über 6A Anlaufstrom 
gehen kann.
Gibt es da eine Parallele zu dir?

von Stefan F. (Gast)


Lesenswert?

Xaver M. schrieb:
> Hervorragendes Beispiel, danke, das Beispiel passt sehr gut, bis auf das
> "gleichzeitig" bei mir nicht geht

Dann setze es halt mit nur einem Thread um. Das wichtigste für die 
Planung ist das Zustandsdiagramm oder die Tabelle.

von Wolfgang (Gast)


Lesenswert?


von Xaver M. (priapos)


Lesenswert?

Wolfgang schrieb:
> Xaver M. schrieb:
>> Linearmotor
>
> Bestimmt nicht ;-)
> https://de.wikipedia.org/wiki/Linearmotor

Stimmt sogar... ist so ein Motor-Dingens
https://www.amazon.de/Justech-Linearmotor-Verstellantrieb-Toröffner-Actuator/dp/B07PFS33PM/ref=sr_1_8?dchild=1&keywords=linearmotor+12v&qid=1627331829&sr=8-8

Ich plane allerdings diesen Motortyp durch einen einfacheren zu 
ersetzen, allerdings wollte ich am Anfang erst einmal ein 
funktionsfähiges Modell bauen, was mir auch gelungen ist.
Und jetzt nehme ich die Verbesserungen in Angriff.

von Xaver M. (priapos)


Lesenswert?

Stefan ⛄ F. schrieb:
> Xaver M. schrieb:
>> Hervorragendes Beispiel, danke, das Beispiel passt sehr gut, bis auf das
>> "gleichzeitig" bei mir nicht geht
>
> Dann setze es halt mit nur einem Thread um. Das wichtigste für die
> Planung ist das Zustandsdiagramm oder die Tabelle.

Genau, ein Thread der nacheinander die Motoren ansteuert.
In dem letzten Beispiel am Ende auf der Seite müsste meinem Verständnis 
nach das Intervall
1
 intervalle = intervalle + 1;

hochgezählt werden, bis
1
 if (intervalle >= 10)

Fehlt das in der Schleife? oder checke ich es doch nicht?
1
 void loop() 
2
{
3
    static unsigned long int warteSeit = 0;
4
    
5
    if (millis() - warteSeit >= 10) 
6
    {
7
        thread_rot();
8
        thread_gelb();
9
        thread_gruen();
10
        warteSeit = warteSeit + 10;
11
    }
12
}

von Stefan F. (Gast)


Lesenswert?

Xaver M. schrieb:
> Fehlt das in der Schleife?

Der Thread hat seinen eigenen Intervall-Zähler. Deswegen ist die 
Variable static. Die Idee ist, dass jeder Thread (wen mehrere 
existieren) für sich zählt.

Man kann die Intervalle natürlich alternativ zentral zählen, aber dürfen 
die Threads die Variable nicht auf 0 setzen. Das müsste man dann auch 
noch umschreiben - dann wäre es am Ende nicht mehr einfacher, als das 
Beispiel darüber.

von Xaver M. (priapos)


Lesenswert?

Danke für die Rückmeldung, dann mache ich mich mal an das Umschreiben.

von MaWin (Gast)


Lesenswert?

Ich würde das anders machen.

von Stefan F. (Gast)


Lesenswert?

MaWin schrieb:
> Ich würde das anders machen.

Gut dass wir dass jetzt alle wissen. Nachts ist es kälter als draußen.

von seventeen (Gast)


Lesenswert?

>Ich würde das anders machen.

Ich auch.
Ich finde den Umgang mit den Zeiten sehr unübersichtlich.
1
if (millis() - warteSeit >= 10) 
2
{
3
..
4
}

besser:
1
timmer ++;
2
3
if (timer > 10) 
4
{
5
..
6
}

und dann halt alle ThreadABCs zu synchronen TaskABCs_1ms ändern.

von Xaver M. (priapos)


Lesenswert?

Ist schon geändert.
Eine Rückfrage ist beim Umschreiben entstanden.
In der Teil-Schleife oben, wird das Fenster 2000ms lang geöffnet, von 
insgesamt 10.000 ms. Ergo gibt es noch 2 weitere Teilschleifen deren 
Öffnungszeit in Summe 10.000ms ergibt, das gleich nochmal zum schließen.

Kann ich die Öffnungszeit (aus diesem Bsp 378 ms) auch aus der Loop 
übergeben?
1
switch (zustand)
2
    {
3
        case AUS:   
4
            if (millis() - warteSeit >= 378) 
5
            {
6
                digitalWrite(LED_GRUEN, HIGH); 
7
                warteSeit = millis();  
8
                zustand = EIN; 
9
            }          
10
            break;
ich sehe da nämlich Konfliktpotential aufgrund der Startparameter
1
static unsigned long int warteSeit = 0;

von Veit D. (devil-elec)


Lesenswert?

Hallo,

ich kann dir bei der letzten Frage nicht ganz folgen, dass ist mir dann 
doch zu zerstückelt und unvollständig. Aber laut deinem 
Eingangscodeschnipsel soll alles nacheinander und nicht parallel 
abgearbeitet werden. Dafür habe ich ein Bsp. wie man das machen könnte.

Die Funktion wartezeit() wird mit 2 Parametern aufgerufen, die intern 
nur auf "Zeit ist um" oder "nicht um" verglichen werden 
Demententsprechend wird true oder false zurückgegeben. Wenn 'true' wird 
der 'lastMillis' aktualisiert für den nächsten Vergleich im switch case. 
Die Cases kannste nach dem Schema beliebig ausbauen. Die Funktion 
'steuerung()' läuft damit ohne Blockierung wie man am Kontrollblinker 
heartbeat() sieht.
1
/*
2
  Arduino IDE 1.8.15
3
  Arduino Mega2560
4
*/
5
6
const byte pinLED {30};
7
8
void setup (void)
9
{
10
  pinMode(pinLED, OUTPUT);
11
  pinMode(LED_BUILTIN, OUTPUT);
12
}
13
14
void loop (void)
15
{
16
  steuerung();
17
  heartbeat(500);
18
}
19
20
// ****** Funktionen ******
21
void steuerung (void)
22
{
23
  enum class Job : byte {EIN, AUS};
24
  static Job job {Job::AUS};
25
26
  static unsigned long lastMillis {0};
27
28
  switch (job) {
29
    case Job::EIN:
30
                  if (wartezeit(lastMillis, 900) )
31
                  {
32
                    digitalWrite(pinLED, HIGH);
33
                    lastMillis = millis();
34
                    job = Job::AUS;
35
                  }
36
                  break;
37
38
    case Job::AUS:
39
                  if (wartezeit(lastMillis, 100) )
40
                  {
41
                    digitalWrite(pinLED, LOW);
42
                    lastMillis = millis();
43
                    job = Job::EIN;
44
                  }
45
                  break;              
46
  }
47
}
48
49
bool wartezeit (const unsigned long lastTime, const unsigned long interval)
50
{
51
  bool state {false};
52
53
  if (millis() - lastTime >= interval) {
54
    state = true;
55
  }
56
  return state;
57
}
58
59
void heartbeat (const unsigned long interval)   // Kontrolle ob Sketch blockiert
60
{
61
  static unsigned long lastMillis {0};
62
  static bool state {LOW};
63
  const unsigned long ms {millis()};
64
  
65
  if (ms - lastMillis >= interval) {
66
    lastMillis = ms;
67
    state = !state;
68
    digitalWrite(LED_BUILTIN, state);
69
  }
70
}

von Xaver M. (priapos)


Lesenswert?

Ich sehe da jetzt nicht den großen Unterschied in den Switch-Cases, bin 
allerdings auch kein Programmierer um das genauer Beurteilen zu können.

Deine Einschätzung, Veit, ist richtig, ich habe 3 Motoren die ich 
NACHEINANDER ansteuern muss.
Gerade bin ich dabei zu überlegen, ob ich die Zeit, die sie Motoren 
angesteuert werden, aus dem Loop an die Switch-Case-Bedingung übergeben 
kann, weil ich andernfalls 3 Switch-Cases zum Öffnen brauche (es sind 3 
Teilöffnungen bis zur Gesamtöffnung), und das gleiche nocheinmal zum 
Schließen, oder es bequemerweise in jeweils 3 gleich große 
Öffnungszeiten aufteile;-) die dann auch 3 mal 1/3 = ein ganzes ergeben.

von Veit D. (devil-elec)


Lesenswert?

Hallo,

den Zeitvergleich im case kannste auch machen wie du es geschrieben 
hast, dann fällt der wartezeit() Funktionsaufruf weg. Erstmal egal. 
Wichtig ist, dass war glaube ich deine Frage, dass die Variable 
'warteSeit' bzw. 'lastMillis' lokal static ist, dann gibts erstmal keine 
Konflikte.

Die Intervallzeiten kannste natürlich bspw. der Funktion 'steuerung()' 
als Parameter übergeben und verwenden.

Wenn das alles noch mehr ausarten soll, dann wäre es ratsam eine Klasse 
zu schreiben, wenn alles nach gleichen Schema funktioniert.

von Xaver M. (priapos)


Lesenswert?

Ja, Öffnen und Schließen der Fenster Funktioniert nach Schema F, öffnen 
über 3 Pins, schließen über 3 andere, das ganze steuert dann Relais mit 
einer H-Brücke an, um die Polarität der Motoren zu steuern.

Bis ich mich mit Klassen beschäftige wird es noch ein wenig dauern.
Die Öffnungsintervalle/-zeiten habe ich jetzt mal gleich gemacht (immer 
1/3tel, so das immer die gleiche Funktion aufgerufen wird. Ich glaube 
nicht das es einen großen Unterschied machen wird bei 1/10tel 
Unterschied, evtl. wird an kalten und sonnigen Tagen das Fenster 
zwischendurch mal geschlossen.
Um das zu überprüfen werde ich die Temp und Feuchte mal auf einer 
SD-Karte mitloggen und dann ggf. neu entscheiden.
Im Moment ist das alles noch im Prototypenstadium und wird entsprechend 
getestet.

von Xaver M. (priapos)


Lesenswert?

Danke für alle Hinweise zur Lösung.
Ich konnte es schlußendlich erfolgreich umsetzen, auch wenn es einige 
Zeit und Mühe gekostet hat.
Zustandsautomat ist von der Erklärung nicht so schwer zu verstehen, 
dafür hat es die Umsetzung in einen Code als Anfänger in sich.

@ Stefanus: dein Link hat mir, nach dem es zwar in der Loop 
funktionierte aber nicht als ausgelagerte Funktion,  gezeigt wie ich es 
in eine Fkt auslagern muss damit es eigenständig funktioniert. Danke 
dafür.

@devil-elec: mal sehen ob ich es jetzt in eine Klasse auslagern kann.

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.