125khz rfid Reader - char* / char[]

Hallo,

ich würde gerne ein Tür mit einem Rfid chip "öffnen" ! Zuvor probiere ich es erstmal mit einer LED, die grün leuchtet wenn die richtige RFID Karte drangehalten wurde und rot wenn eine falsche Rfid Karte erkannt wurde.
Dazu habe ich mir erstmal dieses RFID modul besorgt http://www.seeedstudio.com/depot/-p-171.html.
Anschließend habe ich erstmal so ein paar Tutorials gemacht:
http://www.instructables.com/id/Arduino-and-RFID-from-seeedstudio/ (das hat mir an sich gut gefallen, nur funktioniert der Sketch bei mir nicht so wie er sollte. Es wurde nicht der "fertige" RFID Tag in eine Zeile in den Seriellen Monitor geschrieben, sondern jede Zeile kam eine neue Zahl dazu ?!)
RFID Button - 16mm (125KHz) - Proto-PIC
Da ist ja weiter unten, unter dem Punkt Documents ein Bespiel für den Arduino mit einer Anwendung für VB. Ich habe mich natürlich nur auf den "Arduino Teil" konzentriert.
Erstmal der Sketch von der Website:

// Interfacing the 125Khz RFID module - UART(PPRFR101A1M-X)
// Created 20 May 2011 www.proto-pic.co.uk

#include <SoftwareSerial.h>

String msg;
char inChar;
int x; 
SoftwareSerial Rfid(2, 3);    // RX as pin 2

void setup()
{
 Serial.begin(115200);
 Rfid.begin(9600);
}

void loop()

{
  
while (inChar = Rfid.read() != 2)    // Wait for start byte from Rfid reader when tag is swiped
{
}

msg = "";
for (x = 0; x < 12; x++)
{
    delay(10);  
    inChar = Rfid.read();
    msg += inChar;
}

Serial.print(msg);    // Sends the ID plus the checksum byte (12 bytes)
}

Da dieser Sketch erstmal nur die Tags lesen konnte, und auch vernünftig dem Seriellen Monitor die Tags übergeben konnte, habe ich diesen Sketch mal etwas abgeändert !

#include <SoftwareSerial.h>

#define RICHTIG "05003E22627B" 
String msg;
char inChar;
int x; 
SoftwareSerial Rfid(2, 3);    // RX as pin 2
int rot = 5;
int gruen = 6;
int rgbblinkhigh[] = {250, 250, 2000};
int rgbblinklow[] = {250, 250, 100};
boolean var = true; 

void setup()
{
 pinMode(rot, OUTPUT);
 pinMode(gruen, OUTPUT);
 Serial.begin(9600);
 Rfid.begin(9600);
}

void loop()

{
  digitalWrite(rot, HIGH);
  digitalWrite(gruen, HIGH);
  
if(var == true) //dieses soll bewirken das der Rfid reader nur einmal liest, wenn er einen Tag erkennt ! also nicht mehrmals in den Seriellen monitor schreiben. (Das ist das Problem)
{
  var = false;
while (inChar = Rfid.read() != 2)    // Wait for start byte from Rfid reader when tag is swiped
{
}

msg = "";

for (x = 0; x < 12; x++)
{
    delay(10);
    inChar = Rfid.read();
    msg += inChar; 
    
}
}

Serial.println(msg);
// Sends the ID plus the checksum byte (12 bytes)

if(msg.indexOf(RICHTIG)>=0) 
{
  Serial.println("Zutritt erlaubt");
  for(int i = 0; i < 3; i++)
       {
       digitalWrite(gruen, LOW);
       delay( rgbblinkhigh [i]);
       digitalWrite(gruen, HIGH);
       delay( rgbblinklow [i]); 
       }      
 
}

else
{
  Serial.println("Zutritt verboten");
  for(int i = 0; i < 3; i++)
       {
       digitalWrite(rot, LOW);
       delay( rgbblinkhigh [i]);
       digitalWrite(rot, HIGH);
       delay( rgbblinklow [i]); 
       }              
       
  }
  var = true; //wieder auf true, damit wieder ein Tag gelesen werden kann
 
}

