Это моя первая статья, пожалуйста, не судите строго. Также хочу отметить, что я не являюсь скиловым embedded разработчикам. Сказать по правде, я только учусь, и до того как поступил в универ, для меня это было темным лесом. Я даже мечтать не мог, что я когда-то своими руками сделаю свой прибор. Данный проект является очень простым — каждый может сделать свой дальномер у себя дома, если есть определенный комплекс ардуинщика.

Комплектующие для сборки и как должен работать

Чтобы сделать, этот проект мне понадобились следующие компоненты:

  • Макетная плата;

  • Плата Arduino Uno;

  • Ультразвуковой датчик HC-SR04;

  • LCD монитор 1602 i2c;

  • Несколько проводов «папа-мама», «папа-папа».

Скажу сразу — никакой комплект я не покупал, мне кафедра выделила для самостоятельного обучения. Но я советую взять комплект Arduino Uno суперстартовый набор по цене ~ 17 435 тенге (смотрел на озоне). Ссылку не буду вставлять, так как модераторы могут решить, что я рекламирую этот комплект. По сути, вы можете брать любой набор на ваше усмотрение, но желательно - большие комплекты с разными компонентами.

Ультразвуковой датчик HC-SR04 работает по простому принципу: Arduino посылает на пин Trig короткий импульс длительностью 10 микросекунд, и датчик в ответ излучает ультразвуковую волну. Эта волна распространяется в воздухе, отражается от ближайшего объекта и возвращается к датчику. Как только волна возвращается, пин Echo становится активным (HIGH) — в этот момент Arduino начинает измерять длительность сигнала. Полученное время используется для расчёта расстояния по формуле:

расстояние = (время * скорость звука) / 2

Деление на два нужно потому, что сигнал проходит путь до объекта и обратно. Затем рассчитанное расстояние выводится не только в Serial Monitor, но и на ЖК-дисплей, где пользователь может в реальном времени видеть, на каком расстоянии находится объект перед датчиком.

Схема подключение

Мои фотографии схемы подключения не сохранились, поэтому пришлось взять изображение из интернета.

Схема подключение
Схема подключение

Чтобы было более понятно, я расписал в таблицах, что к чему подключается.

HC-SR04

Arduino

Vcc

5V

GND

GND

Trig

D3

Echo

D2

LCD (I2C)

Arduino

Vcc

5V

GND

GND

SDA

A4

SCL

A5

I2C - это протокол передачи данных, который позволяет подключать к Arduino несколько устройств по двум проводам, вместо множества отдельных пинов. Обычный дисплей 1602 требует 6 пинов. Но если к нему припаять или купить готовый I2C-модуль, то он будет работать через всего 2 пина (SDA и SCL). В нашем случае I2C-модуль припаять к ЖК-дисплею.

Код Arduino

Основной язык программирования для Arduino является упрощённый C/C++.

#include <LiquidCrystal_I2C.h> // Подклучение билиотеки для использование ЖК Дисплея

LiquidCrystal_I2C lcd(0x27, 16, 2); // Формат ЖК Дисплея (Адресс, Длина, Ширина)

#define echoPin 2 // Подключение echoPinа к порту №2
#define trigPin 3 // Подключение trigPinа к порту №3

long duration; // Объявление переменной duration типа long, которая будет хранить длительность эхо-сигнала от датчика
              
int distance; // Обьявление переменной distance(дистанции) типа int, для хранение результата растояния
             

void setup()
{
    lcd.init(); // Инициализация дисплея
    lcd.backlight(); // Включение подсветки
  
    pinMode(trigPin,OUTPUT); // Пин Trig — выход        
    pinMode(echoPin, INPUT); // Пин echo — вход
  
    Serial.begin(9600); // Запуск связи с компьютером через Serial Monitor                                       
    Serial.println("Distance measurement using Arduino Uno");  
                   

    delay(500);
}

void loop()
{
    // Генерация ультразвукового импульса 
    digitalWrite(trigPin, LOW);         // Устанавливаем низкий уровень (0 В) на пин Trig
    delayMicroseconds(2);               // Ждём 2 микросекунды для стабильности сигнала
    
    digitalWrite(trigPin, HIGH);        // Устанавливаем высокий уровень (5 В) на Trig —
                                        // это и есть "триггер" (запуск) сигнала
    delayMicroseconds(10);              // Удерживаем сигнал HIGH 10 микросекунд —
                                        // датчику этого хватает, чтобы отправить ультразвук
    
    digitalWrite(trigPin, LOW);         // Сразу же сбрасываем пин обратно в LOW —
                                        // импульс завершён, датчик начал измерение
  
    duration = pulseIn(echoPin, HIGH); // Замер времени эхо-сигнала
    distance = duration * 0.0344 / 2; // Расчёт расстояния (в см)
    // Вывод в Serial Monitor
    Serial.print("Distance: ");
    Serial.print(distance);
    Serial.println(" cm");

    lcd.clear(); // Очистить буфер дисплея (стереть весь текст с экрана)
    lcd.setCursor(0, 0); // Установить курсор в позицию (0, 0) — первая строка, первый символ
        
    lcd.print("Distance:"); // Напечатать слово "Distance:" на экране (покажется в первой строке)
    lcd.setCursor(0,1); // Установить курсор в позицию (0, 1) — вторая строка, первый символ
                  
    lcd.print(distance); // Напечатать значение переменной distance (расстояние в см)
    lcd.setCursor(4, 1); // Переместить курсор на 5-й символ второй строки (позиция 4, 1)
    lcd.print("cm"); // Напечатать "cm" рядом с числом

    delay(100);
}

