sobota, 2 kwietnia 2011

Arduino + moduł Ethernet, czyli łączenie z Internetem


Autor: Piotr Rzeszut (Piotrva)
Redakcja: Dondu

Zobacz inne artykułu z cyklu: Arduiono ... czyli prościej się nie da :-)


Chyba jednym z ciekawszych modułów, który możemy podłączyć do Arduino jest moduł sieciowy - Ethernet Shield. Układ ten umożliwia nam po podłączeniu zestawu do routera za pomocą kabla sieciowego komunikację przez internet na różne sposoby.

Moim zdaniem najciekawsze możliwości to prezentacja danych i sterowanie poprzez protokół http, czyli przez przeglądarkę internetową, a także możliwość wymiany pakietów UDP z innymi urządzeniami.


Moduł ethernet WIZ5100


Na rynku dostępnych jest kilka rodzajów modułów sieciowych. Moim zdaniem najlepiej wybrać jest takie oparte o kontroler WIZ5100, gdyż wtedy możemy korzystać z "firmowych" bibliotek Arduino oraz wielu ciekawych przykładów dostarczanych wraz ze środowiskiem (np. pobieranie czasu z serwera internetowego).


Podłączenie

Zasadniczo właściwie nie musimy nic robić, poza nałożeniem modułu na Arduino, jednak projektanci obwodu uraczyli nas pewną osobliwością związaną z pinami analogowymi. Otóż moduł posiada slot na karty micro SD oraz możliwość przylutowania slotu na zwykłe karty SD, który obsługuje też pin detekcji włożenia karty i zabezpieczenia zapisu:

Moduł ethernet - slot na kartę SD


Z niewiadomych dla mnie przyczyn projektanci dodali też na tych liniach (podpiętych do pinów A0 i A1) rezystory podciągające, co może ograniczać przydatność tych wejść analogowych. Dlatego zachęcam do uwolnienia się od tej funkcji i przecięcia ścieżek odpowiedzialnych za podciąganie tych pinów do zasilania. Przeciąć należy ostrożnie ścieżki zaznaczone na poniższym obrazku kolorem czerwonym:

Moduł ethernet - na czerwono zaznaczone ścieżki, które należy przeciąć.


Nie ponoszę odpowiedzialności za wszelkie szkody wynikłe podczas wykonywania modyfikacji. Robisz to na własną odpowiedzialność.

Przed podłączeniem modułu wgraj najpierw odpowiednie oprogramowanie. W przeciwnym wypadku poprzednio używane oprogramowanie może uszkodzić moduł lub inne elementy sprzętu.


Ethernet a kwestie... Prawne!

Na początek warto wspomnieć jeszcze o jednej drobnej (wszak dotyczącej 6 bajtów), ale ważnej sprawie, jaką jest adres MAC naszego urządzenia. Przede wszystkim musimy zadbać, by taki adres nie powtarzał się w naszej sieci a ponadto aby zgodnie z prawem korzystać z danego adresu MAC (który w naszym urządzeniu jest przypisywany ręcznie) powinniśmy taki adres MAC wykupić lub skorzystać z odpowiedniej klasy adresów MAC.

Rzecz jasna dla nas, potrzebujących pojedynczego lub kilku adresów MAC wykupowanie całej puli w odpowiedniej organizacji byłoby skrajnie nieopłacalne i bezsensowne. Co więc zrobić w tej sytuacji?


Budowa adresu MAC (źródło: wikipedia.pl)

Po pierwsze możemy pokusić się o skorzystanie z grupy lokalnie zarządzanych adresów MAC, czyli takich które bit b2 w najbardziej znaczącym bajcie mają ustawony na 1 (jednocześnie do poprawnej pracy urządzenia bit b1 musi być ustawiony na 0). Wtedy adres taki należy do grupy lokalnie zarządzanych i możemy bez obaw z takiego adresu korzystać w naszej sieci.

