středa 18. března 2015

Tasker - "multitasking" na Arduinu

Poměrně rychle jsem přijal programovací styl navrhovaný Arduino IDE, tj. dvě hlavní funkce - setup() a loop(). Do setup() dám inicializační věci a do loop() dám samotné jádro programu, které provádí nějakou periodickou činnost. Dokud jde jen o jednu činnost, například blikání LEDkou v sekundovém rytmu, je to jednoduché: rozsvítím LED, počkám půl sekundy díky volání funkce delay(500), zhasnu, počkám další půlsekundu dík dalšímu volání delay(500) a je to:

void loop() {
    digitalWrite(13, HIGH);
    delay(500);
    digitalWrite(13, LOW);
    delay(500);
}

Bezva, ale co když potřebuji vykonávat činnosti dvě a navíc různé? Třeba během blikání LED chci ještě číst teploty z čidel. Anebo kreslit na displej. Anebo odpovídat na požadavky web klientů. Anebo tisíc jiných věcí. Co pak? Jak během delay(), kdy program stojí a čeká, vykonávat jinou činnost?

Když jsem pracoval na řídicím programu pro můj vytuněný domácí termostat, docela mě zarazilo, kolik chvílemi až protichůdných požadavků musí zvládat vyřizovat v podstatě naráz:
  • měřit teploty z obou větví digitálních čidel DS18B20, třeba jednou za tři sekundy
  • měřit teplotu a vlhkost z čidla DHT11, nesmí častěji než jednou za sekundu
  • podle teplot a dalších údajů spínat kotel, stačí jednou za pět sekund
  • řídit podsvícení displeje, to je potřeba nejméně dvakrát za sekundu
  • kontrolovat, jestli se někdo nedotkl displeje, to je potřeba co nejčastěji, aspoň 10x za sekundu
  • překreslovat displej, nejlépe přesně jednou za sekundu, pokud se ho někdo nedotkl, jinak dřív
  • sledovat požadavky na interní webserver, to stačí tak 2-3x za sekundu
  • občas ujistit watchdog, že ještě žiju a nemá mě zresetovat (aspoň jednou za 4 sekundy)
Mimochodem, stejné problémy vyvstanou třeba při řízení robotického auta (což mám se synem taky v řešení) - tam je potřeba číst údaje z různých snímačů (vzdálenosti, světla, rychlosti), poslouchat dálkové ovládání a zároveň neustále řídit motory, do toho sem-tam blikat blinkry, svítit světly, přehrávat hlasy či muziku a podobně.

Na tohle všechno by byl ideální nějaký multitasking/multithreading, ne? Po více než dvaceti letech strávených v multitáskových operačních systémech to k němu člověka tak nějak táhne.  Ono se to všechno sice dá nacpat do té funkce loop(), ale je potřeba u toho moooc přemýšlet a hlavně - nikdy nevolat delay()!

Takže můj termostat měl všechny výše uvedené činnosti napsané za sebou v loop() cyklu jednu za druhou, ale zároveň jsem si musel definovat hromadu proměnných, které si pamatují, kterou z těch výše popsaných událostí jsem kdy zavolal a pak je nutné kontrolovat, jestli už je čas ji zavolat znovu - a pokud ještě není, tak ji nevolat a pokračovat v cyklu dál.

Nemusím vysvětlovat, že zdrojový kód ty dodatečné podmínky a přeskoky docela znepřehledňují. Navíc čtení z digitálních čidel DS18B20 napájených parazitně je potřeba provádět vlastně nadvakrát - nejdřív je nutno požádat o změření teploty, pak počkat 750 milisekund (pro 12bitovou přesnost) a pak teprve přečíst samotné teploty z čidel. To je další komplikace.

A když jsem teď začal znovu rozvíjet ten můj termostat a chtěl jsem tam přidat další funkci, která by na definovaný interval, třeba 30 minut, změnila nějaké nastavení a po uplynutí času ho zase sama vrátila zpět (ano, jde o větrání), uvědomil jsem si, že už to dál nechci takto plácat. A začal jsem hledat nějakou knihovnu, která by toto spouštění úloh v čase řešila za mě.

