Forum: Mikrocontroller und Digitale Elektronik Programmstruktur


von Jens (nichtdiemama)


Lesenswert?

Hallo Leute

Ich habe mich seit langem mal wieder mit Controllern beschäftigt. 
Momentan habe ich ein ESP32 Kit und diverses Zubehör.

Ich habe zuletzt in meiner Ausbildung Controller mit Assembler 
programmiert wo wir immer Verrücktes Labyrinth mit Goto und Labels 
gespielt haben. Die Struktur hat sich ja geändert und soweit ich es 
gelesen habe funktionieren diese Befehle noch, sind aber nicht mehr 
erwünscht / Zeitgemäß.

Ich habe schon einige Tutorials zu den Sensoren usw. durch, doch hier 
hat man nie komplexe Programme und es wird meistens mit delays 
gearbeitet. Mein Problem besteht weniger Sensoren usw. anzusteuern 
sondern ehr im Programm Aufbau / Struktur. Habe leider keine Tutorials 
zur Struktur gefunden.

Habt ihr für mich Infos wo ich mich in sowas einlesen kann oder evtl. 
Tutorials zu diesem Thema? (Kann auch in Englisch sein)

Würde Gerne ein Programm schreiben in dem ich auf einem Oled Display 
Daten anzeigen kann und mit Tastern durch verschiedene Anzeigen 
blättere. Gleichzeitig müssen ja im Hintergrund die  Sensoren ausgelesen 
und Werte Berechnet werden.

Wie gesagt geht es mir hier um die Struktur wie man sowas aufbaut.

Danke im Voraus.
Jens

von Rahul D. (rahul)


Lesenswert?

Parallele Verarbeitung macht man mit Interrupts oder einem Scheduler / 
RTOS, bei dem in regelmäßig Abständen die Eingangssignale eingelesen, 
verarbeitet und ausgegeben werden.

von N. M. (mani)


Lesenswert?

Jens schrieb:
> und es wird meistens mit delays gearbeitet

Zu 95% der erste Fehler.

von Rahul D. (rahul)


Lesenswert?

N. M. schrieb:
> Zu 95% der erste Fehler.

Bei RTOS sind die Standard, tun aber was anderes als in diesem Fall.

von Sherlock 🕵🏽‍♂️ (rubbel-die-katz)


Lesenswert?

Ein bewährter Ansatz ist eine Endlosschleife, die zuerst alle Eingaben 
holt, sie dann verarbeitet, und dann alle Ausgaben macht.

Wobei die aktuelle Systemzeit (meist in Millisekunden) auch eine Eingabe 
sein kann, nämlich um Dinge (verzögert) zu bestimmten Zeitpunkten 
auszuführen.

Dazu gehören Status Variablen, die für jeden Thread darüber Auskunft 
geben, worauf sie warten oder was sie gerade tun.

von N. M. (mani)


Lesenswert?

Rahul D. schrieb:
> N. M. schrieb:
>> Zu 95% der erste Fehler.
>
> Bei RTOS sind die Standard, tun aber was anderes als in diesem Fall.

Die heißen dann aber normalerweise anders z.B. vTaskDelay o.ä.
Bei Anfängern, einfachen Programmen und leider auch  bei irgendwelchen 
Libs wird oft das normale (busy wait) delay verwendet und nicht richtig 
darüber nachgedacht was das bedeutet wenn man Mal ein Projekt hat wo 
zwei Libraries verwendet werden.
In einem System ohne RTOS würde man das dann über eine Statemachine 
abbilden um eben nicht zu blockieren.

Ein super Beispiel sind viele DS18B20 Libs wo per Bitbanging die Daten 
abgeholt werden. Versuch da Mal gleichzeitig ein flüssiges Bild auf 
einem Graphik Display auszugeben wenn der nebenher gerade die Temperatur 
abholt 😄

Deshalb noch ergänzend zu deiner Aufzählung:

Rahul D. schrieb:
> Parallele Verarbeitung macht man mit Interrupts oder einem Scheduler /
> RTOS, bei dem in regelmäßig Abständen die Eingangssignale eingelesen,
> verarbeitet und ausgegeben werden.

Geschickt die Hardware Peripherie des verwendeten uC ausnutzen.
Im obigen Beispiel z.B. über SPI oder was auch super geht das RMT 
Peripheral des ESP.

: Bearbeitet durch User
von Rahul D. (rahul)


Lesenswert?

N. M. schrieb:
> Die heißen dann aber normalerweise anders z.B. vTaskDelay o.ä.

oder "osDelay(...)", wie beim KEIL-RTOS. ;)

N. M. schrieb:
> In einem System ohne RTOS würde man das dann über eine Statemachine
> abbilden um eben nicht zu blockieren.

Nicht nur du.

von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Jens schrieb:
> Momentan habe ich ein ESP32 Kit

