Schnapsglas Füllmaschine im Feuerwehr style

SefanH

Active member
Ich habe ein Video gesehen in dem jemand eine solche Maschine mit einem Mähdrescher gebaut hat.
Da ich in der Freiwilligen Feuerwehr Mitglied bin, möchte ich ein ähnliches Gerät im Stiel der Feuerwehr bauen.
Hier werde ich meine Überlegungen, den Aufbau und den Code Posten. Falls jemand was ähnliches gebaut hat oder hilfreiche Tips für mich hat, kann sich gerne einbringen!

Ich habe nun im Auktionshaus eine kleine Pumpe, ein paar Servos und Miniservos bestellt. Auch ein paar infrarot Abstandssensoren habe ich bestellt.
Die ersten Tests dieser Sensoren zeigen gute Ergebnisse.
Das Feuerwehrauto hat eine Drehleiter die mit einem Miniservo angehoben werden soll und dann mit einem anderen Servo zu den Gläsern gedreht wird.

Mal sehen wie's weiter geht...!?
 
So.... Da ich beruflich nichts mit programmieren und ähnlichem zu tun habe, Und alles nur durch dieses Projekt und den vielen fleißigen Mitgliedern in diesem Forum gelernt habe, mache ich natürlich auch viele Fehler. Aber aus Fehlern lernt man bekanntlich.
Die ersten Versuche der Firmware haben einen Servo zum anheben einer Leiter angesteuert, dann wurde der Drehkranz bewegt, die Leiter wieder abgesenkt und das Glas galt als voll.
Da ich in dieser ersten Version mit delay gearbeitet hatte, musste ich alles nochmals umbauen. Viele Male! LED Licht fehlte noch und Feuerwehr Blaulicht. Wie ist die Blitzabfolge?
Nunja, nach ca. 16 Stunden harter Hirn-Arbeit steht der Prototyp!
 
Zuletzt bearbeitet:
Foto des Aufbaus.

Video mit der jetzigen Firmware

Und falls jemand in die Firmware gucken möchte... Ist noch Experimentell!
C++:
#include <Servo.h>
#include <Wire.h>
#include <Adafruit_NeoPixel.h>                                                                  // 1000uF Kondensator zwischen + und -, 300-500 OHM Widerstand in der Datenleitung!

// Servos und Kontakte
Servo ServoDrehkranz;                                                                           // Servo-Objekte anlegen
Servo ServoLeiter;
const int PinKontaktGlas[] = {2, 3, 4, 5, 6, 7, -1};                                            // Pins der IR-Kontakte
const int PinServoDrehkranz = 8;                                                                // Servo Pin Drehkranz
const int PinServoLeiter = 9;                                                                   // Servo Pin Leiter
int ServoWinkelDrehkranzIst = 0;                                                                // Startwinkel Drehkranz
int ServoWinkelLeiterIst = 0;                                                                   // Starteinkel Leiter
int ServoGeschwingigkeit = 30;                                                                  // Höhere Werte verlangsamen die Servos
unsigned long LeiterTimer = millis();                                                           // Merker - Zuständig für die Geschwindigkeit der Servos
unsigned long DrehkranzTimer = millis();                                                        // Merker - Zuständig für die Geschwindigkeit der Servos
int Schritt = 1;                                                                                // Diese Variable steuert die Bewegungsabläufe der Servos (Leiter hoch(1), Drehen(2), Leiter runter(3))
int InArbeit = 0;                                                                               // Diese Variable sorgt dafür dass erst ein Glas befüllt wird, dann das Nächste
int Winkel = 22;                                                                                 // Winkelvariable für den Drehkranz (Soll)

//Neopixel
const int PinLed = 10;                                                                          // LED Pin (Neopixel)
const int AnzahlLeds = 6;                                                                       // Anzahl LEDs
const int LedHelligkeit = 50;                                                                   // Helligkeit 0-255

// Blaulicht
const int Blaulicht[] = {1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, -1};                // Blitzabfolge
const unsigned long BlaulichtTempo = 36;                                                        // Höhere Werte verlangsamen den Blitz
int Zaehler = 0;                                                                                // Wird in der Funktion Blitzer benötigt. Dias Array Blaulicht wird damit Stück für Stück abgearbeitet
unsigned long Merker = 0;                                                                       // Merker für die Verzögerung beim Blaulicht (Tempo) - Dieser Variable wird der Wert von millis() übergeben.
int BlaulichtAnforderung = 0;                                                                   // 0 = Kein Blaulicht, 1 = Angefordert, 2 = Baulicht durchläuft die Schleife
int Ein = 0;                                                                                    // Merker fürs Blaulicht
int ON = 0;                                                                                     // Merker fürs Blaulicht
unsigned long Leerlauftimer = 0;


