Close

TinyLab: WiFi modul ESP8266

TinyLab s ESP8266 wifi modulem

Tento článek vychází z poslední kapitoly knihy Průvodce světem Arduina. Ta popisuje ovládání modulu ESP8266 pomocí desky Arduino Mega. My se dnes podíváme, jak ESP8266 používat s deskou TinyLab. Jen pozor. Dnes bude článek pro trochu pokročilejší čtenáře. Pokud si nejste programováním zatím jistí, nechte ho na později 🙂


Už je to pár let, co byl představen WiFi modul ESP8266. Jeho původní určení bylo rozšíření možností procesorů o WiFi komunikaci. Takových modulů je ale spousta. Zajímavé na tomto modulu je to, že je možné jej přímo programovat. Díky tomu, a také jeho nízké ceně, se stal mezi bastlíři a kutily velice oblíbený.

Když nám přijde tento modul od výrobce, měl by v sobě mít nahraný program, se kterým si dnes vystačíme. Tento program poslouchá po sériové lince speciální příkazy – takzvané AT příkazy. Ty umožňují základní operace s modulem. Dnes se tedy nebudeme zabývat přímo programováním ESP8266 (o tom zase jindy), ale ukážeme si, jak se dá modul ovládat pomocí AT příkazů.

Rodina modulů ESP8266

Rodina modulů ESP8266

ESP8266

Můžeme se setkat s více verzemi modulů ESP8266 různých tvarů a velikostí, na všech však nalezneme čip ESP8266EX. Jednotlivé verze se od sebe liší například tím, jestli je elektronika na desce stíněná či nikoliv, nebo také počtem pinů. My použijeme asi nejrozšířenější verzi ESP8266-1. Ta nám, stejně jako ostatní modely, nabídne připojení WiFi 802.11 b/g/n, integrovaný TCP/IP stack (obsluha síťové komunikace) a mnohé další.
Tento modul vezmeme a zapojíme do konektoru označeného ESP8266 v horní pravé části desky TinyLab.

Ovládání modulu

Jak už jsem zmínil dříve, ovládání modulu probíhá pomocí AT příkazů. Přehled dostupných příkazů naleznete zde, popřípadě i s vysvětlením zde. My si ty důležitější představíme dále. Na začátek musíme ověřit funkčnost modulu. ESP8266 komunikuje po sériové lince a je propojen s Arduinem na TinyLabu. Z desek, jako je Arduino UNO, jsme zvyklí pouze na jednu sériovou linku. Ale protože máme na TinyLab desku Arduino Leonardo, máme k dispozici dvě sériové linky – Serial, která komunikuje s PC, a Serial1, která má fyzicky vyvedené RX a TX piny na digitálních pinech 0 a 1. Serial1 také využijeme pro komunikaci s modulem.

Nejjednodušším příkladem AT příkazu je příkaz „AT“ doprovázený znaky CR (cariage return – pozůstatek z doby psacích strojů a jehličkových tiskáren, symbolizuje návrat tiskací hlavy na začátek řádku) a LF (line feed – nový řádek). V programu ale nemůžeme napsat „CR“, popřípadě „LF“, protože by je program považoval za řetězec se dvěma znaky. Existuje vícero způsobů, jak tyto znaky napsat – jedním z nich jsou takzvané escape sekvence (CR = „\r“, LF = „\n“), druhým například přímé použití jejich číselné hodnoty z ASCII tabulky (CR = 13, LF = 10). Další možností je použít funkci Serial.println(„…“), která znaky CR a LF odešle za nás na konci řetězce v uvozovkách. Ne vždy se nám ale toto chování hodí.

//ekvivalentní jsou tedy zápisy:
Serial.print("AT\r\n");

Serial.print("AT");
Serial.print("\r");
Serial.print("\n");

Serial.println("AT");

Serial.print("AT");
Serial.write(13);
Serial.write(10);

