XBee serielle Kommunikation (Problem bei der Verabareitung der übertragen Daten)

Hallo zusammen,

ich möchte gerne über eine Funkverbindung Daten von einem Sensor (LDR, Ultraschall, IR Distanz etc.) am “Sender-Uno” übertragen und im “Empfägner-Uno” auswerten. Hierzu habe ich 2 Arduino UNO R3 Boards (1x SainSmart Uno R3 und einmal Arduino Uno R3) mit jeweils einem XBee Schield mit einem XBee Series 1 verwendet. Zusätzlich habe ich an jedem Uno ein LCD Display 1602 zur Anzeige der Daten angeschlossen.

Beide Xbee Module sind auf 57600 Baud eingestellt und können miteinander kommunizieren. Ich lese auf dem “Sender-Uno” einen LDR 07 am Analogport A0 aus und sende die empfangenen Werte über serial/XBee an den Empfänger. Auf dem LCD Display des “Sender-Uno” werden die Werte angezeigt (bei eingeschaltetem Licht schwanken die Werte des LDR in einem Bereich von ca. 900-950). Der “Empfänger-Uno” empfängt die Werte und stellt sie auch korrekt im LCD dar (natürlich ebenfalls in einem Bereich von ca. 900-950).

Soweit so gut…

Ich habe es jedoch noch nicht geschafft die empfangenen Werte zur Weiterverarbeitung zu nutzen, Ich möchte gerne ein Relais auslösen, wenn der empfangene Wert (“value = Serial.read();”) des LDR vom “Sender-Uno” größer als 1000 ist. Das passiert z. B. wenn ich den LDR mit einer Taschenlampe anleuchte. Das Relais reagiert leider nicht auf Werte über 1000 und schaltet nicht ein. Wenn ich die Variable relais im Sketch des “Empänger-Uno” um Testen auf 1001 setze, löst das Relais aus, es scheint also korrekt angesteuert zu werden.

Als Nebenkriegsschauplatz spinnt mein LCD-Display, wenn ich die delay-Werte im Sketch des “Empfänger-Uno” über 400 ms einstelle. Aber das nur nebenbei.

Hier noch der Code der beiden Uno’s:

“Sender-Uno”:

#include <LiquidCrystal.h>
LiquidCrystal lcd(12, 11, 10, 7, 6, 5, 4);
int outgoing;
int analogPin = 0;
int ldr=0;
void setup() {
** Serial.begin(57600);**
** lcd.begin(16, 2);**
** lcd.clear();**
** lcd.print(“XBee Sender LDR”);**
** lcd.setCursor(0, 1);**
}
void loop() {
** lcd.write(" ");**
** lcd.setCursor(0, 1);**
** ldr = analogRead(analogPin);**
** lcd.print(ldr);**
** Serial.print(ldr);**
** delay(300);**
}

“Empfänger-Uno”:

#include <LiquidCrystal.h>
LiquidCrystal lcd(12, 11, 10, 7, 6, 5, 4);
int relais = 9;
int value = 999;
void setup() {
** pinMode(relais, OUTPUT);**
** Serial.begin(57600);**
** lcd.begin(16, 2);**
** lcd.clear();**
** lcd.print(“XBee Rec LDR”);**
** lcd.setCursor(0, 1);**
}
void loop() {
** if (Serial.available()) {**
** lcd.setCursor(0, 1); **
** lcd.write(" ");**
** lcd.setCursor(0, 1); **
** while (Serial.available() > 0) {**
** value = Serial.read();**
** lcd.write(value);**
** }**
** }**
** if (value > 1000) {**
** digitalWrite(relais, HIGH);**
** delay(200);**
** }**
** else {**
** digitalWrite(relais, LOW);**
** delay(200);**
** }**
}

Hat von Euch einer eine Idee, wo der Fehler liegen könnte. Ich verzweifle, da ich seit mehreren Stunden des testens keine lösung finden kann.

Viele Grüße
Frank_

Sorry, ich habe den Code wohl nicht korrekt geposted. Die Code-Tags habe ich zu spät gesehen.

Probier mal statt

 int value = 999;

das:

 int value = 0;

Und wegen dem Display:

void loop() {
  if (Serial.available()) {
    lcd.clear();
    lcd.setCursor(0, 1);   
    lcd.write("                ");
    lcd.setCursor(0, 1);   
    while (Serial.available() > 0) {
      value = Serial.read();
      lcd.write(value);
    }
  }
  if (value > 1000) {
    digitalWrite(relais, HIGH);
    delay(200);
  }
  else {
    digitalWrite(relais, LOW);
    delay(200);
  }
delay(1000);
}

Fällt dir was auf? In deinem Sketch wird das Display immer überschrieben.
Der oberer setzt erstmal das Display zurück.
Außerdem habe ich mal eine Sekunde verzögerung eingebaut, damit das Programm nicht zu schnell läuft.

Frank_:
Hat von Euch einer eine Idee, wo der Fehler liegen könnte. Ich verzweifle, da ich seit mehreren Stunden des testens keine lösung finden kann.

Ja. Das Problem liegt darin, dass Du nicht mal ansatzweise versuchst das zu empfangen, was Du sendest.

Insbesondere sendest Du Klartext-ASCII-Codes der Messwerte raus, z.B.
10[pause]20[pause]100[pause]300[pause]1011[pause]

Aber Du versuchst per
value = Serial.read();
jedes einzelne empfangene Zeichen als Messwert zu interpretieren.

Also wenn eine “1” kommt liest Du als Messwert 49, weil 49 der ASCII-Code der Ziffer “1” ist.
Unmittelbar darauf liest Du für “0” den Messwert 48, weil 48 der ASCII-Code der Ziffer “0” ist.

Statt value=10 zu erhalten wie gesendet, bekommst Du stattdessen schnell nacheinander value=49 und value=48.

[Nachtrag] Zu Deiner Senderoutine passender Code folgt:

void setup() {
  // put your setup code here, to run once:
  Serial.begin(57600);
}

int receiveMesswert()
// Rückgabewert wenn nichts empfangen wurde: -1
// Sonst empfangener Wert als Rückgabewert
{
  char buf[5];
  int bufcount;
  if (!Serial.available()) return(-1);
  delay(1); // 1 ms Zeit, ausreichend zum Empfangen von 5 Zeichen bei 57600 Baud
  memset(buf,0,sizeof(buf));
  bufcount=0;
  while (Serial.available())
  { // Zeichen einlesen und dabei Lesepuffer leeren
    char c=Serial.read();
    if (c>='0' && c<='9')
    {
      buf[bufcount]=c;
      if (bufcount<sizeof(buf)-1) bufcount++;
    }  
  }
  if (bufcount>0) return atoi(buf);
  else return -1;
}


void loop() {
  // put your main code here, to run repeatedly: 
  int messWert=receiveMesswert();
  if (messWert>=0) Serial.println(messWert);
}

@ legotechnicus

Danke für die Hinweise.

1.) int value = 0; hilft leider auch nicht
2.) lcd.clear(); löscht mir das ganze Display, ich möchte aber nur die Zweite Zeile löschen (mit Leerzeichen überschreiben) und den Statustext der 1. Zeile behalten
3.) delay(1000); am Ende des Sketches löst die besagten Störungen im LCD Display aus. Die empfangenen Werte werden dann 10-14stellig angezeigt.

@ jurs

Danke auch Dir für die Hinweise.

