Die alte Uhr musste weg. Also musste eine neue her in Form einer Murmelbahn. Mit 9 Servomotoren, durch ein Programm gesteuert, zeigt sie durch die Anzahl der Murmeln die Uhrzeit an.
Im Grundlegenden ist dieses Projekt eine automatisierte Murmelbahn, welche in 5 verschiedenen Plexiglasröhren die Uhrzeit anzeigen kann. Der Streckenverlauf wurde an die gegebenen Umstände, also die Raumaufteilung, angepasst und könnte theoretisch auch verändert werden. Dadurch muss jede Nachbildung individuell gestaltet werden, sowohl Hardware als auch Software. Dieses ist hardwaretechnisch eines der aufwendigsten und umfangreichsten Projekte. Das Programm, welches die Zeitliche Steuerung übernimmt, ist nicht all zu kompliziert, aber sehr umfangreich. Wer die Murmelbahn reproduzieren will, müsste Software wie Hardware zwar anpassen aber nicht komplett neu erfinden.
Hardware
Schienenbau
Die Schienen bestehen aus 1,6mm dickem Draht. Dieser wird so zurechtgebogen, dass man aus meistens 3 Drähten, welche mit Verbindungsdrähten aneinander gelötet werden, einen gewünschten Streckenverlauf herstellen kann. Manchmal muss über den Murmeln ein weiterer Draht verlaufen, um die Murmeln vor Hausrausfallen zu sichern. Um die Drähte am Anfang gerade zu bekommen, spannt man diese einfach in einen Akkuschrauber ein, befestigt diese mit der anderen Seite und zieht, während man den Akkuschrauber langsam in eine Richtung drehen lässt.
Wie man genau solche Schienen herstellt, ist in diesem Video gut erklärt.
Anzeige
Um die Uhrzeit mit Murmeln anzeigen zu können, benötigt man eine Art Anzeige. Dafür haben wir 5 Plexiglasröhren aufrecht in eine Halterung gestellt und mit 5 Servomotoren unten verschlossen. Die erste Röhre auf der linken Seite stellt die Stunden dar, die 4 rechts daneben die Minuten. Jede Murmel, steht somit entweder für eine Stunde oder eine Minute. Das Stundenformat ist in unserem Fall 12-stündig. Damit kontrolliert werden kann, welche Murmel in welche Röhre fällt, ist über der Anzeige eine drehbare Vorrichtung, welche verschiedene Schienenlängen fasst. Je nachdem wie diese Vorrichtung ausgerichtet ist, fallen die Kugeln in die bestimmten Röhren. Die Lichtschranke davor, zählt die Murmeln, welche bereits durch die Weiche gelaufen sind. Um die Röhren bei voller Stunde oder zu Beginn zu leeren, werden die Servomotoren unter den Röhren nacheinander geöffnet.
Lager
Die Murmeln werden in zwei verschiedenen Lagern gelagert, um auf Befehl auf den Weg zu Anzeige losgelassen werden können. Das erste Lager, wird bei dem „normalen“ Gebrauch benutzt, wenn aktuell nur eine Murmel benötigt wird. Diese Murmel läuft daraufhin die gesamte Strecke der Bahn ab. Da dies nach dem Anschalten der Murmelbahn viel zu lange dauern würde alle 10 Sekunden eine Murmel loszuschicken, benötigt man ein zweites Lager, welches es ermöglicht einen Großteil der Bahn zu überspringen, um Zeit zu ersparen. Der zeitliche Abstand der Murmeln muss individuell auf die Strecke angepasst werden. Dies hängt vor allem von Aufzügen ab, welche nicht viele Murmeln auf einmal transportieren können. Das zweite Lager kann man weglassen, wenn man von einem Lager direkt die Anzeige und die restliche Bahn erreichen kann. Durch eine Weiche kann dann der Weg ausgewählt werden. Um das zweite Lager wieder aufzufüllen kann dann wieder das erste Lager benutzt werden. Somit ist die Uhr schneller mit der Zeit synchronisiert. Das zweite Lager (Schnelldosierungslager), sollte genügend Murmeln fassen, um jede Uhrzeit darstellen zu können. Somit benötigt man theoretisch doppelt so viele Kugeln, wenn man zwei Lager wählt. Ein Lager ist eigentlich nur eine normale Schiene, auf welcher die Murmeln hintereinander liegen. Diese Bahn, benötigt aber eine größere Steigung, da die Murmeln aneinander reiben. Wenn das Schnelldosierungslager nicht genügend Murmeln fasst, werden durch eine Lichtschranke neue Murmeln aus dem „normalen“ Lager angefragt.
Dosierung
Die Dosierung der Murmeln wird durch einen einfachen Mechanismus realisiert, welcher immer nur eine Murmel loslassen kann. Dafür wird ein Servomotor benötigt, welcher mit einem 3D gedrucktem Aufsatz die Murmeln aufhält und bei einer Drehung genau eine Murmel durchlässt1Dosierungstechnik von: Wintergatan.
Spiralaufzug
Vor der ersten Dosierung, welche die Murmeln im Normalfall dosiert, ist ein Spiralaufzug verbaut. Dieser Spiralaufzug wird bei uns als Lager benutzt, während die Murmeln nach oben transportiert werden. So wird dieser nur in Bewegung versetzt, wenn das kleinere Lager direkt vor der Dosierung weniger als 6 Murmeln fasst. Vor dem Spiralaufzug befindet sich ein normales Lager, welches in der Theorie direkt in den Aufzug übergehen könnte. Allerdings ist das Gewicht der Murmeln, wenn das Lager voll, ist so stark, dass Murmeln herausgedrückt werden. Somit direkt vor dem Aufzug nochmal eine Dosierung, welche für eine Unterbrechung sorgt und das Gewicht der Murmeln davor abfängt.
Rundaufzug
Der Rundaufzug, welcher die Kugeln nach ihrer Dosierung auf eine gewünschte Höhe befördert, kann immer nur eine Murmel gleichzeitig transportieren. Sobald eine Murmel durch eine Lichtschranke rollt, wird dieser ausgelöst und befördert diese nach oben. eine zweite Lichtschranke im Rundaufzug, legt fest wann der Aufzug wieder stehen bleibt. Dies könnte theoretisch auch über eine zeitliche Konstante gelöst werden.
Weiche
Direkt nach dem Rundaufzug gibt es drei verschiedene Wege, welche die Murmel laufen kann. Einmal in das Schnelldosierungslager, wenn dieses nicht mehr voll ist oder den restlichen Streckenverlauf in die Anzeige. Um die Auswahl der Wege zu bestimmen, kann eine Weiche benutzt werden. Es gibt viele verschiedene Ansätze eine Weiche zu bauen. Wir haben uns für eine horizontale Weiche entschieden, welche mit einem Servomotor einen Schienenteil bewegt. Der dritte Weg, welchen die Murmel laufen kann, führt zu dem Schussmechanismus, welchen ich im Anschluss beschreibe.
Schussmechanismus
Direkt nach dem Rundaufzug kann die Murmel durch die dortige Weiche, einen Weg wählen, welcher der Murmel entlang eines Balkens, gegen einen Gong schießt. Der Schuss wird durch einen Solenoid 2Solenoid auf Amazon verwirklicht. Der Solenoid gibt durch einen Elektromagnet einen kurzen, aber sehr starken Impuls auf die Murmel weiter, wodurch diese sehr schnell beschleunigt werden kann. Jedoch benötigt dieser bei 12V, 2A (24W), um eine maximale Leistung zu garantieren. Um die Spannungsversorgung möglichst einfach zu gestalten, haben wir einfach einen 12V Bleiakku in die Nähe des Schussmechanismus gestellt.
Am Ende der Schussbahn, befindet sich ein Uhrengong 3Tonfeder welchen die Murmel anschlägt. Eine Sekunde nach dem Abschuss der Kugel wird außerdem eine Weiche umgestellt, welche die Kugel beim Zurücklaufen auf der Schussbahn, auf den ursprünglichen Streckenverlauf in die Anzeige umleitet.
Es werden jedoch nicht alle Murmeln geschossen. Die Weiche wird nur umgestellt, wenn eine Stundenkugel kommt oder wenn ein Knopf gedrückt wird, wird die nächste Murmel geschossen.
Kuckuck
Der Mechanismus um die zwei Kuckuckpfeifen 4Kuckuckpfeifen auszulösen, sitzt hinter dem Schussmechanismus, und wird somit nur zur vollen Stunde ausgelöst. Da die Murmeln nicht genug Kraft haben selbst die Kuckuckpfeifen gegen eine Feder herunterzudrücken, muss der Mechanismus zuvor von einem Servo aufgezogen werden.
Steuerung
Die Steuerung übernimmt ein ESP-32, da dieser über W-Lan eine Internetverbindung herstellen kann und über genügend Anschlüsse verfügt, um verschiedenste Sensoren, Motoren oder ähnliches verbinden zu können.
Ich verwende zudem auch noch ein kleines Display5OLED Display auf Amazon, welches über SDA und SCL angeschlossen wird und somit nur wenige Ports (verbraucht). Über das Display gebe ich wichtige Variablen aus, wie die Anzahl der Murmeln, welche noch dosiert werden müssen, die Anzahl der Murmeln, welche noch durch eine Weiche rollen müssen und natürlich die Uhrzeit. Somit lässt sich mit einem Blick auf das Display feststellen, ob die Uhr noch im richtigen Takt ist und keine Murmeln herausgefallen sind.
Software
Die Uhrzeit bekommt der ESP über die Library „NTPClient“. Da in Deutschland zwischen Sommer- und Winterzeit umgestellt werden muss, benötigt man einen Schalter, welcher 3600 oder 7200 Sekunden „Offset“ zur UDP-Zeitzone einstellt.
Das gesamte Programm zu erklären, würde viel zu lange dauern. Zudem ist jeder Befehl im Programm erklärt.
Ein paar Besonderheiten, erkläre ich allerdings trotzdem. So musste ich die Lichtschranke vor dem Rundaufzug (Pin23) mit einem Interrupt6attachInterrupt belegen, da die Murmeln bei dieser Lichtschranke oft nicht erkannt wurden, da sie zu schnell waren.
Die Zeitlichen Abstände zwischen verschiedenen Vorgängen, habe ich zunächst viel zu lange ausgewählt, um dann immer näher durch Ausprobieren an einen idealen Wert zu gelangen.
Generell sind in dem gesamten Loop des Codes keine delay() Befehle über einer Millisekunde, um Pausen in zum Beispiel der Erkennung von Murmeln zu verhindern. Der Befehl delay(1); scheint Unsinnig zu wirken, jedoch muss man diesen vor allem bei ESP’s verwenden, wenn man eine while-Schleife verwenden will.
//Murmelbahn_v2 mit schießen //Searchindex: //Bsp.: MUZU01 //MU: Murmelbahn (Der Programmname) //ZU: Zusammenhalt (Das Unterprogramm oder der Programmabschnitt) //01: 01 (Unterteilung des Programmabschnitts) //Display #include <SPI.h> #include <Adafruit_GFX.h> #include <Adafruit_SSD1306.h> #define SCREEN_WIDTH 128 // OLED display width, in pixels #define SCREEN_HEIGHT 64 // OLED display height, in pixels //Murmeln int hourskugeln = 0; //...kugeln Variable ist für das Zählen der Minuten welche schon abgezählt wurden int minuteskugeln = 0; int sekundenpufferm = 40; //...puffer Variable ist die Sekunde in der Minute davor, wann die Kugel abgelassen wird wegen der Verzögerung int sekundenpufferh = 40; //Das wird vllt doof aber egal int sperrem = 0; //sperre... Variable ist wenn vor beendigung der Stude oder oder des Tages die Anzeige geleert wird, dass nicht gleich wieder Kugeln kommen int sperreh = 0; int hoursdiff = 0; //...diff Variable ist die Anzahl der Kugeln welche noch abgelassen werden müssen int minutesdiff = 0; int fastdoslagerdiff = 0; //Ist 1 wenn die Lichtschranke nicht unterbrochen ist und somit das Fastdosierungslager aufgefüllt werden muss, könnte theoretisch auch bool sein, verwirrt aber egal int dosierungslagerdiff = 0; //Ist 1 wenn die Lichtschranke nicht unterbrochen ist und somit das Dosierungslager aufgefüllt werden muss, könnte theoretisch auch bool sein, verwirrt aber int hourswei = 0; //...wei ist die Anzahl der Kugeln, welche die Weiche in eine bestimmte Richtung passieren müssen int minuteswei = 0; int uhrwei = 0; //uhrwei, fastdoswei und schiesswei entscheiden wieviele Kugeln noch in das Fastdosierungslager, wieviele für die Uhr und wie viele Kugeln geschossen werden müssen (Dosierung) int fastdoswei = 0; bool schiesswei = 0; //Ist nur bool, weil immer nur eine Kugel geschossen werden soll (nur Stundenkugel aus Dosierung), haben priorität unsigned long Zeittime = 0; //Für delay von Zeitabfrage unsigned long Servo0time = 0; //Für delay von Servo 0 loslassen unsigned long Servo1time = 0; //Für delay von Servo 1 loslassen bool Servo0sperre = 0; //Die Sperre für die Dosierun, welche vom Rundaufzug aufgehoben wird bool Servo0spak = 0; //Die Aktivierung der Zeitzählung von der Aufhebung der Sperre von Servo0 (Dosierung) unsigned long Servo0sptime = 0; //Die Zeitvariable, welche die Zeit zwischen Durchlaufen der LS am Rundaufzug und der Aufhebung der Dosierungssperre macht bool Servo0akh = 0; //Wenn 1 dann muss der Servo 0 noch zugemacht werden und Stunden abgezogen werden bool Servo0akm = 0; //Wenn 1 dann muss der Servo 0 noch zugemacht werden und Minuten abgezogen werden bool Servo0akl = 0; //Wenn 1 dann muss der Servo 0 noch zugemacht werden (Fastlager auffüllen) int Servo1akh = 0; //Wenn 1 dann muss der Servo 1 noch zugemacht werden und Stunden abgezogen werden int Servo1akm = 0; //Wenn 1 dann muss der Servo 1 noch zugemacht werden und Minuten abgezogen werden bool Fastlagerweicheschranke = 0; //Wenn 1 dann muss der Counter der Fastdosweiche noch gezählt werden unsigned long Fastlagerweichetime = 0; //Zeitvariable dass nach bestimmter Zeit der Counter der Weiche noch gezählt wird int fastdosweistellung = 0; //gibt die aktuelle Stellung der Fastdosweiche an int Weichenstellung = 0; //gibt die aktuelle Stellung der Weiche an int Weicheschranke = 0; //Sperre für die Lichtschranke der Weiche unsigned long Weischranketime = 0; //Die Zeitvariable die den delay von der Kugel durch die Weiche bestimmt, damit danach die Weiche umgestellt werden kann int min15 = 0; //Die Anzahl der Kugeln die in der 15 min Röhre liegen int min30 = 0; int min45 = 0; int min60 = 0; int Stunden = 0; //Die Anzahl der Kugeln die in der Stundenröhre liegen bool Hleeren = 0; //Ist 1 wenn die Stunden geleert werden sollen bool M15leeren = 0; //Ist 1 wenn die 15 min geleert werden sollen bool M30leeren = 0; //Die hätte mna auch zusammenfassen können weil die minuten ja immer alle zusammen geleert werden, aber die dürfen nicht gleichzeitig aufgehen, ist somit übersichtlicher bool M45leeren = 0; bool M60leeren = 0; unsigned long leerentime = 0; //DIe Zeitvariable die den delay zwischen Leeren aufmachen und wieder zumachen bestimmt bool leerensperre = 0; //Das immer nur eine Röhre auf einmal geleert wird bool Aufzug1schranke = 0; //Die Sperre für die Lichtschranke vor dem Rundaufzug (Aufzug1) unsigned long Aufzug1schranketime = 0; //Ist die Zeitliche Sperre für den Rundaufzug, ist glaube ich nicht nötig, aber ka unsigned long Aufzug1btime = 0; //Die Zeitvariable nach der der Aufzug1 anfängt, um das auspendeln zu beachten bool Aufzug1b = 0; //Das der Aufzug1 überhaupt nach bestimmter Zeit auslöst, könnte man auch mit ner fetten Rechnung mit Aufzug1btime machen ist mir aber zu viel aufwand bool Aufzug1s2 = 0; //Ist 1 wenn die Motorlichtschranke vom Aufzug1 unterbrochen wurde und dann nach bestimmter Zeit der Aufzug anhalten soll unsigned long Aufzug1etime = 0; //Die Zeitvariable nach der der Aufzug anhält das "e" steht für end :) bool Spiralauf = 0; //Ist 1 wenn der Spiralaufzug gerade läuft unsigned long Spiralauftime = 0; //Die Zeitvariable die bestimmt wie lange der Spiralaufzug pro Kugel laufen soll bool notaufa = 0; //Für den Interrupt bei dem Rundaufzug bool Servo9sperre = 0; //Sperre für den Spiralaufzugsservo unten bool Spiraldiff = 0; //Aufforderung den Spiralservo unten aufzumachen unsigned long Servo9time = 0; //Zeitliche Variable für die Sperre von dem Spiralservo unten bool Balkenwei = 0; //Bei Null soll Weiche auf gerader Stellung zum Schuss stehen, bei 1 schräg, um die rücklaufende Kugel richtung Uhr zu leiten bool Balkenweistellung = 0; //Gibt die aktuelle Stellung der Balkenweiche an bool Schuss = 0; //Wenn 0 dann kann geschossen werden, wenn 1 dann wurde Schuss bereits angefragt unsigned long Schusstime = 0; //Schusstime sperrt den Schuss für bestimmte Zeit unsigned long Schusstimesperre = 0; //Schuss kann erst nach 500 ms Angefragt werden, damit der Letzte Schuss durch den Arm nicht die Lichtschranke auslöst bool Schusssperre = 0; //Damit sich nicht 2 mal aktiviert wird //Zeit #include <NTPClient.h> //bei ESP32 #include <WiFi.h> //Bei ESP8266 //#include <ESP8266WiFi.h> #include <WiFiUdp.h> const char *ssid = "Peters Netzwerg"; const char *password = "chrpetlispau"; const long utcOffsetInSeconds = 3600; char daysOfTheWeek[7][12] = {"So", "Mo", "Di", "Mi", "Do", "Fr", "Sa"}; WiFiUDP ntpUDP; //NTPClient timeClient(ntpUDP, "pool.ntp.org", utcOffsetInSeconds); NTPClient timeClient(ntpUDP, "pool.ntp.org"); #define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin) #define SCREEN_ADDRESS 0x3C //Adresse für Display steht auf Datasheet oder hinten drauf per Widerstand umlöten; 0x3D für 128x64, 0x3C für 128x32 Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); //Servo #include <Wire.h> #include <Adafruit_PWMServoDriver.h> Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(); #define SERVOMIN 80 //Minimum Pulse Length (out of 4096) 80 bei kleinen, 150 bei großen Servos #define SERVOMAX 470 //Maximum Pulse Length (out of 4096) 470 bei kleinen, 480 bei großen Servos #define SERVOGMIN 150 #define SERVOGMAX 480 #define SERVO_FREQ 50 uint8_t servonum = 0; uint16_t pulselen = SERVOMAX; uint16_t apulselen = SERVOMAX; int movement = 0; void setup() { pinMode(2, OUTPUT); //LED_BUILDIN pinMode(27, INPUT); //Weichenschranke pinMode(23, INPUT); //Aufzug1schranke pinMode(13, INPUT); //Aufzug1motorschranke pinMode(26, INPUT); //Dosierungslager pinMode(32, INPUT); //Fastdosierungslager pinMode(25, OUTPUT); //Spiralaufzug Motor pinMode(33, OUTPUT); //Aufzug1Motor pinMode(19, INPUT); //Spiralschranke (unten) pinMode(14, INPUT_PULLUP); //Sommerzeit / Winterzeit pinMode(4, OUTPUT); //Solenoid (schießmechanismus) pinMode(5, OUTPUT); //Kerze pinMode(34, INPUT); //Schiesslichtschranke pinMode(35, INPUT_PULLUP); //Schussknopf digitalWrite(4, LOW); digitalWrite(5, LOW); digitalWrite(33, LOW); digitalWrite(25, LOW); Serial.begin(115200); //Display if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) { Serial.println(F("SSD1306 allocation failed")); for(;;); // Don't proceed, loop forever } display.clearDisplay(); display.setCursor(0, 0); display.setTextSize(2); display.setTextColor(SSD1306_WHITE); //WiFi WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); display.print("."); display.display(); } timeClient.begin(); if(digitalRead(14) == HIGH) { timeClient.setTimeOffset(7200); //7200 Sommerzeit } else { timeClient.setTimeOffset(3600);//3600 Winterzeit } //Servo pwm.begin(); pwm.setPWMFreq(SERVO_FREQ); delay(10); //Kleine Servo double pulselength = map(60, 0, 180, SERVOMIN, SERVOMAX); pwm.setPWM(0, 0, pulselength); //Dosierung pulselength = map(40, 0, 180, SERVOMIN, SERVOMAX); pwm.setPWM(1, 0, pulselength); //Schnelldosierung pulselength = map(0, 0, 180, SERVOMIN, SERVOMAX); pwm.setPWM(2, 0, pulselength); //Uhr-Weiche pulselength = map(180, 0, 180, SERVOMIN, SERVOMAX); pwm.setPWM(3, 0, pulselength); //Stundenleeren pulselength = map(180, 0, 180, SERVOMIN, SERVOMAX); pwm.setPWM(4, 0, pulselength); //15Minutenleeren pulselength = map(175, 0, 180, SERVOMIN, SERVOMAX); pwm.setPWM(5, 0, pulselength); //30Minutenleeren pulselength = map(160, 0, 180, SERVOMIN, SERVOMAX); pwm.setPWM(6, 0, pulselength); //45Minutenleeren pulselength = map(180, 0, 180, SERVOMIN, SERVOMAX); pwm.setPWM(7, 0, pulselength); //60Minutenleeren pulselength = map(97, 0, 180, SERVOMIN, SERVOMAX); pwm.setPWM(8, 0, pulselength); //Fastlager-Auffüllweiche pulselength = map(150, 0, 180, SERVOMIN, SERVOMAX); pwm.setPWM(9, 0, pulselength); //Vor Spiralaufzug pulselength = map(0, 0, 180, SERVOMIN, SERVOMAX); pwm.setPWM(10, 0, pulselength); //Balkenweiche (Schießweiche war schon vergeben bei[Fastlager-Auffüllweiche]) pulselength = map(0, 0, 180, SERVOMIN, SERVOMAX); pwm.setPWM(11, 0, pulselength); //Kuckuck-Aufziehen //Display display.clearDisplay(); display.display(); //Leeren Serial.println("leeren"); delay(500); pulselength = map(0, 0, 180, SERVOMIN, SERVOMAX); pwm.setPWM(3, 0, pulselength); delay(3000); pulselength = map(180, 0, 180, SERVOMIN, SERVOMAX); pwm.setPWM(3, 0, pulselength); pulselength = map(0, 0, 180, SERVOMIN, SERVOMAX); pwm.setPWM(4, 0, pulselength); delay(3000); pulselength = map(180, 0, 180, SERVOMIN, SERVOMAX); pwm.setPWM(4, 0, pulselength); pulselength = map(0, 0, 180, SERVOMIN, SERVOMAX); pwm.setPWM(5, 0, pulselength); delay(3000); pulselength = map(175, 0, 180, SERVOMIN, SERVOMAX); pwm.setPWM(5, 0, pulselength); pulselength = map(0, 0, 180, SERVOMIN, SERVOMAX); pwm.setPWM(6, 0, pulselength); delay(3000); pulselength = map(160, 0, 180, SERVOMIN, SERVOMAX); pwm.setPWM(6, 0, pulselength); pulselength = map(15, 0, 180, SERVOMIN, SERVOMAX); //der ist auf 15° weil der scheiße eingebaut ist pwm.setPWM(7, 0, pulselength); delay(3000); pulselength = map(180, 0, 180, SERVOMIN, SERVOMAX); pwm.setPWM(7, 0, pulselength); digitalWrite(33, HIGH); while(digitalRead(13) == LOW) { delay(1); } delay(2500); digitalWrite(33, LOW); attachInterrupt(23, notaufzug, HIGH); //timeClient.update(); delay(300); } void loop() { //////////MUZE /////MUZE01 //Zeitabfrage alle Sekunde if(millis() >= Zeittime + 1000) { timeClient.update(); Zeittime = millis(); } /////MUZE02 //Stunden in 12-Format if(timeClient.getHours() > 12 && timeClient.getHours() - 12 != Stunden) { Stunden = timeClient.getHours() - 12; } else if(timeClient.getHours() <= 12 && timeClient.getHours() != Stunden) { Stunden = timeClient.getHours(); } //////////MUAN /////MUAN01 //Anzeige refresh anzeige(); //////////MUZA /////MUZA01 if(timeClient.getHours() == 23 && timeClient.getMinutes() == 59 && timeClient.getSeconds() >= 30 && sperreh == 0) { //Wenn es 30 Sekunden vor Mitternacht ist, werden die Stunden gelehrt und eine Sperre für diese Minute aktiviert, da diese eigentlich dann weitere Kugeln anforderen würde Serial.println("Leeren H "); Hleeren = 1; hourskugeln = 0; sperreh = 1; Serial.print(timeClient.getSeconds()); Serial.print("Sperreh: "); Serial.println(sperreh); } if(timeClient.getHours() == 12 && timeClient.getMinutes() == 59 && timeClient.getSeconds() >= 30 && sperreh == 0) { //Wenn es 30 Sekunden vor Mittertag ist, werden die Stunden gelehrt und eine Sperre für diese Minute aktiviert, da diese eigentlich dann weitere Kugeln anforderen würde Serial.println("Leeren H "); Hleeren = 1; hourskugeln = 0; sperreh = 1; Serial.print(timeClient.getSeconds()); Serial.print(" Sperreh: "); Serial.println(sperreh); } if(timeClient.getHours() != 23 && sperreh == 1 && timeClient.getHours() != 12 && timeClient.getMinutes() != 59) { //Wenn die 23. Stunde rum ist, wird die Sperre aufgehoben sperreh = 0; Serial.print(timeClient.getSeconds()); Serial.print("Sperreh: "); Serial.println(sperreh); } /////MUZA02 //Minutes while(minuteskugeln < timeClient.getMinutes() && sperrem == 0) { //Wenn mehr als eine Kugel zu wenig drin ist, werden diese sofort alle angefragt minuteskugeln = minuteskugeln + 1; minutesdiff = minutesdiff + 1; delay(1); } /////MUZA03 //Wenn die Kugeln in den Minuten mehr als die eigentlichen Minuten sind wird gelehrt if(minuteskugeln > timeClient.getMinutes() + 1) { Serial.println("Leeren M fehler"); M15leeren = 1; M30leeren = 1; M45leeren = 1; M60leeren = 1; Serial.println(timeClient.getMinutes()); minuteskugeln = 0; min15 = 0; min30 = 0; min45 = 0; min60 = 0; } else if(minuteskugeln == timeClient.getMinutes() && timeClient.getSeconds() >= sekundenpufferm && timeClient.getMinutes() != 59) { //40 sekunden bevor die Minute anbricht wird die Kugel angefragt //Serial.print("LOS M "); minuteskugeln = minuteskugeln + 1; minutesdiff = minutesdiff + 1; } /*else if(minuteskugeln < timeClient.getMinutes() && sperrem == 0) { //Wenn mehr als eine Kugel zu wenig drin ist, werden diese sofort alle angefragt minuteskugeln = minuteskugeln + 1; minutesdiff = minutesdiff + 1; }*/ /* while(minuteskugeln < timeClient.getMinutes() && sperrem == 0) { //Wenn mehr als eine Kugel zu wenig drin ist, werden diese sofort alle angefragt minuteskugeln = minuteskugeln + 1; minutesdiff = minutesdiff + 1; delay(1); }*/ /////MUZA04 if(timeClient.getMinutes() == 59 && timeClient.getSeconds() >= 20 && sperrem == 0) { //Bei Minute 59 und 20 Sekunden also 40 Sekunden vor Stunde, werden die Minuten gelehrt und eine Sperre für Nachfüllen wird aktiviert, weil ja dann zu wenig Kugeln für die Minute drin wären Serial.println("Leeren M "); M15leeren = 1; M30leeren = 1; M45leeren = 1; M60leeren = 1; minuteskugeln = 0; sperrem = 1; min15 = 0; min30 = 0; min45 = 0; min60 = 0; } else if(timeClient.getMinutes() != 59 && sperrem == 1) { //Wenn die Minute 59 rum ist, wird die Sperre aufgehoben sperrem = 0; } /////MUZA05 //Stunden while(hourskugeln < Stunden && sperreh == 0) { //Wenn mehr als eine Kugel zu wenig in der Stunde sind, werden alle auf einmaln angefragt hourskugeln = hourskugeln + 1; hoursdiff = hoursdiff + 1; Serial.print(timeClient.getSeconds()); Serial.print(" Hourskugeln: "); Serial.println(hourskugeln); } if(hourskugeln > Stunden + 1) { //Wenn die Kugeln in den Stunden mehr als die eigentlichen Stunden sind, wird gelehrt Serial.println("Leeren H fehler"); Hleeren = 1; hourskugeln = 0; Serial.print(timeClient.getSeconds()); Serial.print(" Hourskugeln-1: "); Serial.println(hourskugeln); } else if(hourskugeln == Stunden && timeClient.getMinutes() == 59 && timeClient.getSeconds() >= sekundenpufferh && timeClient.getHours() != 23 && timeClient.getHours() != 12) { //Wenn es 30 Sekunden vor der nächten Stunde ist, wird die Kugel angefragt //Serial.print("LOS H "); hourskugeln = hourskugeln + 1; hoursdiff = hoursdiff + 1; Serial.print(timeClient.getSeconds()); Serial.print(" Hourskugeln: "); Serial.println(hourskugeln); } /*else if(hourskugeln < Stunden && sperreh == 0) { //Wenn mehr als eine Kugel zu wenig in der Stunde sind, werden alle auf einmaln angefragt hourskugeln = hourskugeln + 1; hoursdiff = hoursdiff + 1; }*/ //////////MUFA //Fastdosierungslager auffüllen if(digitalRead(32) == LOW && fastdoslagerdiff == 0) { //Wenn die Lichtschranke von dem Fastdosierungslager nicht unterbrochen ist dann wird dann werden Kugeln für das Lager angefragt fastdoslagerdiff = 1; } if(digitalRead(32) == HIGH && fastdoslagerdiff != 0) { //Wenn das Lager voll ist werden die Anfragen für Kugeln gelöscht fastdoslagerdiff = 0; } //////////MUDA //Dosierungslager auffüllen if(digitalRead(26) == LOW && dosierungslagerdiff == 0) { //Wenn die Lichtschranke von dem Dosierungslager nicht unterbrochen ist dann wird dann werden Kugeln für das Lager angefragt dosierungslagerdiff = 1; } if(digitalRead(26) == HIGH && dosierungslagerdiff != 0) { //Wenn das Lager voll ist werden die Anfragen für Kugeln gelöscht dosierungslagerdiff = 0; } //////////MUSP //Spiralaufzug if(dosierungslagerdiff >=1 && Spiralauf == 0) { //Geht an wenn Kugeln per Lichtschranke angefragt wurden digitalWrite(25, HIGH); Spiralauf = 1; Spiralauftime = millis(); } else if(Spiralauf == 1 && millis() >= Spiralauftime + 1000) { //So lange läuft der Spiralaufzug pro Anmachen digitalWrite(25, LOW); Spiralauf = 0; } //////////MUSU /////MUSU01 if(digitalRead(19) == LOW && Spiraldiff == 0) { Spiraldiff = 1; } else if(digitalRead(19) == HIGH && Spiraldiff != 0) { Spiraldiff = 0; } if(Spiraldiff == 1 && millis() >= Servo9time + 500 && Servo9sperre == 0) { Servo9los(); Servo9time = millis(); Servo9sperre = 1; } if(Servo9sperre == 1 && millis() >= Servo9time + 500) { Servo9zu(); Servo9time = millis(); Servo9sperre = 0; } //////////MUDS //Dosierungssperre if(Servo0spak == 1 && millis() >= Servo0sptime + 5500) { //Zeit von LS durchlaufen bis Sperre wieder aufgehoben wird Servo0spak = 0; Servo0sperre = 0; } //////////MUDO /////MUDO01 //Dosierungssteuerung //Viele Servoak... Variablen sind unnötig weil sich durch die Zeitsperre eh nicht zwei gleichzeitig öffnen können ist aber zur sicherheit, falls während der if Schleife die Zeit freigegeben wird //Bei Servo0 also der Dosierung, ist die Servo0time eigentlich auch unnötig weil der Rundaufzug die Zeit eh begrenzt, jedoch auch als Sicherung und weil ich kein Bock hab die weg zu machen //Die Servo0sperre gilt nur für die Dosierung weil diese durch den Rundaufzug gebremst wird if(hoursdiff > 1 && millis() >= Servo1time + 4000 && Servo1akh == 0 && Servo1akm == 0 && Servo0akh == 0 && Servo0akm == 0 && Servo0akl == 0) { //Fastdosierung aufmachen für Stunden fastloslassen(); Servo1time = millis(); Servo1akh = 1; } else if(hoursdiff == 1 && millis() >= Servo1time + 4000 && Servo1akh == 0 && Servo1akm == 0 && minutesdiff > 1 && Servo0akh == 0 && Servo0akm == 0 && Servo0akl == 0) { //Sonderfall Fastdosierung: Muss vor Dosierung Stunden kommen;Wenn danach noch Minuten kommen muss die letzte Stundenkugel durch Fastdosierung laufen fastloslassen(); Servo1time = millis(); Servo1akh = 1; } else if(minutesdiff > 1 && millis() >= Servo1time + 4000 && Servo1akm == 0 && Servo1akh == 0 && Servo0akh == 0 && Servo0akm == 0 && Servo0akl == 0) { //Fastdosierung aufmachen für Minuten fastloslassen(); Servo1time = millis(); Servo1akm = 1; } else if(hoursdiff == 1 && millis() >= Servo0time + 500 && Servo0akh == 0 && Servo0akm == 0 && Servo1akh == 0 && Servo1akm == 0 && minutesdiff <= 1 && Servo0akl == 0 && Servo0sperre == 0) { //Dosierung aufmachen für Stunden loslassen(); Servo0time = millis(); Servo0akh = 1; Servo0sperre = 1; } else if(minutesdiff == 1 && millis() >= Servo0time + 500 && Servo0akm == 0 && Servo0akh == 0 && Servo1akm == 0 && Servo1akh == 0 && Servo0akl == 0 && Servo0sperre == 0) { //Dosierung aufmachen für Minuten loslassen(); Servo0time = millis(); Servo0akm = 1; Servo0sperre = 1; } else if(fastdoslagerdiff == 1 && millis() >= Servo0time + 500 && Servo0akm == 0 && Servo0akh == 0 && Servo0akl == 0 && Servo0sperre == 0) { loslassen(); Servo0time = millis(); Servo0akl = 1; Servo0sperre = 1; } //minutesdiff ist die anzahl die noch freigegeben werden müssen; Servo0time ist die Sperre der Zeit -> delay; Servo0akm und Servo0akh ist die Sperre falls der Servo erst noch zugemacht werden muss //Servo0akh nur wenn schon aufgemacht wurde; Servo0time ist Zeit für Servo -> delay; /////MUDO02 if(Servo0akh == 1 && millis() >= Servo0time + 500) { //Dosierung zumachen für Stunden zumachen(); hoursdiff = hoursdiff - 1; hourswei = hourswei + 1; schiesswei = 1; digitalWrite(5, HIGH); //Kerze wird angeschaltet uhrwei = uhrwei + 1; //Braucht man ebenfalls, wegen dem Knopf der Schuss anfragt, weil der nur wenn uhr wei >= 1 ist schießen darf, sonst schießt der Fastdoslagerkugeln Servo0akh = 0; Servo0time = millis(); }else if(Servo0akm == 1 && millis() >= Servo0time + 500) { //Dosierung zumachen für Minuten zumachen(); minutesdiff = minutesdiff - 1; minuteswei = minuteswei + 1; uhrwei = uhrwei + 1; Servo0akm = 0; Servo0time = millis(); }else if(Servo0akl == 1 && millis() >= Servo0time + 500) { zumachen(); fastdoswei = fastdoswei + 1; Servo0akl = 0; Servo0time = millis(); } /////MUDO03 if(Servo1akh == 1 && millis() >= Servo1time + 500) { //Fastdosierung zumachen; hier ist kein uhrwei, weil dies nur für die Dosierung ist weil nur die die erste Weiche durchläuft fastzumachen(); hoursdiff = hoursdiff - 1; hourswei = hourswei + 1; Servo1akh = 0; Servo1time = millis(); }else if(Servo1akm == 1 && millis() >= Servo1time + 500) { fastzumachen(); minutesdiff = minutesdiff - 1; minuteswei = minuteswei + 1; Servo1akm = 0; Servo1time = millis(); } //////////MUFW //////MUFW01 //Fastdosierungslagerweichensteuerung (Das längste Wort) if(Fastlagerweicheschranke == 1 && millis() >= Fastlagerweichetime + 4500) { Fastlagerweiche(); Fastlagerweicheschranke = 0; } //////MUFW02 if(schiesswei == 1 && uhrwei >= 1 && fastdosweistellung != 3) { //Die Weichenstellung (3) ist weil ich kein Bock hab die anderen zu Tauschen macht also von der Anordnung kein Sinn double pulselength = map(97, 0, 180, SERVOMIN, SERVOMAX); //uhrwei muss >= 1 sein, damit nur Uhrkugeln geschossen werden pwm.setPWM(8, 0, pulselength); fastdosweistellung = 3; } else if(uhrwei >= 1 && fastdosweistellung != 1) { double pulselength = map(70, 0, 180, SERVOMIN, SERVOMAX); pwm.setPWM(8, 0, pulselength); fastdosweistellung = 1; } else if(fastdoswei >= 1 && uhrwei == 0 && fastdosweistellung != 2) { //Default einstellung ist Uhr weil das andere bäuchte Zeitverzögerung und des is zu doof double pulselength = map(135, 0, 180, SERVOMIN, SERVOMAX); pwm.setPWM(8, 0, pulselength); fastdosweistellung = 2; } //////////MUWE /////MUWE01 //Weichensteuerung if(digitalRead(27) == HIGH && Weicheschranke == 0 && millis() >= Weischranketime + 500) { Weiche(); Weicheschranke = 1; }else { Weicheschranke = 0; } /////MUWE02 if(hourswei >= 1 && Weichenstellung != 1 && millis() >= Weischranketime + 1000) { double pulselength = map(180, 0, 180, SERVOMIN, SERVOMAX); pwm.setPWM(2, 0, pulselength); Weichenstellung = 1; } else if(minuteswei >= 1 && hourswei == 0 && min15 < 15 && Weichenstellung != 2 && millis() >= Weischranketime + 1000) { double pulselength = map(145, 0, 180, SERVOMIN, SERVOMAX); pwm.setPWM(2, 0, pulselength); Weichenstellung = 2; } else if(minuteswei >= 1 && hourswei == 0 && min15 >= 15 && min30 < 15 && Weichenstellung != 3 && millis() >= Weischranketime + 1000) { double pulselength = map(105, 0, 180, SERVOMIN, SERVOMAX); pwm.setPWM(2, 0, pulselength); Weichenstellung = 3; } else if(minuteswei >= 1 && hourswei == 0 && min15 >= 15 && min30 >= 15 && min45 < 15 && Weichenstellung != 4 && millis() >= Weischranketime + 1000) { double pulselength = map(65, 0, 180, SERVOMIN, SERVOMAX); pwm.setPWM(2, 0, pulselength); Weichenstellung = 4; } else if(minuteswei >= 1 && hourswei == 0 && min15 >= 15 && min30 >= 15 && min45 >= 15 && min60 < 15 && Weichenstellung != 5 && millis() >= Weischranketime + 1000) { double pulselength = map(30, 0, 180, SERVOMIN, SERVOMAX); pwm.setPWM(2, 0, pulselength); Weichenstellung = 5; } //Servo0akh und Servo0akm müssen getrennt sein, da die Variable minutesdiff bzw. hoursdiff darin verändert wird //Servo0time muss in beiden das selbe sein, da dies die Zeitbeschränkung (delay) ist, welche der Servo für öffnen und schließen braucht, in beiden Fällen wird Servo0 gesteuert und braucht somit die selbe Variable. //////////MULE /////MULE01 //Leeren if(Hleeren == 1 && leerensperre == 0) { double pulselength = map(0, 0, 180, SERVOMIN, SERVOMAX); pwm.setPWM(3, 0, pulselength); leerensperre = 1; hourskugeln = 0; leerentime = millis(); Hleeren = 0; } else if(M15leeren == 1 && leerensperre == 0) { double pulselength = map(0, 0, 180, SERVOMIN, SERVOMAX); pwm.setPWM(4, 0, pulselength); leerensperre = 1; leerentime = millis(); M15leeren = 0; } else if(M30leeren == 1 && leerensperre == 0) { double pulselength = map(0, 0, 180, SERVOMIN, SERVOMAX); pwm.setPWM(5, 0, pulselength); leerensperre = 1; leerentime = millis(); M30leeren = 0; } else if(M45leeren == 1 && leerensperre == 0) { double pulselength = map(0, 0, 180, SERVOMIN, SERVOMAX); pwm.setPWM(6, 0, pulselength); leerensperre = 1; leerentime = millis(); M45leeren = 0; } else if(M60leeren == 1 && leerensperre == 0) { double pulselength = map(15, 0, 180, SERVOMIN, SERVOMAX); pwm.setPWM(7, 0, pulselength); leerensperre = 1; minuteskugeln = 0; leerentime = millis(); M60leeren = 0; } /////MULE02 if(leerensperre == 1 && millis() >= leerentime + 3000) { double pulselength = map(180, 0, 180, SERVOMIN, SERVOMAX); pwm.setPWM(3, 0, pulselength); pulselength = map(180, 0, 180, SERVOMIN, SERVOMAX); pwm.setPWM(4, 0, pulselength); pulselength = map(175, 0, 180, SERVOMIN, SERVOMAX); pwm.setPWM(5, 0, pulselength); pulselength = map(160, 0, 180, SERVOMIN, SERVOMAX); pwm.setPWM(6, 0, pulselength); pulselength = map(180, 0, 180, SERVOMIN, SERVOMAX); pwm.setPWM(7, 0, pulselength); leerensperre = 0; } //////////MURA /////MURA01 //Aufzug1(Rundaufzug) if(notaufa == 1 && Aufzug1schranke == 0 && millis() >= Aufzug1schranketime + 7000) { //Auslösen dass Aufzug ausgelöst wird Aufzug1btime = millis(); Aufzug1schranketime = millis(); Aufzug1b = 1; Aufzug1schranke = 1; Fastlagerweicheschranke = 1; Fastlagerweichetime = millis(); Servo0spak = 1; Servo0sptime = millis(); } if(digitalRead(23) == LOW) { //Hier ist ne änderung drin Aufzug1schranke = 0; // Das ist drin damit der Interruppt nicht zweimal auslöst notaufa = 0; //Die Interruppt Variable wird wieder auf 0 gesetzt } //////MURA02 if(Aufzug1b == 1 && millis() >= Aufzug1btime + 150) { //Auslösen des Aufzugs nach soviel zeit wie die Lichtschranke unterbrochen wurde digitalWrite(33, HIGH); Aufzug1b = 0; } /////MURA03 if(digitalRead(13) == HIGH && Aufzug1s2 == 0) { //Auslöser der Lichtschranke zum Stoppen Aufzug1etime = millis(); Aufzug1s2 = 1; } /////MURA04 if(Aufzug1s2 == 1 && millis() >= Aufzug1etime + 2400) { //So lange läuft der Aufzug noch nach der Lichtschranke Aufzug1s2 = 0; digitalWrite(33, LOW); } ////////MUBW ////MUBW01 if(Balkenwei == 0 && Balkenweistellung != 0) { //Balkenweiche auf Schussstellung stellen double pulselength = map(30, 0, 180, SERVOMIN, SERVOMAX); pwm.setPWM(10, 0, pulselength); Balkenweistellung = 0; } else if(Balkenwei == 1 && Balkenweistellung != 1) { //Balkenweiche auf schräge Stellung stellen double pulselength = map(30, 0, 180, SERVOMIN, SERVOMAX); pwm.setPWM(10, 0, pulselength); Balkenweistellung = 1; } ////////MUSC ////MUSC01 if(digitalRead(34) == HIGH && Schuss == 0 && millis() >= Schusstimesperre + 500) { //Schuss wird angefragt, wenn Lichtschranke unterbrochen, und seit 500 ms kein Schuss ausgeführt wurde, damit der Schussarm nicht die Lichtschranke auslöst Schuss = 1; Schusstime = millis(); pulselength = map(180, 0, 180, SERVOMIN, SERVOMAX); //Kuckuckaufladen wird angefangen pwm.setPWM(11, 0, pulselength); } ////MUSC02 if(Schuss == 1 && millis() >= Schusstime + 1000 && Schusssperre == 0) { //Schuss wird 1 Sekunde später ausgeführt, damit sich die Kugel berühigt Schusstime = millis(); digitalWrite(5, LOW); //Kerze wird ausgeschaltet digitalWrite(4, HIGH); Schusssperre = 1; pulselength = map(0, 0, 180, SERVOMIN, SERVOMAX); //Kuckuckaufladen wird beendet pwm.setPWM(11, 0, pulselength); } ////MUSC03 if(Schusssperre == 1 && millis() >= Schusstime + 1000) { //1 Sekunde später wird der Arm zurückgeholt und die Weiche umgestellt Balkenwei = 1; digitalWrite(4, LOW); Schusssperre = 0; Schusstimesperre = millis(); Schuss = 0; } ////MUSC04 if(digitalRead(35) == LOW && schiesswei == 0) { //Wenn der Knopf gedrückt wird, wird schuss angefragt (Weiche) schiesswei = 1; digitalWrite(5, HIGH); //Kerze geht an wenn der Knopf gedrückt wurde } } //////////MUWE03 void Weiche() { if(hourswei >= 1) { Weischranketime = millis(); hourswei = hourswei - 1; } else if(minuteswei >= 1 && min15 < 15) { Weischranketime = millis(); min15 = min15 + 1; minuteswei = minuteswei - 1; } else if(minuteswei >= 1 && min30 < 15) { Weischranketime = millis(); min30 = min30 + 1; minuteswei = minuteswei - 1; } else if(minuteswei >= 1 && min45 < 15) { Weischranketime = millis(); min45 = min45 + 1; minuteswei = minuteswei - 1; } else if(minuteswei >= 1 && min60 < 15) { Weischranketime = millis(); min60 = min60 + 1; minuteswei = minuteswei - 1; } } /////////MUFW03 void Fastlagerweiche() { if(schiesswei == 1) { Balkenwei = 0; schiesswei = 0; uhrwei = uhrwei - 1; //muss hier auch gemacht werden, weil somit garantiert wird, dass nur Uhrkugeln geschossen werden können } else if(uhrwei >= 1) { uhrwei = uhrwei - 1; } else if(fastdoswei >= 1) { fastdoswei = fastdoswei - 1; } } //////////MUDO04 void loslassen() { double pulselength = map(120, 0, 180, SERVOMIN, SERVOMAX); pwm.setPWM(0, 0, pulselength); } void zumachen() { double pulselength = map(60, 0, 180, SERVOMIN, SERVOMAX); pwm.setPWM(0, 0, pulselength); } void fastloslassen() { double pulselength = map(110, 0, 180, SERVOMIN, SERVOMAX); pwm.setPWM(1, 0, pulselength); } void fastzumachen() { double pulselength = map(40, 0, 180, SERVOMIN, SERVOMAX); pwm.setPWM(1, 0, pulselength); } //////////MUSU02 void Servo9los(){ double pulselength = map(60, 0, 180, SERVOMIN, SERVOMAX); pwm.setPWM(9, 0, pulselength); } void Servo9zu(){ double pulselength = map(150, 0, 180, SERVOMIN, SERVOMAX); pwm.setPWM(9, 0, pulselength); } //////////MUNA void notaufzug() { notaufa = 1; } //////////MUAN02 void anzeige() { display.clearDisplay(); display.setTextSize(2); display.setTextColor(SSD1306_WHITE); display.drawLine(0, 25, 128, 25, SSD1306_WHITE); display.drawLine(0, 45, 128, 45, SSD1306_WHITE); display.drawLine(60, 45, 60, 80, SSD1306_WHITE); display.drawLine(60, 0, 60, 25, SSD1306_WHITE); display.setCursor(0, 0); display.println(daysOfTheWeek[timeClient.getDay()]); display.setCursor(0, 28); display.print(timeClient.getHours()); display.print(":"); display.print(timeClient.getMinutes()); display.print(":"); display.println(timeClient.getSeconds()); display.setCursor(65, 0); display.print(fastdoswei); display.setCursor(100, 0); display.print(uhrwei); display.setCursor(0, 50); display.print(hoursdiff); display.setCursor(35, 50); display.print(minutesdiff); display.setCursor(65, 50); display.print(hourswei); display.setCursor(100, 50); display.print(minuteswei); display.display(); }