Это моя первая статья, пожалуйста, не судите строго. Также хочу отметить, что я не являюсь скиловым embedded разработчикам. Сказать по правде, я только учусь, и до того как поступил в универ, для меня это было темным лесом. Я даже мечтать не мог, что я когда-то своими руками сделаю свой прибор. Данный проект является очень простым — каждый может сделать свой дальномер у себя дома, если есть определенный комплекс ардуинщика.
Комплектующие для сборки и как должен работать
Чтобы сделать, этот проект мне понадобились следующие компоненты:
- Макетная плата; 
- Плата Arduino Uno; 
- Ультразвуковой датчик HC-SR04; 
- LCD монитор 1602 i2c; 
- Несколько проводов «папа-мама», «папа-папа». 
Скажу сразу — никакой комплект я не покупал, мне кафедра выделила для самостоятельного обучения. Но я советую взять комплект Arduino Uno суперстартовый набор по цене ~ 17 435 тенге (смотрел на озоне). Ссылку не буду вставлять, так как модераторы могут решить, что я рекламирую этот комплект. По сути, вы можете брать любой набор на ваше усмотрение, но желательно - большие комплекты с разными компонентами.
Ультразвуковой датчик HC-SR04 работает по простому принципу: Arduino посылает на пин Trig короткий импульс длительностью 10 микросекунд, и датчик в ответ излучает ультразвуковую волну. Эта волна распространяется в воздухе, отражается от ближайшего объекта и возвращается к датчику. Как только волна возвращается, пин Echo становится активным (HIGH) — в этот момент Arduino начинает измерять длительность сигнала. Полученное время используется для расчёта расстояния по формуле:
Деление на два нужно потому, что сигнал проходит путь до объекта и обратно. Затем рассчитанное расстояние выводится не только в 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)
 - technomancer22.07.2025 08:19- "комплекс ардуинщика" (в начале) - это опечатка или новое слово в психиатрии? :)  - Dev_N_Router Автор22.07.2025 08:19- По любому новое слово в психиатрии. А если серьезно то я имел ввиду набор ардуино. Спасибо что указали мою ошибку  - randomsimplenumber22.07.2025 08:19- Комплекс - несколько отдельных самостоятельных изделий, решающих общую задачу (зенитно-ракетный комплекс). Комплект - набор деталей или узлов, не соединённых производителем в одно целое. 
 
 
 - VT10022.07.2025 08:19- Домашка-тудушка: - Выяснить "накладные расходы" на выполнение digitalwrite/read и выполнение деления пополам; 
- По документации HC-SR04 - выяснить максимальную частоту отправки зондирующих импульсов; 
- Разобраться, как снизить "накладные расходы" на отправку данных к ПК; 
- Выяснить необходимость "delay(100)"; 
- Математически показать минимальную и максимальную дистанции работы (пренебрегая затуханием сигнала) и разрешение по дальности. 
  - randomsimplenumber22.07.2025 08:19- Выяснить "накладные расходы" на выполнение digitalwrite/read и выполнение деления пополам - А умножение на 0.0344 нам бесплатно досталось? ;) Как для этого проекта - важна только точность измерения. Судя по по тому что дёргается функция из API - там аппаратный таймер (я надеюсь). - Как эту программу не оптимизируй, хоть на ассемблере перепиши - она все равно влезет в память ардуины ;)  - VT10022.07.2025 08:19- Начнём с простых вещей - с двойки, которую видел в кошмаре Бендер. А там уж и до точечных чисел доберёмся.  - Dev_N_Router Автор22.07.2025 08:19- Ооо. Ну тут для меня уже начинается темный лес. Но я постараюсь в этом разобраться  - randomsimplenumber22.07.2025 08:19- Вам тут намекают, что ардуиновские функции медленные и лучше писать на ассемблере. Не ведитесь ;) Они сделаны умными людьми и достаточно быстрые.  - VT10022.07.2025 08:19- Грамотно переписав их на Си - можно получить значимый прирост в скорости работы и/или плотности кода.  - randomsimplenumber22.07.2025 08:19- Грамотно переписав их на Си - Хе-хе. Как вы думаете, на каком языке они написаны? ;)  - VT10022.07.2025 08:19- Все знают, что они написаны на Си. Но - неграмотно.  - randomsimplenumber22.07.2025 08:19- (Пожимает плечами) - Все знают, где лежат исходники Arduino core. Но форкнуть и переписать грамотно, почему то, нет желающих. - А что там неграмотного? Например.  - VT10022.07.2025 08:19 - randomsimplenumber22.07.2025 08:19- Понятно, что если ногами дрыгать через регистры, а писать код на ассемблере, то дрыгать можно быстрее. И в байтоложество тоже можно упороться, да Удобство работы оно не бесплатное. 
  - xSVPx22.07.2025 08:19- Тут вообще-то такая задача обсуждается, для которой ЛЮБЫЕ тормоза - это потеря точности. Вот конкретно тут идея упарываться в скорость совершенно не выглядит плохой. - Что-то можно победить калибровками, но в целом "пишите как можно быстрее - тормозить будет само". 
  - randomsimplenumber22.07.2025 08:19- так то оно да. только 1 мкс это 0.7мм, в этой физической модели. такое разрешение этот датчик не думаю что сможет выдать. - Упарываться в такты процессора имеет смысл, например, в светодиодной ленте. И в либах для rgb светодиодов критичные функции на ассемблере, интерфейс на плюсах, вызывается все это из обычного ардуиновского скетча, ичсх работает как надо. 
 
 
 
 
 
 
 
 
 
 
 - theult22.07.2025 08:19- Delay - зло, надо отвыкать) - Ещё в большинстве плат есть вотчдог, для долго работающих устройств не повредит: - include <avr/wdt.h> // Библиотека Watchdog таймера - в начале кода - wdt_enable (WDTO_8S); // Включаем watchdog, время 8с (максимальное) - в setup - wdt_reset(); // Обнуляем таймер вотчдога - регулярно в бесконечном цикле 
 - Alexis_Che22.07.2025 08:19- Ну, путь в тысячу миль начинается с первого шага. - Упорства в развитии и успехов 
 - xSVPx22.07.2025 08:19- Ожидал увидеть результаты по точности и повторяемости измерений, диапазонам итд итп. Про калибровку может чего. - Вместо этого "узнал" что если крутить подстроечный резистор меняется яркость. - Разочарован :(  - ExternalWayfarer22.07.2025 08:19- Зачем что-то ожидать от первого проекта. Ну хоть не резистором помигали, уже что-то. 
 
 
           
 
randomsimplenumber
Ну, комментарии от Кэпа можно и не писать.
Dev_N_Router Автор
Спасибо большое за совет
xSVPx
Это скорее всего чатбот пишет. Т.е. мне уже неоднократно попадались листинги, где было к каждому объявлению приписано "объявляем переменную ... типа ...".