Servo zeitgesteuert verlangsamen

Hey liebe Community,
ich habe jetzt vor etwas über einer Woche mit der Arduino programierung angefangen durch ein Familienprojekt. Da ich absoluter Neuling in C/C++ und auch Elektrontechnik bin verstehe ich nicht unbedingt alles und versuche es mir durch try´n error selbst bei zu bringen mit Youtube und Foren durchlesen. Jedoch habe ich jetzt gerade den Punkt erreicht das ich seit einigen tagen nicht so wirklich das erziele was ich eigentlich haben möchte.

Jetzt aber erstmal zu meinem Problem.

Ich möchte einen Shotausschenker bauen, der nach drücken der Start-Taste (T1) einmal durchschaut welche der 4 weiteren Tasten belegt sind und somit welche Position angefahren werden muss.

Jetzt habe ich mich schonmal dran versucht das über eine Funktion (In meinem Code "Unterprogram" genannt) und bin mit delay und millis und berechnungen gerade derbe überfragt.

Hier nun mal mein Code. Ich habe absichtlich erstmal nur T1 & T2 verwendet um zu testen. die anderen Tasten ist ja meines wissens nach nur copy&paste mit ein paar änderungen zur tasterbelegung und position.

#include <Servo.h>

	// Definierung der Taster

		int T1 = 2; 						// Taste 1 Zyklus-Start
	// Variablen für Taster 1
		int T1_Status = 0;					// Statusanzeige

		int T2 = 4; 						// Taste 2 Servo Pos 1
	// Variablen für Taster 2
		bool T2_Aktiv = true;				//Variable, die den tasterzustand verfolgt

		int T3 = 7; 						// Taste 3 Servo Pos 2
	// Variablen für Taster 3
		bool T3_Aktiv = true;				//Variable, die den tasterzustand verfolgt

		int T4 = 8; 						// Taste 4 Servo Pos 3
	// Variablen für Taster 4
		bool T4_Aktiv = true;				//Variable, die den tasterzustand verfolgt

		int T5 = 12; 						// Taste 5 Servo Pos 4
	// Variablen für Taster 3
		bool T5_Aktiv = true;				//Variable, die den tasterzustand verfolgt

	// Definierung des Servos
		int servoPin = 9; 					// Servo-Pin
		Servo servo1;						// Name für den Servo
		
	//Angaben im Unterprogramm Servo
		/*int Pause =  200;					// Pausenzeit zwischen den Servo schritten
		int letzteBewegung = 0;				// Zeit der letzten bewegung
		
	// Definierung der Positionen
		int AktuellePos = 0;     			// Aktuelle Position
		int Pos0  =   0;					// Home Position*/
		
		int Pos1  =  20;					// Servoposition 1
		int Pos2  =  40;					// Servoposition 2
		int Pos3  =  60;					// Servoposition 3
		int Pos4  =  80;					// Servoposition 4
		
	// Definierung des Motors
		int motorPin = 5; 					// Motor-Pin

	// Definierung der LED
		int ledPin = 13;					// LED für Ausgabe des Startsignal

// Beginn vom Setup
		void setup()
		{
      Serial.begin(9600);
    		//Input Pins
			pinMode(T1, INPUT);				// Taster 1 ist Input
			pinMode(T2, INPUT);				// Taster 2 ist Input
			pinMode(T3, INPUT);      		// Taster 3 ist Input
			pinMode(T4, INPUT);       		// Taster 4 ist Input
			pinMode(T5, INPUT);       		// Taster 5 ist Input
  
				//Output Pins
			pinMode(motorPin, OUTPUT);		// Motorpin ist Output
			pinMode(ledPin, OUTPUT);		// LED-Pin ist Output
		}
	// Ende vom Setup



// Beginn Loop
		void loop()
		{
		// Wenn Taste 1 Aktiv, halte das Signal
			if(digitalRead(T1) == HIGH)
			{
				digitalWrite(ledPin, LOW);
				T1_Status = 1;
				Serial.println("T1 Aktiv");
			}
			
			else
			{
				digitalWrite(ledPin, HIGH);
				//T1_Status = 0;
				Serial.println("T1 nicht Aktiv");
				goto reset;
			}
		// Wenn Taste 1 & Taste 2 Aktiv, verbinde den Servo & fahre auf Position 1
			if((T1_Status == 1) && digitalRead(T2) == HIGH)
			{
				ArmRaus(Pos1);
			}
		// Abschalten von Taste 1
				reset:
				T1_Status = 0;
		// Servo zurücksetzen
				servo1.attach(servoPin);
				servo1.write(0);
				delay(500);
				servo1.detach();
				// Servo auf Position
		}
	// Ende vom Loop
	


