SDWebServer – enc28J60 e Arduino (20)

luca 04/11/2014 57

Nel precedente articolo, vi ho mostrato come sia ora possibile inviare in rete documenti di grandi dimensioni usando uno shield con chip ENC28J60. Oggi vi presento il progetto di un completo web server!

Descrizione

Il browser dell’utente invia la richiesta, tramite rete, al modulo ENC28J60. La richiesta viene quindi interpretata dallo sketch in esecuzione su Arduino il quale prepara la risposta. Sia il modulo ENC28J60 che quello per la SD card sono connessi ad Arduino tramite il bus SPI:

webserver-1

Il webserver supporta sia risorse statiche, che risorse dinamiche:

  • le risorse statiche (pagine html, immagini…) vengono lette da SD card
  • le risorse dinamiche (richieste AJAX…) vengono generate direttamente dallo sketch

Lo sketch utilizza il metodo spiegato nel precedente articolo per inviare risorse statiche di qualsiasi dimensione.

Collegamenti

Sia il modulo SD card che quello ethernet utilizzano il bus SPI. Tale bus prevede la possibilità di collegare contemporaneamente più dispositivi, utilizzando un apposito PIN (detto di chip-select) per indicare a quale dispositivo il comando inviato sul bus è destinato. E’ quindi necessario collegare i PIN di CS dei vari moduli a diversi PIN di Arduino e configurare di conseguenza le librerie.

Purtroppo mi sono accorto che gli shield spesso utilizzano il medesimo PIN (ad esempio il 10) oppure che – peggio – non sono collegabili in cascata. Ho quindi dovuto utilizzare due moduli ed effettuare i collegamenti tramite cavetti:

webserver-2

I collegamenti da realizzare sono:

  • alimentazione (5Vmassa);
  • MOSI (da Arduino al dispositivo), PIN 11
  • MISO (dal dispositivo ad Arduino), PIN 12
  • CLK (clock), PIN 13
  • CS (chip select) -> modulo SD PIN 4 di Arduino, modulo ENC28J60 PIN 10 di Arduino

I PIN utilizzati per il segnale di chip select sono configurati all’inizio dello sketch:

#define SD_CS_PIN   4
#define ETH_CS_PIN  10

Risorse statiche da SD card

Per accedere alla SD card, ho utilizzato la libreria tinyFAT invece di quella inclusa nell’IDE perché occupa molto meno memoria RAM.

All’interno del setup(), viene configurato il PIN Arduino collegato al chip select del modulo SD card e viene inizializzata la libreria:

file.setSSpin(SD_CS_PIN);
res = file.initFAT();

Quando viene richiesto un file, per prima cosa lo sketch verifica se il file è presente; in caso negativo viene inviato al browser l’errore 404 (= file non trovato):