while (Serial.available() > 0) {
      value = Serial.read();

Ich dachte, dass diese While-Schleife den kompletten Messwert "abwartet". Komischerweise werden die gesendeten Messwerte ja korrekt am Empfänger-LCD angezeigt. Lediglich die Weiterverarbeitung funktioniert nicht. Über Empfängercode wäre ich natürlich sehr dankbar, da ich mich bisher mit serieller Datenübertragung nicht gut auskenne, aber das habt Ihr ja bereits gemerkt.

Frank_:

while (Serial.available() > 0) {

value = Serial.read();




Ich dachte, dass diese While-Schleife den kompletten Messwert "abwartet".

Nein, die Routine wartet gar nichts ab, sondern verarbeitet das, was im Eingangspuffer vorhanden ist, wenn die Schleife startet. Nur wenn während der Verarbeitung noch ein Zeichen dazukommt, wird ein Zeichen mehr verarbeitet.

Mehr als ein Zeichen ist bei Deiner Empfangsroutine nur deshalb im Eingangspuffer vorhanden, weil Du weiter unten bei Deinen Schaltaufrufen für das Relais 200 ms delay eingebaut hast. Dadurch ist dann beim nächsten loop-Durchlauf oft gleich mehr als ein Zeichen im Eingangspuffer und kann ausgelesen werden. Sehr unschöne Programmstruktur, erstens wegen der recht langen Delays, aber auch weil Du Dich an einer Stelle des Programms auf Seiteneffekte aus einer Programmierung an ganz anderer Stelle verläßt. Und es funktioniert ja auch nicht.

Frank_:
Komischerweise werden die gesendeten Messwerte ja korrekt am Empfänger-LCD angezeigt.

So komisch ist das gar nicht, wegen der Code-Kombination:
int value = Serial.read();
Serial.write(value);
Dieser Code gibt exakt das aus, was kommt. Kommt ein ASCII-Zeichen "1", dann wird in value eine 49 geschrieben und Serial.write sendet beim Senden einer 49 dann wieder das ASCII-Zeichen, das diesem Code entspricht, also wieder die "1", in dem Fall auf Dein Display.

Über Empfängercode wäre ich natürlich sehr dankbar, da ich mich bisher mit serieller Datenübertragung nicht gut auskenne, aber das habt Ihr ja bereits gemerkt.

Passenden Empfängercode habe ich beim Ändern meiner letzten Message mitgepostet.
Wenn Du den empfangenen "messWert" auf dem LCD ausgeben möchtest, mußt Du natürlich "lcd.print(messWert)" verwenden, damit dieser wieder in einen ASCII-String umgewandelt ausgegeben wird (also nicht lcd.write!).

@ jurs

Es funktioniert! Vielen Dank für den Code und Deine ausführlichen Erklärungen. Dieses Forum ist echt klasse. Ein Lösung in weniger als 2 Stunden, toll.

Zwei Fragen hätte noch:

1.) Ich habe den Code etwas angepasst, sodass das Relais nur einen Impuls von delay(500); auslöst. Deine Funktion habe ich eingebaut (eigentlich nur reinkopiert). Warum erhalte ich bei einem großen Wert für delay(2000); am Ende des Hauptprogramms fünfstellige Messwerte? Lasse ich den delay weg, wird alles korrekt angezeigt.

void loop() {
  lcd.setCursor(0, 1);    
  lcd.write("                ");
  lcd.setCursor(0, 1);    
  int messWert = receiveMesswert();
  if (messWert >= 0) lcd.print(messWert);
  if (messWert > 1000) {
    digitalWrite(relais, HIGH);
    delay(500);
    digitalWrite(relais, LOW);
  }
  else {
    digitalWrite(relais, LOW);
    delay(100);
  }
  delay (2000);
}

2.) Kennst Du eine Arduino-bezogene Anleitung/Doku im Internet für die serielle Kommunikation, die nicht nur code-Examples beinhaltet sondern auch Hintergrundinformationen liefert. Sozusagen "Arduino serial for Dummies".

Nochmals vielen Dank für Deine Hilfe!

Liebe Grüße
Frank_

Frank_:
Warum erhalte ich bei einem großen Wert für delay(2000); am Ende des Hauptprogramms fünfstellige Messwerte?