Pro účely zkoušení si napíšeme program, který bude pouze přeposílat znaky mezi Serial a Serial1 (tedy mezi PC a modulem). Poté budeme pomocí monitoru sériové linky posílat modulu AT příkazy, abychom si je vyzkoušeli.

void setup(){
    Serial.begin(9600);
    Serial1.begin(115200); //9600, 57600, 115200
}

void loop(){
    while(Serial.available()){
        Serial1.write(Serial.read());    
    }

    while(Serial1.available()){
        Serial.write(Serial1.read());    
    }
}

Poznámka: Modul ESP8266 může komunikovat na různých rychlostech, které se liší podle verze programu (firmware), který je v nich nahraný. Moduly dodávané s TinyLab by měly komunikovat na rychlosti 115200. Pokud ale modul koupíte samostatně, může se stát, že nebude komunikovat. V tom případě zkuste zvolit jinou z uvedených rychlostí. 

Než zkusíme první příkaz, musíme sériovému monitoru říci, že chceme za každým odeslaným řetězcem odeslat i znaky CR a LF. Toto je možné nastavit hned vedle výběru rychlosti komunikace.

Nastavení monitoru sériové linky v Arduino IDE

Nastavení monitoru sériové linky

Nyní už máme vše nastavené. Do textového pole monitoru zadáme příkaz AT a odešleme. Modul by měl odpovědět:

AT

OK

Tím máme jistotu, že modul funguje. To nám ale určitě stačit nebude. Budeme po modulu například chtít, aby se připojil k serveru a něco z něj stáhl. Předtím si ale musíme vysvětlit, na co jsou které AT příkazy.

AT příkazy pro ESP8266

Abychom se v příkazech neztratili, zavedeme si do nich trochu systém. Prvním způsobem dělení je rozdělení podle jejich funkčnosti a také způsobu zápisu:

TYP Příklad Popis
Dokumentační AT+PRIKAZ=? Vrátí rozsah hodnot, které je možné předat v parametru příkazu
Informační AT+PRIKAZ? Vrátí aktuálně nastavenou hodnotu
Nastavovací AT+PRIKAZ=parametr Nastaví hodnotu podle zadaného parametru
Vykonávací AT+PRIKAZ Provede zadaný příkaz

Druhým způsobem dělení, který také dále budeme používat, je rozdělení do tří kategorií. První kategorii bychom mohli nazvat Základní. Nalezneme v ní ty příkazy, které se starají o samotný běh modulu. Další dvě kategorie můžeme nazvat WiFi příkazy (připojení k wifi…) a TCPIP příkazy (komunikace po síti).

Základní příkazy

Příkaz Odpověď Popis
AT  AT OK Jednoduchý příkaz, který slouží pouze k tomu, abychom zjistili, jestli modul funguje.
AT+RST  AT+RST OK Restartuje modul a vrátí informace (liší se podle verze firmware)
AT+GMR  AT+GMR verze Vrátí aktuální verzi firmware
AT+GSLP  AT+GSLP=cas čas Uvede modul do stavu spánku na zadaný čas (v milisekundách)
ATE  ATE0 OK Zakáže opakování zadaného příkazu modulem
 ATE1 OK Povolí opakování zadaného příkazu modulem

WiFi příkazy

Příkaz Očekávaná odpověď Popis
AT+CWMODE AT+CWMODE=? +CWMODE:(1-3) Vrátí rozsah možných hodnot módu
AT+CWMODE? +CWMODE: mod Vrátí číslo aktuálního módu
AT+CWMODE=mod OK Nastaví mód

  • 1 – klient
  • 2 – přístupový bod
  • 3 – klient + přístupový bod
AT+CWJAP AT+CWJAP? +CWJAP: ssid Vypíše SSID bodu, ke kterému je modul aktuálně připojen.
AT+CWJAP=ssid,heslo OK Připojí se k WiFi se zadaným jménem a heslem.
AT+CWLAP AT+CWLAP +CWLAP:(zab,ssid,sila,mac) Vrátí seznam viditelných wifi sítí.
Zab udává typ zabezpečení:

  • 0 = otevřená síť
  • 1 = WEP
  • 2 = WPA_PSK
  • 3 = WPA2_PSK
  • 4 = WPA_WPA2_PSK

