Arduino web server

Salve a tutti,
ho appena cominciato a utilizzare l’Ethernet-Shield e le relative librerie per provare a mettere arduino in rete e creare una pagina web che mi permetta di inviare alcuni comandi. Ho scritto un po di codice che secondo me dovrebbe rispondere a una richiesta “GET” inviata da un web-browser facendo caricare al client una paginetta html. Il problema è che non funziona e non riesco a capire l’errore. E’ concettualmente corretto quello che sto facendo???Se si dove sta l’errore???(scusate i commenti ma sono prove che sto facendo a casa e sistemero piu avanti :stuck_out_tongue: )

//Le librerie per la comunicazione SPI servono per il
//corretto funzionamento della libreria Ethernet
#include <SPI.h>
//Importo le librerie per l'Arduino networking
#include <Client.h>
#include <Ethernet.h>
#include <Server.h>
#include <Udp.h>


//Setto alcune costanti
const unsigned int BAUD_RATE = 9600;
const unsigned int LED_PIN = 7;
const unsigned int SERVER_PORT = 80;
const unsigned int TEST_LED_PIN = 3;
const unsigned int LINE_FEED = 10;

//Setto l'indirizzo MAC
byte mac[] = {0xDE,0xAD,0xBE,0xEF,0xFE,0xED};  
//Setto l'indirizzo IP
byte ip[] = { 192,168,1,31};
//Setto il gateway
byte gateway[] = {192,168,1,1};
//Setto la subnetmask
byte subnetmask[] = {255,255,255,192};

//Creo il server
Server server = Server(SERVER_PORT);

//Controllo che sia il primo messaggio ricevuto dal client
boolean first_message = true;
//Stringa contenente il messaggio in arrivo
String incoming_string = "";

//Comandi
String GET_STRING = "GET / HTTP/1.1";

void setup(){
  
  //inizializzo le comunicazioni della schedina
  Ethernet.begin(mac,ip,gateway,subnetmask);
  Serial.begin(BAUD_RATE);
  server.begin();
  
  pinMode(LED_PIN,OUTPUT);
  pinMode(TEST_LED_PIN,OUTPUT);
  
  Serial.println("Waiting for connection...");
  
  digitalWrite(TEST_LED_PIN,HIGH);
  delay(1000);
  digitalWrite(TEST_LED_PIN,LOW);
  
}

void loop(){
  //Controllo se ci sono messaggi in arrivo dal client
  Client client = server.available();
  //se rivela una connessione da un client
  if(client){
    while(client.connected()){
      //Se è il primo messaggio avvisa che è avvenuta la connessione
      if(first_message){
      Serial.println("Client connected...");
      first_message = false;
      }
      //Se sono disponibii byte allora leggi il byte e aggiungilo in coda alla stringa
      while(client.available()){
        char c = client.read();
        incoming_string = incoming_string + c;
        //Visualizzo la riga appena giunta
        if(c == '\n'){
        Serial.println(incoming_string);
        }
      }
      if(incoming_string.startsWith(GET_STRING)){
        client.println("HTTP/1.1 200 OK");
        client.println("Content-Type: text/html");
        client.println();
        client.println("<html><head><title>Prova ethernet</title>");
        client.println("</head><body>");
        client.println("<form name=\"input\" method=\"POST\">");
        client.println("Testo di input: <input type=\"text\" name=\"variabile\" />");
        client.println("<input type=\"submit\" value=\"Invia\" />");
        client.println("</form></body></html> ");
      }
      delay(5);
      client.stop();
      Serial.println("Disconnected...");
    }
  }
}

