125khz rfid Reader - char* / char[]

Danke ! Oh maaan ! Das habe ich sowas mal übersehen, geht ja mal gar nicht ! :confused:
Naja...werde das später mal ausprobieren, mal schauen ob das wirklich der Fehler war !

BTW Schön und gut aber wofür soll ich das bitte gebrauchen wenn ich das schon längst, aber wirklich längst gelöst habe ?!
Es wär wirklich besser wenn du mal die erste Seite von dem Thread liest ! Das würde einiges leichter machen ! Naja....du hast es zum mindest schonmal erkannt.

Grüße
Lorenz

lgrube96:
Naja...werde das später mal ausprobieren, mal schauen ob das wirklich der Fehler war !

Der zu große Index in "msg[13] = 0;" ist zwar ein Fehler, aber wenn Du stattdessen "msg[12] = 0;" schreibst, ist es wohl immer noch falsch.

Denn die ganze Kopierreihenfolge zwischen msg und lastRead ist meiner Meinung nach genau vertauscht für den Zweck, für den Du es benötigst.

Statt:

for(int i = 0; i < 13; i++)
{
  msg[i] = lastRead[i];
}
msg[13] = 0;

sollte es wohl eher heißen:

for(int i = 0; i < 13; i++)
{
  lastRead[i]=msg[i];
}
lastRead[12] = 0;   // diese Zeile ist doppelt gemoppelt, das \0 Zeichen wird schon in der Schleife kopiert

oder wenn ich es geschrieben hätte mit weniger Code ohne Schleife:

strcpy(lastRead,msg);

Soweit ich den Code überblicke, sollte er dann wenigstens so funktionieren, wie Du es wohl beabsichtigst.

Vielen Dank jurs, dass du den Fehler entdeckt hast ! Ist auch wieder so ein Flüchtigkeitsfehler, die mir irgendwie oft passieren...

Naja, auf jedenfall, habe ich das mit dem Kopieren der beiden msg und lastRead jetzt mit strcpy erledigt, ist einfach bequemer..

Aber mittlerweile kotzt mich dieses Problem echt an ! Es ist immer noch das gleiche, wie es mal am Anfang war ! Ich glaube ich brauch das jetzt nicht nochmal hier zu beschreiben ! Aber wooooorran liegt das ? Ich dreh echt durch....der Code ist doch jetzt mittlerweile einwandfrei ?!

Aktueller Code:

#include <SoftwareSerial.h>

char msg[13];
char lastRead[13];
char inChar;
int x; 
SoftwareSerial Rfid(2, 3); // RX als pin 2

int rot = 11;
int gruen = 10;

long lastMillis;


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

}

void loop()
{  
  unsigned long currentMillis = millis();

  while (inChar = Rfid.read() != 2)    
  {
  }  

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

  if(strcmp("05003E22627B", msg)== 0)
  {

    if(strcmp(msg, lastRead)== 0 )
    {
      if(millis() - lastMillis > 5000) {
        lastMillis = currentMillis; 
        lastRead[0] = 0;
      } //Lastread "Resetten"   
    }

    else
    { 

      Serial.println("Zutritt genehmigt");
      Serial.println( msg);    
      digitalWrite(gruen, LOW);
      delay(400);
      digitalWrite(gruen, HIGH);
      delay(400);             
      lastMillis = currentMillis;
      strcpy(lastRead, msg);
      
    }
  }
  else
  {    
    if(strcmp(msg, lastRead)== 0 )
    {
      if(millis() - lastMillis > 5000) {
        lastMillis = currentMillis; 
        lastRead[0] = 0;
      } //Lastread "Resetten"   
    }
    else
    {
      Serial.println("Zutritt verweigert");
      Serial.println( msg);
      digitalWrite(rot, LOW);
      delay(400);
      digitalWrite(rot, HIGH);
      delay(400); 
      lastMillis = currentMillis;
      strcpy(lastRead, msg);  
    }

  }
}

Grüße
Lorenz

Ich dreh echt durch....der Code ist doch jetzt mittlerweile einwandfrei ?!

Einwandfrei ist anders:

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

Du liest von der SoftwareSerial ohne zu überprüfen, ob überhaupt ein Zeichen empfangen wurde. Wenn Du das machen würdest, wäre dafür der delay() komplett überflüssig.

Zudem: überall, wo in Deinem Code "int" steht, sollte "byte" oder "uint8_t" stehen.

Es ist immer noch das gleiche, wie es mal am Anfang war !

