Daten seriell empfangen und auswerten

Hallo zusammen.

Ich habe da auch wieder eine Frage an Euch. Wie werte ich Daten aus, die ich seriell empfangen habe?

Zum meinem Projekt:
Ich bekomme von einem Datenlogger über eine RS232-Verbindung (angeschlossen über Max232) jede Minute einen Datensatz. Dieser besteht aus x Zeichen und enthält jede Menge Informationen von denen ich nur drei Werte benötige um sie im Code auszuwerten.
Zum Beispiel: "temp=222;dir=33;vel=2875;xyz=5241;zyx=231".

Wie programmiere ich: suche im Datensatz nach "dir=" und schreibe die nächsten zwei Zeichen als Zahl in die Variable Temp?

Bis jetzt habe ich dieses Beispiel hier gefunden, welches zumindest schon mal den Datensatz mit x Zeichen abspeichert:

if (Serial.available() >= 5){  //reading a single 5 byte command
for (int i = 0; i <= 4; i++) {
MPresponse[i] = Serial.read()
}
MPresponse[5] = '\0';               //add null character to end of string
}

if (strcmp(MPresponse, "LEDON")  == 0) {      // turn LED on
digitalWrite(13,HIGH);
}
else if (strcmp(MPresponse, "LEDOF")  == 0) {   //turn LED off
digitalWrite(13,LOW)
}

Für Eure Hilfe und wertvollen Tipps zum "Programmieren lernen für Anfänger" danke ich im Voraus :wink:

Gruß
Poldi

finite state machine (FSB) ist immer eine gute Idee für Protokolle.

Betrachten wir mal zwei Zustände: STATE_READING_KEY und STATE_READING_VALUE
Der erste Zustand liest einen Schlüssel (temp, dir, vel, etc.) und der andere einen Wert (222,33,2875, etc.).

dann bau Dir eine globale Variable currentState und dann muss man überlegen, wann der Zustand wechselt. In Deinem Fall gibt es zwei besondere Zeichen: = und ;

Also: wäre es sinnvoll, wenn das Programm am Anfang im Zustand STATE_UNDEFINED ist und in den Zustand STATE_READING_KEY wechseln möchte sobald ein Semikolon gelesen wurde. Wurde das Semikolon gelesen, so wechselt das Programm in den Zustand STATE_READING_KEY, bis dann das = Zeichen gelesen wird. Dann geht das Programm nämlich in den Zustand STATE_READING_VALUE über, bis erneut ein Semikolon gelesen wird...

ungetesteter Code aus dem Kopf könnte dann so aussehen:

#define STATE_UNDEFINED 0
#define STATE_READING_KEY 1
#define STATE_READING_VALUE 2

uint8_t currentState = STATE_UNDEFINED;

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

void loop()
{
  uint8_t c = Serial.read();
  if(currentState==STATE_UNDEFINED)
  {
    if(c == ';')
    {
      currentState = STATE_READING_KEY;
    }
  }
  else if(currentState== STATE_READING_KEY)
  {
    if(c == '=')
    {
      currentState = STATE_READING_VALUE;
      //RESET VALUE STRING
    }
    else
    {
      //ADD c TO KEY STRING
    }
  }
  else if(currentState== STATE_READING_VALUE)
  {
    if(c == ';')
    {
      //PROCESS KEY AND VALUE NOW

      currentState = STATE_READING_KEY;
      //RESET KEY STRING
    }
    else
    {
      //ADD c TO VALUE STRING
    }
  }
}

Du musst nur noch die gross geschriebenen Kommentare beachten...
denn da wirst Du die Strings erweitern bzw. leeren oder mit den beiden Werten Deine eigentliche Aufgabe triggern.

ok, so gehts auch, wollte grad was mit strtok_r() schreiben, aber das ist dann eher zu komplex, hatte auch nicht dran gedacht, das die daten ja seriell kommen :wink:

Vielen Dank für den Anhaltspunkt. Werde mich mal ans Werk machen...

Bericht folgt :wink:

Gruß
Poldi

So, auch wenn es etwas gedauert hat... nun soll / muss es weiter gehen. Das Projekt soll nach Ostern abgeschlossen werden und leider komme ich nicht weiter.