Erstmal musst du dich für eine Sprachfamilie entscheiden
Python oder c/c++ sind wohl die häufigsten für ESP32

Solltest du Arduino verwenden wollen, dann C/C++ und FreeRTOS ist gleich 
mit im Rennen.
delay() blockiert dann nicht, sondern sorgt für Taskwechsel.

Jens schrieb:
> Gleichzeitig müssen ja im Hintergrund die Sensoren ausgelesen
> und Werte Berechnet werden.

Gleichzeitig, nur bei einem dual Core ESP
Ansonsten quasi gleichzeitig.
Beides wird von FreeRTOS unterstützt.

: Bearbeitet durch User
von Jens (nichtdiemama)


Lesenswert?

Hallo Danke für die Infos.
Werde mich mal in die Interrupts und RTOS einlesen. Gibt es vielleicht 
Tutorials für die Programmstruktur?. Hab noch keine gefunden.

Welche Sprache verwendet wird bin ich mir ehrlich gesagt nicht zu 100% 
sicher. Die meisten Tutorials erwähnen dass auch nicht wirklich.

Ich verwende wie gesagt ein ESP32 und VS Code mit der PlatformIO 
Erweiterung.
Im VS Code wird unten rechts C++ angezeigt.

die main.cpp hat diesen Aufbau:
#define

void setup(){
}
void loop(){
}

Gruß Jens

von Sherlock 🕵🏽‍♂️ (rubbel-die-katz)


Lesenswert?

Im Mikrocontroller Bereich kommt meistens C zum Einsatz. Manchmal auch 
C++. Arduino ist ein Framework in C++. In deinem PlatformIO Plugin nutzt 
du offenbar das Arduino Framework.

> Gibt es vielleicht Tutorials für die Programmstruktur?

Sicher. Ich würde da aber eher Fachbücher empfehlen, wenn du das Thema 
schon nicht studierst. Suche nach Büchern, die das Wort "Patterns" im 
Namen haben. Die können ruhig alt sein sein. Gute Patterns sind wie 
Lösungen in Mathe, die gelten immer.

: Bearbeitet durch User
von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Jens schrieb:
> main.cpp

Also C++.
Eine mächtige und komplexe Sprache.
Wenn nicht sogar die mächtigste und komplexeste überhaupt.
Das ist gut, damit ist wohl alles überhaupt machbare auch machbar.

Jens schrieb:
> void loop()
Die Funktion ist schon eine FreeRTOS Task.

: Bearbeitet durch User
von Gregor J. (Firma: Jasinski) (gregor_jasinski)


Lesenswert?

Jens schrieb:
> Ich habe zuletzt in meiner Ausbildung Controller mit Assembler
> programmiert wo wir immer Verrücktes Labyrinth mit Goto und Labels
> gespielt haben. Die Struktur hat sich ja geändert und soweit ich es
> gelesen habe funktionieren diese Befehle noch, sind aber nicht mehr
> erwünscht / Zeitgemäß.

Bei den meisten Mikroprozessoren und µControllern gibt es in Assembler 
keine GOTOs, sondern nur Sprungbefehle (Jump) – bedingte, unbedingte, 
relative oder absolute Sprungbefehle. GOTO kommt aus der Basic-Ecke und 
wird seit vielen Jahren in z.B. 'Visual Basic' nicht mehr verwendet, da 
es durch die Strukturierte Programmierung ersetzt wurde, und in C# kann 
man sich das mit dem GOTO eh abschminken – das sollte man nicht 
miteinander verwechseln oder vermischen, auch wenn es für den Laien 
gleich klingt oder gleichgesetzt wird. Es gibt in C noch den 
Break-Befehl, der einem GOTO etwas ähnlich ist, dieser Abbruchsprung ist 
allerdings auf einen kleinen Block beschränkt, so dass die Regeln der 
strukturierten Programmierung im Wesentlichen nicht beeinträchtigt oder 
verletzt werden. Ferner sind die Sprungbefehle selbst nach 50 Jahren 
nach wie vor zeitgemäß, erwünscht und notwendig – auf Assemblerebene 
natürlich. Programmiert man auf der Abstraktionsebene darüber, hat man 
damit nichts mehr zu tun – die Vor- und Nachteile von 
GOTO-Nicht-Gebrauch in z.B. Visual Basic sollte man aber kennen, wenn 
man in Assembler und einer höheren Sprache programmieren kann, es nach 
wie vor tut oder früher mal getan hat. Modern sind in der Programmierung 
momentan z.B. Klassen, Vererbung, Überschreibung, Einkapselung, 
Multithreading etc, was aber gar nicht so einfach zu begreifen ist und 
nur eines von diesen Themen wird die meisten User hier überfordern – so 
etwas wird man aber beim µController selten brauchen, da diese in der 
Regel gar nicht die Ressourcen dafür haben.

von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Im großen und ganzen stimme ich dir zu

Gregor J. schrieb:
> Multithreading etc, was aber gar nicht so einfach zu begreifen ist und
> nur eines von diesen Themen wird die meisten User hier überfordern – so
> etwas wird man aber beim µController selten brauchen, da diese in der
> Regel gar nicht die Ressourcen dafür haben.
Hier habe ich Einwände

Irgendeine Form des (Kooperativen)Multitasking ist auch bei kleinen µC 
recht üblich. Auch wenn es manche nicht so nennen/sehen wollen.

z.B. Gerade  Goto und/oder Switch/Case kann man dafür prächtig 
einsetzen.

: Bearbeitet durch User
von Uwe E. (uexude)


Lesenswert?

Hallo,

unbedingte Sprünge sind für jemanden, der sich in einen fremden 
Quelltext einarbeitet ein absoluter Graus und nicht nötig 
(Spaghetti-Code) oder siehe ganz oben "Labyrinth".
@Jens Zur strukturierten Programmierung gibt es doch massenhaft Zeug zum 
lesen, wenn man schon nicht selbst drauf kommt.

Uwe

von Sherlock 🕵🏽‍♂️ (rubbel-die-katz)


Lesenswert?

Gregor J. schrieb:
> Es gibt in C noch den Break-Befehl, der einem GOTO etwas ähnlich ist

C hat auch einen GOTO Befehl. Sollte man aber nur sparsam einsetzen, 
wenn überhaupt. Die meisten C Programme kommen ohne GOTO aus.

von Daniel A. (daniel-a)


Lesenswert?


von Bruno V. (bruno_v)


Lesenswert?

Jens schrieb:
> Wie gesagt geht es mir hier um die Struktur wie man sowas aufbaut.

Kümmer Dich nicht darum. Wähle zuerst einen Ansatz:

1) Fertige Projekte, die in etwa das tun, was Du möchtest und die 
modifizieren.

2) Starte mit einer Blink-LED (eines Entwicklerboards) und erweitere das 
um Schalter, Taster, Display,

Bei 1) sind viele Sprachen denkbar. Von SPS (IEC61131) über C/C++ bis 
Python oder Pascal.

Bei 2) solltest Du C/C++ nehmen. Etwas anderes nur, wenn Du 
RL-Unterstützung oder Erfahrung hast.

2 ist ein langer und intensiver Weg. Du stößt auf viele Probleme und 
bist erst dann in der Lage, die Standardlösungen zu verstehen.

1 ermöglicht Dir schnelle Erfolgserlebnisse. Das Ändern des 
Blinkfrequenz durch ändern eine Zahl z.B..

Am Ende gibt es viele Gründe aufzugeben. Es ist ein langer Weg, so oder 
so. Springe ins kalte Wasser und bitte dann um Hilfe. Davor ist jeder 
gute Rat zur Programmstruktur vergebene Mühe.

von Uwe E. (uexude)


Lesenswert?

Hallo,

Jens schrieb:
> und mit Tastern durch verschiedene Anzeigen
> blättere.

z.B.
Sowas wie Menus; der Klassiker für bedingte Sprünge (if Taste A then...) 
usw.
So kannst Du Dich durch eine komplette Struktur hangeln.

Uwe

von Klaus F. (klaus27f)


Lesenswert?

Uwe E. schrieb:

1.)
> Menus;

2.)
> der Klassiker für bedingte Sprünge


Zu 1.)
Du meinst wohl entweder
  Menü
oder
  menues

Zu 2.)
Für Anfänger -- vielleicht.
Ansonsten ist  switch , case  bereits erfunden.
Im Kernighan Ritchie vor langer Zeit.

von Daniel A. (daniel-a)


Lesenswert?

Ich packe Menus immer in eine Datenstruktur. Ein Pointer / Stack für den 
momentanen Eintrag / welche Menus vorher kamen. Eine Funktion, um einen 
beliebigen Eintrag anzuzeigen. Eine Funktion für jeden Eintrag, für wenn 
er ausgewählt wurde.

Dann muss man auch nicht immer den bestehenden Code anfassen, wenn man 
mal einen Eintrag verschiebt, umbenennt, entfernt, oder hinzufügt. Alles 
nur Daten, und eigenständige Funktionen.

Und noch ein Vorteil: Ändert sich das Ausgabemedium / die 
Eingabemethoden, oder kommen weitere dazu, etc. kann man die selbe 
Datenstruktur / das selbe Menu, weiterverwenden.

: Bearbeitet durch User
von Jens (nichtdiemama)


Lesenswert?

Hallo
Habe zu euren Stichpunkten etwas im Internet gelesen und eifach mal was 
ausprobiert. Konnte es aber noch nicht auf dem Board testen. Wenn ihr 
wollt  könnt ihr ja mal drüber gucken.

