enc28J60 e Arduino (14)

luca 22/04/2013 48

In questo tutorial vedremo come realizzare un piccolo sito web che riceve dati da Arduino (tramite il nostro ethernet shield), li memorizza e li visualizza su un grafico.

Schema logico

Il nostro sito web sarà formato da due pagine:

  • la prima, saveTemp.php, riceverà i dati da Arduino e li memorizzerà in un file di testo;
  • la seconda, displayTemp.php, leggerà il file di testo e visualizzerà i dati memorizzati.

Nell’esempio, leggerò con Arduino la temperatura dell’ambiente tramite il sensore DS18B20 di cui vi ho già parlato in questo articolo.

Per realizzare le pagine, ho scelto di utilizzare il linguaggio PHP, molto diffuso in ambito web.

Per semplicità, i dati saranno salvati in un file CSV (comma-separated values); in applicazioni reali conviene invece utilizzare un database, ad esempio MySQL

Invio dati

Per inviare i dati al server Web, ho scelto di utilizzare il metodo GET del protocollo HTTP. Tale metodo invia i parametri in chiaro all’interno della URL, con la forma nome=valore e separandoli con il simbolo &:

Per evitare che altri possano inviare dati, oltre al parametro temp, è necessario inviare alla pagina anche una password tramite il parametro pwd.

Lo sketch Arduino legge la temperatura dal sensore, la converte in formato stringa e la “stampa” in un oggetto Stash:

sensors.requestTemperatures();
float float_temp = sensors.getTempCByIndex(0);
 
char string_temp[7];
dtostrf(float_temp, 4, 2, string_temp);
 
Stash stash;
byte sd = stash.create();
stash.print(string_temp);
stash.save();

Infine prepara la richiesta in un altro oggetto Stash e la invia con il metodo tcpSend():

Stash::prepare(PSTR("GET /demo/saveTemp.php?temp=$H&pwd=$F HTTP/1.0" "\r\n"
    "Host: $F" "\r\n" "\r\n"),
    sd, password, website);
session_id = ether.tcpSend();

Grazie all’identificativo di sessione (session_id) è possibile ottenere in seguito la risposta ottenuta dal server e verificare se contiene un messaggio di errore (“KO”):

const char* reply = ether.tcpReply(session_id);
[...]
if(strstr(reply, "KO - ") != 0) Serial.println(strstr(reply, "KO - "));

In PHP è possibile accedere ai dati tramite l’array associativo $_GET:

$temp = $_GET["temp"];
$pwd = $_GET["pwd"];

Verificata la password, il valore di temperatura ricevuto viene salvato nel file insieme ad un timestamp:

define("LOG_FILE", "./temperatures.csv");
[...]
$file_handler = fopen(LOG_FILE, "a+");
fwrite($file_handler, time() . "," . $temp . "\n");

Nella prossima pagina vedremo come visualizzare i dati in un grafico…

Pagine: 1 2

