SDWebServer – enc28J60 and Arduino (20)

luca 04/11/2014 37

In the previous blog post, you learned how to send big files (html pages, images…) with your ENC28J60 shield. Today I’m going to show you a complete webserver!

Description

The user’s browser sends a request, through the network, to the ENC28J60 module. The request is analyzed by the sketch that is running on the Arduino, which also prepares the response. Both the ENC28J60 module and the SD card one are connected to the Arduino via SPI bus:

webserver-1

The webserver handles both static and dynamic resources:

  • the static resources (html pages, images…) are fetched from the SD card
  • the dynamic resources (AJAX requests…) are handled by the sketch itself

The sketch leverages the method described in my previous post to send static resources of any size.

Connections

Both the SD module and the ethernet one uses the SPI bus. That bus allows to connect more than one device at a time; thanks to a PIN (called chip-select) Arduino can decide which device will handle the command it is sending on the bus. You therefore need to connect the CS PINs of the different modules to different PINs of your Arduino and to configure the libraries accordingly.

Unfortunately I noticed that often the shields use the same PIN (for example PIN 10) as CS PIN or that – worse – they don’t allow to stack other SPI shields. I was forced to use two modules (instead of shields) and to connect them using wires:

webserver-2

The connections are:

  • power supply (5V and ground);
  • MOSI (Arduino->Device), PIN 11
  • MISO (Device->Arduino), PIN 12
  • CLK (clock), PIN 13
  • CS (chip select) ->for the SD module PIN 4, for the ENC28J60 modyule PIN 10

The two PINs I chose to be chip select PINs are configured at the top of the sketch:

#define SD_CS_PIN   4
#define ETH_CS_PIN  10

Static resources from the SD card

To read the SD card, I chose the tinyFAT library instead of the built-in one because it needs less RAM memory.

In the setup(), I configure the Arduino PIN connected to the chip select one of the SD module and initialize the library:

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

When a file is requested, at first the sketch verifies if the file exists on the SD card; if not a 404 error (= file not found) is sent back to the browser:

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

If the file exists, it is opened and read using blocks of 512 bytes. Each block is sent (one block => one packet) to the client:

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

A particular static resource is the default webpage: when you type an Internet address in the browser, the request it sends to the server is simply GET /. The server administrator can define which is the default page that is sent if the incoming request doesn’t include a specific one. In my sketch, the default webpage is defined as a constant:

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

Dynamic resources

In the sketch you can find an example of a dynamic resource: the uptime.ino page.

That page is not retrieved from the SD card but it is created by the sketch itself and its content is the output of the millis() function, which counts the number of milliseconds since the Arduino was turned on (and therefore the uptime of your 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());

Example

I prepared a full example: a website that displays an homepage where, thanks to an AJAX call triggered every 30 seconds, the uptime of the webserver is displayed:

sdweb

here are the AJAX calls performed by the browser:

uptime

The sketch and the resources for the example website are available on Github!

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

Leave A Response »

This website uses cookies to ensure you get the best experience on our website 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