Мои ошибки при разработке

При разработке у меня возникали проблемы со ЖК-Дисплеем, он у меня все никак не высвечивался. Поначалу я подумал, что у меня в коде неправильно вызван адрес ЖК-дисплея. Чтобы узнать адрес, мне понадобилось найти в интернете проверочный скетч для просмотра адреса. Скетч предоставлен внизу этого абзаца.

#include <Wire.h> // Подключаем библиотеку для работы с шиной I2C

void setup() {
  Wire.begin();              // Инициализируем шину I2C
  Serial.begin(9600);        // Запускаем последовательную связь для вывода в монитор порта
  Serial.println("I2C Scanner"); // Сообщение о запуске сканера
}

void loop() {
  byte error, address;       // error — код ошибки передачи; address — текущий адрес для проверки
  int nDevices = 0;          // Счётчик найденных устройств

  Serial.println("Scanning..."); // Выводим сообщение о начале сканирования

  // Цикл от 1 до 126 (возможные I2C-адреса от 0x01 до 0x7E)
  for(address = 1; address < 127; address++ ) {
    Wire.beginTransmission(address);     // Пытаемся начать передачу к устройству по этому адресу
    error = Wire.endTransmission();      // Завершаем передачу и сохраняем код ошибки

    if (error == 0) { // Если ошибок нет — устройство откликнулось
      Serial.print("I2C device found at address 0x");
      if (address < 16) Serial.print("0"); // Добавляем ведущий 0 для адресов до 0x10
      Serial.print(address, HEX);         // Выводим адрес в 16-ричном виде
      Serial.println("  !");
      nDevices++;                         // Увеличиваем счётчик найденных устройств
    }
  }

  // Если не найдено ни одного устройства
  if (nDevices == 0)
    Serial.println("No I2C devices found\n");
  else
    Serial.println("done\n"); // Иначе — сканирование завершено успешно

  delay(5000); // Ждём 5 секунд перед повторным сканированием
}

Данный код я взял на сайте вольтик. https://voltiq.ru/how-to-find-a-device-i2c-address/ вот его URL-адрес.

После проверки выяснилось, что я поставил правильный адрес — значит, ошибка в другом. Я начал проверять, всё ли правильно подключил, и с помощью мультиметра определять, все ли компоненты рабочие. Все умные люди сразу же проверяют через мультиметр работоспособность компонентов, но я не отношусь к данной группе людей. Это уже потом я начал делать это в первую очередь. Ну я увлекся — вернемся к нашей проблеме. После всей проделанной операции, где я все проверил, я пошел к заведующему кафедрой с просьбой о помощи. Он также проверил всё, минуту подумал и сказал об одной интересной вещи, о которой я не догадывался. Сзади ЖК-Дисплея находиться маленький синенький кружок с углублением под отвертку крестовой формы. Он регулирует напряжение, и от этого напряжение зависит — загорится дисплей или нет. Мы покрутили его — и дисплей загорелся! Все заработала.

Заключение

Это был мой первый проект по Ардуино.

Если вам понравилась моя статья — напишите, пожалуйста, об этом в комментариях. Также хочется узнать мои недочеты и как лучше мне сделать в дальнейшем. Кстати, если вас интересуют сети — я скоро опубликую понятную статью о том, как всё это работает простым языком.

