Skip to main content

3.2 Udaljenost i zatamnjivanje LED-ice

U 3.1 Mjerenje udaljenosti vrijednost senzora išla je pravo u Serial Monitor i nikamo drugdje. Ovaj primjer uzima istu vrijednost i s njom nešto radi: što je objekt bliže senzoru, to LED-ica jače svijetli.

To je veći korak nego što zvuči. Do sada je pin bio ili uključen ili isključen, a LED-ica ili upaljena ili ugašena. Ovdje pin mora proizvesti sve između, a udaljenost u centimetrima mora se pretvoriti u razinu svjetline. Taj posao obavljaju dvije nove funkcije: analogWrite() i map().

U ovoj dokumentaciji naučit ćeš:

  • Što je PWM, i kako tjera digitalni pin da se ponaša kao prigušivač.
  • Kako koristiti analogWrite() za postavljanje svjetline umjesto samo HIGH ili LOW.
  • Zašto analogWriteResolution() mora biti postavljen prvi, ili se LED-ica uopće neće zatamnjivati.
  • Kako ograničavanje (clamping) drži mjerenje unutar raspona kojem ostatak koda može vjerovati.
  • Kako map() preskalira jedan raspon u drugi, i što se dogodi kada ga obrneš.

Potreban hardver:

  • 1x Soldered NULA MINI pločica
  • 1x Breadboard
  • 1x Ultrazvučni senzor udaljenosti (HC-SR04)
  • 1x Qwiic kabel
  • 1x LED-ica (bilo koje boje)
  • 1x otpornik od 330 Ω
  • 3x Jumper žice
  • 1x USB-C kabel
ℹ️
Otpornik od 330 Ω nije neobavezan. LED-ica gotovo da nema vlastitog otpora, pa bez ičega što bi ograničilo struju vuče daleko više nego što su LED-ica ili pin građeni izdržati, i oboje se može oštetiti. Otpornik ide serijski s LED-icom, jedan za drugim u istom lancu, i nije važno kojim je krajem okrenut. Prstenovi mu se čitaju narančasti-narančasti-smeđi.

Spajanje komponenti

ℹ️
Ovaj primjer doseže samo dva pina na pločici: IO2 u redu 25 i GND u redu 30, oba na f–j strani. Tijelo pločice prekriva stupce f–i, pa su j25 i j30 rupice u koje stvarno možeš uvesti žicu. Sve ostalo odvija se dalje gore duž iste f–j strane, dobro odmaknuto od pločice.

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. Zauzet će redove 25 do 30.

NULA MINI pločica postavljena na breadboard, zauzima redove 25 do 30
Korak 1: pločica postavljena na breadboard, s IO2 u redu 25 i GND u redu 30

2. Izvedi IO2 u slobodan red

IO2 je pin koji će pobuđivati LED-icu, a pločica prekriva cijeli red 25 osim krajnje rupice. Zato prva žica samo nosi taj pin nekamo gdje možeš raditi: jumper žica iz j25 gore u j15.

Jumper žica ide duž stupca j iz reda 25 u red 15
Korak 2: IO2 izveden iz j25 u j15

3. Dodaj otpornik od 330 Ω

Otpornik je sljedeći član lanca. Postavi ga tako da premosti red 15, red u koji je jumper žica upravo došla, i red 13, oba na f–j strani.

Otpornik od 330 ohma premošćuje redove 15 i 13 na strani f do j
Korak 3: otpornik od 330 Ω premošćuje redove 15 i 13
ℹ️
Otpornik nema polaritet, pa nije važno kojim je krajem okrenut. Nožice su mu dugačke i lako se savijaju, i zato se izvijaju preko površine umjesto da idu ravno. To je u redu, dokle god je svaki kraj čvrsto utisnut u svoju rupicu.

4. Dodaj LED-icu

LED-ica nastavlja lanac: njezina duga nožica (anoda) u red 13, isti red u kojem otpornik završava, i njezina kratka nožica (katoda) u red 11.

LED-ica dodana, s jednom nožicom u redu 13 uz otpornik i drugom u redu 11
Korak 4: LED-ica na mjestu, duga nožica prema otporniku
⚠️
Postavi LED-icu na pravu stranu. LED-ica propušta struju samo u jednom smjeru. Duga nožica mora biti okrenuta prema otporniku i IO2; kratka nožica mora biti okrenuta prema masi. Okrenuta obrnuto, jednostavno se neće upaliti. Ništa se neće pokvariti, ali se ni ništa neće dogoditi. Ako nožice ne možeš razlikovati po dužini, potraži ravni zarez na rubu plastičnog tijela: nožica na toj strani je kratka.

