Skip to main content

7.4 RGB LED kontroler

Ovaj projekt tjera RGB LED-icu da mijenja boju sa svjetlom u prostoriji. U tami svijetli crveno; kako svjetlo raste, boja klizi kroz zelenu i plavu, a na stvarno jakom svjetlu postaje bijela.

Spaja dvije stvari koje si već sagradio. Krug s fotootpornikom točno je onaj iz 2.3 Analogno čitanje fotootpornika, a ideja postavljanja svjetline umjesto samo uključivanja i isključivanja pina dolazi iz 3.2 Udaljenost i zatamnjivanje LED-ice. Novo je to što jedna vrijednost senzora sada pobuđuje tri izlaza u isto vrijeme.

U ovoj dokumentaciji naučit ćeš:

  • Kako naći četiri nožice RGB LED-ice i koja je dijeljena.
  • Kako se jedna analogna vrijednost može pretvoriti u tri odvojene svjetline.
  • Kako PWM miješa crvenu, zelenu i plavu u bilo koju boju između.
  • Kako pretvoriti poznate brojeve boja 0–255 u ono što duty_u16() želi.
  • Zašto boja glatko prelazi umjesto da skače.

Potreban hardver:

  • 1x Soldered NULA MINI pločica
  • 1x Breadboard
  • 1x Fotootpornik (LDR)
  • 1x RGB LED-ica
  • 1x otpornik od 10 kΩ
  • 3x otpornika od 330 Ω
  • 8x Jumper žice
  • 1x USB-C kabel
ℹ️
Ovaj primjer koristi dvije različite vrste otpornika, koje rade dva potpuno različita posla. Onaj od 10 kΩ ne štiti ništa. On je polovica mjernog kruga, točno kao u 2.3. Tri od 330 Ω su ograničivači struje, po jedan za svaki kanal boje. RGB LED-ica je tri LED-ice u jednom pakiranju, pa joj trebaju tri.
⚠️
Provjeri prstenove prije nego bilo što utisneš. 10 kΩ se čita smeđi-crni-narančasti, a 330 Ω narančasti-narančasti-smeđi. Lako ih je pomiješati, a njihova zamjena neće ništa oštetiti ali će zaustaviti rad kruga.

Spajanje komponenti

Prati dvanaest koraka ispod. Svaka fotografija snimljena je s istog položaja, pa je možeš usporediti s prethodnom i vidjeti točno što se promijenilo.

ℹ️
Sve u ovom primjeru živi na f–j strani breadboarda, jer je svaki pin koji mu treba na tom rubu pločice: IO2 u redu 25, IO3 u redu 26, IO4 u redu 27, IO5 u redu 28, 3V3 u redu 29 i GND u redu 30. Pločica prekriva većinu tih redova, pa su krajnje rupice (j25 do j30) one do kojih možeš doći.

1. Umetni NULA MINI pločicu na breadboard

ℹ️
Ovaj korak pretpostavlja da znaš kako je breadboard spojen iznutra i što su njegove naponske vodilice (power rails). Za uvod pogledaj stranicu dokumentacije Osnove breadboarda.

Utisni pločicu u jedan kraj breadboarda tako da njezina dva reda pinova sjednu s obje strane središnjeg kanala, s čipom okrenutim prema dolje. Trebala bi zauzeti redove 25 do 30.

NULA MINI pločica postavljena na breadboard
Korak 1: pločica postavljena na breadboard

2. Izvedi masu na vodilicu

Jedna jumper žica iz j30 (GND) do plave vodilice. Ta vodilica postaje dijeljena masa cijelog kruga, i obje se polovice vraćaju na nju.

Jumper žica iz j30 na plavu negativnu vodilicu
Korak 2: GND izveden na plavu − vodilicu

3. Izvedi 3.3 V u red 14

Druga jumper žica, iz j29 (3V3) gore u red 14. To je vrh mjernog kruga.

Jumper žica iz j29 u red 14
Korak 3: 3V3 izveden u red 14

4. Dodaj fotootpornik

Fotootpornik premošćuje red 14, u koji sada dolazi 3.3 V, i red 11.

