#include <Servo.h> // Import der Servo-Libraries //TODO: Startknopf einbauen Eingan A3 /** MERKZETTEL: malloc() nachschlagen. Koennte uns sehr mit RAM helfen. Beispiel: ptr = malloc(100) ...oder so. **/ // Pins // Start-Knopf const int startButton = A3; // Stepper - Motoren /* Zusatz-Infos zu den Steppern: Pololu 1 für den Linken Stepper befindet sich direkt neben dem Arduino, Pololu 2 für den Rechten Stepper ist der Andere. Den Linken Stepper so anbringen, dass das schwarze Kabel direkt neben dem Kondensator ist, den Rechten so, dass das blaue Kabel direkt daneben ist. */ const int dirL = 2; //Linker Stepper const int stpL = 3; const int dirR = 7; //Rechter-Stepper const int stpR = 9; const int stepDelay = 15; // Delay zwischen steps. Steuer die Geschwindigkeit const double stepLength = 1.414; // Länge eines Schrittes mit den 90mm Rädern. boolean driveStraight = false; // Sonar const int inPinSonar = A4; const int pingPinSonar = 12; // Arm-Servos für Drehbewegung + Heben const int rotor = 6; //mittlerer Anschluss const int lifter = 4; //innerer Anschluss // Servo mit Ventil-Funktion - 0 ist weitestmöglich Richtung geschlossen, 180 ist weitestmöglich geöffnet. const int vent = 5; //äußerer Anschluss const int valveShut = 40; // Grandzahl, bei welcher der Servo vollständi geschlossen ist. const int valveOpen = 180; // LEDs const int led = 12; // LED für die Beleuchtung der Steuerungs-Phototransistoren const int resetLed = 13; // LED auf dem Arduino, soll Leuchten, wenn der Arduino abstürzt und sich resetted // Phototransistoren const int photoL = A6; const int photoR = A7; // Das blaue Kabel // Kontakt für Fuellstand, Erdung G56 (schwarz), Einspeisung G55 (rot) const int fuellstandsPin =A5; // Konstanten const int armLength = 235; // Armlänge in mm const int armOffAxis = 30; //TODO: einbauen const double pi= 3.15; const int threshold = 1020; // für Fuellstanderkennung TODO: testen const int distSonar = 310; const int radiusGlas = 20; // Variablen // für Glaserkennung long length = 0; // Speichert die Anzahl der Schritte, die der Roboter an einem Glas vorbei fährt boolean found = false; //Boolean, ob Roboter an einem Glas vorbei fährt boolean passed = false; //Boolean, ob Roboter ein Glas passiert hat int glassDistanz = 0; int glassCounts = 0; // für Fahrfunktion boolean rightWasLast = false; //Boolean, ob Roboter vorher nach rechts gefahren ist boolean leftWasLast = false; //Boolean, ob Roboter vorher nach links gefahren ist int brightnessLeft = 0; //Wert des Phototransitoren auf der linken Seite int brightnessRight = 0; //Wert des Phototransistoren auf der rechten Seite const int lightThreshold = 150; // Abfrage, wie viel heller der helle Bereich ist, als die schwarze Linie const int lightBlack = 600; // Lichtintensität, ab welcher wir von Erkennung der schwarzen Linie ausgehen const int photoCorrection = 100; // Korrektur, die auf den rechten Phototransistor angewendet wird. // Servos Servo rotate; Servo lift; Servo ventil; void setup(){ //TODO Serielle Ausgabe entfernen Serial.begin(9600); Serial.println("ABSTURZ oder vielleicht auch erster Start"); // Arduino-LED: Leuchtet auf, wenn der Arduino neu startet. pinMode(resetLed,OUTPUT); digitalWrite(resetLed,HIGH); delay(100); digitalWrite(resetLed,LOW); //Initialisierungen // Steuerungs-LED anmachen pinMode(led,OUTPUT); digitalWrite(led,HIGH); // Arm-Servos anschließen und ausrichten rotate.attach(rotor, 800, 2100); lift.attach(lifter, 800, 2100); resetArm(); // Ventil-Servo anschließen TODO: ausrichten - benötigt wahrscheinlich nicht die Korrekturwerte ventil.attach(vent); ventil.write(valveShut); // Ventil schliessen. TODO Wert gegen valveShut ersetzen. // Stepper-Motoren initialisieren pinMode(dirL, OUTPUT); pinMode(stpL, OUTPUT); pinMode(dirR, OUTPUT); pinMode(stpR, OUTPUT); // Stepper-Motoren Richtung auf vorwärts stellen. digitalWrite(dirL,LOW); digitalWrite(dirR,LOW); // TODO testen, ob der Startknopf so funktioniert. while(analogRead(startButton)<1020) delay(1); } void loop(){ Glaserkennung(); // prüfe, ob sich ein Glas vor dem Sonar befindet. /* Merke: Jedes Objekt, das auf weniger als die Armlänge des Roboters von ihm entfernt zu seiner Rechten aufgestellt ist, wird als Glas erkannt. */ if(found){ // found ist true, wenn ein Glas entdeckt wurde. Serial.println("Glas gefunden"); driveStraight = true; if(passed){ // wird ausgeführt, wenn ein Glas gefunden und vom Sonar passiert wurde Serial.println("Glas passed"); glassDistanz = glassDistanz/glassCounts; Serial.print("GlassDistanz = "); Serial.println(glassDistanz); double h = calcH(); //fahre restliche Distanz Serial.print("h = "); Serial.println(h); double steps = convertToSteps(h)-(length/2); Serial.print("steps = "); Serial.println(steps); Glaserkennung(); //damit drive length nicht abändert for(int i=0; i<steps; i++){ drive(); } moveArm(glassDistanz+radiusGlas); //Bewege Arm fill(); //Füllen resetArm(); //resetten length=0; //length glassDistanz = 0; glassCounts = 0; }else{ // wird ausgeführt, wenn ein Glas gefunden aber noch nicht vollständig vom Glas passiert wurde for(int i = 0; i<5; i++) drive(); //fahre und zähle Schritte } }else{ // wird ausgeführt, wenn noch kein Glas gefunden wurde driveStraight = false; for(int i = 0; i<5; i++) drive(); //fahre bis zum nächsten Glas } } /* Diese Methode setzt uns passed und found. D.h. wir überprüfen, ob wir an einem Glas vorbei fahren, es passieren oder kein Glas in der Nähe ist. Außerdem wird hier die Entfernung zum Glas mitgeschrieben - glassDistanz wird später durch Mess-Anzahl dividiert. */ void Glaserkennung(){ long distance = distanceSonar(); Serial.print("distance = "); Serial.println(distance); if(distance > 0) { found = true; glassDistanz = glassDistanz + distance; glassCounts++; } //wir haben ein Glas gefunden else if(found &&!passed && distance == 0) passed = true; //gerade am Glas vorbei else if(found && passed && distance ==0){ //wir haben das Glas bereits passiert=> auf Anfang setzen passed = false; found = false; } } /* Methode, welche mit dem Sonar die Distanz zu etwaigen Objekten überprüft und uns in mm zurück gibt. Gibt es kein Objekt in direkter Nähe, wird 0 zurück gegeben, */ long distanceSonar(){ // Initialisiere Variablen für die Distanz // duration sind ms und mm sind millimeter long duration = 0; long mm; for(int i=0; i<10;i++){ // The PING))) is triggered by a HIGH pulse of 2 or more microseconds. // Give a short LOW pulse beforehand to ensure a clean HIGH pulse: pinMode(pingPinSonar, OUTPUT); digitalWrite(pingPinSonar, LOW); delayMicroseconds(2); digitalWrite(pingPinSonar, HIGH); delayMicroseconds(10); digitalWrite(pingPinSonar, LOW); // The same pin is used to read the signal from the PING))): a HIGH // pulse whose duration is the time (in microseconds) from the sending // of the ping to the reception of its echo off of an object. pinMode(inPinSonar, INPUT); duration = duration + pulseIn(inPinSonar, HIGH); delay(25); } // in mm umrechnen mm = microsecondsToMillimeters(duration/10); //TODO Schwelle erhöhen, falls möglich. if(mm > 160) mm = 0; return mm; } /* Methode, die den Arm in Ausgangsposition bringt. */ void resetArm(){ lift.write(100); //TODO: nach oben zeigen lassen rotate.write(175); } /* Methode, welche die Armbewegung ausführt, d.h. ausfahren und senken. */ void moveArm(double distance){ // TODO die Servos machen keine vollen 180° - das müssen wir manuell messen // TODO hier müssen wir noch die Distanz des Sonar-Sensors auf die Achse des Rotators normieren double alpha = asin(distance/armLength); // Berechnung der Arm-Position bei gegebenem Abstand alpha = (alpha/pi)*180; // Umrechnung von Rad in Deg delay(500L); rotate.write( (int) 175-alpha); //Drehung delay(500L); lift.write(40); //TODO: Testen ob das gut ist //Senkung } /* Restlänge, die Der Roboter zurücklegen muss, nachdem das Glas vom Sonar passiert wurde, aber ehe der Arm ausgefahren wird. TODO: Wird hier beachtet, wie viele Schritte wir bereits mit dem Sonar am Glas vorbeigefahren sind? -Rene */ double calcH(){ double g = glassDistanz + radiusGlas; //Distanz von Roboter zu Glasmitte Serial.print("g = "); Serial.println(g); double square = round(sqrt(sq((double) armLength) - sq(g))); // Wir casten hier auf Double, weil Arduino behindert ist. Serial.print("sqrt(armLength*armLength - g*g) = "); Serial.println(square); return distSonar - square; } /* Methode, die anhand der Elektroden am Zapfarm erkennt, ob das darunter befindliche Glas voll ist. Gibt bei Kontakt der Elektroden mit der Flüssigkeit (also Glas voll) true aus, sonst false. */ boolean fuellstand(){ //Wenn Strom fließt, ist die Flüssigkeit hoch genug, um Strom zu leiten if(analogRead(fuellstandsPin) <= threshold) return false; else return true; } /* Öffnet das Ventil, wartet bis das darunter befindliche Glas voll ist, schließt das Ventil wieder. */ void fill(){ ventil.write(valveOpen); while(!fuellstand()) delay(100); ventil.write(valveShut); } /* Methode Um MicroSekunden in mm umzurechnen. Schallgeschwindigkeit liegt bei 340 m/s oder 29 mikrosekunden/cm. Der Ping geht den Weg zum Objekt und zurück, also rechnen wir durch 2. */ long microsecondsToMillimeters(long microseconds){ return (microseconds / 2.9 / 2); } /* Methode um an der Linie entlang zu fahren, Achtung: ändert die Variable length. rightWasLast und leftWasLast bleiben auch zwischen Iterationen der Methode erhalten, daher kann der Roboter auch nachdem die Linie überhaupt nicht mehr gesehen wird zu ihr zurückfinden. Der boolean driveStraight wird im Loop auf true gesetzt, wenn wir zu Messzwecken oder um ein Glas zu erreichen nur genau geradeaus fahren dürfen. Danach findet der Roboter trotzdem zur Linie zurück. Mit dem int stepDelay lässt sich die Geschwindigkeit des Roboters regulieren. */ void drive(){ brightnessLeft = (analogRead(photoL) + analogRead(photoL))/2; brightnessRight = (analogRead(photoR) + analogRead(photoR))/2 + photoCorrection; //Überprüfe mit den Phototransitoren, wo die Linie ist //z.B. wenn Links mehr weiß ist als rechts, dann müssen wir von rechts über die Linie gefahren sein if(brightnessLeft> brightnessRight + lightThreshold){ rightWasLast = true; leftWasLast = false; } else if (brightnessRight > brightnessLeft + lightThreshold ){ leftWasLast = true; rightWasLast = false; } else if(brightnessRight < lightBlack && brightnessLeft < lightBlack){ leftWasLast = false; rightWasLast = false; } //Fahrbewegung if(rightWasLast && !driveStraight){ //nach rechts fahren digitalWrite(stpL, HIGH); delay(1); digitalWrite(stpL, LOW); delay(stepDelay); } else if(leftWasLast && !driveStraight){ //nach links fahren digitalWrite(stpR, HIGH); delay(1); digitalWrite(stpR, LOW); delay(stepDelay); } else if((!rightWasLast && !leftWasLast) || driveStraight){ // geradeaus fahren digitalWrite(stpL, HIGH); digitalWrite(stpR, HIGH); delay(1); digitalWrite(stpL, LOW); digitalWrite(stpR, LOW); delay(stepDelay); } //Wenn wir gerade an einem Glas vorbei fahren, zählen wir die Schritte if(found && !passed){ //Merke: muss danach auf 0 gesetzt werden length++; } } /* Rechnet Stepperschritte in mm um */ long convertToMM(long steps){ return steps*stepLength; } /* Rechnet mm in Stepperschritte um */ long convertToSteps(long mm){ return mm/stepLength; }