in questo modo: while(client.available()){ dai per scontato che la richiesta get ti arrivi in un unico pacchetto (probabilme ma non sicuro) cosa ti comapre nel seriale?

Questo è quello che si vede sulla seriale...è esattamente quello che mi aspetto...ovviamente il codice nn è molto furbo ma era solo una prova...ogni volta che aggiunge una riga plotta a video la stringa ovviamente alla fine del ciclo while la variabile incoming_message contiene l'intera stringa...

questo viene direttamente dalla seriale:

Waiting for connection... Client connected... lettura messaggio GET / HTTP/1.1

GET / HTTP/1.1 Host: 192.168.1.31

GET / HTTP/1.1 Host: 192.168.1.31 Connection: keep-alive

GET / HTTP/1.1 Host: 192.168.1.31 Connection: keep-alive User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.835.187 Safari/535.1

GET / HTTP/1.1 Host: 192.168.1.31 Connection: keep-alive User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.835.187 Safari/535.1 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8

GET / HTTP/1.1 Host: 192.168.1.31 Connection: keep-alive User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.835.187 Safari/535.1 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8 Accept-Encoding: gzip,deflate,sdch

t-IT,it;q=0.8,en-US;q=0.6,en;q=0.4

t-IT,it;q=0.8,en-US;q=0.6,en;q=0.4 Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3

t-IT,it;q=0.8,en-US;q=0.6,en;q=0.4 Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3

Disconnected...

ok, prima di

 if(incoming_string.startsWith(GET_STRING)){

metti

 Serial.println( incoming_string.startsWith(GET_STRING) )

e invece subito dipo l'if

Serial.prinltn("risposta inviata")

Ottima idea la tua!!!Allora il problema è che il risultato dell’espressione

incoming_string.startsWith(GET_STRING)

è 0…quindi nn entra mai nell’if di risposta…il problema dovrebbe quindi essere in quel confronto…secondo te è sbaglaita la stringa con cui confronto il messaggio ricevuto???Ho fatto copia e incolla direttamente dalla seriale però…magari nn ho capito bene come funziona il metodo

.startsWith()

…bo tu hai qualche idea???

Anche io sto lavorando ad un web server ma su microsd, già che ci sono faccio una domanda anch'io, perchè usate byte gateway[] = {192,168,1,1}; byte subnetmask[] = {255,255,255,192};

se li tolgo funziona bene, se li metto addio :)

naturalmente la linea Ethernet.begin(mac,ip,gateway,subnetmask); diventa Ethernet.begin(mac,ip);

che funzione hanno di preciso?

ciao

imposti il default getway (indirizzo ip del router) cioè l'indirizzo di default per i pacchetti che devono andare fuori (questi pacchetti ovviamente vengono inviati al router che provvedera a mandarli al destinatario)e la sottomaschera della tua rete privata...per la sottomaschera devi controllare quella della tua rete...

Hai qualche consiglio da darmi???Come fai tu a riconoscere la richiesta GET del protocollo HTML??? Risolto questo problema anche io vorrei caricare una pagina presente sulla memory card è molto complesso???

pablos71: Anche io sto lavorando ad un web server ma su microsd, già che ci sono faccio una domanda anch'io, perchè usate byte gateway[] = {192,168,1,1}; byte subnetmask[] = {255,255,255,192};

se li tolgo funziona bene, se li metto addio :)

naturalmente la linea Ethernet.begin(mac,ip,gateway,subnetmask); diventa Ethernet.begin(mac,ip);

che funzione hanno di preciso?

ciao

perchè il tuo gateway al 99% è 255,255,255,0 (ovvero quelli di default)

paolo_fiorini3: imposti il default getway (indirizzo ip del router) cioè l'indirizzo di default per i pacchetti che devono andare fuori (questi pacchetti ovviamente vengono inviati al router che provvedera a mandarli al destinatario)e la sottomaschera della tua rete privata...per la sottomaschera devi controllare quella della tua rete...

Hai qualche consiglio da darmi???Come fai tu a riconoscere la richiesta GET del protocollo HTML??? Risolto questo problema anche io vorrei caricare una pagina presente sulla memory card è molto complesso???

ho il dibbio che la classe String sia sbagliata, non sei il primo che ha questo genere di problemi. Ora devo uscire, ma quando torno indago.

si so cosa significano quei parametri

Il mio router ha come gateway 192.168.2.1
e subnetmask 255.255.255.0

se li inserisco come te nello sketch non entro più, eppure sono quelli i dati ne sono certo il router lo avevo impostato nei dettagli io

bho me ne occuperò più avanti

Per quanto riguarda il GET POST PUT e upload vari … guarda qui ci facciamo la guerra tra poveri ahahahahahah, sono una scarpa totale in html. e nel c++ sto implementando parti sconosciute.
Nei miei primi esperimenti non ho mai fatto html all’interno degli sketch con client.println(…)
Il mio scopo finale è quello di creare pagine web su sd, inutile studiare una cosa che non userò, per cui lavoro solo su html direttamente uploadate da sd, lo scambio dati lo faccio in ajax e uso la libreria jquery.js su sd

per la ethernet shield+sd queste

#include <SPI.h>
#include <Ethernet.h>
#include <Flash.h>
#include <SdFat.h>
#include <SdFatUtil.h>

per la gestione delle stringhe
#include <string.h>

Mi spiace non ti sono di grande aiuto, ma altri qui sono dei guru

ciao

Ti ringrazio...va be per ora faccio un passetto per volta...piu avanti indagherò anche su sd+ethernet...

@Leso...nn credo che le librerie siano sbagliate...ci ho pensato anche io e per curiosita ho aggiunto al codice una prova... Per prima cosa ho dichiarato due stringhe:

String string1 = "Hello";
String string2 = "Hello World";

Dopo di che nel setup gli ho fatto valutare e inviare via seriale il confronto:

Serial.println(string2.startsWith(string1),DEC);

In questo caso nell'esecuzione del programma il risultato è 1 cioè vero...Sono sempre piu convinto che possa essere sbagliata la stringa GET_STRING di confronto ma nn ho veramente la piu pallida idea in come cambiarla...

idea: prova a stampare l'esadecimale della stringa ricevuta e della srtinga GET...

ps. non mi chiamo "leso" :grin:

se li inserisco come te nello sketch non entro più, eppure sono quelli i dati ne sono certo il router lo avevo impostato nei dettagli io

se non li inserisci succede:

void EthernetClass::begin(uint8_t *mac, uint8_t *ip)
{
  uint8_t gateway[4];
  gateway[0] = ip[0];
  gateway[1] = ip[1];
  gateway[2] = ip[2];
  gateway[3] = 1;
  begin(mac, ip, gateway);
}

ovvero prende il suo ip e lo mette col .1 finale (e quindi subnet 255.255.225.0), infatti richiama:

void EthernetClass::begin(uint8_t *mac, uint8_t *ip, uint8_t *gateway)
{
  uint8_t subnet[] = { 
    255, 255, 255, 0   };
  begin(mac, ip, gateway, subnet);
}

che a sua volta come puoi vedere richiama la "normale" begin. è giusto che tu usi quella con meno parametri per comodità, ma mi pare molto strano che passandogli i parametri non funzioni!

Per quanto riguarda il GET POST PUT e upload vari ...... guarda qui ci facciamo la guerra tra poveri ahahahahahah, sono una scarpa totale in html

quì non stiamo parlano di HTML (ovvelo il linguaggio che descrive come sono fatte le pagine) ma dell'HTTP, ovvero il PROTOCOLLO secondo cui quests pagine sono scambiate, e che anche tu, nonostante userai ajax o quel che vuoi, dovrai implementare (a meno che ajax o chi per lui usi un socket per richiedere le pagine, allora lì ti dovrai implementare un tuo protocollo personale per la richiesta e ottenimento delle pagine HTML)

Ciao Leso opss Lesto :grin: grazie per il tuo tempo

Se ho capito bene il risultato di Paolo alla fine è quello che voglio ottenere anch'io ed è cio che cerchiamo in molti con difficoltà, nel mio caso più complicato perchè mi devo occupare anche degli upload delle pagine da sd, ma questo già lo faccio, ora dal momento che ho 2 pulsantini sull'web o un input text o come ci pare, vorremmo che al submit ci arrivasse la stringa completa da elaborare e invece ci arriva nell output dalla seriale: Content-Length: 10 con la lunghezza della stringa, oppure solo la prima lettera mentre riesco a ottenere correttamente l'attributo del pulsante che premo 'button.element.attrib('href')' sempre col metodo post e una libreria in ajax.

var url =button.element.attrib('href'); $.ajax({type: "POST", data: ("provatesto", dataType: "text", cache: false, url: url, ............ ............

Il secondo passo è quello di rispondere al client una stringa senza che le nostre pagine si incasinino con paginate di simboli strani tra i quali i pulsanti ripetuti centinaia di volte :)

Poco fa ho trovato un post di qualche mese fa dove ti sei sbattuto insieme a Paletta su questo argomento qui http://arduino.cc/forum/index.php?action=printpage;topic=50721.0 dove dici di modificare http 1.1 con http 1.0 non c'e' altro modo? (se non ho capito male dovrei caricare la pagina 10 volte e unire le lettere col concat per avere l'intera stringa) dico bene o dico giusto :astonished:

Visto che hai esperienza in ethernet shield potresti indicarci qualche link dove picchiarci :) Non voglio annoiarti oltre :sleeping:

RingraziandoTi porgo distinti saluti

Il bello è che non ho mai usato un ethernet shield, sono tutte esperienze che puoi fare coi socket sul PC. :D

@pablos71: non ho capito molto di quello che scrivi,a parte che ti manca un sacco di teoria non tanto sulle comunicazioni tcp o HTTP, ma proprio del linguaggio. Se la stringa la scrivi su seriale, allora vuol dire che l'ahi elaborata. se ti arriva solo in parte, vuol dure che l'hai eleborata male. Escludi ajax o menate varie, che complicano solo la vita, cosa succede se ti colleghi?

Il secondo passo è quello di rispondere al client una stringa senza che le nostre pagine si incasinino con paginate di simboli strani tra i quali i pulsanti ripetuti centinaia di volte

per esempio questo mi pare un errore logico della scrittura del codice...

Poco fa ho trovato un post di qualche mese fa dove ti sei sbattuto insieme a Paletta su questo argomento qui http://arduino.cc/forum/index.php?action=printpage;topic=50721.0 dove dici di modificare http 1.1 con http 1.0 non c'e' altro modo? (se non ho capito male dovrei caricare la pagina 10 volte e unire le lettere col concat per avere l'intera stringa) dico bene o dico giusto smiley-eek

no, ogni volta che ricartichi la pagina ottieni la pagina completa. L'HTTP1.1 però permette l'uso di più GET e POST nella stessa connessione, cosa abbastanza incasinata da gestire, se non si riescie a fare neanche una 1.0...

molto probabilmente il tuo codice è molto differente da quello di paolo_fiorini3, ti conviene aprire una discussione a parte.

per qualche link: http://it.wikipedia.org/wiki/Http

Scusami per la gaffe Lesto...spero nn te la sia presa...piu tardi provo a fare quello che mi hai detto...ora purtroppo devo scappare...

Si probabilmente mi sono espresso male,

  • non ho problemi a inviare una stringa sul seriale
  • non ho problemi a inviare una stringa dal client col metodo POST
  • il serial monitor lo uso solo come debug per stampare a video i contenuti delle variabili

non sono capace col c++ a leggere e trattare la stringa per ora ottengo solo la prima lettera di quello che invio dal client alla pressione del submit

Comunque il thread non è mio, sono un intruso 8), casomai farò un altro thread, però seguo il vostro.

