Go Down

Topic: Ethernet, TextFinder e stream: pagine incomplete (risolto!) (Read 1 time) previous topic - next topic

zoomx

Salve a tutti,

sto provando ad implementare un codice che estragga le informazioni meteo da una pagina HTML del servizio ilmeteo.it
Per far questo, richiedo la pagina HTML con i dati e faccio la ricerca delle parti utili utilizzando la libreria TextFinder
http://playground.arduino.cc/Code/TextFinder
Il codice è derivato da un'analogo presente in rete che prende i dati da Yahoo.
http://forum.arduino.cc/index.php?topic=121992.msg920175#msg920175
Quello che succede è che la ricezione della pagina si interrompe, quasi sempre dopo lo stesso numero di caratteri. Per accorgermene ho abilitato il debug attivando la linea #define DEBUG_OUTPUT nel file TextFinder.cpp
Questo problema sembra esserci anche nello sketch originale solo che siccome i dati si trovano giusto nella prima parte ricevuta, non si evidenzia l'errore.

Il codice è questo.
Code: [Select]
/*
 * IlMeteo01
 * derived from Yahoo01
 * http://forum.arduino.cc/index.php?topic=121992.msg920175#msg920175
 *
 * Original code by Webmeister
 * http://forum.arduino.cc/index.php?action=profile;u=15387
 * Read Yahoo Weather API XML
 * 03.09.2012
 * http://arduino-praxis.ch
 *
 * Maybe this is the original one
 * SimpleClientGoogleWeatherDHCP.pde
 *
 * Modifications by zoomx
 * 2015-10-14
 * Added DHCP
 * Added Current Condition
 * http://forum.arduino.cc/index.php?topic=121992.msg933688#msg933688
 *
 * 2015-10-22
 * Changed to IlMeteo.it
 *
 */

//#define DEBUG_OUTPUT  // in TextFinder.cpp messo qui non funziona!

#include <SPI.h>
#include <Ethernet.h>
#include "TextFinder.h"   //http://playground.arduino.cc/Code/TextFinder

byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xAD };
byte ip[] = { 10, 0, 1, 101 };
byte gateway[] = { 10, 0, 1, 1 };
byte subnet[] = { 255, 255, 255, 0 };

IPAddress server(94, 32, 108, 10);   //IlMeteo 94.32.108.10

EthernetClient client;
TextFinder  finder( client );

char place[20];
char hum[5];
char temp[5];
char currentcond[20];

void setup()
{

  // Start Serial Port
  Serial.begin(115200);
  Serial.println(F("IlMeteo01"));
  Serial.println(F("Setup..."));
  pinMode(4, OUTPUT);
  digitalWrite(4, HIGH);
  // Start Ehternet
  //Ethernet.begin(mac, ip);
  if (Ethernet.begin(mac) == 0) {
    Serial.println(F("Failed to configure Ethernet using DHCP"));
    // no point in carrying on, so do nothing forevermore:
    for (;;)
      ;
  }
  // print your local IP address:
  Serial.print(F("My IP address: "));
  for (byte thisByte = 0; thisByte < 4; thisByte++) {
    // print the value of each byte of the IP address:
    Serial.print(Ethernet.localIP()[thisByte], DEC);
    Serial.print(".");
  }
  Serial.println();
}


void loop()
{
  if (client.connect(server, 80))
  {
    Serial.println(F("Connecting to IlMeteo..."));
    client.println("GET /box/previsioni.php?citta=5913&type=real1 HTTP/1.0");
    client.println("HOST:www.ilmeteo.it\n\n");
    client.println("Connection: close");
    client.println();
    Serial.println(F("Connected..."));
  }
  else
  {
    Serial.println(F(" connection failed"));
  }


  if (client.connected())
  {

    // Place/City
    if ( (finder.getString("<div class=\"titolo\">", "</div>", place, 20) != 0) )
    {
      //Serial.print(F("Citta':  "));
      //Serial.println(place);
    }

    // Current Condition
    //if ( (finder.getString("_blank\" title=\"Meteo %%CITY%%\">", "</a></b><br>", currentcond, 20) != 0) )
    if ( (finder.getString("%%\">", "</a></b><br>", currentcond, 20) != 0) )
    {
      //Serial.print(F("Tempo attuale:  "));
      //Serial.println(currentcond);
    }

    // Temperature
    if ( (finder.getString("color:red\">", "&deg;C", temp, 4) != 0) )
    {
      //Serial.print(F("Temperatura C:  "));
      //Serial.println(temp);
    }
    else
    {
      //Serial.println(F("Nessun dato di temperatura"));
    }


    if ( (finder.getString("Umidit&agrave;: ", "<br>", hum, 4) != 0) )
    {
      //Serial.print(F("Umidita':  "));
      //Serial.println(hum);
    }
    else
    {
      //Serial.println(F("Nessun dato di umidita'"));
    }

    // END XML
    Serial.println();
    Serial.print(F("Citta':  "));
    Serial.println(place);
    Serial.print(F("Tempo attuale:  "));
    Serial.println(currentcond);
    Serial.print(F("Temperatura C:  "));
    Serial.println(temp);
    Serial.print(F("Umidita':  "));
    Serial.println(hum);

    Serial.println(F("FINE"));
  }
  else
  {
    Serial.println(F("Disconnected"));
  }

  client.stop();
  client.flush();
  delay(60000);
}