Ich hoffe jeder versteht was da so in dem Sketch "vor sich geht" ! Sonst fragt !
Bisher liest das RFID module die Tags ja ununterbrochen (d.h. der Tag wird immer weiter an den Monitor gesendet), natürlich nur wenn er einen erkennen kann, sprich wenn jemand einen vor die Antenne hält. Ich würde es gerne schaffen, dass, wenn der RFID reader ein Chip erkennt, diesen nur einmal liest, und danach eine "blink" - Folge der LED nur einmal startet. Zur Zeit ist es noch so, dass wenn ich den Chip mal etwas zu lang gegen die Antenne gehalten habe, dann wurde die blink Folge zweimal oder dreimal, je nach dem wie lange man den Chip vor die Antenne hält , abgespielt. Dazu wurde natürlich auch der Tag mehrmals in den Seriellen Monitor geschrieben.
Ich würde es nun gerne hinbekommen, dass das RFID module sozusagen nur einmal liest...sodass nur einmal die LED blinkfolge abläuft und auch nur einmal der Tag in den Seriellen Monitor geschrieben wird....ich finde das gerade echt schwer, dass hier alles so zu beschreiben !
Dazu habe ich mir erstmal gedacht, einfach eine If bedingung aufzustellen, wie man in dem Sketch sieht (var). Also solange var = true ist, soll das Module lesen und wenn false halt nicht. Direkt nach der Bedingung wird var auf false gesetzt, sodass dieser Abschnitt erstmal nicht nochmal durchlaufen kann (habe ich mir so vorgestellt), und wenn die LED fertig mit ihrer Blinkfolge sind, dann wird var wieder auf true gesetzt, wodurch der Abschnitt, also der rfid reader, wieder lesen kann. Da man den Chip wieder wegnimmt, sobald die LED anfängt so blinken, müsste es ja eigentlich passen. Aber irgendwie bekomme ich das nicht so hin, wie ich das gerne haben möchte ! :confused:

Also ich hoffe mich kann da jemand etwas helfen....entweder ist es mega einfach und ich bin einfach zu doof das mal wieder zu finden :smiley: oder es ist aus irgendeinem Grund nicht so einfach wie ich mir gedacht habe...
Falls ihr das nicht so versteht, was ich jetzt so genau meine, dann fragt nochmal ! Wird wahrscheinlich so sein, habe mir leider nicht so viel Mühe gegeben, ist aber auch nicht immer einfach hier so alles im Detaill zu beschreiben !

Hoffe auf hilfreiche Antworten/Fragen ! :slight_smile:

Grüße Lorenz

Das ist eigentlich recht einfach. Du nimmst einfach die ID der aktuellen Karte und speichert sie. Jedes mal, wenn du nun eine neue Karte empfängst, vergleichst du die gespeicherte ID mit der, die du gerade gelesen hast. Wenn die beiden gleich sind, machst du nichts. Und wenn sie sich unterscheiden führst du deine Blinksequenz durch.

chrisliebaer:
Das ist eigentlich recht einfach. Du nimmst einfach die ID der aktuellen Karte und speichert sie. Jedes mal, wenn du nun eine neue Karte empfängst, vergleichst du die gespeicherte ID mit der, die du gerade gelesen hast. Wenn die beiden gleich sind, machst du nichts. Und wenn sie sich unterscheiden führst du deine Blinksequenz durch.

Für den Türöffner solltest du aber noch eine zeitliche Komponente einbauen, sonst kannst du die Tür mit dem selben Tag nicht mehr öffnen, bevor sie mit einem anderen Tag geöffnet wurde.
Oder du machst es gleich nur mit einer gespeicherten Zeit. Beim Lesen des Tags speicherst du die Zeit in eine Variable: lastread = millis();
beim Auslösen des Öffners (oder LED) prüfst du, ob das letzte Lesen mehr als 5(?) Sekunden her ist:

if(abs(millis()-lastread) > 5000){
//Aufmachen oder Blinken
}

Vielen Dank erstmal für die Antworten !

Für den Türöffner solltest du aber noch eine zeitliche Komponente einbauen, sonst kannst du die Tür mit dem selben Tag nicht mehr öffnen, bevor sie mit einem anderen Tag geöffnet wurde.

Das stimmt allerdings ! Wäre echt unpraktisch wenn man vorher immer erst einen anderen Tag benutzen muss, damit das wieder funktioniert.

Mit millis() kenn ich mich jetzt eigentlich gar nicht so wirklich mal aus ! Habe mal unter References gesucht und habe da auch was gefunden.
Aber da ist das Beispiel auf etwas anderes bezogen :smiley: Ich weiß auch gar nicht ob das auch wirklich funktioniert ! millis() - Arduino Reference

Ich habe dann mal gedacht so zu machen:

while (inChar = Rfid.read() != 2)    // Wait for start byte from Rfid reader when tag is swiped
{
}
lastread = millis();

Und weiter unten, jetzt nur bei der grünen Led:

if(msg.indexOf(RICHTIG)>=0) 
{
  if(abs(millis()-lastread > 120)
{ //hier wusste ich nicht genau welche Zahl am besten passt, mit 120 funktioniert es noch gerade so. 
  Serial.println("Zutritt erlaubt");
  for(int i = 0; i < 3; i++)
       {
       digitalWrite(gruen, LOW);
       delay( rgbblinkhigh [i]);
       digitalWrite(gruen, HIGH);
       delay( rgbblinklow [i]); 
       }      
} 
}

Ich muss schon irgendwie noch relativ viel lernen so mit millis(); :confused:
Wäre nett wenn mich da jemand unterstützen könnte !

Grüße
Lorenz

Hallo Lorenz,

millis() gibt die Anzahl der Millisekunden (tausendstel!) zurück, die vergangen sind, seit der Arduino gestartet wurde. Mit

if(abs(millis()-lastread) > 5000)

sagst du dem Arduino "führ das folgende aus, wenn der Unterschied zwischen dem akuellen millis()-Wert und dem beim letzten Lesen mindestens 5000 ist", sprich wenn es länger als 5 Sekunden her ist. Bei dir sind es 0,12 Sekunden... bissl kurz vielleicht.
abs() steht für "Absolutwert" (macht z.B. **-**2000 zu 2000). Das hat zwei Gründe: Erstens must du nicht aufpassen, welche Zahl zu von welcher abziehst und zweitens fängt millis() nach ca. 50 Tagen wieder bei Null an.

ulli.

Wenn lastread als unsigned long (oder uint32_t) definiert ist, kannst Du Dir das abs() sparen, denn ein unsigned integer kann nicht negativ werden. Aber es ist durchaus relevant, welche Zahl Du von welcher abziehst, da hilft dann das abs() nicht weiter.

Sorry, Pylon... aber das sehe ich anders.

Nehmen wir mal last=80000, now=90000, schwellenwert 5000 (-> die Bedingung sollte TRUE sein)

now-last=10000 > 5000 true
abs(now-last)=10000 > 5000 true
last-now=-10000 < 5000 false
abs(last-now)=10000 > 5000 true

nehmen wir vereinfachend an, dass der Arduino nach 99999 wieder bei 0 anfängt und nehmen last=95000, now=2000, schwellenwert 5000 (-> die Bedingung sollte TRUE sein)

now-last=-93000 < 5000 false
abs(now-last)=93000 > 5000 true
last-now=93000 > 5000 true
abs(last-now)=93000 > 5000 true

Bei "rund um die 99999" in diesem Beispiel ist es nicht egal, welche Zahl ich von welcher abziehe; aber das sollte in der Realität (alle ~50 Tage) auch untergehen.

Oder sehe ich da was falsch?

ulli.

Oder sehe ich da was falsch?

Ja. Bei einem unsigned integer gibt es KEINE negativen Zahlen. Ein unsigned long kann Zahlen von 0 bis 4294967295 darstellen. Wenn ich bei 4294967295 eins dazu zähle, kriege ich 0. Wenn ich von 0 eins abzähle, kriege ich 4294967295.

In Deinem Beispiel:

now-last = 10000
last-now = 4294957296

Die abs()-Funktion hat keine Funktion auf diesen Zahlen.

pylon:
Wenn ich von 0 eins abzähle, kriege ich 4294967295.

Danke, dieses Verhalten kannte ich noch nicht.
Again what learned :slight_smile:

Hallo,
erstmal vielen Dank für die Antworten, auch wenn die jetzt nicht direkt etwas mit der Frage zu tun hatten.

@jubi40 : Also ich verstehe jetzt was millis() genau machen, nur weiß ich noch nicht gut drum bescheit wie ich die einem Sketch anwenden kann.
Ich wüsste absolut nicht wie ich das mit dem RFID Sketch in den Griff bekommen soll, du hast mir zwar schon da so ein Schnipsel mal geschrieben, aber so ganz funktioniert das auch nicht -_-

Der Aufruf von millis() liefert Dir die Zeit seit dem Starten des Arduinos in Millisekunden(!).

Der Post von jubi40 wollte Dir zeigen, dass Du die Zeit, wann Tag gelesen wurde, abspeichern kannst. Das macht nur dann Sinn, wenn Du auch den Inhalt des Tags speicherst, denn wenn unterschiedliche Tags gelesen werden, interessiert Dich der zeitliche Abstand wahrscheinlich nicht.

Also sieht das etwa so aus:

Lese Tag - überprüfe Tag auf Gültigkeit - wenn gültig, führe Aktion aus und speichere Zeitpunkt in "lastmillis" und Tag in "lasttag" ab.
Lese Tag - wenn Tag == lasttag und (millis()-lastmillis < 5000) -> speichere nur Zeitpunkt in lastmillis, andernfalls führe die Aktion trotzdem aus, denn die letzte Schlüsselverwendung liegt mehr als 5 Sekunden zurück.
Die 5 Sekunden sind willkürlich gewählt, Du kannst sie auch anpassen. Wichtig ist, dass Du den Wert von lastmillis erst änderst, wenn Du ihn mit millis() verglichen hast, sonst ist die Funktion unnütz.

Ouh...Vielen Dank !
Hatte zur Zeit das kleine Problem erst mal auf Eis gelegt, aber ich habe dann mal gerade versucht das mit deiner super Anleitung zu basteln. Der Sketch ist jetzt nicht wirklich komprimiert geschrieben, einfach mal so dahin "geworfen" , von daher verzeiht mir so paar undurchsichtigkeiten, falls es welche geben sollte.
An sich funktioniert er eigentlich, solange ich immer nur den "RICHTIGEN" Tag an die Antenne halte, dann klappt alles so wie es geplant war, also dass das "blinken" nur einmal ausgeführt wird. An sich schon mal gut ! Aber wenn ich dann einen anderen Tag dranhalte, dann funktioniert das nicht so ?! Entweder übersehe ich da gerade was relativ einfaches, oder ich habe mich bei der ganzen Vorgehensweise geirrt ?!
Hier erstmal der Code:

#include <SoftwareSerial.h>

String RICHTIG  = "05003E22627B";
String msg;
String lastRead; //Speichert zuletzt erkannten RFID Tag
char inChar;
int x; 
SoftwareSerial Rfid(2, 3); // RX als pin 2
int rot = 11;
int gruen = 12;
int rgbblinkhigh[] = {250, 250, 2000};
int rgbblinklow[] = {250, 250, 100};
boolean var = false;
long lastMillis; // speichert millis();


void setup()
{
 pinMode(rot, OUTPUT);
 pinMode(gruen, OUTPUT);
 Serial.begin(9600);
 Rfid.begin(9600);
}

void loop()

{
  unsigned long currentMillis = millis();
  
  digitalWrite(rot, HIGH); 
  digitalWrite(gruen, HIGH);
  
while (inChar = Rfid.read() != 2)    
{
}
msg = "";

for (x = 0; x < 12; x++)
{
    delay(10);  
    inChar = Rfid.read();
    msg += inChar;
    
}

if(msg == RICHTIG)
{
if(msg == lastRead)
{
  if(millis() - lastMillis > 5000) {lastMillis = currentMillis; lastRead = 0;} //Lastread "Resetten"   
}
else 
{
  Serial.println(msg);
  Serial.println("Zutritt erlaubt");
  for(int i = 0; i < 3; i++)
       {
       digitalWrite(gruen, LOW);
       delay( rgbblinkhigh [i]);
       digitalWrite(gruen, HIGH);
       delay( rgbblinklow [i]);             
       } 
  lastMillis = currentMillis;
  lastRead = msg;
}
}

else
{ 
  if(msg == lastRead)
  {
  if(millis() - lastMillis > 5000) {lastMillis = currentMillis; lastRead = 0;} //Lastread "Resetten"   
  }
else
{
  Serial.println(msg);
  Serial.println("Zutritt verboten");
  for(int i = 0; i < 3; i++)
       {
       digitalWrite(rot, LOW);
       delay( rgbblinkhigh [i]);
       digitalWrite(rot, HIGH);
       delay( rgbblinklow [i]); 
       }
  lastMillis = currentMillis;
  lastRead = msg;      
}
}
}

Ich hoffe jeder blickt durch und mir könnte jemand helfen, falls ich bald noch nicht auf den Fehler komme !

Grüße
Lorenz

Aber wenn ich dann einen anderen Tag dranhalte, dann funktioniert das nicht so ?!

Wie funktioniert's denn in diesem Fall? Was passiert? Ich hätte erwartet, dass die rote LED das Blinkmuster zeigt und "Zutritt verboten" auf die serielle Schnittstelle geschrieben wird.

Dein Sketch ist etwas übermässig komplex geschrieben, scheint aber grundsätzlich in Ordnung. Der grosse Fehler ist die Verwendung der String-Klasse. Die solltest Du nicht verwenden, da der Speicher im Nu fragmentiert ist und schnell kein dynamischer Speicher mehr zur Verfügung steht. Zusätzlich ist in den aktuellen IDEs (bzw. den dort enthaltenen C-Bibliotheken) ein Fehler in der free()-Funktion, womit ein Sketch, der viele Stringmanipulationen mit der String-Klasse macht, früher oder später in einem unkontrollierten Zustand endet.

Ups, hab ich ganz vergessen. Also wenn ich den falschen Tag dranhalte dann blinkt das wieder öfters !

Dein Sketch ist etwas übermässig komplex geschrieben, scheint aber grundsätzlich in Ordnung.

Joa das stimmt schon ! Ich bin nicht so einer der immer so alles gleich super kompriemirt schreibt :smiley:

Der grosse Fehler ist die Verwendung der String-Klasse. Die solltest Du nicht verwenden, da der Speicher im Nu fragmentiert ist und schnell kein dynamischer Speicher mehr zur Verfügung steht. Zusätzlich ist in den aktuellen IDEs (bzw. den dort enthaltenen C-Bibliotheken) ein Fehler in der free()-Funktion, womit ein Sketch, der viele Stringmanipulationen mit der String-Klasse macht, früher oder später in einem unkontrollierten Zustand endet.

Ist String wirklich so katastrophal ? verbraucht wohl leider viel Speicher -_- Was könnte ich gegen die Verstreuung machen ? Arrays bilden ? Oder wie meinst du das ? Das vielleicht : http://arduino.cc/en/Reference/String Ich weiß gerade absolut nichts...
Und unter free() verstehe ich absolut nichts....
Scheint mir ja doch nicht so ganz einfach !

Grüße Lorenz

Ist String wirklich so katastrophal ?

Ja, leider schon. Auch wenn das Teil schön angenehm zu benutzen ist (vor allem für Anfänger), ist auf dem Arduino leider wirklich dringendst davon abzuraten, mal von ein paar Übungssketches abgesehen.

Verwende char-Arrays, Vergleiche machst Du dann mit strcmp(), Anhängen mit direktem Zugriff auf den entsprechenden Index plus danach noch ein Null-Byte schreiben, etc.

// nur Beispielcode, nicht zum unveränderten Gebrauch gedacht
#define BUFFER_SIZE

char buffer[BUFFER_SIZE];
byte i = 0;
char c;
buffer[0] = 0;
while (i < BUFFER_SIZE - 1 && (c = Serial.read()) > 48) {
  buffer[i++] = c;
  buffer[i] = 0;
}

if (strcmp("2234553", buffer) == 0) {
  Serial.println("Got the right one!");
}

Ouh nein, ich dachte ich komm schnell da mit voran, aber so wie es aussieht nicht -_-
Ich nehme jetzt mal nur diesen Ausschnitt:

if (strcmp("2234553", buffer) == 0) {
Serial.println("Got the right one!");
}

Ich habe mir mal dein Beispiel angeschaut. Soweit ich das verstanden habe, soll dort ein String und ein char Element mit einander verglichen werden. Lieg ich da richtig ?

Also ich habe das dann mal folgt ausprobiert:

#define RICHTIG "05003E22627B"
char msg;

if (strcmp(msg, RICHTIG) == 0) {
  Tue etwas bestimmtes);
}