// Unterprogramm Servo
		void ArmRaus(int Pos)
		{
			int Pause =  200;					// Pausenzeit zwischen den Servo schritten
			int letzteBewegung = 0;				// Zeit der letzten bewegung
			int AktuellePos = 0;     			// Aktuelle Position
			int Pos0  =   0;					// Home Position
			unsigned long currentMillis = millis();
			
		// Servo Start befüllen
			while(AktuellePos < Pos)
			{
				servo1.attach(servoPin);
				AktuellePos = AktuellePos + 1;
				servo1.write(AktuellePos);
				Pause;
			}
			AktuellePos = 0;
			servo1.detach();
		    Pumpe(1000);    		
			// Servo auf Position
		}
		// Unterprogramm Ende


 
// Unterprogramm Pumpe
		void Pumpe(int Dauer)
		{
		    // Motor Start
					digitalWrite(motorPin, HIGH);
					delay(Dauer);
					digitalWrite(motorPin, LOW);
					delay(2000);
				// Motor fertig
		}
		// Unterprogramm Ende

Wäre super wenn mir jemand recht Einsteigerfreundlich erklären könnte wo mein fehler liegt und was ich verbessern könnte.

Vielen dank schonmal im vorraus

E:\Programme\arduino\portable\sketchbook\sketch_feb17e\sketch_feb17e.ino: In function 'void ArmRaus(int)':
E:\Programme\arduino\portable\sketchbook\sketch_feb17e\sketch_feb17e.ino:119:9: warning: statement has no effect [-Wunused-value]
  119 |         Pause;
      |         ^~~~~
E:\Programme\arduino\portable\sketchbook\sketch_feb17e\sketch_feb17e.ino:108:11: warning: unused variable 'letzteBewegung' [-Wunused-variable]
  108 |       int letzteBewegung = 0;       // Zeit der letzten bewegung
      |           ^~~~~~~~~~~~~~
E:\Programme\arduino\portable\sketchbook\sketch_feb17e\sketch_feb17e.ino:110:11: warning: unused variable 'Pos0' [-Wunused-variable]
  110 |       int Pos0  =   0;          // Home Position
      |           ^~~~
E:\Programme\arduino\portable\sketchbook\sketch_feb17e\sketch_feb17e.ino:111:21: warning: unused variable 'currentMillis' [-Wunused-variable]
  111 |       unsigned long currentMillis = millis();
      |                     ^~~~~~~~~~~~~

Zudem gefallen mir die durchnummerierten Dinge nicht.
Und noch ein paar Sachen.

Ich hoffe, du hast die Elektronik nicht so zusammengebaut, wie auf deinem Schaltplan gezeichnet, der M1 hängt in der Luft, der M2 würde deinen Controller grillen, die Schalter sind seltsam verdrahtet....

Der Code ist leider auch nicht besser.

Sorry, aber ich würde empfehlen, anhand der zahlreich verfügbaren Beispiele und einer guten c++-Lektüre erstmal mit kleinen Übungsaufgaben das Programmieren an sich erlernen. Und um Elektronik selber zu entwerfen ist schon deutlich mehr Wissen erforderlich. Nicht umsonst ist das ein Studiengang.

Ansonsten einmal durchatmen und präzise beschreiben, was genau der Servo machen soll, wofür der M1 und M2 da ist, was das ganze mit zeitgesteuertem verlangsamen zu tun hat, welche Komponenten du hast, wie der Ablauf Schritt für Schritt sein soll etc.

Guten Abend @combie, danke für deine schnelle Nachricht. Jedoch werde ich da gerade nicht so wirklich schlau draus... Das "int Pause" ist ja am Ende in Verwendung genau so wie "unsigned long currentMillis" und bei mir hat das arduino IDE auch keine Fehler ausgespuckt. Das einzige Problem weshalb ich mich jetzt hier halt gemeldet habe war halt das der Servo halt so garnichts mehr tut..

Und wie genau meinst du das mit den durchnummeriert Dingen?

Das Projekt soll ein Modell von einer Zapfseule werden mit 4 Stellplätzen für Schnapsgläser drum herum. Diese werden dann auf Podeste gestellt welche dann einen Endschalter betätigen. Und mit dem drücken von taste 1 soll dann einfach nur freigegeben werden das der Servo fahren darf. Die zeitgesteuert verlangsamung einfach nur damit das Modell ggf keinen Schaden durch zu rückartige Bewegung erhält und der Schlauch zum befüllen der gläser nicht anfängt durch die Gegend zu spritzen wenn noch ein Tropfen im Schlauch sein sollte.