Se lo provate, mostrerà la parte scaricata che, appunto, si interrompe. Quindi stampa i dati trovati, in questo caso la sola città. Lo sketch non si pianta ne va in reset, dopo 60 secondi viene rieffettuata la richiesta e così all'infinito.
Ho provato anche ad usare la classe stream ma succede esattamente lo stesso, stampa solo la città. Non so se in stream c'è un debug per vedere cosa effettivamente viene trattato.
Se provo uno degli esempi della libreria Ethernet, quello che scarica la pagina iniziale di Google, va tutto bene e viene stampato l'intero codice ricevuto, ben più lungo della pagina de il meteo.it
Ho provato a cambiare shield ma il risultato è identico.
Ho anche provato su due reti diverse al lavoro e a casa ma il risultato è sempre identico.
Una cosa strana è che a volte, prima che abilitassi il debug, il codice ha funzionato la prima volta ma poi non ha più funzionato alle richieste successive. Da quando ho abilitato il debug però non succede mai.
Avete qualche idea?
In allegato ho messo lo sketch e la libreria già modificata per il debug. Vanno nella stessa cartella.

Edit: ho cambiato ed accorciato il titolo perché non avevo più caratteri a disposizione.
Edit: finalmente risolto! Buffer overrun!!! Vedi post #24.

pablos71

Lo provo anche se lo avrei fatto in modo diverso.

e questo?
Quote
pinMode(4, OUTPUT);
  digitalWrite(4, HIGH);
dovrebbe essere un 10 non un 4
L'esperienza è il tipo di insegnante più difficile ....
Prima ti fa l'esame e poi ti spiega la lezione.

zoomx

Era un'aggiunta che avevo fatto cercando in giro ma non ha portato ad alcuna differenza nei risultati.

Accetto anche il suggerimento del modo diverso: come?
Io avevo pensato a riprendere l'esempio della pagina di google e farmi un parser da me ma mi sembra di reinventare l'acqua calda.
Questa è l'altra implementazione che mi è venuta in mente ma il risultato è identico
Code: [Select]
/*
 * IlMeteo02
 * derived from Yahoo01
 * http://forum.arduino.cc/index.php?topic=121992.msg920175#msg920175
 *
 * Original code by Webmeister
 * http://forum.arduino.cc/index.php?action=profile;u=15387
 * Read Yahoo Weather API XML
 * 03.09.2012
 * http://arduino-praxis.ch
 *
 * Maybe this is the original one
 * SimpleClientGoogleWeatherDHCP.pde
 *
 * Modifications by zoomx
 * 2015-10-14
 * Added DHCP
 * Added Current Condition
 * http://forum.arduino.cc/index.php?topic=121992.msg933688#msg933688
 *
 * 2015-10-22
 * Changed to IlMeteo.it
 *
 */

//#define DEBUG_OUTPUT  //messo qui non funziona!
// Max string length may have to be adjusted depending on data to be extracted
#define MAX_STRING_LEN  20


#include <SPI.h>
#include <Ethernet.h>

byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xAD };
byte ip[] = { 10, 0, 1, 101 };
byte gateway[] = { 10, 0, 1, 1 };
byte subnet[] = { 255, 255, 255, 0 };

IPAddress server(94, 32, 108, 10);   //IlMeteo 94.32.108.10

EthernetClient client;

char place[20];
char hum[5];
char temp[5];
char currentcond[20];

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

// Flags to differentiate XML tags from document elements (ie. data)
boolean tagFlag = false;
boolean dataFlag = false;


