Go Down

Topic: Arduino web client - server Apache - Arduino web server..... dove sbaglio? (Read 972 times) previous topic - next topic

Rob13

Ciao a tutti.
Sottopongo alla vostra "esperta" attenzione un fenomeno strano che mi sta succedendo.
La situazione è questa: ho due arduino (nano e Uno), il primo configurato come web client, il secondo come web server. Non si parlano direttamente, ma attraverso un server Apache con PHP e MySQL.
Il client invia un POST ad uno script PHP sul server Apache, il quale lo interpreta e invia il risultato dell'interpretazione al web server arduino tramite curl.
La stessa operazione si può effettuare da una semplice pagina web che si trova sul server Apache: la pagina web invia i dati del POST allo script PHP e questo li gira al web server arduino con curl.
Fin qui niente di straordinario: sul web si trovano decine di queste cose!
Il fatto strano che si verifica è questo: se il POST arriva al web server arduino tramite pagina web, il web server arduino esegue il comando senza esitazioni, ma, se arriva dal client arduino, 9 volte su 10 si perde per strada.
Ho provato a collegare un arduino per volta: prima il solo client, inviando il comando al server Apache e tutto funziona correttamente. Il comando viene ricevuto dal server.
Poi ho collegato solo il web server arduino (senza client arduino) e riceve correttamente il comando dalla pagina web, eseguendolo senza problemi di nessun tipo.
Il problema si presenta solo quando sono entrambi collegati.
Indagando, ho riscontrato che il web server arduino ha bisogno di circa 60 secondi di stop tra un comando e l'altro (inviato del client arduino): già 55 secondi non vanno bene!
Siccome non ho una grande esperienza, chiedo a voi un parere: è un problema della mia rete lan (che si perde per strada i pacchetti del client arduino)? Un conflitto tra le due schede arduino sulla rete? O forse un problema di codice (anche se, come ho detto, presi singolarmente funzionano senza intoppi...)?
Naturalmente, grazie per ogni suggerimento che vorrete darmi.
qui sotto il codice del web server arduino.
Code: [Select]


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

byte mac[] = { 0x90, 0xA2, 0xDA, 0x0D, 0xCD, 0xCA };
IPAddress ip(192,168,1,52);
EthernetServer server(80);
char stanza[15];
char opzione[15];
char action[15];

boolean optOn = false;

int optionPin;

int opt_0 = 0;
int opt_1 = 0;
int opt_2 = 0;

void setup() {

 Serial.begin(57600);
 
 pinMode(8, OUTPUT);
 pinMode(7, OUTPUT);
 pinMode(6, OUTPUT);
 
 Serial.println("********************************************");
 Serial.println("** Establishing network connection...     **");
 Ethernet.begin(mac, ip);
 delay(300);
 server.begin();
 
 Serial.println("** Connected.                             **");
 Serial.print("** Arduino IP address: ");
 Serial.print(Ethernet.localIP());
 Serial.print("       **");
 Serial.println();
 Serial.println("********************************************");
}

#define bufferMax 128
int bufferSize;
char buffer[bufferMax];

void loop() {
 // listen for incoming clients
 EthernetClient client = server.available();
 if (client) {
   Serial.println("** Client connected.                      **");
   // an http request ends with a blank line
   boolean currentLineIsBlank = true;
   bufferSize = 0;
   
   while (client.connected()) {
     if(client.available()){
       char c = client.read();
       // if you've gotten to the end of the line (received a newline
       // character) and the line is blank, the http request has ended,
       // so you can send a reply
       if (c == '\n' && currentLineIsBlank) {
         // Here is where the POST data is.  
         while(client.available()) {  
           char post = client.read();  
           if(bufferSize < bufferMax)    // if the character is NOT a 'RETURN'
                                         // and the number of characters received
                                         // is les than bufferMax, keep reading
           buffer[bufferSize++] = post;  // save the new character in buffer and increment bufferSize
           }
         
         // Prints the received request to serial monitor
         Serial.println("** Received POST request:                 **");
         Serial.println(buffer);  // buffer contains the received request
                   
         ParseReceivedRequest();
         PerformRequestedCommands();
       
         // Sends response to client (browser)
         Serial.println("********************************************");
         Serial.println("** Sending response                       **");
         Serial.println("********************************************");
         // send a standard http response header that cause a redirect to the Apache server
         client.print("<html>");
         client.print("<head>");
         client.print("<meta http-equiv='refresh' content='0; url=http://192.168.1.53/superhouse/rmprocessor.php?stanza=");
         client.print(stanza);
         client.print("'>");
         client.print("</head>");
         client.print("</html>");
         client.flush();
         client.stop();
         delay(300);
       }
       else if (c == '\n') {
         // you're starting a new line
         currentLineIsBlank = true;
       }
       else if (c != '\r') {
         // you've gotten a character on the current line
         currentLineIsBlank = false;
         }
       }
     }
   
   Serial.println("** Port closed. Waiting for next request. **");
   Serial.println("********************************************");
   Serial.println();
   
}

}