Ssid značí jméno wifi, sila sílu signálu a mac adresu přístupového bodu.

AT+CWLAP=ssid,mac,kan +CWLAP:(zab,ssid,sila,mac) Zjistí, jestli existuje přístupový bod se zadanou ssid, mac a kanálem.
AT+CWQAP AT+CWQAP OK Odpojí se od aktuálně připojeného přístupového bodu.
AT+CWSAP AT+CWSAP=ssid,heslo,kan,zab OK Vytvoří přístupový bod (SoftAP), ke kterému je možné se připojit pomocí zadaných údajů. Zabezpečení se řídí stejnými pravidly jako u AT+CWLAP.
AT+CWSAP? +CWSAP:ssid,heslo,kan,zab Vrátí aktuální nastavení přístupového bodu.
AT+CWLIF AT+CWLIF ip, mac Vrátí informace o připojených zařízeních.

TCPIP příkazy

ESP8266 podporuje i vícenásobná připojení. Aktuální mód připojení se nastavuje pomocí AT+CIPMUX. Od jeho nastavení se odvíjí i použití některých příkazů z této skupiny.

Příkaz Odpověď Popis
AT+CIPSTATUS AT+CIPSTATUS  Status:cislo Vrátí aktuální status modulu

  • 2 = má přidělenou IP
  • 3 = připojen
  • 4 = nepřipojen
AT+CIPSTART AT+CIPSTART=typ,adr,port OK Pro jednoduché připojení. Připojí se jako klient na zadanou adresu a port. Typ připojení může být TCP, nebo UDP.
AT+CIPSTART=id,typ,adr,port OK Pro vícenásobné připojení. Připojí se jako  klient. Stejné jako předchozí, jen musíme specifikovat id připojení (může být mezi 0 a 4).
AT+CIPSTART=? +CIPSTART:(id)(„type“),(„ip address“),(port)
+CIPSTART:((id)“type“),(„domain name“),(port)
Vrátí možné parametry příkazu a jejich zápis.
AT+CIPSEND AT+CIPSEND=delka SEND OK Jednoduché připojení. Oznámí, že budou odeslána data. Délka značí, kolik znaků bude odesláno (maximálně 2048 znaků najednou).
AT+CIPSEND=id,delka SEND OK Vícenásobné připojení. Id značí id požadovaného připojení.
AT+CIPCLOSE AT+CIPCLOSE OK Jednoduché připojení. Ukončí aktuální připojení.
AT+CIPCLOSE=id OK Vícenásobné připojení. Ukončí aktuální připojení s daným id.
AT+CIFSR AT+CIFSR +CIFSR:ip Vrátí lokální IP adresu ESP8266 modulu.
AT+CIPMUX AT+CIPMUX? +CIPMUX:mod Vrátí aktuální mód připojení

  • 0 = jednoduché připojení
  • 1 = vícenásobné připojení
AT+CIPMUX=mode OK Nastaví mód připojení
AT+CIPSERVER AT+CIPSERVER=mod[,port] OK Upraví server. Mod může nabývat hodnot:

  • 0 = smaže aktuální server (musí následovat restart)
  • 1 = vytvoří server

Pokud parametr port nezadáme, je nastavena výchozí hodnota 333.
Server může být vytvořen pouze pokud AT+CIPMUX=1.

AT+CIPMODE AT+CIPMODE? +CIPMODE:mod Vrátí aktuální mód přenosu dat.

  • 0 = normální mód
  • 1 = unvarnished mód
 AT+CIPMODE=mod OK Nastaví mód přenosu dat
