Ciljevi učenja
- Povezati dva NPN stepena sa IN1 i IN2 odgovarajućeg aktivno-niskog modula.
- Predstaviti četiri kombinacije kanala dvobitnom maskom.
- Prebacivati kanale nezavisnim događajima tastera A i B.
- Dati ALL_OFF prioritet i odbaciti komande nastale dok je aktivan.
- Provjeriti napajanje za obje zavojnice i razlikovati softversko isključivanje od hardverskog hitnog zaustavljanja.
Prije početka
Kursevi 16 i 20: relejni kontakti, tranzistorski ulazni stepen, obrada više tastera i prioritet komande za isključivanje.
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 upravljanje dvama nezavisnim relejnim kanalima. Taster A mijenja stanje prvog, taster B stanje drugog, a treći taster daje zajedničku komandu za isključivanje. Kontakti prvog releja uključuju crvenu, a drugog zelenu LED, obje napajane sa 3,3 V. Dobijamo četiri jasno vidljiva stanja: oba isključena, samo prvi uključen, samo drugi uključen i oba uključena.
Vježba proširuje prethodni rad sa jednim relejem. Sada je potrebno istovremeno razmotriti dvije izlazne komande, prioritet tastera i napajanje dvije zavojnice. Cilj je napraviti razumljiv model upravljanja i provjeriti ga kroz unaprijed određene slučajeve.
Šta ćeš naučiti
- Povezati dva tranzistorska upravljačka stepena sa aktivno-niskim ulazima modula.
- Razdvojiti logičko stanje kanala od električnog nivoa na IN priključku.
- Predstaviti dva izlaza pomoću bitova i izračunati konačno stanje prije upisa.
- Dati zajedničkom isključivanju prednost nad ostalim komandama.
- Provjeriti napajanje za istovremeni rad obje zavojnice.
Predznanje
Završi kurseve 16 i 20. Potrebno je razumjeti COM, NO i NC, NPN priključke B/C/E, taster sa INPUT_PULLUP i obradu jednog događaja po pritisku. Koristimo klasični ESP32/WROOM32; raspored ne prenosi se automatski na C3, S2 ili S3 ploče.
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. |
| Dvokanalni relejski modul 5 V — aktivno LOW | 1 | Koriste se oba kanala referentnog aktivno-LOW modula 5 V, sa ulaznim pull-up otpornicima, ugrađenim drajverima i diodama zavojnica. Strujni budžet obuhvata obje zavojnice i ploču; tačne karakteristike provjeri na stvarnom modulu. |
| Dodatni NPN tranzistor PN2222A | 2Obavezno 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. |
| Otpornik 1 kΩ | 2 | R1 i R3 ograničavaju struju baza Q1 i Q2; po 1 kΩ za svaki tranzistor. |
| Otpornik 10 kΩ | 2 | R2 i R4 povlače baze Q1 i Q2 prema GND; jedan otpornik po bazi. |
| Otpornik 220 Ω | 2 | Po jedan otpornik za svaku LED granu; 220 Ω iz kompleta. |
| Crvena LED dioda | 1 | Anoda A i katoda K; provjeriti polaritet stvarne komponente. |
| Zelena LED dioda | 1 | Anoda A i katoda K. |
| Trenutni taster | 3 | Koristiti par priključaka koji se spaja pritiskom; ista strana četvoronožnog tastera može već biti spojena. |
| Spojni kablovi | 26 | M-M ili F-M prema priključcima ploče. Okvirna količina; zavisi od rasporeda i vrste priključaka. |
Potrebna su dva dodatna PN2222A tranzistora, koji nisu navedeni na fotografiji kompleta. Provjeri raspored B/C/E u dokumentaciji proizvođača svojih primjeraka. Slične oznake PN2222A i P2N2222A ne opravdavaju pretpostavku da imaju isti fizički redoslijed izvoda.
Pripremi po dva otpornika od 1 kΩ, 10 kΩ i 220 Ω. Fotografija kompleta ne potvrđuje raspoloživi broj svakog otpora. Referentni dvokanalni modul mora biti za 5 V, sa aktivno-niskim ulazima, njihovim pull-up vezama, ugrađenim upravljačima zavojnica i zaštitnim diodama. Q1 i Q2 upravljaju ulazima takvog modula; ne predstavljaju ovdje kompletan pogon golih releja.
Dva kanala, jedan prioritet i napajanje
Svaki kanal ima isti lanac: GPIO HIGH uključuje NPN, NPN obara odgovarajući IN na LOW, a aktivno-niski modul uključuje relej. GPIO LOW isključuje tranzistor, pa ulazni pull-up vraća IN na HIGH. Program zbog toga može koristiti jednostavno značenje „jedinica znači uključeno”.
| Kanal 1 | Kanal 2 | Crvena kontaktna LED | Zelena kontaktna LED |
|---|---|---|---|
| 0 | 0 | Ugašena. | Ugašena. |
| 1 | 0 | Svijetli. | Ugašena. |
| 0 | 1 | Ugašena. | Svijetli. |
| 1 | 1 | Svijetli. | Svijetli. |
Prije uključivanja obje zavojnice potvrdi ukupnu struju prema dokumentaciji modula i mogućnostima USB izvora, kabla i konkretne ploče. Budžet mora obuhvatiti ESP32, ulazna kola, obje zavojnice i indikaciju. Podatak 15–20 mA za upravljački ulaz nije ukupna potrošnja dvokanalnog modula. Ako ukupan budžet nije potvrđen, ne isprobavaj stanje 1/1 dok se napajanje ne provjeri.
ALL_OFF je prioritetna softverska komanda. Nije sertifikovano hardversko hitno zaustavljanje: zavisi od rada programa, napajanja i ispravnosti releja. U ovoj vježbi kontakti preklapaju samo LED krugove od 3,3 V; ne priključujemo motor, grijač ili mrežni napon.
Šema i redoslijed povezivanja
Šema povezivanja
Oba relejska ulaza koriste zasebne NPN drajvere. GPIO HIGH uključuje tranzistor i obara IN na LOW. Potrebna su dva dodatna PN2222A; modul već ima drajvere i diode zavojnica. COM/NO kontakti uključuju samo dvije LED grane na 3,3 V. Provjeri strujni kapacitet USB5V voda za ploču i obje uključene zavojnice.
| Od | Do | Opis veze |
|---|---|---|
ESP32.GPIO25 | 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.GPIO26 | R3.1 | R3, 1 kΩ, ograničava struju baze. |
R3.2 | Q2.B | Baza dodatnog PN2222A; provjeri fizički raspored B/C/E. |
Q2.B | R4.1 | R4, 10 kΩ, povlači bazu prema masi. |
R4.2 | ESP32.GND | Drugi kraj baznog pull-down otpornika. |
Q2.E | ESP32.GND | Emiter prema zajedničkoj masi. |
Q1.C | K1.IN1 | Q1 obara IN1 na LOW za uključivanje kanala 1. |
Q2.C | K1.IN2 | Q2 obara IN2 na LOW za uključivanje kanala 2; oba ulaza se sada koriste. |
ESP32.USB5V | K1.VCC | Provjereni vod 5 V i strujni kapacitet za oba kanala; dokumentovani JD-VCC kratkospojnik ako postoji. |
ESP32.GND | K1.GND | Zajednička masa modula i oba upravljačka tranzistora. |
ESP32.3V3 | K1.COM1 | Zajednički kontakt kanala 1: samo LED krug na 3,3 V. |
K1.NO1 | R5.1 | Normalno otvoreni kontakt prema R5, 220 Ω. |
R5.2 | D1.A | Otpornik prema anodi LED. |
D1.K | ESP32.GND | Katoda prema masi; NC1 ostaje nepovezan. |
ESP32.3V3 | K1.COM2 | Zajednički kontakt kanala 2: samo LED krug na 3,3 V. |
K1.NO2 | R6.1 | Normalno otvoreni kontakt prema R6, 220 Ω. |
R6.2 | D2.A | Otpornik prema anodi LED. |
D2.K | ESP32.GND | Katoda prema masi; NC2 ostaje nepovezan. |
ESP32.GPIO27 | SW1.CONTACT_A | Taster A; ulaz INPUT_PULLUP. |
SW1.CONTACT_B | ESP32.GND | Par kontakata koji se spaja samo pritiskom. |
ESP32.GPIO32 | SW2.CONTACT_A | Taster B; ulaz INPUT_PULLUP. |
SW2.CONTACT_B | ESP32.GND | Par kontakata koji se spaja samo pritiskom. |
ESP32.GPIO33 | SW3.CONTACT_A | Taster ALL_OFF; ulaz INPUT_PULLUP. |
SW3.CONTACT_B | ESP32.GND | Par kontakata koji se spaja samo pritiskom. |
- Isključi USB. Potvrdi modul, raspored oba tranzistora i budžet napajanja. Identifikuj COM1/NO1/NC1 i COM2/NO2/NC2 prema oznakama modula.
- Spoji ESP32 GND, K1 GND i emitere Q1 i Q2 na zajednički GND.
- GPIO25 poveži preko R1 od 1 kΩ na bazu Q1. Između baze Q1 i GND postavi R2 od 10 kΩ. Kolektor Q1 poveži na K1 IN1.
- GPIO26 poveži preko R3 od 1 kΩ na bazu Q2. Između baze Q2 i GND postavi R4 od 10 kΩ. Kolektor Q2 poveži na K1 IN2.
- VCC modula poveži na dokumentacijom potvrđeni USB izvod od 5 V na ploči. Ako modul ima JD-VCC, primijeni njegovu dokumentovanu vezu sa istim izvorom od 5 V.
- Poveži 3V3 sa COM1 i COM2. NO1 poveži preko R5 od 220 Ω sa anodom crvene D1, a NO2 preko R6 od 220 Ω sa anodom zelene D2. Obje katode poveži na GND; oba NC kontakta ostaju slobodna.
- SW1/A poveži između GPIO27 i GND, SW2/B između GPIO32 i GND, a SW3/ALL_OFF između GPIO33 i GND. Provjeri odabrane prekidane parove kontakata tastera.
- Ponovo pregledaj veze i otpornike, pa uključi USB. Početno oba releja i obje kontaktne LED moraju biti isključeni.
U ovoj šemi IN2 više nije vezan na 5 V kao nekorišćeni kanal iz kursa 16. Sada ga kontroliše kolektor Q2. Ni IN1 ni IN2 ne povezuje se direktno sa GPIO. Ne dodajemo diodu preko ulaza: zaštita zavojnice već mora postojati na odgovarajućem modulu. Ne spajaj nezavisni izvor od 5 V paralelno sa USB napajanjem.
Priprema Arduino okruženja
Koristi Arduino IDE 2.x, esp32 by Espressif Systems 3.3.12, ploču ESP32 Dev Module i odgovarajući port. Dodatne biblioteke nisu potrebne. Otvori dual_relay/dual_relay.ino, provjeri izlaze GPIO25/26 i tastere GPIO27/32/33, pa pokreni Verify i Upload. Serial Monitor podesi na 115200 baud.
Serijski ispis prati promjene logičkih stanja. Kontaktne LED daju zasebnu provjeru puta kroz COM i NO. Sam zvuk releja ili ugrađena indikatorska LED ne potvrđuju da si spoljašnji LED krug povezao na odgovarajuće kontakte.
Kompletan Arduino program
#include <Arduino.h>
constexpr uint8_t RELAY_1_PIN = 25, RELAY_2_PIN = 26;
constexpr uint8_t BUTTON_A_PIN = 27, BUTTON_B_PIN = 32, ALL_OFF_PIN = 33;
constexpr uint32_t DEBOUNCE_MS = 30;
// 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 buttonA(BUTTON_A_PIN), buttonB(BUTTON_B_PIN), offButton(ALL_OFF_PIN);
uint8_t channelMask = 0;
void applyChannels(uint8_t nextMask) {
channelMask = nextMask & 0x03;
// GPIO HIGH -> NPN ON -> module IN LOW -> relay ON.
digitalWrite(RELAY_1_PIN, channelMask & 0x01 ? HIGH : LOW);
digitalWrite(RELAY_2_PIN, channelMask & 0x02 ? HIGH : LOW);
Serial.print("Relay 1="); Serial.print(channelMask & 0x01 ? "ON" : "OFF");
Serial.print(" Relay 2="); Serial.println(channelMask & 0x02 ? "ON" : "OFF");
}
void setup() {
Serial.begin(115200);
pinMode(RELAY_1_PIN, OUTPUT); pinMode(RELAY_2_PIN, OUTPUT);
digitalWrite(RELAY_1_PIN, LOW); digitalWrite(RELAY_2_PIN, LOW);
const uint32_t now = millis();
buttonA.begin(now); buttonB.begin(now); offButton.begin(now);
applyChannels(0);
}
void loop() {
const uint32_t now = millis();
buttonA.update(now); buttonB.update(now); offButton.update(now);
uint8_t nextMask = channelMask;
if (offButton.pressed) nextMask = 0;
// Raw LOW blocks new toggles immediately; a qualified OFF press clears
// both outputs. Stable LOW keeps the block through release debounce.
const bool offInhibits = !offButton.rawHigh || !offButton.stableHigh;
if (offInhibits || offButton.pressed) {
buttonA.inhibit();
buttonB.inhibit();
} else {
if (buttonA.pressed) nextMask ^= 0x01;
if (buttonB.pressed) nextMask ^= 0x02;
}
// Compute the complete mask before changing outputs. GPIO writes are still
// sequential; this is an LED demonstration, not a hardware interlock.
if (nextMask != channelMask) applyChannels(nextMask);
}
Počni sa neizmijenjenim programom i unaprijed označenim tasterima. Nemoj preuzeti obrnuti polaritet iz šeme sa direktnim upravljanjem: ovdje dodatni NPN mijenja odnos GPIO i IN nivoa.
Kako program radi
channelMask čuva stanje dva kanala. Bit 0 odgovara prvom, a bit 1 drugom kanalu. Vrijednosti su zato 0 za oba isključena, 1 za samo prvi, 2 za samo drugi i 3 za oba uključena. Bitovska operacija XOR sa maskom 1 ili 2 prebacuje samo izabrani kanal.
Tri nezavisno obrađena tastera koriste INPUT_PULLUP i 30 ms stabilnosti. Jedan novi pritisak daje jedan događaj; držanje ne ponavlja uključivanje i isključivanje. Taster koji je pritisnut pri resetu mora se prvo otpustiti prije prihvatanja nove komande.
Potvrđen novi pritisak ALL_OFF, poslije 30 ms stabilnosti, postavlja nextMask na nulu. Raniji sirovi LOW već potiskuje komande A i B, a stabilni LOW održava zabranu kroz potvrđivanje otpuštanja. Pritisci učinjeni tokom te zabrane ne čuvaju se za kasnije. Nakon toga A ili B moraju biti otpušteni i ponovo pritisnuti da bi uključili kanal.
Bez ALL_OFF zabrane, dva prihvaćena događaja A i B u istoj iteraciji prebacuju oba kanala. Program prvo izračuna konačnu masku, pa je predaje applyChannels(). Time se izbjegava da redoslijed obrade stvori dodatno privremeno logičko stanje. Fizički upisi na dva GPIO ipak se izvršavaju redom; ovo nije obećanje istovremenog pomjeranja kontakata u istom mikrosekundu.
Eksperiment i očekivani rezultati
| Postupak | Očekivano stanje kanala 1 / kanala 2 |
|---|---|
| Reset sa otpuštenim tasterima. | 0 / 0. |
| Pritisni A, otpusti, pa pritisni B. | Najprije 1 / 0, zatim 1 / 1. |
| Iz 1 / 1 napravi novi pritisak A. | 0 / 1; drugi kanal ostaje uključen. |
| Pritisni ALL_OFF. | 0 / 0. |
| Drži ALL_OFF i pritiskaj A i B. | Oba ostaju isključena; nema odložene komande po otpuštanju. |
| Iz 0 / 0 program prihvati A i B u istoj iteraciji, bez ALL_OFF. | 1 / 1. |
| Resetuj dok držiš A. | 0 / 0; za uključivanje je potrebno otpuštanje i novi pritisak. |
Zabilježi serijski ispis i obje kontaktne LED. Dva fizička pritiska „u isto vrijeme” ne moraju biti potvrđena u istoj iteraciji; mogu se pojaviti kao dvije uzastopne, ispravne promjene. Provjera 1/1 izvodi se tek poslije potvrde napajanja za obje zavojnice.
Najčešći problemi
| Simptom | Provjera i postupak |
|---|---|
| Drugi kanal nikada ne radi. | Provjeri GPIO26, Q2 B/C/E i IN2; ukloni staru vezu IN2 na 5 V iz prethodne vježbe. |
| LED svijetli kad program prijavljuje OFF. | Isključi USB i provjeri da li koristiš NC umjesto NO. |
| ESP32 se resetuje pri uključivanju oba kanala. | Isključi sklop i provjeri ukupnu struju, USB put i spojeve; ne zaključuj da je uzrok program. |
| Kanal reaguje više puta na držanje. | Provjeri da koristiš kanonski kod sa debouncingom i događajem novog pritiska. |
| A i B ne rade poslije ALL_OFF. | Otpusti sva dugmad, pa napravi nov pritisak; provjeri da ALL_OFF ulaz nije stalno na GND. |
Samostalni izazovi
Prvo napiši tok maski za slijed A, B, A, B, uz početnu nulu i otpuštanje između pritisaka. Zatim napravi posebnu varijantu u kojoj istovremeno može raditi samo jedan kanal: uključivanje jednog isključuje drugi. Unaprijed odredi pravilo kada se A i B prihvate zajedno, a ALL_OFF ostavi najvišim prioritetom.
Razrađeno rješenje izazova
Osnovni slijed daje maske 0 → 1 → 3 → 2 → 0. Poslije drugog pritiska oba kanala rade, jer osnovni program nema međusobno isključivanje. Svaki XOR mijenja samo svoj bit.
Za dodatnu varijantu izaberi pravilo „A ima prednost nad B kada stignu zajedno”. Sačuvaj ALL_OFF provjeru, offInhibits i pozive inhibit(). Samo u postojećoj else grani zamijeni dva XOR uslova ovim blokom:
if (buttonA.pressed) {
nextMask = (channelMask & 0x01) ? 0 : 1;
} else if (buttonB.pressed) {
nextMask = (channelMask & 0x02) ? 0 : 2;
}
Zadrži postojeći završni poziv applyChannels() kada se maska promijeni. Već uključeni izabrani kanal se gasi; inače se uključuje samo on.
Primjer: iz maske 1 pritisak B daje 2; iz 2 pritisak B daje 0; istovremeni A/B iz 0 daje 1. Ovo je izmjena pravila LED demonstracije. Sama softverska uzajamna isključivost ne predstavlja zaštitni sklop za promjenu smjera motora.
Provjera znanja sa odgovorima
Zašto GPIO HIGH uključuje aktivno-niski modul? GPIO uključuje tranzistor, a kolektor spušta IN na LOW. Naziv aktivno-niski odnosi se na IN, ne na GPIO prije tranzistora.
Zašto ne staviti diodu preko IN1? IN1 je upravljački ulaz. Dioda za energiju zavojnice pripada izlaznom kolu modula i mora biti ugrađena prema njegovoj dokumentaciji.
Da li ALL_OFF prekida napajanje uređaja? Ne. Postavlja izlazne komande na OFF; ESP32 i modul ostaju napajani. Zašto zbir nije samo dva puta 15–20 mA? Takav podatak za ulaz ne obuhvata nužno zavojnice i ostalu potrošnju.
Primarni izvori
- SunFounder: referentni dvokanalni aktivno-niski modul od 5 V.
- SunFounder: relej i COM/NO/NC kontakti.
- onsemi: PN2222A tehnički list.
- onsemi: P2N2222A tehnički list za poređenje priključaka.
- Espressif: ESP32-DevKitC i napajanje.
- Espressif: GPIO API.
Preuzimanja
Preuzmi program i šemu oba kanala. Zabilježi zasebno rezultat stvarne ESP32 kompilacije i fizičke probe; očekivano ponašanje iz lekcije nije potvrda da su tvoj modul, ploča i napajanje već ispitani.
