Tworzenie rozbudowanych sieci komputerowych już od dobrych kilkunastu lat przestało służyć jedynie łączeniu komputerów.
Spadek cen oraz wzrost mocy obliczeniowej małych mikrokontrolerów, rozpoczął gwałtowny proces przyłączania do lokalnych sieci Ethenetowych czy nawet globalnej sieci Internetowej, niskomocowych urządzeń, pełniących głównie funkcje kontrolne, sterujące i pomiarowe. Co więcej, rozwiązania te zaczęły pojawiać się także w profesjonalnych sieciach przemysłowych, stopniowo wypierające starsze systemy oparte o RS232 i pochodne. Tym samym z początkiem XXI wieku rozpoczęta została era tzw. Internetu Rzeczy (ang. Internet of Things – IoT). Choć obecny rynek IoT zdominowany jest przez urządzenia komunikujące się głównie za pomocą sieci bezprzewodowych i standardów WiFi, ZigBee, BLE czy Z-Wave, wciąż w wielu rozwiązaniach sprzętowych (głównie z tzw. segmentu IIoT – Industrial Internet of Things), wymagających niezawodności transmisji i bezpieczeństwa danych, jednym z popularniejszych rozwiązań nadal jest sieć Ethernet. Twórcy platformy Arduino nie pozostawili bez odpowiedzi zapotrzebowania zgłaszanego ze strony konstruktorów urządzeń IIoT i standardową ofertę modułów Arduino poszerzyli o nakładki typu Ethernet Shield 2, kierowana dla użytkowników indywidualnych, czy Arduino MKR ETH SHIELD dla rozwiązań profesjonalnych, bazujące na kontrolerach WIZnet W5100/W5200/W5500 i integrujące układy MAC oraz PHY w jednym układzie scalonym. Oferta ta została dość szybko rozbudowana przez niezależnych producentów o kolejne i znacznie tańsze moduły bazujące na popularnych układach ENC28J60. W niniejszym artykule dokonano krótkiej charakterystyki obydwu rozwiązań: oficjalnego, bazującego na układach z serii W5x00, oraz rozwijanych głównie przez społeczność Open Source/Open Hardware rozwiązań bazujących na modułach ENC28J60.
Komunikacja z wykorzystaniem modułów WIZnet W5x00 oraz biblioteki Arduino Ethernet
Niewątpliwą zaletą oficjalnych modułów bazujących na układach serii W5x00 (w tym również ich sprzętowych odpowiedników, np. nakładek OKYSTAR OKY2102 czy DFROBOT DFR0125) jest zapewnienie pełnego wsparcia programowego w postaci wbudowanej w stos Arduino biblioteki Ethernet. Tym samym użytkownik może rozpocząć tworzenie programu tuż po uruchomieniu Arduino IDE, bez potrzeby instalacji dodatkowych pakietów oprogramowania.
Rysunek 1. Moduły OKY2102 (z lewej) oraz DFR0125 (z prawej), wyposażone w kontroler WIZnet W5100
W zależności od wariantu układu WIZnet oraz ilości dostępnej pamięci RAM, biblioteka Ethernet wspiera maksymalnie cztery (dla układu W5100 oraz pamięci RAM <= 2 kB) lub osiem (układy W5200 oraz W5500) równoległych połączeń wychodzących/przychodzących. Interfejs programowy biblioteki został podzielony na pięć klas, grupujących poszczególne funkcjonalności. Za inicjalizację biblioteki oraz konfigurację ustawień sieciowych (w tym adresu IP, adresu podsieci czy ustawień bramy dostępowej) odpowiedzialna jest klasa Ethernet. Dla potrzeb adresacji IP utworzono klasę IPAddress. Do uruchomienia po stronie Arduino prostej aplikacji serwera, niezbędne będzie wykorzystanie klasy EthernetServer, umożliwiającej zapis i odczyt danych z wszystkich podłączonych urządzeń. Komplementarną klasę stanowi klasa EthernetClient, umożliwiająca w kilku prostych wywołaniach przygotowanie funkcjonalnego klienta sieciowego, realizującego operacje zapisu i odczytu danych z serwera. Na potrzeby komunikacji UDP, biblioteka Ethernet udostępnia klasę EthernetUDP. Pełny opis klas wraz z metodami, został udostępniony pod adresem:
Przejdź na stronę Arduino
W charakterystyczny dla platformy Arduino sposób, wszystkie złożone operacje programowe zostały zaimplementowane bezpośrednio w dostarczanej bibliotece – programista otrzymuje do dyspozycji ograniczony, ale bardzo funkcjonalny zestaw API, dzięki czemu proces tworzenia aplikacji jest szybki i nie wymaga szczegółowej wiedzy z zakresu stosów sieciowych. Przeanalizujmy zatem budowę najprostszej aplikacji serwerowej, dostarczanej wraz z biblioteką Ethernet, której zadaniem jest nasłuch na połączenia przychodzące od klienta protokołu Telnet.
Kod aplikacji serwera rozpoczyna dodanie plików nagłówkowych niezbędnych do nawiązania komunikacji SPI (moduły WIZnet wymieniają dane z mikrokontrolerem z wykorzystaniem tego protokołu) oraz plików nagłówkowych biblioteki Ethernet:
#include <SPI.h>
#include <Ethernet.h>
Następnym krokiem jest konfiguracja parametrów sieciowych (adresu MAC kontrolera, adresu IP bramy dostępowej i maski podsieci) oraz utworzenie serwera nasłuchującego na porcie numer 23 (domyślnym porcie dla protokołu Telnet):
byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
IPAddress ip(192,168,1, 177);
IPAddress gateway(192,168,1, 1);
IPAddress subnet(255, 255, 0, 0);
EthernetServer server(23);
W ciele funkcji setup() niezbędne jest przeprowadzenie inicjalizacji biblioteki Ethernet oraz rozpoczęcie procesu nasłuchiwania. Dodatkowo umieszczona została również konfiguracja portu szeregowego, na którym zostaną wyświetlone komunikaty o adresie serwera, podłączeniu nowego klienta oraz danych otrzymanych w trakcie ustanowionej sesji:
void setup() {
Ethernet.begin(mac, ip, gateway, subnet);
server.begin();
Serial.begin(9600);
while (!Serial) {
}
Serial.print("Chat server address:");
Serial.println(Ethernet.localIP());
}
Główna pętla programu loop() oczekuje na połączenie ze strony klienta oraz sprawdza dostępność danych do odczytu. W przypadku otrzymania danych, odsyła je w niezmienionej postaci do klienta, realizując tym samym prostą funkcję echo:
void loop() {
EthernetClient client = server.available();
if (client) {
if (!alreadyConnected) {
client.flush();
Serial.println("We have a new client");
client.println("Hello, client!");
alreadyConnected = true;
}
if (client.available() > 0) {
char thisChar = client.read();
server.write(thisChar);
Serial.write(thisChar);
}
}
}
Poprawność działania powyższej aplikacji może zostać przetestowana z wykorzystaniem dowolnego klienta protokołu Telnet (np. program Putty w systemie Windows lub polecenie telnet w systemie Linux) lub z wykorzystaniem kolejnego zestawu Arduino i klasy EthernetClient.
Komunikacja z wykorzystaniem modułów ENC28J60 oraz zewnętrznych bibliotek
Alternatywnym rozwiązaniem dla oficjalnie wspieranych układów WIZnet W5x00 są moduły bazujące na kontrolerze ENC28J60 (np. OKYSTAR OKY3486 lub ETH CLICK). Dzięki niższej cenie oraz łatwiejszej w montażu ręcznym obudowie (w odróżnieniu od układów W5x00 zawartych w 80-pinowych obudowach LQFP, kontroler ENC28J60 jest dostępny w 28-pinowych obudowach typu SSOP, SOIC, QFN oraz przeznaczonej do montażu przewlekanego obudowie SPDIP), układ ten cieszy się dużą popularnością wśród elektroników hobbystów.
Rysunek 2. Moduły OKY3486 (z lewej) oraz ETH CLICK (z prawej) wyposażone w kontroler ENC28J60
Pomimo braku oficjalnego wsparcia ze strony Arduino do dyspozycji programistów zostało oddanych wiele bibliotek typu open source, zapewniających szybką integrację układów ENC28J60 z oprogramowaniem. Szczególną uwagę należy poświęcić bibliotece UIPEthernet oraz udostępnionej na licencji GPLv2, bibliotece EtherCard. Niewątpliwą zaletą pierwszego z wymienionych projektów jest kompatybilność interfejsu API z oficjalną biblioteką Arduino Ethernet, co pozwala uniezależnić proces tworzenia aplikacji od wyborów dokonanych pomiędzy układami W5x00 a układem ENC28J60 w warstwie sprzętowej. Drugi z projektów – EtherCard – implementuje niezależny interfejs programistyczny, który w zależności od preferencji programisty, może się okazać ciekawą alternatywą. Podobnie jak w przypadku biblioteki Arduino Ethernet, implementacja dość złożonej funkcjonalności (np. implementacja klienta DHCP) może zostać zrealizowana w kilku liniach kodu:
#include <EtherCard.h>
static byte mymac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
byte Ethernet::buffer[700];
void setup () {
Serial.begin(57600);
Serial.println(F("
[testDHCP]"));
if (ether.begin(sizeof Ethernet::buffer, mymac, SS) == 0)
Serial.println(F("Failed to access Ethernet controller"));
Serial.println(F("Setting up DHCP"));
if (!ether.dhcpSetup())
Serial.println(F("DHCP failed"));
ether.printIp("My IP: ", ether.myip);
ether.printIp("Netmask: ", ether.netmask);
ether.printIp("GW IP: ", ether.gwip);
ether.printIp("DNS IP: ", ether.dnsip);
}
void loop () {
ether.packetLoop(ether.packetReceive());
}
https://www.tme.eu/pl/news/library-articles/page/43654/Arduino-komunikacja-z-wykorzystaniem-sieci-Ethernet/