Fotootpornik premošćuje redove 14 i 11
Korak 4: fotootpornik premošćuje redove 14 i 11
ℹ️
Fotootpornik nema polaritet: bilo koja nožica može ići na bilo koju stranu. To je samo otpornik čija se vrijednost mijenja sa svjetlom.

5. Dodaj otpornik od 10 kΩ

Otpornik nastavlja lanac prema dolje, iz reda 11 u red 9.

Otpornik od 10k premošćuje redove 11 i 9
Korak 5: otpornik od 10 kΩ premošćuje redove 11 i 9

6. Spoji otpornik na masu

Jedna jumper žica iz reda 9 do plave vodilice.

Jumper žica spaja red 9 na negativnu vodilicu
Korak 6: donji kraj otpornika od 10 kΩ spojen na − vodilicu

Lanac je sada potpun: 3.3 V → fotootpornik → 10 kΩ → GND. Struja teče kroz oba dijela serijski.

7. Zahvati sredinu pomoću IO5

Sada samo mjerenje. Povuci jumper žicu iz j28 (IO5) u red 11, red koji dijele donja nožica fotootpornika i gornji izvod otpornika.

IO5 spojen na spoj između fotootpornika i otpornika
Korak 7: IO5 zahvaća spoj u redu 11
ℹ️
Red 11 je cijela svrha ove polovice kruga. On je središnja točka između dva otpora, i njegov se napon mijenja kako se mijenja svjetlo. To je ono što IO5 mjeri. Ako žica sleti u red 14 ili red 9, i dalje ćeš dobivati vrijednost. Samo se neće mijenjati kada se svjetlo mijenja.

8. Dodaj RGB LED-icu

RGB LED-ica je tri LED-ice u jednom pakiranju koje dijele jednu nožicu. Drži je s nožicama okrenutima prema dolje i nađi najdulju. To je dijeljena nožica, zajednička katoda. Na ovoj LED-ici ona je treća po redu, s jednom nožicom s jedne strane i dvjema s druge.

Postavi LED-icu tako da njezine četiri nožice slete u redove 1, 2, 3 i 4, s dugom nožicom u redu 3:

NožicaRed
Crvena, krajnja, najdalja od duge nožice1
Zelena, između crvene i duge nožice2
Zajednička katoda, najdulja nožica3
Plava, sama s druge strane duge nožice4
RGB LED-ica postavljena s četiri nožice u redovima 1 do 4
Korak 8: RGB LED-ica s četiri nožice u redovima 1, 2, 3 i 4
⚠️
To nije redoslijed nožica koji koristi većina RGB LED-ica, i nije redoslijed koji ćeš naći u većini vodiča, koji obično pretpostavljaju crvena–zajednička–zelena–plava s dijeljenom nožicom na drugom mjestu. LED-ica u ovom kitu stvarno ide crvena, zelena, zajednička, plava. Vodi se dužinom nožica, a ne dijagramom, a ako boje na kraju izađu pogrešno, ovo je prva stvar koju treba provjeriti.

9. Spoji zajedničku katodu na masu

Jedna jumper žica iz reda 3 do plave vodilice. Sva tri kanala boje vraćaju se na masu preko te jedne žice.

Jumper žica spaja red zajedničke katode na negativnu vodilicu
Korak 9: zajednička katoda spojena na plavu − vodilicu

10. Dodaj tri otpornika od 330 Ω

Svakom kanalu boje treba vlastiti ograničivač struje. Premosti svaku od tri nožice boje do slobodnog reda dalje duž pločice:

  • iz reda 1 (crvena) u slobodan red
  • iz reda 2 (zelena) u drugi slobodan red
  • iz reda 4 (plava) u treći slobodan red
Tri otpornika od 330 ohma dodana, po jedan za svaki kanal boje
Korak 10: po jedan otpornik od 330 Ω za svaki kanal boje
ℹ️
Nije važno u koje redove sletiš, dokle god je svaki prazan i nijedan otpornik ne dijeli red s drugim. Raširi ih po različitim stupcima da im tijela ne sjede jedno na drugom. Drži red 3 slobodnim, jer je to zajednička katoda, a i red 9, jer on već pripada spoju mase fotootpornika.
⚠️
Otpornik s obje nožice u istom redu ne radi ništa. Svaka rupica u redu je jedan spoj, pa je otpornik koji premošćuje dvije rupice u istom redu jednostavno kratko spojen. Kanal se i dalje pali, često svjetlije od druga dva, ali radi bez ikakvog ograničenja struje. Svaki otpornik mora obuhvatiti dva različita reda.

