OK

Při poskytování služeb nám pomáhají soubory cookie. Používáním našich služeb vyjadřujete souhlas s naším používáním souborů cookie. Více informací

Úvodní stránka » Arduino » Arduino v příkladech - II.díl

Arduino v příkladech - II.díl - Měření objemu nádrže a zobrazení na progressbaru

Pojďme se podívat na konkrétní příklad využití čtení na analogovém pinu Arduino. Ukážeme si, jak změřit objem nádrže pomocí tlakového čidla, zobrazení objemu vody v nádrži na progres baru a samozřejmě i na LCD 16x2.

Než začneme projekt vůbec připravovat, je zapotřebí určit, jakým čidlem budeme tlak měřit a pak si udělat úkoly z fyziky, geometrie a matematiky.

Pro naše účely využiji tlakové čidlo 5VDC 0-0.15 psi (0,103MPa), výstup 0.5-4.5VDC. Na ebay je za nějakých 450,- Kč.

Začneme geometrií a matematikou. Počítat můžeme nádrž libovolného rozměru, čtvercovou, obdélníkovou, kulatou na výšku, kulatou naležato a cokoliv jiného. My zůstaneme u prvních třech a kulatou naležato vynecháme, protože se zde jedná o výpočet objemu kruhové úseče a to je na samostatný článek.

Pro ty co nechtějí vzpomínat na matiku ze základní školy: objem vody v nádrži nádrže vypočítáme – plocha * výška hladiny. Pro nádrž o rozměrech 1x1m a výšce hladiny 30 cm platí 1*1*0,3 = 0,3m3. To je 0,3 * 1000 = 300 litrů.

Tlakové čidlo budeme používat pouze k určení výšky hladiny. Pracovat budeme s MPa. Z fyziky víme, že 0,1MPa odpovídá 10 m výšky vodního sloupce, tedy čidlo dokáže změřit výši hladiny do 10,3 m. Tedy 0.5V = 0 m, 4.5V = 10,3 m. Pokud to budete chtít opravdu přesně, tak je to 10,546 m. Nám ale stačí základní přepočet.

Tím jsme si určili základní vstupní údaje a teď jdeme počítat. Vypočítáme rozsah čtení / referenční napěrí * min nebo max výstupní hodnotu čidla:
Minimální hodnota analogového čtení: 1024/5*0,5 = 102,4
Maximální hodnota analogového čtení: 1024/5*4,5 = 921,6

Rozsah měření čidla 0 – 10,3 m nám bude arduino bude reprezentovat hodnotami v rozsahu 102 – 922 jednotek.

Aby progres bar fungoval jak má, je zapotřebí určit maximální výšku hladiny, kterou nám bude zobrazovat jako 100% objemu vody v nádrži. Pro náš příklad využijeme nádrž, kde je maximální výška hladiny 1 m. Vypočteme maximální hodnotu analogového čtení pro tuto konkrétní nádrž. 922-102 = 820/10,3 = 79 * 1 = 79 + 102 = 181.

Z výpočtu je zřejmé, že při využití tohoto čidla bude rozsah čtení analogového portu v rozmezí 79 jednotek. V praxi nám jedna jednotka bude reprezentovat 0,01266 m, 1,266 cm. Pokud použijeme nádrž o rozměrech 1x1 m, bude jedna jednotka odpovídat 12,66 litru.

V článku "Měření napění na analogovém pinu a zobrazení na LCD 16x2" jsme si popsali logiku měření na analogovém pinu. Předchozí výpočet platí při referenčním napětí 5V. Pokud referenční napětí snížíme, zvýšíme i přesnost měření. Abych Vám ušetřil práci, bude výpočet rozsahu provádět Arduino. Stačí do programu doplnit proměnné.

UPOZORNĚNÍ – pokud snížíte referenční napětí např. na 1.1V a do analogového pinu pošlete 5V, možná se nestane nic, v lepším případě zničíte analogový pin, v horším se zakouří s celého Arduina.

