XML Probleme...

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.:

D Kurhaus 18:36 = String 1 E Hauptbahnhof 4 min =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:

#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!

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 FAQ Neuigkeiten und Probleme ab Arduino IDE 1.0 - #2 by uwefed - Deutsch - Arduino Forum

Grüße Uwe

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()!

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.

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'..

thmmax:
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. :wink: :wink: :wink:
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

 // 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 "" 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.

matrix.setCursor(24, Zeile*8);

Grüße Uwe

Lieber Uwe,
es handelt sich um folgende Displays: 16x24 Red LED Matrix Panel - Chainable HT1632C Driver : ID 555 : $24.95 : Adafruit Industries, Unique & fun DIY electronics and kits.

Ich weiß, es ist ein bisschen kompliziert :slight_smile:
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...

Lieber Uwe,

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

Allein mit dem z.B. 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

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

Lieber Uwe,
hab ich ganz übersehen :slight_smile: Aber leider tut sich bei 0 auch nicht viel, alles bleibt in der ersten Zeile..
Hier der abgeänderte Code:

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();
      }
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:

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

Lieber Uwe,

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

Hab also meinen Code mal umgeändert, aber wie definiere ich dann "Zeile"?

      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

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

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

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

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

Und jetzt hab ich das Problem, dass er mir wegen dem Zeile++ einen 'lvalue required as increment operand' Fehler anzeigt...

Und er hat recht. Versuchs mal mit

int Zeile;

Grüße Uwe

Natürlich :slight_smile:
Danke vielmals!
LG Max

Lieber Uwe,

habe gestern und heute wirklich versucht, deine Tipps umzusetzen, aber irgendwas mach ich wohl falsch!

'Zeile' habe ich also ganz oben definiert: #define Zeile 0
Dann habe ich bei jedem tag die Zeile eingefügt: matrix.setCursor(16, Zeile*8);
Und dann noch jeweils das int Zeile++ hinzugefügt. Ich hab das int und Zeile in allen möglichen Kombinationen probiert, ich bekomm immer einen 'expected unqualified-id before numeric constant' Error.

Könntest du mir da nochmal weiterhelfen? Und brauch ich im Code sonst noch was, damit die Daten jeweils auf die nächste Zeile kommen?

Insgesamt sieht der Code in diesem Bereich also so aus:

    if (matchTag("<time>")) {
      Serial.print(F(" 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();
      }
      int Zeile++;

    }
    if (matchTag("<direction>")) {
      Serial.print(F(" Ziel: "));
      Serial.print(dataStr);
      char mostSignificantDigit = dataStr[0];
      matrix.setTextSize(1);
      matrix.setTextColor(1);
      {
        matrix.setCursor(16, Zeile*8);
        matrix.println(mostSignificantDigit);
        matrix.writeScreen();
      }
      int Zeile++;
    }
    if (matchTag("<route>")) {
      Serial.print(F(" Linie: "));
      Serial.print(dataStr); 
      matrix.setTextSize(1);
      matrix.setTextColor(1);
      {
        matrix.setCursor(0, Zeile*8);
        matrix.print(dataStr);
        matrix.writeScreen();
      }
      }
      int Zeile++;

Vielen Dank!
LG Max

thmmax:
'Zeile' habe ich also ganz oben definiert: #define Zeile 0
Dann habe ich bei jedem tag die Zeile eingefügt: matrix.setCursor(16, Zeile*8);
Und dann noch jeweils das int Zeile++ hinzugefügt. Ich hab das int und Zeile in allen möglichen Kombinationen probiert, ich bekomm immer einen 'expected unqualified-id before numeric constant' Error.

0++ geht eben nicht, eine definierte Konstante ist eine Konstante und kann ihren Wert zur Programmlaufzeit nicht ändern.

Probier's mal mit der Definition einer Variablen, wenn sich der Wert im Programm ändern soll!

Danke für den Tipp!
Aber wie soll ich dann die Variable definieren? Die Zeile muss ja 0 sein, sonst stimmt die Anzeige auf der Matrix nicht?