void ParseReceivedRequest() {
 
 // Prints message to serial monitor
 Serial.println("********************************************");
 Serial.println("** Parsing received POST request          **");
 Serial.println("********************************************");
 Serial.println("** Printing parsed strings for debugging  **");
 Serial.println("********************************************");
   
 // Set some variables to parse the received string
 char* p1;
 char* p2;
 char* p3;
 
   
 p1 = strstr(buffer, "=") +1;
 p2 = strstr(p1, "=") +1;
 p3 = strstr(p2, "=") +1;
 
 if(strlen(p3) > 1) {
   strncpy(p3, p3, 1);
   p3[1] = '\0';
 }
   
 // Prints the strings to serial monitor
 PrintString("p1", p1);
 PrintString("p2", p2);
 PrintString("p3", p3);
 
 
 // Initializes the strings
 stanza[0] = 0;
 opzione[0] = 0;
 action[0] = 0;
 
 // Builds the command and parameter strings
 strncat(stanza, p1, p2-p1-12);
 strncat(opzione, p2, p3-p2-8);
 strncat(action, p3, p2-p3-1);
 
 // Prints the command and the parameter to serial monitor
 PrintString("stanza", stanza);
 PrintString("opzione", opzione);
 PrintString("action", action);  
}


void PerformRequestedCommands() {
 if(strcmp(opzione, "24") == 0) {
   optionPin = 6;
  }
 if(strcmp(opzione, "100") == 0) {
   optionPin = 7;
  }
 if(strcmp(opzione, "101") == 0) {
   optionPin = 8;
 }
 if(strcmp(action, "1") == 0) {
   optOn = true;
   RemoteDigitalWrite();
 }
 if(strcmp(action, "0") == 0) {
   optOn = false;
   RemoteDigitalWrite();
   }
}


void RemoteDigitalWrite() {
 if (optOn) {
   digitalWrite(optionPin, HIGH);
   if(optionPin == 6) {
     opt_0 = 24;
     } else if(optionPin == 7) {
       opt_1 = 100;
       } else if(optionPin == 8) {
         opt_2 = 101;
         }
 } else {
   digitalWrite(optionPin, LOW);
    if(optionPin == 6) {
      opt_0 = 0;
      } else if(optionPin == 7) {
        opt_1 = 0;
        } else if(optionPin == 8) {
          opt_2 = 0;
          }  
 }
 
// Information sent to the serial monitor
 Serial.println("Remote Digital Write: ");
 
 PrintNumber("optionPin", optionPin);
 if (optOn) {
   PrintNumber("Option#0", opt_0);
   PrintNumber("Option#1", opt_1);
   PrintNumber("Option#2", opt_2);
 } else {
   PrintNumber("Option#0", opt_0);
   PrintNumber("Option#1", opt_1);
   PrintNumber("Option#2", opt_2);
 }
}


void PrintString(char* label, char* str) {
 Serial.print(label);
 Serial.print("=");
 Serial.println(str);  
}


void PrintNumber(char* label, int number) {
 Serial.print(label);
 Serial.print("=");
 Serial.println(number, DEC);
}

madmatt71

Non è che usi lo stesso mac addres e/o indirizzo ip per client e server ?

Rob13

madmatt71...... hai ragione! (spero...)
L'IP è diverso, ma ho dimenticato di modificare il MAC...... errore da newbie....
Lo modifico e provo a vedere cosa succede... speriamo bene!
Faccio sapere appena ci metto sopra le mani.

Grazie, per adesso!

Rob13

madmatt71 mi hai risolto il problema!!!!! Grazie.
Proprio un errore da principiante: avevo distrattamente assegnato lo stesso indirizzo mac ad entrambi i moduli..... ovviamente non poteva funzionare!
Dagli errori si impara (o almeno si dovrebbe !): questo sicuramente non me lo dimenticherò, visto il tempo che ci ho perso.
Ancora grazie per il suggerimento.

madmatt71

