Układy ESP8266 można wykorzystać jako proste serwery WWW. Przykładową konfigurację opisałem w  artykułach: ESP8266 jako serwer WWW oraz ESP8266 jako serwer WWW cz.2. Jest to idealne rozwiązanie w sytuacji, gdy chcemy mieć dostęp do urządzenia z poziomu sieci lokalnej. Układ podłączony do domowego routera jest dostępny z poziomu przeglądarki wewnątrz sieci. Problem pojawia się w momencie, gdy chcielibyśmy mieć dostęp do naszego sterownika z sieci Internet. Osoby posiadające stały zewnętrzny adres IP mogą odpowiednio skonfigurować router tak, aby z Internetu również można było łączyć się z naszym sterownikiem. Większość użytkowników łącz internetowych nie posiada jednak wykupionej usługi stałego zewnętrznego adresu IP. W takim wypadku musimy wykorzystać inny sposób na udostępnienie naszych danych w sieci Internet

Skoro Internet nie może "dostać się" do naszego urządzenia, to nasze urządzenie musi dostać się do Internetu. Układ ESP8266 może być klientem dowolnej strony internetowej. Jeżeli zapewnimy mu dostęp do sieci Internet będziemy mogli udostępniać wyniki pomiarów na serwerze zewnętrznym.

Krok 1 - łączymy ESP8266 z siecią Internet

Wbrew pozorom podłączenie do sieci jest bardzo proste do wykonania. To co, musimy skonfigurować to dostęp do naszej domowej sieci WIFI.

#include <ESP8266WiFi.h>
const char* ssid     = "nazwa_sieci";
const char* password = "klucz";

void setup() {
  Serial.begin(115200);  
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("Połączono z siecią");  
  Serial.println("Mój address IP: ");
  Serial.println(WiFi.localIP());
}  

Podłączenie do sieci sprowadza się do podania identyfikatora sieci (SSID) i hasła. WiFi.begin() rozpoczyna proces połączenia z siecią. Pętla while czeka na prawidłowe połączenie. Dopiero po otrzymaniu potwierdzenia prawidłowego połączenia można przejść do wykonywania dalszej części programu. Polecenia Serial.print w powyższym przykładzie służą wyłącznie do kontroli prawidłowego wykonania podłączenia. Po wykonaniu powyższego kodu układ ESP8266 dysponuje połączeniem z siecią Internet (zakładając, że sieć domowa jest podłączona do Internetu).

Krok 2 - połączenie z serwerem

Po podłączeniu układu do sieci Internet można przejść do właściwego połączenia z serwerem HTTP. Sposób komunikacji z wykorzystaniem protokołu HTTP sprowadza się w dużym uproszczeniu do wysłania zapytania do serwera i otrzymaniu odpowiedzi. Każda transmisja to nowe zapytanie i nowa odpowiedź (Dociekliwym proponuję dokładniejsze zapoznanie się z protokołem artykuł na stronie Wikipedii). Do obsługi połączenia z serwerem wykorzystamy bibliotekę ESP8266HTTPClient.h, która całą operację sprowadzi do kilku prostych kroków. Uzupełnimy nasz kod o dodatkowe polecenia:

#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
const char* ssid = "nazwa_sieci"; 
const char* password = "klucz"; 
void setup() { 
  Serial.begin(115200); 
  WiFi.begin(ssid, password); 
  while (WiFi.status() != WL_CONNECTED) { 
    delay(500); 
    Serial.print("."); 
  }
  Serial.println(""); 
  Serial.println("Połączono z siecią"); 
  Serial.println("Mój address IP: "); 
  Serial.println(WiFi.localIP());
}
void loop(){
  HTTPClient http;
  http.begin("http://testy.plociennik.info");

  http.end();
}

W pierwszym kroku musimy załączyć bibliotekę ESP8266HTTPClient.h, reszta obsługi znajduje się w funkcji loop(). Na początku tworzymy obiekt http, który będzie odpowiedzialny za połączenie z serwerem. Za pomocą metody begin() łączymy się z serwerem i przekazujemy ewentualne parametry. W powyższym przykładzie łączymy się z serwerem o adresie "testy.plociennik.info". W efekcie serwer zwróci zawartość strony lub kod błędu.  Metoda end() kończy połączenie z serwerem.

W prosty sposób możemy przekazać dane do serwera z wykorzystaniem metody GET(czyli doklejając parametry za nazwą serwera).

