Go Down

Topic: XML Probleme... (Read 8247 times) previous topic - next topic

thmmax

Hey Leute,

ich bastle gerade an einer LED Matrix, die in Echtzeit die Abfahrten des lokalen Busunternehmens anzeigt.
Dafür verwende ich einen Arduino mit Ethernet Shield und einen modifizierten XML-Parser.

Das Script holt sich die Daten von folgender Website: http://www.ivb.at/smartinfo/ivb_smartinfo_kernel.php?olifServerId=84&autorefresh=20&default_autorefresh=20&routeId=&stopId=Absam Dorf&optDir=-1&nRows=4&showArrivals=n&optTime=now&time=&allLines=y&app=sionline.

Je nachdem, ob im Link Rows=1 oder Rows=4 steht, werden eine Busabfahrt (ein XML Array) oder 4 Abfahrten (4 Arrays) angezeigt.

Das Script holt sich diese Daten und spielt sie auf die LED Matrix (Foto 1).
Solange es bei einer Abfahrt (Rows=1) bleibt, funktioniert alles wunderbar.

Die Tafel wäre aber eigentlich dafür gedacht, 4 Abfahrten (Rows=4) anzuzeigen.
Dabei ergibt sich aber das Problem, dass alle Abfahrten auf der LED Matrix in einer Zeile (Foto 2) und nicht so wie geplant in vier Zeilen (Foto 3) angezeigt werden.

Im Prinzip müsste man also die einzelnen Strings trennen, z.B.:

<smartinfo>
<route>D</route>
<direction>Kurhaus</direction>
<time>18:36</time>
</smartinfo>
= String 1

<smartinfo>
<route>E</route>
<direction>Hauptbahnhof</direction>
<time>4 min</time>
</smartinfo>
=String 2...

Im Serial Monitor werden die Daten auch schön säuberlich in 4 Zeilen ausgegeben, z.B.:
Linie: D Ziel: H Abfahrt: 2 min
Linie: E Ziel: K Abfahrt: 16 min
Linie: D Ziel: H Abfahrt: 16 min
Linie: D Ziel: H Abfahrt: 10:51

Hier mal der Code:

Code: [Select]
#include <SPI.h>
#include <string.h>
#include <Ethernet.h>
#include "HT1632.h"

#define MAX_STRING_LEN  20
#define DATA 2
#define WR   3
#define CS   4
#define CS2  5
#define CS3  6
#define CS4  7

HT1632LEDMatrix matrix = HT1632LEDMatrix(DATA, WR, CS, CS2, CS3, CS4);

char tagStr[MAX_STRING_LEN] = "";
char dataStr[MAX_STRING_LEN] = "";
char tmpStr[MAX_STRING_LEN] = "";
char endTag[3] = {
  '<', '/', '\0'};
int len;

boolean tagFlag = false;
boolean dataFlag = false;

byte mac[] = {
  0x90, 0xA2, 0xDA, 0x0D, 0x52, 0xBD };
byte ip[] = {
  192, 168, 2, 3 };
byte server[] = {
  83, 175, 126, 90 };

EthernetClient client;

void setup()
{
  Serial.begin(9600);
  matrix.begin(HT1632_COMMON_16NMOS);
  Serial.println("Starting IVB.at");
  Serial.println("connecting...");
  Ethernet.begin(mac, ip);
  delay(0);

  if (client.connect(server, 80)) {
    Serial.println("connected");
    client.println("GET /smartinfo/ivb_smartinfo_kernel.php?olifServerId=84&autorefresh=20&default_autorefresh=20&routeId=&stopId=Absam%20Dorf&optDir=-1&nRows=4&showArrivals=n&optTime=now&time=&allLines=y&app=sionline HTTP/1.0");   
    client.println("Host: www.ivb.at"); 
    client.println();
    delay(10);
  }
  else {
    Serial.println("connection failed");
    matrix.clearScreen();
    matrix.setTextSize(1);
    matrix.setTextColor(1);
    {
      matrix.setCursor(0, 0);
      matrix.print("Bitte   beachten");
      matrix.setCursor(0, 8);
      matrix.print("Fahrplan");
      matrix.writeScreen();
    }
    delay(100);
  } 
}

