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 s 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ů.
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.
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
|
|
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í:
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
|
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í
|
AT+CIPMUX=mode | OK | Nastaví mód připojení | |
AT+CIPSERVER | AT+CIPSERVER=mod[,port] | OK | Upraví server. Mod může nabývat hodnot:
Pokud parametr port nezadáme, je nastavena výchozí hodnota 333. |
AT+CIPMODE | AT+CIPMODE? | +CIPMODE:mod | Vrátí aktuální mód přenosu dat.
|
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:
|
AT+CWDHCP | AT+CWDHCP=mod,pov | OK | Zapnutí/vypnutí automatického přidělení IP adresy (DHCP)
Parametr mod značí:
Parametr pov:
|
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.