void setup()
{

  // Start Serial Port
  Serial.begin(115200);
  Serial.println(F("IlMeteo02"));
  Serial.println(F("Setup..."));
  // Start Ehternet
  //Ethernet.begin(mac, ip);
  if (Ethernet.begin(mac) == 0) {
    Serial.println(F("Failed to configure Ethernet using DHCP"));
    // no point in carrying on, so do nothing forevermore:
    for (;;)
      ;
  }
  // print your local IP address:
  Serial.print(F("My IP address: "));
  for (byte thisByte = 0; thisByte < 4; thisByte++) {
    // print the value of each byte of the IP address:
    Serial.print(Ethernet.localIP()[thisByte], DEC);
    Serial.print(".");
  }
  Serial.println();
}


void loop()
{
  if (client.connect(server, 80))
  {
    Serial.println(F("Connecting to IlMeteo..."));
    client.println("GET /box/previsioni.php?citta=5913&type=real1 HTTP/1.0");
    client.println("HOST:www.ilmeteo.it\n\n");
    client.println("Connection: close");
    client.println();
    Serial.println(F("Connected..."));
  }
  else
  {
    Serial.println(F(" connection failed"));
  }


  if (client.connected())
  {

    // Place/City
    if ( client.find("<div class=\"titolo\">"))
    {
      tagFlag = false;
      do
      {
        char inChar = client.read();
        if (inChar == '<')
        {
          tagFlag = true;
        }
        else
        {
          Serial.print(inChar);
          addChar(inChar, tmpStr);
        }
      } while (tagFlag == false);
    }

    Serial.println();
    Serial.println(tmpStr);

    clearStr(tmpStr);


    // Current Condition
    if ( client.find("\">"))
    {
      tagFlag = false;
      do
      {
        char inChar = client.read();
        if (inChar == '<')
        {
          tagFlag = true;
        }
        else
        {
          Serial.print(inChar);
          addChar(inChar, tmpStr);
        }
      } while (tagFlag == false);
    }
    Serial.println(tmpStr);
    Serial.println(F("FINE"));
  }
  else
  {
    Serial.println(F("Disconnected"));
  }

  client.stop();
  client.flush();
  Serial.println("Fine ciclo");
  delay(60000);

}


//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 temp buffer and flags to stop current processing
    clearStr(tmpStr);
    tagFlag = false;
    dataFlag = false;

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


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



pablos71

Sto cercando di capire quali sono i dati che restituisce il file
http://www.ilmeteo.it/box/previsioni.php

ad esempio roma è 5913
http://www.ilmeteo.it/box/previsioni.php?citta=5913

catania è 1832
http://www.ilmeteo.it/box/previsioni.php?citta=1832

e di stamparli sul serial

analizzando il sorgente della pagina HTML col tasto destro o dal menu si tratta poi alla fine di prendere solo

Quote
<tr>
               <td class="dati"><a href="http://www.ilmeteo.it/meteo/Catania" target="_blank" class="day" title="Meteo Catania - Marted&igrave;&nbsp;10">Marted&igrave;&nbsp;10</a></td>
               <td class="dati"><a href="http://www.ilmeteo.it/meteo/Catania" target="_blank" title="Meteo Catania - Marted&igrave;&nbsp;10"><img src="ico1/w1.png" alt="Sereno" title="Sereno" border="0"></a></td>
               <td class="dati tMin">15</td>
               <td class="dati tMax">25</td>
               <td class="dati">NE</td>
               <td class="dati">4&nbsp;km/h</td>
               <td class="dati"><table style='height:15px'><tr><td width='7.5' bgcolor='#008000'></td><td width='142.5'>&nbsp;5%</td></tr></table></td>
            </tr>
L'esperienza è il tipo di insegnante più difficile ....
Prima ti fa l'esame e poi ti spiega la lezione.

pablos71

Quella pagina alla fine è tutto un contorno per visualizzarla sul pc, ma in realtà sarebbe sufficiente interrogare il database facendosi restituire una stringa con 4-5 dati separati da una virgola. Questo era il metodo alternativo, ora cerco qual'è la domanda giusta da fare .. io lo facevo col jquery

