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
Parallele Verarbeitung macht man mit Interrupts oder einem Scheduler / RTOS, bei dem in regelmäßig Abständen die Eingangssignale eingelesen, verarbeitet und ausgegeben werden.
N. M. schrieb: > Zu 95% der erste Fehler. Bei RTOS sind die Standard, tun aber was anderes als in diesem Fall.
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.
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
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.
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
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
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
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
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.
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
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
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.
Die gute Art, goto zu nutzen: https://www.kernel.org/doc/html/v6.12/process/coding-style.html#centralized-exiting-of-functions
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.
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
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.
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
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() |
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
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
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.
Hat sich erledigt musste nur die #include <Adafruit_Sensor.h> hinzufügen. manchmal ist man echt blind.
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
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
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
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
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.
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
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
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. :-)
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
@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
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
Manche Fehler muss man (ich) selbst ausprobieren, um den Nutzen der Lösung zu begreifen.
:
Bearbeitet durch User
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.
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() |
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
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.
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
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
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.