Jen pro pořádek si vypočítáme rozlišení čidla při snížení referenčního napění:

  • referenční napětí 3.3V, počítané rozmezí 155-1396 jednotek (pozor, jsme mimo rozsah), jedna jednotka = 7,38 l
  • referenční napětí 1.1V, počítané rozmezí 465-4189 jednotek (pozor, jsme mimo rozsah a teď už opravdu hodně), jedna jednotka = 2,4 l (při výšce hladiny 1 m je rozsah měření do 872 jednotek a lze tedy použít)

Z příkladů je vidět, že snížením referenčního napětí u čidel s velkým rozsahem, můžeme zlepšit přesnost měření. Otázkou je, zda to bude fungovat. Narážíme velikost na rozlišení čidla, které může být tak veliké, že bude měřit v několika jednotkách a tak v praxi jsme tam, kde jsme byli na začátku. Bohužel, levná čidla z ebay tento údaj obvykle neuvádějí a nezbývá než metoda pokus omyl.

Náš příklad zůstane u původního modelu. Čidlo nahradíme otočným potenciometrem a snížíme rozsah čidla na 0,01 MPa tak, aby se dalo pracovat s potenciometrem a progres bar nahradíme samostatnými diodami.

Potřebné součástky:

Zapojení:

Program použijeme z článku "Měření napění na analogovém pinu a zobrazení na LCD 16x2" a doplníme ho o pár řádků kódu a debugSerial, který nám zobrazí výchozí hodnoty na serial monitoru.

Žádnou další knihovnu nepotřebujeme.

//------------------------------------------------------------------
// inicializace zakladnich knihoven
#include <Wire.h>               // knihovna pro komunikaci I2C
#include <LiquidCrystal_I2C.h>  // knihovna pro komunikaci s diplejem
//------------------------------------------------------------------
// nastaveni nazvu pinu
#define inputVoltage   A0     // vstup pro merení napeti
// ostatní piny si popiseme, ale nechame zakomentovane
// #define SDA      A4       // komunikace I2C displej
// #define SCL      A5       // komunikace I2C displej
// #define LED_1     8       // pin pro LED
// #define LED_2     9       // pin pro LED
// #define LED_3    10       // pin pro LED
// #define LED_4    11       // pin pro LED
// #define LED_5    12       // pin pro LED
//------------------------------------------------------------------
// promenne ktere je nutno zadat
const float analog_reference = 5.0; // nastaveni referencniho napeti V
const float min_voltage = 0.5; // minimalni napeti vystupu cidla V
const float max_voltage = 4.5; // maximalni napeti vystupu cidla V
const float max_pressure = 0.01; // maximalni merena hodnota cidla v MPa (0,1MPa = 10m vodniho sloupce)
const boolean tank_shape = true; // tvar nadrze - ctverec a obdelnik = true, kruh = false
const float tank_x = 1; // ctvercova nadrz rozmer a v metrech
const float tank_y = 1; // ctvercova nadrz rozmer b v metrech
const float tank_D = 1; // v pripade kruhove nadrze prumer v metrech
const float max_surface = 1; // maximalni vyska hladiny v metrech
const byte numberLED = 5; // pocet diod progres baru - v nasem prikladu je jich 5
const int timeout = 500; // prodleva mezi ctenim v milisekundach
const boolean debugSerial = true; // povoli nebo zakaze zobrazeni na serial monitoru
//------------------------------------------------------------------
// ostatni promenne
int value; // promenna pro ulozeni hodnoty cteni z analogoveho portu
int lastValue; // promenna pro ulozeni predchozi hodnoty cteni z analogoveho portu
unsigned long readingTime; // cas posledniho cteni
int min_value; // vypoctena hodnota spodniho rozsahu mereni
int max_value; // vypoctena hodnota horniho rozsahu mereni
float tank_area; // plocha nardze
float tank_capacity; // objem nardze
int water_volume; // aktualni objem nadrze
byte lightsLED; // pomocna promenna pro vypocet rozsvicenych LED
boolean error = false; // nastaveni pro zobrazeni chyby
//------------------------------------------------------------------
//nastaveni nazvu, adresy a typu displeje
LiquidCrystal_I2C LCD(0x27, 16, 2); // nazev, adresa, pocet znaku na radku, pocet radku
//------------------------------------------------------------------
// inicializace programu
void setup() {
  LCD.init(); // inicializujeme displej
  LCD.backlight(); // rozsvitime pozadi
  // vypocet minimalni merene hodnoty = prazdna nadrz
  min_value = (1024 / analog_reference) * min_voltage;
  // vypocet maximalni merene hodnoty = plna nadrz
  max_value = (((((1024 / analog_reference) * max_voltage) - min_value) / (max_pressure * 100)) * max_surface) + min_value;
  if (max_value > 1023) { // kdyz bude rozsah mimo povolene hodnoty
    LCD.setCursor (1, 0); // nastavime pozici kurzoru na 2 misto 1 radku
    LCD.print("ERROR SETTINGS"); // na displeji zobrazime text
    return; // a zastavime program
  }
  else { // jinak pripravime displej pro dalsi zpracovani
    LCD.setCursor (0, 0); // nastavime pozici kurzoru na 1 misto 1 radku
    LCD.print("Objem:"); // na displeji zobrazime text
    LCD.setCursor (0, 1); // nastavime pozici kurzoru na 1 misto 2 radku
    LCD.print("Vyuziti:"); // na displeji zobrazime text
  }
  // vypocteme plochu nadrze
  if (tank_shape) tank_area = tank_x * tank_y; // kdyz se jedna o ctvercovou nadrz
  else tank_area = 3.14 * ((tank_D * tank_D) / 4); // kdyz se jedna o kruhovou nadrz
  // vypocteme objem nadrze v L
  tank_capacity = tank_area * max_surface * 1000;
  // nastaveni vystupu
  pinMode(8, OUTPUT);
  pinMode(9, OUTPUT);
  pinMode(10, OUTPUT);
  pinMode(11, OUTPUT);
  pinMode(12, OUTPUT);
  //------------------------------------------------------------------
  if (debugSerial) { // pokud je povoleno zobrazeni
    Serial.begin(9600);
    Serial.print(F("Min cteni: "));
    Serial.println(min_value);
    Serial.print(F("Max cteni: "));
    Serial.println(max_value);
    Serial.print(F("Plocha nadrze: "));
    Serial.print(tank_area);
    Serial.println(F(" m2"));
    Serial.print(F("Objem nadrze: "));
    Serial.print((unsigned int)tank_capacity);
    Serial.println(F(" l"));
    Serial.print(F("Presnost cteni: "));
    Serial.print(tank_capacity / (max_value - min_value));
    Serial.println(F(" l"));
  }
}
//------------------------------------------------------------------
// hlavni smycka programu
void loop() {
  // kazdych 500 milisekund (timeout) provedeme funkci
  if ((unsigned long)(millis() - readingTime) >= timeout) { // pouzivame misto delay(); cekame na uplynuti timeout
    readingTime = millis(); // nastavime novy cas pro pristi zpracovani

    lastValue = value; // ulozime predchozi hodnotu cteni
    value = analogRead(inputVoltage); // precteme hodnotu analogoveho portu A0
    // zkontrolujeme zda je cteni v povolenem rozsahu
    if (min_value <= value && max_value >= value) { // pokud ano pokracujeme ve cteni
      // overime zda je cteni jine
      if (lastValue != value) { // pokud ano aktualizujeme displej a progres bar
        // zobrazime objem vody v nadrzi
        water_volume = map(value, min_value, max_value, 0, tank_capacity); // vypocteme objem
        LCD.setCursor (6, 0); // nastavime pozici kurzoru na 7 misto 1 radku
        LCD.print("          "); // vymazeme zobrazeny text
        LCD.setCursor (6, 0); // nastavime pozici kurzoru na 7 misto 1 radku
        LCD.print((String)water_volume + " l"); // na displeji zobrazime hodnotu
        // zobrazime vyuziti nadrze
        water_volume = map(value, min_value, max_value, 0, 100); // vypocteme objem
        LCD.setCursor (8, 1); // nastavime pozici kurzoru na 9 misto 2 radku
        LCD.print("        "); // vymazeme zobrazeny text
        LCD.setCursor (8, 1); // nastavime pozici kurzoru na 9 misto 2 radku
        LCD.print((String)water_volume + " %"); // na displeji zobrazime chybu
        if (error) { // kdyz byla drive chyba, zhasni diody
          error = false; // nastav novou vychozi hodnotu
          for (byte i = 0; i < numberLED; i++) { // to je tu jenom na ukazku
            digitalWrite(8 + i, LOW);
          }
        }
        // aktualizujeme diody progres baru
        lightsLED = map(value, min_value, max_value, 0, numberLED + 1);
        for (byte i = 0; i < lightsLED; i++) { //rozsvit diody
          digitalWrite(8 + i, HIGH);
        }
        for (byte i = lightsLED; i < numberLED; i++) { //zbytek zhasni
          digitalWrite(8 + i, LOW);
        }
      }
    }
    else { // jinak zobrazime chybu
      if (!error) { // pokud se jedna o prvni chybu zhasni progres bar
        error = true; // nastav novou vychozi hodnotu
        for (byte i = 0; i < numberLED; i++) {
          digitalWrite(8 + i, LOW);
        }
        LCD.setCursor (6, 0); // nastavime pozici kurzoru na 7 misto 1 radku
        LCD.print("          "); // vymazeme zobrazeny text
        LCD.setCursor (6, 0); // nastavime pozici kurzoru na 7 misto 1 radku
        LCD.print("ERROR"); // na displeji zobrazime chybu
        LCD.setCursor (8, 1); // nastavime pozici kurzoru na 9 misto 2 radku
        LCD.print("        "); // vymazeme zobrazeny text
        LCD.setCursor (8, 1); // nastavime pozici kurzoru na 9 misto 2 radku
        LCD.print("ERROR"); // na displeji zobrazime chybu
      }
      // a pak pouze blikame progres barem
      for (byte i = 0; i < numberLED; i++) {
        digitalWrite(8 + i, !digitalRead(8 + i));
      }
    }
  }
  // protoze na nic necekame, tady muzeme v realnem case provadet dalsi cinnosti
}