5. Spoji LED-icu i pločicu na masu

Dvije jumper žice zaokružuju krug, i obje idu na plavu vodilicu, koja djeluje kao dijeljena linija mase:

  • Iz reda 11, gdje sjedi kratka nožica LED-ice, do plave vodilice.
  • Iz j30 (GND) do te iste vodilice.
Dvije crne jumper žice, jedna iz reda 11 i jedna iz j30, obje idu na negativnu vodilicu
Korak 5: LED-ica i GND pin pločice oboje vezani na plavu − vodilicu
ℹ️
Obje žice moraju ići u isti stupac vodilice, onaj koji ide uz plavu liniju. Vodilica uz ružičastu liniju je zaseban pojas i nije spojena s njom.

Cijeli put je sada IO2 → 330 Ω → LED-ica → − vodilica → GND. To je isti lanac kao u 1.2 Blinkanje LED-ice, izgrađen na drugom pinu. Razlika je u potpunosti u kodu: umjesto da pin uključujemo i isključujemo, držat ćemo ga negdje između.

ℹ️
Redovi 15, 13 i 11 samo su redovi koje te fotografije slučajno koriste. Bilo koji slobodni redovi rade, dokle god se drži redoslijed lanca: jumper žica s IO2, pa otpornik, pa LED-ica, pa masa.

6. Utisni senzor i USB-C kabel

Senzor ne treba nikakvo spajanje. To je Qwiic modul, pa jedan kabel s qwiic konektora pločice, onog bijelog između tipki USER i RST, do bilo kojeg konektora na senzoru nosi napajanje i podatke zajedno. Ako si upravo došao iz 3.1, već je utisnut.

Gotov sklop: krug s LED-icom na breadboardu, Qwiic kabel ide na ultrazvučni senzor, USB-C spojen
Korak 6: gotov sklop, s krugom LED-ice na breadboardu i senzorom na jednom Qwiic kabelu
ℹ️
Sve što se tiče senzorske strane ovog primjera točno je kako je bilo u 3.1: isti jedan kabel, ista adresa 0x30, isti ADDR klizači ostavljeni na miru, i isti zahtjev da dva srebrna pretvarača gledaju u čist prostor. Budući da razgovara preko I2C na IO6 i IO7, koji nisu izvedeni na headere, nikada se ne bori s LED-icom za pin.

Razumijevanje PWM-a i upravljanja svjetlinom

NULA MINI ne može proizvesti napon na pola puta između isključenog i 3.3 V. Ono što može je uključivati i isključivati pin vrlo brzo, tisućama puta u sekundi, i mijenjati koliko dijela svakog ciklusa provodi uključen. Taj dio naziva se radni ciklus (duty cycle), a tehnika je Pulse Width Modulation, ili PWM.

LED-ica prebacivanje vjerno prati, bljeskajući uključeno i isključeno zajedno s pinom. Tvoje oko ne: ono treperenje usrednjava u postojan sjaj čija prividna svjetlina prati radni ciklus.

Radni ciklusVizualni učinak
0%LED-ica potpuno UGAŠENA
50%LED-ica na pola svjetline
100%LED-ica potpuno UPALJENA
PWM valni oblici koji prikazuju mali radni ciklus uz veliki radni ciklus
Dva radna ciklusa: u drugom pin provodi dulje uključen, pa je prosjek veći i LED-ica izgleda svjetlije

analogWrite(pin, value) je funkcija koja postavlja radni ciklus. Što je vrijednost veća, to pin dulje ostaje uključen u svakom ciklusu, i LED-ica izgleda svjetlije.

Zašto rezolucija mora biti postavljena prva

analogWrite() mora znati što se računa kao "potpuno uključeno". Po zadanom pločica očekuje 8 bitova, dakle vrijednosti od 0 do 255, a sve veće jednostavno se odbija.

Ova skica želi finiju kontrolu od 255 koraka, pa traži 12 bitova, vrijednosti od 0 do 4095, prije nego bilo što upiše:

analogWriteResolution(LED_PIN, 12);
⚠️
Izostavi tu liniju i LED-ica se uopće neće zatamnjivati. Svaka svjetlina koju skica izračuna je negdje u rasponu 0–4095, a 8-bitnom pinu su gotovo sve one izvan raspona. To je česta i zbunjujuća greška: kod se prevede, Serial Monitor ispisuje razumne brojeve, a LED-ica i dalje ne radi ništa.

