Programování | Pojďme programovat elektroniku | Arduino

Pojďme programovat elektroniku: Postavíme si titěrnou Wi-Fi meteostanici s lepším teploměrem než Netatmo

  • Dnes se podíváme na maličkou Wi-Fi destičku Wemos D1 mini
  • A připojíme k ní barometrický a teplotní shield
  • Poběží na ní web a nabídne i JSON API

V našem seriálu Pojďme programovat elektroniku jsme si nejčastěji pohrávali buď s jednoduchými mikročipy ATmega328p na prototypovacích destičkách Arduino, anebo s mnohem výkonnějšími čipy ESP8266 s vestavěnou podporou Wi-Fi.

Jednou z nejoblíbenějších prototypovacích destiček s ESP8266 je Wemos D1 mini a zástup jeho klonů z asijských e-shopů. Na eBay, AliExpressu aspol. Jej seženete za částku okolo stokoruny. Hlavní specialitou malého tištěného spoje o velikosti SD karty je hromada dostupných shieldů – rozšiřujících desek, které můžete zapojovat na sebe do vrstev jako sendvič.

264199297
Wemos D1 mini a jeho rozšiřující shieldy

Podobné shieldy jsou samozřejmě k dispozici i pro jiné prototypovací mašinky včetně již zmíněných Arduin nebo třeba Raspberry Pi, v případě Wemos D1 mini a jeho klonů však mohou přijít zvláště vhod, protože když k už tak titěrnému mikrokontroleru připojíte bez potřeby jediného drátku třeba shield s elektromagnetickým relé pro spínání běžného domácího 230V napětí, výsledkem bude elegantní krychlička s rozměry 3 × 2 × 4 centimetrů.

950625849 604478315 365096639
Wemos D1 mini a relé shield pro spínání 230V napětí. Spojením tedy vznikne třeba docela elegantní Wi-Fi spínač nějakého spotřebiče (světlo, větrák, zavlažování aj.)

Zatímco shield s elektromagnetickým relé po nasazení na D1 mini zabere jeden pin GPIO pro samotné ovládání, mnohé jiné shieldy po nasazení všechny původní piny zrcadlí, protože pro svoji vlastní komunikaci zpravidla používají sběrnici I2C a její dva piny SDA a SCL, na které lze připojit až 127 zařízení rozlišitelných podle jejich vnitřní adresy (číslo 0-127).

Když tedy na jednotlivé destičky připájíte propojovací piny a vše do sebe zacvaknete, stačí D1 mini připojit přes micro USB k počítači a napsat pro něj program třeba v prostředí Arduino. Žádné kablíky, žádné nepájivé pole – prostě ideální řešení pro domácí produkční elektroniku, pokud si netroufáte na výrobu ještě prostorově mnohem optimalizovanějších vlastních plošných spojů na míru.

Maličká kostička s meteostanicí uvnitř

Pojďme si to vyzkoušet. Dnes si vyrobíme miniaturní tlakoměr, vlhkoměr a teploměr v jednom. Jelikož se o jeho běh bude starat mikročip ESP8266 na destičce Wemos D1 mini, po připojení napájení se čip připojí do domácí sítě Wi-Fi a spustí si webový server.

Když pak do prohlížeče zadáte jeho IP adresu, zobrazí se jednoduchá webová stránka s aktuální hodnotou tlaku, teploty a vlhkosti a bude se zhruba každou minutu obnovovat.

4959444 482580090 478954607
Průběžně aktualizovaná webová stránka běžící na Wemos D1 mini. Díky tvárnosti HTML a CSS může připomínat třeba podobný stavový displej.

JSON API pro napojení na další systémy

Naše miniaturní meteostanice bude mít i jednoduché API. Když totiž zadáte adresu http://ipadresameteostanice/?api=json, zobrazí se hodnoty ve formátu JSON, které můžete zpracovat v nějaké další aplikaci.

Aby byla demonstrace kompletní, na domácím routeru povolím pomocí přesměrování portu přístup k meteostanici z internetu, aby se právě skrze toto API mohl ke krabičce každých 15 minut připojovat jednoduchý program napsaný v Google Apps Scriptu, který bude naměřené hodnoty průběžně ukládat do tabulky na Google Drive.

Stejně tak by samozřejmě mohla s vnějším internetem komunikovat sama krabička a v daném intervalu by skrze HTTP GET, POST nebo jiný protokol posílala data na libovolný server.

Nakupujeme komponenty

Když třeba na eBay vyhledáte frázi d1 mini shield, zobrazí se vám pár tisíc výsledků. V praxi to je ale nejčastěji už zmíněné elektromagnetické relé, bateriový shield, prototypovací shield, shield s micro SD čtečkou, ale nás dnes bude zajímat shield s teploměrem a vlhkoměrem SHT30 a konečně shield s tlakoměrem BMP180.

496438734 508887474 519241379
Příprava k pájení vhodných header pinů a hotový mrakodrap naší meteostanice. Na prostřední fotografii s teploměrem SHT30 si všimněte údaje o adrese pro sběrnici I2C. Mohu ji změnit spojením, nebo rozpojením speciálního obvodu. V tomto případě tedy bude mít teploměr adresu 0x45, kterou použiji i v kódu.

Oba mají komunikační rozhraní I2C, takže nezablokují žádné další GPIO mimo pinů SDA a SCL vyhrazených pro tuto sběrnici.

K získání hodnot z obou senzorů použiji knihovny od Adafruitu, které mohu stáhnout přímo v prostředí Arduino (Projekt → Přidat knihovnu → Spravovat knihovny → vyhledat BMP180 a zpětně kompatibilní SHT31).

Pak už použiji vestavěné knihovny Arduina pro ESP8266 a to jak pro samotné připojení k Wi-Fi, tak pro spuštění webového serveru.

Lepší než Netatmo?

Levné I2C teploměry z řady Sensirion SHT3x jsou pokročilejší verzí teploměrů řady Sensirion SHT2x, které najdete třeba v populární chytré domácí meteostanici Netatmo.

Jak SHT30, tak SHT20 mají za běžných teplot (0-60 °C) typickou odchylku do 0,3 °C, která může růst, ale nikterak kriticky, ve velmi nízkých teplotách pod nulou.

SHT30 je papírově podobný jako SHT20, pokud byste však hledali velmi přesný teploměr i pro zimní podmínky, nabízí se pokročilejší SHT31, který by měl dle specifikace držet odchylku 0,3°C i při -40 °C. Na eBay seženete jeho modul pro snadné prototypování za cenu lehce přes stokorunu.

Dost bylo řečí, pojďme se podívat na kód:

/*
  Namisto obvykleho vypisu do seriove linky pomoci Serial.print()
  pouziji knihovnu Streaming a format Serial << promenna << promenna atd.
  Streaming stahnete z webu http://arduiniana.org/libraries/streaming/
*/
#include <Streaming.h>
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <Wire.h>
#include <Adafruit_BMP085.h>
#include <Adafruit_SHT31.h>

/*
  Objekty tlakomeru, teplomeru a weboveho serveru,
  ktery se spusti na standardnim portu 80
*/
Adafruit_BMP085 tlakomer;
Adafruit_SHT31 teplomer = Adafruit_SHT31();
ESP8266WebServer server(80);

// Promenne meteorologickych udaju
float teplota0 = 0.0f, teplota1 = 0.0f, tlak = 0.0f;
uint8_t vlhkost = 0;
uint16_t vyska = 0;

// Prihlasovaci udaje k Wi-Fi
const char ssid[] = "Klobouk";
const char heslo[] = "mameradikubucizka";