grazie ciao

Scusa Paolo, ho provato a incollare il tuo listato così per capire, alla prima connessione mi appare l'input text, metto una parola e mi aspettavo di trovarla nel serial monitor e non la vedo visto che hai messo

incoming_string = incoming_string + c; Serial.println(incoming_string);

poi dopo il disconnected quando provo a rientrare da browser ho pagina bianca, refresh tutto morto :) fa cosi' anche a te?

ciao

no, da quel che vedo il suo codice dovrebbe funzionare. devi postare il codice, se no è impossibile capire che succede

Ho provato a smanettare un po con le stringhe e alla fine sono riuscito a trovare una soluzione... Nn mi convinceva molto l'operazione stringa+stringa per concatenare e quindi ho cercato soluzioni differenti...in sostanza le cose che ho cambiato sono state

String GET_STRING = String("GET / HTTP/1.1");
String incoming_string = String();

Cioè ho usato il costruttore per dichiarare le stringhe e al posto della somma col + ogni volta che leggo un nuovo byte ho usato

incoming_string.concat(c);

che concatena le due stringhe (nel mio caso c è dichiarata come char ma funziona lo stesso)

Ora il programma riconosce il get e invia la pagina solo in quel caso.

questo è cio che viene visualizzato in seriale :

Waiting for connection... Client connected... GET / HTTP/1.1 Host: 192.168.1.31 Connection: keep-alive Cache-Control: max-age=0 User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.835.187 Safari/535.1 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8 Accept-Encoding: gzip,deflate,sdch Accept-Language: it-IT,it;q=0.8,en-US;q=0.6,en;q=0.4 Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3

Risposta inviata Disconnected...

GET /favicon.ico HTTP/1.1 Host: 192.168.1.31 Connection: keep-alive Accept: / User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.835.187 Safari/535.1 Accept-Encoding: gzip,deflate,sdch Accept-Language: it-IT,it;q=0.8,en-US;q=0.6,en;q=0.4 Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3

Disconnected...

mi riuscite a dire che cosa significa il secondo messaggio che arriva subito dopo la richiesta get???Che tipo di messaggio HTTP è???

Se volete metto su tutto il codice...mi fermo che mi sembra gia abbastanza lungo :P

se gaurdi nella barra degli indirizzi, vedrai che molti siti hanno un simbolino, nel caso di arduino è un cerchio verde col simbolo infino e il + e il -.

Questo simbolo è il file immaggine "favicon.ico", contenuta nella root directory

quindi il browser, prima richiede la pagina /, ovvero la pagina iniziale, e dopo richiede il file di immaggine.

per esempio, se richedi l'immagine nella cartella immagini "io.jpg", arriverà una richiesta del tipo:

GET /immagini/io.jpg HTTP/1.1

sottolineo che se la cartella immagini fosse nella cartella "cose" arriverebbe

GET /cose/immagini/io.jpg HTTP/1.1

ATTENZIONE: gli spazi e i caratteri speciali vengono codificati, per esempio se la cartella fosse "cose strane" che contiene immagini che a sua volta contiene io.jpg, verrebbe fuori:

GET /cose%20strane/immagini/io.jpg HTTP/1.1

(ovvero lo spazio diventa %20)

scusa ancora, ma riesci a stampare a video nel serial la stringa che inserisci sulla pagina web? diciamo nel text input? Io la vorrei vedere davvero se ti arriva :)

@Lesto il codice che ho provato è il primo che ha postato Paolo

ciao