void loop() {

  // Read serial data in from web:
  while (client.available()) {
    serialEvent();
  }

  if (!client.connected()) {
    //Serial.println();
    //Serial.println("Disconnected");
    client.stop();

    // Time until next update
    //Serial.println("Waiting");
    for (int t = 1; t <= 15; t++) {
      delay(1000); // 1 minute
    }

    if (client.connect(server, 80)) {
      //Serial.println("Reconnected");
      client.println("GET /smartinfo/ivb_smartinfo_kernel.php?olifServerId=84&autorefresh=20&default_autorefresh=20&routeId=&stopId=Absam%20Dorf&optDir=-1&nRows=4&showArrivals=n&optTime=now&time=&allLines=y&app=sionline HTTP/1.0");   
      client.println("Host: www.ivb.at");
      matrix.clearScreen(); 
      client.println();
      delay(0);
    }
    else {
      Serial.println("Reconnect failed");
    }       
  }
}

// Process each char from web
void serialEvent() {

  // Read a char
  char inChar = client.read();
  //Serial.print(".");

  if (inChar == '<') {
    addChar(inChar, tmpStr);
    tagFlag = true;
    dataFlag = false;

  }
  else if (inChar == '>') {
    addChar(inChar, tmpStr);

    if (tagFlag) {     
      strncpy(tagStr, tmpStr, strlen(tmpStr)+1);
    }

    // Clear tmp
    clearStr(tmpStr);

    tagFlag = false;
    dataFlag = true;     

  }
  else if (inChar != 10) {
    if (tagFlag) {
      // Add tag char to string
      addChar(inChar, tmpStr);

      // Check for </XML> end tag, ignore it
      if ( tagFlag && strcmp(tmpStr, endTag) == 0 ) {
        clearStr(tmpStr);
        tagFlag = false;
        dataFlag = false;
      }
    }

    if (dataFlag) {
      // Add data char to string
      addChar(inChar, dataStr);
    }
  } 

  // If a LF, process the line
  if (inChar == 10 ) {

    /*
      Serial.print("routeStr: ");
     Serial.println(directionStr);
     Serial.print("directionStr: ");
     Serial.println(directionStr);
     */

    // Find specific tags and print data
    if (matchTag("<time>")) {
      Serial.print(" Abfahrt: ");
      Serial.print(dataStr);
      String stringOne = dataStr;
      Serial.println(stringOne);
      String stringTwo = stringOne;
      stringTwo.replace("min","'");
      stringTwo.replace(":","");
      matrix.setTextSize(1);
      matrix.setTextColor(1);
      {
        matrix.setCursor(24, 0);
        matrix.println(stringTwo);
        matrix.writeScreen();
      }

    }
    if (matchTag("<direction>")) {
      Serial.print(" Ziel: ");
      Serial.print(dataStr);
      char mostSignificantDigit = dataStr[0];
      matrix.setTextSize(1);
      matrix.setTextColor(1);
      {
        matrix.setCursor(16, 0);
        matrix.println(mostSignificantDigit);
        matrix.writeScreen();
      }
    }
    if (matchTag("<route>")) {
      Serial.print(" Linie: ");
      Serial.print(dataStr);
      matrix.setTextSize(1);
      matrix.setTextColor(1);
      {
        matrix.setCursor(0, 0);
        matrix.print(dataStr);
        matrix.writeScreen();
      }
      }
     
    // Clear all strings
    clearStr(tmpStr);
    clearStr(tagStr);
    clearStr(dataStr);

    // Clear Flags
    tagFlag = false;
    dataFlag = false;
  }
}

