Ciljevi učenja
- Razlikovati isključivanje nadzora od potvrde zapamćenog događaja.
- Zahtijevati neprekidan PIR LOW tokom 2 s prije nadzora.
- Obrađivati dva debouncovana tastera uz prednost ARM/DISARM komande.
- Upravljati aktivnom zujalicom preko dodatnog PN2222A izlaza.
- Uskladiti sedam stanja alarma, LED ritmove i brojač događaja u RAM-u.
Prije početka
Kursevi 15 i 03: PIR alarm sa stanjima, taster sa INPUT_PULLUP i uklanjanjem odskakivanja, te neblokirajuće vremensko upravljanje.
Referentna postavka
- Ploča u Arduino IDE-u
- ESP32 Dev Module
- Arduino-ESP32
- 3.3.12
- Serial Monitor
- 115200 baud
Ovo je referentni profil, a ne identifikacija ploče sa fotografije. Za ESP32-C3/S2/S3 ili drugačije označenu ploču treba prilagoditi GPIO i provjeriti šemu prije spajanja.
Šta ćemo napraviti
Napravićemo stolni model alarma sa dva tastera: jednim uključujemo ili isključujemo nadzor, a drugim potvrđujemo da smo primijetili događaj. PIR prati pokret u blizini stola. Kada aktivira alarm, crvena LED stalno svijetli, a aktivna zujalica daje kratke ponavljane signale. Pritiskom tastera za potvrdu zvuk prestaje, dok LED ostaje uključena kao podsjetnik na zapamćeni događaj.
U odnosu na kurs 15 dodajemo razliku između utišavanja, isključivanja i spremnosti za novi događaj. Sistem prije nadzora čeka da PIR neprekidno daje LOW dvije sekunde. Ovo je demonstracija obrade događaja blizu stola. Senzor ne utvrđuje da li je predmet pomjeren ili ukraden i projekat nije sigurnosni uređaj.
Šta ćeš naučiti
- Organizovati alarm sa odvojenim komandama ARM/DISARM i ACK.
- Razlikovati potvrdu događaja od njegovog brisanja.
- Sačekati neprekinut miran ulaz prije početka nadzora.
- Dodijeliti prednost komandama koje se prihvate istovremeno.
- Obrađivati dva tastera bez ponavljanja radnje pri držanju.
- Usaglasiti stanje programa, LED indikaciju i ritam zvučnog izlaza.
Predznanje
Završi kurs 15 o PIR alarmu i kurs 03 o tasteru. Potrebno je razumjeti INPUT_PULLUP, odskakivanje kontakata i računanje proteklog vremena pomoću millis(). Ponovi tranzistorski izlaz iz kursa 13 ako aktivnu zujalicu još nisi samostalno povezao. Prije zajedničke probe provjeri ulaze oba tastera: u otpuštenom stanju treba da budu HIGH, a pri pritisku LOW.
Potrebna oprema
| Komponenta | Količina | Specifikacija i napomene |
|---|---|---|
| ESP32 razvojna ploča sa klasičnim ESP32-WROOM-32 modulom | 1 | GPIO nazivi moraju odgovarati referentnom profilu; fizički položaj izvoda provjeriti na ploči. |
| USB kabl za podatke i napajanje | 1 | Priključak prilagoditi ploči; komplet na slici navodi Micro-USB. |
| Eksperimentalna ploča | 1 | Komplet navodi 830 kontaktnih tačaka. Provjeriti eventualni prekid naponskih vodova. |
| PIR senzor pokreta HC-SR501 | 1 | Napajanje modula 5 V sa USB voda ploče; izlaz OUT 3,3 V. Provjeriti raspored pinova, H/L kratkospojnik i trajanje impulsa. |
| Aktivni buzzer — referenca 5 V, do 20 mA | 1 | Fotografija navodi aktivni buzzer, ali ne potvrđuje njegov napon i struju. Za ovu šemu izaberi dokumentovani 5 V model sa najviše 20 mA radne struje. |
| Dodatni NPN tranzistor PN2222A | 1Obavezno dodatno — nije na slici kompleta | DODATNO, nije naveden u kompletu. Identifikuj B/C/E prema tačnom proizvođaču; PN2222A i P2N2222A mogu imati različit raspored. DODATNO: tranzistor nije naveden na fotografiji kompleta. |
| Dodatna zaštitna dioda 1N4148 | 1Obavezno dodatno — nije na slici kompleta | DODATNO, nije navedena u kompletu. Prsten označava katodu K. U buzzer krugu katoda ide prema +5 V. DODATNO: dioda nije navedena na fotografiji kompleta. |
| Otpornik 1 kΩ | 1 | Otpornik baze; u kursu 14 i paralelni otpornik piezo elementa. Količina ove vrijednosti u kompletu nije potvrđena. |
| Otpornik 10 kΩ | 1 | R2 je bazni pull-down od Q1.B prema GND. |
| Trenutni taster | 2 | Koristiti par priključaka koji se spaja pritiskom; ista strana četvoronožnog tastera može već biti spojena. |
| Crvena LED dioda | 1 | Anoda A i katoda K; provjeriti polaritet stvarne komponente. |
| Otpornik 220 Ω | 1 | Po jedan otpornik za svaku LED granu; 220 Ω iz kompleta. |
| Spojni kablovi | 18 | M-M ili F-M prema priključcima ploče. Okvirna količina; zavisi od rasporeda i vrste priključaka. |
PN2222A i dioda 1N4148 dodatne su komponente koje nisu navedene na fotografiji kompleta. Za jedan sklop potreban je po jedan primjerak. Referentna aktivna zujalica koristi 5 V i ima nazivnu struju najviše 20 mA. Provjeri stvarnu oznaku, tip i struju; spoljašnji izgled ne potvrđuje ove osobine.
Koristimo klasični ESP32-WROOM-32, HC-SR501 sa napajanjem 5 V i izlazom logike 3,3 V, dva trenutna tastera i crvenu LED. Otpornici od 1 kΩ, 10 kΩ i 220 Ω imaju različite namjene. Prije povezivanja provjeri vrijednosti i raspoložive količine. Napajanje cijelog primjera dolazi preko USB-a.
Stanja, potvrda i prednost komandi
| Stanje | Značenje i izlazi | Prelazak |
|---|---|---|
WARMUP | Smirivanje 60 s; LED i zvuk ugašeni. | Automatski u DISARMED. |
DISARMED | Nadzor isključen; oba izlaza ugašena. | Novi ARM pritisak započinje kašnjenje. |
EXIT_DELAY | Deset sekundi za udaljavanje; LED 250 ms uključena i 250 ms ugašena. | Poslije 10 s u WAIT_CLEAR. |
WAIT_CLEAR | Čeka miran PIR; isti brzi ritam LED. | Neprekidan PIR LOW 2 s vodi u ARMED. |
ARMED | Nadzor aktivan; LED 50 ms uključena i 1950 ms ugašena. | PIR HIGH aktivira alarm. |
ALARM | LED stalna; zvuk 200 ms, pauza 800 ms. | ACK utišava; ARM isključuje. |
ACKNOWLEDGED | Događaj potvrđen; LED stalna, zvuk ugašen. | ARM isključuje i briše aktivno stanje događaja. |
ARM/DISARM ima prednost nad ACK-om kada se oba pritiska prihvate u istom prolazu petlje. Iz svakog stanja osim WARMUP i DISARMED vraća sistem u DISARMED. ACK djeluje samo u ALARM; drugdje se zanemaruje. Potvrda ne znači da je uzrok nestao, niti automatski započinje novi nadzor.
Šema i povezivanje
Šema povezivanja
D1 je zaštitna dioda preko buzzera, D2 je crvena LED. Dodatni Q1 i D1 nisu navedeni u kompletu. SW1 uključuje/isključuje sistem, SW2 samo potvrđuje aktivni alarm i utišava zvuk. PIR i buzzer su na provjerenom USB5V vodu, izlazi prema GPIO su 3,3 V.
| Od | Do | Opis veze |
|---|---|---|
ESP32.GPIO26 | R1.1 | R1, 1 kΩ, ograničava struju baze. |
R1.2 | Q1.B | Baza dodatnog PN2222A; provjeri fizički raspored B/C/E. |
Q1.B | R2.1 | R2, 10 kΩ, povlači bazu prema masi. |
R2.2 | ESP32.GND | Drugi kraj baznog pull-down otpornika. |
Q1.E | ESP32.GND | Emiter prema zajedničkoj masi. |
ESP32.USB5V | BZ1.PLUS | Dokumentovani aktivni buzzer 5 V, do 20 mA radne struje. |
BZ1.MINUS | Q1.C | Negativni izvod buzzera prema kolektoru. |
D1.A | BZ1.MINUS | Anoda zaštitne diode 1N4148 na negativnom izvodu buzzera. |
D1.K | ESP32.USB5V | Katoda diode, označena prstenom, prema +5 V. |
ESP32.USB5V | U1.VCC | HC-SR501: provjereni vod 5 V izveden sa USB napajanja ploče. |
ESP32.GND | U1.GND | Zajednička masa. |
U1.OUT | ESP32.GPIO33 | PIR OUT, logika 3,3 V, prema GPIO33. |
ESP32.GPIO25 | R3.1 | Izlaz za LED (crvena) prema otporniku 220 Ω. |
R3.2 | D2.A | Serijski otpornik prema anodi A. |
D2.K | ESP32.GND | Katoda K prema masi. |
ESP32.GPIO27 | SW1.CONTACT_A | Taster ARM/DISARM; ulaz INPUT_PULLUP. |
SW1.CONTACT_B | ESP32.GND | Par kontakata koji se spaja samo pritiskom. |
ESP32.GPIO32 | SW2.CONTACT_A | Taster ACK; ulaz INPUT_PULLUP. |
SW2.CONTACT_B | ESP32.GND | Par kontakata koji se spaja samo pritiskom. |
- Isključi USB i provjeri raspored B, C i E svog tranzistora u tehničkom listu njegovog proizvođača. PN2222A i slično označeni P2N2222A ne moraju imati isti raspored nožica.
- Spoji zajednički GND ESP32, PIR-a i emitera Q1. Poveži i povratne vodove LED i oba tastera.
- Poveži GPIO26 → R1, 1 kΩ → baza Q1. Spoji R2, 10 kΩ između baze Q1 i GND; to je pull-down, a ne veza prema 3V3.
- Kolektor Q1 spoji na minus BZ1, a plus zujalice na potvrđeni USB vod od 5 V.
- Diodu D1 poveži paralelno sa BZ1: anoda na minus zujalice, katoda sa trakom na plus od 5 V.
- PIR U1 napajaj sa istog USB voda od 5 V. Njegov OUT poveži na GPIO33.
- ARM/DISARM taster SW1 poveži između GPIO27 i GND, a ACK taster SW2 između GPIO32 i GND. Odaberi par kontakata koji se zaista spaja pritiskom.
- Poveži GPIO25 → R3, 220 Ω → anoda crvene LED D2, a katodu na GND.
D1 je zaštitna dioda, dok je D2 LED. Kada GPIO26 postane HIGH, tranzistor otvara strujni put za zujalicu prema GND. Struja zujalice ne prolazi kroz GPIO. Provjeri mogućnost napajanja svih potrošača i ne dodaj nezavisnih 5 V paralelno sa USB-om. PIR OUT mora ostati kompatibilan sa 3,3 V logikom ESP32.
Priprema Arduino okruženja
U Arduino IDE-u 2.x izaberi esp32 by Espressif Systems 3.3.12, ESP32 Dev Module i port ploče. Otvori desk_alarm/desk_alarm.ino. Dodatne biblioteke nisu potrebne; Serial Monitor koristi 115200 baud. Označi tastere na maketi da njihova funkcija bude jasna i bez gledanja u program.
Pregledaj GPIO33 za PIR, GPIO27 za ARM, GPIO32 za ACK, GPIO26 za tranzistorski izlaz i GPIO25 za LED. Ako prenosiš sklop iz kursa 15, GPIO32 je nova veza drugog tastera; ostale navedene veze zadržavaju iste funkcije.
Kompletan Arduino program
#include <Arduino.h>
constexpr uint8_t PIR_PIN = 33;
constexpr uint8_t BUZZER_PIN = 26;
constexpr uint8_t LED_PIN = 25;
constexpr uint8_t ARM_PIN = 27;
constexpr uint8_t ACK_PIN = 32;
constexpr uint32_t DEBOUNCE_MS = 30;
constexpr uint32_t WARMUP_MS = 60000;
constexpr uint32_t EXIT_DELAY_MS = 10000;
constexpr uint32_t CLEAR_MS = 2000;
// A fresh event needs 30 ms at one level. A held or inhibited press must
// release stably before another event can be accepted.
struct DebouncedButton {
uint8_t pin;
bool rawHigh = true, stableHigh = true, armed = true;
bool pressed = false, released = false;
uint32_t rawChangedAt = 0;
explicit DebouncedButton(uint8_t inputPin) : pin(inputPin) {}
void begin(uint32_t now) {
pinMode(pin, INPUT_PULLUP);
rawHigh = stableHigh = digitalRead(pin) == HIGH;
armed = rawHigh;
rawChangedAt = now;
pressed = released = false;
}
void update(uint32_t now) {
pressed = released = false;
const bool readingHigh = digitalRead(pin) == HIGH;
if (readingHigh != rawHigh) {
rawHigh = readingHigh;
rawChangedAt = now;
}
if (static_cast<uint32_t>(now - rawChangedAt) < DEBOUNCE_MS) return;
if (rawHigh != stableHigh) {
stableHigh = rawHigh;
if (stableHigh) released = true;
else if (armed) {
pressed = true;
armed = false;
}
}
// Also recovers from an inhibited LOW pulse too short to become stable.
if (rawHigh && stableHigh) armed = true;
}
void inhibit() { armed = false; pressed = false; }
bool isReleased(uint32_t now) const {
return rawHigh && stableHigh &&
static_cast<uint32_t>(now - rawChangedAt) >= DEBOUNCE_MS;
}
};
DebouncedButton armButton(ARM_PIN), ackButton(ACK_PIN);
enum AlarmState { WARMUP, DISARMED, EXIT_DELAY, WAIT_CLEAR, ARMED, ALARM, ACKNOWLEDGED };
AlarmState state = WARMUP;
uint32_t stateSince = 0;
bool clearTiming = false;
uint32_t clearSince = 0;
uint32_t eventCount = 0;
void enterState(AlarmState next, uint32_t now);
void enterState(AlarmState next, uint32_t now) {
state = next;
stateSince = now;
clearTiming = false;
Serial.print("State=");
switch (state) {
case WARMUP: Serial.println("WARMUP"); break;
case DISARMED: Serial.println("DISARMED"); break;
case EXIT_DELAY: Serial.println("EXIT_DELAY"); break;
case WAIT_CLEAR: Serial.println("WAIT_CLEAR"); break;
case ARMED: Serial.println("ARMED"); break;
case ALARM: Serial.println("ALARM"); break;
case ACKNOWLEDGED: Serial.println("ACKNOWLEDGED"); break;
}
}
void updateOutputs(uint32_t now) {
const uint32_t elapsed = static_cast<uint32_t>(now - stateSince);
bool led = false, buzzer = false;
if (state == EXIT_DELAY || state == WAIT_CLEAR) led = elapsed % 500UL < 250UL;
else if (state == ARMED) led = elapsed % 2000UL < 50UL;
else if (state == ALARM) {
led = true;
buzzer = elapsed % 1000UL < 200UL;
} else if (state == ACKNOWLEDGED) led = true;
digitalWrite(LED_PIN, led ? HIGH : LOW);
digitalWrite(BUZZER_PIN, buzzer ? HIGH : LOW);
}
void setup() {
Serial.begin(115200);
pinMode(BUZZER_PIN, OUTPUT); digitalWrite(BUZZER_PIN, LOW);
pinMode(LED_PIN, OUTPUT); digitalWrite(LED_PIN, LOW);
pinMode(PIR_PIN, INPUT);
const uint32_t now = millis();
armButton.begin(now); ackButton.begin(now);
enterState(WARMUP, now);
}
void loop() {
const uint32_t now = millis();
armButton.update(now);
ackButton.update(now);
const bool pirHigh = digitalRead(PIR_PIN) == HIGH;
if (state == WARMUP) {
if (static_cast<uint32_t>(now - stateSince) >= WARMUP_MS) {
// Suppress unfinished raw presses at this boundary as well as events.
if (!armButton.isReleased(now)) armButton.inhibit();
if (!ackButton.isReleased(now)) ackButton.inhibit();
enterState(DISARMED, now);
}
} else if (armButton.pressed) {
// ARM/DISARM has priority over ACK and sensor events in this iteration.
enterState(state == DISARMED ? EXIT_DELAY : DISARMED, now);
} else if (ackButton.pressed && state == ALARM) {
enterState(ACKNOWLEDGED, now); // Keep the event latched, silence the buzzer.
} else {
if (state == EXIT_DELAY && static_cast<uint32_t>(now - stateSince) >= EXIT_DELAY_MS) {
enterState(WAIT_CLEAR, now);
}
if (state == WAIT_CLEAR) {
if (pirHigh) clearTiming = false;
else if (!clearTiming) {
clearTiming = true;
clearSince = now;
} else if (static_cast<uint32_t>(now - clearSince) >= CLEAR_MS) {
enterState(ARMED, now);
}
}
if (state == ARMED && pirHigh) {
++eventCount;
enterState(ALARM, now);
Serial.print("Events since reset="); Serial.println(eventCount);
}
}
updateOutputs(now);
}
Klikni Verify, zatim Upload. Za prvu probu zadrži sva originalna vremena. Reset započinje novo smirivanje PIR-a i vraća brojač događaja na nulu.
Kako čitati program
AlarmState navodi sedam stanja. state pamti trenutno, a stateSince trenutak ulaska. Funkcija enterState() mijenja stanje, postavlja vremensku osnovu i briše započeto mjerenje mirnog PIR ulaza. updateOutputs() na osnovu stanja i proteklog vremena određuje LED i zujalicu.
Svaki DebouncedButton prati sopstveni ulaz. Prihvaćen pritisak zahtijeva stabilan nivo 30 ms i stvara samo jedan događaj. Držanje pri resetu ne izvršava komandu. Tokom WARMUP ulazi se i dalje čitaju, ali komande ne djeluju. Na završnoj granici potiskuje se i pritisak koji je počeo neposredno prije isteka, a još nije prošao potvrdu. Potrebni su stabilno otpuštanje i novi pritisak.
U WAIT_CLEAR, clearTiming označava da mjerenje mirnog ulaza traje, a clearSince njegov početak. Svaki opaženi HIGH prekida interval. Tek novih neprekinutih 2000 ms na LOW omogućava ARMED. Raniji miran period tokom izlaznog kašnjenja ne uračunava se u ove dvije sekunde.
Pri prelasku iz ARMED u ALARM povećava se eventCount. Kasnije utišavanje, prestanak PIR signala i držanje tastera ne stvaraju dodatne događaje. Broj ostaje u RAM-u do resetovanja; nije trajni dnevnik. ACK čuva aktivno stanje događaja kroz ACKNOWLEDGED, dok ARM gasi izlaze i vraća sistem u DISARMED. Oduzimanje vremenskih oznaka tipa uint32_t omogućava rad ovih kratkih intervala preko preliva brojača.
Eksperiment i opažanja
Sačekaj početnih 60 s. Pritisni i otpusti ARM, udalji se tokom deset sekundi i sačekaj da PIR daje LOW najmanje dvije sekunde. Tek rijetko treptanje označava aktivan nadzor. Izazovi pokret, sačekaj alarm, pa pritisni ACK. Zatim pritisni ARM da isključiš sistem. Za novi nadzor otpusti ARM i pritisni ga ponovo.
| Proba | Očekivani rezultat |
|---|---|
| Drži ARM kroz završetak početnog čekanja. | Ostaje DISARMED do otpuštanja i novog pritiska. |
PIR je HIGH poslije izlaznog kašnjenja. | Ostaje WAIT_CLEAR, bez zvuka. |
U WAIT_CLEAR dobij 1,5 s LOW, kratki HIGH, zatim LOW. | Dvije sekunde počinju iznova poslije posljednjeg HIGH. |
PIR padne na LOW poslije alarma. | Alarm ostaje aktivan dok korisnik ne zada komandu. |
| Pritisni ACK u alarmu. | Zvuk prestaje, LED ostaje stalno uključena. |
| Ponovo izazovi pokret poslije ACK. | Ostaje ACKNOWLEDGED; nema novog zvuka niti povećanja broja. |
| ARM i ACK budu prihvaćeni zajedno u alarmu. | ARM ima prednost; sistem postaje DISARMED. |
Tabela opisuje očekivane rezultate, koje treba provjeriti na stvarnom sklopu. Za preciznu provjeru istovremenih događaja važno je kada ih program prihvati poslije odskakivanja, ne samo prividno istovremen dodir tastera.
Najčešći problemi
| Simptom | Provjera i objašnjenje |
|---|---|
| Sistem dugo brzo trepće nakon deset sekundi. | PIR nije bio neprekidno LOW pune dvije sekunde; provjeri kretanje i njegovo zadržavanje. |
| ACK ne uključuje nadzor. | To nije njegova funkcija; ARM počinje ciklus iz DISARMED. |
| Poslije ACK LED ne prestaje svijetliti. | LED namjerno čuva informaciju o potvrđenom događaju; ARM je briše. |
| Zvuk ne radi, a LED pokazuje alarm. | Provjeri aktivni tip BZ1, struju, polaritet diode, B/C/E i napajanje. |
| Jedan taster izgleda stalno pritisnut. | Provjeri par kontakata četvoronožnog tastera i vezu na odgovarajući GPIO. |
| Nakon držanja tastera nema nove akcije. | Potrebno je stabilno otpuštanje, pa novi pritisak. |
Samostalni zadaci
- Promijeni
CLEAR_MSsa 2000 na 5000. Izračunaj najraniji početak nadzora ako prihvaćeni ARM pritisak nastupi u 70. sekundi, a PIR neprekidno ostajeLOW. - U alarmu postavi 100 ms zvuka i 900 ms pauze, uz nepromijenjen ciklus od jedne sekunde.
- Predvidi brojač poslije sljedećeg niza: prvo aktiviranje, ACK, dodatni pokret, ARM za isključivanje, novi ARM ciklus i drugo aktiviranje. Objasni šta reset mijenja.
Razrađena rješenja
Za prvi zadatak postavi constexpr uint32_t CLEAR_MS = 5000;. Izlazno kašnjenje traje do približno 80. sekunde. Od prvog LOW opaženog u WAIT_CLEAR teče novih pet sekundi, pa nadzor počinje približno u 85. sekundi. Ne počinje u 80. sekundi samo zato što je PIR i ranije bio miran.
U updateOutputs() promijeni uslov za zujalicu iz elapsed % 1000UL < 200UL u elapsed % 1000UL < 100UL. Period ostaje 1000 ms, a udio vremena sa zvukom opada sa 20% na 10%. Mijenja se ritam uključivanja aktivne zujalice, ne njena frekvencija tona.
Prvo aktiviranje daje broj 1. ACK i dodatni pokret ne mijenjaju ga. ARM uklanja aktivno stanje alarma, ali ne vraća brojač na nulu. Nakon novog namjernog uključivanja i drugog aktiviranja broj je 2. Reset briše RAM brojač, vraća ga na 0 i ponovo pokreće WARMUP.
Provjera znanja
Šta tačno potvrđuje ACK? Da je korisnik prihvatio obavještenje. Ne potvrđuje da je prostor miran ili da je uzrok uklonjen.
Zašto WAIT_CLEAR dolazi nakon izlaznog kašnjenja? Time početak nadzora zahtijeva poseban, neprekinut period mirnog ulaza poslije udaljavanja.
Može li LOW sam ugasiti zapamćeni alarm? Ne. Alarm se zadržava kroz stanje programa sve do odgovarajuće korisničke komande.
Šta ima prednost u istom prolazu: ARM ili ACK? ARM/DISARM. U alarmu to znači potpuno isključivanje aktivnog stanja.
Zašto koristimo dva debouncera? Svaki taster ima nezavisna odskakivanja i sopstveni trenutak prihvatanja pritiska.
Primarni izvori
- SunFounder: HC-SR501 i početna stabilizacija.
- Adafruit: kako PIR detektuje promjene.
- onsemi: PN2222A i P2N2222A.
- Vishay: 1N4148.
- Arduino: primjer uklanjanja odskakivanja tastera.
- Arduino: millis().