Heisst das, wenn Du den Tag 2 Sekunden an den Reader hältst, wird der Blinkcode mehrfach ausgegeben? Falls Du ihn länger als 5 Sekunden hin hältst, soll er mehrfach ausgegeben werden (steht so in Deinem Code).

Du liest von der SoftwareSerial ohne zu überprüfen, ob überhaupt ein Zeichen empfangen wurde. Wenn Du das machen würdest, wäre dafür der delay() komplett überflüssig.

Ich habe das einfach aus einem Code vom Internet so übernommen...

Zudem: überall, wo in Deinem Code "int" steht, sollte "byte" oder "uint8_t" stehen.

Warum das ? Was ändert das ?

Heisst das, wenn Du den Tag 2 Sekunden an den Reader hältst, wird der Blinkcode mehrfach ausgegeben? Falls Du ihn länger als 5 Sekunden hin hältst, soll er mehrfach ausgegeben werden (steht so in Deinem Code).

Ja es heisst, dass ich den Tag 2 Sekunden an den Reader halte und der Blinkcode wird mehrfach ausgefürht !
Das es öfters ausgeführt wird, wenn ich den länger als 5 Sekunden dranhalte war mir schon von vornrein klar, wird ja auch so definiert...

Grüße Lorenz

EDIT: Auch byte ändert nichts

... und es ist immer genau derselbe (falsche) Code ?

Und wenn du den richtigen Tag beliebig lang dranhältst, siehst du die grüne LED und den "genehmigt" - Text nur einmal ?

( Ich seh's auch nicht, falls dir das hilft )

Ein byte ist nur halb so groß wie ein int, aber solang der Arduino schnell genug ist und genug Platz hat, ist das nicht soo wichtig, solang du es nur für dieses Projekt brauchst.
Das delay(10) ist auch nicht wirklich entscheidend.

"einwandfrei" ist halt ein dehnbarer Begriff :wink:

lgrube96:
Ja es heisst, dass ich den Tag 2 Sekunden an den Reader halte und der Blinkcode wird mehrfach ausgefürht !
Das es öfters ausgeführt wird, wenn ich den länger als 5 Sekunden dranhalte war mir schon von vornrein klar, wird ja auch so definiert...

Hast Du mal ausgemessen, mit wieviel "Bytes pro Sekunde" Dein RFID-Reader die Zeichen rausfeuert, wenn er feuert?

Weil, Du hast ja eine schnarchlangsame Verarbeitung mit 12 * 10 ms delay beim Einlesen plus 2 * 400 ms delay beim Blinken, d.h. wenn der erste Code verarbeitet wird, wird der zweite Code danach erst nach 120 + 800 = 920 ms = 0,92 s aus dem seriellen Eingangspuffer ausgelesen.

Die Größe des seriellen Eingangspuffers beträgt (wenn es wie bei der Hardware-Serial ist) 64 Zeichen, ein Code mit Start- und Endezeichen sind 14 Zeichen, d.h. es passen 4 ganze und ein zerhackter Code in den Eingangspuffer.

Wenn die Zeichen so schnell reinkommen, dass der serielle Eingangspuffer wegen Deiner langsamen Verarbeitung überläuft, solltest Du den Puffer zumindest immer dann leeren, wenn Du vorher die langen delays beim Blinken aufgerufen hast, also nach jedem:
delay(400);
den seriellen Eingangspuffer leeren mit:
while (Rfid.available()) Rfid.read();

Macht das einen Unterschied?

Ein byte ist nur halb so groß wie ein int, aber solang der Arduino schnell genug ist und genug Platz hat, ist das nicht soo wichtig, solang du es nur für dieses Projekt brauchst.
Das delay(10) ist auch nicht wirklich entscheidend.

Ah ok, vielen Dank !
Und wenn delay(10) weglasse, dann funktioniert da absolut nichts ! Dann wird der Tag immer nur als falsch erkannt. Warum das so ist kann ich nicht sagen, aber es macht einen Unterschied.

Und jurs du hast meinen Tag echt gerade irgendwie gerettet !
Es funktioniert ! ^^
Schöne Vorrechnung mit Zeiten und bla...wüsste nicht so was man da so wirklich mit Anfangen sollte...aber passt !

Aber warum macht while (Rfid.available()) Rfid.read(); alles gleich funktionsfähig ?
"nur" weil der cache wieder geleert wird ?!
Hier jetzt der endgültige Code (bis jetzt):

#include <SoftwareSerial.h>

char msg[13];
char lastRead[13];
char inChar;
byte x; 
SoftwareSerial Rfid(2, 3); // RX als pin 2

byte rot = 11;
byte gruen = 10;

long lastMillis;


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

}