11. Spoji tri kanala boje na pločicu

Još tri jumper žice, s dalekog kraja svakog otpornika natrag na odgovarajući pin. Koristi tri različite boje žica, jer idu jedna uz drugu i kasnije ćeš ih htjeti razlikovati.

KanalIzU
Crvenaslobodan red crvenog otpornikaj25 (IO2)
Zelenaslobodan red zelenog otpornikaj26 (IO3)
Plavaslobodan red plavog otpornikaj27 (IO4)
Tri obojene jumper žice spajaju otpornike na IO2, IO3 i IO4
Korak 11: tri kanala boje spojena na IO2, IO3 i IO4
⚠️
Redovi 25, 26 i 27 razmaknuti su jednu rupicu i skriveni pod rubom pločice, zbog čega ih je lako pogrešno prebrojiti. Broji unutra od j30 na samom kraju (GND, 3V3, IO5, IO4, IO3, IO2), a ne nagađaj iz sredine.

12. Spoji pločicu na računalo

Gotov krug napajan preko USB-C
Korak 12: gotov krug, napajan preko USB-C

Kako jedan senzor postaje tri boje

Čitanje svjetla

Pločica ne može mjeriti otpor, samo napon. Fotootpornik mijenja svoj otpor sa svjetlom, pa sam za sebe pločici ne daje ništa za čitanje. Spajanje s fiksnim otpornikom od 10 kΩ pravi djelitelj napona, a napon u točki između njih ovisi o odnosu njihovih otpora:

  • Na jakom svjetlu otpor fotootpornika pada, zadržava manje od 3.3 V za sebe, i vrijednost na IO5 raste.
  • U tami njegov otpor raste, zadržava veći dio napona, i vrijednost pada.

ldr.read() taj napon pretvara u broj od 0 do 4095, jer NULA MINI ima 12-bitni ADC.

Postavljanje boje

Svaki kanal je PWM objekt, stvoren s brzinom prebacivanja zadanom unaprijed:

pwm_r = PWM(Pin(RED_PIN), freq=PWM_FREQ)
pwm_g = PWM(Pin(GREEN_PIN), freq=PWM_FREQ)
pwm_b = PWM(Pin(BLUE_PIN), freq=PWM_FREQ)

Umjesto da pin samo uključi potpuno ili isključi potpuno, PWM ga uključuje i isključuje vrlo brzo, ovdje 1000 puta u sekundi, i što pin dulje ostaje uključen unutar svakog ciklusa, to taj kanal izgleda svjetlije. Pisanje svih triju odjednom je ono što miješa boju.

Problem 0–255, i funkcija koja ga rješava

Svi o boji razmišljaju u rasponu 0 do 255: 255, 0, 0 je crvena, 0, 255, 0 je zelena, 255, 255, 255 je bijela. To je ono što koristi svaki program za slikanje i svaka web stranica.

Ali duty_u16() želi broj od 0 do 65535. Umjesto da tu pretvorbu razaspe po kodu za boje, skripta je radi jednom u vlastitoj funkciji:

def write_colour(pwm, brightness):
pwm.duty_u16(int(brightness * 65535 / 255))
ℹ️
To je uredan odgovor na pravu razliku između dvije platforme. Arduino skica piše analogWrite(RED_PIN, r) i izvorno radi u 0–255, pa joj pretvorba ne treba uopće. MicroPython radi u 0–65535, a write_colour() tu činjenicu drži na jednom mjestu tako da logika boja ispod može ostati u poznatom rasponu.
ℹ️
int() ponovno radi nužan posao. brightness * 65535 / 255 u Pythonu proizvodi decimalni broj, a duty_u16() ga odbija. Bez int() skripta pada s TypeError.

Spajanje svega

Raspon svjetla razdijeljen je na tri jednaka dijela. FIRST_THIRD je 1365 a SECOND_THIRD je 2730, jednostavno 4095 podijeljen na trećine, a svaki dio dobiva svoj prijelaz:

LDR vrijednostŠto kod radiRezultat
0 → 1365crvena slabi dok zelena jačacrvena → zelena
1366 → 2730zelena slabi dok plava jačazelena → plava
2731 → 4095plava ostaje puna dok crvena i zelena rastuplava → bijela

U svakom dijelu jedan kanal preslikava se prema gore dok se drugi preslikava prema dolje. To je ono što jednu boju tjera da klizne u sljedeću umjesto da skoči. A budući da svaki dio počinje točno tamo gdje je prethodni završio, granice su nevidljive: na 1365 boja je čista zelena s koje god strane joj pristupiš.

Samo preskaliranje koristi istu funkciju value_map() koju si napisao u 3.2, jer MicroPython i dalje nema ugrađen map().


Kod

# ADC mjeri stvarni napon na pinu i daje nam broj za njega, a tako čitamo razinu svjetla. PWM
# uključuje i isključuje pin vrlo brzo, a tako postavljamo svjetlinu umjesto samo uključeno ili isključeno.
from machine import Pin, ADC, PWM
import time

# Ovo je varijabla kojoj predajemo broj pina na koji smo spojili izlaz fotootpornika.
# Budući da ovdje moramo pročitati cijeli raspon vrijednosti a ne samo visoko ili nisko, to mora biti pin koji podržava
# analogni ulaz.
#
# Ovom primjeru također treba otpornik od 10k. Fotootpornik mijenja svoj otpor sa svjetlom, ali pločica može
# mjeriti samo napon, pa ta dva spajamo u ono što se naziva djelitelj napona.
LDR_PIN = 5

# Ovo su varijable kojima predajemo brojeve pinova na koje smo spojili tri kanala boje
# RGB LED-ice. RGB LED-ica je u stvari tri LED-ice u jednom pakiranju, jedna crvena, jedna zelena i jedna plava, a paljenjem
# na različitim jačinama možemo pomiješati bilo koju boju koju želimo.
#
# Zapamti da svakom od tri kanala boje treba vlastiti otpornik od 330 ohma u seriji s njim. RGB LED-ica se računa
# kao tri LED-ice, pa traži tri otpornika.
RED_PIN = 2
GREEN_PIN = 3
BLUE_PIN = 4

# Ovoliko brzo PWM pinovi prebacuju uključeno i isključeno. 1000 puta u sekundi je daleko brže nego što naše oči mogu pratiti, pa
# vidimo postojane boje umjesto treperenja.
PWM_FREQ = 1000

# Ove dvije varijable razdjeljuju raspon svjetla na tri jednaka dijela, a to je ono što nam daje naša tri prijelaza
# boje. Vrijednosti fotootpornika idu od 0 do 4095, a 4095 podijeljeno s tri je 1365.
FIRST_THIRD = 1365
SECOND_THIRD = 2730

# Ovdje stvaramo naš ADC objekt, koji smo nazvali "ldr". Postavka atten odabire koliko velik napon pretvarač
# može mjeriti, a ADC.ATTN_11DB je najšira postavka, koja nam dopušta korištenje cijelog raspona fotootpornika.
ldr = ADC(Pin(LDR_PIN), atten=ADC.ATTN_11DB)

# Ovdje stvaramo po jedan PWM objekt za svaki kanal boje, sve na istoj brzini prebacivanja.
pwm_r = PWM(Pin(RED_PIN), freq=PWM_FREQ)
pwm_g = PWM(Pin(GREEN_PIN), freq=PWM_FREQ)
pwm_b = PWM(Pin(BLUE_PIN), freq=PWM_FREQ)


def value_map(value, in_min, in_max, out_min, out_max):
# Ovo je funkcija koju smo napisali sami, jer ne postoji gotova funkcija za preskaliranje broja.
# Uzima broj iz jednog raspona i preskalira ga u drugi raspon, a tako razinu svjetla pretvaramo u
# svjetlinu.
return int((value - in_min) * (out_max - out_min) / (in_max - in_min) + out_min)


def write_colour(pwm, brightness):
# Ova funkcija šalje jedan kanal boje na njegov pin.
# O bojama razmišljamo kao o brojevima od 0 do 255, kako to radi gotovo svaki program, ali duty_u16() želi
# broj od 0 do 65535. Ova funkcija tu pretvorbu radi na jednom mjestu, pa kod za boje ispod ostaje lako
# čitljiv.
pwm.duty_u16(int(brightness * 65535 / 255))


# Ispiši početnu poruku da znamo da se program uspješno pokrenuo.
print("RGB LED Controller with full-spectrum color mapping started")

while True:

# read() čita napon na analognom pinu i pretvara ga u broj. Budući da NULA pločica koristi 12-bitni
# ADC, vrijednost ide od 0 u potpunoj tami do 4095 na jakom svjetlu. Što više svjetla pada na
# fotootpornik, to je njegov otpor manji a ovaj broj veći.
ldr_value = ldr.read()
print("LDR value:", ldr_value)

# Ovdje se boja odlučuje. Raspon svjetla razdjeljujemo na tri dijela i svakom dajemo vlastiti
# prijelaz, tako da boja nikada ne skoči: uvijek klizne odakle je bila u sljedeću boju.
# Primijeti kako se u svakom dijelu jedan kanal preslikava prema gore dok se drugi preslikava prema dolje.
if ldr_value <= FIRST_THIRD:

# Najtamnija trećina: prijelaz od crvene (255, 0, 0) do zelene (0, 255, 0).
r = value_map(ldr_value, 0, FIRST_THIRD, 255, 0)
g = value_map(ldr_value, 0, FIRST_THIRD, 0, 255)
b = 0

elif ldr_value <= SECOND_THIRD:

# Srednja trećina: prijelaz od zelene (0, 255, 0) do plave (0, 0, 255).
r = 0
g = value_map(ldr_value, FIRST_THIRD + 1, SECOND_THIRD, 255, 0)
b = value_map(ldr_value, FIRST_THIRD + 1, SECOND_THIRD, 0, 255)

else:

# Najsvjetlija trećina: prijelaz od plave (0, 0, 255) do bijele (255, 255, 255). Bijela je jednostavno sva tri kanala uključena
# odjednom, i zato crvena i zelena ovdje rastu dok plava ostaje na punoj svjetlini.
r = value_map(ldr_value, SECOND_THIRD + 1, 4095, 0, 255)
g = value_map(ldr_value, SECOND_THIRD + 1, 4095, 0, 255)
b = 255

# Ovdje pišemo sva tri kanala odjednom, a to je ono što miješa boju.
write_colour(pwm_r, r)
write_colour(pwm_g, g)
write_colour(pwm_b, b)

# Ispiši i pomiješanu boju, da je možemo usporediti s razinom svjetla iznad.
print("RGB:", r, g, b)

# Kratka pauza između vrijednosti. Držanje male čini promjene boje glatkima.
time.sleep_ms(100)

Što bi trebao vidjeti

Pritisni Run. Pojavljuje se početna poruka, a zatim dvije linije dolaze deset puta u sekundi: sirova vrijednost svjetla i tri svjetline koje je kod iz nje izračunao.

U običnoj osvijetljenoj prostoriji vrijednost sjedi nešto ispod trećine puta gore po rasponu, u prvom pojasu, pa se LED-ica smjesti na zelenu s nešto crvene u sebi.

RGB LED-ica svijetli zeleno na gotovom krugu
Obično svjetlo u prostoriji: LED-ica svijetli zeleno
RGB LED Controller with full-spectrum color mapping started
LDR value: 1057
RGB: 57 197 0
LDR value: 1058
RGB: 57 197 0

Sada stavi ruku preko fotootpornika. Vrijednost pada negdje oko stotinu, što je gotovo dno prvog pojasa, i boja prelazi na crvenu.

LDR value: 106
RGB: 235 19 0

Odmakni ruku i umjesto toga prisloni svjetlo mobitela na senzor. Vrijednost se uzdiže u srednji pojas i boja ide na plavu.

RGB LED-ica svijetli plavo na gotovom krugu
Svjetlo mobitela na senzoru: LED-ica svijetli plavo
LDR value: 2465
RGB: 0 49 205

Te vrijednosti vrijedi usporediti, jer računanje koda možeš provjeriti napamet. Na 1057 vrijednost je u prvom pojasu, pa se crvena preslikava dolje od 255 a zelena gore od 0: 255 − 1057 × 255 ÷ 1365 je 57.5, a int() odbacuje decimalni dio i daje 57 koji je ispisan. Broj na ekranu i boja pred tobom su ista činjenica, dvaput.