Eh .. La cosa incredibile e' che comunque piano piano funzionava .... Il mac address e' usato dagli switch per decidere a chi mandare i pacchetti ip ... Due mac address sulla stessa rete sono garanzia di mal di testa .... Buon divertimento

Rob13

Ciao,

è passato un po' di tempo da quando mi hai risolto il problema, ma....si è ripetuto ancora!

Il sistema che ho messo in piedi (anche grazie al tuo aiuto  ;)) è online da 2 mesi e funziona benone!

Per implementare nuove funzioni, ho un paio di arduino nano che uso per i test, insieme ad una macchine virtuale dove gira il web server apache con php e mysql.

Il problema è questo: lo stesso identico codice che sta girando nel sistema funzonante, non gira sul sistema di test! :0
In pratica, quando viene inviato un POST al web server arduino, il primo messaggio lo riceve senza problemi, ma poi si inchioda per un tempo variabile tra 30 e 60 secondi: in questo intervallo di tempo non riceve più i POST, ma riesco ugualmente a pingarlo...
Ho, naturalmente, assegnato un indirizzo ip e uno mac opportuno a ciascuno degli arduino (client e server) del sistema di test, così come il server apache della macchina virtuale ha il suo indirizzo ip, diverso da qualunque indirizzo ip già presente sulla rete.
La cosa strana è che ho usato lo stesso sistema per sviluppare il codice che gira da due mesi, ma con queste nuove schede non funziona...
Qualche idea?
Non so veramente dove andare a guardare per risolvere il problema e ogni suggerimento è moooooolto apprezzato!

Qui sotto metto il codice che ho usato per il web server arduino (quello che si inchioda!):
Code: [Select]



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

byte mac[] = { 0xDE, 0xAD, 0xC0, 0xA8, 0x01, 0x34 };  // TEST MAC Address: 0xDE, 0xAD + HEX(192.168.1.52)
IPAddress ip(192,168,1,52);
EthernetServer server(80);
EthernetClient client;

// Delay time
int WAIT = 300;

// Buffer to store HTTP POST Request
#define bufferMax 128
int bufferSize;
char buffer[bufferMax];

// *********************************************************************************
// SETUP
// Initilizing Arduino
// *********************************************************************************
void setup() {
  //Start serial port for debugging
  Serial.begin(9600);

 
  // Set pin D8,D7,D6 as OUTPUT
  pinMode(8, OUTPUT);
  pinMode(7, OUTPUT);
  pinMode(6, OUTPUT);

 
  // Start network connection
  setupCommunications();
     
}

//********************************************************
//** loop
//** This is the main function
//********************************************************
void loop() {
   
  // if client connected
  client = server.available();
 
  // catch HTTP POST Request
  getPostRequest();

}

// ************************************************
// Functions definitions
// ************************************************

// ********************************************************
// ** setupCommunications
// ** This function is called in order to initialize
// ** the communication with the ethernet port.
// ********************************************************
void setupCommunications() {

  //
  // Attempt to establish ethernet connection.
  //
  Serial.println("********************************************");
  Serial.println("** Establishing network connection...     **");
  Ethernet.begin(mac, ip);
 
  // Pause to ensure successful connection.
  delay(WAIT);
 
  // start the server
  server.begin();
 
  //
  // Display the established IP address for debugging
  //
  Serial.println("** Connected.                             **");
  Serial.print("** Arduino IP address: ");
  Serial.print(Ethernet.localIP());
  Serial.print("      **");
  Serial.println();
  Serial.println("********************************************");
 
}


// ********************************************************
// ** getPostRequest
// ** This function catches the HTTP POST Request
// ** coming from the client.
// ********************************************************
void getPostRequest() {
 
  // if a client is connected....
  if (client) {
    Serial.println("** Client connected.                      **");
    boolean currentLineIsBlank = true;
    bufferSize = 0;
   
    while (client.connected()) {
      if(client.available()){
        char c = client.read();
        // if you received a newline character
        // and the line is blank, the HTTP POST Request has ended,
        // so you can send a reply
        if (c == '\n' && currentLineIsBlank) {
          // Here is where the POST data is. 
          while(client.available()) { 
            char post = client.read();   
            if(bufferSize < bufferMax)    // if the character is NOT a 'RETURN'
                                          // and the number of characters received
                                          // is less than bufferMax, keep reading
            buffer[bufferSize++] = post;  // save the new character in buffer and increment bufferSize
            }
           
          // Prints the received request to serial monitor
          Serial.println("** Received POST request:                 **");
          Serial.println(buffer);  // buffer contains the received HTTP POST Request
       
          // Parse HTTP POST Request                 
          ParseReceivedRequest();
         
          // Execute commands
          PerformRequestedCommands();
       
          // Send response to client
          sendResponse();
         
         } else if (c == '\n') {
            // you're starting a new line
            currentLineIsBlank = true;
           } else if (c != '\r') {
               // you've gotten a character on the current line
               currentLineIsBlank = false;
             }
          }
      }
  Serial.println("** Port closed. Waiting for next request. **");
  Serial.println("********************************************");
  Serial.println();
 
  }

}