Drugą opcją jest pozyskanie adresu MAC globalnie unikalnego (czyli takiego, który ma bit b2 w tym bajcie ustawiony na 0). Tu do wyboru mamy 2 opcje. Pierwsza polega na odnalezieniu starej, nieużywanej przez nas karty sieciowej i przepisanie z niej adresu MAC, zaś drugi elegancki sposób to kupienie pamięci z wpisanym adresem MAC.

Przykładowo firma Microchip oferuje pamięci 24AA025E48 z interfejsem I2C, które zawierają na końcu tablicy pamięci adres MAC, do korzystania z którego prawo otrzymujemy kupując daną pamięć - jest to więc najwygodniejszy sposób na pozyskanie adresu MAC, jeśli planujemy produkcję jakiegoś urządzenia na większą skalę - wystarczy wtedy kupić odpowiednią ilość pamięci i wlutować je do urządzenia, które w czasie działania przepisze adres MAC do odpowiedniego rejestru modułu sieciowego.


Adres IP

Kolejną kwestią jest wybór odpowiedniego adresu IP zgodnego z ustawieniami naszej sieci.
Nie wchodząc dalej w szczegóły (bo porady na ten temat na pewno znajdziecie na forach dotyczących konfiguracji sieci i podłączenia komputera ze statycznym adresem IP) musimy wybrać adres, który będzie zgodny z ustawieniem naszej sieci i który nie będzie wchodził w konflikt z innymi urządzeniami w sieci oraz najlepiej będzie spoza puli DHCP.

W moim przypadku konfiguracja sieci jest następująca:
Brama domyślna (i IP routera): 192.168.1.1
Maska podsieci: 255.255.255.0 (ostatni oktet przeznaczony jest na adres komputera w sieci, więc adresy będą miały postać 192.168.1.x, gdzie x=2-254)
Pula DHCP od adresu: 192.168.1.100 (zatem u mnie przez DHCP przydzielane mogą być adresy 192.168.1.100 do 192.168.1.254)
Dodatkowo adresy 192.168.1.2 do 192.168.1.40 są zajęte przez różne urządzenia (komputery, dyski sieciowe, drukarkę itp.), w związku z tym zdecydowałem się nadać swojemu modułowi adres 192.168.1.60





Co dalej? Czyli co możemy zrobić z naszym modułem sieciowym...

Teraz przyszła kolej na oprogramowanie, które możemy wykorzystać wraz z naszym modułem. Na pewno warto przyjrzeć się przykładom dostarczanym wraz z Arduino - ja postanowiłem przygotować dla Was 2 dodatkowe programy, które omówię osobno. W obu oprócz modułu sieciowego do Arduino podłączamy diodę LED na pinie 8 (oczywiście z odpowiednim rezystorem) oraz układ LM35DZ (zasilanie z 5V, wyjście podłączamy do pinu A0)


Arduino i UDP


Jednym z prostszych protokołów sieciowych jest protokół UDP. Jest to protokół bezpołączeniowy za pomocą którego możemy przesyłać np. proste pakiety.

To, ze protokół ten jest bezpołączeniowy oznacza, że pakiety wysyłamy na podobnej zasadzie jak zwykły list wrzucony do skrzynki - zasadniczo nie mamy pewności czy dotarł do adresata czy nie, jednakże w świecie informatyki pakiety takie są najszybszym sposobem transportu danych i są stosowane np. w telefonii VoIP.
Nasz program (załączony) jest niezwykle prosty - na początku (po inicjalizacji adresu IP i MAC) ustawiamy nasłuchiwanie na odpowiednim porcie (8888), następnie odbieramy pakiety UDP i sprawdzamy ich treść. Jeśli jest to znane polecenie (L1 - włącza diodę, L0 - wyłącza diodę, L? - sprawdza stan diody, T - wyświetla temperaturę) to wykonujemy zadaną czynność i wysyłamy odpowiedź do urządzenia, które wysłało nam pakiet.

Do wysyłania pakietów możemy wykorzystać praktycznie dowolny program do testowania UDP, wiele aplikacji dostępnych jest także na system Android - możemy tu nawet w niektórych programach (np. UDP Remote Control) przypisać konkretne pakiety do przycisków.

