Projekt mit RFID, Keypad, Stepper und Servo

Hallo zusammen!

Ich versuche mich seit Kurzem auch an der Programmierung eines Arduinos. Bin also ein frischer Anfänger. Mein Projekt möchte ich im Folgenden kurz beschreiben:

Ich habe einen RFID-Receiver, an den 4 Chips in bestimmter Reihenfolge gehalten werden sollen. Daraufhin öffnet ein Stepper eine Tür, hinter dem sich ein Keypad befindet. Dies funktioniert auch alles einwandfrei. Durch das Vorhalten der Chips erhält man den Code für das Keypad. Wird der richtige Code eingegeben, wird ein Servo angesteuert, der eine weitere Tür, bzw. Schublade öffnet. Der Stepper schließt daraufhin wieder die erste Tür. Auch das funktioniert ohne Probleme. Nun hätte ich aber gerne, dass bei falscher Codeeingabe die erste Tür auch wieder geschlossen wird. Dies bekomme ich aber irgendwie nicht so recht hin. Rein theoretisch kann man momentan den richtigen Code einfach durch Ausprobieren herausfinden, da bei den richtigen Zahlen, in der richtigen Reihenfolge, auch mit anderen Zahlen dazwischen, irgendwann der Servo betätigt wird.

Also z.B., der Code ist "1234". Der Servo funktioniert, wenn man z.B. 1527354 eingibt. Dies möchte ich irgendwie abstellen, weiß aber nicht wie.

Da meine Kenntnisse bisher nur sehr rudimentär sind, habe ich den Programmcode aus mehreren zusammen"gebastelt". So sieht er dann wahrscheinlich auch aus. Nicht schön, aber selten. :P Ich hatte zwar einige Fehler, die ich aber mit etwas Recherche und Ausprobieren ausmerzen konnte. Auch habe ich die Kombination der Programme für RFID und Keypad-Schloss zuerst mittels mehrerer Tabs zusamengefügt und letztendlich als unter_loop programmiert.

Kommentare habe ich mal nur für den Keypad-Teil eingetragen. Alles andere funktioniert einwandfrei.

Vielen Dank schonmal für eure Hilfe.

Viele Grüße Christian

Hier mal noch der Programmcode:

#include <Servo.h>
#include <Key.h>
#include <Keypad.h>
#include <Stepper.h>
#include <SPI.h>
#include <MFRC522.h>
#define SS_PIN 53
#define RST_PIN 4
MFRC522 mfrc522(SS_PIN, RST_PIN);
int rot = 6;
int gruen = 5;
int pieps = 8;
int reihenfolge;
int blau = 7;
int SPU = 2048;
int LEDr = 10;
int LEDg = 9;
Stepper Motor(SPU, 22, 24, 23, 25);
Servo servoblau;
const char* password = "1234";
int position = 0;

const byte COLS = 4;
const byte ROWS = 4;
char keys[ROWS][COLS] = {
  {'1', '4', '7', '*'},
  {'2', '5', '8', '0'},
  {'3', '6', '9', '#'},
  {'A', 'B', 'C', 'D'}
};
byte rowPins[ROWS] = {39, 41, 43, 45};
byte colPins[COLS] = { 31, 33, 35, 37 };
Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );

void setup()
{
  Serial.begin(9600);
  SPI.begin();
  mfrc522.PCD_Init();
  Motor.setSpeed (5);
  pinMode (LEDr, OUTPUT);
  pinMode (LEDg, OUTPUT);
  pinMode (blau, OUTPUT);
  pinMode (pieps, OUTPUT);
  pinMode (gruen, OUTPUT);
  pinMode (rot, OUTPUT);// Der Pin 2 ist jetzt ein Ausgang (Hier wird eine LED angeschlossen)
  servoblau.attach (11);
  digitalWrite (LEDr, HIGH);
 
}
void loop()
{
  RFID();
  KEYPAD();
 
}