// Declare our NeoPixel strip object:
Adafruit_NeoPixel LedStreifen(AnzahlLeds, PinLed, NEO_GRB + NEO_KHZ800);
// Argument 1 = Number of pixels in NeoPixel strip
// Argument 2 = Arduino pin number (most are valid)
// Argument 3 = Pixel type flags, add together as needed:
//   NEO_KHZ800  800 KHz bitstream (most NeoPixel products w/WS2812 LEDs)
//   NEO_KHZ400  400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers)
//   NEO_GRB     Pixels are wired for GRB bitstream (most NeoPixel products)
//   NEO_RGB     Pixels are wired for RGB bitstream (v1 FLORA pixels, not v2)
//   NEO_RGBW    Pixels are wired for RGBW bitstream (NeoPixel RGBW products)

unsigned long Timer = 0;                                                                        // Dieser Variable wird der Wert von millis() übergeben - für die Wartezeit bevor das Glas befüllt wird
unsigned long StandzeitNeuesGlas = 1000;                                                        // Wie lange muss das Glas dort stehen, bevor es befüllt wird? Angabe in Millisekunden.

int GlasDefinitionen[] = { 0, 0, 0, 0, 0, 0 };   // 6 Werte für 6 Gläser - 7. Wert ist Ruhestellung!

void setup() {
 
  ServoDrehkranz.attach(PinServoDrehkranz);                                                     // ServoDrehkranz mit Pin verbinden
  ServoLeiter.attach(PinServoLeiter);                                                           // ServoLeiter mit Pin Verbinden

  pinMode(PinKontaktGlas[0], INPUT_PULLUP);
  pinMode(PinKontaktGlas[1], INPUT_PULLUP);
  pinMode(PinKontaktGlas[2], INPUT_PULLUP);
  pinMode(PinKontaktGlas[3], INPUT_PULLUP);
  pinMode(PinKontaktGlas[4], INPUT_PULLUP);
  pinMode(PinKontaktGlas[5], INPUT_PULLUP);

  Serial.begin(9600);

  LedStreifen.begin();
  LedStreifen.show();
  LedStreifen.setBrightness(LedHelligkeit);

  Serial.println("FireFiller 2.0 - 08.12.23");
  ServoDrehkranz.write(ServoWinkelDrehkranzIst);
  ServoLeiter.write(ServoWinkelLeiterIst);
}

void loop() {
  Check();
  Tanken();
  Leerlaufcheck();
}