// ********************************************************
// ** sendResponse
// ** This function sends response to the client.
// ********************************************************
void sendResponse() {

  Serial.println("********************************************");
          Serial.println("** Sending response                       **");
          Serial.println("********************************************");
          // send a standard http response header that causes a redirect to the Apache server
            client.println(F("HTTP/1.1 200 OK"));
            client.println(F("Content-Type: text/html"));
            client.println(F("Connection: close"));
            client.stop();
            delay(WAIT);
}

// ************************************************
// ** ParseReceivedRequest
// ************************************************
void ParseReceivedRequest() {...}


// ************************************************
// ** PerformRequestedCommands
// ************************************************
void PerformRequestedCommands() {...}


smania2000

Visto che il caso è complesso e difficilmente riceverai molte risposte, ho dato una veloce occhiata al tuo codice. Ho trovato un piccolo potenziale problema, ma dipende com'è scritta la ParseReceiveRequest che non hai riportato probabilmente per ridurre il codice postato sul forum.
Quando scrivi :
Code: [Select]
       while(client.available()) {  
           char post = client.read();  
           if(bufferSize < bufferMax)    // if the character is NOT a 'RETURN'
                                         // and the number of characters received
                                         // is less than bufferMax, keep reading
           buffer[bufferSize++] = post;  // save the new character in buffer and increment bufferSize
           }

hai la totale certezza che i dati in ingresso non siano MAI superiori ai 128bytes del buffer?Te lo dico perchè se fossero ad esempio 130, quel codice è corretto e non determina buffer-overflow visto che controlla costantemente di non superare il limite massimo, MA i caratteri in eccesso non verrebbero tenuti in considerazione e la funzione di parse potrebbe fallire, bloccarsi o che so io, dipende da come l'hai scitta. Ovviamente è solo un'ipotesi visto che non ho il codice da analizzare.
Ti consiglierei di aggiungere a quelle righe una else Seriel.println("Buffer owerflow error!!!"), in questo modo se non altro puoi identificare se il problema si verifica o meno.

Rob13

Grazie smania2000 per la risposta.
Anche io avevo avuto il sospetto che potesse essere qualcosa relativo alla varibile buffer[]: avevo pensato che, per qualche ragione, si riempisse completamente e impedisse ogni ulteriore lettura di stringhe in ingresso.
La stringa che riceve ha una lunghezza di 33 bytes, quindi ben al di sotto del limite massimo.
Per sicurezza, ho provato ad aggiungere questa riga:

Code: [Select]

memset(buffer, 0, sizeof(buffer));


ma il problema rimane.
Ho, inoltre e solo per test, eliminato la funzione di parse della stringa: una volta ricevuta, la stampo sul monitor seriale.

Il punto è che la seconda stringa che invio non la riceve proprio perchè è inchiodato a fare chissà cosa!
Dopo un po' (qualcosa più di 30 secondi) si sblocca e riceve il nuovo POST...

Non so se può essere indice di qualcosa, ma ho notato che il led del pin13 rimane acceso fisso anche se io non ho impostato nessun valore.... le sto pensando tutte, non che altro fare...

Se non chiedo troppo, potresti indicarmi il codice che useresti te per leggere un POST in ingresso con arduino? Forse esiste un modo migliore e più efficiente di quello che ho usato io....

Comunque, grazie per l'aiuto!

smania2000

Il tuo codice riprende pari pari quello degli esempi base di Arduino per cui non credo che il problema sia qui. Al router /switch o quel che hai, ci sono collegati solamente gli Arduini ? Se la risposta è si potresti provare a collegare anche un PC (via cavo, non wireless) acceso e connesso, e vedere se cambia qualcosa?
Io non ho "giocato" molto con le ethernet shield, ma devo ammettere che mi hanno dato un bel po' di problemi. Anche qui a casa ho un termometro con display che invia le letture sullo spazio web del mio blog, ma funziona per un certo periodo variabile e poi si blocca e devo riavviare Arduino. Non so dove sia il problema, ma magari stiamo combattendo contro lo stesso nemico .-). Fai per favore la prova che ti dicevo, se dovesse cambiare qualcosa poi ti cerco la spiegazione tecnica, a memoria non mi ricordo proprio, mi è rimasto impreso solo in concetto.