void RFID()
{
  if ( ! mfrc522.PICC_IsNewCardPresent())
  {
    return;
  }
  if ( ! mfrc522.PICC_ReadCardSerial())
  {
    return;
  }
  long code = 0;
  bool falsch = false;
  bool richtig = false;
  for (byte i = 0; i < mfrc522.uid.size; i++)
  {
    code = ((code + mfrc522.uid.uidByte[i]) * 10);
  }
  Serial.print("Die Kartennummer lautet:");
  Serial.println(code);
  {
    switch (reihenfolge)                 //RFID-Abfrage. 4 RFID-Chips sollen in der richtigen Reihenfolge an dem Empfänger gehalten werden, um einen Code
      // rauszubekommen.
    {
      case 0:
        if (code == 1544770)
        {
          digitalWrite (LEDg, HIGH);
          digitalWrite (LEDr, LOW);
          digitalWrite (rot, HIGH);
          delay (300);
          digitalWrite (rot, LOW);
          delay (100);
          digitalWrite (rot, HIGH);
          delay (300);
          digitalWrite (rot, LOW);
          delay (100);
          digitalWrite (rot, HIGH);
          delay (300);
          digitalWrite (rot, LOW);
          digitalWrite (LEDg, HIGH);
          reihenfolge = 1;
        }
        else
        {
          falsch = true;
        }
        break;
      case 1:
        if (code == 283870)
        {
          digitalWrite (LEDg, HIGH);
          digitalWrite (LEDr, LOW);
          digitalWrite (blau, HIGH);
          delay (300);
          digitalWrite (blau, LOW);
          delay (100);
          digitalWrite (blau, HIGH);
          delay (300);
          digitalWrite (blau, LOW);
          digitalWrite (LEDg, HIGH);
          reihenfolge = 2;
        }
        else
        {
          falsch = true;
        }
        break;
      case 2:
        if (code == 772070)
        {
          digitalWrite (LEDg, HIGH);
          digitalWrite (LEDr, LOW);
          digitalWrite (rot, HIGH);
          digitalWrite (gruen, HIGH);
          delay (300);
          digitalWrite (rot, LOW);
          digitalWrite (gruen, LOW);
          delay (100);
          digitalWrite (rot, HIGH);
          digitalWrite (gruen, HIGH);
          delay (300);
          digitalWrite (rot, LOW);
          digitalWrite (gruen, LOW);
          delay (100);
          digitalWrite (rot, HIGH);
          digitalWrite (gruen, HIGH);
          delay (300);
          digitalWrite (rot, LOW);
          digitalWrite (gruen, LOW);
          delay (100);
          digitalWrite (rot, HIGH);
          digitalWrite (gruen, HIGH);
          delay (300);
          digitalWrite (rot, LOW);
          digitalWrite (gruen, LOW);
          delay (100);
          digitalWrite (rot, HIGH);
          digitalWrite (gruen, HIGH);
          delay (300);
          digitalWrite (rot, LOW);
          digitalWrite (gruen, LOW);
          digitalWrite (LEDg, HIGH);
          reihenfolge = 3;
        }
        else
        {
          falsch = true;
        }
        break;
      case 3:
        if (code == 2503570)
        {
          digitalWrite (LEDg, HIGH);
          digitalWrite (LEDr, LOW);
          digitalWrite (gruen, HIGH);
          delay (300);
          digitalWrite (gruen, LOW);
          delay (100);
          digitalWrite (gruen, HIGH);
          delay (300);
          digitalWrite (gruen, LOW);
          delay (100);
          digitalWrite (gruen, HIGH);
          delay (300);
          digitalWrite (gruen, LOW);
          delay (100);
          digitalWrite (gruen, HIGH);
          delay (300);
          digitalWrite (gruen, LOW);
          delay (100);
          digitalWrite (gruen, HIGH);
          delay (300);
          digitalWrite (gruen, LOW);
          delay (100);
          digitalWrite (gruen, HIGH);
          delay (300);
          digitalWrite (gruen, LOW);
          delay (100);
          digitalWrite (gruen, HIGH);
          delay (300);
          digitalWrite (gruen, LOW);
          digitalWrite (LEDg, HIGH);
          richtig = true;
        }
        else
        {
          falsch = true;
        }
        break;
    }
    if (falsch)
    {
      digitalWrite (LEDg, LOW);
      digitalWrite (LEDr, HIGH);
      tone (8, 100);
      delay (500);
      noTone (8);
      digitalWrite (LEDr, LOW);
      reihenfolge = 0;
    }
    if (richtig)
    {
      delay (1000);
      digitalWrite (LEDg, HIGH);
      digitalWrite (LEDr, LOW);
      tone(8, 100);
      delay (100);
      noTone (8);
      digitalWrite (LEDg, LOW);
      delay (100);
      tone (8, 200);
      digitalWrite (LEDg, HIGH);
      delay (100);
      noTone (8);
      digitalWrite (LEDg, LOW);
      delay (100);
      tone (8, 300);
      digitalWrite (LEDg, HIGH);
      delay (100);
      noTone (8);
      digitalWrite (LEDg, LOW);
      delay (1000);
      digitalWrite (LEDg, HIGH);
      Motor.step(2048);
      digitalWrite(LEDg, LOW);
      digitalWrite (LEDr, HIGH);
      reihenfolge = 0;
    }
  }
}
void KEYPAD()
{
  char key = keypad.getKey();                // nach der Eingabe des richtigen Codes, soll ein Servomotor 
  if (key == '*' || key == '#')                  // eine Schublade oder Klappe öffnen
  {
    position = 0;
    
    Motor.step(-2048);                         // der Stepper soll bei bestimmten Tasten das Keypad wieder
  }                                                   // verschließen
  if  (key == password[position])
  {
    position ++;
    digitalWrite(gruen, HIGH);
    delay(200);
    digitalWrite(gruen, LOW);
  }
  if (position == 4)
  {
    servoblau.write(360);
    delay(1000);
    servoblau.write(90);
  }
  if ((position == 4) && (key != password[position]))  //bei falschem Code schließt der Stepper die Tür
  {                                                                   //wieder
    position = 0;
    Motor.step(-2048);
    digitalWrite(rot, HIGH);
    digitalWrite(pieps, HIGH);
    delay(200);
    digitalWrite(rot, LOW);
    digitalWrite(pieps, LOW);
  }
  
  
}