Ist die Tastenerkennung so okay oder sollte man das lieber über ein 
Interrupt machen?

Nochmals vielen Dank für die schnellen Tips.
1
#include <Arduino.h>
2
#include <Wire.h>
3
#include <Adafruit_GFX.h>
4
#include <Adafruit_SSD1306.h>
5
6
#define SCREEN_WIDTH 128 // OLED display width,  in pixels
7
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
8
Adafruit_SSD1306 oled(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1); // create SSD1306 display object connected to I2C
9
10
#define BUTTON_PIN  22 // ESP32 pin GPIO22 connected to button
11
int prev_button_state; // the previous state of button
12
int button_state;     // the current state of button
13
14
int Page = 1;                 // variable: actual menu page
15
int maxPage = 2;              // variable: number of all pages
16
int lastPage = 0;              // variable: last pages
17
float Temperature = 0;        // variable: temperature
18
float maxTemperature = 0;     // variable: maximum temperature
19
float lastTemperature = 0;     // variable: last temperature
20
21
22
void setup() {  // put your setup code here, to run once:
23
24
  Serial.begin(9600);
25
26
  if (!oled.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {  // initialize OLED display with address 0x3C for 128x64
27
    Serial.println(F("SSD1306 allocation failed"));
28
    while (true);
29
  }
30
  delay(2000);                    // wait for initializing
31
  oled.clearDisplay();            // clear display
32
33
    pinMode(BUTTON_PIN, INPUT_PULLUP);            // set ESP32 pin to input pull-up mode
34
    button_state = digitalRead(BUTTON_PIN);       // read button state
35
36
  
37
}   // End of setup()
38
39
void loop() {   // put your main code here, to run repeatedly:
40
41
  prev_button_state = button_state;      // save the last state
42
  button_state = digitalRead(BUTTON_PIN); // read new state
43
44
  if (prev_button_state == HIGH && button_state == LOW) // detect pressed Button 
45
    Page ++;                                            // Page counter + 1
46
  if (Page > maxPage)                                   // check Page overflow
47
    Page = 1;                                           // set Page to 1
48
  
49
  // read temperature (comes here later)
50
  if (Temperature > maxTemperature)   // set maxTemperature
51
    maxTemperature = Temperature;
52
53
  if ((Page != lastPage)||(Temperature != lastTemperature)){  // checking if something changed
54
    switch (Page){
55
      case 1:                  // Page 1 Temperature
56
      oled.clearDisplay();     // clear display
57
      oled.println(Temperature);      // display Temperature
58
      oled.display();          // show on OLED
59
      break;
60
      case 2:                  // Page 2 maxTemperature
61
      oled.clearDisplay();    // clear display
62
      oled.println(maxTemperature);  // display maxTemperature
63
      oled.display();         // show on OLED
64
      break;
65
    } // End of switch()
66
    lastPage = Page;                    // set new lastPage
67
    lastTemperature = Temperature;      // set new lastTemperature
68
  } // End of if something changed
69
  
70
}   //End of loop()

von Sherlock 🕵🏽‍♂️ (rubbel-die-katz)


Lesenswert?

Da fehlt eine Entprellung des Tasters. In deinem konkreten Fall ist das 
vielleicht egal, wenn die Display-Ausgabe genug Zeit in Anspruch nimmt. 
Fall doch mal Pages übersprungen werden, erinnere dich an diesen 
Hinweis.

: Bearbeitet durch User
von Uwe E. (uexude)


Lesenswert?

Klaus F. schrieb:
> Zu 1.)
Danke.

> Zu 2.)
Nur ein Beispiel (siehe GOTO).

von A. B. (funky)


Lesenswert?

https://www.amazon.de/dp/1838826734

Kann dir das Buch empfehlen. Wird dich zu Beginn erschlagen, aber da 
sind die Strukturen & Feinheiten und worauf man achten muss ganz gut 
erklärt.

Irgendwelche delays sind für Mini Demos wo mal ne Temperatur aus einem 
Sensor gelesen wird ja gut & schön, in der realen Welt dann aber doch 
Murks wie schon gesagt.
Andererseits wird es für Anfänger eben doch erstmal fix relativ komplex, 
da man sich mit Interrupts, einem Rtos oder state machines 
auseinandersetzen muss während dir der nächste erzählt, dass auch goto 
durchaus ne valide Option ist und du das überhaupt nicht einordnen 
kannst

von Jens (nichtdiemama)


Lesenswert?

Danke für den Tipp. Werde mir das mal angucken.

Wollte es mal auf dem Board testen bekomme aber diesen Fehler.

 #include <Adafruit_Sensor.h>
          ^~~~~~~~~~~~~~~~~~~