if(!file.exists(filename)) {
[...]
bfill.emit_p(PSTR("HTTP/1.0 404 Not Found\r\n\r\nNot Found"));

Se invece il file è presente, questo viene aperto e letto a blocchi di 512 bytes. Ogni blocco è inviato singolarmente (un blocco, un pacchetto) in rete:

res = file.openFile(filename, FILEMODE_BINARY);
[...]
bytesRead = file.readBinary();
uint8_t* payloadBuffer = ether.tcpOffset();
for(int i = 0; i < bytesRead; i++)
  payloadBuffer[i] = file.buffer[i];

Default webpage

Un caso particolare di risorsa statica è la default webpage: quando inseriamo nel browser un indirizzo Internet, la richiesta inviata è semplicemente GET /. Sta a chi configura il server web definire quale è la pagina di default inviata se non ne viene chiesta una specifica. Nello sketch tale pagina è configurata come costante:

static char* defaultPage = "index.htm";
[...]
if(strncmp("GET / ", request, 6) == 0) {
  sendFile(defaultPage);

Risorse dinamiche

Nello sketch è presente un esempio di risorsa dinamica: la pagina uptime.ino.

Tale pagina non viene cercata sulla scheda SD ma viene generata dallo sketch inviando al browser il valore della funzione millis(), che conta il numero di millisecondi da quando lo sketch è in esecuzione (e quindi l’uptime del webserver):

bfill.emit_p(PSTR("HTTP/1.0 200 OK\r\nContent-Type: text/html\r\nPragma: no-cache\r\n\r\n"
  "$L"), millis());
ether.httpServerReply(bfill.position());

Esempio

Ho preparato un completo esempio: un sito che visualizza una homepage dove, tramite chiamate AJAX ogni 30 secondi, viene visualizzato l’uptime del server:

sdweb

ecco il dettaglio delle chiamate AJAX effettuate dal browser:

uptime

Trovate sketch e risorse per il sito di esempio nel mio repository su Github!

57 Comments »

  1. marco arigliani 02/12/2014 at 21:53 - Reply

    ciao..
    ho seguito in questi giorni un po dei tuoi post per quanto riguarda il modulo enc28j60 per arduino..
    volevo solo chiederti se c’è un modo per gestire le richieste “url” e creare una cosa simile a dei file …
    ho fatto un programma che va a leggere la richiesta dell’utente esempio http://ip_arduino/file1 e il programma restituisce la variabile testuale file1 e cosi via per altri file..
    ho solo il problema che se l’url ho l’html delle pagine sono troppo lunghe il programma salta e restituisce sempre una scermata bianca ….
    […]

    • luca 13/12/2014 at 17:33 - Reply

      Ciao Marco (ho rimosso lo sketch per non appesantire il commento): se ti da problemi con URL molto lunghe, significa che stai superando la dimensione massima del buffer di ricezione: a quanto lo hai impostato?

  2. Abhishek Joshi 21/02/2015 at 18:00 - Reply

    Hello sir, after learning from your tutorials about the enc28j60 module, i managed to make an IR remote out of an arduino connected with the enc28j60 to my wifi router and an ios app to go along with it. Now, i dont need to be in line of sight to control my tv. Keep up the good work thanks!.
    Check it out here : https://github.com/abhiace/arduino-IRemote-iOS

    • luca 24/02/2015 at 20:58 - Reply

      Hi! Great work, thanks for sharing!

  3. Luigi 18/03/2015 at 19:22 - Reply

    Ciao Luca, inutile farti i complimenti per i tuoi tutorial, li sto divorando con grande voracità. Bravo, soprattutto per gente come me alle prime armi. Sto cercando di estrarre il numero di likes dalla pagina facebook ma non riesco con la procedura GET mentre leggere una pagina html grazie alle tue informazioni non ci sono più difficoltà. Mi puoi dare delle dritte in proposito.

    • luca 19/03/2015 at 11:28 - Reply

      Ciao Luigi, per leggere i likes usi le API di Facebook o vuoi cercare di “interpretare” la pagina HTML?

  4. Samuel 09/04/2015 at 20:27 - Reply

    Ciao Luca e grazie per il tuo interessante articolo. Sto cercando di far visualizzare sulla mia pagina html un valore di temperatura piuttosto che il valore della funzione “millis() adattando opportunamente il seguente frammento di codice:

    else if(strncmp(“GET /temp.ino “, request, 16) == 0) {

    Serial.print(F(“Requested dynamic page: temp.ino”));

    BufferFiller bfill = ether.tcpOffset();
    bfill.emit_p(PSTR(“HTTP/1.0 200 OK\r\nContent-Type: text/html\r\nPragma: no-cache\r\n\r\n”
    “$L”), temperatura());
    ether.httpServerReply(bfill.position());
    Serial.println(F(” …sent :)”));
    }

    Il valore fornito dalla funzione “temperatura()” non viene visualizzato nella pagina html.

    Ciao e grazie ancora!

    • luca 10/04/2015 at 08:08 - Reply

      Ciao Samuel, $L è un “segnaposto” per un valore di tipo long, che cosa restituisce la tua funzione temperatura()? Prova anche a salvarti prima in una variabile locale il valore restituito e poi usare quella variabile nel bfill.emit_p

  5. Samuel 10/04/2015 at 18:52 - Reply

    Ho dichiarato “long num=100000;” e usato tale variabile nella “bfill.emit_p” ma la pagina mi restituisce l’errore 404: temp.ino non trovata.

    Non capisco come la tua pagina “uptime.ino” viene invece trovata.

    • luca 10/04/2015 at 19:07 - Reply

      L’errore sta qui: strncmp("GET /temp.ino ", request, 16): il valore 16 indica il numero di caratteri da confrontare, essendo il nome della tua pagina più corto, dovrai correggerlo di conseguenza (GET /temp.ino con lo spazio finale è lungo 14 caratteri)

  6. Samuel 13/04/2015 at 14:51 - Reply

    Grazie era proprio quello. Adesso ho assemblato vari sketch che gestiscono un lcd, un tastierino numerico, il modulo per le chiamate e gli sms. Ma provando a visualizzare la pagina web (sia sul pc che sul cellulare) arduino non riesce a ricevere la richiesta e quindi la pagina non viene visualizzata: sul monitor seriale, infatti, non stampa il messaggio “nome della pagina.estensione sent :). Questo problema può derivare dal fatto che arduino sta svolgendo troppe operazioni? Preciso che ogni loop impiega circa 3 sec.

    • luca 13/04/2015 at 17:04 - Reply

      3 secondi sono davvero tanti per gestire i pacchetti di rete… come mai il loop dura così tanto? Hai dei delay? Se fai uno sketch con la sola parte relativa all’enc28j60 funziona? Al ping arduino risponde?

  7. Samuel 13/04/2015 at 19:45 - Reply

    Avevo un delay di 2 secondi alla fine del loop che ho tolto ma il problema persiste. Ho caricato su sd una pagina html contenente solo un tag ma invano. Se carico solo la parte di sketch relativa all’enc28j60 riesco invece a visualizzare la pagina.

  8. Samuel 13/04/2015 at 20:17 - Reply

    Ho appena notato che aumentando la dimensione del l’ethernet buffer (da byte Ethernet::buffer[8000]; a byte Ethernet::buffer[1600];) e ricaricando lo sketch nel microcontrollore, la pagina web viene visualizzata solo una volta: se riaggiorno la pagina o provo a visualizzarla una seconda volta con un altro dispositivo, non visualizzo nulla.

    • luca 13/04/2015 at 21:34 - Reply

      il problema sembra essere di consumo di RAM, ci puoi fare poco: probabilmente il tuo sketch è troppo complesso per le risorse di Arduino. Se già non lo hai fatto e hai delle stringhe di testo, puoi provare a metterle in PROGMEM per risparmiare RAM come spiegato qui: http://playground.arduino.cc/Learning/Memory

  9. Samuel 14/04/2015 at 07:27 - Reply

    Ma l’ide 1.6.1. di arduino, in fase di compilazione, mi dice che ho usato soltanto il 49% di memoria dinamica, ovvero di RAM (correggimi se sbaglio). Per questo motivo, ho scelto di utilizzare Arduino Mega2560.

  10. Igor 14/04/2015 at 12:56 - Reply

    Complimenti per i vari tutorial. In quest’ultimo avresti la possibilità di inserire un link ad una seconda pagina presente su SD ? Secondo me così sarebbe più completo come esempio. Spesso sul forum Arduino ci sono persone che richiedono proprio il multipagina prelevandole da SD (a meno che non lo hai già spiegato in un altro tutorial) ^-^

    • luca 14/04/2015 at 16:08 - Reply

      Ciao, questo esempio gestisce qualsiasi pagina o risorsa statica da SD

  11. majdi 17/04/2015 at 23:59 - Reply

    Hello, it is possible convert tinyFAT with SD.h library for sd card function. Im using MEGA2560.

    • luca 18/04/2015 at 09:22 - Reply

      Hi! It should work fine (I used TinyFAT due to the limited RAM memory of Arduino UNO) but I don’t own a Mega and I can’t test it… if you try, let us know the result!

  12. krp 18/05/2015 at 12:00 - Reply

    Hello Luca! have you any scheme for connection encj->sd->arduino ? or maybe other pics ? i can’t connec properly my one and it can’t work ;< greetings!

  13. Maurizio 07/09/2015 at 11:07 - Reply

    I complimenti per il tuo lavoro sono dovuti ma per saltare subito al problema: tu hai implementato la tinyFAT.h mentre io utilizzo SdFat.h;
    ho tentato la conversione ma, evidentemente, ho sbagliato qualcosa! Non è che potresti ………

    • luca 07/09/2015 at 11:24 - Reply

      Ciao Maurizio, purtroppo avevo provato anch’io ad usare la SdFat ma è troppo “pesante” in termini di RAM, non rimane sufficiente spazio per il buffer ethernet… quindi sono stato costretto ad usare la TinyFAT.

  14. Maurizio 08/09/2015 at 08:37 - Reply

    Grazie Luca, allora provo anch’io il … trapianto!

  15. Maurizio 08/09/2015 at 09:40 - Reply

    Rieccomi in panne!
    Al di là della sostituzione del riferimento WProgram.h -> Arduino.h (sia in tinyFat.h che .cpp) la compilazione riporta una sequenza di errori in mmc.h.

    che fare????

    • luca 10/09/2015 at 07:39 - Reply

      Ciao Maurizio, ma stai usando questa libreria? Non ho dovuto modificare nulla, è già compatibile con le versioni >1.0

  16. Maurizio 10/09/2015 at 18:56 - Reply

    A me ha dato una prima serie di errori; da un blog ho trovato la soluzione ovvero sostituire WProgram.h -> Arduino.h. Gli errori aono cambiati e sono quelli che ti ho elencato.

    Ora sta emergendo un altro strano problema: sviluppando le tue rountine per la gestione web di 2 led fino a 18 a volte il Mega risponde subito aggiornando la pagina, a volte ci mette anche un minuto e a volte … non risponde!
    Questa instabilità nei risultati mi mette un po’ in difficoltà!

  17. Maurizio 11/09/2015 at 11:20 - Reply

    In merito alla tinyFat quella presente al link da te segnalato è operativa; io l’ho scaricata da gitHub e quella ha problemi.

  18. Gale 01/12/2015 at 12:12 - Reply

    Ciao Luca, thank you for sharing your work with us.
    Please help me, I’m your code use for Arduino Pro Micro (Chinese copy), the Ethernet module ENC28J60 and SD Card module LcSoft.
    Everything is done according to your instructions and everything nice upload to the Arduino, but I have no info on serial monitor, and my website is not working.
    Best regards, Gale

    • luca 02/12/2015 at 20:12 - Reply

      Gale, start using one of the simpler sketches, testing only the “ethernet” part with a ping… if it works we’re sure the connection to the ethernet module is ok and we can investigate further.

  19. Gale 03/12/2015 at 08:45 - Reply

    Dear Luca, I connect separately Ethernet shield with Arduino – and it works.
    Then I connect only the SD card shield with Arduino – and it works.
    But when both modules connect to the Arduino, I do not get anything
    Was thinking that there might be a problem that tinyFat library does not support the Arduino Leonardo (my Arduino Pro Micro is classified as Leonardo).
    Thank you for your quick reply.
    Best regards

    • luca 07/12/2015 at 09:07 - Reply

      Hi! Yes, TinyFAT doesn’t support Leonardo so that could be your issue… anyway with Leonardo you should be able to use the “standard” SD library

  20. Leandro 10/12/2015 at 14:24 - Reply

    can you explain the connections you made ​​? I do not know how to connect the SD module and ENC28J60 simultaneously on the Arduino

  21. Jan 06/01/2016 at 14:15 - Reply

    Hi,
    when i use EtherCard and TinyFAT library i and declare ethernet buffer to 500 i have problem with dynamic memory – Arduino inform me about that it used about 89% dynamic memory and board can not work correct. When i decrese buffer i have problem with ICMP ethernet module etc.

    • luca 07/01/2016 at 09:39 - Reply

      Hi Jan, unfortunately it’s a common problem: the enc28j60 does not include a buffer and therefore you’re forced to use the Arduino RAM… moving to the W5100 chip or using a more “powerful” Arduino may solve.

  22. Piero 12/05/2016 at 18:47 - Reply

    Ciao Luca, ho un problema riguardo SD, in primis ho utilizzato una SD da 1GB e tutto funzionava, poi all’improvviso non andava più .. nemmeno leggerla con il PC.. ho provato con altre SD da 2 GB ma non le legge, mi sai dare qualche dritta? Grazie mille

    • luca 16/05/2016 at 07:51 - Reply

      Ciao Piero, controlla come sono formattate le SD… la libreria usata (TinyFAT) supporta solo memory fino a 2Gb in FAT16.

  23. Riccardo 06/07/2016 at 15:46 - Reply

    Ho una domanda ma il modulo ethernet con enc28j60 non va alimentato a 3.3 volt ?

    • luca 25/07/2016 at 09:27 - Reply

      ciao, dipende: molti moduli (incluso quello che ho io) hanno a bordo un regolatore quindi puoi alimentarli a 5V

  24. Lada 29/09/2016 at 10:04 - Reply

    Thx for yours tuturials about enc28j60. You are very smart! How can I receive big post request (700 kB) from web browser client and save it on the SD card (TinyFAT)?

    • luca 30/09/2016 at 15:55 - Reply

      Hi Lada, well you may try to save the different packets to a file on your sd but you’ll probably find some problems due to the limited memory of both arduino and the ethernet driver.

  25. Lada 02/10/2016 at 15:02 - Reply

    Thx :-) Can you please write, how to store packets? I have problems with oveloading buffer. Your examples are only about reading from the sd card… Thank you.

  26. Avrah 11/10/2016 at 09:10 - Reply

    help! Failed to initialize SD card

    ethernet shield work but sd shield not work.

    • luca 13/10/2016 at 14:53 - Reply

      are you sure that the CS pin is connected correctly? Is the SD formatted in FAT?

  27. Wenderson Cunha Campos 09/11/2016 at 14:23 - Reply

    I’m trying to make an application to the master Arduino communicates with the ENC28J60 and other Arduino as a slave. But I do not know how to disable the select chip ENC28J60 and enable the chip select slave according to the command master receives the Arduino ENC28J60. How I could disable it only to send the byte to the slave and then enable it again.
    Grateful
    Wender

    • luca 12/11/2016 at 14:45 - Reply

      Hi! you need to setup a different CS pin for each SPI device you connect to the “master” Arduino

  28. Massimo 05/01/2017 at 13:07 - Reply

    Ciao, sono riuscito a far funzionare l’esempio qui sopra e ti ringrazio, ma volevo riportare la mia esperienza per evitare a qualcun altro di perdere un sacco di tempo: all’inizio non funzionava nulla (ping saltuari, alcune volte si bloccava sull’apertura della SD)
    Alla fine ho cambiato i collegamenti portando direttamente da Arduino a ENC28J60 i 3.3V (prima era collagato ai 3.3V della scheda SD) e lasciando la scheda SD alimentata con i 5V arduino: per magia tutto si è messo a funzionare. Il mio modulo ethernet per completezza è il HanRun HR9111105A 16/02
    Spero di essere stato utile a qualcuno!

    • luca 09/01/2017 at 08:25 - Reply

      grazie per il commento Massimo!

  29. Waruna Abayarathna 27/04/2017 at 15:00 - Reply

    Dear
    How this 2 module connect to Arduino mega
    Thank you

    • luca 29/04/2017 at 16:03 - Reply

      Hi! you can find the required connections on the library’s webpage.

      • Waruna Abayarathna 03/05/2017 at 12:05 - Reply

        Thank you for your reply
        I already use this 2 module individually
        But my problem is How this 2 module connect in simultaneously

        • luca 06/05/2017 at 15:48 - Reply

          Hi! You can connect both the modules to the SPI bus (use the same pins) but use two different pins for the SS (slave select) signal

  30. jacopo 28/07/2017 at 11:25 - Reply

    ciao, non riesco a far funzionare entrambe le schede insieme , che fare ? i collegamenti sono corretti .

    • luca 28/07/2017 at 12:45 - Reply

      ciao, collegate singolarmente vanno? Il pin CS delle due schede è collegato a due diversi PIN di Arduino?

      • jacopo 28/07/2017 at 17:31 - Reply

        si, da sole funzionano entrambe e quando insieme solo la ethernet funziona. i cs sono separati 4 sd e 10 ethernet

      • jacopo 28/07/2017 at 17:50 - Reply

        mi correggo da sola la sd non va con la libreria tinyfat (la sd è una 2 GB in configurazione FAT) mentre con la libreria SD funziona

  31. jacopo 28/07/2017 at 19:07 - Reply

    Per chiunque si trovi come nel mio caso ad usare l’esempio proposto da Luca Dentella e non riesca ad inizializzare la SD: assicurarsi che sia formattata correttamente e non corrotta. Provate ad usare l’esempio cardinfo della libreria SD e nel caso in cui vi siano problemi formattatela con il programma SD Formatter utilizzando l’azione di overwrite totale !

Leave A Response »

Questo sito usa i cookie per poterti offrire una migliore esperienza di navigazione maggiori informazioni

Questo sito utilizza i cookie per fonire la migliore esperienza di navigazione possibile. Continuando a utilizzare questo sito senza modificare le impostazioni dei cookie o clicchi su "Accetta" permetti al loro utilizzo.

Chiudi