Ciao

Rob13

@smania2000 ho provato a collegare sia le schede Arduino da sole sullo switch, sia anche col pc e non è cambiato nulla....
Ormai penso sempre di più che sia un problema hw: arduino difettoso....

smania2000

ok, mi spiace, non so cos'alttro suggerirti. La prova non era casuale, ho avuto io stesso un problema simile: ci sono router che per un qualche motivo (non ricordo i dettagli tecnici) "non sentono" la ethernet shield ma se ci attacchi un pc il tutto funziona....a casa mia quando il NAS si spegne nelle ore notturne, la ethernet shield non è più vista dal router e i dati di temperatura non vengono inviati. Speravo fosse una cosa simile, anche se non ci credevo molto perchè tu stesso mi dici che riesci a pingare le schede anche quando non funzionano.
Dato che usi le nano non puoi certo usare l'ethernet shield classica, per cui avrai dei moduli diversi...sei sicuro che non siano quelli la causa di tutti i tuoi problemi? Non è che avresti degli UNO e relative ethernet shield "classiche" per fare lo stesso test e vedere se funziona. Io purtroppo ho terminato tutti i suggerimenti che avevo, se riesci a risolvere facci sapere.

Ciao.

Stefano

Rob13

Ciao smania2000,
sospettando un problema hw, ho ordinato un UNO e il suo bel Ethernet shield: oggi è arrivato l'UNO e ho già verificato che si presenta lo stesso identico problema. Quindi, se c'è un problema hw, non è nell'arduino.
A questo punto rimane la scheda di rete: appena arriva lo shield nuovo faccio anche questo test e speriamo di risolvere!
Dopodichè, siccome le schede di rete che sto usando con i Nano hanno 10 giorni di vita, dovrò incaz....mi con chi me le ha vendute.
Per la cronaca, sono gli ethernet network module della Itead Studio: ne ho già uno perfettamente funzionante che sta lavorando da due mesi, ormai.
In ogni caso, grazie per i suggerimenti che mi hai dato.
Ti tengo aggiornato se dovessi riuscire a risolvere il problema.

pablos

aggiungi questo nel setup
Code: [Select]
pinMode(10, OUTPUT);
 digitalWrite(10, 1);
pinMode(4, OUTPUT); //se il tuo shield ha lo slot SD
 digitalWrite(4, 1);//se il tuo shield ha lo slot SD

Ti garantisco al 99% di risolvere il problema ... fammi sapere

Il perchè è molto semplice:
Il pin 10 controlla l'enable e il disable della ethernet, esso passa a 0 quando deve leggere o scrivere sull'ICSP e passa a 1 quando il canale verso lo shield è chiuso, a questo punto è ben chiaro che un pin non settato si comporta in modo random  passa da 0 a 1 in modo casuale (pin flottante).
Quindi se il pin 10 commuta random a 1 quando c'è bisogno di dialogare con lo shield ecco che avrai un freeze
viceversa se va a 0 quando non serve il dialogo ecco un altra probabilità di freeze .
nelle librerie troverai il controllo del pin digitalWrite(CS, high/low); ma non troverai  pinMode(10, OUTPUT);
ciao
no comment

Rob13

@pablos
Grazie per il suggerimento, ma non ha funzionato.
E siccome ho comprato un nuovo Uno e un nuovo Ethernet shield, comincio a pensare a qualcosa di veramente grave!
Se il codice è corretto e gli arduino sono nuovi, cosa può impedire il funzionamento del sistema?
Può essere un problema di rete? Ma quale?
Come ho detto all'inizio, ho già in funzione una coppia di arduino connessi alla rete che funzionano perfettamente con questo identico codice da ormai 2 mesi!

Se posto l'intero codice del mio sistema, c'è qualche anima pia disposta a "sacrificarsi" e farlo girare sulla propria rete per verificarne il funzionamento?
Fatemi sapere e grazie comunque.

Rob.

pablos

posso provare a caricarlo sul mio 2560, ma il client che usi ? da dove mandi i post da un php o da un browser con script js?

ciao
no comment

Go Up