3.2 Udaljenost i zatamnjivanje LED-ice
Cilj ovog primjera je upravljati svjetlinom LED-ice na temelju udaljenosti izmjerene ultrazvučnim senzorom. Š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 se mora pretvoriti u razinu svjetline. Taj posao obavljaju dva alata: PWM i map funkcija koju napišeš sam.
U ovoj dokumentaciji naučit ćeš:
- Što je PWM i kako čini da LED-ica izgleda slabije od potpuno upaljene.
- Kako
duty_u16()postavlja svjetlinu i koji je njegov raspon. - Kako napisati vlastitu map funkciju, jer MicroPython ne dolazi s njom.
- Kako ograničavanje (clamping) drži vrijednost unutar raspona koji možeš koristiti.
- Što se dogodi kada obrneš izlazni raspon.
Potreban hardver:
- 1x Soldered NULA MINI pločica
- 1x Breadboard
- 1x Ultrazvučni senzor udaljenosti (HC-SR04)
- 1x LED-ica (bilo koje boje)
- 1x otpornik od 330 ohma
- 1x Qwiic kabel
- 3x Jumper žice
- 1x USB-C kabel
Spajanje komponenti
1. Umetni NULA MINI pločicu na breadboard
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.

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.

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.

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.

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.

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.
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.

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
Digitalni pin ima samo dva stanja, pa ne može proizvesti pola napona. Ono što može je prebacivati između njih vrlo brzo i u svakom provesti različitu količinu vremena. To je PWM, Pulse Width Modulation.
Ako je pin uključen pola svakog ciklusa, LED-ica prima pola energije koju bi dobila da je pin uključen cijelo vrijeme, a tvoje oko, daleko presporo da vidi prebacivanje, to čita kao pola svjetline. Dio svakog ciklusa proveden u uključenom stanju naziva se radni ciklus (duty cycle).
| Radni ciklus | Vizualni učinak |
|---|---|
| 0% | LED-ica potpuno UGAŠENA |
| 50% | LED-ica na pola svjetline |
| 100% | LED-ica potpuno UPALJENA |