Poměrně brzy jsem našel celou řadu hotových řešení přímo pro Arduino (či pro nejbližší ATMEL procesory), které všechny slibovaly modré z nebe a světový mír. Vlastně je tu všechny můžu vypsat, ať ušetřím jiným hledačům čas:
No, strávil jsem docela dost času procházením těch existujících řešení, našel v nich chyby v návrhu i implementaci a nakonec jsem se rozhodl: napíšu si vlastní!

Upřímně řečeno, většinou lidi znovuvynalézající kolo nepovažuju za nejmoudřejší, ale v tomto případě jsem si byl docela jistý, že žádné z 11 dostupných řešení nefunguje tak, jak potřebuju, a že tedy bude OK věnovat část mé životní energie na řešení dvanácté, napsané mně na míru. Stanovil jsem si přitom následující zásady:
  1. co nejjednodušší použití (NE C++ třídám, dědičnosti, složitému API, mnoha parametrům)
  2. co nejkratší implementace (aby bylo na první pohled zřejmé, že to funguje správně)
  3. co nejúspornější provoz (malá zabraná jak flash tak RAM paměť)
  4. co nejpružnější a nejmocnější vlastnosti (předávání parametrů do volaných funkcí, ideálně dynamické vytváření úkolů za běhu)
  5. 100% kompatibila s Arduinem (žádný vlastní časovač či přerušení)
  6. a samozřejmě bez chyb přetečení časovače po 49 dnech
Nakonec bylo napsání vlastního řešení rychlejší, než jsem čekal. Výsledný kód implementace se vejde na jednu obrazovku monitoru, pro použití stačí zavolat jednu jedinou funkci s navíc intuitivním jménem i parametry, v RAM zabírá jedna úloha jen 14 bajtů včetně parametru předávaného do volané funkce a dokonce je možné vytvářet další úlohy dynamicky za běhu!

Výsledek jsem nazval Tasker pro Arduino a ihned ho použil v mém termostatu. Špagetový kód z funkce loop() jsem rozdělil na jednotlivé malé funkce a volám je jako samostatné úlohy v jejich pravý čas. Zdrojový kód termostatu se tím krásně pročistil a výsledek funguje díky prioritám implementovaným v Taskeru mnohem lépe, než dříve (například vyhodnocení dotykové vrstvy má teď jednu z nejvyšších priorit, aby reakce na dotyk byla okamžitá - tohle mi dřív trochu zlobilo). Super!

Můj Tasker jsem s radostí uvolnil pro všechny na GitHubu: https://github.com/joysfera/arduino-tasker

Nechci zde opisovat či překládat anglicky psaný manuál viditelný hned na tom URL uvedeném výše, ale pár ukázek elegantního řešení typických problémů s mým Taskerem si neodpustím. Pro začátek obligátní blikání LEDkou - na čtyři řádky a bez delay():

Tasker tasker;
void blik(int) { digitalWrite(13, !digitalRead(13)); }
void setup() { tasker.setInterval(blik, 500); }
void loop() { tasker.loop(); }

A ještě třeba nekompletní ukázka možnosti čtení těch DS18B20 periodicky každé tři sekundy, a to bez jakéhokoliv čekání na cokoliv, postaveném na zřetězení úkolů díky možnosti dynamického přidávání nových úkolů za běhu:

Tasker tasker;
OneWire oneWire(4);
DallasTemperature sensor(&oneWire);
float g_teplota;

void ctiSensor(int) {
    g_teplota = sensor.getTempC(0);
}

void ctiTeplotu(int) {
    sensor.requestTemperatures();
    tasker.setTimeout(ctiSensor, 750);
}

void setup() {
    sensor.begin();
    sensor.setWaitForConversion(false);
    tasker.setInterval(ctiTeplotu, 3000);
    tasker.run();
}

void loop() { }

To mi připomíná, že bych měl vyvézt na GitHub i mé úpravy v knihovně DallasTemperature... Snad někdy brzy.

Lepší ukázka využití Taskeru a kompletní popis API je na URL výše. A samozřejmě nejlepší dokumentací je samotný zdrojový kód, o kterém si troufám tvrdit, že je přehledný a dobře čitelný. Tak si to užijte! :)