void loop()
{  
  unsigned long currentMillis = millis();

  while (inChar = Rfid.read() != 2)    
  {
  }  

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

  if(strcmp("05003E22627B", msg)== 0)
  {

    if(strcmp(msg, lastRead)== 0 )
    {
      if(millis() - lastMillis > 5000) {
        lastMillis = currentMillis; 
        lastRead[0] = 0;
      } //Lastread "Resetten"   
    }

    else
    { 

      Serial.println("Zutritt genehmigt");
      Serial.println( msg);    
      digitalWrite(gruen, LOW);
      delay(400);
      while(Rfid.available()) Rfid.read(); 
      digitalWrite(gruen, HIGH);
      delay(400); 
      while(Rfid.available()) Rfid.read();       
      lastMillis = currentMillis;
      strcpy(lastRead, msg);
      
      
      
    }
  }
  else
  {    
    if(strcmp(msg, lastRead)== 0 )
    {
      if(millis() - lastMillis > 5000) {
        lastMillis = currentMillis; 
        lastRead[0] = 0;
      } //Lastread "Resetten"   
    }
    else
    {
      Serial.println("Zutritt verweigert");
      Serial.println( msg);
      digitalWrite(rot, LOW);
      delay(400);
      while(Rfid.available()) Rfid.read(); 
      digitalWrite(rot, HIGH);
      delay(400); 
      while(Rfid.available()) Rfid.read(); 
      lastMillis = currentMillis;
      strcpy(lastRead, msg); 
      
    }

  }
}

Ist vielleicht nicht alles so schön, aber es funktioniert !
Morgen werde ich das ganze dann mal mit meinem MEGA ausprobieren, weil da gab es "früher" ein paar Probleme bezüglich der Arbeit mit dem RFID Reader !

Grüße
Lorenz

Ja es heisst, dass ich den Tag 2 Sekunden an den Reader halte und der Blinkcode wird mehrfach ausgefürht !

Einfach, damit das klar ist: wir reden von der grünen LED, die blinkt? Wie oft die rote blinkt, ist mir egal, das kann gut an meinem Einwand mit dem falschen Auslesen des seriellen Puffers liegen.

Warum das ? Was ändert das ?

Es ändert, dass nicht unnötig Speicherplatz verschwendet wird. Einwandfrei heisst bei mir, dass daran nichts rumzumeckern ist. Verschwendung von Speicher ist ein Grund zu meckern.

Hast Du die Routine mal so angepasst?

unsigned long timeout = millis();
for (x = 0; x < 12; x++)
  {
    while (Rfid.available() < 1 && millis() - timeout < 200);
    if (millis() - timeout >= 200) {
      Serial.println(F("Error receiving RFID tag!"));
      msg[x] = 0;
      break;
    }
    msg[x] = Rfid.read();
  }

Einfach, damit das klar ist: wir reden von der grünen LED, die blinkt? Wie oft die rote blinkt, ist mir egal, das kann gut an meinem Einwand mit dem falschen Auslesen des seriellen Puffers liegen.

Also eigentlich rede ich die ganze Zeit von der roten die zu oft blinkt ! Hat sich ja jetzt aber eh erledigt !

Deinen Vorschlag mit der neuen Routin, werde ich morgen mal kurz ausprobieren !
Was genau sollte diese verbessern ? Einfach weniger speicher verbrauch ?!

Grüße
Lorenz

Na dann ist ja alles gut.

Warum das so ist kann ich nicht sagen, aber es macht einen Unterschied.

Dann kannst du auch nicht wissen, ob dein Code "einwandfrei" ist.
Was pylons Modifikation mit dem verräterischen Wort timeout für eine Startzeit (?) soll, solltest du verstehen, während du es einbaust.
Es ist nur ein Schnipsel, der nicht überall hin passt :wink:

Noch ein Nachtrag:

Eine Zugangskontrolle darf übrigens gar nicht so schnell sein, wenn sie ernst genommen werden will.
"Schnarchlangsam" solltest du zu keinem Security, Zöllner oder ähnlichen sagen, wenn du nicht lernen willst, wie langsam alles gehen kann :wink:

Ungenutzer RAM verbraucht übrigens genausoviel Strom wie verschwendeter ...
Allerdings gebe ich dir recht, pylon, dass die Verwendung von int für kleine vorzeichenlose Zahlen im Prinzip eine schlechte Angewohnheit ist.
Bin nicht sicher, ob avrgcc das wirklich immer richtig wegoptimiert.

Bin nicht sicher, ob avrgcc das wirklich immer richtig wegoptimiert.

In vielen Fällen kann er das gar nicht, weil er nicht den ganzen Wertbereich kennt, der möglich ist.
Ich habe auch nicht behauptet, meine Modifikation sei notwendig, aber einwandfreier Code darf solche Sachen nicht enthalten.

Was pylons Modifikation mit dem verräterischen Wort timeout für eine Startzeit (?) soll, solltest du verstehen, während du es einbaust.

Korrekter Einwand, aber ich wollte dem OP einen Hinweis geben, wofür die Variable verwendet wird. Und wenn ich das übliche lastMillis genommen hätte, wäre der Konflikt programmiert gewesen.

Was genau sollte diese verbessern ? Einfach weniger speicher verbrauch ?!

Nein, sie eliminiert einen Fehler in Deinem Code. Du liest einfach 12 Zeichen von der SoftwareSerial ein, ohne zu wissen, dass die dort überhaupt angekommen sind. Da Du das überaus langsam machst (und damit Deinen ganzen Sketch blockierst), funktioniert es in den meisten Fällen, trotzdem ist es sehr unschön und kann zu Race-Conditions führen. Der Timeout ist eingebaut, damit der Sketch nicht gleich stehen bleibt, wenn Dein Reader, aus welchen Gründen auch immer, mal nicht die gewünschten 12 Zeichen liefert.

lgrube96:
Und jurs du hast meinen Tag echt gerade irgendwie gerettet !
Es funktioniert ! ^^

Na bravo!

lgrube96:
Aber warum macht while (Rfid.available()) Rfid.read(); alles gleich funktionsfähig ?
"nur" weil der cache wieder geleert wird ?!

Das komplette Wegschmeißen der Zeichen im Eingangspuffer nach einem Buffer-Overrun wegen der schnarchlangsamen Verarbeitung mit langen Delays bei gleichzeitig hoher Eingangsdatenrate ist ein probates Mittel, um den Eingangspuffer wieder leer zu bekommen, damit er sich wieder mit neuen und hoffentlich nützlichen Daten füllen kann.

Dein Code ist nicht darauf ausgelegt, vernünftig mit dem Fall umzugehen, dass plötzlich irgendwann nur noch ein "halber, angefangener Code" aus dem Eingangspuffer gezogen werden kann und der Rest fehlt. Und da Dein Code damit nicht umgehen kann, ist es dann am vernünftigsten, den ganzen Buffer-Overrun-mit-Codequatsch im Eingangspuffer wegzuschmeißen und den Puffer wieder freizuräumen, so dass sich der Puffer danach wieder mit solchen Daten füllen kann, mit denen Dein Code was anfangen kann.

Du liest einfach 12 Zeichen von der SoftwareSerial ein, ohne zu wissen, dass die dort überhaupt angekommen sind. Da Du das überaus langsam machst (und damit Deinen ganzen Sketch blockierst), funktioniert es in den meisten Fällen, trotzdem ist es sehr unschön und kann zu Race-Conditions führen. Der Timeout ist eingebaut, damit der Sketch nicht gleich stehen bleibt, wenn Dein Reader, aus welchen Gründen auch immer, mal nicht die gewünschten 12 Zeichen liefert.

Kleiner Einspruch: Sicher können auf seriellen Leitungen Zeichen verloren gehen.
read() blockiert aber nicht, sondern liefert -1, was zu einem ungültigen Code führt und sich mit der nächsten Start-Kennung ( 0x02 ) wieder synchronisiert.

  while (inChar = Rfid.read() != 2)    
  {
  }

Vermutlich weiss Lorenz gar nicht, dass dies der beste Teil seines Codes ist :wink:
(Zusammen mit der Tatsache, dass der rfid-Reader ein Start-Zeichen hat und irgendwann aufhört zu senden, wenn kein Tag in der Nähe ist.)

Kleiner Einspruch: Sicher können auf seriellen Leitungen Zeichen verloren gehen.
read() blockiert aber nicht, sondern liefert -1, was zu einem ungültigen Code führt und sich mit der nächsten Start-Kennung ( 0x02 ) wieder synchronisiert.