Комментарии (27)


  1. randomsimplenumber
    22.07.2025 08:19

    long duration; // Объявление переменной duration типа long, которая будет хранить длительность эхо-сигнала от датчика

    Ну, комментарии от Кэпа можно и не писать.


    1. Dev_N_Router Автор
      22.07.2025 08:19

      Спасибо большое за совет


    1. xSVPx
      22.07.2025 08:19

      Это скорее всего чатбот пишет. Т.е. мне уже неоднократно попадались листинги, где было к каждому объявлению приписано "объявляем переменную ... типа ...".


  1. technomancer
    22.07.2025 08:19

    "комплекс ардуинщика" (в начале) - это опечатка или новое слово в психиатрии? :)


    1. Dev_N_Router Автор
      22.07.2025 08:19

      По любому новое слово в психиатрии. А если серьезно то я имел ввиду набор ардуино. Спасибо что указали мою ошибку


      1. randomsimplenumber
        22.07.2025 08:19

        Комплекс - несколько отдельных самостоятельных изделий, решающих общую задачу (зенитно-ракетный комплекс). Комплект - набор деталей или узлов, не соединённых производителем в одно целое.


  1. VT100
    22.07.2025 08:19

    Домашка-тудушка:

    • Выяснить "накладные расходы" на выполнение digitalwrite/read и выполнение деления пополам;

    • По документации HC-SR04 - выяснить максимальную частоту отправки зондирующих импульсов;

    • Разобраться, как снизить "накладные расходы" на отправку данных к ПК;

    • Выяснить необходимость "delay(100)";

    • Математически показать минимальную и максимальную дистанции работы (пренебрегая затуханием сигнала) и разрешение по дальности.


    1. randomsimplenumber
      22.07.2025 08:19

      Выяснить "накладные расходы" на выполнение digitalwrite/read и выполнение деления пополам

      А умножение на 0.0344 нам бесплатно досталось? ;) Как для этого проекта - важна только точность измерения. Судя по по тому что дёргается функция из API - там аппаратный таймер (я надеюсь).

      Как эту программу не оптимизируй, хоть на ассемблере перепиши - она все равно влезет в память ардуины ;)


      1. VT100
        22.07.2025 08:19

        Начнём с простых вещей - с двойки, которую видел в кошмаре Бендер. А там уж и до точечных чисел доберёмся.


        1. Dev_N_Router Автор
          22.07.2025 08:19

          Ооо. Ну тут для меня уже начинается темный лес. Но я постараюсь в этом разобраться


          1. randomsimplenumber
            22.07.2025 08:19

            Вам тут намекают, что ардуиновские функции медленные и лучше писать на ассемблере. Не ведитесь ;) Они сделаны умными людьми и достаточно быстрые.


            1. VT100
              22.07.2025 08:19

              Грамотно переписав их на Си - можно получить значимый прирост в скорости работы и/или плотности кода.


              1. randomsimplenumber
                22.07.2025 08:19

                Грамотно переписав их на Си

                Хе-хе. Как вы думаете, на каком языке они написаны? ;)


                1. VT100
                  22.07.2025 08:19

                  Все знают, что они написаны на Си. Но - неграмотно.


                  1. randomsimplenumber
                    22.07.2025 08:19

                    (Пожимает плечами)

                    Все знают, где лежат исходники Arduino core. Но форкнуть и переписать грамотно, почему то, нет желающих.

                    А что там неграмотного? Например.


                    1. VT100
                      22.07.2025 08:19

                      (Пожимает плечами)
                      Там оверхед.


                      1. randomsimplenumber
                        22.07.2025 08:19

                        Понятно, что если ногами дрыгать через регистры, а писать код на ассемблере, то дрыгать можно быстрее. И в байтоложество тоже можно упороться, да Удобство работы оно не бесплатное.


                      1. xSVPx
                        22.07.2025 08:19

                        Тут вообще-то такая задача обсуждается, для которой ЛЮБЫЕ тормоза - это потеря точности. Вот конкретно тут идея упарываться в скорость совершенно не выглядит плохой.

                        Что-то можно победить калибровками, но в целом "пишите как можно быстрее - тормозить будет само".


                      1. randomsimplenumber
                        22.07.2025 08:19

                        так то оно да. только 1 мкс это 0.7мм, в этой физической модели. такое разрешение этот датчик не думаю что сможет выдать.

                        Упарываться в такты процессора имеет смысл, например, в светодиодной ленте. И в либах для rgb светодиодов критичные функции на ассемблере, интерфейс на плюсах, вызывается все это из обычного ардуиновского скетча, ичсх работает как надо.


  1. theult
    22.07.2025 08:19

    Delay - зло, надо отвыкать)

    Ещё в большинстве плат есть вотчдог, для долго работающих устройств не повредит:

    include <avr/wdt.h> // Библиотека Watchdog таймера - в начале кода

    wdt_enable (WDTO_8S); // Включаем watchdog, время 8с (максимальное) - в setup

    wdt_reset(); // Обнуляем таймер вотчдога - регулярно в бесконечном цикле


    1. randomsimplenumber
      22.07.2025 08:19

      Delay - зло

      Всё зло, если нет понимания зачем оно.


    1. Dev_N_Router Автор
      22.07.2025 08:19

      Спасибо за пояснения


  1. Alexis_Che
    22.07.2025 08:19

    Ну, путь в тысячу миль начинается с первого шага.

    Упорства в развитии и успехов


    1. Dev_N_Router Автор
      22.07.2025 08:19

      Спасибо большое


  1. xSVPx
    22.07.2025 08:19

    Ожидал увидеть результаты по точности и повторяемости измерений, диапазонам итд итп. Про калибровку может чего.

    Вместо этого "узнал" что если крутить подстроечный резистор меняется яркость.

    Разочарован :(


    1. ExternalWayfarer
      22.07.2025 08:19

      Зачем что-то ожидать от первого проекта. Ну хоть не резистором помигали, уже что-то.


      1. xSVPx
        22.07.2025 08:19

        Я от статьи на хабре ожидал вообще-то...