AT+CIPSTO AT+CIPSTO? +CIPSTO:time Vrátí timeout serveru v sekundách
AT+CIPSTO=cas OK Nastaví timeout serveru (0 – 7200 sekund).
AT+CIUPDATE AT+CIUPDATE +CIPUPDATE:n Spustí update firmware přes internet. Nedělejte, pokud přesně nevíte, co děláte. Může dojít až k nefunkčnosti modulu.
Parametr n nabývá hodnot:

  • 1 = server nalezen
  • 2 = připojeno k serveru
  • 3 = nový firmware nalezen
  • 4 = aktualizace firmware
AT+CWDHCP AT+CWDHCP=mod,pov OK Zapnutí/vypnutí automatického přidělení IP adresy (DHCP)

Parametr mod značí:

  • 0 = nastavení pro přístupový bod
  • 1 = nastavení pro klienta
  • 2 = nastavení pro oba

Parametr pov:

  • 0 = vypnuto
  • 1 = zapnuto
IPD +IPD,delka:data Jednoduché připojení. Není tak úplně příkaz, ale návratová hodnota. Označuje data, který do modulu přišla z vnější.
+IPD,id,delka:data Vícenásobné připojení. Zde máme navíc ID připojení.

Pokud jsou parametry příkazů textové řetězce, musíme je dát do uvozovek. AT příkazy se mohou mírně lišit mezi jednotlivými verzemi firmware.

Snad jsem vás neodradil dlouhým výčtem příkazů. Rozhodně není potřeba je umět nazpaměť, jen je dobré vědět, že něco takového existuje a kde najít další informace. Teď už ale k prvnímu příkladu.

Jednoduchá interakce se serverem

Připojíme se k serveru www.google.com (přímo na jeho IP adresu) a stáhneme pomocí HTTP požadavku jeho obsah jako HTML. Jelikož by se nám některé příkazy často opakovaly, jsou umístěné ve funkcích. Jsou to funkce: resendData(), která jen přeposílá data mezi Serial a Serial1. Pokud si nepamatujete, jak funguje HTTP, podívejte se do článku o Ethernet shieldu, popřípadě Programování webových rozhraní pro Arduino.

boolean resendData(){
    while(Serial.available()){
        Serial1.write(Serial.read());    
    }

    while(Serial1.available()){
        Serial.write(Serial1.read());  
    }    
}

Dále funkce waitFor(), který čeká na zadaný řetězec. Pokud je nalezen, vrátí true, pokud je po určitou dobu nenalezen, vrátí false. Všimněte si funkce Serial1.find(), která čte buffer sériové linky dokud nenarazí na zadaný řetězec, nebo nevyprší čas zadaný funkcí Serial1.setTimeout().

boolean waitFor(char *s, long waitingTime){
    Serial1.setTimeout(waitingTime);
    return Serial1.find(s);
}

A poslední vytvořenou funkcí je command(). Ta odešle zadaný AT příkaz, vypíše přes sériovou linku, co se děje, a čeká na výsledek. Pokud se příkaz povedl, vrátí true. Jinak vrátí false.

boolean command(String cmd, String comment, String success, String unsuccess, char *waitForString, long waitingTime){
    Serial.println();
    Serial.println(comment);
    Serial.println("] " + cmd);
    Serial1.println(cmd);
    boolean result = waitFor(waitForString, waitingTime);
    if(result){
        Serial.println("[ " + success);
    }
    else{
        Serial.println("[ " + unsuccess);    
    }
    resendData();

    return result;
}

Celý program, který stáhne zdrojový kód úvodní stránky google.com, tedy vypadá takto. Jelikož musíme občas poslat po sériové lince znak uvozovek, použijeme escape sekvenci pro uvozovky: \“.

char ssid[] = "ssid"; //SSID wifi sítě
char pwd[] = "pass"; //heslo sítě
char ip[] = "216.58.194.78"; //IP adresa www.google.com

String cmd; //řetězec pro ukládání a skládání příkazů 
String http;

