Dva releja: nezavisni kanali i zajedničko isključivanje

Upravljaj dvama relejnim kanalima preko dodatnih NPN tranzistora i prikaži četiri stanja pomoću LED krugova od 3,3 V. Obradi nezavisne tastere, prioritet zajedničkog isključivanja i napajanje obje zavojnice.

Integrisani projekti i korisničke komandeSrednji nivo105 min

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

KomponentaKoličinaSpecifikacija i napomene
ESP32 razvojna ploča sa klasičnim ESP32-WROOM-32 modulom1GPIO nazivi moraju odgovarati referentnom profilu; fizički položaj izvoda provjeriti na ploči.
USB kabl za podatke i napajanje1Priključak prilagoditi ploči; komplet na slici navodi Micro-USB.
Eksperimentalna ploča1Komplet navodi 830 kontaktnih tačaka. Provjeriti eventualni prekid naponskih vodova.
Dvokanalni relejski modul 5 V — aktivno LOW1Koriste 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 PN2222A2Obavezno dodatno — nije na slici kompletaDODATNO, 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Ω2R1 i R3 ograničavaju struju baza Q1 i Q2; po 1 kΩ za svaki tranzistor.
Otpornik 10 kΩ2R2 i R4 povlače baze Q1 i Q2 prema GND; jedan otpornik po bazi.
Otpornik 220 Ω2Po jedan otpornik za svaku LED granu; 220 Ω iz kompleta.
Crvena LED dioda1Anoda A i katoda K; provjeriti polaritet stvarne komponente.
Zelena LED dioda1Anoda A i katoda K.
Trenutni taster3Koristiti par priključaka koji se spaja pritiskom; ista strana četvoronožnog tastera može već biti spojena.
Spojni kablovi26M-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 1Kanal 2Crvena kontaktna LEDZelena kontaktna LED
00Ugašena.Ugašena.
10Svijetli.Ugašena.
01Ugašena.Svijetli.
11Svijetli.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.

Šema povezivanja — električne veze navedene u tabeli ispod
Šema povezivanjaUvećajSVGPNG
Veze · Šema povezivanja
OdDoOpis veze
ESP32.GPIO25R1.1R1, 1 kΩ, ograničava struju baze.
R1.2Q1.BBaza dodatnog PN2222A; provjeri fizički raspored B/C/E.
Q1.BR2.1R2, 10 kΩ, povlači bazu prema masi.
R2.2ESP32.GNDDrugi kraj baznog pull-down otpornika.
Q1.EESP32.GNDEmiter prema zajedničkoj masi.
ESP32.GPIO26R3.1R3, 1 kΩ, ograničava struju baze.
R3.2Q2.BBaza dodatnog PN2222A; provjeri fizički raspored B/C/E.
Q2.BR4.1R4, 10 kΩ, povlači bazu prema masi.
R4.2ESP32.GNDDrugi kraj baznog pull-down otpornika.
Q2.EESP32.GNDEmiter prema zajedničkoj masi.
Q1.CK1.IN1Q1 obara IN1 na LOW za uključivanje kanala 1.
Q2.CK1.IN2Q2 obara IN2 na LOW za uključivanje kanala 2; oba ulaza se sada koriste.
ESP32.USB5VK1.VCCProvjereni vod 5 V i strujni kapacitet za oba kanala; dokumentovani JD-VCC kratkospojnik ako postoji.
ESP32.GNDK1.GNDZajednička masa modula i oba upravljačka tranzistora.
ESP32.3V3K1.COM1Zajednički kontakt kanala 1: samo LED krug na 3,3 V.
K1.NO1R5.1Normalno otvoreni kontakt prema R5, 220 Ω.
R5.2D1.AOtpornik prema anodi LED.
D1.KESP32.GNDKatoda prema masi; NC1 ostaje nepovezan.
ESP32.3V3K1.COM2Zajednički kontakt kanala 2: samo LED krug na 3,3 V.
K1.NO2R6.1Normalno otvoreni kontakt prema R6, 220 Ω.
R6.2D2.AOtpornik prema anodi LED.
D2.KESP32.GNDKatoda prema masi; NC2 ostaje nepovezan.
ESP32.GPIO27SW1.CONTACT_ATaster A; ulaz INPUT_PULLUP.
SW1.CONTACT_BESP32.GNDPar kontakata koji se spaja samo pritiskom.
ESP32.GPIO32SW2.CONTACT_ATaster B; ulaz INPUT_PULLUP.
SW2.CONTACT_BESP32.GNDPar kontakata koji se spaja samo pritiskom.
ESP32.GPIO33SW3.CONTACT_ATaster ALL_OFF; ulaz INPUT_PULLUP.
SW3.CONTACT_BESP32.GNDPar kontakata koji se spaja samo pritiskom.
  1. Isključi USB. Potvrdi modul, raspored oba tranzistora i budžet napajanja. Identifikuj COM1/NO1/NC1 i COM2/NO2/NC2 prema oznakama modula.
  2. Spoji ESP32 GND, K1 GND i emitere Q1 i Q2 na zajednički GND.
  3. 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.
  4. 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.
  5. 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.
  6. 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.
  7. 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.
  8. 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

dual_relay.ino Arduino / C++
Preuzmi .ino

#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

PostupakOč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

SimptomProvjera 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

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.