Čidlo využívající ultrazvuku pro měření vzdálenosti na délku až 4 metrů.

Velice přesné měření vzdálenosti, s přesností +/- 3mm na vzdálenost do 4 metrů. Dá se použít téměř ke všemu, funguje na principu odrazu od překážky. Maximální dálka měření ale zaleží na napájení a jeho kvalitě.

Novější model RCWL-9610, které má inteligentnější komunikační čip a nativně umí I2C, UART a 1Wire režimy, které se volí proletováním propojek na zadní straně. Dále popsané řešení nebude s tímto novějším čidlem fungovat, pokud nebudou správně propájené spojky a čidlo bude nastavené na I2C, UART nebo 1Wire. Čidlo se musí nastavit do původního režimu GPIO (TRIGGER — ECHO měření času ozvěny).

Specifikace:

  • Pracovní rozsah: 2-400cm

  • Vysoká přesnost: až 3mm

  • Pracovní úhel: <15 stupňů

  • Klidový proud:< 2mA

  • Napájecí napětí: 3.3 - 5VDC

  • Velikost: 45x20x16mm

Použití

Čidlo se musí napájet z 5V, při napájení 3.3V nefunguje. Toto je pro starší verzi čidla, poznáte ji podle obrázků

HC-SR04 zepředu — starší verze

IMG 20250820 193804

HC-SR04 zezadu — starší verze

IMG 20250820 193747

Konstrukce, TRIG je na GPIO0 a ECHO je na GPIO1

IMG 20250222 002332

HC-SR04 zepředu — novější verze 2021

IMG 20250917 230953

HC-SR04 zezadu — novější verze 2021

IMG 20250917 231017

Pokud jsou propojky M1 a M2 nepropojeny (jako je na obrázku), čidlo je nastavené na práci v režimu GPIO a bude fungovat. Novejší verze funguje s napájecím napětím 3.3V.

Schéma zapojení

hc sr04 schema

Čidlo funguje tak, že se na TRIG pin přivede impuls o délce minimálně \(10 \mu s\) a potom se měří časová délka impulsu na pinu ECHO. Po obdržení TRIG pulsu čidlo vyšle ultrazkukový tón o kmitočtu 40 kHz a čeká na ozvěnu tohoto zvuku. Doba mezi vysláním zvuku a ozvěnou je přímo úměrná dvojnásobku vzdálenosti, protože rychlost šížení zvuku ve vzduchu pokládáme za konstantní 334 m/s.

Délka impulsu na pinu ECHO je přímo úměrná vzdálenosti podle vzorce:

\(s = \frac{\Delta t}{2} \cdot v\), kde s je vzálenost objektu, \(\Delta t\) je čas měření (rozdíl mezi náběžnou a sestupnou hranou pulsu na ECHO pinu) a \(v\) je rychlost šíření zvuku ve vzduchu.

Časový průběh

Timing Diagram HC SR04

Měření založené na událostech

Program pro měření je velmi jednoduchý. Pico SDK nám umožňuje pohodlně měřit čas s přesností na \(\mu s\) pomocí funkce time_us_64(). Tato funkce vrací 64bitové celé číslo, počet mikrosekund od startu mikrokontroléru. Pico má v sobě registr iobank0_hw, kde jsou zaznamenány události, které se staly na pinu. Viz Měření pulsů pomocí událostí. Nejprve vynulujeme události na pinu pomocí funkce gpio_get_events(uint gpio) a poté periodicky voláme funkci gpio_get_events(uint gpio), dokud nezachytíme událost. Je to ve funkci mer_vzdálenost().

uzcidlo.c
/*
 * Měření vzdálenosti čidlem HC-SR04 v režimu trigger - echo
 * na Raspberry Pi Pico
 *
 * (c) Jirka Chráska 2025; jirka@lixis.cz. Všechna práva vyhrazena.
 *
 * Redistribuce a použití zdrojových i binárních forem díla,
 * v původním i upravovaném tvaru, jsou povoleny za následujících podmínek:
 *
 * Šířený zdrojový kód musí obsahovat výše uvedenou informaci o copyrightu,
 * tento seznam podmínek a níže uvedené zřeknutí se odpovědnosti.
 *
 * Šířený binární tvar musí nést výše uvedenou informaci o copyrightu, tento seznam podmínek
 * a níže uvedené zřeknutí se odpovědnosti ve své dokumentaci
 * a/nebo dalších poskytovaných materiálech.
 *
 * Ani jméno vlastníka práv, ani jména přispěvatelů nemohou být použita při podpoře
 * nebo právních aktech  * souvisejících s produkty odvozenými z tohoto softwaru
 * bez výslovného písemného povolení.

 * TENTO SOFTWARE JE POSKYTOVÁN DRŽITELEM LICENCE A JEHO PŘISPĚVATELI „JAK STOJÍ A LEŽÍ“
 * A JAKÉKOLIV VÝSLOVNÉ NEBO PŘEDPOKLÁDANÉ ZÁRUKY VČETNĚ, ALE NEJEN, PŘEDPOKLÁDANÝCH OBCHODNÍCH
 * ZÁRUK  A ZÁRUKY VHODNOSTI PRO JAKÝKOLIV ÚČEL JSOU POPŘENY. DRŽITEL, ANI PŘISPĚVATELÉ NEBUDOU
 * V ŽÁDNÉM PŘÍPADĚ  ODPOVĚDNI ZA JAKÉKOLIV PŘÍMÉ, NEPŘÍMÉ, NÁHODNÉ, ZVLÁŠTNÍ, PŘÍKLADNÉ NEBO
 * VYPLÝVAJÍCÍ ŠKODY (VČETNĚ, ALE NEJEN, ŠKOD VZNIKLÝCH NARUŠENÍM DODÁVEK ZBOŽÍ NEBO SLUŽEB;
 * ZTRÁTOU POUŽITELNOSTI, DAT NEBO ZISKŮ; NEBO PŘERUŠENÍM OBCHODNÍ ČINNOSTI) JAKKOLIV ZPŮSOBENÉ
 * NA ZÁKLADĚ JAKÉKOLIV TEORIE O ZODPOVĚDNOSTI, AŤ UŽ PLYNOUCÍ Z JINÉHO SMLUVNÍHO VZTAHU,
 * URČITÉ ZODPOVĚDNOSTI NEBO PŘEČINU (VČETNĚ NEDBALOSTI) NA JAKÉMKOLIV ZPŮSOBU POUŽITÍ TOHOTO
 * SOFTWARE, I V PŘÍPADĚ, ŽE DRŽITEL PRÁV BYL UPOZORNĚN NA MOŽNOST TAKOVÝCH ŠKOD.
 */

#include "pico/stdlib.h"
#include "hardware/structs/iobank0.h"
#include <stdio.h>
#include "hardware/gpio.h"
#include "pico/time.h"
#include "pico/types.h"


uint TRIGGER_PIN = 0;
uint ECHO_PIN    = 1;
uint LED_PIN     = 25;

// přehled událostí na pinu
uint32_t gpio_get_events(uint gpio)
{
    int32_t mask = 0xF << 4 * ( gpio % 8 );
return (iobank0_hw->intr[gpio / 8] & mask) >> 4 * ( gpio % 8 );
}

// vymazání událostí na pinu
void gpio_clear_events(uint gpio, uint32_t events)
{
    gpio_acknowledge_irq(gpio, events);
}

// inicializace ultrazvukového čidla HC-SR04 (klasického)
void uzcidlo_init( uint trigger_pin, uint echo_pin  )
{
    gpio_init(trigger_pin);
    gpio_init(echo_pin);
    gpio_set_dir( trigger_pin, GPIO_OUT);
    gpio_set_dir( echo_pin, GPIO_IN);
    gpio_pull_down( echo_pin );
    gpio_put(trigger_pin, 0);
}

// měření vzdálenosti v centimetrech
// měříme dobu mezi náběžnou a sestupnou hranou na echo pinu,
// polovina doby vynásobená rychlostí zvuku nám dá vzdálenost
// funkce vrací vzdálenost v centimetrech, v případě chyby vrací -1
float mer_vzdalenost(uint trigger_pin, uint echo_pin )
{
float centimetry = -1.0;
uint64_t t, rtime, ftime;
int i = 0;
uint32_t rise=0, fall=0;

    gpio_put(trigger_pin, 1);
    sleep_us(10);
    gpio_put(trigger_pin, 0);
    t = time_us_64();
    gpio_clear_events( echo_pin, GPIO_IRQ_EDGE_RISE | GPIO_IRQ_EDGE_FALL );
    // čekáme na nábežnou hranu na ECHO pinu
    do {
        rise = (gpio_get_events(echo_pin) & GPIO_IRQ_EDGE_RISE);
        if( rise ) {
            rtime = time_us_64();
            break;
        }
        i++;
    } while ( i<400000 ); // toto je timeout, když nedostáváme z čidla nic
    // tento timeout je možné odstranit, ale má to za následek, že při poruše čidla
    // nebo jeho připojení program bude viset a celé Pico se zakousne

    if( ! rise ) { // nedostal jsem náběžnou hranu v časovém limitu
        return centimetry;
    }
    gpio_clear_events( echo_pin, GPIO_IRQ_EDGE_RISE | GPIO_IRQ_EDGE_FALL );
    i = 0;
    // čekáme na sestupnou hranu na ECHO pinu
    do {
        fall = (gpio_get_events(echo_pin) & GPIO_IRQ_EDGE_FALL);
        if( fall ) {
            ftime = time_us_64();
            break;
        }
        i++;
    } while ( i<400000 ); // toto je timeout, když nedostáváme z čidla nic
    // tento timeout je možné odstranit, ale má to za následek, že při poruše čidla
    // nebo jeho připojení program bude viset a celé Pico se zakousne

    if( ! fall ) { // nedostal jsem sestupnou hranu v časovém limitu
        return centimetry;
    }
    // rozdíl času mezi sestupnou a náběžnou hranou/2 * rychlost zvuku dává vzdálenost
    centimetry = (ftime-rtime)*0.5*0.0343;

    // printf("ftime - rtime: %llu us;  vzdálenost %5.1f cm, měření trvalo: %llu us. \n",
    //        ftime-rtime, centimetry, ftime-t);
return centimetry;
}