compilation terminated.
Compiling .pio/build/esp32dev/FrameworkArduino/FirmwareMSC.cpp.o
*** [.pio/build/esp32dev/lib53a/DHT sensor library/DHT_U.cpp.o] Error 1
====================================================== [FAILED] Took 
3.64 seconds ======================================================

 *  The terminal process "platformio 'run', '--environment', 'esp32dev'" 
terminated with exit code: 1.
 *  Terminal will be reused by tasks, press any key to close it.

von Jens (nichtdiemama)


Lesenswert?

Hat sich erledigt musste nur die #include <Adafruit_Sensor.h> 
hinzufügen.
manchmal ist man echt blind.

von Marci W. (marci_w)


Lesenswert?

Jens schrieb:
> Welche Sprache verwendet wird bin ich mir ehrlich gesagt nicht zu 100%
> sicher.

C(++), Punkt!

OK OK, zum Einstieg und für einfache Sache ist auch MicroPython ganz 
brauchbar.

ciao

Marci

: Bearbeitet durch User
von Marci W. (marci_w)


Lesenswert?

Bruno V. schrieb:
> 2 ist ein langer und intensiver Weg. Du stößt auf viele Probleme und
> bist erst dann in der Lage, die Standardlösungen zu verstehen.

Na ja, ist vllt. ein wenig zu drastisch dargestellt. Selbst mit dem 
ESP-IDF kommt man doch recht schnell voran. Wenn man natürlich "from 
scratch" arbeitet (Controller komplett selbst initialisieren etc.), das 
Ganze natürlich in Assembler, ja, dann ist die Lernkurve bei modernen uC 
a bissle arg steil ;-)
Bei einfachen Atmel-Chips geht das noch! Habe meine ersten 
Atmel-Experimente auf Breadboards gebastelt, in Assembler programmiert 
(ohne Lib oder fertigen Startup-Code), und gleich dazu noch ein 
Download-Tool, bei dem ich die parallele Schnittstelle meines PC 
zweckentfremdet habe. Aber schon bei Cortex-M3 wird das ein Kraftakt.

ciao

Marci

von Sherlock 🕵🏽‍♂️ (rubbel-die-katz)


Lesenswert?

Marci W. schrieb:
> Aber schon bei Cortex-M3 wird das ein Kraftakt.

Bei mir kam der Punkt mit Ethernet und USB. Das ist die Stelle, wo ich 
akzeptieren musste, dass ich es nicht mehr alleine programmieren kann, 
sondern auf fremden Code aufbauen muss. Darauf verzichten will ich 
nicht.

Ich fürchte nur, dass durch die Verwendung von viel fremden Code Fehler 
entstehen, die man nicht erklären und lösen kann. Beruflich programmiere 
ich das Backend von Web Anwendungen. Manchmal sage ich "Ich verstehe nur 
zum Bruchteil, was ich da mache". Vor 30 Jahren wäre das ein 
Kündigungsgrund gewesen. Heute lächelt mein Chef verständnisvoll und 
sagt: "Keine Sorge, du kennst wenigstens noch die Grundlagen. Damit bist 
du deinen Kollegen weit voraus". In meinen schlimmsten Visionen schauen 
wir in Zukunft machtlos zu, wie unsere Kartenhäuser zusammen fallen, 
weil keiner weiß, wie man es repariert (Gruß an Microsoft). Hoffentlich 
kommt es nicht dazu.

: Bearbeitet durch User
von Adam P. (adamap)


Lesenswert?

Sherlock 🕵🏽‍♂️ schrieb:
> Ich fürchte nur, dass durch die Verwendung von viel fremden Code Fehler
> entstehen, die man nicht erklären und lösen kann.

Kommt immer drauf an.
Wenn die Hersteller schon Code liefern, dann kann man den auch 
verwenden, falls er funktioniert.
Man möchte nicht unbedingt USB oder SD Karten Zugriff über HSMCI selbst 
schreiben.

Und ja, es ist möglich Code anzupassen bzw. zu korrigieren.
Das ASF für den SAM4E hatte z.B. ein paar Krankheiten im USB Code und 
bei der SD Karte haben Funktionen gefehlt die die Karte jedoch kann.
Dies dauert zwar bis man die Stellen findet, aber immer noch schneller 
als das Rad neu zu erfinden.

@Jens (nichtdiemama)
Jeder Anfang ist schwer, aber es wird mit der Zeit einfacher ;)
Und wenn ich mir heute Code von früher anschaue...OMG, Asche über mein 
Haupt.

: Bearbeitet durch User
von Rolf (rolf22)


Lesenswert?

Sherlock 🕵🏽‍♂️ schrieb:
> Da fehlt eine Entprellung des Tasters. In deinem konkreten Fall ist das
> vielleicht egal, wenn die Display-Ausgabe genug Zeit in Anspruch nimmt.

