Podłączamy internet do Arduino niskim kosztem

Aby podłączyć Arduino do internetu wcale nie musimy wydawać fortuny. Co więcej – w dzisiejszym artykule pokażę Wam sposób jak zrobić to tanio i w 100% kompatybilnie ze szkicami przeznaczonymi dla płytek wyposażonych w oryginalny Ethernet Shield.

Co będzie potrzebne?

W zasadzie potrzebujemy tylko płytki opartej o kontroler ENC28J60 taki jak np. ten lub jego mniejsza wersja. To tyle. Tanio prawda? Zabierzmy się więc teraz do podłączenia tego modułu.

Schemat połączeń

Aby podłączyć ENC28J60 do Arduino nie potrzebujemy nawet breadborda. Jedyne co nam będzie potrzebne to kabelki połączeniowe żeńsko-męskie. Układ składamy tak jak na poniższym schemacie:

Należy tutaj bezwzględnie pamiętać, żeby zasilanie opisane na płytce jako VCC podłączyć do pinu 3.3V Arduino. W przeciwnym wypadku spalimy układ. Reszta pinów komunikacyjnych toleruje 5V, dlatego też magistralę SPI możemy podłączyć bez żadnych pośredniczących układów takich jak np. konwerter poziomów napięć.

Biblioteka do Arduino

Kiedy wszystko jest podłączone możemy pobrać odpowiednią bibliotekę, która pozwoli nam na komunikację z naszym nowym nabytkiem. W tym celu w Arduino IDE wchodzimy w SzkicDołącz bibliotekę > Zarządzaj bibliotekami. Otworzy nam się okienko menedżera bibliotek, w którym możemy wpisać frazę ENC28J60. Wyświetli nam się kilka propozycji, jednak moim zdaniem najlepszym wyborem będzie ta o nazwie UIPEthernet. Poza obsługą naszego modułu zapewnia ona też identyczne nazwy klas i metod co w oryginalnej bibliotece przeznaczonej dla Arduino Ethernet Shield. Oznacza to, że uruchomimy za jej pomocą dowolny szkic przeznaczony dla tego shielda podmieniając jedynie plik nagłówkowy z Ethernet.h na UIPEthernet.h.

Przykładowy program

Aby udowodnić, że wystarczy podmienić plik nagłówkowy załadujmy szkic z Przykłady > Ethernet > WebServer. Następnie przejdźmy do linijki 21 i zmieńmy

#include <Ethernet.h>

na

#include <UIPEthernet.h>

Teraz możemy z powodzeniem wgrać program na płytkę po czym wejść na adres 192.168.1.177 w naszej przeglądarce (jeżeli nasz LAN ma inną podsieć to musimy zmienić ten adres w szkicu) i naszym oczom ukaże się lista kanałów analogowych z ich aktualnymi wartościami automatycznie odświeżana co 5 sekund.

My jednak dodatkowo zmodyfikujemy nieco ten program oraz omówimy jego nową wersję krok po kroku. Nasza nowa wersja będzie wypisywała stan wszystkich wyjść w formacie HTML lub JSON.

Początek szkicu praktycznie w niczym nie różni się od oryginału:

#include <SPI.h>
#include <UIPEthernet.h>

byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
IPAddress ip(192, 168, 1, 75);
EthernetServer server(80);

Załączamy biblioteki, wybieramy adres MAC (tak, możemy sami sobie wybrać jaki MAC będzie miała nasza karta sieciowa :)), wybieramy domyślny adres IP i deklarujemy zmienną serwera webowego na porcie 80.

Teraz czas na trochę ustawień:

void setup() {
  Serial.begin(115200);

  if(!Ethernet.begin(mac)) {
    Serial.println("Nie udalo sie pobrac adresu IP z DHCP, ponowna proba z domyslnym adresem statycznym.");
    Ethernet.begin(mac, ip);
  }
  
  server.begin();
  Serial.print("Uruchomiono serwer pod adresem ");
  Serial.println(Ethernet.localIP());
}

Jak widać tutaj zmieniło się odrobinę. W oryginalnym szkicu od razu ustawiamy adres IP na sztywno. W naszej modyfikacji Arduino najpierw spróbuje uzyskać adres IP z DHCP, a jeżeli to się nie uda to uruchamia kartę sieciową na domyślnym wybranym przez nas wcześniej adresie. Następnie uruchamiany jest serwer i na port szeregowy wypisywana jest informacja o tym na jakim adresie jest on dostępny.

Główna pętla programu zmieniła się w stopniu minimalnym. Zamiast linijek client.print mamy wywołanie odpowiedniej funkcji (outputHTML lub outputJSON w zależności od tego jak chcemy przekazać wynik).

void loop() {
  EthernetClient client = server.available();
  if (client) {
    boolean currentLineIsBlank = true;
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
        Serial.write(c);
        if (c == '\n' && currentLineIsBlank) {
          outputHTML(client);
          break;
        }
        if (c == '\n') {
          currentLineIsBlank = true;
        } else if (c != '\r') {
          currentLineIsBlank = false;
        }
      }
    }
    delay(1);
    client.stop();
  }
}

Zobaczmy więc jak wyglądają funkcje outputHTML oraz outputJSON:

void outputHTML(EthernetClient &client) {
  client.println("HTTP/1.1 200 OK");
  client.println("Content-Type: text/html");
  client.println("Connection: close");
  client.println("Refresh: 5");
  client.println();
  client.println("<!DOCTYPE HTML>");
  client.println("<html>");

  client.println("<h1>Stany pinow Arduino</h1>");

  for (int digitalPin = 0; digitalPin <= 13; digitalPin++) {
    client.print("<p>");
    client.print("Pin ");
    client.print(digitalPin);
    client.print(" = ");
    client.print(digitalRead(digitalPin));
    client.println("</p>");
  }
  
  for (int analogChannel = A0; analogChannel <= A5; analogChannel++) {
    client.print("<p>");
    client.print("Pin A");
    client.print(analogChannel - A0);
    client.print(" = ");
    client.print(analogRead(analogChannel));
    client.println("</p>");
  }
  
  client.println("</html>");
}

void outputJSON(EthernetClient &client) {
  client.println("HTTP/1.1 200 OK");
  client.println("Content-Type: text/json");
  client.println("Connection: close");
  client.println();
  client.print("{");

  client.print("\"Digital\": [");
  for (int digitalPin = 0; digitalPin <= 13; digitalPin++) {
    client.print(digitalRead(digitalPin));
    if(digitalPin != 13) {
      client.print(",");
    }
  }
  client.print("], \"Analog\": [");
  for (int analogChannel = A0; analogChannel <= A5; analogChannel++) {
    client.print(analogRead(analogChannel));
    if(analogChannel != A5) {
      client.print(",");
    }
  }
  client.println("]}");
}

W obu jako parametr przyjmujemy referencję do obiektu klienta z którym „rozmawiamy”, a następnie wykonujemy na nim operacje wypisywania danych. W zależności którą funkcję wykonamy w takim formacie klient dostanie odpowiedź.

Po skompilowaniu programu na porcie szeregowym podglądamy jaki adres IP został przydzielony do naszego Arduino:

Jeżeli użyliśmy funkcji outputHTML, to wynik naszego działania zobaczymy ładnie sformatowany w przeglądarce internetowej:

Jeżeli postawiliśmy na format JSON to ładnie sparsowaną odpowiedź możmey podejrzeć np. programem Postman: