Arduino měří bezdrátově teplotu v bazénu

16. 11. 2015
Doba čtení: 8 minut

Sdílet

Znovu se vrátíme k uspávání mikrokontrolérů z rodiny AVR ATMEGA, tentokrát už s digitálním multimetrem v ruce. Následně se ponoříme do zdrojových kódů knihoven, vysvětlíme si další část aplikace na měření a vysílání teploty nejen bazénové vody, a podíváme se i na zpracování naměřených dat na serveru.

Na úvod mi dovolte shrnout některé cenné podněty z diskuse pod minulým článkem (za které tímto autorům komentářů děkuji):

1) Pokud je digitální teplotní čidlo DS18B20 napájeno poctivě (nikoliv paraziticky), je možné spolehnout se na to, že během měření a převádění teploty drží datovou linku na logické 0 a na konci konverze, až má teplotu připravenu k vyčtení, datovou linku zvedne nahoru. Jelikož máme teplotní čidlo připojeno na pin 2, který je zároveň u AVR ATMEGA328p mikrokontroléru jedním ze dvou pinů, který dokáže vyvolat HW přerušení, můžeme procesor uspat ihned po zahájení teplotní konverze, celých těch 375 ms (nebo u 12bitové přesnosti dokonce 750 ms) prospat a nechat se probudit RISING hranou přerušení na datovém pinu č.2. Má původní obava, že procesor nemohu uspat, neboť datovým pinem č. 4 napájí termočidlo, je prý lichá – datové piny procesoru udrží svou napěťovou úroveň i ve spánku, takže pin č.4 bude dál napájet teplotní čidlo DS18B20. Oboje nemám ještě osobně ověřeno, ale brzy to zkusím a pak na G+ poreferuji.

2) Dále se ukazuje, že RF vysílač může být napájen přímo z VCC, protože má na vstupu FET tranzistor, který v případě vysílání logické 0 prakticky odpojuje celý vysílač od zdroje napětí, takže má nulový odběr. Další možnost je koupit některý z novějších/lepších RF vysílačů, které kromě datového mají i „ENABLE“ pin, kterým se nejspíš zapínají/vypínají – a pro snížení spotřeby vysílače během spánku procesoru bude nejspíš stačit shodit tento pin do logické nuly.

Novější typ vysílače (vpravo) má už i ENABLE pin

Zdá se, že je lepší pořídit si dobrý miliampérmetr, než vymýšlet zapojení plné fint, které ve výsledku nic moc neušetří. Dobrý digitální multimetr jsem si před měsícem skutečně pořídil a tak jsem dnes postavil kopii bazénového teploměru a pro účely článku provedl pár měření.

Při standardním napájecím napětí 5 V s originální svítící POWER LED spotřebuje Pro Mini ve spánku 6,24 mA, při měření či vysílání teploty pak až 20 mA.

Pro další měření jsem snížil napájecí napětí na 4 V, což více odpovídá situaci, kdy je zdrojem napětí Li-Ion článek. Spotřeba při 4 V se stále ještě svítící POWER LED klesla ve spánku na 4,72 mA, při měření či vysílání vyskakovala až na 14 mA.

Pak přišel čas odpájet POWER LED. Spotřeba ve spánku klesla jen o 2 mA na 2,68 mA. To je pořád příliš.

Začal jsem proměřovat jednotlivé komponenty: odběr samotného DS18B20 v klidu je pouhý 1 µA, zatímco odběr samotného 433 MHz vysílače v klidu je čistých 0 µA. Z toho plyne, že je možné nechat je napájet přímo z VCC, není potřeba je napájet z datových pinů procesoru.

Vrátil jsem se k ladění uspání procesoru. Jako první jsem vypnul ADC (analogově-digitální převodník). Spotřeba na 4 V s vypnutým ADC ve spánku klesla na 2,44 mA, takže vypnutím ADC jsem ušetřil 0,24 mA.

Pak jsem vypnul BOD (Brown Out Detection – detekce příliš nízkého napájecího napětí). Spotřeba klesla na 2,42 mA, tedy vypnutí BOD ušetří 20 µA. Mohl jsem ještě pokračovat vypínáním dalších drobností na procesoru, ale podle zkušeností jiných už bych tím pokroku v řádu mA nedosáhl. Zdá se, že drtivá většina proudu mizí někde ve výstupu (78)L05 stabilizátoru (který je na desce proto, aby Pro Mini fungovalo s napájecím napětím od 5 do 15 V), jinak si to neumím vysvětlit. Až budu mít po ruce ten skutečný bazénový teploměr (a nikoliv jeho repliku), odpájím celou tu vstupní stabilizační část (která se v případě napájení z jednoho Li-Ion článku nepoužívá) a pak to znovu proměřím.

Při pročítání Arduino fóra jsem našel další knihovnu na uspávání procesoru: Sleep_n0m1 – vypadá velmi dobře. A také článek o módech spánku: ATMega328P sleep mode.

Poslední dva odkazy k problematice uspávání a šetření energií jsou asi ty nejlepší: opravdu detailní Power saving techniques for microprocessors a praktické AVR microcontroller sleep demonstrations.

Skutečné zapojení v detailu, kombinace vrabčího hnízda s kouskem plošného spoje.

Věřím, že tímto bychom měli techniku uspávání procesoru pro snížení celkové spotřeby naprosto pokrytou a můžeme se vrátit k měření teploty bazénové vody, jejímu přenosu a dalšímu zpracování. V minulém článku jsem ukázal části kompletního zdrojového kódu, který jsem napsal, aby mi měřil teplotu vody v bazénu a vysílal ji v kódování, kterému rozumí původní základnová stanice bazénového teploměru. Dále vysílá naměřenou teplotu, napájecí napětí a ještě i napětí naměřené na solárním panelu protokolem knihovny VirtualWire, aby bylo možné data zachytit a dekódovat počítačem a využít pro statistické a možná i regulační účely. Podíváme se na to podrobněji.

Pro účely tohoto článku jsem vytvořil na GitHubu hned tři repozitáře. První obsahuje kompletní zdrojový kód aplikace běžící v krabičce u bazénu. Tato aplikace potřebovala knihovnu, která by dokázala odesílat data ve správném formátu tak, aby teplotu rovnou zobrazila osiřelá základnová stanice bazénového teploměru. Vhodnou knihovnu jsem po silném googlení našel v diskusi pod článkem o dekódování signálů a po mikro úpravách jsem ji začal používat. A dnes jsem ji pod názvem Water Temp Transmitter nahrál na GitHub, aby moje aplikace byla kompletní a přeložitelná.

Třetí repozitář pak obsahuje knihovnu, která dokáže data vysílaná čidly KW9043 a jim podobnými dekódovat. Tuto knihovnu jsem nemohl nikde najít, a tak jsem ji nakonec vytvořil sám postupem ne nepodobným reverznímu inženýrství: četl jsem kód knihovny pro vysílání v jednom okně a ve vedlejším okně psal kód, který by dokázal toto vysílání přijímat a správně dekódovat. Knihovnu jsem nazval Water Temp Receiver a dávám ji na GitHub proto, že nefunguje pouze s tím původním kloboučkem, co plaval v bazénu, ale také s běžnými externími čidly mnoha levných „meteostanic“, které měří jen teplotu (nikoliv teplotu a vlhkost).

Jednoduché použití knihovny pro příjem teploty vysílané běžným komerčním čidlem ilustruje následující příklad:

#include <watertempreceiver.h>

void setup()
{
    Serial.begin(115200);
    Serial.println("Jedem!");
    WaterTempReceiver::init(0, printdata);
}

void loop()
{
}

void printdata(byte &id, int &temp, byte &chan, boolean &batt, boolean &beep)
{
    Serial.print("id = ");
    Serial.print(id);
    Serial.print(", temp = ");
    Serial.print(temp/10);
    Serial.print(".");
    Serial.print(temp%10);
    Serial.print(", chan = ");
    Serial.println(chan);
}

Vraťme se ještě k aplikaci, která měří a odesílá data pro počítač. Jelikož knihovna VirtualWire umí odesílat jen řetězec (resp. pole bajtů), funkce void vwSendTempAndMore(float temp) sestaví právě takový řetězec z naměřené teploty, napětí na Arduinu a napětí na solárním článku. Celé to uvádí prefix „Bazen:“, aby se mi to při dekódování na serveru nepopletlo s případným jiným čidlem:

void vwSendTempAndMore(float temp)
{
    char msg[VW_MAX_MESSAGE_LEN] = "Bazen:";

    int t = (int)(temp * 10);
    itoa(t, msg + strlen(msg), DEC);
    strcat(msg, ":");

    itoa(vcc, msg + strlen(msg), DEC);
    strcat(msg, ":");

    unsigned int solar = analogRead(A0) * vcc / 512UL; // 1024 = vcc * 2 (voltage divider on A0)
    itoa(solar, msg + strlen(msg), DEC);

    vw_send((uint8_t *)msg, strlen(msg));
    vw_wait_tx(); // Wait until the whole message is sent
}

Napájecí napětí na Arduinu se dá změřit zajímavou a nepříliš známou fintou. ADC multiplexer umožňuje měřit nejenom na 8 analogových vstupech, ale umí se také přepnout na změření napětí vnitřní napěťové reference, která má mít přibližně 1,1 V. Tuto měříme oproti neznámému napájecímu napětí a následně trojčlenkou vypočítáme VCC. Je to vidět ve funkci  unsigned int readVcc():

unsigned int readVcc() {
    // Read 1.1V reference against AVcc
    // set the reference to Vcc and the measurement to the internal 1.1V reference
    ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
    delay(2); // Wait for Vref to settle
    ADCSRA |= _BV(ADSC); // Start conversion
    while (bit_is_set(ADCSRA,ADSC)); // measuring
    unsigned int a = ADCW;
    unsigned int result = 1119162UL / a; // Calculate Vcc (in mV)
    return result; // Vcc in millivolts
}

Podivná konstanta 1119162UL je vyladěná na konkrétní Arduino, jelikož napětí vnitřní reference je různé kus od kusu v rozsahu (prý až) 1,0 – 1,2 V. Tato přesně změřená a spočítaná konstanta mi ale asi moc nepomůže, protože vnitřní referenční napětí je nejspíš závislé i na okolní teplotě, a její kompenzaci zde zahrnutou nemám.

Napětí na solárním panelu pak měřím tak, že na analogovém vstupu A0 mám napěťový dělič 1:2 (ze dvou stejných rezistorů) a ten je připojen přímo na přívod od solárního panelu. Napěťový dělič tam je proto, že Arduino může měřit jen do výše svého napájecího napětí (tedy maximálně do 4,2 V), zatímco na solárním panelu může být napětí až 6 V.

ict ve školství 24

Jak tyto údaje zachytit a strojově zpracovat? Jedna z možností je opět Arduino s 433 MHz přijímačem, které údaje dekóduje a pak podle nich třeba sepne bazénové čerpadlo, bazénový ohřev či jinou část systému. Také je ale možné naměřené hodnoty ukládat třeba do databáze na serveru, anebo tam z hodnot teploty a napětí jen kreslit RRD grafy, aby se člověk mohl z práce smutně dívat, jak je doma v bazénu hezky. Osobně bych použil Arduino s 433 MHz přijímačem a Ethernetovým modulem, naměřené hodnoty bych si zpřístupnil třeba přes jednoduchý web server a na mém domácím serveru pak cronem spouštěl například následující kód:

#!/usr/bin/perl
use POSIX qw(strftime);
use Fcntl qw(:flock);
use LWP::UserAgent;

my $SEMFILE="/dev/shm/digitemp_log_temp.sem";
die "File lock exists" if (-e $SEMFILE);
open(SEM, ">$SEMFILE") or die "Can't write-open digitemp_log_temp.sem: $!";
flock(SEM, LOCK_EX);

my $CMD='wget -q -O - 192.168.1.155:10000';
my $output = `$CMD`;
my @pole = split(' ', $output);
my $voda = $pole[1];
my $vcc = $pole[2];
my $solar = $pole[3];
system("rrdtool update /var/digitemp_rrdb/digitemp.rrd N:$voda:$vcc:$solar\n");
system("mysql bazen -ubazen -ptajneHeslo -e \"INSERT INTO bazen (voda, vcc, solar) VALUES (NOW(), $voda, $vcc, $solar)\"");
close(SEM);
unlink($SEMFILE);

Pokud nemáte po ruce ethernetový modul, šlo by propojit to přijímací Arduino se serverem například přes sériový port (i převedený na USB či přes Bluetooth). Ještě zajímavější možností by bylo popadnout nějaký lepší router typu Turris Omnia a připíchnout 433 MHz přijímač přímo na jeho GPIO piny. Pak by odpadlo celé přijímací Arduino, neboť software na routeru by mohl vkládat data do RRD či MySQL rovnou, předpokládám. Ale s tímto nemám praktickou zkušenost, tak se budu těšit na komentáře pod článkem.

Autor článku

Petr Stehlík vystudoval aplikovanou informatiku a pracuje jako vývojář webových aplikací a administrátor linuxových serverů. Provozuje vlastní server tvpc.cz.