Hlavní navigace

Šetříme energií aneb Arduino na baterky

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

Sdílet

Arduino je energeticky velmi nenáročné, ale když jej napájíte z baterie, hodí se každá úspora. Dnes si doplníme minule ukázané symbolické schéma bazénového teploměru o systém úspory energie a ukážeme si, jak uspat mikrokontrolér v době, kdy ho nepotřebujeme. Prodloužíme výdrž z hodin na desítky dnů.

minulém článku jsem na závěr ukázal propojení jednotlivých modulů. Sice tam chybělo digitální teplotní čidlo, ale to hned napravíme a přidáme ho na pin D2.

Ve skutečnosti je celé zapojení nepatrně složitější, jak ukážu za okamžik. Teď si popíšeme, jak se vlastně systém chová. Pro maximální úsporu energie totiž většinu času procesor prospí, stejně jako originální/komerční bezdrátová čidla. Ta obvykle vysílají naměřené hodnoty jednou za 30 – 40 sekund. U mého teploměru jsem byl netrpělivý, tak jsem vsadil na kratší, jen 24sekundový spánek mezi dvěma měřeními. Asi by bylo rozumné tuto hodnotu ještě o 50 % navýšit (změnou jedné konstanty ve zdrojovém kódu), čímž bych jistě dosáhl delší životnosti na jedno nabití akumulátoru. Koneckonců voda v bazénu mění teplotu jen velmi pozvolna, takže není potřeba měřit a vysílat ji tak často.

Procesor ATMEGA328p, použitý v Arduinu Pro Mini (a UNO, a mnoha dalších), má hned několik úrovní hloubky spánku. Technicky jeho uspání zařídíme poměrně snadno: nastavíme hlídače (watchdog) tak, aby za nějakou dobu nečinnosti procesoru vyvolal přerušení, a pak procesor uspíme do stavu, ze kterého se probudí právě tím přerušením od watchdogu. Na  Rocket Stream blogu jsem našel velmi dlouhý a velmi podrobný popis knihovny pro uspávání Arduin. Pokud nemáte sílu pročítat ten blogpost celý a chcete ihned začít uspávat procesor ve svém projektu, rovnou stahujte knihovnu lowpower (což je vylepšený fork z původní Low-Power)

Tuto knihovnu jsem před dvěma roky, když tento bazénový teploměr vznikal, ještě neznal, takže v mém zdrojovém kódu najdete „ruční“ verzi uspávání programující přímo jednotlivé registry procesoru. Pokud chci procesor uspat, ta vždy resetnu watchdog, nastavím jeho periodu na 8 sekund (to je nejdelší čas, který jde nastavit), povolím ho a zavolám sleepNow(), což uspí samotný procesor. Za 8 sekund watchdog nakopne procesor, což zavolá funkci ISR(WDT_vect), která rychle vypne watchdog a pokračuje se v normální práci. A 24 sekund získáme jednoduše zavoláním této sekvence třikrát. Tu je to místo, kde můžu nastavit 32 sekund (4×8) nebo třeba 40 sekund (5×8) pouhou změnou konstanty v podmínce for cyklu:

    // sleep for 3 x 8 seconds
    for(int i=0; i<3; i++) {
        wdt_reset();                         // Get ready to go to sleep...
        watchdogEnable();                    // Turn on the watchdog timer
        sleepNow();                          // Go to sleep, watchdog timer will wake us later
    }

// WDT Wakeup
ISR(WDT_vect)
{
    cli();
    wdt_disable();
    sei();
}

// Turn on watchdog timer; interrupt mode every 8.0s
void watchdogEnable()
{
    cli();
    MCUSR = 0;
    WDTCSR |= B00011000;
    //WDTCSR = B01000111;                // 2 Second Timeout
    //WDTCSR = B01100000;                // 4 Second Timeout
    WDTCSR = B01100001;                  // 8 Second Timeout
    sei();
}

void sleepNow()
{
    /* In the avr/sleep.h file, the call names of these sleep modus are to be found:
     *
     * The 5 different modes are:
     *     SLEEP_MODE_IDLE         -the least power savings
     *     SLEEP_MODE_ADC
     *     SLEEP_MODE_PWR_SAVE
     *     SLEEP_MODE_STANDBY
     *     SLEEP_MODE_PWR_DOWN     -the most power savings
     *
     *  the power reduction management <avr/power.h>  is described in
     *  http://www.nongnu.org/avr-libc/user-manual/group__avr__power.html
     */

    set_sleep_mode(SLEEP_MODE_PWR_DOWN); // Sleep mode is set here

    sleep_enable();                      // Enables the sleep bit in the mcucr register
                                         // so sleep is possible. just a safety pin
    sleep_mode();                        // Here the device is actually put to sleep!!
                                         // THE PROGRAM CONTINUES FROM HERE AFTER WAKING UP
    sleep_disable();                     // First thing after waking from sleep:
                                         // disable sleep...
}

Takže procesor spí, a přitom spotřebovává proud zřejmě jen kolem 100 µA (mohl by mnohem mnohem méně, ale zapomněl jsem povypínat různé periférie procesoru, jako třeba ADC = analogově digitální převodník nebo BOD = kontrolu na napájecí podpětí). To bych měl teď, po dvou letech provozu a nových zkušenostech, při zazimovávání bazénu a teploměru napravit.

Jenže na napájecí napětí z akumulátoru jsou (podle obrázku výše) připojené ještě tři další součásky: nabíječka Li-Ion článku (do které zpětným směrem téměř žádný proud neteče), dále čidlo DS18B20 a nakonec samotný vysílač na 433 MHz. Správný přístup by byl změřit, kolik proudu tyto moduly v době nečinnosti (když procesor spí) skutečně odebírají a poté se rozhodnout, co s tím. Před lety jsem ovšem neměl pořádný miliampérmetr, takže jsem usoudil, že nejjistější bude je v době, kdy je nepotřebuju, prostě od přívodu napětí odříznout úplně. To jsem udělal poměrně jednoduchým a snad i sofistikovaným způsobem: přepojil jsem jejich napájení přímo na datové piny mikrokontroléru, které dokážu velmi jednoduše programově ovládat. Využívám při tom faktu, že logická jednička se na datovém pinu přepnutém do výstupního módu projeví jako téměř plné napájecí napětí, zatímco logická nula výstupní datový pin prostě nastaví na napětí 0 voltů.

Takovýto trik s napájením periférií přímo z procesoru je nutno dělat s maximální rozvahou. Datové piny mikrokontroléru jsou poměrně citlivé a například proti zkratu (nebo obecně průtoku proudu větším než malém) nejsou nijak chráněné. Každý z nich dokáže podle údajů výrobce dodávat či absorbovat proud až/jenom 40 mA. S tím je nutno počítat a proměřit si, kolik připojené obvody odebírají proudu ve své plné aktivitě (tj. u čidla teploty při její konverzi a u vysílače při vysílání). Proměřit jsem to ještě nestihl, ale díky tomu, že tento článek popisuje dva roky staré, funkční a osvědčené řešení, usuzuji, že je to v daném limitu 40 mA.

Díky tomu mohu jednoduše „zapínat“ napětí jdoucí do termočidla a do vysílače jen na nezbytně krátkou dobu a tak snad ušetřit další cenné mAh. Níže vidíte kousek kódu okolo měření teploty: zapnu napájení čidla (tím, že pin nazvaný ONEWIRE_POWER_PIN nastavím na hodnotu HIGH, takže se na něm objeví prakticky stejné napětí jako má v tu chvíli akumulátor napájející procesor), počkám 250 milisekund, aby se čidlo trošku probudilo a „nabilo“, požádám ho o změření teploty, teplotu vyčtu a okamžitě vypínám napájecí napětí nastavením toho ONEWIRE_POWER_PIN  na  LOW:

#define ONE_WIRE_BUS_PIN    2    // Data wire is plugged into port 2 on the Arduino
#define TX433MHZ_PIN        3    // Transmitter Arduino pin
#define ONEWIRE_POWER_PIN   4    // power for one wire bus
#define TX_POWER_PIN        5    // power for transmitter
#define LED_BLINK          13    // LED indicator of transmitting

    digitalWrite(ONEWIRE_POWER_PIN, HIGH);    // power the one wire bus
    delay(250);

    dprint("Requesting temperatures... ");
    sensors.requestTemperatures(); // Send the command to get temperatures

    // fetch address of device (to initialize parasitely powered device?) and read temperature
    // use -0.1 C to indicate error reading the temperature
    float tempC = sensors.getAddress(tempDeviceAddress, 0) ? sensors.getTempC(tempDeviceAddress) : -0.1f;

    digitalWrite(ONEWIRE_POWER_PIN, LOW);     // disable power to the one wire bus

Podobně drsně zacházím i s vysílačem na 433 MHz. Zapnu mu napájení nastavením pinu TX_POWER_PIN na hodnotu HIGH, přečtu aktuální napájecí napětí (= stav akumulátoru), odešlu naměřenou teplotu do základnové stanice, bliknu si LEDkou na 5 milisekund, odešlu naměřenou teplotu ještě svým vlastním protokolem pro sebe a pak ihned vypínám napájecí napětí do vysílače:

    // transmit the temperature
    digitalWrite(TX_POWER_PIN, HIGH);    // power the transmitter

    vcc = readVcc();
    boolean low_battery = (vcc < 3700);

    tx.send(tempC, low_battery, beep_on_first_tx);

    digitalWrite(LED_BLINK, HIGH);       // blink to indicate the transmit
    delay(5);
    digitalWrite(LED_BLINK, LOW);

    vwSendTempAndMore(tempC);

    digitalWrite(TX_POWER_PIN, LOW);     // disable power to the transmitter

Chyby, které ve zdrojovém kódu při psaní článku vidím: za prvé, měření teploty DS18B20 může trvat až 375 milisekund (mám nastavenou 11bitovou přesnost čidla DS18B20) a v tuto dobu procesor nemůže nic moc dělat než čekat. Bylo by nejlepší ho znovu na tu chvilku uspat – ale to by nám asi přestal napájet to čidlo, když je „chytře“ zapojeno přímo na jeho datový pin. Takže procesor žere proud při pouhém téměř půlsekundovém čekání na čidlo.

Druhá věc – proč měřím napětí ( readVcc()) v době, kdy už mám vysílač pod proudem? Mohl jsem ho změřit ještě předtím, než jsem zapnul „power to the transmitter“. Ono to měření netrvá dlouho, je to jen pár milisekund, ale v extrémním případě (třeba v nositelné elektronice typu smart watch) je každá taková neúsporná milisekunda drahá. Naštěstí v případě tohoto zařízení dobíjeného téměř denně ze solárního článku máme energie relativně dostatek. Je to vidět i na tom, že si troufnu krátce bliknout LEDkou standardně připojenou na pin 13, přestože na vysílač není běžně vidět. Při ladění se to blikání v momentu vysílání hodilo, v produkční verzi kódu bych ho mohl (a měl) vypnout.

Zároveň v kódu vidíte, že za nízké napětí považuji už 3,7 V. Základnová stanice mi v takovém případě zobrazí na displeji značku „nutno vyměnit baterii“, což mě vždy přiměje ke kontrole solárního panelu – jestli někam nezapadl. Během normálního léta se nestává, že by se akumulátor skutečně vybil, pokud je solární článek alespoň částečně vystavený slunečnímu záření. Mám tam stejně ještě solidní rezervu, protože Li-Ion článek se považuje za skutečně vybitý až při poklesu napětí k 3,0 V.

CS24_early

Mimochodem, když jsme znovu u měření proudu v řádech miliampér a mikroampér. Dave L Jones vymyslel a vyrobil skvělý mikroampérmetr. Tato krabička dokáže převést proud až v řádu nanoampér na napětí, které změříte běžným digitálním voltmetrem. Díky tomu můžeme měřit opravdu malé proudy i s levným vybavením. Tento šikovný přístroj bych si měl postavit a proměřit jím ty spotřeby výše zmíněných modulů. Ano, postavit, protože Dave uveřejnil návod, schéma i plošný spoj jako Open Source Hardware pod CC licencí, přestože zároveň hotový výrobek velmi úspěšně prodává.

Příště uveřejním kompletní zdrojový kód (jehož klíčové části bylo třeba po částech vysvětlit – o což se snažil tento článek) a dále ukážu, jak přijímat a dekódovat zprávy posílané bazénovým teploměrem, takže je možné je například ukládat do databáze či z nich nechat kreslit graf.

Byl pro vás článek přínosný?

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.