Hallo zusammen, ich bin Neuling und habe mir gerade einen Arduino Uno Rev.3 bestellt um einen intelligenten Kundenzähler zu bauen.
Ich betreibe ein mobiles Kinderattraktionsgerät, ein Bungee-Trampolin, welches man von Straßenfesten kennt. Dabei wird eine 230V-Motorwinde über zwei Druckknöpfe/Schalter zum rauf- und runterziehen der Springer gesteuert.
Zur besseren Kontrolle der tatsächlichen Springerzahlen, möchte ich zusätzlich einen Zähler im Gehäuse verbauen, der bei einem bestimmten Impuls jeweils um eins nach oben zählt.
Wir hatten es bereits vorher mit einem Zeitrelais versucht, welches nur Strom für eine bestimmte Zeitdauer liefert, aber dieses war nicht wirklich praxisnah. Per Zufall kam ich nun auf den Arduino als intelligentere Steuerung und habe mir grob folgende Lösung erdacht:
Von den beiden 230V-Bedienschaltern werden jeweils parallel über einen USB-Ladeadapter 5V-Impulse auf zwei Controller-Eingänge übertragen.
Nach erfolgreichem Durchlauf der Programmierung erfolgt ein 5V-Impuls an das analoge Zählwerk, welches dann um eins nach oben zählt.
Die Schwierigkeit ist, dass ein Sprungzyklus, der ca. 5 Min. dauert, leider nicht eindeutig über die Schalter definierbar ist , da diese auch zwischendurch öfters zum Korrigieren betätigt werden oder dieser auch deutlich kürzer ausfallen kann.
Es lässt sich jedoch ein rel. eindeutiges Muster für den Beginn eines neuen Sprungs definieren:
Der vorhergehenden Springers wird zum Ende für mind. 3 Sek. abgelassen.
Danach folgt eine Pause für den Springerwechsel ohne jegliche Aktivität vom mind. 45 Sek.
Danach wird der neue Springer hochgefahren, und zwar für eine Dauer von mind. für 3 Sek.
Sind diese Bedingungen erfüllt, kann ein Impuls an den Zähler gesendet werden.
Dabei sollte man jedoch ausschließen, dass die jeweiligen mind. 3 Sek. Dauerimpulse durch ein kurzzeitiges Unterbrechen des Schaltvorgangs manipuliert werden können. Es sollte also jeweils diese 3 Sekunden Mindestdauer innerhalb eines längeren Zeitfensters gezählt werden, z.B. 6 Sek.
Soweit meine Überlegungen. Für konkrete Umsetzungsmöglichkeiten und auch Programmiervorschläge wäre ich euch sehr dankbar.
Mir ist die Aufgabe der gewünschten Anwendung noch nicht ganz klar..
Willst du nur die Menge der Sprünge/Sprungdurchgänge zählen? Quasi ein Spielrundenzähler?
Dann würde ich ohne zusätzliche Sensoren -wenn du die TastenSignale der Winde für AUF/AB als geschlossen/offen messen kannst- auf langen Tastendruck auswerten. Ich denke, einen Springer auf die Höhe zu ziehen dauert ja bestimmt länger als 3 Sekunden, danach noch etwas korrigieren (hoch/runter/hoch...) .. wenn danach 5 Sekunden keine Taste gedrückt wurde, hat der Sprungvorgang wohl begonnnen und der Zähler bekommt seinen Impuls.
Oder soll das zusätzlich ein Timer werden, der nach Beginn der Runde die 5 Minuten herunterzählt?
Was soll nach Beginn/Ende des Sprungvorgangs passieren? Nur ein Signal ausgeben (akustisch/optisch)?
Ich denke mal, das Heraufziehen und Hinablassen der Springer soll weiterhin manuell erfolgen und nicht vom Arduino selbst automatisch übernommen werden, oder?
Reden wir nur von einem einzelnen Sprunggerät oder ist das so eins mit mehreren Trampolinen?
Und natürlich ein herzliches Willkommen hier im Forum!
Man könnte auch an der Winde oder am Seil, das über eine Rolle mit Drehgeber läuft, messen, wie lange das Seil auf- und abgewickelt wird: lang abgerollt = Spielrunde startet .. wieder aufgewickelt = reset, neue Runde kann gestartet werden.
(Edit: wie doof, natürlich umgekehrt )
Auf die Weise wären auch Fehlinterpretationen der Tasterbetätigungen ausgeschlossen.
Ja, richtig, ein Spielrundenzähler. Ohne Timer und nur für einen einzelnen Platz.
Einen Springer hochzuziehen dauert normalerweise etwa 5 Sek. es sei denn man hat dort ein sehr kleine Kind, welches man nur wenig hochzieht, daher die 3 Sek. Minimum.
Tatsächlich wäre es eine Möglichkeit, einige Sekunden abzuwarten bis nach dem Hochziehen und ersten korrigieren erstmal nichts mehr passiert, um dann den Zählerimpuls zu senden.
Darauffolgend müssen dann die Eingänge vom Controller für ca. 5 Min. pausieren, damit keine weiteren Eingaben innerhalb des Zyklus fälschlicherweise verarbeitet werden
Da hast du doch schon die Bedingungen für deine "state machine" ( "endlicher Automat" oder wie immer du diese Art der Programmierung nennen willst).
Eventuell für alle Fälle noch einen Hand-Taster, der den Sprungzähler rücksetzt und sofort freigibt, falls der "intelligente Zähler" mal zu intelligent ist?
Ich hätte jetzt vermutet, das saubere Zählen der Sprünge sei der schwierigere Teil des Ganzen. Hat man darauf als Springer überhaupt einen wesentlichen Einfluss, bzw. ist es gerechter, die Anzahl Sprünge oder die Zeit im aktiven Modus zu zählen?
Mein Grundprogramm müsste dann wohl folgendermaßen aussehen:
Keine Eingangssignale seit mind. 30 Sek. (Pause zum Wechseln des Springers)
Danach wird der neue Springer hochgefahren, und zwar für eine Dauer von mind. für 3 Sek., aber innerhalb eines Zeitfensters von mind. 5 Sek. ( um kurzzeitige Unterbrechungen beim Betätigen des Schalters zu ignorieren.
Innerhalb der nachfolgenden 5 Sek. erfolgt kein RUNTER für eine Dauer von mind. 3 Sek. um sofortige Abbrecher auszusortieren.
Es folgt ein Ausgangssignal an das Zählwerk.
Da ich noch nicht weiß ich das genau programmieren soll, wäre ich sehr dankbar für Tipps, welches Programmiertool ich dafür am besten benutzen kann. Meine Programmierschulung mit COBOL liegt dafür schon zu lange zurück
Falls jemand den Code dafür sozusagen aus dem Ärmel schütteln kann, wäre das natürlich supertoll
So ein "Zustandsautomat" kennt mehrere Zustände. Das Programm läuft ewig, merkt sich (in einer oder mehreren globalen Variablen) in welchem Zustand es grade ist und entscheidet, in welchen es wechseln soll, weil sich was verändert hat ( Ein Signal hat einen andern Zustand, Zeit ist abgelaufen, ... ).
In C / C++ gibt es den Datentyp enum, der sich ideal dafür eignet, solchen Zuständen Namen zu geben, um das eigene Verständnis zu erleichtern .
"Aus dem Ärmel schütteln" können das einige, manche haben da auch Spaß dran, aber ich möchte dir das Erfolgserlebnis nicht schmälern.
Viel Spaß.
Bin gerade kurzärmelig unterwegs, wird also schwierig
Generell halte ich es für eine schlechte Idee, wenn Du ein fremdes Programm nutzt, weil Du es nicht anpassen kannst. Andererseits bist Du vermutlich alt genug, selbst die Konsequenzen abschätzen und tragen zu können. Wer Cobol gelernt hat, ist schätzungsweise uralt.
Ich biete Dir einen Versuch in prozeduralem C an, alles mit OOP ist in den Bibliotheken versteckt, die ich als fauler Mensch gerne nutze.
Programm
#define MAX8BUTTONS
#include <MobaTools.h>
const byte tasterPin [] = { 6, 7 }; // define pin numbers
const byte tasterCnt = sizeof(tasterPin);
MoToButtons Taster( tasterPin, tasterCnt, 30, 500 ); // Entprellzeit, langer Tastendruck
const byte zaehlPin = 8;
uint32_t jetzt = millis();
uint32_t vorhin = jetzt;
const uint32_t zeitPause = 30000; // Pause zum Wechseln des Springers in ms
const uint32_t zeit3sek = 3000; // in ms
const uint32_t zeit5sek = 5000; // in ms
const uint32_t zeitSignal = 500; // in ms
uint8_t schritt = 0;
void setup() {
Serial.begin(9600);
for (byte j = 0; j < tasterCnt; j++) {
pinMode(tasterPin[j], INPUT_PULLUP); // buttons must switch to Gnc
}
pinMode(zaehlPin, OUTPUT);
Serial.println("\nStarting loop");
}
void loop() {
enum {RUNTER, RAUF};
enum {S0, S1, S2, S3, S4, S5, S6};
Taster.processButtons(); // read and process buttons
jetzt = millis();
switch (schritt) {
case S0:
if ( Taster.pressed(RAUF) ) {
Serial.println("Taste rauf erstmalig gedrückt");
vorhin = jetzt;
schritt = S1;
}
break;
case S1:
if ( Taster.released(RAUF) ) {
Serial.println("Taste rauf losgelassen");
if (jetzt - vorhin >= zeit3sek) {
Serial.println("Zeit größer 3 Sek");
schritt = S2;
} else {
Serial.println("Zeit kleiner 3 Sek");
schritt = S0;
}
}
break;
case S2:
if ( Taster.pressed(RAUF) ) {
Serial.println("Taste rauf erneut gedrückt");
vorhin = jetzt;
schritt = S1;
}
if (jetzt - vorhin >= zeit5sek) {
Serial.println("Zeit größer 5 Sek, Zählimpuls an");
digitalWrite(zaehlPin, HIGH);
vorhin = jetzt;
schritt = S3;
}
break;
case S3:
if (jetzt - vorhin >= zeitSignal) {
Serial.println("Zählimpuls aus");
digitalWrite(zaehlPin, LOW);
vorhin = jetzt;
schritt = S4;
}
break;
case S4:
if ( Taster.pressed(RAUF) || Taster.pressed(RUNTER) ) {
Serial.println("Positionskorrektur oder Runterlassen");
vorhin = jetzt;
}
if (jetzt - vorhin >= zeitPause) {
Serial.println("Pausenzeit vorbei");
schritt = S0;
}
break;
default:
schritt = S0;
}
}
Ob das Programm tut, was Du Dir vorstellst, weiß ich nicht, mußt Du probieren.
Da Du mit dem Springen anderer Leute Geld verdienst, möchte ich Dich als Gegenleistung bitten, auch etwas springen zu lassen. Was und wieviel überlasse ich Deiner Großzügigkeit und Kreativität, muß ich auch nicht wissen. Eine schöne Idee finde ich beispielsweise, daß Menschen mit nicht soviel Geld im Januar ohne Formalitäten umsonst ins Miniaturwunderland eingelassen werden.
Ob es für Dich eine Lösung ist, weiß ich nicht, denn ich habe nur programmiert, was ich meine, verstanden zu haben. Möglicherweise habe ich auch noch Fehler drin, ich übernehme keinerlei Gewährleistung, für mich ist das Programmieren nur Vergnügen
Als Hardware könntest Du einen Nano einsetzen. Als einfach anzusteuernde Anzeige eignet sich ein LCD 2004 I2C Modul.
Alle Links dienen nur der Visualisierung und stellen keine Kaufempfehlung dar!
Es ist unklar, wie du die Pinbelegung für die Hoch/Runterschalter definiert hast. Pin Nr. 6 und 7 ist klar, aber welcher ist jeweils der for hoch und runter?
Ich beziehe mich auf mein Programm in #12.
Diese Zeilen wirken zusammen:
RUNTER ist 0 und RAUF ist 1, also bezieht sich if ( Taster.pressed(1) auf tasterPin [1], also Pin 7.
vielen Dank für Erklärungen, aber richtig verstanden habe ich es im Code nicht, warum RUNTER = 0 und RAUF = 1 ist.
Ich habe ja zwei verschiedene Taster, bei denen jeweils nur beim Drücken abwechselnd ein Signal übermittelt wird.
Dann lege ich auf PIN 6 also das Signal für runter und auf PIN 7 für auf, ist das richtig verstanden?
Da hast du recht.
Die Tatsache auszunutzen, dass enum { RUNTER, RAUF };
RUNTER den Wert 0 zuweist, ist mMn noch blöder als die verpönten Magic Numbers im Code.
Eine enum macht Sinn, wenn es sich um eine Aufzählung einiger Werte handelt, die sich unterscheiden, deren intern verwendeter Wert aber eigentlich irrelevant ist. (Meine Meinung)
Wenn RUNTER und RAUF nichts mit tasterPin [] = { 6, 7 }; // define pin numbers
zu tun hätten, ok.
Ob jetzt constexpr byte RUNTER = 0, RAUF = 1;
oder enum { RUNTER=0, RAUF=1};
schöner ist, ist mir egal. Dass es einem auf den Wert tatsächlich ankommt, wird jedenfalls klar.
Was mich seit dem Eröffnungspost schon interessiert: wie soll das genau passieren? Ich bin nur neugierig, wie man den doch mitunter heiklen 230V-Teil mit dem Arduino-Part sicher und fachmännisch korrekt verbinden will..
Hallo Geppi, vielen Dank für deine Bemerkung. Das wäre tatsächlich das nächste gewesen, was ich hier fragen wollte.
Ich muss ja von zwei 230-Volt-Tastern jeweils eine 5 V Verbindung zu den Ardulino-Eingängen legen, um zu detektieren, ob und wie lange ein ein Strom fließt.
Meine Idee wäre der Anschluß von jeweils einem Mini-Netzteil, um auf die 5 V runterzukommen.
Kann man dieses dann direkt an einen Eingang und Ground anschließen?
Das ganze befindet sich in einem wetterfesten Schaltkasten, die 230V-Verbindungen werden natürlich ordentlich isoliert.
Ich werde dazu keine verbindliche Aussage geben, da ich kein Elektriker bin
Ich verweise nur auf einen Thread mit gleicher Fragestellung. Hier einigt man sich auf eine Schaltung mit Optokoppler, der die beiden Stromkreise getrennt hält. Dafür gibt es wohl auch fertige Module, die man dafür einsetzen kann. Auch eine Kaufempfehlung werde ich hier nicht aussprechen
Aber ich frage mal in die Runde, ob man so einen dafür verwenden könnte: