View on GitHub

arduino-deti

Zápisky z našich domácích Arduino projektů

← zpět na zápisky z Arduino projektů

Teploměr

Teploměr pro měření teploty vzduchu se zobrazováním hodnoty na segmentovém displeji. Celkem náročnější úloha, záleží, kolik nových věcí najednou se dítě učí. Pokud je vše nové, tak to může být opravdu těžké (segmentový displej, posuvný registr, teploměr). Je možné provést jednoduchou variantu pouze s teploměrem, který vypisuje hodnoty např. na konzoli. A nebo kombinovat s jiným typem displeje (jednodušší ovládání).

Co je potřeba umět

Foto

Hardware

Jak to funguje

Teploměr LM35

Parametry:

Teploměr má 3 nožičky, napájení po stranách (+Vs, GND) a výstupní hodnotu na Vout. Ve specifikaci jsem napoprvé nějak přehlédl popisek k rozložení nožiček a zapojil jsem obráceně polaritu. Naštěstí jen na chvíli.

LM35

Při pohledu zespodu je +Vs vlevo a GND vpravo :-) Hodnota Vout pak uprostřed.

Teploměr vrací 10 mV/°C, rozsah analogového vstupu Arduina je 0-5V, vzorkovaných do 1024 hodnot. Z analogového pinu přečteme hodnotu, tu je nejdříve potřeba přepočítat na napětí, které vrací teploměr.

napětí na výstupu teploměru = analogový pin vrací /1024 * 5.0

protože teploměr vrací 10 mV na 1 °C, převedeme dále

teplota = napětí na výstupu teploměru / 0,01

výsledný přepočet je pak

teplota = (analogový pin vrací /1024 * 5.0) / 0,01

nebo také podstatně slavnější rovnice

teplota = (5.0 * analogový pin vrací * 100.0) / 1024;

Pokud bychom chtěli zvýšit trochu přesnost teploměru, snížíme referenční napětí z 5 V na 1,1 V. Teploměr tak bude měřit pouze v rosahu 0-110 °C, ale s přesností na desetinky - tedy v rámci možností, protože sám LM35 má přesnost 0,5 °C. Výpočet pak vypadá takto:

teplota = (1.1 * analogový pin vrací * 100.0) / 1024;

V kódu je pak potřeba v metodě setup() nastavit analogReference(INTERNAL).

Segmentový displej

Jde o hromadu LED, které jsou uspořádány do nějakého obrazce. Obvykle hovoříme o 7 segmentovém displeji (ale vlastně je 8 segmentový, protože máme tečky). Obvykle má displej kromě napájení piny, kterými vybíráme, kterou číslici chceme zrovna napájet. Tady se displeje dělí na 2 druhy - se společnou katodou a společnou anodou. Tedy je nejdříve potřeba zjistit, jaký displej držíme v ruce, podle toho pak bude zapojení a programování vypadat.

3461BS je segmentovka se společnou anodou (ve specifikaci je to strana 39). To znamená, že výběrový pin přivádí HIGH a na druhý konec vybraných LED potřebujeme přivést LOW, aby se rozsvítily respektive HIGH, aby ne (u displeje se společnou katodou je to právě naopak). Protože Arduino nemá tolik pinů, využijeme posuvný registr, který vždy nejdříve nakrmíme všemi nulami a jedničkami pro jednotlivé LED a následně ho necháme, aby je pro nás rozsvítil.

Tady jen poznámka ke kódu níže - v konstantě pro jednotlivé číslice máme, že hodnota 1 = LED svítí, ale výše jsem uvedl, že aby svítila, musí mít na anodě HIGH a na katodě LOW. A protože naše LED už HIGH na svou anodu “dostaly”, musíme jim pro rozsvícení dodat LOW na katodu. Tedy nulu do posuvného registru tam, kde mají svítit. Proto se v kódu objevuje binární negace v podobě ~, která nám přehodí všechny 0 na 1 a obráceně. Pro displeje se společnou katodou to není potřeba, tam posíláme pro rozsvícení na anodu jedničky.

Pozor i 7/8 segmentový displej vyžaduje zapojení přes rezistory!

Posuvný registr 74HC595

Posuvný registr nám dovoluje obsluhovat např. více LED než máme na Arduinu pinů. Zjednodušeně do něj nacpeme seriově potřebné informace a pak mu dáme signál, že to má najednou (paralelně) přenést na své datové výstupy (LED).

74HC595

Podrobněji: na vstupu máme 3 nožičky:

Na výstupu pak máme 8 datových nožiček (pin 1-7 + 15), na které se přenáší paměť registru při zvednuté závoře. Napájení jsou piny GND + OEGND, Vcc + MR → +5 V.

Pro vlastní plnění posuvného registru využíváme metodu Arduina shiftOut. Té předáváme datový pin, kudy tlačíme data, clock pin, kde bude Arduino posuvný registru “tikat”, aby veděl, kdy má data číst, jestli začínáme nejméně významným bitem nebo nejvíce významným bitem a vlastní přenášenou hodnotu.

Jen malé upozornění pro další pokusy s posuvným registrem, najednou z něj lze tahat asi maximálně 70 mA. Více se dá dočíst v pěkném povídání v angličtině.

Schéma zapojení

teplomer.fzz Zapojení kreslilo 7 leté dítě :-)

teplomer

Program

teplomer.ino

const int digitPins[4] = {4, 5, 6, 7};
const int clockPin = 10; // 74HC595 Pin 11
const int latchPin = 11; // 74HC595 Pin 12
const int dataPin = 12;  // 74HC595 Pin 14
const int tempPin = A0;  // datovy pin teplomeru
const byte digits[10] =  // cislice zakodovane jako jednotlive LED 7segmentoveho
                         // displeje
    {
        /*
               A
              +--+
           F  |G | B
              +--+
           E  |  | C
              +--+
               D
        */
        // ABCDEFG. (posledni je tecka)
        B11111100, // 0
        B01100000, // 1
        B11011010, // 2
        B11110010, // 3
        B01100110, // 4
        B10110110, // 5
        B10111110, // 6
        B11100000, // 7
        B11111110, // 8
        B11110110  // 9
};

// jen na zkousku, pozdrav
const byte letters[4] = {
    // ABCDEFG.
    B11101110, // A
    B01101110, // H
    B11111100, // O
    B01111000  // J
};

void setup() {
  for (byte i = 0; i < 4; i++) {
    pinMode(digitPins[i], OUTPUT);
  }
  pinMode(latchPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(dataPin, OUTPUT);
  // pin pro teplotu
  pinMode(tempPin, INPUT);

  // analogReference(INTERNAL);

  // testovaci konzole
  // Serial.begin(9600);

  // zobraz pozdrav
  for (byte i = 0; i < 200; i++) {
    displayLetter(3, 0);
    displayLetter(2, 1);
    displayLetter(1, 2);
    displayLetter(0, 3);
  }
}

// zobrazi pismeno
// digit = pozice na displeji (zleva, od 0)
// letter = poradi pismene v poli letters
void displayLetter(byte digit, byte letter) {
  // pred posouvanim bitu je potreba vypnout displej
  displayOff();
  // zavora dolu
  digitalWrite(latchPin, LOW);
  // napln shit registr daty o pismenu
  // ~ = bitova negace https://www.arduino.cc/en/Reference/BitwiseXorNot
  // protoze je displej se spolecnou anodou, privadime na anodu +5V z pinu, ktery vybira cislici
  // na katodu je pak potreba privest LOW, aby LED svitila, HIGH, aby nesvitila
  // toho docilime tak, ze prevratime 1 z pole letters/digits na 0 a obracene
  shiftOut(dataPin, clockPin, LSBFIRST, ~letters[letter]);
  // zavora nahoru -> odesilame na vystupni piny shift registru
  digitalWrite(latchPin, HIGH);
  // pust HIGH do vybrane cislice displeje
  digitalWrite(digitPins[digit], HIGH);
  // pockame chvilku na POV (persistence of vision)
  delay(3);
}

// zobrazi cislici na vybrane pozici displeje, automaticky osetruje desetinnou
// tecku za druhou cislici
// digit = pozice, na ktere chceme cislici zobrazit
// number = cislice, kterou chceme zobrazit
void displayDigit(byte digit, byte number) {
  // pred posouvanim bitu je potreba vypnout displej
  displayOff();
  // zavora dolu
  digitalWrite(latchPin, LOW);
  // pokud piseme treti cislici (pocitano od 0), tak rozsvitime i desetinnou
  // tecku
  //
  // ~ = bitova negace https://www.arduino.cc/en/Reference/BitwiseXorNot
  // protoze je displej se spolecnou anodou, privadime na anodu +5V z pinu, ktery vybira cislici
  // na katodu je pak potreba privest LOW, aby LED svitila, HIGH, aby nesvitila
  // toho docilime tak, ze prevratime 1 z pole letters/digits na 0 a obracene
  if (digit == 2) {
    // desetina tecka je bitove posledni, provedeme bitovy OR
    shiftOut(dataPin, clockPin, LSBFIRST, ~(digits[number] | B00000001));
  } else {
    // normalni zapis do shift registru
    shiftOut(dataPin, clockPin, LSBFIRST, ~digits[number]);
  }
  // zavora nahoru -> odesilame na vystupni piny shift registru
  digitalWrite(latchPin, HIGH);
  // pust HIGH do vybrane cislice displeje
  digitalWrite(digitPins[digit], HIGH);
  // pockame chvilku na POV (persistence of vision)
  delay(3);
}

// zhasni vsechny LED
void displayOff() {
  for (byte i = 0; i < 4; i++) {
    digitalWrite(digitPins[i], LOW);
  }
}

// aktualizuj displej
void updateDisplay(float number) {
  byte digitBuffer[4] = {0};
  // orizni na 2 desetiny
  number = int(number * 100);
  // setiny
  digitBuffer[3] = int(number) / 1000;
  // desetiny
  digitBuffer[2] = (int(number) % 1000) / 100;
  // jednotky
  digitBuffer[1] = (int(number) % 100) / 10;
  // desitky
  digitBuffer[0] = (int(number) % 100) % 10;

  for (byte i = 0; i < 50; i++) {
    for (byte digit = 0; digit < 4; digit++) {
      // zobraz po sobe vsechny cislice hodnoty
      displayDigit(digit, digitBuffer[digit]);
    }
  }
}

void loop() {
  // precti hodnotu teplomeru
  int reading = analogRead(tempPin);
  // prepocitame hodnotu na stupne
  // rozsah hodnot analogoveho vystupu je 0-1024, (0-5V)
  // teplomer vraci 10mV/stupen C
  // 5 V / 1024 = 4,8828 mV
  // teplota = (5 * hodnota * 100) / 1024
  float tempC = (5 * reading * 100.0) / 1024.0;

  // pokud bychom chteli vyssi presnost, muzeme nastavit jine referencni napeti
  // pro analogovy vstup
  // odpoznamkovat v setup() analogReference(INTERNAL);
  // to nastavi misto 5V referencni napeti 1.1V
  // zmenou referencniho napeti na 1.1 se zmeni rozsah LM35 na 0-110 stupnu C
  // tempC = (1.1 * reading * 100.0) / 1024.0;

  //  Serial.print(tempC);
  //  Serial.println(" C");
  for (int i = 0; i < 10; i++) {
    updateDisplay(tempC);
  }
}

Možná vylepšení

Poznatky

Nejvíce asi zabralo vysvětlování toho, jak jsou ty LED uspořádány uvnitř segmentového displeje. Pak samozřejmě posuvný registr a nějak se poprat s bitovými operacemi (ale ty se dají obejít tím, že uděláme bitový zápis v kódu a nepřevádíme mezi soustavami). A pak samozřejmě ta nehoda s teploměrem… ;-) Celkově je to náročnější úloha, která se nemusí stihnout ani za jedno odpoledne, pokud jsou všechny ty věci nové.