U MicroPythonu LED-icu pobuđuje PWM objekt, a ne obični Pin, i on prima dvije postavke:
led = PWM(Pin(LED_PIN))
led.freq(5000)
freq() postavlja koliko brzo se pin uključuje i isključuje, ovdje 5000 puta u sekundi, daleko brže nego što tvoje oči mogu pratiti, pa vidiš postojanu svjetlinu umjesto treperenja. duty_u16() zatim postavlja samu svjetlinu, kao broj od 0 (potpuno ugašeno) do 65535 (potpuno upaljeno).
analogWriteResolution(LED_PIN, 12) i onda radi u rasponu 0–4095. MicroPython uvijek koristi cijeli 16-bitni raspon pomoću duty_u16(), pa nema rezolucije za postavljanje i nema što zaboraviti. Brojevi svjetline u konzoli su prema tome veći.duty_u16() s istim pozivom u 2.4 Piskanje buzzera. Na buzzeru 65535 znači da pin nikada ne prebacuje i buzzer je tih. Na LED-ici 65535 znači da je pin uključen cijelo vrijeme i LED-ica je na punoj svjetlini. Ista funkcija, a razlika je u potpunosti u tome što je spojeno na pin.Pretvaranje udaljenosti u svjetlinu
Tri stvari moraju se dogoditi između čitanja senzora i pisanja na pin.
Prvo, odbacivanje ne-mjerenja. Udaljenost 0 znači da senzor nije čuo upotrebljiv odjek, pa nema s čime raditi. Skripta to kaže i odmah skoči na sljedeći prolaz:
if distance == 0:
print("No echo received, nothing in range.")
continue
continue napušta ostatak ovog prolaza kroz petlju i vraća se na vrh. Bez njega bi se nula tretirala kao objekt priljubljen uz senzor, i LED-ica bi bljesnula u punu svjetlinu svaki put kada prostorija utihne.Drugo, ograničavanje. Skriptu 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.
Treće, preskaliranje. Arduino za to ima ugrađenu funkciju map(). MicroPython nema, pa skripta definira vlastitu:
def value_map(value, in_min, in_max, out_min, out_max):
return int((value - in_min) * (out_max - out_min) / (in_max - in_min) + out_min)
def je način na koji se u Pythonu definira funkcija. Nakon imena slijede vrijednosti koje očekuje u zagradama, a return predaje odgovor onome koji ju je pozvao. Napisati je jednom na vrhu datoteke znači da je petlja ispod može samo koristiti, a računanje postoji samo na jednom mjestu.int() oko cijelog izraza je važan. Dijeljenje u Pythonu proizvodi decimalni broj čak i kada se obje strane dijele bez ostatka, a duty_u16() decimalni broj ne prima. Bez int() skripta pada s TypeError: can't convert float to int.Zatim se poziva s izlaznim rasponom namjerno naopako:
brightness = value_map(distance, MIN_DISTANCE, MAX_DISTANCE, MAX_BRIGHTNESS, 0)
Čitaj to kao: udaljenost ide od 2 cm do 50 cm; svjetlina ide od 65535 prema 0. 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 skripta ispisati:
| Udaljenost | Svjetlina | Zašto |
|---|---|---|
| 2 cm ili manje | 65535 | ograničeno na minimum, pa potpuno upaljeno |
| 33 cm | 23210 | oko trećine puta gore po rasponu |
| 39 cm | 15018 | slabo ali jasno upaljeno |
| 45 cm | 6826 | tek jedva vidljivo |
| 50 cm ili više | 0 | ograničeno na maksimum, pa potpuno ugašeno |
Kod
# Soldered driver za ultrazvučni senzor udaljenosti, koji se nalazi u lib folderu repozitorija primjera.
from UltrasonicSensor import UltrasonicSensor
# I2C je ono što Qwiic konektor nosi, a senzor je Qwiic modul.
# PWM je skraćeno od Pulse Width Modulation. Uključuje i isključuje pin vrlo brzo, i što dulje ostaje uključen
# unutar svakog ciklusa, to LED-ica spojena na njega našim očima izgleda svjetlija.
from machine import I2C, Pin, PWM
import time
# 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, pobuđujemo je PWM-om a ne običnim Pin.
#
# 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.
LED_PIN = 2
# Ove dvije varijable definiraju raspon udaljenosti koji nas zanima. 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 ovima.
MIN_DISTANCE = 2
MAX_DISTANCE = 50
# duty_u16() postavlja svjetlinu kao broj od 0 (potpuno ugašeno) do 65535 (potpuno upaljeno), pa je 65535 najveća
# svjetlina koju možemo zatražiti.
MAX_BRIGHTNESS = 65535
# Ovoliko čekamo nakon što zatražimo mjerenje, u milisekundama.
MEASURE_WAIT_MS = 50
# Ovdje postavljamo I2C vezu. Na NULA MINI, I2C koristi IO6 za liniju podataka (SDA) i IO7 za liniju
# takta (SCL), a to su točno pinovi na koje je Qwiic konektor spojen.
i2c = I2C(0, scl=Pin(7), sda=Pin(6))
# Ovdje stvaramo naš objekt senzora i predajemo mu I2C vezu. Za senzor ne predajemo nikakve brojeve pinova:
# svaki Qwiic modul dijeli ta ista dva I2C pina, a ovaj senzor odgovara na adresi 0x30.
sensor = UltrasonicSensor(i2c)
# Ovdje stvaramo naš PWM objekt za LED-icu. freq() postavlja koliko brzo se pin uključuje i isključuje. 5000 puta u
# sekundi je daleko brže nego što naše oči mogu pratiti, pa vidimo postojanu svjetlinu umjesto treperenja.
led = PWM(Pin(LED_PIN))
led.freq(5000)
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. Budući da ćemo izlazni raspon predati
# obrnuto, najmanja udaljenost proizvodi najveću svjetlinu.
return int((value - in_min) * (out_max - out_min) / (in_max - in_min) + out_min)
# begin() priprema senzor za upotrebu, prije nego počnemo čitati iz njega.
sensor.begin()
# Ispiši početnu poruku da znamo da se program uspješno pokrenuo.
print("Distance Fade LED Example started!")
while True:
# 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.
sensor.takeMeasure()
time.sleep_ms(MEASURE_WAIT_MS)
# getDistance() zatim dohvaća spremljeni odgovor, već pretvoren u centimetre.
distance = sensor.getDistance()
# Senzor odgovara s 0 kada nije čuo nikakav odjek. To nije pravo mjerenje, pa preskačemo ostatak
# ovog prolaza umjesto da to tretiramo kao objekt priljubljen uz senzor.
# continue skače pravo natrag na vrh petlje, spreman za sljedeće mjerenje.
if distance == 0:
print("No echo received, nothing in range.")
continue
# 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.
if distance < MIN_DISTANCE:
distance = MIN_DISTANCE
if distance > MAX_DISTANCE:
distance = MAX_DISTANCE
# Ovdje udaljenost pretvaramo u svjetlinu pomoću naše funkcije iznad. Primijeti da je izlazni raspon
# obrnut, od MAX_BRIGHTNESS prema 0: najmanja udaljenost daje najveću svjetlinu.
brightness = value_map(distance, MIN_DISTANCE, MAX_DISTANCE, MAX_BRIGHTNESS, 0)
# duty_u16() upisuje svjetlinu na pin, i LED-ica se mijenja odmah.
led.duty_u16(brightness)
# Ispiši obje vrijednosti, da vidimo kako se svjetlina mijenja zajedno s udaljenošću.
print("Distance: {} cm -> Brightness: {}".format(distance, brightness))
Što bi trebao vidjeti
Pritisni Run i drži ruku nekoliko centimetara pred senzorom. LED-ica dođe na punu svjetlinu:

Sada polako odmakni ruku i sjaj slabi, a onda se ugasi sasvim. Svaka linija u Shellu nosi obje polovice priče: što je senzor našao i što je LED-ici rečeno da s tim učini:
Distance Fade LED Example started!
Distance: 2 cm -> Brightness: 65535
Distance: 33 cm -> Brightness: 23210
Distance: 39 cm -> Brightness: 15018
Distance: 45 cm -> Brightness: 6826
Distance: 50 cm -> Brightness: 0
Distance: 50 cm -> Brightness: 0
U tom ispisu vrijedi primijetiti tri stvari.
Svaka vrijednost od 50 cm ili više daje Brightness: 0. Svaka od njih 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.
Ispisana udaljenost nikada ne izlazi iz raspona od 2 cm do 50 cm. To je ograničavanje u radu: skripta prepisuje distance ograničenom vrijednošću prije ispisa, pa se ruka metar daleko i dalje prikazuje kao 50. Sirova vrijednost ne prikazuje se nigdje.
Vrijednosti oko 1000 cm također daju Brightness: 0. Kako je opisano u 3.1, to je način na koji senzor prijavljuje da nije našao ništa, a ne 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.
Probaj sam: promijeni MAX_DISTANCE s 50 na 150 i pokreni 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.py
Primjer koji čita Qwiic ultrazvučni senzor udaljenosti i koristi udaljenost za postavljanje svjetline LED-ice pomoću PWM-a.