void Leerlaufcheck() {
  if (Leerlauftimer + 100 < millis() && (ServoWinkelDrehkranzIst > 0 || ServoWinkelLeiterIst > 0)) {
    switch (Schritt) {
      case 1:
        LeiterBewegen(50, ServoGeschwingigkeit);
        break;
        
      case 2:
        DrehkranzBewegen(0, ServoGeschwingigkeit);
        break;
        
      case 3:
        LeiterBewegen(0, ServoGeschwingigkeit);
        break;
    }
  }
}
void Check(){                                                                                   // Livezustände checken und mit gemerkten Zuständen vergleichen. Stati und LEDs nach Situation verändern.
  for (int i = 0; PinKontaktGlas[i] > 0 ; i++) {                                                // Kontaktnummer zum Auslesen festlegen
    int LiveZustand = digitalRead(PinKontaktGlas[i]);                                           // Kontakt auslesen und Wert in Variable schreiben
    if (LiveZustand == 0) {                                                                     // 0 = Glas erkannt, 1 = Kein Glas
      switch (GlasDefinitionen[i]) {
        case 0:                                                                                 // 0 = Vormals wurde kein Glas erkannt, nun steht hier eins! --> Licht rot, Timer starten, Status auf 1 setzen.
          LedStreifen.setPixelColor(i, LedStreifen.Color(255, 0, 0));
          LedStreifen.show();
          Timer = millis();
          GlasDefinitionen[i] = 1;
          break;

        case 1:                                                                                 // 1 = Glas steht noch. Timer abwarten -->dann Status auf 2 setzen
          Leerlauftimer = millis();
          if (Timer + StandzeitNeuesGlas < millis()) {
            GlasDefinitionen[i] = 2;
          }
          break;

        case 2:                                                                                 // 2 = (Status 2 = Blaulicht ist an!!!) Servos werden angesteuert und dann Status auf 3
          Leerlauftimer = millis();                                                                             
          if (BlaulichtAnforderung == 0) {
            BlaulichtAnforderung = 1;
          }
          break;

        case 3:                                                                                 // 3 = Glas ist voll! Blaulicht abschalten und Licht grün. Status auf 4 setzen.
          BlaulichtAnforderung = 0;
          LedStreifen.setPixelColor(i, LedStreifen.Color(0, 255, 0));
          LedStreifen.show();
          GlasDefinitionen[i] = 4;
          break;
        
        case 4:                                                                                 // 4 = Glas ist voll, licht ist bereits grün... Nichts machen!
          break;
      }
    } else if (LiveZustand == 1 && GlasDefinitionen[i] > 0) {                                   // Kein Glas erkannt, vorher stand hier jedoch eins. --> Status ändern!
      GlasDefinitionen[i] = 0;                                                                  // Status auf 0 setzen
      LedStreifen.setPixelColor(i, LedStreifen.Color(0, 0, 0));                                 // LEDs definieren
      LedStreifen.show();
      BlaulichtAnforderung = 0;
      Schritt = 1;
    }
    Blitzer(i);
  }
}

int Blitzer(int LedNummer) {
  if (BlaulichtAnforderung == 1){                                                               // Wenn Blaulichtanforderung = 1 ist, soll der Blitzer laufen und die Servos angesteuert werden.
    Merker = millis();                                                                          // Merker setzen
    Zaehler = 0;                                                                                // Variable zum Auslesen der Blitzabfolge
    BlaulichtAnforderung = 2;                                                                   // Blaulichtanforderung auf 2 setzen, sonst startet der Timer bei jedem durchgang wieder von vorne.
  }
  if(BlaulichtAnforderung == 2 && millis() >= (Merker + BlaulichtTempo)) {                      // Erst ausführen wenn Der Timer abgelaufen ist
    for (int h = 0; PinKontaktGlas[h] > 0 ; h++) {
      if (GlasDefinitionen[h] == 2 && Blaulicht[Zaehler] == 1 && Ein == 0) {
        LedStreifen.setPixelColor(h, LedStreifen.Color(0, 0, 255));
        ON = 1;
      }
      if (GlasDefinitionen[h] == 2 && Blaulicht[Zaehler] == 0 && Ein == 1) {                    // Wenn Blaulicht[i] = 0 dann Licht ausschalten und Merken dass es aus ist
        LedStreifen.setPixelColor(h, LedStreifen.Color(0, 0, 0));
        ON = 0;
      }
    }
    Ein = ON;
    Zaehler++;
    Merker = millis();
    LedStreifen.show();
    if (Zaehler > 17) {
      Zaehler = 0;
    }
  }
}

void Tanken() {
 
  int x = (Winkel / 22) - 1;                                                                    // x hat Werte von 0 - 5 (Für 6 Gläser)
  if (PinKontaktGlas[x] >-1) {                                                                  // -1 ist das Abbruchkriterium, der letzte Wert in dem Array.
    int LiveZustand = digitalRead(PinKontaktGlas[x]);                                           // Kontakt auslesen und Wert in Variable schreiben
    if (LiveZustand == 0 && GlasDefinitionen[x] == 2 && (InArbeit == 0 || InArbeit == x)) {     // Wenn Ein Glas erkannt wird UND das Glas befüllt werden soll (Status 2) UND die Variable InArbeit 0 ODER die Nummer des Felldes hat DANN weiter
      if (InArbeit == 0) {                                                                      // Die Servos sollen sich bewegen. Wenn InArbeit = 0 dann
        InArbeit = x;                                                                           // muss InArbeit die nummer des Glases erhalten um die Bewegungen für dieses Glas komplett abzuarbeiten
      }
      switch (Schritt) {                                                                        //Schritt 1 = Leiter hoch, 2 = Drehen, 3 = Leiter runter
        case 1:
          LeiterBewegen(50, ServoGeschwingigkeit);
          break;
        
        case 2:
          DrehkranzBewegen(Winkel, ServoGeschwingigkeit);
          break;
        
        case 3:
          LeiterBewegen(0, ServoGeschwingigkeit);
          break;
      }
    }
    Winkel += 22;
    if (Winkel > (6 * 22)){
      Winkel = 22; 
    }
  }                         
}