Hi

Habe Deinen Code nur überflogen und den Sinn dahinter noch nicht erkannt - Kommentare könnten mir da helfen ;)

Ernn ich Deine Ausführungen oben korrekt interpretiert habe (auch nur überflogen ... heute war lang ...): Ich denke, Dein Problem besteht derzeit darin, daß Du so lange wartest, bis die nächste Ziffer endlich als Gültig erkannt wird. Sobald die letzte Ziffer als gültig erkannt wurde, wird das Schloß geöffnet. Die Falsch-Ziffern müssen aber das Schloß zurück setzen - dann klappt nur noch die richtige Reihenfolge mit genau den richtigen Ziffern.

MfG

Moin!

Genau das ist mein Problem, bzw. meine Vorstellung. Im Grunde genommen geht es nur um die KEYPAD-Loop:

void KEYPAD()
{
  char key = keypad.getKey();                // der Stepper soll bei den Tasten '*' und '#' die Tür wieder 
  if (key == '*' || key == '#')                 //schließen
  {
    position = 0;
    
    Motor.step(-2048);                         
  }                                                   
  if  (key == password[position])             //der Servo soll bei richtigem Code aktiviert werden
  {
    position ++;
    digitalWrite(gruen, HIGH);
    delay(200);
    digitalWrite(gruen, LOW);
  }
  if (position == 4)
  {
    servoblau.write(360);
    delay(1000);
    servoblau.write(90);
  }
  if ((position == 4) && (key != password[position]))  //bei falschem Code schließt der Stepper die Tür
  {                                                                          //wieder
    position = 0;
    Motor.step(-2048);
    digitalWrite(rot, HIGH);
    digitalWrite(pieps, HIGH);
    delay(200);
    digitalWrite(rot, LOW);
    digitalWrite(pieps, LOW);
  }

Ich hatte ursprünglich versucht, diese https://funduino.de/tastenfeld-schloss Anleitung einzupflegen. Allerdings hat sich der Servo dann kein bisschen gerührt. Und wenn man den falschen Code eingibt, würde sich auch nichts tun.

So, das Problem mit dem Passwort hab ich folgendermaßen gelöst:

#include <Servo.h>
#include <Key.h>
#include <Keypad.h>
#include <Stepper.h>
#include <SPI.h>
#include <MFRC522.h>
#define SS_PIN 53
#define RST_PIN 4
MFRC522 mfrc522(SS_PIN, RST_PIN);
int rot = 6;
int gruen = 5;
int pieps = 8;
int reihenfolge;
int blau = 7;
int SPU = 2048;
int LEDr = 10;
int LEDg = 9;
Stepper Motor(SPU, 22, 24, 23, 25);
Servo servoblau;
char password[] = "1234";

const byte COLS = 4;
const byte ROWS = 4;
char keys[ROWS][COLS] = {
  {'1', '4', '7', '*'},
  {'2', '5', '8', '0'},
  {'3', '6', '9', '#'},
  {'A', 'B', 'C', 'D'}
};
byte rowPins[ROWS] = {39, 41, 43, 45};
byte colPins[COLS] = { 31, 33, 35, 37 };
Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );

boolean validCode()

{
  static byte counter = 0;
  static char code[sizeof(password)];
  char key = keypad.getKey();
  if (!key) return false;                                        //wird keine Taste gedrückt, passiert nichts
  if (counter == 0) memset(code, 0, sizeof(code));  //Counter wird zurücgesetzt

  switch (key)
  {

    case'*': counter = 0;                              //neuer Code wird gestartet
      break;
    case'#': if (strcmp(code, password) == 0) //Codeeingabe wird bestätigt, PW stimmt überein
      {
        counter = 0;
        return true;
      }
      if (strcmp(code, password) != 0)   // PW stimmt nicht überein
      {
        counter = 0;
        digitalWrite(rot, HIGH);
        digitalWrite(pieps, HIGH);
        delay(500);
        digitalWrite(rot, LOW);
        digitalWrite(pieps, LOW);
        Motor.step(-2048);

        return false;
      }
      break;

    default: code[counter] = key;
      if (counter < sizeof(code) - 1) counter++;

  }
  return false;                            //Code war nicht korrekt

}

void setup()
{
  Serial.begin(9600);
  SPI.begin();
  mfrc522.PCD_Init();
  Motor.setSpeed (5);
  pinMode (LEDr, OUTPUT);
  pinMode (LEDg, OUTPUT);
  pinMode (blau, OUTPUT);
  pinMode (pieps, OUTPUT);
  pinMode (gruen, OUTPUT);
  pinMode (rot, OUTPUT);// Der Pin 2 ist jetzt ein Ausgang (Hier wird eine LED angeschlossen)
  servoblau.attach (11);
  digitalWrite (LEDr, HIGH);
  servoblau.write(360);

}

void loop()
{
  if (validCode())            //wenn das PW stimmt...
  {

    for (int i = 0; i < 1; i++)   //...blinken LEDs, Servo und Stepper laufen
    {
      digitalWrite(LEDr, LOW);
      digitalWrite(LEDg, HIGH);
      digitalWrite(gruen, HIGH);
      digitalWrite(pieps, HIGH);
      servoblau.write(90);
      delay(500);
      digitalWrite(LEDr, HIGH);
      digitalWrite(LEDg, LOW);
      digitalWrite(gruen, LOW);
      digitalWrite(pieps, LOW);
      servoblau.write(360);
      Motor.step(-2048);
    }

  }
}

Allerdings ergibt sich ein weiteres Problem, da dies ja quasi eine Endlos-Schleife ist. Egal, ob das PW richtig oder falsch ist, man kann direkt danach ein neues eingeben.
Kann mir jemand einen Tip geben, wie ich die Schleife beenden kann, egal, ob das PW richtig oder falsch ist?
Sie soll in jedem Fall beendet werden.
Danke schonmal im Voraus.

Viele Grüße
Christian

Hi

Ich denke, Du meinst was Anderes, als Du gerade sagst.

Die loop()-Schleife wird ununterbrochen durchlaufen. Bei jedem Durchlauf wird geprüft, ob der richtige Code eingegeben wurde (true, wenn der Code richtig war + Schloßöffnung, Wartezeit, Schließung). Der loop() ist es herzlich egal, ob der Code jetzt gerade richtig oder falsch ist, sie hat nur die Aufgabe, bei richtigem Code den Riegel auf zu machen - macht Sie wohl auch.