Pretvaranje udaljenosti u svjetlinu

Dvije stvari moraju se dogoditi između čitanja senzora i pisanja na pin.

Prvo, ograničavanje. Skica odlučuje da je zanima samo raspon od 2 cm do 50 cm, i svaku vrijednost izvan tog raspona sili na najbliži rub:

if (distance < MIN_DISTANCE) distance = MIN_DISTANCE;
if (distance > MAX_DISTANCE) distance = MAX_DISTANCE;

Bez ovoga bi objekt tri metra daleko proizveo svjetlinu daleko ispod nule, besmislenu vrijednost za pisanje na pin. Ograničavanje jamči da sljedeći korak dobije broj unutar raspona za koji je napravljen.

Drugo, preskaliranje. map() uzima broj iz jednog raspona i rasteže ga ili stišće u drugi:

int brightness = map(distance, MIN_DISTANCE, MAX_DISTANCE, 4095, 0);

Čitaj to kao: udaljenost ide od 2 do 50; svjetlina ide od 4095 prema 0. Izlazni raspon je namjerno naopako. Mala udaljenost sleti na visoki kraj raspona svjetline a velika na niski, a to je točno učinak koji želimo: bliže znači svjetlije.

Prolazak kroz nekoliko vrijednosti napamet pokazuje što će skica ispisati:

UdaljenostSvjetlinaZašto
2 cm ili manje4095ograničeno na minimum, pa potpuno upaljeno
33 cm1451oko trećine puta gore po rasponu
39 cm939slabo ali jasno upaljeno
45 cm427tek jedva vidljivo
50 cm ili više0ograničeno na maksimum, pa potpuno ugašeno
ℹ️
Svjetlina ne pada ravnomjerno za oko. Prepolovljenje broja ne prepolovljuje svjetlinu koju vidiš, jer naša percepcija svjetline nije linearna. Niski kraj raspona pokriva mnogo više vidljive promjene od visokog, i zato se čini da LED-ica naglo plane kada tvoja ruka dođe unutar oko 45 cm, a nakon toga se mijenja blaže.

Kod

/**
**************************************************
*
* @file 3.2_Distance_Fade_LED.ino
* @brief Primjer koji pokazuje kako upravljati svjetlinom LED-ice na temelju udaljenosti izmjerene
* Soldered ultrazvučnim senzorom udaljenosti. Što je objekt bliže senzoru, to LED-ica
* postaje svjetlija. Ovaj primjer uvodi analogWrite() i map(), dvije funkcije koje nam dopuštaju da
* izmjerenu vrijednost pretvorimo u razinu svjetline.
* Za detalje, shemu spajanja i više, pogledaj dokumentaciju primjera na: <link placeholder>
* @author Soldered
***************************************************
*/

/*
Uključi Soldered biblioteku za ultrazvučni senzor udaljenosti, da udaljenost možemo pročitati jednim pozivom
funkcije umjesto da sami mjerimo vrijeme odjeka.
*/
#include "Ultrasonic-distance-sensor-easyC-SOLDERED.h"

/*
Ovdje stvaramo naš objekt senzora, koji smo nazvali "hc". Senzor se spaja preko easyC-a, a to je Soldered ime za
I2C vezu preko jednog kabela, pa ne predajemo nikakve brojeve pinova: I2C uvijek koristi ista dva pina na pločici (IO6 i
IO7 na NULA pločici) a biblioteka već zna da tamo gleda.
*/
Ultrasonic_Sensor hc;

/*
Ovo je varijabla kojoj predajemo broj pina na koji smo spojili LED-icu. Budući da ovu LED-icu želimo
zatamnjivati a ne samo uključivati i isključivati, moramo odabrati pin koji podržava PWM. PWM je objašnjen niže u
ovom primjeru.
NULA pločica ima logiku imenovanja pinova ovako: IO2, gdje je 2 broj koji dajemo varijabli.
Ako želiš koristiti drugi pin, pobrini se da koristiš pin označen s IO__.

Zapamti da LED-ici treba otpornik od 330 ohma u seriji s njom. Taj otpornik ograničava koliko struje teče, a
bez njega LED-ica vuče više nego što su ona ili pin građeni izdržati, pa se oboje može oštetiti.
*/
const int LED_PIN = 2;

