Ich steuere mit einem ESP32 4 DC Motoren (12V). Jeder Motor wird mit einer BTS7960 43A H-bridge gesteuert, welche vom ESP32 via PWM kontrolliert wird. Die Zuleitungen von den H-Bridges zu den Motoren ist über 1mm2 Klingeldraht von 1-3m Länge realisiert. Die Motoren werden von einem 14.4V Akku angetrieben.
Das hat wunderbar funktioniert, so lange ich den ESP32 über einen kleinen Webserver am Handy bedient habe. Nun wollte ich die Schaltung mit 3 Buttons ergänzen, was auf dem Breadboard auch problemos funktioniert hat.
Nachdem ich alles eingebaut hatte, gab es auf den GPIOs für die Buttons massenweise Geisterinputs. Die Buttons sind ca 1.5m vom ESP32 entfernt. Die Ursache war dann auch rasch gefunden: Die Zuleitungen für die DC Motoren und die Buttons liegen im gleichen Kabelkanal. Jedesmal wenn ein Motor ein-/auschaltet haben auch die GPIOs der Buttons reagiert.
Ich werde die Kabel für die Buttons getrennt verlegen, ev. genügt das ja schon.
Aber wenn ich schon mal dran bin: So viel ich ergoogelt habe wäre es sinnvoll vor den GPIOs einen physischen Pullupwiederstand und einen Kondensator einzubauen. Im Anhang seht ihr ein Foto der Schaltung und wie ich mir die Entstöhrung vorgestellt habe. Macht das so Sinn?
Ev. gibt es ja noch bessere Lösungen. Ich habe z.B. noch ein paar NE555 rumliegen. Oder die Buttons mit einem ESP8266 auslesen und mit I2C Verbinden?
Nur weil man eine Funktion debounce nennt, entprellt sie noch lange nicht. Ich sehe darin nichts, was entprellen könnte.
Eine Entprellfunktion muß ja Tastenänderungen sicher erkennen, d.h. braucht für jede Taste eigene Variablen zur Filterung.
Bewährt hat sich eine Filterung über 4 oder mehr gleiche Zustände einer Taste je Zeitintervall (z.B. 10ms).
Zusätzliche Hardware ist dann unnötig und die CPU-Last ist auch lächerlich gering.
Gut möglich, das es hier noch Verbesserungspotential gibt. Aber
Entprellung ist hier nicht das Problem.
Natürlich ist die fehlende Entprellung Dein Hauptproblem. Es gibt leider auch viele gewerbliche Programmierer, die das nicht einsehen wollen und der Benutzer muß dann darunter leiden (Aufzüge, Kaffeemaschinen, Oszilloskope und und und).
Nun wollte ich die Schaltung mit 3 Buttons ergänzen
Du darfst die Dinger hier gerne "Taster" nennen ;-)
Nachdem ich alles eingebaut hatte, gab es auf den GPIOs für die Buttons
massenweise Geisterinputs.
Wie sehen die "Geisterinputs" aus und wieviel Energie muss so eine Störung mitbringen, damit der ESP einen Puls erkennt. Vielleicht ist dein Eingang einfach zu hochohmig. Gegen kurze Störungen hilft ein Kondensator, aber bitte mit Serienwiderstand zur Strombegrenzung. Sonst gibt es jedes Mal beim Schließen des Kontaktes einen Funken, der langfristig deine Kontaktoberfläche verbrennt.
Ich bevorzuge immer die Entprellung/Entstörung eines ganzen IO-Ports per vertical Counter. Dann muß man nicht umständlich für jede Taste den Code copy & pasten. Das Main wählt dann einfach per Bitmaske aus, welche Taste man an einer Stelle gerade auswerten will.
Ungenutze entprellte IO-Pins kosten weder Code noch RAM.
Daß alle Pins parallel entprellt werden, macht es auch leicht, Zusatzfunktionen (repeat, lang/kurz Erkennung) zu implementieren.
So viel ich ergoogelt habe wäre es sinnvoll vor den GPIOs einen
physischen Pullupwiederstand und einen Kondensator einzubauen. Im Anhang
seht ihr ein Foto der Schaltung und wie ich mir die Entstöhrung
vorgestellt habe. Macht das so Sinn?
Ja. Die internen Pull-Up Widerstände im IC sind für die meisten Anwendungen zu hochohmig. Wegen Funkstörungen, aber auch weil die Kontakte der Taster ohne gemug Last nicht lange zuverlässig arbeiten. Gehe da ruhig auf 1k oder 2,2k Ohm runter.
Ich würde noch 100 Ohm in Reihe zu den Tastern ergänzen, damit der Entladestrom (aus den Kondensatoren) nicht die Kontakte der Taster überfordert.
aber auch weil die
Kontakte der Taster ohne gemug Last nicht lange zuverlässig arbeiten.
Das betrifft nur Taster (250VAC/10A) für Grobmotoriker.
Übliche Steuertaster (30V/100mA) brauchen keinen Frittstrom.
Aber wie gesagt, wozu noch externe Hardware dranpappen, wenn ordentliche Firmware völlig ausreichend ist.
Software schreibt man nur einmal, Hardware muß man jedesmal einlöten.
Und als Lib ist das besonders komfortabel, man kann sie einfach einbinden und muß nicht jedesmal erneut darüber nachdenken, wie man eine Flanke erkennt usw.
Also mit C++ werde ich wohl nie warm werden. Man braucht Stunden, um sich durch die ganzen Abhängigkeiten der h- und cpp-Files durchzukämpfen, was wann passieren soll und wo.
Das debounce prüft nur, ob zwischen 2 Aufrufen eine genügend lange Zeit vergangen ist. Wobei das nichtmal überlauffest passiert. Erfolgt dazwischen ein Überlauf, wird die Summe lastReadTime_ + DEBOUNCE_MILIS ganz klein und die Bedingung (fast) nie mehr wahr.
Das check wiederum prüft nur, ob nach der Zeit eine Flanke erfolgt, mag sie auch noch so kurz sein. Da es in der Loop permanent aufgerufen wird, reagiert es sogar sehr wahrscheinlich auf jede Störung.
Wie schon gesagt, eine Filterung (Test auf Plausibilität) erfolgt nirgends.
Ich habe mir schon was dabei gedacht, daß bei mir die Filterung mit einem festen Zeitintervall im Timerinterrupt (Systick) erfolgt. Somit ist die Entprellung vollkommen unabhängig von jeglichen Programmlaufzeiten. Es werden sogar Tastendrücke gespeichert, wenn mal andere Tasks etwas länger beschäftigt sind und dadurch die Loop anhalten. Es gehen also keine Tastendrücke verloren, was auch ganz schön nervig für den Benutzer sein kann.
Ich hatte auch erst mit nur 2 Samples gefiltert und dabei gemerkt, daß das noch nicht zuverlässig entprellt bzw. entstört. Daher habe ich den Code auf 4 gleiche Samples erweitert.
Die Idee, zu den Tasten Callbacks zu hinterlegen, ist an sich nicht schlecht. Nur hat man oft Mehrfachbelegungen auf den Tasten und dann wird der ständige Wechsel der Callbacks schnell unübersichtlich und fehleranfällig. Ich bevorzuge daher, daß die gerade auszuführende Aktion direkt im jeweiligen Kontext steht und nicht irgendwo völlig anders.
Im Anhang seht ihr ein Foto der Schaltung und wie ich mir die
Entstöhrung vorgestellt habe. Macht das so Sinn?
Nein, auch damit bekommst du irgendwelche Störungen (ohne 'h') nicht weg. Zumindest nicht zuverlässig. Und zeichen das nächste Mal besser einen Schaltplan deiner Schaltung. Du brauchst ihn sowieso, dass du in einem halebn Jahr noch verstehst, was du da gebastelt hast.
Entprellung/Entstörung so eines Signals macht man in Software. Nicht in Hardware.
Oder die Buttons mit einem ESP8266 auslesen und mit I2C Verbinden?
Abgesehen vom technischen Overkill ist das dann das Prinzip Hoffnung. Meinst du, die Störungen, die sich derzeit auf deinen Tasterleitungen finden, verschwinden dann auf dem I²C auf magische Weise?
Ich werde die Kabel für die Buttons getrennt verlegen, ev. genügt das ja
schon.
Bis irgendwer in der Nähe eine Lampe oder gar den Mixer einschaltet...
So viel ich ergoogelt habe wäre es sinnvoll vor den GPIOs einen
physischen Pullupwiederstand
Vorneweg: es ist ein Widerstand mit nur 1 'e' hinter dem 'd'.
Wenn du bisher tatsächlich nur den eingebauten sehr hochohmigen 30..50k Pullup verwendest, dann wäre es zumindest nötig und sicher hilfreich, extern einen niederohmigen im Bereich 470R..1k an den Portpin zu schalten.
Insofern würde dein Workaorund mit dem I²C sogar "besser" funktionieren als die Schaltung bisher, denn der I²C hat Pukkups zwischen 2k2 und 4k7.
Das debounce prüft nur, ob zwischen 2 Aufrufen eine genügend lange Zeit
vergangen ist.
Richtig, der Name debounce() für diese Funktion ist absolut irreführend, das ist ein simples (nichtsperrendes) Delay aufwändig formuliert.
Somit ist die Entprellung vollkommen unabhängig von jeglichen
Programmlaufzeiten.
Wenn man mit millis() arbeitet, dann bekommt man das genausogut (wenn auch nicht so kompakt) hin. Nur muss man eben den Taster auch tatsächlich mehrfach auf den selben Wert abfragen. Und nur, wenn der eingelesene Wert mehrmals (3..5x) innerhalb von z.B. 40ms immer gleich ist, dann liegt auch wirklich ein stabiler Wert am µC-Eingang an.
Ich hab ja doch riesen Probleme, mich durch C++ durchzufitzen.
Und es wird auch nirgends kommentiert, wie der eigentliche Ablauf ist.
Ich sehe nur, daß durch die Tasten irgendwelche Callbacks aufgerufen werden sollen, aber dann komme ich nicht mehr weiter.
Versteht irgend jemand, wie sich die 4 Motoren mit den 3 Tasten steuern lassen und wo in dem ganzen Wust dieser Ablauf codiert ist?
Die .h Dateien kannst du für den Ablauf erstmal ignorieren.
Den Ablauf sieht man in der main.cpp ganz unten. Das Programm läuft in einem “loop” und prüft dauernd ob einer der Buttons gedrückt oder losgelassen wurde(was keine Auswirkung hat).
Wenn ein Button gedrückt wurde, wird die Funktion in Zeile 197 ausgeführt, welche den jeweiligen Motor in eine der 3 “Richtungen” (vorwärts, rückwärts und aus) drehen lässt.
Wenn ein Button gedrückt wurde, wird die Funktion in Zeile 197
ausgeführt, welche den jeweiligen Motor in eine der 3 “Richtungen”
(vorwärts, rückwärts und aus) drehen lässt.
Das bringt mich leider auch nicht weiter. Welche der 3 Tasten bewirkt was?
Wenn ich 4 Motoren mit 3 Tasten steuern sollte, würde ich das so implementieren:
Taste 1: toggle Motor 1, Motor 2, Motor 3, Motor 4
Taste 2: toggle PWM 25%, 50%, 75%, 100%
Taste 3: toggle Vorwärts, Stop, Rückwärts, Stop
Kann es sein, daß mit den 3 Tasten nur die Richtung und Stop für alle 4 Motoren gleichzeitig gesetzt werden kann?
Kann es sein, daß mit den 3 Tasten nur die Richtung und Stop für alle 4
Motoren gleichzeitig gesetzt werden kann?
Ich seh in dem Code keine 3 Buttons, nur 2. Und einer davon steuert alle 4 Motoren in die eine, der andere Button alle 4 in die andere Richtung… wenn kein Button gedrückt ist bleiben die Motoren stehen.