/*
  Nemenny HTML kod stranky
  ulozeny ve flashove pameti cipu
*/
PROGMEM const char html_hlavicka[] = "<!DOCTYPE html><html><head><title>Meteostanice</title><meta http-equiv=\"refresh\" content=\"70\"><style>html,body{font-family:'Segoe UI',Tahoma,Geneva,Verdana,sans-serif;margin:0;padding:0;display:flex;justify-content:center;align-items:center;width:100%;height:100%;overflow:hidden;background-color:black;}div{font-size:10vw;color:grey;resize:none;overflow:auto;}.value{color:white;font-weight:bolder;}</style></head><body><div>";
PROGMEM const char html_paticka[] = "</div></body>";

/*
  Pomocna promenna casovace, ktery kazdou
  minutu aktualizuje udaje ze senzoru
*/
uint64_t posledniObnova = 0;

// Funkce setup se zpracuje hned po spusteni
void setup() {
  // Nastartuj seriovoou linku na rychlosti 115 200 bps
  Serial.begin(115200);
  // Dvakrat odradkuj seriovou linku a napis uvitani
  Serial << endl << endl;
  Serial << "===============================" << endl;
  Serial << "=== M E T E O S T A N I C E ===" << endl;
  Serial << "===============================" << endl << endl;

  /*
    Pokud se nepodarilo nastartovat tlakomer
    nebo teplomer (I2C adresa 0x45 je napsana na jeho desticce),
    vypis do seriove linky chybove hlaseni a zastav dalsi zpracovavani
  */
  if (!tlakomer.begin() || !teplomer.begin(0x45)) {
    Serial << "Tlakomer nebo teplomer neodpovida. Zkontroluj zapojeni!" << endl;
    while (1) {}
  }

  /*
    Precti hodnoty ze sensoru a vypis
    je do seriove linky
  */
  ziskejHodnoty();

  // Jmeno zarizeni v siti
  WiFi.hostname("meteostanice");
  // Rezim Wi-Fi (sta = station)
  WiFi.mode(WIFI_STA);
  // Zahajeni pripojovani
  WiFi.begin(ssid, heslo);
  Serial << endl << "Pripojuji se k Wi-Fi siti " << ssid << " ";
  // Dokud se nepripojim, vypisuj do seriove linky tecky
  while (WiFi.status() != WL_CONNECTED) {
    Serial << ".";
    delay(500);
  }

  // Vypis do seriove linky pridelenou IP adresu
  Serial << endl << "Meteostanice ma IP adresu " << WiFi.localIP() << endl;

  /*
    Nastaveni weboveho serveru. Zareaguje, pokud zachyti pozadavek GET /,
    tedy pozadavek na korenovy adresar. Stejne tak bych mohl nastavit
    reakce na fiktivny stranky, treba server.on("/index.html" a tak dale.
  */
  server.on("/", []() {
    // Ziskej parametr URL jmenem api, tedy /?api=...
    String api = server.arg("api");
    // Preved jej na mala pismena
    api.toLowerCase();
    /*
      Pokud URL parametr obsahuje text 'json',
      posli klientovi HTTP kod 200 a JSON s hodnotami ze senzoru
      a vypis do seriove linky IP adresu klienta
    */
    if (api == "json") {
      server.send(200, "application/json", "{\"tlak\":" + String(tlak, 2) + ",\"teplota\":" + String(teplota1, 2) + ",\"vlhkost\":" + String(vlhkost) + "}");
      Serial << "HTTP GET: Klient si stahl JSON data" << endl << endl;
    }
    /*
      Pokud URL parametr obsahuje neco jineho, nebo
      neexistuje, posli klientovi HTTP kod 200 a HTML stranku
    */
    else {
      server.send(200, "text/html", String(html_hlavicka) + "Teplota: <span class=\"value\">" + String(teplota1, 2) + " &#x00B0;C</span><br/>Tlak: <span class=\"value\">" + String(tlak, 2) + " hPa</span><br/>Vlhkost: <span class=\"value\">" + String(vlhkost) + " %</span>" + String(html_paticka));
      Serial << "HTTP GET: Klient si stahl HTML stranku" << endl << endl;
    }
  });

  // Nastavili jsme webovy server a ted ho uz muzeme spustit
  server.begin();
  Serial << "Webovy sever je spusteny a ceka!" << endl;
}