/////////////////////
// Other Functions //
/////////////////////

// Function to clear a string
void clearStr (char* str) {
  int len = strlen(str);
  for (int c = 0; c < len; c++) {
    str[c] = 0;
  }
}

//Function to add a char to a string and check its length
void addChar (char ch, char* str) {
  char *tagMsg  = "<TRUNCATED_TAG>";
  char *dataMsg = "-TRUNCATED_DATA-";

  // Check the max size of the string to make sure it doesn't grow too
  // big.  If string is beyond MAX_STRING_LEN assume it is unimportant
  // and replace it with a warning message.
  if (strlen(str) > MAX_STRING_LEN - 2) {
    if (tagFlag) {
      clearStr(tagStr);
      strcpy(tagStr,tagMsg);
    }
    if (dataFlag) {
      clearStr(dataStr);
      strcpy(dataStr,dataMsg);
    }

    // Clear the time buffer and flags to stop current processing
    clearStr(tmpStr);
    tagFlag = false;
    dataFlag = false;

  }
  else {
    // Add char to string
    str[strlen(str)] = ch;
  }
}

// Function to check the current tag for a specific string
boolean matchTag (char* searchTag) {
  if ( strcmp(tagStr, searchTag) == 0 ) {
    return true;
  }
  else {
    return false;
  }
}



BTW ich hab das Ganze schon mal im Englischen Forum gepostet, aber leider keine wirklich brauchbare Antwort bekommen...
Ich hoffe ihr könnt mir helfen! Sitze wirklich schon Wochen über dem Script und finde einfach keine Lösung!
Habe es schon mit diversen String-Funktionen probiert, aber nicht wirklich erfolgreich!

uwefed

#1
Mar 25, 2013, 07:20 pm Last Edit: Mar 25, 2013, 07:24 pm by uwefed Reason: 1
Schöne Grüße nach Tirol.
Ich tippe mal auf ein RAM-Speicher Problem, dh Du brauchst mehr als Du hast. Hast Du den Sketch mal auf einen Arduino MEGA probiert?

Du solltest auch bei allen print bzw println mit Text die F() Funktion verwenden damit der Text nicht RAM-Speicher verbraucht. siehe http://arduino.cc/forum/index.php/topic,127552.msg965539.html#msg965539

Grüße Uwe

thmmax

Lieber Uwe,
danke  8) Schöne Grüße zurück!

Am Ram sollte es nicht liegen, es ist z.b. kein Problem alle vier Zeilen anzuzeigen, nur lädt er eben in jeder Zeile alle 4 Abfahrten übereinander!
Man müsste also irgendwie die Daten 'trennen', so dass das erste Array in der ersten Zeile angezeigt wird etc..

Leider stehe ich da mit meinen beschränkten Programmier-Kenntnissen mittlerweile völlig an...
Edit: und danke für den Tipp mit F()!

pylon

Ich würde mal damit beginnen, alle konstanten Strings, die an eine Print-Klasse (also print() oder println()) übergeben werden, mit dem F()-Macro einzukleiden. Damit kannst Du viel Speicher sparen, ohne Funktionalität einzubüssen.

thmmax

Danke für die Tipps! Werde das gleich mal umsetzen!

Das eigentliche Problem liegt aber sicher im Code, irgendwie müsste man wie gesagt die Daten auf die Zeilen 'aufteilen'..

uwefed


Am Ram sollte es nicht liegen, es ist z.b. kein Problem alle vier Zeilen anzuzeigen, nur lädt er eben in jeder Zeile alle 4 Abfahrten übereinander!
Man müsste also irgendwie die Daten 'trennen', so dass das erste Array in der ersten Zeile angezeigt wird etc..

Das mußt Du sagen und uns nicht raten lassen was das Problem sein könnte.  ;) ;) ;)
Also bitte Schaltung oder wenigstens welche Displays hast Du?
So wie ich verstanden habe kannst Du etwas auf 4 Zeilen ausdrucken aber nicht die empfangenen Daten?