EDIT 2017/01/27:
Na přání jsem doplnil klíčový řádek do příkladu se čtením čidel od Dallasu, takže už to nebude blokovat. A víte co? Rovnou jsem tam doplnil i tři další řádky, aby ten program byl celý funkční.
Tento příklad najdete i v mém git repozitáři jako druhý example vedle MultiBlink.

36 komentářů:

  1. Super, mě osobně to zaujalo, žádné složité nastavování proměnných, nevadí přetečení časovače, priority..... Přestože jsem nováček, rychle jsem kód pochopil a naprogramoval řízení přímotopů a vzduchotechniky. Prostudoval jsem Váš kód a návod a jsou zde 3 oblasti kterým nerozumím. Chci Vás požádat o vysvětlení.

    1) po vypnutí topení, omezuji opětovné zapnutí až do uplynutí požadované doby. Chci tak omezit počet sepnutí relé. Jak v tomto případě využít Váš multitasking?

    Část kódu:

    if (atKuchyn <= (EEPROM.read(2) - hysterze) && EEPROM.read(1) != 0 && millis() >= casVypnutiKuchyn) // zapne topeni pokud je aktualni teplota nizsi nez pozadovana - hysterze a soucasne uplynula doba od posledniho vypnuti
    {
    digitalWrite (KUCHYN, HIGH); // zapne topeni
    }
    else if (atKuchyn >= (EEPROM.read(2) + hysterze)) // vypne topeni pokud je aktualni teplota vyzsi nez pozadovana + hysterze
    {
    digitalWrite (KUCHYN, LOW); // vypne topeni
    casVypnutiKuchyn = millis() + prodleva; //nastavi cas kdy lze nejdrive zapnout topeni
    }

    2) v multitaskingu nechám povoleny priority a nastavím více procesů s krátkou dobou opakování. Je zde riziko, že nebude zbývat čas na další činnosti. Co se stane s ostatními procesy? Je možné že se nikdy neprovedou?

    3) není potřeba všechny procesy časovat a je možné je ponechat v loop. Jak zajistit aby se tento kód provedl?

    Předem děkuji za odpověď.

    OdpovědětVymazat
    Odpovědi
    1. ad 1) Tasker nemusíte použít mermo-mocí všude - jsou věci, které jdou naprogramovat lépe i bez něj.

      ad 2) ano, možná, že se nikdy neprovedou. Mají-li nižší prioritu a není-li na ně čas, tak holt mají smůlu.

      ad 3) když nezavoláte tasker.run(), tak se normálně opakovaně volá loop() jak jsme z Arduina zvyklí. V něm máte svůj kód jako předtím, než jste poznal Tasker, a stačí tam přidat volání tasker.loop(). Mám to v jednom z příkladů použito.

      Vymazat
  2. Nebyl by příklad jednoduchého ovládání topení ?

    OdpovědětVymazat
    Odpovědi
    1. topim = (teplota < požadovaná + (topim ? hystereze : 0);
      digitalWrite(kotel, topim);

      Vymazat
    2. SUPER! Zatím jsem se C++ vyhýbal, ale když vidím to elegantní naplnění proměnné topim a ty neuvěřitelné možnosti s Arduinem, tak se asi budu muset velmi hluboce nad sebou zamyslet a něco s tím udělat.

      Vymazat
    3. Vřelé díky za vaše články. Mají hlavu a patu. Děkuji

      (topim ? hystereze : 0).....................tuhle část programu jsem nechápal, tak jsem si to vyzkoušel

      topim = (teplota < pozadovana + (topim ? hystereze : 0)) .....přičte k požadované hysterezi při vzestupu a to co je za : přičte k požadované při sestupu

      (topim ? hystereze : 0))
      teplota= 20 topim= 1 pozadovana= 24 hystereze= 3
      teplota= 21 topim= 1 pozadovana= 24 hystereze= 3
      teplota= 22 topim= 1 pozadovana= 24 hystereze= 3
      teplota= 23 topim= 1 pozadovana= 24 hystereze= 3
      teplota= 24 topim= 1 pozadovana= 24 hystereze= 3
      teplota= 25 topim= 1 pozadovana= 24 hystereze= 3
      teplota= 26 topim= 1 pozadovana= 24 hystereze= 3
      teplota= 27 topim= 0 pozadovana= 24 hystereze= 3
      teplota= 28 topim= 0 pozadovana= 24 hystereze= 3
      teplota= 28 topim= 0 pozadovana= 24 hystereze= 3
      teplota= 27 topim= 0 pozadovana= 24 hystereze= 3
      teplota= 26 topim= 0 pozadovana= 24 hystereze= 3
      teplota= 25 topim= 0 pozadovana= 24 hystereze= 3
      teplota= 24 topim= 0 pozadovana= 24 hystereze= 3
      teplota= 23 topim= 1 pozadovana= 24 hystereze= 3
      teplota= 22 topim= 1 pozadovana= 24 hystereze= 3
      teplota= 21 topim= 1 pozadovana= 24 hystereze= 3
      teplota= 20 topim= 1 pozadovana= 24 hystereze= 3
      (topim ? hystereze : 1))
      teplota= 20 topim= 1 pozadovana= 24 hystereze= 3
      teplota= 21 topim= 1 pozadovana= 24 hystereze= 3
      teplota= 22 topim= 1 pozadovana= 24 hystereze= 3
      teplota= 23 topim= 1 pozadovana= 24 hystereze= 3
      teplota= 24 topim= 1 pozadovana= 24 hystereze= 3
      teplota= 25 topim= 1 pozadovana= 24 hystereze= 3
      teplota= 26 topim= 1 pozadovana= 24 hystereze= 3
      teplota= 27 topim= 0 pozadovana= 24 hystereze= 3
      teplota= 28 topim= 0 pozadovana= 24 hystereze= 3
      teplota= 28 topim= 0 pozadovana= 24 hystereze= 3
      teplota= 27 topim= 0 pozadovana= 24 hystereze= 3
      teplota= 26 topim= 0 pozadovana= 24 hystereze= 3
      teplota= 25 topim= 0 pozadovana= 24 hystereze= 3
      teplota= 24 topim= 1 pozadovana= 24 hystereze= 3
      teplota= 23 topim= 1 pozadovana= 24 hystereze= 3
      teplota= 22 topim= 1 pozadovana= 24 hystereze= 3
      teplota= 21 topim= 1 pozadovana= 24 hystereze= 3
      teplota= 20 topim= 1 pozadovana= 24 hystereze= 3
      boolean topim;
      byte teplota=20;
      byte kotel=3;
      byte pozadovana=24;
      byte hystereze=3;

      void setup() {
      Serial.begin(9600);
      }

      void loop() {
      for (int i=20; i <= 28; i++){
      teplota=i;
      Serial.print("teplota= ");Serial.print(teplota);
      topim = (teplota < pozadovana + (topim ? hystereze : 2));
      Serial.print(" topim= ");Serial.print(topim);
      Serial.print(" pozadovana= ");Serial.print(pozadovana);
      Serial.print(" hystereze= ");Serial.println(hystereze);
      //digitalWrite(kotel, topim);
      delay (1000);
      }
      for (int i=28; i >= 20; i--){
      teplota=i;
      Serial.print("teplota= ");Serial.print(teplota);
      topim = (teplota < pozadovana + (topim ? hystereze : 2));
      Serial.print(" topim= ");Serial.print(topim);
      Serial.print(" pozadovana= ");Serial.print(pozadovana);
      Serial.print(" hystereze= ");Serial.println(hystereze);
      //digitalWrite(kotel, topim);
      delay (1000);
      }
      }

      Vymazat
  3. Je možné tasker nějak zastavit? Respektive pouze jeden task? Potřeboval bych to použít podobně jako Timer ve VB.NET. Ten jde zapnout/vypnout a i za běhu (já za běhu nepotřebuji) změnit interval.

    OdpovědětVymazat
    Odpovědi
    1. nejde, úmyslně. Tasky by musely vracet ID, aby to šlo. Původně jsem to zvažoval, ale dospěl jsem k přesvědčení, že by to vše příliš zesložitilo, a tak jsem to tam nedal. Většina těch ostatních knihoven to podporuje, ale jsou řádově složitější...

      Vymazat
    2. Mimochodem, někde v dokumentaci pro to doporučuji si ten task prostě přeskakovat, pokud ho člověk nepotřebuje. Tj. mít hned na začátku úlohy
      if (! running) return;
      a když task chci zastavit, tak nastavím running na false a je to.

      Vymazat
  4. dobry den studujem ako zaciatocnik vas prispevok , teoreticky to chapem, ale v praxi mam vyrobeny kod na spinanie teploty čerpadla, ale nedari sa mi nastavit hysterezu povedzme 3 stupne celzia. viete mi poradit ? vopred dakujem

    tu je moj kod :


    //program pre kotol UK , LCD 16x2 . jednokanálové relé, snímač teploty Dallas temperature waterproof DS18B20
    //lcd je zapojené na I2C zbernici, výstupy zbernice komunikacny port SCL a SDA,GND , 5V
    //snímač teploty je cez modul zapojený na gnd, 5v, a pin 2
    //jednokanálové relé je zapnuté na gnd, 5v a pin 8
    //20.12.2016

    #include
    #include "LCD.h" // For LCD
    #include "LiquidCrystal_I2C.h" // Added library*

    //Set the pins on the I2C chip used for LCD connections
    //ADDR,EN,R/W,RS,D4,D5,D6,D7
    LiquidCrystal_I2C lcd(0x3F,2,1,0,4,5,6,7); // 0x27 0x3F is the default I2C bus address of the backpack-see article
    int DS18S20_Pin = 2; //DS18S20 Signalny pin na digital 2, takto napojime dalsie cidlo bez modulu na pin 3 (2,3);

    int relay =8;

    //Temperature chip i/o
    OneWire ds(DS18S20_Pin); // zapojene na digitalny pin 2

    void setup(void) {

    // Set off LCD module
    lcd.begin (16,2); // 16 x 2 LCD module
    lcd.setBacklightPin(3,POSITIVE); // BL, BL_POL
    lcd.setBacklight(HIGH);

    Serial.begin(9600);
    pinMode(8, OUTPUT); //slovensky: nastav rele pin 8 ako výstup
    }

    void loop(void) {
    float temperature = getTemp();



    //float tempF = (temperature * 9.0)/ 5.0 + 32.0;
    Serial.println(temperature); // názov volania cidla
    lcd.setCursor (0,0); // horný riadok displeja, teplota čerpadlo viz riadok niž
    lcd.print("Teplota Cerpadlo");




    if (temperature >25) { //podmienka pre zapínanie čerpadla ak je teplota vyššia, ako 22 st C zapni
    digitalWrite(8,HIGH); //pust napatie na pin 8
    lcd.setCursor (7,1); //vypíš na druhom riadok od pozície 7 ON viz riadok niž druhy riadok zacina 15.1
    lcd.print(" ON");
    } else {
    digitalWrite(8,LOW); // ked skonci horna podmienka teplota vyššia ako 22 st C teda je nižšia ako 22 st C vypne sa repadlo
    lcd.setCursor (7,1);
    lcd.print(" OFF"); // tu potrebujem prida histerziu zap 22 vyp 20 !!!!!!!

    }

    delay(100); //spomal opakovanie aby bol citatelny vysledok volanej hodnoty teploty 1000= 1s

    }


    float getTemp(){
    //vracia teplotu z čidla v stupnoch celzia



    byte data[12];
    byte addr[8];

    if ( !ds.search(addr)) {
    //žiadne ďalšie senzory na reťazce, resetovanie vyhľadávanie
    ds.reset_search();
    return -1000;
    }

    if ( OneWire::crc8( addr, 7) != addr[7]) {
    Serial.println("CRC is not valid!");
    return -1000;
    }

    if ( addr[0] != 0x10 && addr[0] != 0x28) {
    Serial.print("Device is not recognized");
    return -1000;
    }

    ds.reset();
    ds.select(addr);
    ds.write(0x44,1); // start conversion, with parasite power on at the end

    byte present = ds.reset();
    ds.select(addr);
    ds.write(0xBE); // Read Scratchpad


    for (int i = 0; i < 9; i++) { // we need 9 bytes
    data[i] = ds.read();
    }

    ds.reset_search();

    byte MSB = data[1];
    byte LSB = data[0];

    float tempRead = ((MSB << 8) | LSB); //using two's compliment
    float TemperatureSum = tempRead / 16;
    lcd.setCursor (0,1);
    lcd.print(TemperatureSum);
    lcd.println("*C..");

    return TemperatureSum;


    // pridat dalsie cidlo potrebujem !!!!
    // ako kalibrovat ????? ked premeriava
    // nastavit len 1 desatinne cislo, alebo ziadne !!!!



    }

    OdpovědětVymazat
    Odpovědi
    1. jak zacházet s hysterezí jsem napsal v komentáři výše již 24. června 2015 v 13:04.

      Vymazat
  5. Dobrý den, bude Tasker funkčni i na desce Wemos D1 ??

    A druhý dotaz, při kompilaci příkladu MultiBlink (s deskou arduino uno) vyhodí ide (ver.1.8.) chybu :
    exit status 1
    'blink1' was not declared in this scope

    Kde jsem asi udělal chybu? Knihovna Tasker je vidět v seznamu, ale není instalovaná ze zipu, jen jsem ji kopiroval dle instrukcí.

    OdpovědětVymazat
    Odpovědi
    1. Ano, bude tam skvěle funkční. Mám v plánu ho ještě rozšířit o takovou drobnost, která jej výrazně vylepší. Zatím asi bude potřeba volat yield() ručně.

      "blink1" not declared je chyba nového Arduino IDE, kterou jsem už v příkladu opravil. Stáhněte si novou verzi z githubu.

      Vymazat
    2. Tak už jsem tam doplnil i ten yield(), hned na dvě místa.

      Vymazat
  6. Petře, velké poděkování za Tasker
    vynalézal jsem kolo a natrefil na Váš Tasker
    Pokud ještě budete ochoten vyvézt úpravy v knihovně DallasTemperature, tak Vám snad osobně dovezu flašu něčeho dobrého

    OdpovědětVymazat
    Odpovědi
    1. beru Vás za slovo :) Podívám se, co jsem do knihovny DallasTemperature přidal před těmi třemi lety, jestli to tam dnes ještě není, a pokud ne, tak to vyvezu :)

      Vymazat
    2. tak jsem obešel celou DallasTemperature knihovnu a zůstal jen na úrovni OneWire, což je základní práce s DS čidly, ale za Tasker si odměnu zasloužíte. Určitě se někde sejdeme :-)

      Vymazat
    3. já se k tomu dostanu, jen mi dejte chvilku, mám toho rozdělaného moc!

      Vymazat
    4. Tak jsem právě vyvezl své 3,5 roku staré úpravy knihovny DallasTemperatures. Moji verzi najdete na https://github.com/joysfera/Arduino-Temperature-Control-Library a má navíc kromě nějakých čistek především možnost podržet libovolný počet čidel v paměti, takže se pak při každé operaci nemusí znovu hledat na sběrnici.
      Nicméně tyto moje úpravy nejsou pro spolupráci s Taskerem nezbytné, je to jen pro pohodlnější a rychlejší práci s hodně čidly na sběrnici.

      Vymazat
  7. Díky, skvěle hutný kód. Co jsem se ale natrápil s yield()! Ať jsem googlil, jak googlil, yield se zmiňuje spíš v iteracích a v jednom případě jsem to našel u multitaskingu - ovšem tam to využívalo ještě třídu definovanou v nějaké hlavičce. Než jsem zjistil, že zdejší yield() je z arduinové hlavičky, už mi z toho cukalo v koutku... :-)

    OdpovědětVymazat
    Odpovědi
    1. yield() je ve většině případů definovaný jako no-op, prázdnota, žádná instrukce. Používá se u větších systémů, než je Uno (např. u HW, který musí zároveň obsluhovat komunikaci s něčím), kde vlastně předává čas operačnímu systému. Takže i když na Arduino Uno to nemá žádný význam, raději jsem to zahrnul, kdyby tu knihovnu použil někdo na těch nových ARMových potvorách, nebo třeba na ESP8266.

      Mohl jsem použít místo toho delay(0), ale to by bylo asi ještě víc matoucí.

      Vymazat
  8. ... dodatek: Ovšem v arduino.h je pouze deklarace yield(), takže v kódu nemá žádný smysl.

    OdpovědětVymazat
    Odpovědi
    1. naopak, v kódu má maximální smysl, vysvětlil jsem to výše.

      Vymazat
  9. Díky za vysvětlení. Pochopil jsem, že jste yield použil z "ideového" důvodu. Jen mě nejdřív mátla jeho praktická nadbytečnost v daném kódu, tedy jen s danou hlavičkou. Díky vám jsem do problému více pronikl.

    OdpovědětVymazat
    Odpovědi
    1. To není ideový, ale ryze praktický důvod. Tasker lze použít na všech Arduino platformách, přičemž řada z nich potřebuje provozovat třeba WiFi či jiné periférie a potřebuje, abyste jim na chvíli "půjčil" CPU. A to přesně dělá yield().
      Jinými slovy, Tasker bez yield() na třeba ESP8266 způsobí do 8 sekund restart celého počítače, protože watchdog znervózní, že 8 sekund nedostal půjčený CPU.

      Vymazat
  10. Mimochodem, požádal jsem o zahrnutí Taskeru do seznamu Arduino knihoven, takže ho možná brzy najdete v Arduino Library Manageru :-)

    OdpovědětVymazat
  11. Ahoj, mohl bych tě požádat o radu jak se zbavit při dotazu na teplotu sensors.requestTemperatures() toho nepříjemného zaseknutí na cca 750ms? Jsem začátečník, můj názor je že tohle nemá s taskerem co dočinění (tasker to jen načasuje) ale potřebuju k tomu tvoji úpravu kniovny DallasTemperature. Je to tak nebo se mýlím?
    Díky, Tomáš :)

    OdpovědětVymazat
    Odpovědi
    1. Než uveřejním svoji tři roky starou verzi, musím se podívat, co je nového v aktuální "veřejné" verzi, porovnat, a případně moje změny přidat do té "veřejné" verze a pak zveřejnit tu svoji - to abych neštěpil vývoj.

      Vymazat
    2. Tomáši, tu zatracenou DallasTemperature nepotřebujete.
      Stačí jen OneWire knihovna a na její úrovni si ubrat rozlišení z 12bit třeba jen na 10bit.
      A už jste jen na 200ms (187.5)

      Vymazat
    3. zkracovat ten interval je zbytečné a nikam nevedoucí - správné je právě použít Tasker, započít konverzi teploty a jít dělat něco jiného. Tasker sám se po 750 ms vrátí a teplotu vyčte - tak, jak to mám v příkladech.

      Vymazat
    4. Jasně, takhle to Tasker dělá...
      Ale obvykle opravdu stačí teplota na 10bit

      Vymazat
    5. Tomáši, tak jsem právě vyvezl své 3,5 roku staré úpravy knihovny DallasTemperatures. Moji verzi najdete na https://github.com/joysfera/Arduino-Temperature-Control-Library a má navíc kromě nějakých čistek především možnost podržet libovolný počet čidel v paměti, takže se pak při každé operaci nemusí znovu hledat na sběrnici.
      Nicméně tyto moje úpravy nejsou pro spolupráci s Taskerem nezbytné, je to jen pro pohodlnější a rychlejší práci s hodně čidly na sběrnici.

      Vymazat
    6. Ještě jedna věc (ta důležitá, tj. odpověď na tvoji půvoní otázku): to nepříjemné zaseknutí se má vypnout pomocí setWaitForConversion(false) - doplnil jsem to do příkladu v blogpostu.

      Vymazat
  12. Právě jsem zeditoval příklad v blogpostu tak, že je plně funkční, a zároveň jsem ho doplnil i mezi příklady do knihovny na githubu.

    OdpovědětVymazat