// Funkce loop se po spusteni opakuje stale dokola
void loop() {
  // Zpracuj pozadavky weboveho serveru
  server.handleClient();
  /*
    Kazdych sedesat sekund precti hodnoty ze senzoru.
    Nesmim pouzit delay(60000), protoze by se na 60s
    zastavil cely program, a kdyby v tuto chvili nekdo
    navstivil server, ten by na pozadavek zareagoval az
    po minute.

    Funkci ziskejHodnoty bych take mohl zavolat az pri
    pozadavku na webovy server, takto ale mohu prubezne zaznamenavat
    treba rekordni hodnoty aj.
  */
  if ((millis() - posledniObnova) > 60000) {
    ziskejHodnoty();
    posledniObnova = millis();
  }
}

// Funkce pro precteni hodnot ze senzoru do promennych
void ziskejHodnoty() {
  // Prepocet tlaku v Pa na hPa
  tlak = tlakomer.readPressure() / 100.0f;
  // Vypocet velmi hrube nadmorske vysky podle tlaku
  vyska = tlakomer.readAltitude();
  // Teplota z tlakomeru
  teplota0 = tlakomer.readTemperature();
  // Presnejsi teplota z teplomeru
  teplota1 = teplomer.readTemperature();
  // Vlhkost vzduchu
  vlhkost = teplomer.readHumidity();

  // Vypsani cerstvych hodnot do seriove linky
  Serial << "System bezi: " << millis() << " ms" << endl;
  Serial << "Volna pamet heap: " << ESP.getFreeHeap() << " B" << endl << endl;

  Serial << "Udaje z tlakomeru BMP180" << endl;
  Serial << "Atmosfericky tlak: " << tlak << "hPa" << endl;
  Serial << "Teplota vzduchu: " << teplota0 << " C" << endl;
  Serial << "Nadmorska vyska: " << vyska << " m n.m." << endl << endl;

  Serial << "Udaje z teplomeru a vlhkomeru SHT30" << endl;
  Serial << "Teplota vzduchu: " << teplota1 << " C" << endl;
  Serial << "Relativni vlhkost vzduchu: " << vlhkost << " %" << endl << endl << endl;
}

Tak, teď už jen stačí nahrát program do čipu a během pár okamžiků by už se měla malá kostička hlásit v 802.11bgn síti.

921534202 136358557
Výpis ze sériové linky

Data z krabičky čte Google Apps Script

Pojďme se nyní ještě podívat, jak by mohl vypadat kód drobného programu v Google Apps Scriptu (Javascript), který běží na serverech Googlu jako součást služby Google Drive a s krabičkou se skrze můj domácí router spojí každých 15 minut, přečte údaje v JSONu a hodnoty uloží do tabulkového dokumentu na úložišti Google Drive. Stačí mít veřejnou IP adresu a v konfiguraci routeru povolit/přesměrovat port.

Kód Google Apps Scriptu:

/*
* Funkce run se bude spouštět každých 15 minut.
* To nastavím v konfiguraci spouštěčů ve webovém editoru.
*/
function run() {
 /*
 * Webový server na meteostaničce bude z vnějšího internetu
 * dostupný třeba na portu 166. Stáhnu JSON a rozluštím jej.
 */
 var data = UrlFetchApp.fetch("http://kloboukuv.cloud:166/?api=json").getContentText();
 var json = JSON.parse(data);
 
 // Z JSONu získám a na čísla převedu teplotu, tlak a vlhkost
 var teplota = parseFloat(json.teplota);
 var tlak = parseFloat(json.tlak);
 var vlhkost = parseInt(json.vlhkost) / 100;
 
 // Otevřu dokument tabulky pode jeho ID (je součástí jeho webové adresy)
 var doc = SpreadsheetApp.openById("1Y-iUtREWOhOTzHqTeK_d_k8h-BCMGyTlzVrM");
 // Otevřu první list tabulky
 var list = doc.getSheets()[0];
 // Získám aktuální čas ve formátu HH:mm:ss dd.MM. yyyy
 var cas = Utilities.formatDate(new Date(), "Europe/Prague", "HH:mm:ss dd.MM. yyyy");
 // Vložím hodnoty na nový řádek v tabulce
 list.appendRow([cas, teplota,tlak, vlhkost]);
 /* 
 * Pokud počet řádků dosáhl více než 96, smažu první řádek a ostatní posunu nahoru.
 * V sešitu mám tedy vždy jen posledních 96 hodnot, což odpovídá 24 hodinám.
 */
 orezTabulku(list, 96) 
}