Aktuell habe ich den Schaltplan nur mit tinkercad gemacht um halbwegs etwas darstellen zu können. An meinem arduino Uno ist bisher nur T1 auf D2 gesteckt für das Signal und T2 auf D4. Der Servo bekommt über D9 sein Signal.

Die Taste für T1 habe ich an 5V und über einen 10K Wiederstand an Masse angeschlossen.
Den Endschalter für T2 habe ich an 5V, Maße und Signal geschaltet.
Der Servo ist ebenso an 5V und Masse angeschlossen.

Für den Motor habe ich mir eine 12v Pumpe sowie die dazu passende 12v batteriebox bestellt um das Board nicht zu grillen. Die habe ich aber noch nicht hier und bevor ich die anschließe möchte ich mich erst in Ruhe um die Steuerung von Servo kümmern.

Glaube den Meldungen!

Ich fasse mal zusammen:

  1. es gibt 4 Stellplätze für Gläser
  2. jeder Stellplatz hat einen Schalter, der durch das Glas betätigt werden soll
  3. es gibt einen zentralen Taster, der den Vorgang startet
  4. Der Servo soll den Schlauch zum Glas bringen? Was aber, wenn mehr als eins da steht?
  5. Die Pumpe soll dann was ins Glas pumpen? hast du den Sprit in einem Eimer? Wie lange soll denn die Pumpe laufen?
  6. Zum Schalten der Pumpe brauchst du mindestens noch ein Relais-Board
  7. Jeder schalter benötigt einen eigenen Eingang am Controller, in dem Schaltplan hast du sie alle parallel auf einen Eingang gelegt, damit kannst du sie nicht auseinanderhalten.

Hallo justplay_3d

Die Strategie von "teile und herrsche" hat sich bei größeren Projekten bewährt.

Hierbei wird das Projekt in logische Teilprojekte zerlegt, die einzeln programmiert und getestet werden können.

Teilprojekte:

  • Buttom-Manager
  • Servo-Controller
  • Pump-Controller
  • Timer Module
  • dass was mir nicht einfällt

Als Neuling empfehle ich dir mit dem Design und der Programmierung des Time-Modules zu beginnen.
Das Time-Module stellt Zeitschlitze bereit, in denen ein Stück Programm ausgeführt wird, wie z.B. das Entprellen der Tasten oder das Steuern von Servos und Motoren. So erhält man ein Echtzeitprogramm, das blockadefrei dreht und so zu jeder Zeit auf Ereignisse reagieren kann.
Das Time-Module läßt sich prima aus dem "BlinkWithOutDelay" Beispiel aus dem IDE ableiten.

Das Timer-Module hat die folgenden Funktionen:

  1. init() -> Übergabe der Timer-Variablen
  2. start(currentMillis) -> startet den Timer zur angegebenen Zeit
  3. stop() -> stoppt den Timer
  4. expired(currentMillis) -> prüfen, ob der Timer abgelaufen ist, um eine eine Funktion zu starten.

Viel Spass beim Programmieren und Spielen.

@justplay_3d
da hätte ich ein Beispiel wie man eine langsame Servobewegung ohne delay hinbekommt:

https://werner.rothschopf.net/microcontroller/202207_millis_slow_servo.htm

Perfekt. Danke dir schonmal für die gute und ausführliche Beschreibung. Ich werde mich mal nach der Arbeit dran setzen und schauen was ich aus dem Code blinkwithoutdelay so schönes zaubern kann. Damit sollte ich zumindest schonmal weiter kommen💪

Vielen Dank lese ich mir in der Pause nachher mal durch.

Mein Tipp ist die Programmbibliothek MobaTools, wo Du die Servogeschwindigkeit einstellen kannst und Zeitgeber findest, wenn Dir die millis() Schwierigkeiten bereiten sollten. Kannst Du über die Bibliotheksverwaltung der IDE installieren. Schau mal in die pdf-Datei und die Beispiele.

Bei der ständig laufenden Schleife loop() ist eine Schrittkette (=finite state machine, =endlicher Automat) eine große Hilfe, ich sage sogar, unverzichtbar :slightly_smiling_face:

Das ist auf jeden Fall mal eine falsche Annahme. Copy/Paste ist immer eine sehr schlechte Variante. Such mal nach 'Arrays C++' (konkreter Link ist ein Beispiel). Auch das genannte Stichwort 'endlicher Automat' oder 'Schrittkette' könnte dir weiterhelfen.

Sorry, aber das würde mir zu lange dauern. Lieber schreibe ich Dir ein Einstiegsprogramm mit Schrittkette, langsam laufendem Servo und Zeitgeber als Ausgangspunkt für eigene Erweiterungen:

Einstiegsprogramm
#define MAX8BUTTONS
#include <MobaTools.h>
const byte buttonPin [] = { 2, 3, 4, 5 };
const byte buttonCnt = sizeof(buttonPin);
MoToButtons Buttons( buttonPin, buttonCnt, 30, 500 );  // Parameter siehe pdf-Datei
MoToTimer myTimer;
MoToServo meinServo;
const byte servoPin = 9;              // Servo-Pin

const byte pos[] = {0, 20, 40, 60};   // Winkelpositionen des Servos
const byte ledPin = 13;               // LED für Ausgabe des Startsignal

byte schritt = 0;                     // aktueller Schritt der Schrittkette
byte shot = 0;                        // aktueller Shot, 0 gleich Startposition

void setup()
{
  Serial.begin(9600);
  Serial.println(F("\nShot-Maschine"));

  for (byte j = 0; j < buttonCnt; j++) {
    pinMode(buttonPin[j], INPUT_PULLUP);       // Taster ist Input mit PullUp-Widerstand
  }
  pinMode(ledPin, OUTPUT);    // LED-Pin ist Output
  myTimer.setTime( 3000 );    // Shot Füllzeit
  meinServo.attach(servoPin); // Servo an Pin 9
  meinServo.setSpeed( 5 );    // Verfahrgeschwindigkeit einstellen
  meinServo.write(0);         // Startposition
}

void loop()
{
  Buttons.processButtons();
  switch (schritt) {
    case 0:
      if (Buttons.pressed(0)) {
        shot = 1;
        digitalWrite(ledPin, HIGH);
        Serial.println(F("Start"));
        schritt++;
      }
      break;
    case 1:
      if (Buttons.state(shot)) {
        meinServo.write(pos[shot]);
        Serial.print(F("shot: ")); Serial.print(shot); Serial.print(F(" pos: ")); Serial.println(pos[shot]);
        schritt++;
      } else {
        schritt = 4;
      }
      break;
    case 2:
      if (!meinServo.moving()) {
        myTimer.restart();
        Serial.print(F("shot: ")); Serial.print(shot); Serial.println(F(" füllen"));
        schritt++;
      }
      break;
    case 3:
      if (!myTimer.running()) {
        schritt++;
      }
      break;
    case 4:
      shot++;
      if (shot < buttonCnt) {
        schritt = 1;
      } else {
        digitalWrite(ledPin, LOW);
        Serial.println(F("Ausgangsposition"));
        meinServo.write(pos[0]);
        shot = 0;
        schritt = 0;
      }
      break;
  }
}

Prost :cocktail:

Wow... Danke für den Code. Ich werde mich direkt mal mit auseinander setzen und den durchgehen bis ich verstehe wo, wie, was passiert und ausgeführt wird. Das du da noch die Kommentare bei gepackt hast wird mir das ganze wahrscheinlich sehr vereinfachen.

Mit Kommentaren bin ich eher sparsam, aber Du kannst gerne fragen :slightly_smiling_face:

@agmue: Ist jetzt nicht wirklich wichtig, aber das:

  for (byte j = 0; j < buttonCnt; j++) {
    pinMode(buttonPin[j], INPUT_PULLUP);       // Taster ist Input mit PullUp-Widerstand
  }

braucht's nicht, das erledigen die MoToButtons in diesem Fall ( ohne callback ) selber :wink: .

Hier nochmal der richtige Schaltplan nur statt 6V habe ich 12V weil meine peristaltische pumpe das benötigt. So würde ich das wohl aufbauen und ggf mein Vater (gelernter Elektriker) fragen ob das so korrekt ist bevor ich etwas anschließe.

Das habe ich aus Deinem Beispiel PrintStates2.ino entnommen :blush:

Nein, da sind viele Fehler drin, daher bitte erst mit dem Schaltplan zum Pappa gehen, sonst zerstörst Du Dir den Arduino!

Tipp an den Elektriker: Der UNO hat interne PullUp-Widerstände, weshalb die Taster nach GND schalten sollten.