void setup(){
  Serial.begin(9600);
  Serial1.begin(115200); //9600, 57600, 115200

  while(!Serial1);
  while(!Serial);

  cmd = "AT";
  command(cmd, "Zkousim pritomnost modulu", "Modul komunikuje", "Modul nekomunikuje", "OK", 3000);

  cmd = "AT+RST";
  command(cmd, "Restartuji modul", "Modul restartovan", "Modul se nepovedlo restartovat", "ready", 5000);

  cmd = "AT+CWMODE=1";
  command(cmd, "Nastavuji klient mod", "Nastaveno", "Nepovedlo se nastavit", "OK", 2000);

  cmd = "AT+CWDHCP=1,1";
  command(cmd, "Zapínám DHCP", "Zapnuto", "Nepovedlo se Zapnout", "OK", 2000);
  

  cmd = "AT+CWJAP=\"";
  cmd += ssid;
  cmd += "\",\"";
  cmd += pwd;
  cmd += "\"";
  command(cmd, "Pripojuji k WiFi", "Pripojeno", "Nelze se pripojit", "OK", 10000);
  
  cmd = "AT+CIPMUX=0";
  command(cmd, "Nastavuji jednoduche pripojeni", "Nastaveno", "Nepovedlo se nastavit", "OK", 1000);

  cmd = "AT+CIPSTART=\"TCP\",\"";
  cmd += ip;
  cmd += "\",80";
  command(cmd, "Pripojuji k zadane IP", "Pripojeno", "Nepovedlo se pripojit", "OK", 5000);
  waitFor("Linked", 1000);

  http = "GET / HTTP/1.1\r\n\r\n";
  cmd = "AT+CIPSEND=";
  cmd += http.length();
  if(command(cmd, "Posilam HTTP request", "Odeslano", "Cas spojeni vyprsel", ">", 5000)){
    Serial1.print(http); 
    Serial.println(">");
  }
  else{
    cmd = "AT+CIPCLOSE";
    command(cmd, "Ukoncuji", "Ukonceno", "", "OK", 2000);
  }
}

void loop(){
  resendData();
}

boolean command(String cmd, String comment, String success, String unsuccess, char *waitForString, long waitingTime){
  Serial.println();
  Serial.println(comment);
  Serial.println("] <" + cmd + ">");
  Serial1.println(cmd);
  boolean result = waitFor(waitForString, waitingTime);
  if(result){
    Serial.println("[ " + success);
  }
  else{
    Serial.println("[ " + unsuccess); 
  }
  resendData();
  
  return result;
}

boolean waitFor(char *s, long waitingTime){
  Serial1.setTimeout(waitingTime);
  return Serial1.find(s);
}

boolean resendData(){
  while(Serial.available()){
    Serial1.write(Serial.read()); 
  }

  while(Serial1.available()){
    Serial.write(Serial1.read());
  }  
}

Pokud vše proběhne v pořádku, vypíše se nám po sériové lince úvodní stránka google ve formátu HTML včetně HTTP hlavičky. Vrácená data můžeme načítat a dále s nimi pracovat. Pokud nějakému příkazu nerozumíte, nejjednodušší způsob, jak vyzkoušet jeho funkčnost je ho odeslat přes monitor sériové linky. Co když chceme, aby na modulu běžel jednoduchý server?

Vytváříme server

K vytvoření serveru použijeme funkce, které jsme si ukázali v předchozím příkladu. Začneme spuštěním přístupového bodu ESP-WIFI s heslem 12345678. Na něm poběží server, který bude čekat na HTTP request „GET /promenna HTTP/1.1“. Jeho úkolem bude načíst číslo zapsané za lomítkem na místě promenna. ESP8266 má přidělenou IP adresu 192.168.4.1. Na server odešleme tento request tak, že se připojíme na vytvořenou wifi a do adresního řádku prohlížeče zadáme ip/promenna – tedy například 192.164.4.1/5.

Už jsme si řekli, že když modulu přijdou nějaká data, vrátí nám je ve tvaru: „+IPD,id,delka:data“. Data tvoří právě náš HTTP požadavek – například GET /5 HTTP/1.1 (odpovídá zadání 192.164.4.1/5 do adresní řádky prohlížeče). Celá přijatá informace tedy bude vypadat takto: +IPD,id,delka:GET /promenna HTTP/1.1. Nám stačí přečíst třetí číslo po řetězci IPD, čímž získáme naši proměnnou.