// Funkce pro ořez tabulky
function orezTabulku(list, velikost){
 var posledniRadek = list.getLastRow();
 if(posledniRadek > velikost){
 list.deleteRows(1, (posledniRadek - velikost));
 }
}

Za čtvrt hodinky stačí kouknout do kýženého tabulkového dokumentu, který se postupně začne plnit daty, nad kterými můžete kreslit grafy a dále s nimi pracovat.

982223671 8002501 100194420
Data z meteostanice ve formátu JSON a jejich průběžné čtení a ukládání do tabulky pomocí Google Apps Scriptu

Shieldy mají jednu klíčovou fyzikální nevýhodu

Tak a to je celé. Pomocí dvou shieldů jsme si poskládali miniaturní meteostanici. Jenže právě v té miniaturizaci je jeden háček. Čip ESP8266 taktovaný na 80 MHz a během čilé Wi-Fi komunikace se už pochopitelně citelně zahřívá. Pro běžné nasazení to ničemu nebrání, pokud je však v jeho blízkosti citlivý teploměr, může se to projevit odchylkou i několika stupňů.

Při skládání sendviče je tedy nanejvýše vhodné umístit hlavní teploměr co nejdále od Wi-Fi čipu a nikdy by neměl být nad ním, protože teplý vzduch se pohybuje vzhůru.

397724854
Wi-Fi čip topí, takže není úplně ideální, aby byl teploměr v jeho naprosté blízkosti

Pro ještě přesnější výsledky měření při zachování malé velikosti můžete popřemýšlet o dalších formách tepelné izolace, anebo usínání čipu, kdy webový server nepoběží přímo na něm, ale třeba na další destičce s ESP8266. Kostička s teploměrem a tlakoměrem pak může každou minutu změřit hodnoty, odeslat je na server a přepnout se pomocí metody:

ESP.deepSleep(kolikSekund * 1000000)

do úsporného režimu (odběr proudu může klesnout z desítek mA na stovky uA), kdy se nebude Wi-Fi čip tak zahřívat. S tímto úsporným přístupem, který vyžaduje ještě propojení pinů RST (RESET) a D0 (WAKE) a přemístění veškerého kódu do funkce setup, protože se program probudí ze spánku svým vlastním resetem, by meteostanice vydržela běžet na menší Li-ion baterii i několik dnů, a kdyby měla k dispozici dobíjecí modul (bateriový shield), přes den by baterii obnovila menším solárním 5V panelem.

889156412
Wemos D1 mini s tlakoměrem, teploměrem, vlhkoměrem, luxmetrem, detektorem deště a prachových částic a jeho spotřeba při napájení z 2 000 mAh Li-ion baterie s dobíjením pomocí malého 5V solárního panelu.

Ostatně přesně tak pracuje moje meteorologická sonda na balkoně a s jedním lithiovým článkem 18650 s kapacitou 2 000 mAh a 5V solárním panelem za dvě stovky z internetu měří a zasílá data s minutovým intervalem a bez jediné havárie už od února. Zatímco přes noc baterie ztratí asi 5-10 % energie (podle délky noci, teploty aj.), během dopoledne se zase nabije na 100 %.

Diskuze (31) Další článek: Milujeme hardwarovou QWERTY v mobilu. Co na tom, že nedává velký smysl...

Témata článku: , , , , , , , , , , , , , , , , , , , , , , , ,