Da Prellen auch beim Loslassen des Tasters auftreten kann (wenn auch 
diese Gefahr geringer ist), spielt es keine Rolle, wie schnell die 
Anzeigefunktion abgearbeitet wird. Wenn der Nutzer den Daumen zu lange 
draufhält, kann beim Loslassen ein weiterer Tastendruck erkannt werden.

von Rolf (rolf22)


Lesenswert?

Jens schrieb:
> Ist die Tastenerkennung so okay oder sollte man das lieber über ein
> Interrupt machen

Interrupts sind in diesem Fall völlig überflüssig und machen die 
Programmierung fehleranfälliger. Wenn es komplizierter werden soll, z. 
B. wenn kurze und lange Tastendrücke unterschieden werden sollen, kann 
man anders darüber denken.

"int buttonState" ist aber nicht gut. Da es nur zwei Zustände gibt, 
nimmt man "bool buttonIsdown" und fragt auf true bzw. false ab. Das 
ändert nichts an der Funktion, macht es aber klarer lesbar und 
verhindert manche Fehler.
Hauptregel: Defensiv programmieren. Also nicht nach dem Motto "Das 
funktioniert so, also lasse ich es so", sondern nach dem Motto "Wenn es 
'richtig' oder 'besonders richtig' geht, wählt man 'besonders richtig'". 
Außer, wenn Einschränkungen bezüglich 
Speicherplatz/Geschwindigkeit/Stromverbrauch beachtet werden müssen.

: Bearbeitet durch User
von A. B. (funky)


Lesenswert?

Taster/Schalter würde ich nie per Interrupt auswerten. Aufgrund von 
prellen nur ein Rattenschwanz an Problemen.

Einfach alle x ms mal die Tasten einlesen und dann auswerten

von Rolf (rolf22)


Lesenswert?

A. B. schrieb:
> Taster/Schalter würde ich nie per Interrupt auswerten.
> Einfach alle x ms mal die Tasten einlesen und dann auswerten

Wofür man dann oft einen Timer-Interrupt braucht.   :-)

von A. B. (funky)


Lesenswert?

jaaaaahaaa, GPIO Interrupt um es zu präzisieren :P

von Adam P. (adamap)


Lesenswert?

Rolf schrieb:
> Wofür man dann oft einen Timer-Interrupt braucht.   :-)

A. B. schrieb:
> jaaaaahaaa, GPIO Interrupt um es zu präzisieren :P

Kann man machen wie man mag,
doch beides zusammen ist ganz praktisch:
SysTick in ms + GPIO Interrupt
Damit kannst du dir jedes beliebige Szenario zusammenbasteln das dir in 
Sinn kommt :-)

- long button press
- double click
- ...

: Bearbeitet durch User
von Bruno V. (bruno_v)


Lesenswert?

@Jens: Vergiss das mit dem Tasterprellen fürs Erste. Ja, es gibt dazu 
viel Hilfe hier und Peder D.s Routinen sind genial und oft verwendet. 
Aber sinnlos für einen Anfänger.

Als Anfänger reicht es völlig aus, eine "Zykluszeit" von t = 50 ... 200 
ms zu nehmen (z.B. 100ms) und fertig.

Alle 100ms schaust Du, ob die Taste T sich geändert hat, z.B. so:
1
...
2
static bool T_alt;
3
bool T;
4
5
   /* hier kommst Du alle 100ms vorbei */
6
   bool T = GetTasteT(); /* Ein Code um eine Taste auszuwerten */
7
   if(T)
8
   {
9
       ... /* Taste ist gedrückt und noch nicht losgelassen */
10
       if(!T_alt) {...} /* Taste wurde jetzt gedrückt (falls erforderlich) */      
11
   } 
12
   else
13
   {
14
       ... /* Taste ist losgelassen */
15
       if(T_alt) {...} /* Taste wurde jetzt losgelassen (falls erforderlich) */
16
   } 
17
   T_alt = T; /* speichern des aktuellen Zustands als "letzten" */
18
}
Tastenprellen ist damit vollständig verhindert. Die drei Nachteile 
(~50 ms Verzögerung, keine Spike-Filter und "träge" Tasten) sind die 
ersten Jahre egal.

: Bearbeitet durch User
von Oliver S. (oliverso)


Lesenswert?

Bruno V. schrieb:
> Als Anfänger reicht es völlig aus,

Gerade als Anfänger sollte man es doch ganz einfach gleich richtig 
machen, vor allem, weil der Aufwand dafür minimal ist.

Oliver

von Sherlock 🕵🏽‍♂️ (rubbel-die-katz)


Lesenswert?

Manche Fehler muss man (ich) selbst ausprobieren, um den Nutzen der 
Lösung zu begreifen.

: Bearbeitet durch User
von Bruno V. (bruno_v)


Lesenswert?

Oliver S. schrieb:
> Gerade als Anfänger sollte man es doch ganz einfach gleich richtig
> machen, vor allem, weil der Aufwand dafür minimal ist.