ℹ️
int() uvijek reže prema nuli, nikada ne zaokružuje. 57.5 postaje 57, a ne 58. To vrijedi znati ako te brojeve usporediš s Arduino verzijom istog projekta, koja ovdje ispisuje 58: Arduinov map() cijelo računanje radi u cijelim brojevima i decimalni dio odbacuje na drugom mjestu, pa se dvije platforme mogu razlikovati za jedan na svakom kanalu koji se preslikava prema dolje. Jedna jedinica od 255 daleko je previše mala da bi se vidjela.

Cijeli raspon

Što radišPribližno što ćeš pročitatiRGBBoja
Čvrsto ga prekrij dlanom~90–150232, 22, 0crvena
Zasjeni ga rukom, opušteno~260–430190, 64, 0narančasto-crvena
Ostavi ga u osvijetljenoj prostoriji~1000–107061, 193, 0zelena
Osvijetli ga svjetlom mobitela~2300–25300, 58, 196plava
ℹ️
Plava je u praksi vrh raspona, a ne bijela. Kod plavu prelijeva u bijelu kroz sve iznad 2730, ali na ovom krugu svjetlo mobitela prislonjeno na senzor doseže samo oko 2530, pa taj posljednji pojas traži direktno sunčevo svjetlo ili pravu lampu da bi se u njega uopće ušlo. Ne čitaj bijelu koje nema kao grešku u spajanju. Crvena je, s druge strane, lako: obična prostorija već sjedi nisko u rasponu, pa je dovoljno samo staviti ruku nad senzor.
ℹ️
Tvoji brojevi neće se točno poklapati s ovima. Ovise o svjetlu u tvojoj prostoriji i o samom fotootporniku. Važno je da se vrijednost miče kada se svjetlo mijenja, i da se miče kroz dovoljno raspona da promijeni boju. Ako stoji na fiksnom broju bez obzira što radiš, vrati se na korak 7 i provjeri da IO5 stvarno zahvaća red 11.

Ako boje izađu pogrešno

Ako se LED-ica pali ali prikazuje pogrešnu boju (zelenu gdje očekuješ crvenu, ili jedan kanal koji se nikada ne upali), krug je u redu a preslikavanje nije. Prođi kroz to u ovom redoslijedu:

  1. Provjeri je li duga nožica u redu 3 i spojena na vodilicu. Ako je dijeljena nožica negdje drugdje, kanali se međusobno bore i većinom ostaju tamni.
  2. Provjeri obuhvaća li svaki otpornik dva reda. Kratko spojen otpornik čini svoj kanal daleko svjetlijim od druga dva, što se čita kao da ta boja preuzima.
  3. Zamijeni jumper žice na strani pločice. Ako su crvena i plava zamijenjene, zamijeni žice u j25 i j27. Konzola ti kaže koju je boju pločica namjeravala: ako ispisuje RGB: 255 0 0 a ti vidiš plavu, dva kanala su prekrižena.
  4. Blijeda, isprana boja koja se jedva miče upućuje na postavku atten, a ne na LED-icu. S ADC.ATTN_0DB na mjestu ADC.ATTN_11DB isto svjetlo u prostoriji čita blizu 3900 umjesto oko 1050, što kod parkira u njegovom najvišem pojasu i ostavlja LED-icu blizu bijele što god radiš.

Ta posljednja je korisna navika: linija RGB je vlastita izjava pločice o tome što pokušava prikazati, pa je svako neslaganje između nje i LED-ice pred tobom greška u spajanju, a ne greška u kodu.

ℹ️
Boja koja ostane svijetliti nakon što pritisneš Stop je normalna. Ništa kanale ne gasi na izlasku, pa posljednja pomiješana boja ostaje upaljena dok ne pokreneš nešto drugo ili resetiraš pločicu.

Cijeli primjer

Pogledaj cijeli kod primjera na linku ispod:

7.4_RGB_LED_Controller.py

Projekt koji pomoću fotootpornika upravlja RGB LED-icom, glatko mijenjajući boju od crvene do bijele na temelju jačine okolnog svjetla.