Msg wurde natürlich vorher festgelegt:

while (inChar = Rfid.read() != 2)    
  {
  }
  for (x = 0; x < 12; x++)
  {
    delay(10);  
    inChar = Rfid.read();
    msg += inChar;

  }

Ich denke mal ich stelle mich da gerade echt dumm an :confused: Habe damit noch nie so richtig gearbeitet und viel im Internet dazu gibt es auch nicht...
Hoffe mal ich bekomme da etwas Unterstützung, was ich aber denke ! :slight_smile:

Grüße
Lorenz

lgrube96:
Ich denke mal ich stelle mich da gerade echt dumm an :confused:

Warum nennst Du einen Thread, in dem es um einen völlig einwandfrei funktionierenden RFID-Reader geht, eigentlich "125khz rfid Reader - Problem"? Der Reader funktioniert doch fehlerfrei und er sendet raus, was er raussenden soll?

Warum nicht sowas wie "serielle Schnittstelle auslesen" im Subject, wenn darin das eigentliche Problem besteht?

lgrube96:
Hoffe mal ich bekomme da etwas Unterstützung, was ich aber denke ! :slight_smile:

Fragen zum Auslesen der seriellen Schnittstelle kommen hier im Forum alle paar Tage vor.
Bei manchen kommt sogar das Wort "seriell" im Subject vor, z.B.:
http://arduino.cc/forum/index.php/topic,136027.0.html

