4.5 Ultrazvukový senzor vzdialenosti UNIT ULTRASONIC
M5Stack UNIT ULTRASONIC
Modul UNIT ULTRASONIC meria vzdialenosť objektu na princípe odrazu ultrazvukového pulzu. Je vybavený senzorom RCWL-9620 s pripojeným 16 mm vysielačom i prijímačom - kompatibilný je s rozšírenejším senzorom HC-SR04. Pripája sa cez GROVE konektor, k dispozícii je v dvoch verziách:
- I²C: na M5Stick GPIO 33 (SCL) a 32 (SDA), má adresu 0x57, s priloženým GROVE káblom funguje spoľahlivo do 100 kHz;
- I/O: na M5Stick pre vyslanie pulzu slúži GPIO 32 (trigger), pre zachytenie odrazu GPIO 33 (echo).
![]() |
![]() |
Ultrazvukový senzor RCWL-9620
Senzor RCWL-9620 je vybavený ultrazvukovým vysielačom pre frekvenciu 40 kHz. Výrobca M5Stack uvádza, že v ideálnych podmienkach umožňuje merať vzdialenosť od 2 do 450 cm s presnosťou ±2 %, v uhle záberu 60 °. V špecifikáciách samotného senzoru sa však uvádza dosah až 7 metrov, typicky 6 metrov.
Vysielač po aktivácii pulzu odosiela ultrazvuk dovtedy, kým ho prijímač nezachytí, meria sa teda čas trvania prijatého ultrazvuku a z neho sa na základe rýchlosti šírenia zvuku vypočíta vzdialenosť.
V špecifikáciách sa pre režim I/O uvádza cyklus merania (ako často môžeme opakovať meranie) 50 ms, trvanie merania uvedené konkrétne nie je, no pomôže nám fyzika: maximálny uvedený dosah je 7 m, zvuk musí prejsť vzdialenosť 14 m, čo pri rýchlosti zvuku 344 m/s predstavuje čas 40 ms. Za tento čas zaregistrujeme začiatok pulzu, ale ešte musíme čakať na jeho koniec, ktorý by mal prísť o rovnaký čas. Teda celkové trvanie môže byť až 80 ms. Čím kratšia vzdialenosť, tým skôr zistíme výsledok.
Pre režim I²C je v špecifikáciách uvedené povinné čakanie 100 ms, no pri vyššej vzdialenosti sa v praxi ukázala potreba čakať bezpečných 200 ms.
Je ultrazvukový senzor lepší ako laserový?
Podľa deklarovaných parametrov sa to tak môže zdať - poskytuje aj vyšší dosah, aj vyššiu presnosť a zaostávať by nemal ani dobou merania. Z praktického hľadiska nás však zaujímajú dva podstatné rozdiely:
- uhol záberu: laserový merač mal 25 °, ultrazvukový 60 °, záleží, čo viac vyhovuje našej situácii;
- odrazivosť materiálov: laserový merač bude mať problém s čiernou farbou, ultrazvuk sa zase neodrazí od tlmivého textilu.
Dá sa povedať, že ideálnou je kombinácia oboch senzorov, preto ich oba často nájdeme napríklad aj na robotických vysávačoch.
Ukážkový program
Spoločnosť M5Stack, ako výrobca tohto senzoru, na svojom webe veľa informácií neprezrádza, no pripojili aspoň špecifikácie (datasheet) v PDF. V nich sa dajú vyčítať potrebné detaily pre meranie v oboch variantoch.
Ukážkový program pre model I²C
Ako napovedajú špecifikácie, meranie je pomerne jednoduché:
- na zbernicu odošleme príkaz na meranie (0x01);
- počkáme 100 ms (pre väčšie vzdialenosti sa osvedčilo čakať 200 ms);
- prečítame 3-bajtový výsledok (poradie big-endian) v mikrometroch, s presnosťou na milimetre.
Ak by mal niekto problém s čínštinou, názornejší je ovládač od Olivera na GitHube.
Jednoduchý ukážkový program opäť priebežne vypisuje nameranú vzdialenosť, kým nestlačíme tlačidlo, tentokrát však využijeme pripravenú zbernicu v objekte M5Stick.i2c_grove:
from machine import Pin, Signal
from M5Stick import i2c_grove
from time import sleep_ms
def Meraj(i2c, adresa = 0x57):
i2c.writeto(adresa, b'\x01') # spustí meranie
sleep_ms(200) # v špecifikáciách sa uvádza 100 ms, ale pri dlhšej vzdialenosti nestihne
r = i2c.readfrom(adresa, 3) # prečíta 3-bajtový big-endian výsledok
cm = round(((r[0] << 16) + (r[1] << 8) + r[2]) / 10_000)
return cm if cm <= 700 else "-"
# i2c_grove = I2C(1, scl = Pin(33), sda = Pin(32), freq = 100_000)
zariadenia = i2c_grove.scan()
print(i2c_grove, zariadenia)
if 0x57 in zariadenia:
tlacidlo = Signal(37, Pin.IN, invert = True)
while not tlacidlo():
try:
vzdialenost = Meraj(i2c_grove)
except Exception:
print("Nastala chyba pri meraní! ")
sleep_ms(1000)
else:
print("vzdialenosť:", vzdialenost, end = " cm \r")
else:
print("Nie je pripojený správny senzor!")
Všimnite si, čo sa stane, keď je prekážka príliš ďaleko (mimo rozsah).
Tento jednoduchý program niekedy čaká zbytočne dlho na meranie. Preto ho môžeme trochu upraviť, aby čakal len 100 ms a ak to nebude stačiť, aby po malej pauze skúšal znovu načítať výsledok:
from machine import Pin, Signal
from M5Stick import i2c_grove
from time import sleep_ms
def Meraj(i2c, adresa = 0x57):
i2c.writeto(adresa, b'\x01') # spustí meranie
sleep_ms(100) # v špecifikáciách sa uvádza 100 ms, ale pri dlhšej vzdialenosti nestihne
for i in range(10): # pokúsi sa 10×
try: r = i2c.readfrom(adresa, 3) # prečíta 3-bajtový big-endian výsledok
except: sleep_ms(10)
else: break # prebehlo úspešne, ukončí cyklus
else:
raise(Exception("timeout"))
cm = round(((r[0] << 16) + (r[1] << 8) + r[2]) / 10_000)
return cm if cm <= 700 else "-"
# i2c_grove = I2C(1, scl = Pin(33), sda = Pin(32), freq = 100_000)
zariadenia = i2c_grove.scan()
print(i2c_grove, zariadenia)
if 0x57 in zariadenia:
tlacidlo = Signal(37, Pin.IN, invert = True)
while not tlacidlo():
try:
vzdialenost = Meraj(i2c_grove)
except Exception:
print("Nastala chyba pri meraní! ")
sleep_ms(1000)
else:
print("vzdialenosť:", vzdialenost, end = " cm \r")
else:
print("Nie je pripojený správny senzor!")
Všimnite si v programe zopár dôležitých bodov:
- najskôr zisťujeme, či je senzor pripojený, až potom začneme merať;
- meranie je obalené v try-except, aby v prípade poruchy senzoru program neskončil s chybou;
- v samotnej meracej funkcii je takto ošetrené čítanie zo zbernice, pretože práve pri ňom príde ku chybe;
- pri výskyte chyby vyvoláme vlastnú výnimku s textom „timeout“.
Ukážkový program pre model I/O
Podľa špecifikácií senzoru je postup merania nasledovný:
- vyšleme jednotkový pulz (trvajúci 10 μs) na trigger port, čím sa spustí vysielanie signálu;
- po prijatí odrazeného signálu vysielač prestane vysielať signál a na echo porte sa objaví hodnota 1 - v tom okamihu spustíme stopky;
- počkáme, kým bude odrazený signál ukončený, vtedy sa na echo porte objaví hodnota 0 a vypneme stopky;
- čas prepočítame rýchlosťou zvuku (pri izbovej teplote cca 344 m/s), z prejdenej dráhy vezmeme polovicu.
from machine import Pin, Signal, time_pulse_us
from time import sleep_us, sleep_ms
def Meraj(trigger, echo):
trigger(1)
sleep_us(10)
trigger(0)
trvanie = time_pulse_us(echo, 1, 50_000) # meria dĺžku pulzu s hodnotou 1, max. 50 ms
if trvanie < 0:
raise(Exception("timeout"))
cm = round(trvanie * 0.0172)
return cm if cm <= 700 else "-"
trigger = Pin(32, Pin.OUT, value = 0)
echo = Pin(33, Pin.IN)
tlacidlo = Signal(37, Pin.IN, invert = True)
while not tlacidlo():
try:
vzdialenost = Meraj(trigger, echo)
except Exception:
print("Nastala chyba pri meraní! ")
sleep_ms(1000)
else:
print("vzdialenosť:", vzdialenost, end = " cm \r")
sleep_ms(100)
Uvedený program je veľmi jednoduchý - vďaka šikovnej funkcii time_pulse_us() zmeriame šírku odrazeného pulzu a zároveň máme aj poistku pre prípad zacyklenia. Bude to veľmi presné, lenže nešetrí energiou. Ide o aktívne čakanie, počas ktorého CPU neodpočíva a navyše sú zablokované aj iné úlohy na pozadí.
Z hľadiska úspory energie a zdieľania času procesora je efektívnejšie využiť prerušenie:
from machine import Pin, Signal
from time import ticks_us, ticks_diff, sleep_us, sleep_ms
def Echo(echo):
global t1, t2
if echo(): t1 = ticks_us()
else: t2 = ticks_us()
def Meraj(trigger, echo):
global t1, t2
trigger(1)
sleep_us(10)
trigger(0)
t1 = t2 = None
for i in range(10): # poistka, max. 100 ms
sleep_ms(10)
if t2 is not None: break
else:
raise(Exception("timeout"))
cm = round(ticks_diff(t2, t1) * 0.0172)
return cm if cm <= 700 else "-"
trigger = Pin(32, Pin.OUT, value = 0)
t1 = t2 = None
echo = Pin(33, Pin.IN)
echo.irq(handler = Echo)
tlacidlo = Signal(37, Pin.IN, invert = True)
while not tlacidlo():
try:
vzdialenost = Meraj(trigger, echo)
except Exception:
print("Nastala chyba pri meraní! ")
sleep_ms(1000)
else:
print("vzdialenosť:", vzdialenost, end = " cm \r")
sleep_ms(100)
Tento program tiež čaká, no úsporne - v čakacej slučke je sleep_ms(). Napriek tomu sa nezníži presnosť merania, pretože v okamihu zmeny stavu echo portu sa vyvolá prerušenie a zaznamená čas na stopkách.

