Это моя первая статья, пожалуйста, не судите строго. Также хочу отметить, что я не являюсь скиловым 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)
technomancer
22.07.2025 08:19"комплекс ардуинщика" (в начале) - это опечатка или новое слово в психиатрии? :)
Dev_N_Router Автор
22.07.2025 08:19По любому новое слово в психиатрии. А если серьезно то я имел ввиду набор ардуино. Спасибо что указали мою ошибку
randomsimplenumber
22.07.2025 08:19Комплекс - несколько отдельных самостоятельных изделий, решающих общую задачу (зенитно-ракетный комплекс). Комплект - набор деталей или узлов, не соединённых производителем в одно целое.
VT100
22.07.2025 08:19Домашка-тудушка:
Выяснить "накладные расходы" на выполнение digitalwrite/read и выполнение деления пополам;
По документации HC-SR04 - выяснить максимальную частоту отправки зондирующих импульсов;
Разобраться, как снизить "накладные расходы" на отправку данных к ПК;
Выяснить необходимость "delay(100)";
Математически показать минимальную и максимальную дистанции работы (пренебрегая затуханием сигнала) и разрешение по дальности.
randomsimplenumber
22.07.2025 08:19Выяснить "накладные расходы" на выполнение digitalwrite/read и выполнение деления пополам
А умножение на 0.0344 нам бесплатно досталось? ;) Как для этого проекта - важна только точность измерения. Судя по по тому что дёргается функция из API - там аппаратный таймер (я надеюсь).
Как эту программу не оптимизируй, хоть на ассемблере перепиши - она все равно влезет в память ардуины ;)
VT100
22.07.2025 08:19Начнём с простых вещей - с двойки, которую видел в кошмаре Бендер. А там уж и до точечных чисел доберёмся.
Dev_N_Router Автор
22.07.2025 08:19Ооо. Ну тут для меня уже начинается темный лес. Но я постараюсь в этом разобраться
randomsimplenumber
22.07.2025 08:19Вам тут намекают, что ардуиновские функции медленные и лучше писать на ассемблере. Не ведитесь ;) Они сделаны умными людьми и достаточно быстрые.
VT100
22.07.2025 08:19Грамотно переписав их на Си - можно получить значимый прирост в скорости работы и/или плотности кода.
randomsimplenumber
22.07.2025 08:19Грамотно переписав их на Си
Хе-хе. Как вы думаете, на каком языке они написаны? ;)
VT100
22.07.2025 08:19Все знают, что они написаны на Си. Но - неграмотно.
randomsimplenumber
22.07.2025 08:19(Пожимает плечами)
Все знают, где лежат исходники Arduino core. Но форкнуть и переписать грамотно, почему то, нет желающих.
А что там неграмотного? Например.
VT100
22.07.2025 08:19randomsimplenumber
22.07.2025 08:19Понятно, что если ногами дрыгать через регистры, а писать код на ассемблере, то дрыгать можно быстрее. И в байтоложество тоже можно упороться, да Удобство работы оно не бесплатное.
xSVPx
22.07.2025 08:19Тут вообще-то такая задача обсуждается, для которой ЛЮБЫЕ тормоза - это потеря точности. Вот конкретно тут идея упарываться в скорость совершенно не выглядит плохой.
Что-то можно победить калибровками, но в целом "пишите как можно быстрее - тормозить будет само".
randomsimplenumber
22.07.2025 08:19так то оно да. только 1 мкс это 0.7мм, в этой физической модели. такое разрешение этот датчик не думаю что сможет выдать.
Упарываться в такты процессора имеет смысл, например, в светодиодной ленте. И в либах для rgb светодиодов критичные функции на ассемблере, интерфейс на плюсах, вызывается все это из обычного ардуиновского скетча, ичсх работает как надо.
theult
22.07.2025 08:19Delay - зло, надо отвыкать)
Ещё в большинстве плат есть вотчдог, для долго работающих устройств не повредит:
include <avr/wdt.h> // Библиотека Watchdog таймера - в начале кода
wdt_enable (WDTO_8S); // Включаем watchdog, время 8с (максимальное) - в setup
wdt_reset(); // Обнуляем таймер вотчдога - регулярно в бесконечном цикле
Alexis_Che
22.07.2025 08:19Ну, путь в тысячу миль начинается с первого шага.
Упорства в развитии и успехов
xSVPx
22.07.2025 08:19Ожидал увидеть результаты по точности и повторяемости измерений, диапазонам итд итп. Про калибровку может чего.
Вместо этого "узнал" что если крутить подстроечный резистор меняется яркость.
Разочарован :(
ExternalWayfarer
22.07.2025 08:19Зачем что-то ожидать от первого проекта. Ну хоть не резистором помигали, уже что-то.
randomsimplenumber
Ну, комментарии от Кэпа можно и не писать.
Dev_N_Router Автор
Спасибо большое за совет
xSVPx
Это скорее всего чатбот пишет. Т.е. мне уже неоднократно попадались листинги, где было к каждому объявлению приписано "объявляем переменную ... типа ...".