/*
Ove dvije varijable definiraju raspon udaljenosti koji nas zanima, u centimetrima. Sve bliže od minimuma računa se
kao "što je bliže moguće", a sve dalje od maksimuma računa se kao "daleko". Slobodno eksperimentiraj s
ovim vrijednostima.
*/
const int MIN_DISTANCE = 2;
const int MAX_DISTANCE = 50;

/*
Ovoliko čekamo nakon što zatražimo mjerenje, u milisekundama. Senzor odustaje od slušanja odjeka
nakon 38 milisekundi, pa čekanje malo dulje od toga znači da je odgovor uvijek gotov kada za njega pitamo.
*/
const int MEASURE_WAIT_MS = 50;

void setup() {

/*
Serial.begin() uspostavlja serijsku komunikaciju između tvoje pločice i tvog računala preko USB kabela. Ovdje ga
koristimo da bismo u Serial Monitoru mogli gledati i udaljenost i svjetlinu.
*/
Serial.begin(115200);

/*
pinMode() je funkcija koja konfigurira zadani pin da se ponaša ili kao ulaz ili u ovom slučaju kao izlaz.
Budući da naš pin mora upaliti LED-icu, pin ćemo staviti u OUTPUT mod.
*/
pinMode(LED_PIN, OUTPUT);

/*
analogWriteResolution() definira koliko se bitova koristi za vrijednosti svjetline koje pišemo pomoću analogWrite().
Po zadanom pločica očekuje 8 bitova, dakle vrijednosti od 0 do 255, a sve veće jednostavno se odbija. Budući da
želimo finiju kontrolu od 12 bitova, od 0 do 4095, to ovdje moramo reći, inače se LED-ica uopće ne bi zatamnjivala.
*/
analogWriteResolution(LED_PIN, 12);

/*
begin() priprema senzor za upotrebu, pokrećući I2C komunikaciju i govoreći biblioteci na koju adresu da se obraća.
*/
hc.begin();

//Ispiši početnu poruku da znamo da se program uspješno pokrenuo.
Serial.println("Distance Fade LED Example started!");
}

void loop() {

/*
Čitanje easyC senzora ide u dva koraka. takeMeasure() traži od senzora da pošalje puls i izmjeri vrijeme odjeka, a
čekanje nakon toga daje mu trenutak koji mu treba da taj posao dovrši i spremi odgovor.
*/
hc.takeMeasure();
delay(MEASURE_WAIT_MS);

/*
getDistance() zatim dohvaća spremljeni odgovor, već pretvoren u centimetre. Vrijednost se vraća kao cijeli
broj centimetara, a to je i sva točnost koju ovaj senzor iskreno može ponuditi.
*/
int distance = hc.getDistance();

/*
Senzor odgovara s 0 kada nije čuo nikakav odjek, što se događa kada ništa nije u dosegu ili kada površina
pred njim raspršuje zvuk. To nije pravo mjerenje, pa preskačemo ostatak ovog prolaza umjesto da to
tretiramo kao objekt priljubljen uz senzor.
return rano završava ovaj prolaz kroz loop(), a budući da pločica loop() poziva odmah ponovno, pravo smo natrag
na sljedećem mjerenju.
*/
if (distance == 0) {
Serial.println("No echo received, nothing in range.");
return;
}

//Ispiši izmjerenu udaljenost u Serial Monitor.
Serial.print("Distance: ");
Serial.print(distance);
Serial.print(" cm");

/*
Ovdje izmjerenu udaljenost držimo unutar raspona koji smo definirali iznad. To se naziva ograničavanje, a radimo to jer
sljedeći korak očekuje vrijednost unutar poznatog raspona. Bez toga bi nam objekt 300 cm daleko dao vrijednost
svjetline daleko izvan svega što LED-ica može upotrijebiti.
*/
if (distance < MIN_DISTANCE) distance = MIN_DISTANCE;
if (distance > MAX_DISTANCE) distance = MAX_DISTANCE;

/*
map() je funkcija koja uzima broj iz jednog raspona i preskalira ga u drugi raspon. Ovdje uzimamo
udaljenost, koja ide od MIN_DISTANCE do MAX_DISTANCE, i preskaliramo je u svjetlinu, koja ide od 4095 prema
0. Primijeti da je izlazni raspon obrnut: najmanja udaljenost daje najveću svjetlinu, a to je
točno učinak koji želimo.
*/
int brightness = map(distance, MIN_DISTANCE, MAX_DISTANCE, 4095, 0);

/*
analogWrite() je funkcija koja na pin piše vrijednost "između" umjesto samo HIGH ili LOW. To radi
pomoću PWM-a, što je skraćeno od Pulse Width Modulation: pin se uključuje i isključuje vrlo brzo, i što dulje
ostaje uključen unutar svakog ciklusa, to LED-ica našim očima izgleda svjetlija.
*/
analogWrite(LED_PIN, brightness);

//Ispiši i vrijednost svjetline, da vidimo kako se mijenja zajedno s udaljenošću.
Serial.print(" -> Brightness: ");
Serial.println(brightness);
}