tipo
Code: [Select]
GM_xmlhttpRequest({
            method: "GET",          
            url:url_data,
            onload: function(response) {


ora vedo se esiste un metodo semplice da tradurre in client.print
e capire quei numeri da dove li prende, perchè da qualche parte il javascript li pesca

dati tMin" 15 (temperat minima)
dati tMax" 25 (temperat massima)
dati">NE (direzione vento)
dati" 4&nbsp;km/h (vento)
...
...
L'esperienza è il tipo di insegnante più difficile ....
Prima ti fa l'esame e poi ti spiega la lezione.

zoomx

Il meteo.it fornisce anche le tabelle xml ma a pagamento, i dati sono descritti qui
http://www.ilmeteo.it/portale/servizi-web
La tabella che chiedo io nello sketch dovrebbe essere la più piccola che sono riuscito ad ottenere, le altre sono più grandi come dimensione della risposta.

Ci sono anche dei siti stranieri ma la loro affidabilità è minore.

Ma il punto è perché la ricezione si interrompe se cerco nello stream ricevuto mentre non si interrompe se leggo lo stream un carattere alla volta. Ho sospettato problemi di ram ma mi sarei aspettato un reset dell'Arduino che invece continua imperterrito.





pablos71

Se leggi un char alla volta e lo stampi va bene, se concateni questi char dentro una stringa senza poterne definire un limite mandi in pappa il micro che ha una ram molto piccola.
L'esperienza è il tipo di insegnante più difficile ....
Prima ti fa l'esame e poi ti spiega la lezione.

zoomx

Anche io pensavo questo ma il risultato dovrebbe essere un reset del micro. Inoltre non dovrebbe succedermi mai che una volta funziona e le altre no come mi è appena capitato.

Ho usato l'IDE 1.6.6 in versione portable in cui credevo di aver aggiornato tutte le librerie (mi chiede di aggiornare daccapo la Ethernet, ma non dovrebbe avere già l'ultima?), al primo giro mi ha stampato l'intera pagine html fino al tag finale, in seguito si interrompe sempre poco dopo la città. Ho quindi premuto il reset per vedere se il comportamento si ripeteva ma non si è ripetuto più.

Edit:
Ho un'altra ipotesi: la pagina è sicuramente richiesta in più pacchetti. Quello che succede è che una volta ricevuto il primo pacchetto gli altri vengono ignorati o addirittura neanche chiesti.
Mi servirebbe un hub ethernet per spiare le comunicazioni ma ormai sono introvabili.

Edit2:
Con il debug attivato dovresti vedere che la ricezione della pagina si ferma poco dopo la riga dove c'è la città.
Se c'è un errore nel parsing ricevi tutta la pagina ma poi non vengono stampati i dati ma questo è facilmente risolvibile.

PaoloP

Se hai un portatile puoi connettere l'Arduino alla Ethernet e usare il Wifi per la connessione a internet.
Con WireShark puoi controllare il traffico tra le due interfacce.

zoomx

Buona idea! Grazie!
L'altra idea che ho avuto è di provare con un ESP8266. Però così non risolvo il mistero.

pablos71

Ma perchè ti serve sniffare il traffico ? forse non ho capito cosa vuoi fare allora... non volevi ricavare temperature e dati vari da una pag web ?
L'esperienza è il tipo di insegnante più difficile ....
Prima ti fa l'esame e poi ti spiega la lezione.

zoomx

Ho il sospetto che la scheda ethernet prenda solo il primo pacchetto e poi chiuda la connessione. Se sniffando il traffico mi ritrovo con tutta la pagina ho l'indizio che il problema è nel mio sketch o in qualche libreria. Se invece mi accorgo che viene scaricato solo il primo pacchetto allora il problema potrebbe stare nell'interazione tra le librerie e la scheda ethernet.

Il problema c'era anche con lo sketch di origine che usava i dati di Yahoo solo che li i dati che servivano erano tutti nel pezzo che viene scaricato comunque, quando ho provato ad accedere anche agli altri dati ho avuto lo stesso comportamento, ogni tanto funziona ma spessissimo no.

pablos71

L'esperienza è il tipo di insegnante più difficile ....
Prima ti fa l'esame e poi ti spiega la lezione.

zoomx

Certo!
Code: [Select]
/*
 * Yahoo01
 * http://forum.arduino.cc/index.php?topic=121992.msg920175#msg920175
 * To get the city ID
 * Goto https://weather.yahoo.com and enter the city in the search bar
 * You get someting like this
 * https://weather.yahoo.com/italy/sicily/catania-713571/
 * The ID is 713571
 *
 * Original code by Webmeister
 * http://forum.arduino.cc/index.php?action=profile;u=15387
 * Read Yahoo Weather API XML
 * 03.09.2012
 * http://arduino-praxis.ch
 *
 * Modifications
 * 2015-10-14
 * Added DHCP by zoomx
 * Added Current Condition
 * http://forum.arduino.cc/index.php?topic=121992.msg933688#msg933688
 *
 */


#include <SPI.h>
#include <Ethernet.h>
#include "TextFinder.h"

byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xAD };
byte ip[] = { 10, 0, 1, 101 };
byte gateway[] = { 10, 0, 1, 1 };
byte subnet[] = { 255, 255, 255, 0 };

// Server Yahoo
//IPAddress server(87, 248, 122, 181);
IPAddress server(217, 12, 1, 156);

EthernetClient client;
TextFinder  finder( client );

char place[50];
char hum[30];
char currentcond[50];

void setup()
{

  // Start Serial Port
  Serial.begin(9600);
  Serial.println("Yahoo01");
  Serial.println("Setup...");
  // Start Ehternet
  //Ethernet.begin(mac, ip);
  if (Ethernet.begin(mac) == 0) {
    Serial.println("Failed to configure Ethernet using DHCP");
    // no point in carrying on, so do nothing forevermore:
    for (;;)
      ;
  }
  // print your local IP address:
  Serial.print("My IP address: ");
  for (byte thisByte = 0; thisByte < 4; thisByte++) {
    // print the value of each byte of the IP address:
    Serial.print(Ethernet.localIP()[thisByte], DEC);
    Serial.print(".");
  }
  Serial.println();
}


void loop()
{
  if (client.connect(server, 80))
  {
    // Call Wetter-API
    // w: ID from your City
    // http://weather.yahooapis.com/forecastrss?w=713571&u=c
    ///
    Serial.println("Connect to Yahoo Weather...");
    client.println("GET /forecastrss?w=713571&u=c HTTP/1.0");
    client.println("HOST:weather.yahooapis.com\n\n");
    client.println();
    Serial.println("Connected...");
  }
  else
  {
    Serial.println(" connection failed");
  }


  if (client.connected())
  {

    // Humidity
    if ( (finder.getString("<yweather:atmosphere humidity=\"", "\"", hum, 4) != 0) )
    {
      Serial.print("Humidity:  ");
      Serial.println(hum);
    }
    else
    {
      Serial.print("No Humidity Data");
    }


    // Place/City
    if ( (finder.getString("<title>Conditions for ", " ", place, 50) != 0) )
    {
      Serial.print("City:  ");
      Serial.println(place);
    }

    // Current Condition
    //if ( (finder.getString("<b>Current Conditions:</b><br /> ", "C<BR /> ", currentcond, 50) != 0) )
    if ( (finder.getString("text=\"", "\"", currentcond, 50) != 0) )
      //                      <b>Current Conditions:</b><br />    <BR />
      //    CHECK     THIS                                         ^^^^
    {
      Serial.print("Current Cond:  ");
      Serial.println(currentcond);
    }

    // Temperature
    if (finder.find("temp=") )
    {
      int temperature = finder.getValue();
      Serial.print("Temp C:  ");
      Serial.println(temperature);
    }
    else
    {
      Serial.print("No Temperature Data");
    }

    // Forecast doesn't work
    if ( (finder.getString("<BR /><b>Forecast:</b><BR />","<br />", currentcond, 50) != 0) )
    {
      Serial.print("Forecast:  ");
      Serial.println(currentcond);
    }
    // END XML
  }
  else
  {
    Serial.println("Disconnected");
  }

  client.stop();
  client.flush();
  delay(60000);
}



Fa uso della stessa libreria TextFinder.
Ho messo l'IP di Yahoo ma credo che se uso yahoo.it dovrebbe funzionare lo stesso.
Anche qui non arriva alle Current Condition mentre stampa ciò che c'è prima.

pablos71

a me non sembra si blocchi

Quote
Yahoo01
Setup...
My IP address: 192.168.2.107.

Connect to Yahoo Weather...
Connected...
Humidity:  88
City:  Catania,
Current Cond:  Clear
No Temperature Data

Connect to Yahoo Weather...
Connected...
Humidity:  88
City:  Catania,
Current Cond:  Clear
No Temperature Data

Connect to Yahoo Weather...
Connected...
Humidity:  88
City:  Catania,
No Temperature Data
L'esperienza è il tipo di insegnante più difficile ....
Prima ti fa l'esame e poi ti spiega la lezione.

Go Up