int LeiterBewegen (int Soll, unsigned long Verzoegerung) {                                      // Funktion zum bewegen der Leiter
  if (ServoWinkelLeiterIst < Soll && LeiterTimer + Verzoegerung < millis()) {                   // Wenn der SollWinkel nicht erreicht ist und die Wartezeit abgelaufen ist, dann weiterdrehen
    ServoWinkelLeiterIst++;                                                                     // IstWinkel um 1 erhöhen
    ServoLeiter.write(ServoWinkelLeiterIst);                                                    // auf Wert drehen
    LeiterTimer = millis();                                                                     // Timer für Wartezeit neu starten
  }
  if (ServoWinkelLeiterIst > Soll && LeiterTimer + Verzoegerung < millis()) {                   // Wenn der SollWinkel nicht erreicht ist und die Wartezeit abgelaufen ist, dann weiterdrehen
    ServoWinkelLeiterIst--;                                                                     // IstWinkel um 1 verringern
    ServoLeiter.write(ServoWinkelLeiterIst);                                                    // auf Wert drehen
    LeiterTimer = millis();                                                                     // Timer für WarteZeit neu starten
  }
  if (ServoWinkelLeiterIst == Soll) {                                                           // Wenn Sollwinkel erreicht ist dann
    Schritt++;                                                                                  // zum nächsten Schritt gehen
    if (Schritt == 4) {                                                                         // Wenn Schritt 4 oder größer (Es gibt nur 3 Schritte)
      InArbeit = 0;
      Schritt = 1;
      if (Winkel != 0) {
        GlasDefinitionen[(Winkel/22) - 1] = 3;                                                  // Da der ablauf für dieses Glas abgeschlossen ist, wird der Status neu gesetzt
      }   
    }
  }
}


int DrehkranzBewegen (int Soll, int Verzoegerung) {                                             // Funktion zum bewegen des Drehkranzes
  if (ServoWinkelDrehkranzIst < Soll && DrehkranzTimer + Verzoegerung < millis()) {             // Wenn der SollWinkel nicht erreicht ist und die Wartezeit abgelaufen ist, dann weiterdrehen
    ServoWinkelDrehkranzIst++;                                                                  // IstWinkel um 1 erhöhen
    ServoDrehkranz.write(ServoWinkelDrehkranzIst);                                              // auf Wert drehen
    DrehkranzTimer = millis();                                                                  // Timer für Wartezeit neu Starten
  }
  if (ServoWinkelDrehkranzIst > Soll && DrehkranzTimer + Verzoegerung < millis()) {             // Wenn der SollWinkel nicht erreicht ist und die Wartezeit abgelaufen ist, dann weiterdrehen
    ServoWinkelDrehkranzIst--;                                                                  // IstWinkel um 1 verringern
    ServoDrehkranz.write(ServoWinkelDrehkranzIst);                                              // auf Wert drehen
    DrehkranzTimer = millis();                                                                  // Timer für Wartezeit neu Starten
  }
  if (ServoWinkelDrehkranzIst == Soll) {                                                        // Wenn der Winkel erreicht ist,
    Schritt++;                                                                                  // zum nächsten Schritt gehen
  }
}
 
Zuletzt bearbeitet:
Heute habe ich das Feuerwehrauto umgebaut und eine Lochrasterplatine bestückt. Betrieben wird die Anlage jetzt mit einem akku.
Urlaub ist ziemlich anstrengend 😁
Video ist noch mit Steckbrett.

Video mit Feuerwehrauto, mit Servos bestückt.

Als nächstes wird die Pumpe über einen L298N Motortreiber angesteuert und die Schläuche verlegt. Ein Schnapstank fehlt noch, ich weiß auch nicht wie weit die Pumpe den Schnaps ansaugt... Man darf gespannt sein 😜
 
Morgen werde ich versuchen die Basis für die Technik sowie den Tank zu bauen. Es wird eine Multiplex-Kiste. Die Rundung an der Front wird die Herausforderung sein, aber nichts ist unmöglich!
 
Oben