Što bi trebao vidjeti

Prenesi skicu i drži ruku nekoliko centimetara pred senzorom. LED-ica dođe na punu svjetlinu:

LED-ica svijetli punom svjetlinom dok je ruka blizu pred ultrazvučnim senzorom
Ruka držana blizu senzora, i LED-ica na vrhu svog raspona

Sada polako odmakni ruku. Sjaj slabi, a negdje oko pola metra ugasi se sasvim.

Da vidiš zašto, otvori Tools → Serial Monitor i postavi baud rate na 115200. Svaka linija nosi obje polovice priče: što je senzor našao i što je LED-ici rečeno da s tim učini:

Serial Monitor na 115200 bauda prikazuje parove udaljenosti i svjetline, sa svjetlinom koja ostaje na 0 dok udaljenost ne padne ispod 50 centimetara
Udaljenost i svjetlina zajedno: svjetlina ostaje na 0 dok vrijednost ne padne pod 50 cm

U tom ispisu vrijedi primijetiti tri stvari.

Većina linija piše Brightness: 0. Svaka vrijednost od 50 cm ili više ograniči se na MAX_DISTANCE, a 50 cm se preslika u nulu. Tako LED-ica ostaje ugašena za 52 cm, 65 cm, 90 cm i sve dalje od toga. Budi se samo kada ti je ruka stvarno blizu. Ako ne vidiš ništa osim nula, nisi daleko od kruga koji radi; samo si predaleko od senzora.

LED-ica se upali naglo, a onda blago slabi. Prve vrijednosti različite od nule ovdje su 45 cm → 427 i 39 cm → 939, a 427 od 4095 je već jasno vidljivo. Kad dolaziš s 50 cm, čini se da se LED-ica uključuje, a ne da se postupno pojačava, a gladak dio prijelaza odvija se u posljednjih 30 cm ili tako nešto.

Vrijednosti oko 1000 cm također daju Brightness: 0. Linije na vrhu (1001, 1002, 1010 cm) su način na koji senzor nalazi ništa, kako je opisano u 3.1. To nisu prave udaljenosti, ali ih ograničavanje ipak pretvori u 50 cm, pa prazna prostorija na kraju izgleda točno kao "daleko" i LED-ica ostaje ugašena. To je ponašanje koje ovdje želiš, i dobiva se besplatno.

ℹ️
Na kraju loop() nema delay(). Jedina pauza je 50 ms koliko skica čeka senzor, pa vrijednosti dolaze oko dvadeset puta u sekundi i Serial Monitor brzo promiče. To je ono što zatamnjivanju daje osjećaj odzivnosti umjesto stupnjevitosti. Sve je u redu.
⚠️
Ako brojevi izgledaju ispravno a LED-ica se nikada ne upali, greška je u spajanju, a ne u kodu. Provjeri tri stvari po redu: duga nožica LED-ice okrenuta je prema otporniku a kratka prema masi; otpornik i LED-ica stvarno se sastaju u istom redu, pa je lanac neprekinut; i obje žice mase sjede u stupcu vodilice uz plavu liniju. Ako se u Serial Monitoru ne pojavi ništa, provjeri je li baud rate 115200. Na bilo kojoj drugoj postavci ispisuje nečitljive simbole ili ostaje prazan.

Probaj sam: promijeni MAX_DISTANCE s 50 na 150 i prenesi ponovno. LED-ica sada reagira s metar i pol daljine, ali svaki centimetar kretanja mijenja svjetlinu manje, pa je zatamnjivanje blaže i teže ga je vidjeti. Spusti ga na 20 i događa se obrnuto: ništa dok ti ruka nije vrlo blizu, a onda brz, očit prijelaz od tame do pune svjetline. Ništa se na krugu nije promijenilo. Samo si redefinirao što "blizu" znači.


Cijeli primjer

Pogledaj cijeli kod primjera na linku ispod:

3.2_Distance_Fade_LED.ino

Primjer koji čita Qwiic ultrazvučni senzor udaljenosti i koristi udaljenost za postavljanje svjetline LED-ice pomoću PWM-a.