Genau. Und es ist ja einfach und richtig.

Warum auch nur eine Stunde auf Interrupts und Filter verschwenden, um 10 
statt 3 Tastendrücke pro Sekunde zu erlauben.

Das schlimmste beim Erforschen neuer Dinge (Geräte, Sprache, SW, 
Handwerk, Instrument) ist der gutgemeinte Rat, es doch lieber sofort 
richtig oder gar nicht zu machen.

Beim zweiten oder dritten Projekt oder wenn die Bedienung hakelt, kann 
er sich immer noch mit dieser Wissenschaft für sich beschäftigen, die 
wir uns heute aus dem Ärmel schütteln.

von Jens (nichtdiemama)


Lesenswert?

Hallo

Habe etwas weiter gemacht und es auf den ESP übertragen. Leider habe ich 
bisher zwei Fehler.

1. das °C wird unter der Temperatur angezeigt.
2. es wird kein Tastendruck erkannt. Sireal Monitor zeigt durchgehend 0.
1
#include <Arduino.h>
2
#include <Wire.h>
3
#include <Adafruit_GFX.h>
4
#include <Adafruit_SSD1306.h>
5
#include <Adafruit_Sensor.h>
6
#include <dht.h>
7
8
#define SCREEN_WIDTH 128       // OLED display width,  in pixels
9
#define SCREEN_HEIGHT 64       // OLED display height, in pixels
10
Adafruit_SSD1306 oled(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);    // create SSD1306 display object connected to I2C
11
12
#define BUTTON_PIN  2          // ESP32 pin GPIO2 connected to button
13
14
#define DHTPIN 4               // Digital pin connected to the DHT sensor
15
#define DHTTYPE DHT11          // DHT 11
16
DHT dht(DHTPIN, DHTTYPE);
17
18
bool prev_button_state;         // the previous state of button
19
bool button_state;              // the current state of button
20
int Page = 1;                  // variable: actual menu page
21
int maxPage = 3;               // variable: number of all pages
22
int lastPage = 0;              // variable: last pages
23
float Temperature = 0;         // variable: temperature
24
float maxTemperature = 0;      // variable: maximum temperature
25
float lastTemperature = 0;     // variable: last temperature
26
float Humidity = 0;            // variable: temperature
27
float maxHumidity = 0;         // variable: maximum temperature
28
float lastHumidity = 0;        // variable: last temperature
29
30
void setup() {                 // put your setup code here, to run once:
31
32
  Serial.begin(9600);
33
  dht.begin();
34
35
  if (!oled.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {      // initialize OLED display with address 0x3C for 128x64
36
    Serial.println(F("SSD1306 allocation failed"));
37
    while (true);
38
  }
39
40
  delay(2000);                                     // wait for initializing
41
  oled.setTextColor(WHITE);                        // set text color
42
  oled.clearDisplay();                             // clear display
43
    pinMode(BUTTON_PIN, INPUT_PULLUP);             // set ESP32 pin to input pull-up mode
44
    button_state = digitalRead(BUTTON_PIN);        // read button state
45
  
46
}   // End of setup()
47
48
void loop() {   // put your main code here, to run repeatedly:
49
50
  prev_button_state = button_state;                 // save the last state
51
  button_state = digitalRead(BUTTON_PIN);           // read new state
52
  Serial.println(button_state);
53
54
  if (prev_button_state == true && button_state == false) // detect pressed Button 
55
    Page ++;                                              // Page counter + 1
56
  if (Page > maxPage)                                     // check Page overflow
57
    Page = 1;                                             // set Page to 1
58
  
59
  Temperature = dht.readTemperature();                     // read temperature to Temperature
60
  Humidity = dht.readHumidity();                           // read humidity to Humidity
61
  if (isnan(Humidity) || isnan(Temperature))               // check ability to read sensor
62
    Serial.println("Failed to read from DHT sensor!");
63
  
64
65
  if (Temperature > maxTemperature)   // set maxTemperature
66
    maxTemperature = Temperature;
67
  if (Humidity > maxHumidity)         // set maxHumidity
68
    maxHumidity = Humidity;
69
70
  if ((Page != lastPage)||(Temperature != lastTemperature)){  // checking if something changed
71
    switch (Page){
72
      case 1:                                 // Page 1 Temperature
73
      oled.clearDisplay();                    // clear display
74
      oled.setTextSize(1);                    // set text size
75
      oled.setCursor(0, 0);                   // set position to display
76
      oled.println("Temperature:");           // display Temperature
77
      oled.setTextSize(1);                    // set text size
78
      oled.setCursor(0, 9);                   // set position to display
79
      oled.println(Temperature);              // display Temperature
80
      oled.print(" ");                        // display the space
81
      oled.setTextSize(1);                    // set text size
82
      oled.cp437(true);                       // ?
83
      oled.write(167);                        // ?
84
      oled.setTextSize(1);                    // set text size
85
      oled.println("C");                      // display the C
86
      oled.setTextSize(1);                    // set text size
87
      oled.setCursor(0, 33);                  // set position to display
88
      oled.println("Temperature maximum:");   // display Temperature
89
      oled.setTextSize(1);                    // set text size
90
      oled.setCursor(0, 42);                  // set position to display
91
      oled.println(maxTemperature);           // display Temperature
92
      oled.print(" ");                        // display the space
93
      oled.setTextSize(1);                    // set text size
94
      oled.cp437(true);                       // ?
95
      oled.write(167);                        // ?
96
      oled.setTextSize(1);                    // set text size
97
      oled.println("C");                      // display the C
98
      oled.display();                         // show on OLED
99
      break;
100
      case 2:                              // Page 2 maxTemperature
101
      oled.clearDisplay();                 // clear display
102
      oled.setTextSize(1);                 // set text size
103
      oled.setCursor(0, 0);                // set position to display
104
      oled.println("Humidity:");           // display Temperature
105
      oled.setTextSize(2);                 // set text size
106
      oled.setCursor(0, 9);                // set position to display
107
      oled.println(Humidity);              // display Temperature
108
      oled.setTextSize(1);                 // set text size
109
      oled.setCursor(0, 33);               // set position to display
110
      oled.println("Humidity maximum:");   // display Temperature
111
      oled.setTextSize(2);                 // set text size
112
      oled.setCursor(0, 9);                // set position to display
113
      oled.println(maxHumidity);           // display Temperature
114
      oled.display();                      // show on OLED
115
      break;
116
      case 3:
117
      oled.clearDisplay();                     // clear display
118
      oled.setTextSize(1);                     // set text size
119
      oled.setCursor(0, 0);                    // set position to display
120
      oled.println("Dies ist der Test Text");  // display Temperature
121
      oled.startscrollleft(0, 1);              // Scrolls the text
122
      break;
123
    } // End of switch()
124
    lastPage = Page;                    // set new lastPage
125
    lastTemperature = Temperature;      // set new lastTemperature
126
    lastHumidity = Humidity;            // set new lastHumidity
127
  } // End of if something changed
128
  
129
}   //End of loop()

von Oliver S. (oliverso)


Lesenswert?

Bruno V. schrieb:
> Genau. Und es ist ja einfach und richtig.

Bruno V. schrieb:
> Die drei Nachteile
> (~50 ms Verzögerung, keine Spike-Filter und "träge" Tasten) sind die
> ersten Jahre egal.

Warum sollten sie das sein?

Oliver

von Peter D. (peda)


Lesenswert?

Jens schrieb:
> 2. es wird kein Tastendruck erkannt.

Siehste!
Das kommt davon, wenn man alles gleichzeitig und selber machen will.

Nimm erstmal nur 2 Tasten und 2 LEDs. Wenn die LEDs einwandfrei toggeln, 
dann mache ein Delay 1s in die Mainloop, ob dann alles immer noch geht.
Und erst dann kannst Du das in eine Lib packen und diese in größeren 
Projekten aufrufen. Dann braucht man den ganzen Salm nicht immer wieder 
neu zu erfinden und er klaut auch keine Rechenzeit oder hat 
Seiteneffekte.

von Jens (nichtdiemama)


Lesenswert?

Problem mit dem Taster ist behoben.
Keine Ahnung warum aber GPIO 2 konnte ich nicht verwenden. Habe es jetzt 
auf GPIO 5 geändert und es funktioniert sehr gut.

Den Fehler dass die °C nicht hinter der Temperatur sondern unter ihr 
angezeigt wird, habe ich noch nicht gefunden. Auf der zweiten Seite gibt 
es auch ein Anzeigefehler der Luftfeuchtigkeit. An der zweiten Stelle 
wird die Zahl nicht richtig angezeigt.

Falls ihr noch ein Tipp für mich hättet wäre ich sehr dankbar.

Werde den code der Anzeige mal in einem seperaten Programm testen.

Gruß Jens

von Rolf (rolf22)


Lesenswert?

Jens schrieb:
> bool button_state;

Du hast jetzt "bool" statt "int" genommen, gut, das ist besser.

Trotzdem weiß der Leser im eigentlichen Programmtext in der ersten 
Sekunde nicht, was das genau meint.
Deswegen hatte ich "buttonIsDown" vorgeschlagen, das ist eindeutig, ganz 
gleich, wie die Pegel sind und ob es womöglich z. B. den Zustand 
"buttonLongPress" gibt

von A. B. (funky)


Lesenswert?

Jens schrieb:
> Den Fehler dass die °C nicht hinter der Temperatur sondern unter ihr

println gibt ein carriage return mit aus aka ab in die nächste Zeile. 
probiere es doch mal mit print

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.