Hilft meine Antwort mit dem geposteten Code in diesem Thread (Reply #3) nicht, Dich auf eine Idee zu bringen, wie man nach fest vorgegebenen Begriffen fahnden kann, die über eine serielle Schnittstelle reinkommen? Wenn Du in dem von mir geposteten Code z.B. mal "Serial.available()" durch "Rfid.available()". "Serial.read" durch "Rfid.read" und "test" durch "05003E22627B" bzw. RICHTIG ersetzt?

BTW: Die unbedachte Verwendung von String-Objekten in Arduino-Sketchen ist böse (besseres Mittel: C-Strings/Char-Arrays) und das Reinmixen eines Pointers auf ein String-Objekt anstelle eines Pointers auf einen C-String in eine Funktion, die nur mit C-Strings umgehen kann, führt immer zu irregulärem Programmverhalten.

Vielen Dank erstmal für deine Rückmeldung !
In letzter Zeit habe ich auch schon gemerkt das der Titel kaum passt...mir fällt einfach gerade nichts passendes ein ! Und "serielle Schnittstelle auslesen" ist auch nicht so das ganze ! Weil ich kann ja die Ths lesen usw.. Es ging eigentlich nur um das Problem das der immer je nachdem wie lange man den Tag dranhält, die Led öfters blinken lässt !
Jetzt kommt aber noch das mit "kein Sting" verwenden usw.

Ich werde mir mal deinen Vorschlag da angucken..mal schauen was sich machen lässt !

Grüße
Lorenz

lgrube96:
Es ging eigentlich nur um das Problem das der immer je nachdem wie lange man den Tag dranhält, die Led öfters blinken lässt !

Wenn das ein Problem ist, braucht man sich ja nur in einer long-Variablen die Zeit zu merken, wann es zuletzt geblinkt hat, und dann ignoriert man eben so lange alle weiteren Codes wie kein wiederholtes Blinken auftreten soll.

Wenn du mal auf der 1. Seite lesen würdest !, dann wüsstest du, dass ich das Problem eigentlich schln fasst erledigt habe!!!

Jetzt ist halt nur noch das Problem mit dem String das ich diese weglasse !