// testování měření
// -----------------------------------------------------------------------
int main() {
float vzdalenost;
    stdio_init_all();
    sleep_ms(2000);
    // nastavení ultrazvukového čidla HC-SR04
    uzcidlo_init( TRIGGER_PIN, ECHO_PIN);
    printf("Měření vzdálenosti čidlem HC-SR04.\n");

    gpio_init( LED_PIN );
    gpio_set_dir( LED_PIN, GPIO_OUT);
    while (true) {
        // blikáme LEDkou, abychom viděli, že žijeme
        gpio_put( LED_PIN, 1);
        sleep_ms(200);
        vzdalenost = mer_vzdalenost(TRIGGER_PIN, ECHO_PIN);
        if( vzdalenost > 0.0 ) {
            printf( "Nejbližší objekt je %5.1f cm vzdálen.\n", vzdalenost );
        } else {
            printf( "Chyba měření.\n");
        }
        gpio_put( LED_PIN, 0);
        sleep_ms(800);
    }
}
// -----------------------------------------------------------------------

Celý program je velmi jednoduchý, do vašeho projektu si zkopírujte funkce

  • uint32_t gpio_get_events(uint gpio),

  • void gpio_clear_events(uint gpio, uint32_t events),

  • void uzcidlo_init( uint trigger_pin, uint echo_pin ) a

  • float mer_vzdalenost(uint trigger_pin, uint echo_pin )

a vložte

  • #include "hardware/structs/iobank0.h"

  • #include "hardware/gpio.h"

a můžete čidlo používat. Funce mer_vzdalenost() vrací -1.0 v případě chyby čidla.

Na začátku vašeho programu nezapomeňte zavolat funkci uzcidlo_init. Doba jednoho měření je přibližne 5 ms.

Pokud je překážka malá, nemusí být čidlem zachycena. Pokud není překážka kolmo na osu čidla (osu měření), měřená vzdálenost nebude zcela přesná.

Celý projekt si můžete stáhnout: uzcidlo.tar.gz.

Výstup z minicomu (minicom -b 115200 -D /dev/ttyACM0)
Měření vzdálenosti čidlem HC-SR04.
Nejbližší objekt je  10.8 cm vzdálen.
Chyba měření.
Nejbližší objekt je  10.3 cm vzdálen.
Chyba měření.
Nejbližší objekt je  10.8 cm vzdálen.
Chyba měření.
Nejbližší objekt je  10.7 cm vzdálen.
Chyba měření.
Nejbližší objekt je  10.8 cm vzdálen.
Chyba měření.
Nejbližší objekt je  10.7 cm vzdálen.
Chyba měření.
Nejbližší objekt je   8.7 cm vzdálen.
Nejbližší objekt je  29.7 cm vzdálen.
Nejbližší objekt je  28.4 cm vzdálen.
Nejbližší objekt je  27.9 cm vzdálen.
Nejbližší objekt je  28.5 cm vzdálen.
Nejbližší objekt je  28.5 cm vzdálen.
Nejbližší objekt je  28.5 cm vzdálen.
Nejbližší objekt je  29.8 cm vzdálen.
Nejbližší objekt je  28.9 cm vzdálen.
Nejbližší objekt je  28.5 cm vzdálen.
Nejbližší objekt je  28.5 cm vzdálen.
Nejbližší objekt je  28.5 cm vzdálen.
Nejbližší objekt je  28.5 cm vzdálen.
Nejbližší objekt je  28.5 cm vzdálen.
Nejbližší objekt je  29.4 cm vzdálen.
Nejbližší objekt je  29.8 cm vzdálen.
Nejbližší objekt je  28.5 cm vzdálen.
Nejbližší objekt je  28.5 cm vzdálen.
Nejbližší objekt je  28.5 cm vzdálen.
Nejbližší objekt je  28.1 cm vzdálen.
Nejbližší objekt je  28.5 cm vzdálen.
Nejbližší objekt je  28.5 cm vzdálen.
Nejbližší objekt je  28.9 cm vzdálen.
Nejbližší objekt je  29.8 cm vzdálen.