Ja dodatkowo popełniłem w środowisku LabVIEW prosty program umożliwiający testy UDP. Aby go uruchomić potrzebny jest LabVIEW Run-Time Engine w wersji 2010 lub nowszej. Możecie także przeanalizować strukturę aplikacji korzystając z wersji demonstracyjnych (bądź studenckich jeśli do takich uzyskacie dostęp) środowiska LabVIEW.

Można ciekawiej, czyli sterujemy przez stronę internetową.


Kolejny przykład opiera się już o protokół HTTP i TCP, które są już protokołami połączeniowymi. Na szczęście nie musimy analizować tych kwestii i jedyne co będziemy musieli poznać, to strukturę żądania HTTP i sposób generowania odpowiedzi.
Zawsze żądanie HTTP zaczyna się od znaków GET_ lub POST_ (_ symbolizuje spację) w pierwszej linijce. Następnie w tej samej linijce występuje znak / po którym zasadniczo przekazywane jest wszystko, co znajdzie się w pasku adresu przeglądarki. Linijka ta kończy się informacją o wersji protokołu HTTP.
Przykładowo może ona wyglądać tak:
GET / HTTP/1.1
lub jeśli chcemy wyświetlić stronę http://192.168.1.60/led.html
GET /led.html HTTP/1.1
Następnie kilka kolejnych linijek zawiera różne informacje, po czym występuje jedna pusta linijka kończąca zapytanie.

Widzimy zatem, że pierwszą metodą na przesyłanie informacji do naszego modułu ze strony internetowej jest wpisanie ich do pasku adresu. Jeśli kiedyś bawiliście się nieco w tworzenie stron internetowych to zapewne pamiętacie, że dane z formularzy możemy przesyłać automatycznie do paska adresu deklarując jako metodę przesyłania danych GET.

Dzięki temu np. możemy stworzyć w HTML formularz i jeśli wybierzemy z listy rozwijanej daną opcję to zostanie ona dodana do zapytania oraz zostaną tak przesłane inne pola tego formularza w wyniku czego procesor odbierze początek żądania np. tak (po nazwie wywoływanej strony znajduje się znak zapytania i dane):
GET /led.html?mode=on&haslo=piotrva HTTP/1.1
Widzimy jednak, że takie rozwiązanie nie zawsze jest dobre - przesłanie hasła tą metodą zakończy się za każdym razem wpisaniem go w pasku przeglądarki (więc ktoś może je od razu zobaczyć a co więcej jeśli nie skorzystamy z trybu prywatnego zostanie ono zapisane w historii przeglądarki).

Na szczęście hasła i inne dane możemy przesyłać podobnie jak na np. podczas logowania na forach itp. - metodą POST. Wtedy nasze zapytanie będzie miało np. taką strukturę:
POST /led.html HTTP/1.1
Host: 192.168.1.60
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:31.0) Gecko/20100101 Firefox/31.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: pl,en-US;q=0.7,en;q=0.3
Accept-Encoding: gzip, deflate
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
Content-Length: 21


haslo=piotrva&mode=on
Jak widzimy teraz dane przesyłane są na końcu samego zapytania, które zaczyna się literami POST. W ciągu przesyłane jest pole informacyjne Content-Length: xxx, które określa ilość znaków danych przesłanych metodą POST. Uwagę należy zwrócić na to, że dane te przesyłane są po pustej linii kończącej zapytanie oraz (czego tu nie widać) nie są zakończone tradycyjnie znakami CR LF, dlatego będziemy musieli do wykrycia ich zakończenia posłużyć się danymi o ilości przesłanych znaków z pola Content-Length.

Dalej niezależnie od typu zapytania dane przesyłane są w postaci:

nazwa_pola_formularza=wybrana/wpisana_wartość

Jeśli przesyłanych jest kilka pól to są one rozdzielone znakiem &.

Teraz pisząc odpowiedni program analizujący zapytania i przesłane dane możemy realizować sterowanie i monitorowanie. Oprogramowanie takie (opatrzone bogatymi komentarzami) załączam wraz z tym artykułem.

Do pobrania: przyklady (kopia)