String cmd; //řetězec pro ukládání a skládání příkazů
String http;

void setup() {
  Serial.begin(9600);
  Serial1.begin(115200); //9600, 57600, 115200

  while (!Serial);
  while (!Serial1);

  cmd = "AT";
  command(cmd, "Zkousim pritomnost modulu", "Modul komunikuje", "Modul nekomunikuje", "OK", 3000);

  cmd = "AT+RST";
  command(cmd, "Restartuji modul", "Modul restartovan", "Modul se nepovedlo restartovat", "ready", 5000);

  cmd = "AT+CIPSERVER=0";
  command(cmd, "Ukončuji server", "Ukončeno", "Nepovedlo se ukončit server", "OK", 2000);

  cmd = "AT+CWDHCP=0,1";
  command(cmd, "Zapínám DHCP", "Zapnuto", "Nepovedlo se Zapnout", "OK", 2000);

  cmd = "AT+RST";
  command(cmd, "Restartuji modul", "Modul restartovan", "Modul se nepovedlo restartovat", "ready", 5000);

  cmd = "AT+CIPMUX=1";
  command(cmd, "Nastavuji vicenasobne pripojeni", "Nastaveno", "Nepovedlo se nastavit", "OK", 1000);

  cmd = "AT+CWMODE=2";
  command(cmd, "Nastavuji AP mod", "Nastaveno", "Nepovedlo se nastavit", "OK", 2000);

  cmd = "AT+CWSAP=\"ESP-WIFI\",\"12345678\",11,2";
  command(cmd, "Vytvarim pristupovy bod", "Vytvoreno", "Nepovedlo se vytvorit pristupovy bod", "OK", 3000);

  cmd = "AT+CIPSERVER=1,80";
  command(cmd, "Vytvarim server", "Vytvoreno", "Nepovedlo se vytvorit server", "OK", 2000);
}

void loop() {
  if (waitFor("+IPD", 50)) { //hledej výskyt +IPD
    Serial1.parseInt(); //jedno číslo zahoď
    Serial1.parseInt(); //druhé číslo zahoď
    int p = Serial1.parseInt(); //přečti třetí číslo

    Serial.print("Nacetl jsem: ");
    Serial.println(p);

    Serial1.flush(); //zbytek zpravy me ted nezajima, zahod ho
  }
}

boolean command(String cmd, String comment, String success, String unsuccess, char *waitForString, long waitingTime) {
  Serial.println();
  Serial.println(comment);
  Serial.println("] " + cmd);
  Serial1.println(cmd);
  boolean result = waitFor(waitForString, waitingTime);
  if (result) {
    Serial.println("[ " + success);
  }
  else {
    Serial.println("[ " + unsuccess);
  }
  resendData();

  return result;
}

boolean waitFor(char *s, long waitingTime) {
  Serial1.setTimeout(waitingTime);
  return Serial1.find(s);
}

boolean resendData() {
  while (Serial.available()) {
    Serial1.write(Serial.read());
  }

  if (Serial1.available()) {
    while (Serial1.available()) {
      Serial.write(Serial1.read());
    }
  }
}

Po připojení k wifi a načtení adresy modulu by se měla zobrazit zpráva: „Nacetl jsem: x“. Hodnotu x můžeme dále využít – třeba k nastavování stavu LED diody. To už ale jistě zvládnete naprogramovat sami.

Odeslaná data nemusejí být nutně HTTP requesty. Může se jednat například o zprávy protokolu MQTT, který se v zařízeních připojených do internetu věcí používají častěji. MQTT je ale nad rámec této kapitoly a stejně tak přímé programování desky ESP8266.

S představenými příkazy a funkcemi je možné vytvořit jednoduchou stránku, například pro ovládání LED. Více o tvorbě stránek naleznete ve článku Programování webových rozhraní pro Arduino.

Zbyšek Voda

Napsat komentář