Těch řádků přibylo poněkud více.

Program si veškeré potřebné hodnoty pro svůj chod vypočítá hned po spuštění.

Oproti původnímu programu je jinak pojatá aktualizace displeje. Každý zápis trvá cca 12 milisekund. Tvůrce knihovny rozhodně časem nešetřil, nám to nyní nevadí, alespoň to funguje spolehlivě. Hned na začátku je do displeje odesláno základní zobrazení. Jednak ušetříme čas a pak zabráníme nepříjemnému blikání, kterého jste si v minulém díle jistě všimli. Pak nepoužíváme funkci LCD.clear(), a v průběhu programu mažeme jen to co potřebujeme.

Pak je zde navíc debugSerial. Tato část programu Vám při startu vypíše základní nastavení, v praxi slouží pouze k ladění programu, jinak k ničemu není, jenom zabírá místo. Pro běh programu ji lze vymazat.

Přibylo zde množství proměnných, které je nutné zadat vždy pro konkrétní projekt.

Přímo do spuštění programu je přidána funkce, která při chybném zadání na displeji zobrazí chybu nastavení a program zastaví.

A pak už je jenom běh. Pokud hodnota čtení odpovídá zadanému rozsahu, zobrazí se data na displeji a rozsvítí patřičné LED na progres baru. Aby stále neprobíhal zápis a aktualizace displeje, ke změně dat dojde pouze, pokud se hodnota čtení liší od toho předchozího. Pokud hodnota čtení neodpovídá zadanému rozsahu, rozblikáme progres bar a na displeji zobrazíme chybu.