Tak więc życzę wielu udanych projektów związanych z komunikacją Arduino z Internetem oraz jeszcze raz zachęcam do zapoznania się z gotowymi przykładami dotyczącymi modułu Ethernet dostarczonymi wraz z Arduino. :-)

Artykuł powstał dzięki wsparciu:



Przykład 1:
/*
  UDPSendReceive.pde:
 This sketch receives UDP message strings, prints them to the serial port
 and sends an "acknowledge" string back to the sender
 
 A Processing sketch is included at the end of file that can be used to send 
 and received messages for testing with a computer.
 
 created 21 Aug 2010
 by Michael Margolis
 modified 17 Aug 2014
 by Piotr Rzeszut
 
 This code is in the public domain.
 */


#include <SPI.h>         // needed for Arduino versions later than 0018
#include <Ethernet.h>
#include <EthernetUdp.h>         // UDP library from: bjoern@cs.stanford.edu 12/30/2008

const int ledpin=8;

//Adres mac i IP modułu
byte mac[] = {  
  0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
IPAddress ip(192, 168, 1, 60);


unsigned int localPort = 8888;      // port na którym moduł będzie oczekiwał pakietów

// bufor na dane
char packetBuffer[UDP_TX_PACKET_MAX_SIZE+1]; //buffer to hold incoming packet,

// utworzenie obiektu UDP
EthernetUDP Udp;

void setup() {
  // start the Ethernet and UDP:
  Ethernet.begin(mac,ip);
  Udp.begin(localPort);

  Serial.begin(9600);
  
  pinMode(ledpin,OUTPUT);
  
  analogReference(INTERNAL);//ustawmy odniesienie na 1,1V aby lepiej mierzyć temperaturę za pomocą LM35;
}

void loop() {
  // jeśli odebraliśmy pakiet to go odczytujemy (jeśli rozmiar jest większy od 0)
  int packetSize = Udp.parsePacket();
  if(packetSize)
  {
    Serial.print("Received packet of size ");
    Serial.println(packetSize);//wyświetlamy rozmiar pakietu
    Serial.print("From ");//adres IP nadawcy
    IPAddress remote = Udp.remoteIP();
    for (int i =0; i < 4; i++)
    {
      Serial.print(remote[i], DEC);
      if (i < 3)
      {
        Serial.print(".");
      }
    }
    Serial.print(", port ");//port, z którego nadano pakiet
    Serial.println(Udp.remotePort());

    // odczytujemy dane z pakietu
    Udp.read(packetBuffer,UDP_TX_PACKET_MAX_SIZE);
    Serial.println("Contents:");
    //kończymy je jak ciąg znaków znkiem NUL (0)
    packetBuffer[packetSize]=0;
    //wyświetlamy zawartość pakietu
    Serial.println(packetBuffer);
    
    //następnie sprawdzamy w prosty sposób jakie polecenie otrzymaliśmy (obsługujemy L1, L0, L? T)
    if(packetBuffer[0]=='L'){
      if(packetBuffer[1]=='1'){
        digitalWrite(ledpin,LOW);
        sprintf(packetBuffer,"LED ENABLED");
      }else if(packetBuffer[1]=='0'){
        digitalWrite(ledpin,HIGH);
        sprintf(packetBuffer,"LED DISABLED");
      }else if(packetBuffer[1]=='?'){
        sprintf(packetBuffer,digitalRead(ledpin)?"LED DISABLED":"LED ENABLED");
      }else sprintf(packetBuffer,"UNKNOWN COMMAND");
    }else if(packetBuffer[0]=='T'){
       //odczyt z LM35 i przeliczenie przy VREF=1,1V
      int analog=analogRead(A0);
      float temp=(float)analog*0.10742;
      //tu trochę nieelegancki sposób wyświetlania liczby float
      sprintf(packetBuffer,"Temp=%d.%02d",(int)temp,((int)(temp*100.0))%100);
    }else sprintf(packetBuffer,"UNKNOWN COMMAND");

    // wysyłamy odpowiedź na ten sam adres IP i port, z którego dostaliśmy pakiet
    Udp.beginPacket(Udp.remoteIP(), Udp.remotePort());
    Udp.write(packetBuffer);
    Udp.endPacket();
  }
  delay(10);
}





Przykład 2:
/*
  Web Server
 
 A simple web server, that allows to read temperature from LM35 sensor (/ or /index.html) and
 to controll led attached to pin 8 with password-secured page (/led.html)
 
 Circuit:
 * Ethernet shield attached to pins 10, 11, 12, 13
 * Analog input A0 - LM32DZ
 
 created 18 Dec 2009
 by David A. Mellis
 modified 9 Apr 2012
 by Tom Igoe
 modified 15 Aug 2014
 by Piotr Rzeszut
 
 */

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

//Adres MAC - przeczytaj uwagi w artykule
byte mac[] = { 
  0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
//Adres IP - musi być zgodny z ustawieniami sieci i nie być w konflikcie z innymi urządzeniami
IPAddress ip(192,168,1,60);

//tu definiujemy hasło dostępu do sterowania diodą LED
#define PASS "piotr"

const int ledpin=8;//pin do sterowania ledzikiem;

//ustawiamy serwer na port 80 (domyślny dla http) - możemy użyć innego portu, np. w celu przekierowania ruchu z WAN przez router
EthernetServer server(80);


void setup() {
 // Open serial communications and wait for port to open:
  Serial.begin(9600);
   while (!Serial) {
    ; // wait for serial port to connect. Needed for Leonardo only
  }


  // start the Ethernet connection and the server:
  Ethernet.begin(mac, ip);
  server.begin();
  Serial.print("server is at ");
  Serial.println(Ethernet.localIP());
  
  analogReference(INTERNAL);//ustawmy odniesienie na 1,1V aby lepiej mierzyć temperaturę za pomocą LM35;
  
  pinMode(ledpin,OUTPUT);//pin 8 do sterowania diodą LED;
}

//definujemy bufory i zmienne konieczne do odbioru danych
char line_buffer[200];
char page[21];
byte buf_i=0, data_length=0;

//oraz zmienne wyliczeniowe pamiętające typ zapytania
enum request_type_t {UNKNOWN=0,GET,POST,POST_DATA_WAIT,POST_DATA};
enum request_type_t request_type=UNKNOWN;

//ta funkcja pomoże nam odnaleźć określone pole (field_name) w buforze z danymi POST (line_buffer) i zapisze je (pierwsze 20 znaków) w podanej tablicy znakowej (value) 
//zwraca liczbę zapisanych bajtów, 0 jeśli nie znajdzie tego pola
byte value_find(const char field_name[], char line_buffer[], char value[]){
  byte found=0;//liczba skopiownaych bajtów
  for(byte i=0;i<200&&line_buffer[i]!=0;i++){//przeszukamy cały bufor odbiorczy do końca lub znaku zakończenia danych w nim (0)
    if(memcmp(&line_buffer[i],field_name,strlen(field_name))==0&&line_buffer[i+strlen(field_name)]=='='){//porównujemy nazwę pola z kolejnymi podciągami bufora oraz sprawdzamy istnienie znaku równości (tylko to daje nam całe pole)
      do{//..jeśli trafiliśmy to kopiujemy pole do zakończenia bufora lub trafienia na znak & (wskazujący na kolejne pole) lub do limitu 20 znaków
        value[found]=line_buffer[i+strlen(field_name)+1+found];
        if(value[found]==0||value[found]=='&'){
          value[found]=0;
          found++;
          break;
        }
        found++;
      }while(found<20);
      value[20]=0;//ewentualnie 21 znak ustawiamy na 0 (konieć łańcucha znaków
      break;//przerywamy pętlę wyszukującą
    }
  }
  return found;//zwracamy liczbę skopiowanych bajtów (łącznie ze znakiem 0)
}

void loop() {
  // czekamy na połączenie klienta
  EthernetClient client = server.available();
  if (client) {//jeśli nastąpiło
    Serial.println("new client");//wysyłamy informację do komputera po RS232
    request_type=UNKNOWN;//resetujemy zmienne dot. typu zapytania i wywoływanej strony
    page[0]=0;
    while (client.connected()) {//dopóki klient jest połączony
      if (client.available()) {//odczytuyejmy znaki jak z Serial'a
        char c = client.read();
        //Serial.print(c);//tu do celów debuggowania mozemy wyświetlac znak po znaku to co wysyła nam kleint
        if(c=='\r'){//jeśli to znak powrotu karetki to zaraz będzie koniec linii więc kończymy ciąg znaków w buforze
          line_buffer[buf_i++]=0;
        }else if(c=='\n'||(request_type==POST_DATA&&buf_i==(data_length-1))){//tu albo kończymy znakiem \n albo musimy liczyć odebrane bajty w przypadku danych POST
          if(request_type==POST_DATA){//kończymy zapis ostatniego znaku w przypadku odbioru danych POST
            line_buffer[buf_i++]=c;
            line_buffer[buf_i++]=0;
          }
          //tu sprawdzamy co to za linijka
          //Serial.println(line_buffer);//do celów debufgowania mozemy ją wyświetlić
          if(memcmp(line_buffer,"GET ",4)==0){//zapytanie GET
            request_type=GET;
            byte j=0;//za każdym razem skopiujemy informacje o tym, co wpisano w pasku adresu
            for(byte i=4; i<4+20;i++){
              if(line_buffer[i]==' '){
                page[j]=0;
                break;
              }
              page[j++]=line_buffer[i];
            }
            //Serial.println("GET detected");
          }else if(memcmp(line_buffer,"POST ",5)==0){//zapytanie POST
            request_type=POST;
            byte j=0;
            for(byte i=5; i<5+20;i++){
              if(line_buffer[i]==' '){
                page[j]=0;
                break;
              }
              page[j++]=line_buffer[i];
            }
            //Serial.println("POST detected");
          }else if(request_type==POST&&memcmp(line_buffer,"Content-Length: ",16)==0){//w zapytaniu POST znajdziemy określoną ilość danych do odberania
            request_type=POST_DATA_WAIT;
            data_length=atoi(&line_buffer[16]);
            buf_i=0;
            continue;            
          }
          if(buf_i==1&&request_type==POST_DATA_WAIT){//jeśli mamy odebrać dane POST to musimy zignorować pustą linijkę po całym zapytaniu 
          //(pusta linijka jest wtedy, gdy licznik danych bufora jest równy jeden - odebraliśmy przed znakiem \n tylko znak \r zastąpiony znakiem NUL)
            request_type=POST_DATA;
            buf_i=0;
            continue;//wtedy od razu kontynuujemy od ponownego wykonania pętli
          }
          if((buf_i==1&&request_type==GET)||request_type==POST_DATA){//albo pusta linijka po zapytaniu GET albo koniec danych POST sygnalizuje konieczność wysłania odpowiedzi do kleinta
          
              if(strcmp(page,"/")==0||strcmp(page,"/index.html")==0){//jeśli to strona główna to wyświetlamy informacje o temperatuze
                client.println("HTTP/1.1 200 OK");
                client.println("Content-Type: text/html");
                client.println("Connection: close");  // połączenie zamkniemy po wysłaniu odpowiedzi
                client.println();
                client.println("<!DOCTYPE HTML>");
                client.println("<html>");
                client.println("<h1>Welcome!<br/>");
                client.println("Temperature is: ");
                
                //odczyt z LM35 i przeliczenie przy VREF=1,1V
                int analog=analogRead(A0);
                float temp=(float)analog*0.10742;
                
                client.println(temp);
                client.println("°C</h1>");
                client.println("</html>");
                break;
              }else if(strcmp(page,"/led.html")==0){//jeśli ktoś chce zobaczyć stronę led to się trochę pogimnastykujemy
                
                //zakładamy, że nie ma prawidłowego hasła
                boolean valid_pass=0;
                
                client.println("HTTP/1.1 200 OK");
                client.println("Content-Type: text/html");
                client.println("Connection: close");  // the connection will be closed after completion of the response
                client.println();
                client.println("<!DOCTYPE HTML>");
                client.println("<html><form method=\"POST\">");
                //tu wyświetlam początek pola na hasło
                client.print("Password: <input type=\"password\" name=\"pass\" value=\"");
                //jeśli zapytanie jest typu POST
                if(request_type>GET){
                  char data[21];//to szukam pola z hasłem i jeśli je znajdę to sprawdzam hasło
                  if(value_find("pass",line_buffer,data)){
                    if(strcmp(data,PASS)==0){
                      valid_pass=1;
                      client.print(data);//które też wpiszemy do tego pola na hasło (niby niebezpieczne bo ktoś je może wyciągnąć z uruchomionej strony, ale z kolei nie musimy za każdym razem go wpisywać)
                      //możemy powyższą linijkę usunąć, żeby zrezygnować z tej opcji
                    }
                  }
                }
                //koniec pola na hasło
                client.println("\"><br/>");
                //jeśli hasło prawidłowe to wyświetlam informacje o stanie diody
                if(valid_pass==1){
                  char data[21];
                  //a wcześniej szukam pola odpowiedzialnego za sterowanie ledem i jeśli znajdę to zapalam lub gaszę diodkę
                  if(value_find("led",line_buffer,data)){
                    if(data[0]=='1'){
                      digitalWrite(ledpin, LOW);
                    }else if(data[0]=='0'){
                      digitalWrite(ledpin, HIGH);
                    }
                  }
                  client.print("LED IS: ");
                  if(digitalRead(ledpin)==HIGH){
                    client.println("DISABLED");
                  }else{
                    client.println("ENABLED");
                  }
                  //i pole z możłiwością zmiany jej stanu
                  client.println("<select size=\"1\" name=\"led\"><option value=\"1\">ENABLE</option><option value=\"0\">DISABLE</option></select>");
                }
                //na koniec zawsze przycisk do wysyłania formularza
                client.println("<br/><input type=\"submit\" value=\"SEND\"/>");
                client.println("</form></html>");
                break;
              }
              //jeśli doszliśmy tu, to znaczy, że ktoś szukał innej strony, której nie ma - więc informacja 404
              client.println("HTTP/1.1 404 Not Found");
              client.println("Content-Type: text/html");
              client.println("Connection: close");  // the connection will be closed after completion of the response
              client.println();
              client.println("<!DOCTYPE HTML>");
              client.println("<html><center><h1>");
              client.print("Requested page: ");
              client.print(page);
              client.println(" is not available</h1></center><hr/>");
              client.println("</html>");
              break;
          }
          //dla pewności resetujemy wskaźnik bufora
          buf_i=0;
        }else{
          //tu uzupełniamy bufor danymi, jeśli to każdy inny znak niż \r i \n
          line_buffer[buf_i++]=c;
          if(buf_i>198)buf_i=198;//dbamy by linia z danymi nie była za długa (wyciek pamięci) i by nie zmieniło tego dodanie znaku 0;
        }
      }
    }
    //odrobina czasu na odebranie danych
    delay(1);
    //zamykamy połączenie z klientam
    client.stop();
    //wyświetlamy informacje
    Serial.println("client disonnected");
    //dla pewności resetujemy wskaźnik bufora
    buf_i=0;
  }
}



Zobacz inne artykułu z cyklu: Arduiono ... czyli prościej się nie da :-)


3 komentarze:

  1. Witam, widzę że bardzo dobrze znasz się na rzeczy i chciałbym się dowiedzieć czy byłaby możliwość żebyś opisał co musiałbym zrobić i posiadać żeby połączyć się z komputerem na nim napisać stronę która byłaby guzikiem po wciśnięciu którego za pośrednictwem sieci LAN na innym komputerze wyświetliłoby się coś np inna strona? Bardzo mi na tym zależy ;)

    OdpowiedzUsuń
  2. Mogę prosić o , to samo tylko na samodzielnie działającym ESP8266 ?. :)

    OdpowiedzUsuń
  3. Reszta kodu jest bez zmian... . Pozdrawiam :)

    OdpowiedzUsuń