Ich habe den Datenlogger (welcher die Daten sendet) nun für ein paar Tage ausgeliehen. Leider hat sich nun ergeben, das die Daten etwas anders vorliegen. Der Logger sendet alle fünf Sekunden vier Werte hintereinander. Bekonnen wird mit dem Vorzeichen. Der Wert hat eine Gleitkommastelle. Werte sind durch ein Leerzeichen getrennt. Am Ende wird ein "Enter" gesendet. Lange Rede, hier ein Beispiel:

+0.0000 +13.889 +23.054 +22.587
+0.0000 -0.67518 +23.098 +22.432

Ich habe wieder viel im Netz gesucht, weiß aber nicht wie ich die vier Werte in einzelne Variablen bringe um sie weiter verarbeiten zu können.
Selbst wenn ich versuche den ganzen String zu lesen und mir anschließend anzusehen verstehe ich nicht genau was da passiert (Code aus dem Netz):

void get_data() {
  char data[80];
  int incount = 0;
  if ( Serial.available() )
  {
    while (Serial.available()) {
    data[incount++] = Serial.read();      
    }
    //data[incount] = '\0';  // puts an end on the string
    Serial.println(data);
}
}

Sende ich ein Zeichen, bekomme ich es zurück. Sende ich fünf Zeichen bekomme ich zwar alle fünf zurück, aber nach dem ersten Zeichen beginnt plötzlich eine neue Zeile!?

Nun, nach der Mittagspause ist alles merkwürdig. Ich bekomme beim senden einer "1" folgendes zurück:

1ÿÏôëhï¾úþGß??Ó÷Ûïî××»;½ÿ¿×ï%ï£í÷é^ëùÛt?~ýöß7ï|öxÿ¹{÷

Kann mir bitte jemand helfen? Ich möchte nicht den fertigen Code. Viel wichtiger ist mir zu verstehen, was da passiert und wie ich den Code selber schreibe...
Im fertigen Code möchte ich gerne auf ein "Enter" warten (auch wenn dadurch der erste Datensatz verloren ist) und die Werte (Trennung ist ja durch ein Leerzeichen ersichtlich) in vier Variablen schreiben.

DANKE schon mal jedem, der helfen mag :slight_smile:

Gruß
Poldi

Dein data[80] array ist nicht mit 0 aufgefuellt. Daher steht am Anfang die 1 (OK) und dann kommt Muell.

Für byte / char arrays functioniert das ganz gut (normales C Beispiel):

#include <stdio.h>

void clear_buffer(char *buffer, char length);
void print_buffer(char *buffer, char length);

int main() {
  char array[80];
  char ctr;
  printf("\n\nbuffer as is: \n\n");
  print_buffer(array,80);
  printf("\n\nclearing buffer\n\n");
  clear_buffer(array,80);
  printf("\n\ncleared buffer: \n\n");
  print_buffer(array,80);
}

void clear_buffer(char *buffer, char length) {
  char ctr;
  for(ctr = 0; ctr <= (length-1); ctr++) {
    buffer[ctr] = 0;
  }
}


void print_buffer(char *buffer, char length) {
  char ctr;
  for(ctr = 0; ctr <= (length-1); ctr++) {
    printf("%d",buffer[ctr]);
  }
}

Das liefert dann z.B. sowas:

buffer as is:

8000000000000000000000000000000000000-3718-108-65-1839-29-73-1295-16-73-12-97481201-108-650-12548-1295-16-73-12-9748-1041-108-6541-123488054-13

clearing buffer

cleared buffer:

00000000000000000000000000000000000000000000000000000000000000000000000000000000

Hallo madworm,

ich verstehe nun warum es zu diese Ausgaben gekommen ist (nicht leeres Array). Vielen Dank dafür!
Allerdings muss ich zugeben, dass mir die Codeschnipsel nicht wirklich weiter helfen. Da ich kein Programmierer bin verstehe ich nicht welche Zeile was genau und wie macht... :-[

Mir würde es sehr helfen wenn jemand sich die Mühe machen würde mir die Basics im Umgang mit einem Array zu erklären. Ich habe hier wieder einen Beispielcode und darunter meine Kommentare von dem, was ich verstehe:

#include <AFSoftSerial.h>

#include <Servo.h>

Servo myServoPan;  // create servo object to control a servo
Servo myServoTilt;  // create servo object to control a servo


AFSoftSerial mySerial =  AFSoftSerial(3, 2);

int distpin = 0;  // analog pin used to connect the potentiometer
int val;    // variable to read the value from the analog pin
char CurrentServo;
int value;

int valueA;
int valueB;

int fresh = 0;

void setup()  
{
  myServoPan.attach(9);  // attaches the servo on pin 9 to the servo object
  myServoTilt.attach(10);  // attaches the servo on pin 9 to the servo object

  pinMode(13, OUTPUT);
  Serial.begin(9600);
  Serial.begin(1200);
  
  Serial.println("Scanturm V0.1");
}

void loop()                     // run over and over again
{
    
  get_keyboard_commands();
  
}

void get_keyboard_commands() {
  char input[80];
  int incount = 0;
  if ( Serial.available()) {
    
    // read in a string byte by byte
    while (Serial.available()) {
       input[incount++] = Serial.read();      
    }
    input[incount] = '\0';  // puts an end on the string
    Serial.println(input);
          
    switch(input[0]) {
    
      case 'P':        
        CurrentServo='P';
        fresh = 1;
        break;
      case 'T':        
        CurrentServo='T';    
        fresh = 1;
        break;
      case 'A':
        val = analogRead(distpin);            // reads the value of the Distance Sensor (value between 0 and 1023)  
        fresh = 2;
        break;  
      default:
       // value=(ch-'0')*20;  // comes in as ascii value, subtract off '0' to convert to numbers    
        value = atoi(input);
        break;
    }
    
    
    /*if(fresh==0)
    {
      if (CurrentServo=='P') {
        valueA = value;
        myServoPan.write(valueA);
      }
      else if (CurrentServo=='T') {
        valueB = value;
        myServoTilt.write(valueB);
      }
      //Serial.print(CurrentServo);
      //Serial.print(" ");
      Serial.println(value);
    }
    
    if(fresh==1){
      fresh = 0;  
    }

    if(fresh==2){
      Serial.print("A: ");
      Serial.print(val);  
      Serial.println("$");
      fresh = 0;
    }*/
 }
  
}

char input[80]; Macht ein Character Array mit 80 Zeichen auf - Was, wenn nur 23 Zeichen kommen? Wie bringe ich ihm bei, bei einem "Enter" das Array neu zu schreiben
int incount = 0; Ist der Platzhalter für die Schleife
if ( Serial.available()) { sobald Daten empfangen werden...

// read in a string byte by byte
while (Serial.available()) { Während Zeichen da sind???
input[incount++] = Serial.read(); fülle das Array Zeichen für Zeichen
}
input[incount] = '\0'; // puts an end on the string Warum wird das gemacht?
Serial.println(input);

switch(input[0]) { Springe an die erste Stelle des Array und schaue...

case 'P': ob das erste Zeichen ein "P" ist
CurrentServo='P';
fresh = 1; Was macht das?
break; Was bewirkt ein break?? Und warum ist hier keine Schleife? Verstehe nicht wie die einzelnen Stellen des Array abgefragt werden

Wie schon gesagt bin ich über jede Hilfe dankbar. Ich möchte nur verstehen, wie ich einzele Zeichen ins Array schreibe, jedes Zeichen einzeln "vergleiche" und den Datensatz später an eine URL anhängen kann um sie in eine Datenbank zu schreiben.

Gruß
Poldi

Hallo,

ich versuch da jetzt mal was, aber der Code ist nicht getestet
und muss ggf. überarbeitet werden. Also eher Pseudo Code :slight_smile: :

char input[80]; // puffer für die Daten
char current; // puffer für empfangenes Zeichen
int incount = 0; // Zähler zum schreiben in den Puffer
bool lineComplete = false; //

// wiederhole die Schleife, solange ein Zeichen an der Seriellen
// schnittstelle anliegt, der Puffer nicht überschritten ist und noch
// kein lineComplete da ist
while ( (Serial.available()) & (incount < 80) & (!lineComplete) )
{
current = Serial.Read(); // aktuelles Zeichen einlesen
if (current != 13) // ist das empfange Zeichen kein ENTER
{
// dann in den Puffer schreiben
input[incount] = current;
incount++;
}
else
{
// puffer mit NULL Zeichen abschließen, damit das Ende der Zeichenkette
// durch string Operationen erkannt wird
input[incount] = '\0';
lineComplete = true;
}
}

// so wenn man hierher kommt, dann kann das drei Gründe haben:
// - keine zeichen mehr an der seriellen Schnittstelle
// - Puffer voll, aber noch kein Zeilenende
// - Zeilenende erreicht
if (lineComplete)
{
// input ist eine Zeichenkette mit den vier Werten, durch ein Leerzeichen getrennt
// weiter mit String Operationen
}
else if (incount == 80)
{
// - Puffer voll, aber noch kein Zeilenende
// tja, mach was immer Du denkst :slight_smile:
}
else
{
// es liegt nichts mehr an der seriellen Schnittstelle an,
// obwohl nicht alle Daten empfangen wurden
// auch hier musst Du entscheiden, was Du machen willst
// Vielleicht ist eine while Schleife hier ja auch nicht so angebracht
}

Ist ja vielleicht ein Ansatz.

gruß
KiWiX

Hallo KiWiX!!!

Vielen Dank für Deine Mühe. Sie hat sich geloht!!! :slight_smile:

Ich habe Deinen Code mal ausprobiert, ein paar serielle Ausgaben zum Test hinzugefügt und am Ende alles in meinen Ethernet-Code hinzugefügt. So weit funktioniert alles.

Ich lese die Werte vom Logger aus, schreibe sie in ein Array und stelle sie auf einer Webseite da.

#include <Ethernet.h>
byte mac[] = { **, **, **, **, **, ** }; 
byte ip[] = { *, *, *, * };
byte gateway[] = { *, *, *, * };
byte subnet[] = { 255, 255, 255, 0 };
Server server(80);

char input[40];  // puffer für die Daten
char current;     // puffer für empfangenes Zeichen
int incount = 0;  // Zähler zum schreiben in den Puffer
bool lineComplete = false; // 

void setup() {
  Serial.begin(1200);
  Ethernet.begin(mac, ip, gateway, subnet);
}


// wiederhole die Schleife, solange ein Zeichen an der Seriellen
// schnittstelle anliegt, der Puffer nicht überschritten ist und noch
// kein lineComplete da ist
void loop() {
while ( (Serial.available()) & (incount < 40) & (!lineComplete) )
{
  current = Serial.read(); // aktuelles Zeichen einlesen
  if (current != 13)            // ist das empfange Zeichen kein ENTER
  {
     // dann in den Puffer schreiben
     input[incount] = current;
     incount++;
  }
  else
  {
      // puffer mit NULL Zeichen abschließen, damit das Ende der Zeichenkette
      // durch string Operationen erkannt wird
      input[incount] = '\0';
      lineComplete = true;
  }
}
// so wenn man hierher kommt, dann kann das drei Gründe haben:
// - keine zeichen mehr an der seriellen Schnittstelle
// - Puffer voll, aber noch kein Zeilenende
// - Zeilenende erreicht
if (lineComplete) {
Serial.println("Line Complete");
Serial.println(input);
lineComplete = false;
incount = 0;
  // input ist eine Zeichenkette mit den vier Werten, durch ein Leerzeichen getrennt
  // weiter mit String Operationen
}
else if (incount == 40)
{
  Serial.println("Buffer voll!!!");
  // - Puffer voll, aber noch kein Zeilenende
 // tja, mach was immer Du denkst 
}
else 
{
 // es liegt nichts mehr an der seriellen Schnittstelle an,
 // obwohl nicht alle Daten empfangen wurden
 // auch hier musst Du entscheiden, was Du machen willst
 // Vielleicht ist eine while Schleife hier ja auch nicht so angebracht
 Client client = server.available();
  if (client) {
    client.println("HTTP/1.1 200 OK");
    client.println("Content-Type: text/html");
    client.println();
    client.println("<font color='darkblue' size='5'>Arduino Projekt Wetterstation</font>

");
    client.println("<font color='blue'>Daten der Wetterstation (Windgeschwindigkeit, Windrichtung, Interne Temperatur, Externe Temperatur)
");
    client.print("
");
    client.println(input);
    client.println("
");
    client.println("
<font color='grey' size='2'>info@jleopold.de - 2009");
    client.stop();
  }
}
}

Vielen Dank! :sunglasses: :sunglasses: :sunglasses: :sunglasses:

Werde in den nächsten Tagen noch weiter daran arbeiten und verbessern. Wenn man im Moment zu oft im Browser aktualisiert wird der serielle Puffer nicht weiter ausgelesen und läuft über... Parallel werde ich versuchen die Werte per Ethernet in eine My-SQL Datenbank zu schreiben. Ein Kollege baut mit Flash und PHP eine Visualisierung auf unserer Firmenhompage... Wenn das alles steht kann ich das Gesamtprojekt gerne mal vorstellen. Danke nochmal!

Gruß
Poldi