Weil Du in Deiner Senderoutine alle 300 ms einen Wert sendest, also in 2000 ms insgesamt sechs Sendevorgänge stattfinden, dann werden nach 2000ms insgesamt sechs Werte im Eingangspuffer des Empfängers stehen.

Da die einzige Trennung zwischen den Sendevorgängen die von Dir vorgesehene 300 ms Sendepause ist, können eintreffende Zeichen aus dem Eingangspuffer nie mehr auseinanderklabüstert werden.

Du sendest z.B. in 2000 ms:
10[pause]20[pause]100[pause]300[pause]1011[pause]123
Dann steht danach im Eingangspuffer, da der Eingangspuffer nur Zeichen, aber keine Pausen speichert:
10201003001011123

Wie soll das auseinander dividiert werden in Integer-Werte?
Genausogut wie
10 20 100 300 1011 123
könnte gesendet worden sein:
1 0 201 0 0 30 0 10 11 12 3
oder
1020 10 0 30 10 112 3
Oder ganz viele andere Kombinationen.

Dein Sendeprotikoll funktioniert zwar, aber wenn Du als einzige Trennung zwischen den Aussendungen die 300 ms Sendepause hast, dann MUSST Du häufiger als alle 300 ms den Empfangspuffer auslesen. D.h. delays von 300ms und mehr auf der Empfangsseite sind KOMPLETT VERBOTEN und bringen Dein Programm durcheinander.

Besser wäre es, wenn Du beim Senden Deine Werte nicht nur mit einer Sendepause trennst, sondern mit einem Trennzeichen. Sagen wir mal einem Leerzeichen zwischen den gesendeten Werten, also beim Senden:
Serial.print(ldr);
Serial.print(" ");
delay(300);

Dann könntest Du die Emofangsroutine ändern, so dass immer beim Empfang eines Leerzeichens klar ist: Aha, Code wurde vollständig empfangen. Jetzt beginnt ein neuer Code. Dann hast Du im Empfangspuffer nämlich immer zwischen zwei Zahlen ein Trennzeichen, und das kannst Du auch auseinander klabüstern, wenn wegen eines "delay(2000)" mehrere Werte im Eingangspuffer stehen:
10201003001011123
im Eingangspuffer kannst Du nicht wieder vernünftig aufteilen in einzelne Zahlen. Aber
10 20 100 300 1011 123
sind bereits durch die Leerzeichen zwischendrin in Zahlen aufgeteilt.

Und eine Empfangsroutine, die weiß, dass immer ein Leerzeichen zwischen zwei Zahlen steht, kann das berücksichtigen und auch einzelne Zahlen aus dem Empfangspuffer ziehen.

Frank_:
2.) Kennst Du eine Arduino-bezogene Anleitung/Doku im Internet für die serielle Kommunikation, die nicht nur code-Examples beinhaltet sondern auch Hintergrundinformationen liefert. Sozusagen "Arduino serial for Dummies".

Programmieren lernt man nur durch selber Programmieren.

In Bezug auf die serielle Schnittstelle hast Du beim Arduino zwei Funktionen zum Senden:
Serial.write()
Serial.print()
und eine Funktion zum Empfangen von Zeichen;
Serial.read()
Der Rest ist Handwerk im Umgang mit Variablen, Schleifen und Verzweigungen im Code.

Dank Deiner ausführlichen Erklärung, habe ich das verstanden.

Ich werde mal versuchen die Messwerte mit Trennzeichen zu senden und die ankommenden Werte entsprechend auszuwerten.

Nochmals tausend Dank für Deine Hilfe.

Frank_

Vielleicht noch ein interessanter Nachtrag für alle die, die genau wie ich, noch auf der Suche nach Basisinformationen zur seriellen Kommunikation sind.

Es gibt zur Zeit ein kostenloses Kapitel des Buches "Arduino Kochbuch" zum Download beim O'Reilly Verlag: http://www.oreilly.de/catalog/arduinockbkger/chapter/ch04.pdf

Zufällig behandelt dieses Kapitel die "Serielle Kommunikation".