Grüße Uwe

uwefed

Code: [Select]
// If a LF, process the line
  if (inChar == 10 ) {

    /*
      Serial.print("routeStr: ");
     Serial.println(directionStr);
     Serial.print("directionStr: ");
     Serial.println(directionStr);
     */

    // Find specific tags and print data
    if (matchTag("<time>")) {
      Serial.print(" Abfahrt: ");
      Serial.print(dataStr);
      String stringOne = dataStr;
      Serial.println(stringOne);
      String stringTwo = stringOne;
      stringTwo.replace("min","'");
      stringTwo.replace(":","");
      matrix.setTextSize(1);
      matrix.setTextColor(1);
      {
        matrix.setCursor(24, 0);
        matrix.println(stringTwo);
        matrix.writeScreen();
      }


Im Beispiel suchst Du nach dem "<time>" string gibst ihn aber dann immer auf die gleiche Zeile aus (matrix.setCursor(24, 0); ) . Nimm einen Zähler den Du bei jedem letzten TAG einer Zeile erhöhst und dann den Kursor dementsprechend setzt.
Code: [Select]
matrix.setCursor(24, Zeile*8);

Grüße Uwe

thmmax

Lieber Uwe,
es handelt sich um folgende Displays: http://adafruit.com/products/555.

Ich weiß, es ist ein bisschen kompliziert :)
Also: Im Prinzip funktioniert alles. Er empfängt die Daten (man sieht sie im Serial Monitor), er lädt sie auch regelmäßig nach.
Es ist auch kein Problem, eine Abfahrt (einen empfangenen Datensatz) auf der LED Matrix anzuzeigen (eine Zeile).

Wenn man jedoch vier Datensätze hat (im Empfangslink auf Rows=4 umstellt), werden die Daten zwar auch auf der Matrix angezeigt, aber leider alle in einer Zeile, überlagernd.
Ich möchte also die Daten auf die 4 Zeilen der Matrix aufteilen...

thmmax

Lieber Uwe,

danke für deine superschnelle Antwort :)
Das mit dem Zähler habe ich mir auch schon überlegt.

Allein mit dem z.B.
Code: [Select]
matrix.setCursor(24, 1*8); funktioniert es nicht, da setzt es alles eine Reihe nach unten...
Im Prinzip müsste man also eingeben, dass es bei jedem Tag eine Reihe nach unten geht, aber wie bloß...

LG Max

uwefed

Der Zähler muß bei null beginnen.
Grüße Uwe

thmmax

Lieber Uwe,
hab ich ganz übersehen :) Aber leider tut sich bei 0 auch nicht viel, alles bleibt in der ersten Zeile..
Hier der abgeänderte Code:
Code: [Select]
if (matchTag("<time>")) {
      Serial.print(" Abfahrt: ");
      Serial.print(dataStr);
      String stringOne = dataStr;
      Serial.println(stringOne);
      String stringTwo = stringOne;
      stringTwo.replace("min","'");
      stringTwo.replace(":","");
      matrix.setTextSize(1);
      matrix.setTextColor(1);
      {
        matrix.setCursor(24, 0*8);
        matrix.println(stringTwo);
        matrix.writeScreen();
      }

uwefed

Code: [Select]
matrix.setCursor(24, 0*8);
So bringst Du den Text immernoch immer wieder auf die gleiche Zeile.

Nehmen wir mal an route ist der letzte Tag einer Zeile den Du ausgibst:
Code: [Select]

if (matchTag("<route>")) {
      Serial.print(" Linie: ");
      Serial.print(dataStr);
      matrix.setTextSize(1);
      matrix.setTextColor(1);
      {
        matrix.setCursor(0, Zeile);
        matrix.print(dataStr);
        matrix.writeScreen();
      }
     Zeile++;
      }


"matrix.setCursor(0, Zeile);" das gleiche ist mit den anderen Ausgaben zu machen. Die Variable Zeile ist beim herunterladen der Infos auf null zu stellen.

Grüße Uwe

thmmax

Lieber Uwe,

ich bin glaub ich echt zu blöd um das zu kapieren :) Oder das stundenlange Sitzen vor dem Code hat mein Hirn schmelzen lassen :)