Není nad to, když chráníme sami sebe, před sebou samými.

Závěr:

Přesnost měření závisí na rozsahu měření čidla. Čím blíže bude horní mez čidla maximální měřené hodnotě, tím přesnější bude měření. Dá se sehnat čidlo s rozlišením 0-0,01 MPa, ideální pro nádrž o výšce hladiny do 1 m. Bude stát okolo 7 tis. Kč. Otázkou je, zda to za to stojí.

Pokud budete měřit rozměrnější nádrže, je přesnější nejdříve spočítat výšku vodního sloupce a pak teprve vynásobit plochou nádrže.

Jsou i jiné metody měření, např. ultrazvukovým čidlem. Je to levné a i když to tak nevypadá, oproti tlakovému čidlu nespolehlivé a nepřesné.

V příštím díle o analogovém čtení se podíváme, jak pomocí dvou tlačítek a otočného potenciometru změníme údaje na displeji a nastavíme hodnoty, které uložíme do paměti tak, aby tam zůstaly i po vypnutí Arduina. Tam také konečně uvidíte i omezení poskakování analogového čtení. Předtím si odskočíme na jiná témata, která pro tento díl budeme potřebovat.

JB

jaroslav.bohac@arduinotech.cz

Přidat komentář

Zvýrazněné položky jsou povinné.

Přehled komentářů

  1. drobná korekce (Svoboda, 25.4.2016 9:13:36) Odpovědět

    Omlouvám se, ale aby se začátečníci neučili nepřesnosti viz
    "Maximální hodnota analogového čtení: 1024/5*4,5 = 921,6"
    Asi by to mělo být (1024/5*x)-1, jinak 5V nikdy nenaměří.
    A ještě to trochu zamotám. Arduino např při napájení přes USB kabel nemá vždy 5V naměřil jsem už i mezi 4,7-4,8V a to by pak byla trochu jiná čísla
    a z počítače na USB naměřím 5,1V-4,9V, kabel je 1,5m
    Doufám že jsem Vás tím upozorněním nenaštval
    s pozdravem
    J.Svoboda

    JB:
    Určitě se zlobit nebudu. Spíše Vám děkuji že čtete opravdu pozorně. Ten vzorec je správně – nepočítáme napětí, ale určujeme rozsah, který je dán dílky – tedy počtem možných dosažených hodnot a těch je 1024. Pokud by jste to použil při přímém výpočtu, skutečně by jste 5 V nikdy nedosáhl. Tady je také potřeba brát v úvahu počet desetinných míst, se kterými Arduino počítá, tady je to úplně pase. Jinak můj počítač na USB standardně dává 4,8V, při zatížení 300 mA 3,7V. Arduino žije, diody blikají, ale měřit analogové piny se s tím nedají ani přibližně. Jinak píši to i v článku, když budete mít napětí na Arduino 4,2V, tak 4,2V se vlastně rovná 5V, tedy napětí 4,2V = 1023 a celé měření pořádně ujíždí a nic s tím neuděláte. Prostě to chce stabilní zdroj – já používám laboratorní, nastavuji ho na 5,1V a výsledky jsou opravdu přesné a měření v čase nekolísá.

    S pozdravem,
    JB


TOP produkty

NodeMCU s ESP8266

NodeMCU s ESP8266
350 Kč s DPH

Arduinotech GSM shield

Arduinotech GSM shield
877 Kč s DPH

Kontakt

Ing. Petr Foltýn
Kunčice pod Ondřejníkem 814, 73913
TOPlist