Knihovna založená na PIO

Knihovna Pico C++ HC-SR04 Library je psaná v C++, musíme proto dát příponu našemu programu buď .cxx nebo .cpp aby se spustil překladač C++. Pokud budeme překládat překladačem C, tak program neslinkujeme s knihovnou. Kvůli implementaci PIO je potřeba, aby echo pin byl o jednu větší než trigger pin. Jestliže zapojíte trigger pin na GPIO5, potom echo pin musí být na GPIO6. (V našem případě používáme GPIO0 a GPIO1.) Měření vzdálenost je s přesností na centimetry.

Konstruktor
DistanceSensor(PIO pio, uint sm, uint trigger_gpio); (1)
1 Parametr pio je PIO koprocesor, buď pio0 nebo pio1; parametr sm je state machine; parametr trigger_gpio je GPIO pin kam se připojí spouštecí pin (pin pro odpověď je o 1 větší).
main.cxx
// Include the library
#include <stdio.h>
#include "pico/stdlib.h"
#include "distance_sensor.h"

int main() {

    stdio_init_all();
    // Create an instance of the sensor
    // specify the pio, state machine, and gpio pin connected to trigger
    // echo pin must be on gpio pin trigger + 1.
    DistanceSensor hcsr04{pio0, 0, 0}; // pio0, state machine 0, GPIO0

    while(1) {
        // Trigger background sense
        hcsr04.TriggerRead();

        // wait for sensor to get a result
        while (hcsr04.is_sensing) {
            sleep_us(10);
        }

        // Read result
        printf("Reading %d centimeters\n", hcsr04.distance);
        sleep_ms(1000);
    }
}
CMakeLists.txt
cmake_minimum_required(VERSION 3.13)
include(pico_sdk_import.cmake)

project(vzdalenost1 C CXX ASM)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
pico_sdk_init()

include_directories(
    pico-distance-sensor/include
)

add_executable(vzdalenost1
	main.cxx
)

add_subdirectory(pico-distance-sensor)

target_link_libraries(vzdalenost1
                      pico_stdlib
                      hardware_pio
                      hardware_irq
                      distance-sensor
)

pico_enable_stdio_usb(vzdalenost1 1)
pico_enable_stdio_uart(vzdalenost1 0)

pico_add_extra_outputs(vzdalenost1)
Vložení knihovny do projektu
git clone https://github.com/dangarbri/pico-distance-sensor
Sestavení projektu
cd mereni_vzdalenosti_HC-SR04
mkdir build
cd build
cmake ..
make -j12
Výstup minicom -b 115200 -D /dev/ttyACM0
Reading 78 centimeters
Reading 34 centimeters
Reading 27 centimeters
Reading 48 centimeters
Reading 58 centimeters
Reading 49 centimeters
Reading 58 centimeters
Reading 58 centimeters
Reading 58 centimeters
Reading 49 centimeters
Reading 58 centimeters
Reading 48 centimeters
Reading 47 centimeters
Reading 46 centimeters
Reading 45 centimeters
Reading 46 centimeters
Reading 53 centimeters
Reading 51 centimeters
Reading 50 centimeters
Reading 49 centimeters
Reading 48 centimeters
Reading 38 centimeters
Reading 47 centimeters
Reading 47 centimeters

Závěr

Moje řešení měření vzdálenosti čidlem pomocí událostí na GPIO pinu mi přijde jednodušší na použití, je tam velmi málo kódu a je to dobře otestované. Je o něco pomalejší, ale nemusíme používat PIO automat. TRIGGER pin a ECHO pin si můžeme připojit, kam chceme, nemusí být nutně zapojeny za sebou.

Řešení pomocí knihovny Pico C++ HC-SR04 Library je rychlejší, použítí kódu je také velmi jednoduché. Nevýhodou je slučování kódu v C a C++. V případě poruchy čidla, kdy nedostáváme na ECHO pinu odpověď se program zakousne.

Zdroje a odkazy