48 Comments »

  1. tim mckenna 26/04/2013 at 17:47 - Reply

    I’ve just gone through your 14 tutorials and I’m happy to be using the $4.00 enc shields instead of the $20 wiznets. I think you can also get the enc in DIP too.

    You are a great teacher, nice pace and movement from principles to greater complexity.

    Next for me to figure out is a system using an apikey instead of putting the password in the URL

    I’d also like to figure out how to push a notice from the avr to a browser/smartphone.

  2. igor 03/05/2013 at 15:19 - Reply

    Con tcpReply() e tcpSend() sarebbe possibile dialogare con un server smtp per inviare email ?

    • luca 03/05/2013 at 20:46 - Reply

      ciao, teoricamente sì… sto preparando un piccolo tutorial perché è un poco più complesso rispetto ad una singola chiamata web

  3. Matteo 09/05/2013 at 15:11 - Reply

    Ciao Luca. Ho provato questo esempio ma trovo degli errori. Mi spiego meglio:
    1) ho caricato i file php e la cartella JpGraph nella root del mio sito;
    2) nello skecth ho inserito il mio sito web e la password ftp;
    il file csv lo crea da solo?
    Questo è quello che mi spunta nel serial monitor:
    WebTemperature demo

    Ethernet controller initialized
    DHCP configuration done
    Website IP resolved

    IP Address: 192.168.1.58
    Netmask: 255.255.255.0
    Gateway: 192.168.1.1
    Website IP: 217.—————

    Temperature -127.00U sent to website… OK
    Temperature -127.00U sent to website… OK
    Temperature -127.00U sent to website… OK
    Temperature -127.00U sent to website… OK
    Temperature -127.00U sent to website… OK
    Temperature -127.00U sent to website… OK

    dove sta l’errore?

    • luca 13/05/2013 at 09:37 - Reply

      ciao Matteo, il csv lo crea da solo… sembra che lo sketch non riesca a leggere la temperatura dal sensore: i collegamenti sono ok? Prova lo sketch demo della libreria e vedi se ti da qualche informazione in più.

  4. Petr 12/06/2013 at 21:37 - Reply

    Hello,
    great tutorial. I have question about sending variable directly from arduino to web.

    If i want use this:
    analog = analogRead(A0);

    bfill.emit_p(PSTR(analog));

    i get:
    In function ‘void loop()’:
    error: initializer fails to determine size of ‘__c’

    • luca 17/06/2013 at 21:20 - Reply

      Hi Petr,

      in the PSTR you have to enter a “placeholder” for the variables you add as parameters of emit_p, take a look at this example.

  5. Suresh 18/06/2013 at 14:17 - Reply

    who to use this for socket programming please advice !!

    • luca 21/06/2013 at 18:41 - Reply

      Suresh, could you please explain better your question?
      thanks

  6. mande 06/07/2013 at 21:19 - Reply

    Ciao complimenti per i tutorial li ho seguiti tutti e sarei interessato a quello sulle e-mail. La serie di tutorial è finita o ne arriveranno altri? Grazie

    • luca 08/07/2013 at 08:37 - Reply

      ciao! sono stato un po’ impegnato sul lavoro ma sicuramente arriveranno altri tutorials ;)
      grazie per i complimenti!

  7. Peter Murray 08/07/2013 at 03:41 - Reply

    Do you know how I can send live data from the arduino/ENC28J60? I have a CANBus connection working alongside the ENC shield and I would like to forward CAN packets to a computer via the internet. I was thinking of using UDP but I can’t find any examples on UDP with the ENC shield. any tips?

    • luca 08/07/2013 at 08:38 - Reply

      Hi Peter,

      did you read my example about sending actual temperature to a C# application?

  8. David Vaughn 01/08/2013 at 15:46 - Reply

    Luca,

    Great tutorials, thank you. I’m curious, when I try to access the webpage “xxx.com/displayTemp.php”, all it gives me is the code. Am I doing something wrong? Do I need to set something up first?

    • luca 14/08/2013 at 13:59 - Reply

      Hi David

      your webserver must be able to “run” PHP code… so if you’re running your own server, install PHP following the guides on PHP website.

  9. jtreumer 03/08/2013 at 14:30 - Reply

    Hello Luca,

    your ENC28J60 tutorial was very helpful to me in understanding the chip and its usage in real hardware. Moreover, the example listings are well documentated and can be easily adapted onto different tasks. Thank you for providing such valuable infos!

    Best regards, Jürgen

    • luca 06/08/2013 at 14:47 - Reply

      Jürgen, thanks for your comment!

  10. armando 19/08/2013 at 00:30 - Reply

    Thank you very much for this tutorial, I am finally able to send some data to the web :D Just as a comment, I think there is an error in “session_id = ether.tcpSend();”.. don’t you need to add the data type as in “byte session_id = ether.tcpSend();” ? Thanks!

    • luca 22/08/2013 at 08:35 - Reply

      Hi Armando,

      if you analyze the full sketch on Github, you’ll notice that session_id is defined on the top:
      static byte session_id;
      because it must be visible also in the checkResponse() subrutine.

  11. David Vaughn 10/09/2013 at 15:58 - Reply

    Luca,

    Again great tutorials thank you. I’ve the PHP server running correctly (I forgot to install PHP) (DUH). I’m curious if you might be able to do a tutorial on how to bring a couple of your tutorials together like this one and the WebRelay? I was also wondering if PHP allows you to change set points or if I’d have to use HTML for that (still new to both of these languages).

    • luca 11/09/2013 at 13:22 - Reply

      Hi David

      thanks! about set points, the JPGraph library is in PHP so with that language you have full control of the chart’s properties.
      About your other request, are you thinking of a “dashboard” that displays data AND lets the user give commands (for example “close that relay”) to Arduino?

      • David Vaughn 16/09/2013 at 19:35 - Reply

        Luca,

        Yes that basically what I was thinking. The dsahboard could have the graph at the top and buttons at the bottom or somethin like that.

  12. Rob 02/10/2013 at 20:05 - Reply

    Ciao Luca, ho cominciato a seguire da poco i tuoi tutorials e li trovo fantastici.
    Mi chiedevo: in che modo si potrebbe ottenere lo stesso risultato con un POST Request? Il GET raggiunge il risultato, ma vorrei evitare di mostrare variabili e valori nell’url. Grazie.

    • luca 03/10/2013 at 19:08 - Reply

      Ciao Rob

      sto per pubblicare un articolo che usa proprio il metodo POST…

  13. igor 13/11/2013 at 17:14 - Reply

    ciao luca, naturalmente ottimo lavoro ;)
    avrei un problema, io devo inviare i dati ad un server locale, al posto del sito web ho inserito l’indirizzo ip del server, ma arduino non riesce a risolvere l’ip. sbaglio metodo o c’è qualcosa che ho saltato?
    Saluti, igor

    • luca 13/11/2013 at 20:07 - Reply

      Ciao Igor

      se hai già l’indirizzo IP, non devi usare il metodo dnsLookup. Semplicemente definisci l’IP del server (es static byte serverIp[] = {192,168,1,10};) e copia tale indirizzo nella variabile hisip: ether.copyIp(serverIp, hisip);

  14. Francisco 26/11/2013 at 04:07 - Reply

    Good Morning Friend
    I’m tetando example web_client
    but if I try to write my website in order to use a variable at run time I can not

    website char [] PROGMEM = “www.google.com”;

    works well, but
    so
    website char [] = “www.google.com”;

    does not work, can you help me?

    this is my code

    #include

    // ethernet interface mac address, must be unique on the LAN
    static byte mymac[] = { 0×74,0×69,0×69,0x2D,0×30,0×31 };

    byte Ethernet::buffer[700];
    static uint32_t timer;

    //char website[] PROGMEM = “www.vallesolucoes.com”;
    char website[] = “www.vallesolucoes.com”;
    // called when the client request is complete
    static void my_callback (byte status, word off, word len) {
    Serial.println(“>>>”);
    Ethernet::buffer[off+300] = 0;
    Serial.print((const char*) Ethernet::buffer + off);
    Serial.println(“…”);
    }

    void setup () {
    Serial.begin(57600);
    Serial.println(“\n[webClient]“);

    if (ether.begin(sizeof Ethernet::buffer, mymac,14) == 0)
    Serial.println( “Failed to access Ethernet controller”);
    if (!ether.dhcpSetup())
    Serial.println(“DHCP failed”);

    ether.printIp(“IP: “, ether.myip);
    ether.printIp(“GW: “, ether.gwip);
    ether.printIp(“DNS: “, ether.dnsip);

    if (!ether.dnsLookup(website))
    Serial.println(“DNS failed”);

    ether.printIp(“SRV: “, ether.hisip);
    }

    void loop () {
    ether.packetLoop(ether.packetReceive());

    if (millis() > timer) {
    timer = millis() + 5000;
    Serial.println();
    Serial.print(“<<< REQ ");
    ether.browseUrl(PSTR("/photos/envia/"), "envia.php", website, my_callback);
    }
    }

    • luca 27/11/2013 at 14:27 - Reply

      Hi

      why do you need to store the URL in the RAM memory? The browseUrl function expects a website string stored in the PROGMEM… to change it you have to change the library.

  15. Michael 07/12/2013 at 14:18 - Reply

    Hello Luca. I have problem to connect with my WebSpace – Error Message: Unable to resolve Website IP
    char website[] PROGMEM = “http://www.solarstation.lima-city.de”;
    I try also with …
    char website[] PROGMEM = “solarstation.lima-city.de”;
    do you have any idea?
    Thanks a lot for all this tutorials. Michael

    • luca 07/12/2013 at 18:32 - Reply

      Hi Michael,

      try “www.solarstation.lima-city.de” it should work. Are you sure your Arduino gets the correct DNS server? Are you using a DHCP setup or a static one?

      • Michael 10/12/2013 at 22:06 - Reply

        Hello Luca. I’m using DHCP and i think the Server IP Address is resolved correctly. Maybe the reason is the free Webspace of lima-city. That’s what the terminal says…

        WebTemperature demo

        Ethernet controller initialized
        DHCP configuration done
        Website IP resolved

        IP Address: 192.168.2.139
        Netmask: 255.255.255.0
        Gateway: 192.___._._
        Website IP: 212.83.45.137

        Temperature 23.00 sent to website… OK

        should i try it with a Static IP Address?

        • luca 16/12/2013 at 14:16 - Reply

          Hi Michael,

          from the output it seems everything’s working now… doesn’t it?

  16. Ozy 15/12/2013 at 14:39 - Reply

    Hello Luca. I have a project that looks similar with your tutorial here, but I’m using HC-SR04 distance sensor instead of the temperature sensor. I already programmed the sensor but I really have no idea how to send the data to the web just like your tutorial. Can you give me a more detailed step-by-step explanation? I will be very happy if you would like to see my sketch for the HC-SR04 and show me how to integrate it with the ethernet module so it can send the data to the web. Thank you very much

  17. Antonio 03/01/2014 at 08:41 - Reply

    Prima di tutto un sincero ringraziamento per gli ottimi tutorial pubblicati, mi sono stati molto d’aiuto nella realizzazione di un piccolo sito web.
    Stavolta ho difficoltà a gestire la variabile stash in particolare vorrei implementare 4 sonde termiche, ho modificato il programma ed ora ho tre variabili temp numerate da 1 a 4 che restituiscono il loro corretto valore, però non riesco a modificare l’oggetto stash in modo che la variabile “sd” contenga i valori di tutte e 4 le sonde, mi potresti aiutare?

    • luca 14/01/2014 at 19:42 - Reply

      ciao Antonio,

      nella prepare() devi inserire per ogni valore un “segnalibro” con $H (qualcosa tipo saveTemp.php?temp1=$H&temp2=$H&temp3=$H…); quindi chiusa la stringa puoi inserire separate da virgola le varie variabili. Per riceverle e gestirle poi hai modificato di conseguenza lo script php?

  18. Kim Jessen 30/01/2014 at 15:15 - Reply

    Hello Luca!
    I’m fiddling with something similar to your project.
    I just have big problems with the GET function.
    it is not quite the same as your but may be you can help me anyway.

    Base: so I also collects data packs them into a string and then I send it to a server.

    like this:

    Serial.println(“connected”);
    client.println(“GET /test_fil_1.php?name=string”);
    client.println(“HOST:www.XXXXXXX.com”);
    client.println();

    in file test_fil_1.php is written

    but it does not work.

  19. Kim Jessen 30/01/2014 at 15:17 - Reply

    in file test_fil_1.php is written

  20. Kim Jessen 30/01/2014 at 15:18 - Reply


    $temp = $_GET["name"];

    $filename = ‘temperatures.csv’;

    define(“LOG_FILE”, “./temperatures.csv”);

    $file_handler = fopen(LOG_FILE, “a+”);

    fwrite($file_handler, time() . “,” . $temp . “\n”);

  21. kim jessen 02/02/2014 at 11:59 - Reply

    hey it does not matter. I’ve got it to work.
    I do like this
    ” Serial.println(“connected”); // report successful connection
    // Make a HTTP request:

    client.println(“GET /test_fil.php?key=125 HTTP/1.1″);
    client.println(“Host: http://www.moencamping.com“);
    client.println(“Connection: close”);
    client.println();

    it is made for W5100 shield.
    However, I would like to do something similar with ENC28J shield.
    But I can not really get started in it.

    I looked at several client examples to ENC28J, but it’s not really the same code.
    I know it is a different chip and the setup is different but a GET request is probably a GET request. Right!

    The only question is, where to put the GET rotine.
    hope that you will help me

  22. kim jessen 03/02/2014 at 16:25 - Reply

    ‘ve fixed it.
    I use UIPEthernet library.
    So it’s easy.

  23. Massimoi 27/02/2014 at 10:40 - Reply

    Luca,
    ottimo lavoro!!!
    Io sto provando a realizzare la stessa cosa (circa) però usando la libreria Ethernet.h (quella standard per capirci). Ora quello che non riesco a capire bene è la sintassi che devo usare nel comando con GET. Praticamente io eseguo una cosa del genere …

    sprintf(buffer, “%s%s%s/%s/%d/%d/%d/%d/%d/%d/%d/%d/%d/%d/%d/%d/%d/%d/%d/%d %s”, “GET http://xxxx.org:84“/saveTemp.php?temp=15.2&pwd=password”, “HTTP/1.1″);

    Serial.println(buffer);
    client.println(buffer);

    … ma senza risultato.
    Cosa faccio come errore?

    • luca 02/03/2014 at 13:39 - Reply

      Ciao Massimo,

      usi la sprintf() immagino per creare la URL da chiamare, con alcune parti “statiche” (http://xxx.org…) e alcune “dinamiche” (i valori dei sensori)… non capisco tutti quei %s%d…

  24. Massimo 03/03/2014 at 11:53 - Reply

    Luca grazie innanzitutto!
    E’ come avevo previsto a quanto scrivi. Ho provato a lanciare come dici tu ma senza risultato. Quei %s%d … sono riferiti alla formattazione della stringa. Ora ci lavoro e ti tengo aggiornato.

    Grazie ancora

  25. Kafes 28/05/2014 at 19:42 - Reply

    Extraordinary tutorials Luca! You are a great teacher and these are among the best tutorials I have ever read for microcontrollers! Thank you for sharing this…

    • luca 28/05/2014 at 20:27 - Reply

      Thanks Kafes ;)

  26. kim jessen 26/06/2014 at 19:17 - Reply

    hey if I need to send to a particular port, for example:
    xxxxxxxx.yy: 8004
    how do i do that

  27. marco 29/08/2014 at 06:35 - Reply

    ciao, grazie per il blog davvero molto utile.
    non riesco a far comunicare arduino (con ip fisso) con il server. Ambientmonitor, sempre con Arduino con ip fisso, funziona senza problem: saveTemp.php funziona se chiamato dal pc, mentre con Arduino continuo ad avere “No response”.
    C’è modo di leggere l’url trasmesso da arduino?
    Graxie

    • luca 01/09/2014 at 20:14 - Reply

      Ciao Marco,

      puoi installare Wireshark sul tuo PC, con quello riesci a “sniffare” la chiamata di Arduino…

Leave A Response »