State Machines II.
pohybový (PIR) senzor, pulzne šírková modulácia (PWM)
Záznam z prednášky
Introduction
(slide) Na poslednej prednáške sme si ukázali niekoľko nových vecí - hovorili sme o stavových strojoch, ukázali sme si sériovú linku a sériovú komunikáciu, analógové vstupy, hovorili sme o analógovo-digitálnom prevodníku, predstavili sme si projekt nočného svetla, ktorý nás bude chvíľu sprevádzať, a na ktorom sme si všetky spomínané veci prezentovali.
Zatiaľ sme si predstavili správanie nočného bezpečnostného svetla len v dvoch stavoch:
Day
aNight
, kedy sa svetlo rovno zaplo pri prechode do stavuNight
. Dnes však aktuálnu verziu zariadenia rozšírime o senzor pohybu a zabezpečíme, aby sa svetlo rozsvietilo len vtedy, keď bude nie len noc, ale aj niekto prejde okolo.
State 3: The Light
(slide) Tretím stavom, ktorý bude naše zariadenie obsahovať, bude
Light
. Do neho zariadenie prejde zo stavuNight
v prípade, že bude detekovaný pohyb. Späť do stavuNight
sa zariadenie vráti potom, ako pohyb ustane.
The PIR Sensor
(slide) Na detekciu pohybu k doske Arduino UNO pripojíme PIR senzor. Ten bude generovať na výstupe logickú úroveň
HIGH
, keď zistí pohyb. V opačnom prípade sa na výstupe senzora bude nachádzať logická úroveňLOW
.Poznámka
Pre viac informácií ohľadom fungovania PIR senzoru odporúčam prečítať napr. článok How HC-SR501 PIR Sensor Works & Interface It With Arduino.
PIR senzor má tri piny:
VCC
- napájanieGND
- zemOUT
- digitálny výstup
Poznámka
Na doske má obyčajne PIR senzor aj dva potenciometre. Pomocou nich je možné nastaviť citlivosť senzora, ako aj čas spozdenia, počas ktorého bude senzor hlásiť detegovaný pohyb pomocou logickej úrovne
HIGH
. S obe nastavenia je potrebné vyskúšať individuálne a zoznámiť sa s nimi, aby sa nestávalo (hlavne pri testovaní), že práve hodnota času spozdenia bude príliš veľká.Výstup z PIR senzoru bude pripojený k pinu č.
2
, ktorý bude nastavený ako vstupný. Jednoduchá ukážka toho, ako PIR senzor funguje, môže vyzerať takto:#define LED 6 #define PIR 2 int main(){ // taken from original main.cpp (); init // setup (LED, OUTPUT); pinMode(PIR, INPUT); pinMode // superloop for(;;){ (LED, digitalRead(PIR)); digitalWrite(1000); delay} }
Refactoring: Reading the Movement from PIR Sensor
Aktualizujeme teda náš kód reprezentujúci správanie nočného svetla. Začneme tým, že rozšírime enumeračný typ
enum state
o nový stavLIGHT
:enum state { , DAY, NIGHT LIGHT};
zadefinujeme makro
PIR
, kde uvedieme číslo pin-u, ku ktorému je PIR senzor pripojený:#define PIR 2
a nastavíme pin, ku ktorému je PIR senzor pripojený, na vstupný:
// setup (PIR, INPUT); pinMode
Následne vytvoríme funkciu
state_light()
, ktorá bude reprezentovať správanie zariadenia v staveLIGHT
:enum state state_light(){ // on enter .println(">> State Light Entered."); Serial(LED, HIGH); digitalWrite // loop while(digitalRead(PIR) == HIGH){ (1000); delay} // on exit (LED, LOW); digitalWritereturn NIGHT; }
A opravíme správanie v hlavnej slučke pridaním ošetrenia správania v prípade stavu
LIGHT
:case LIGHT: = state_light(); state break;
Zároveň však treba opraviť správanie v stave
NIGHT
, kedy sa nebude automaticky svetlo zapínať pri vstupe do stavu a budeme sledovať dve udalosti: stúpnutie intenzity svetla a vznik pohybu:enum state state_night(){ // on enter .println(">> State Night Entered."); Serial(LED, LOW); digitalWrite // main loop enum state next_state = NIGHT; do{ (1000); delay // check LDR int value = analogRead(LDR); = map(value, 0, 1023, 0, 100); value if(value > 45){ = DAY; next_state } // check PIR if(digitalRead(PIR) == HIGH){ = LIGHT; next_state } }while(next_state == NIGHT); // on leave return next_state; }
Poznámka
Samozrejme - tento kód sa dá napísať aj kratšie. Pre zvýšenie čitateľnosti pre zachovanie pôvodnej štruktúry funkcie ošetrujúcej stav, je teda implementácia zapísaná takto “explicitne”.
Well Done!
A je to! Ak teraz spustíme kód so všetkými pripojenými komponentmi, tak sa naše zariadenie naozaj bude správať, ako sme naplánovali podľa stavového diagramu.
Tu by sme mohli skončiť. Ale miesto toho sa s týmto zariadením budeme hrať ďalej, pretože sa dá rozšíriť niekoľkými spôsobmi:
- svetlo sa bude rozsvecovať a zhasínať postupne a nie naraz
- pre trvalú inštaláciu pridáme ešte aj vypínač, ktorým funkcionalitu budeme vedieť zapnúť resp. vypnúť
- svetlo môže jemne svietiť aj bez toho, aby šiel niekto okolo
- miesto LED diódy je možné pripojiť aj normálnu žiarovku na 220V, ktorú od Arduina oddelíme relé; to ale nebudeme robiť kvôli nebezpečiu úrazu elektrickým prúdom
- zariadenie nemusí pracovať ako nočné svetlo so senzorom pohybu, ale napr. ako automatické svetlo, ktoré sa rozsvieti po otvorení dverí (do špajze alebo pivnice) v prípade, že je noc (takže to vlastne je nočné svetlo ;)
No a zo všetkých uvedených možností sa pozrieme na to, ako je možné svetlo stmievať a rozsvecovať. A budeme na to používať pulzne šírkovú moduláciu (z angl. Pulse Width Modulation), skrátene označovanú aj ako PWM.
Pulse-Width Modulation
Introduction
(slide) Zjednodušene môžeme povedať, že podstatou PWM je (rýchle) zapínanie a vypínanie zdroja napätia. Ak k takémuto zdroju pripojíme LED diódu, tak uvidíme, že bliká. Ak však budeme blikať dostatočne rýchlo, tak pri frekvencii 18 bliknutí za sekundu naše oko začne blikanie vnímať ako súvislé svetlo. Čiže v prípade blikania sme urobili akýsi podvod pre naše oko ;)
PWM teda môže byť použité na vytvorenie ilúzie, že LED dióda môže mať niekoľko úrovní jasu. Jas, ktorý dosiahneme, je stanovený z množstva času, v ktorom je LED dióda zapnutá voči času, v ktorom je LED dióda vypnutá. Čím bude dlhší pracovný cyklus (dlhší čas bude PIN nastavený na hodnotu 1 než na hodnotu 0), tým vyšší jas bude LED dióda mať.
(slide) Pozrime sa teda na to, ako taký pracovný cyklus vyzerá.
(slide) A pozrime sa tiež na niekoľko rozličných pracovných cyklov. Čím je teda šírka pulzu menšia, tým je aj menší celkový výsledný výkon, resp. v našom prípade to bude úroveň jasu. Čím je naopak šírka pulzu väčšia, tým bude väčší aj výsledný výkon, resp. úroveň jasu.
PWM and Arduino UNO
(slide) Pozrime sa teraz na to, ako je to s podporou PWM na doske Arduino UNO. Nie každý pin je možné totiž použiť ako PWM. Pulzne šírkovú moduláciu podporujú tie, ktoré sú označené symbolom “
~
”. Konkrétne sa jedná sa len o digitálne PIN-y3
,5
,6
,9
,10
a11
.Poznámka
Pre úplnosť je potrebné dodať, že pre svoju činnosť PWM používa časovače mikrokontroléra ATmega. Prehľad, ktoré piny používajú ktoré časovače a akú frekvenciu majú, sa nachádza v nasledujúcej tabuľke:
pins timer base freq. (Hz) default freq. (Hz) divisors 5
,6
0 62500 976.56 1, 8, 64, 256, 1024 9
,10
1 31250 490.2 1, 8, 64, 256, 1024 3
,11
2 31250 490.20 1, 8, 32, 64, 128, 256, 1024 (slide) Ak chceme pracovať s PWM na príslušnom pine, budeme používať funkciu
analogWrite()
, ktorá má dva parametre:- číslo digitálneho PIN-u
- šírka pulzu pracovného cyklu v rozsahu od
0
(0% šírka pracovného cyklu) po255
(100% šírka pracovného cyklu).
Enhancing the Security Night Light with PWM
Vráťme sa teda k našej prípadovej štúdii, na ktorej pracujeme - k nočnému svetlu. Ako som spomínal, urobíme tieto úpravy:
- rozsvietenie svetla v stave
LIGHT
nebude skokové, ale postupné; rovnako tak to bude pri jeho zhasínaní - pri prechode do stavu
NIGHT
zvýšime jas na nízku úroveň, čím v danom mieste nebude totálna tma; pri prechode do stavuDAY
zasa postupne jas znížime
- rozsvietenie svetla v stave
Dim/light in the LIGHT
State
Začneme teda s postupným rozsvietením a následne zhasnutím pri prejdení do stavu
LIGHT
resp. pri jeho opúšťaní:enum state state_light(){ // on enter .println(">> State Light Entered."); Serialfor(int level = 0; level < 255; level++){ (LED, level); analogWrite(10); delay} // loop while(digitalRead(PIR) == HIGH){ (1000); delay} // on exit for(int level = 255; level >= 0; level--){ (LED, level); analogWrite(10); delay} return NIGHT; }
Dim/light in the NIGHT
State
Použitie v prípade stavu
NIGHT
bude podobné. Hlavný rozdiel bude v tom, že nebudeme rozsvecovať svetlo naplno, ale len po úroveň, ktorú si zadefinujeme pomocou makraLIGHT_LOW
:#define LIGHT_LOW 100 enum state state_night(){ // on enter .println(">> State Night Entered."); Serialfor(int level = 0; level < LIGHT_LOW; level++){ (LED, level); analogWrite(10); delay} // main loop enum state next_state = NIGHT; do{ (1000); delay// check LDR int value = analogRead(LDR); = map(value, 0, 1023, 0, 100); value if(value > 45){ = DAY; next_state } // check PIR if(digitalRead(PIR) == HIGH){ = LIGHT; next_state } }while(next_state == NIGHT); // on exit if(next_state == DAY){ for(int level = LIGHT_LOW; level >= 0; level--){ (LED, level); analogWrite(10); delay} } return next_state; }
Samozrejme, aby všetko fungovalo tak, ako má, je potrebné upraviť aj ošetrenie stavu
LIGHT
. Rozsvietenie ako aj zhasnutie v tomto prípade nebude začínať od hodnoty0
, ale už len od hodnoty makraLIGHT_LOW
.
Changing the Frequency
Výpočet zmeny frekvencie:
register = register & mask | mode
kde:
register
jeTCCR0B
pre piny 5 a 6,TCCR1B
pre piny 9 a 10 aTCCR2B
pre piny 2 a 11maska
je0b11111000
mode
je hodnota deliča
Príklad zmeny frekvencie:
// for PWM frequency of 30.64 Hz = TCCR1B & B11111000 | B00000101; TCCR1B
PWM Usages
(slide)
dimming of RGB LEDs
brightness of LEDs
control the direction of a servo motors
in telecomumunications (encoding/decoding data)
audio effects and amplification
Looking Forward
- Nabudúce sa ešte pozrieme minimálne na komunikáciu po zbernici I2C a ak nám zvýši čas, povieme si niečo aj o prerušení a jeho ošetrení.