Hab also meinen Code mal umgeändert, aber wie definiere ich dann "Zeile"?
Code: [Select]
   
      if (matchTag("<time>")) {
      Serial.print(" Abfahrt: ");
      Serial.print(dataStr);
      String stringOne = dataStr;
      Serial.println(stringOne);
      String stringTwo = stringOne;
      stringTwo.replace("min","'");
      stringTwo.replace(":","");
      matrix.setTextSize(1);
      matrix.setTextColor(1);
      {
        matrix.setCursor(24, 0*8);
        matrix.println(stringTwo);
        matrix.writeScreen();
      }
    Zeile++;
    }
    if (matchTag("<direction>")) {
      Serial.print(" Ziel: ");
      Serial.print(dataStr);
      char mostSignificantDigit = dataStr[0];
      matrix.setTextSize(1);
      matrix.setTextColor(1);
      {
        matrix.setCursor(16, 0*8);
        matrix.println(mostSignificantDigit);
        matrix.writeScreen();
      }
    Zeile++;
    }
    if (matchTag("<route>")) {
      Serial.print(" Linie: ");
      Serial.print(dataStr);
      matrix.setTextSize(1);
      matrix.setTextColor(1);
      {
        matrix.setCursor(0, 0*8);
        matrix.print(dataStr);
        matrix.writeScreen();
      }
      Zeile++;
      }


Sorry wegen der vielen Nachfragen!
LG Max

uwefed

Mach mal ne Pause dann gehts mit dem Programmieren auch wieder besser.
Die Variable definierst Du am Anfang wo die anderen Variablen sind. Du setzt sie auf Null wenn Du die Fahrplaninformationen vom Internet holst und nach der Ausgabe einer Zeile erhöhst Du sie um 8 ( oder 1 und nummst sie mal 8 in den matrix.setCursor(); )
Grüße Uwe

thmmax

Lieber Uwe,
hab der ganzen Sache heute noch einen letzten Versuch gegeben :)

Also ganz oben die Zeile mit #define Zeile 0 definiert.
Dann nochmal den Code geändert:

Code: [Select]
if (matchTag("<time>")) {
      Serial.print(" Abfahrt: ");
      Serial.print(dataStr);
      String stringOne = dataStr;
      Serial.println(stringOne);
      String stringTwo = stringOne;
      stringTwo.replace("min","'");
      stringTwo.replace(":","");
      matrix.setTextSize(1);
      matrix.setTextColor(1);
      {
        matrix.setCursor(24, Zeile*8);
        matrix.println(stringTwo);
        matrix.writeScreen();
      }
    Zeile++;
    }
    if (matchTag("<direction>")) {
      Serial.print(" Ziel: ");
      Serial.print(dataStr);
      char mostSignificantDigit = dataStr[0];
      matrix.setTextSize(1);
      matrix.setTextColor(1);
      {
        matrix.setCursor(16, Zeile*8);
        matrix.println(mostSignificantDigit);
        matrix.writeScreen();
      }
    Zeile++;
    }
    if (matchTag("<route>")) {
      Serial.print(" Linie: ");
      Serial.print(dataStr);
      matrix.setTextSize(1);
      matrix.setTextColor(1);
      {
        matrix.setCursor(0, Zeile*8);
        matrix.print(dataStr);
        matrix.writeScreen();
      }
      Zeile++;
      }


Und jetzt hab ich das Problem, dass er mir wegen dem Zeile++ einen 'lvalue required as increment operand' Fehler anzeigt...
Das wär dann die letzte Frage für heute 8)
LG Max

Go Up