Eine Falsch-Eingabe musst Du einfach nur anders behandeln. Wenn ein falscher Code eingegeben wurde (und nicht schon, wenn ein falsches Zeichen eingegeben wurde - so habe ich nach spätestens 10 Versuchen eine weitere Ziffer raus gefunden), wird einfach für 10 Sekunden nicht mehr auf Richtigkeit geprüft - die ganze Eingabe und Verarbeitung wird beibehalten, nur auch mit korrektem Code bleib das Tor verschlossen. ... oder alternativ eine rote LED an machen und so lange Diese leuchtet (die 10 Sekunden) ist halt Nichts möglich - Du solltest somit auch eine Wartezeit beim Start einfügen, daß es nicht 'schneller' geht, wenn man dem Arduino immer wieder den Saft klaut ;)

MfG

PS: Wenn Du wirklich einen Abbruch meinst (der Arduino macht bis zum Neuanschluß GAR NICHTS MEHR ... Punkt ;) - kannst Du auch bei Falscheingabe ein while(1); als Endlosschleife rein nehmen - dort rennt der Arduino dann im Kreis, bis Ihm der Saft ausgeht oder Reset gedrückt wird.

Danke für die Anwort.

Ich versuche das Problem mal einfach zu schildern:

Ich habe vor, einen Geocache zu bauen. Zuerst sollen halt die RFID-Chips ran und dann der Code eingegeben werden. Soweit die Grundidee. Ich hatte zunächst die beiden einzelnen Programme (also RFID und KEYPAD) als unter_loops programmiert, sodass quasi beide nebeneinander (oder wie auch immer) laufen. Wäre grundsätzlich auch möglich, nur ist der ganze Geocache dann nicht mehr idiotproof. Werden statt der Codeeingabe nochmals die RFID-Chips drangehalten, würde der Stepper nochmals versuchen, die Tür zu öffnen. Da sie aber bereits offen ist, würde die Mechanik beschädigt werden. Daher kam mir die Idee, den RFID-Teil als Bedingung für den KEYPAD-Teil vorauszusetzen. Das ist mir auch mittels while gelungen. Aber da die Tür bei falscher Codeeingabe schließt (bei richtiger zwar auch, aber dann braucht man das KEYPAD nicht mehr), muss in dem Fall die RFID-Loop nochmal von vorne beginnen. Und das tut sie nunmal nicht. Ist aber zwingend erforderlich, um den richtigen Code rauszubekommen.

Und wie gesagt, dass Passwort-Problem aus dem ersten Post habe ich in den Griff bekommen. Durch den neuen Programmcode wird ja erst die Zeicheneingabe verglichen, wenn '#' gedrückt wird. Das funktioniert auch zuverlässig.

Merke Dir doch in einer Variablen den aktuellen Status, dann kann es bei offen kein öffnen geben. grob so:

uint8_t status = 0;
// 0=zu, 1=RFID ok, 2 = Code ok, 3 = open, 4 = schließen

oder was zu Deinen zuständen passt.

Gruß Tommy

Vielleicht noch eine kurze Ergänzung:

Der richtige Code ist nicht gleich offensichtlich. Der ein oder andere wird mit Sicherheit gleich den richtigen Code rausfinden, aber die Mehrheit wohl eher nicht.

Im Grunde genommen könnte ich nach Falscheingabe einfach auffordern, die Batterie zu entnehmen und den Arduino nochmal von neu zu starten. Das macht man dann solange, bis man entweder den richtigen Code raus hat, oder die Batterien leer sind. :P

Praktischer wäre es aber, wenn nach Falscheingabe wieder die RFID-Loop startet. Und das bekomme ich leider nicht hin.

Tommy56: Merke Dir doch in einer Variablen den aktuellen Status, dann kann es bei offen kein öffnen geben. grob so:

uint8_t status = 0;
// 0=zu, 1=RFID ok, 2 = Code ok, 3 = open, 4 = schließen

oder was zu Deinen zuständen passt.

Gruß Tommy

Das müste ich mal ausprobieren. Da muss ich mich mal einlesen, wie ich das integrieren kann. Bin ja noch blutiger Anfänger.

Nimm einen Zettel (ja richtig oldschool ;) ) und schreibe Dir mal auf, wie Du das machen würdest ohne Elektronik, wenn Du das "Männchen" in der Kiste wärst und keine Kleinigkeit vergessen. Das hilft ungemein.

Gruß Tommy

Hab eine provisorische Lösung gefunden. Nach Eingabe eines falschen Codes Software-Reset.

Zwar nicht elegant, aber zweckmäßig.