Stimme ich grundsätzlich zu, aber die ungültigen Codes sind ja gerade sein Problem (deshalb die mehrfachen roten Blinksignale). Oder habe ich das immer noch falsch verstanden? Wenn ein Byte als -1 eingelesen wird, dann wird ein gültiges Tag zu einem ungültigen (rotes Blinksignal), das nächste richtige Einlesen des gültigen Tags führt dann wieder zu einem grünen Signal, ein -1 an einer anderen Stelle zu einem erneuten roten Signal. Nach seiner Beschreibung sieht er genau diese Symptome.

pylon:
Stimme ich grundsätzlich zu, aber die ungültigen Codes sind ja gerade sein Problem (deshalb die mehrfachen roten Blinksignale). Oder habe ich das immer noch falsch verstanden? Wenn ein Byte als -1 eingelesen wird, ...

Damit hat er überhaupt keine Probleme, denn in seinem Code wartet er immer auf das Startzeichen (ASCII-2) vom Reader, busy waiting bis das erste Zeichen mit dem Startcode da ist. Und nach dem Senden des Startcodes macht der Reader ja keine Pause, sondern sendet hintereinander weg insgesamt 14 Zeichen.

Der gepostete Code wartet einfach jedesmal 10ms auf jedes weitere der 12 folgenden Zeichen. Damit ist er ganz weit auf der sicheren Seite, denn der Reader sendet seine 14 Zeichen natürlich nacheinander weg mit 9600 Baud, d.h. ca. 960 Zeichen pro Sekunde oder fast 10 Zeichen in 10 Millisekunden. Sobald er den Startcode am Arduino detektiert hat ist ca. 20 Millisekunden später der gesamte Readercode im seriellen Eingangspuffer angekommen. Wenn er sein Programm beschleunigen wollte, könnte er statt vor jedem der 12 Zeichen 10 ms mit dem Auslesen zu warten (Gesamt-Wartezeit 120ms) auch einmalig 25ms (mit ausreichend Zeitpuffer) warten und dann sogar alle Zeichen auf einen Rutsch auslesen, weil sie dann bei 9600 Baud Datenrate bereits im seriellen Eingangspuffer vorhanden sind.

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

Damit hat er überhaupt keine Probleme,

Also in gut 12ms sind die 12 Zeichen beim Arduino. Danach sendet der Reader wahrscheinlich gleich wieder die nächsten Zeichen. In den 120ms, die der gepostete Code mit Warten zubringt, können also über 100 Zeichen übertragen worden sein. Der Eingangspuffer einer SoftwareSerial-Instanz ist 64 Zeichen gross (Ringpuffer), somit werden bereits die ersten Zeichen überschrieben sein, bevor die 12 Zeichen fertig ausgelesen sind. Ist das keine Race-Condition? Gibt das keine Probleme? Hier streite ich nicht mal mehr über einwandfrei, das ist schlicht absolut unzuverlässig.

Die einzig richtige Vorgehensweise ist, zu warten bis ein Zeichen empfangen wurde und es dann auszulesen. Fixe Wartezeiten sind bei seriellen Schnittstellen nie eine gute Idee!

pylon:
Der Eingangspuffer einer SoftwareSerial-Instanz ist 64 Zeichen gross (Ringpuffer), somit werden bereits die ersten Zeichen überschrieben sein, bevor die 12 Zeichen fertig ausgelesen sind.

Ach was, die ersten 63 Zeichen, die in den seriellen Eingangspuffer reingespeichert werden, können da in jedem Fall auch wieder ausgelesen werden, selbst wenn zwischendurch ein Buffer-Overrun auftritt. Die Zeichen, die auf einen vollen Eingangspuffer treffen, gehen verloren, aber sie überschreiben nicht die bereits im Eingang befindlichen Zeichen.

Das ist korrekt, hatte ich falsch in Erinnerung, habe es aber im Code nachgeprüft.

Mein Argument zieht trotzdem. Auch wenn es die ersten 5 Durchgänge möglicherweise noch so durchgeht, kommen dann Zeichen in mehr oder weniger zufälliger Originalposition an. Wenn ein Zeichen gelesen wird, wird ja wieder eine Position im Puffer frei. Somit wird das Zeichen das vom Reader gerade gesendet wird, an diese Position geschrieben. Da danach 10ms gewartet wird, ist die nächste Position die nicht verworfen wird, mehr oder weniger zufällig.