http.begin("http://testy.plociennik.info?nazwa=ESP1&login=wojtek");

W przypadku, gdy chcemy wpisywać wartości parametrów przechowywane w zmiennych możemy łączyć je razem z tekstem przekazywanym do serwera:

String nazwa,login;
nazwa="ESP";
login="wojtek";
http.begin("http://testy.plociennik.info?nazwa="+nazwa+"&login="+login);

Ze względów bezpieczeństwa coraz więcej serwerów pracuje z wykorzystaniem protokołu HTTPS. Połączenie z takim serwerem za pomocą powyższej metody będzie niemożliwe. Jest na to jednak proste rozwiązanie. http.begin() umożliwia wykonanie połączenia z serwerem HTTPS. Wystarczy podać tylko do porównania odcisk SHA1. Nawiązanie połączenia z serwerem HTTPS pokazano poniżej:

http.begin("https://plociennik.info",
"36:C8:FD:3C:A0:10:4E:83:A3:79:12:78:FB:C2:9D:6B:4B:23:84:5C");

W porównaniu do pierwszego połączenia zmianie uległa nazwa protokołu na początku (z HTTP na HTTPS), dodano również drugi parametr, który jest tzw. odciskiem SHA1 wczytywanej strony. Ten ciąg cyfr służy do porównania, czy serwer z którym się łączymy to rzeczywiście ten, za który się podaje. Normalna przeglądarka internetowa załatwia procedurę sprawdzania certyfikatu automatycznie. W przypadku ESP8266 sami musimy zadbać o pobranie powyższego odcisku z serwera. Co więcej w momencie, gdy na serwerze zmieni się certyfikat będziemy musieli również zmienić powyższe wartości w kodzie naszego programu.Poniżej krótka obrazkowa instrukcja pokazująca jak odczytać potrzebne dane z serwera.

Klikamy na zielonej kłódce, wybieramy połączenie, następnie więcej informacji, klikamy na "wyświetl certyfikat" i kopiujemy z dołu okienka odcisk SHA1. Niezależnie od wybranej metody połączenia z serwerem otrzymamy wynik naszej operacji. Najbardziej oczekiwanym wynikiem będzie zawartość strony, z którą się łączymy. Możemy jednak otrzymać kod błędu.

Krok 3 - obsługa otrzymanych wyników

Pierwszą czynnością po otrzymaniu odpowiedzi z serwera powinno być sprawdzenie kodu operacji. Jest to bardzo ważne dla prawidłowego funkcjonowania dalszej części programu. W sytuacji, gdy pobieramy z serwera dane, będziemy je próbowali przetworzyć. Nie ma sensu wykonywanie fragmentu programu, który interpretuje otrzymane wyniki w sytuacji, gdy nie udało się połączyć z serwerem, lub serwer nie przesłał prawidłowej odpowiedzi. Do sprawdzania kodu odpowiedzi użyjemy polecenia:

int httpCode = http.GET();

Po wykonaniu powyższego kodu w zmiennej httpCode będziemy przechowywać kod operacji, który umożliwi nam wykonanie odpowiedniej akcji. W tabeli poniżej znajdują się typowe kody HTTP.

Kod Komunikat
200 HTTP_CODE_OK
401 HTTP_CODE_UNAUTHORIZED
404 HTTP_CODE_NOT_FOUND

Jak widać najbardziej pożądanym kodem jest kod 200, który informuje nas, że połączenie zostało zaakceptowane, a serwer zwrócił prawidłowe dane. Po otrzymaniu kodu 200 możemy przejść do odczytania konkretnych danych z serwera. Dane zapiszemy do zmiennej typu tekstowego w celu ich dalszego przetworzenia.

int httpCode = http.GET();
if(httpCode == HTTP_CODE_OK) {
    String strona = http.getString();
    Serial.println(strona);
}

Dysponując danymi w zmiennej tekstowej możemy je wykorzystać w dowolny sposób. W tym miejscu warto zauważyć, że połączenie naszego układu z serwerem HTTP ma sens, gdy serwer ten przesyła konkretne dane przetwarzane przez sterownik, lub, gdy sterownik przekazuje do serwera wyniki swoich pomiarów. Zawsze musi wystąpić para: sterownik z oprogramowaniem oraz serwer HTTP obsługujący transmisję ze sterownikiem. Wczytanie zawartości strony np. plociennik.info poza pokazaniem, że można na nic się nie przyda.... W związku z powyższym musimy posiadać dostęp do serwera na którym umieścimy odpowiednio spreparowaną stronę współpracującą z naszym sterownikiem. Możemy również skorzystać z gotowych ogólnodostępnych serwisów, które umożliwiają przechowywanie danych w chmurze.

