← 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
- Násobení, dělení.
- HW: Základní zapojování LED, rezistorů.
- SW: Je dobré rozumět cyklům, hodí se znát operátor pro zbytek po dělení (modulo), rozumět polím, procedury.
Foto
Hardware
- teploměr LM35 (Aliexpress, specifikace)
- 4 číslicový 7 segmentový displej (použili jsme 3461BS, Aliexpress, specifikace)
- Posuvný registr 74HC595 (Aliexpres, specifikace)
- 8 rezistorů 220 Ω - 330 Ω
Jak to funguje
Teploměr LM35
Parametry:
- rozsah od −55° C do 150 °C
- přesnost 0,5 °C (při 25 °C)
- vstupní napětí od 4 V do 30 V
- nízké sebezahřívání (0,08 °C, pokud se tedy nezapojí obráceně ;-) )
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.
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).
Podrobněji: na vstupu máme 3 nožičky:
- data (pin 14) - data, tudy posuvný registru předáváme informace
- clock (pin 11) - časování, tady se posuvný registr dozví, že by měl číst z datového pinu, po každém čtení si informaci zapamatuje
- latch (pin 12) - závora, která při přechodu na z
LOW
→HIGH
přepíše paměť registru na výstupní datové piny registru
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
+ OE
→ GND
, 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ě :-)
Program
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í
- Zvýšení přesnosti teploměru změnou referenčního napětí z 5 V → 1,1 V.
- Přidávání dalších informací, které by se na segmentovém displeji mohly zobrazovat (písmenka).
- Protože ovládání segmentového displeje je celkem komplikované, je možné zkusit jiný typ displeje, např. LCD nebo OLED.
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é.