Bardzo często serwisy internetowe udostępniają dane w postaci pliku XML lub JSON. Są to formaty umożliwiające przekazywanie danych w prosty do implementacji sposób. Arduino posiada bibliotekę, która obsługuje format JSON. O formace można dokładniej przeczytać na stronie wikipedii (i wielu innych). W dużym uproszczeniu w formacie JSON dane przesyłane są w postaci pary klucz:wartość. Poniżej przykładowa prosta zawartość pliku JSON

{"Rok":2019,"Miesiac":"Styczeń","Dzień":11}

 Na stronie testy.plociennik.info/jsonWWW.php przygtowałem prosty skrypt, który generuje aktualną datę jako trzy niezależne elementy: rok, miesiąc i dzień. Poniżej zrzut ekranu pokazujący wynik działania skryptu w przeglądarce.

Dysponując danymi w formacie JSON uzupełnimy nasz program o możliwość przetworzenia danych w naszym układzie. Poniższy kod pokazuje sposób wykorzystania biblioteki Arduino.

#include <ArduinoJson.h>
DynamicJsonBuffer jsonBuffer(200);
...
JsonObject& root = jsonBuffer.parseObject(strona);
 int rok=root["rok"];
 String miesiac=root["miesiac"];
 int dzien=root["dzien"];
 Serial.println(rok);
 Serial.println(miesiac);
 Serial.println(dzien);

 Pierwsze dwie linie umieszczamy na początku programu, to załączenie biblioteki obsługującej format JSON oraz utworzenie bufora na dane o rozmiarze 200 bajtów. W kolejnym kroku parsujemy dane odczytane z serwera w postaci tekstu na tablicę asocjacyjną. Od tego momentu mamy dostęp do danych na podstawie ich nazwy. W przykładowym kodzie dane te przypisujemy do zmiennych odpowiedniego typu (rok i dzień liczba całkowita, miesiąc tekst). Następnie przesyłamy dane za pomocą Serial.print. Możemy bezpośrednio korzystać z danych w tablicy asocjacyjnej bez potrzeby podstawiania ich pod odpowiednie zmienne. Rozwiązanie powyższe jest prostsze w interpretacji. Na koniec cały działający kod programu łączącego się z serwerem i pobierającego z niego dane. 

#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#include <ArduinoJson.h>

DynamicJsonBuffer jsonBuffer(200);

String ap="Nazwa_sieci_WIFI";
String pass="Hasło_do_WIFI";

void setup() {
  Serial.begin(115200);
  WiFi.begin(ap,pass);
  while (WiFi.status() != WL_CONNECTED) {
      delay(500);
      Serial.print(".");
   }
  Serial.println("");
  Serial.println("WiFi: połączono z AP: "+ap);  
  Serial.println("Adres IP: ");
  Serial.println(WiFi.localIP());
}

void loop() {
      HTTPClient http;
      http.begin("http://testy.plociennik.info/jsonWWW.php");
      int httpCode = http.GET();
      if(httpCode > 0) {
            Serial.printf("[HTTP] Odczytano kod: %d\n", httpCode);
            if(httpCode == HTTP_CODE_OK) {
                String strona = http.getString();
                JsonObject& root = jsonBuffer.parseObject(strona);
                int rok=root["rok"];
                String miesiac=root["miesiac"];
                int dzien=root["dzien"];
                Serial.println(rok);
                Serial.println(miesiac);
                Serial.println(dzien);
            }
        }
     http.end();
     delay(5000);  //5 sekund do następnego połączenia
}

Podsumowanie

Połączenie układu ESP8266 do sieci Internet i wykorzystanie serwerów HTTP/HTTPS jako magazynu danych jest bardzo proste. Wystarczy kilka instrukcji, żeby obsłużyć podstawowe połączenie. Problemem może okazać się bezpieczeństwo transmisji i sposób uwierzytelniania urządzeń. Wszędzie tam, gdzie jest to możliwe proponuję łączyć układ za pomocą protokołu HTTPS. Szyfrowana transmisja utrudnia odgadnięcie sposobu autoryzacji układów.