Лазерный модуль KY-008 и Arduino: лазерная сигнализация и таймер
Лазерный модуль KY-008 и Arduino: подключение, безопасность для глаз (5 mW, 650 нм), лазерный барьер с LDR, автоматическая калибровка порога, зеркала, таймер забега и уведомления с ESP32.
Лазерный модуль — одна из самых эффектных деталей для Arduino: он даёт тонкий красный луч, который проходит метры, почти не расходясь. Лазерный модуль KY-008 из любительских наборов — это лазерный диод 650 нм мощностью около 5 mW на маленькой плате; он подключается к одному цифровому пину Arduino и включается и выключается через digitalWrite(). Сам по себе это просто указка, но стоит поставить напротив датчик света — и получится лазерная сигнализация как в кино, счётчик людей, проходящих через дверь, или таймер забега с точностью до миллисекунды.
В этом руководстве ты узнаешь характеристики KY-008, насколько на самом деле опасен для глаз лазер на 5 mW и каких правил придерживаться, а также как правильно подключить модуль к Arduino Uno, ESP32 и Raspberry Pi Pico. Затем соберём лазерный барьер с LDR, научим Arduino самостоятельно измерять порог вместо того, чтобы подбирать его наугад, превратим один лазер в несколько лучей с помощью зеркал, будем считать проходы и измерять скорость, а с ESP32 — отправлять уведомление на телефон, когда луч прерывается.

Что такое лазерный модуль KY-008 и как он работает?
Сердце модуля — маленький лазерный диод внутри латунной трубки. В отличие от обычного светодиода, лазерный диод, как только ток превышает определённый порог, излучает свет одной длины волны и упорядоченно (когерентно); крошечная линза на конце трубки превращает этот свет в почти параллельный луч. Поэтому свет светодиода расплывается уже через несколько сантиметров, а лазерная точка даже за несколько метров остаётся размером всего в несколько миллиметров. 650 нм — насыщенный красный, который глаз видит легко. Резистор на маленькой плате за лазерной головкой ограничивает ток через диод. Лазерные диоды гораздо чувствительнее к перегрузке по току, чем светодиоды; если обойти этот резистор и подать питание прямо на диод, он может сгореть за секунды.
| Параметр | Значение |
|---|---|
| Модель | KY-008 (некоторые продавцы называют его HW-493) |
| Длина волны | 650 нм (видимый красный) |
| Выходная мощность | Около 5 mW |
| Класс лазера | 3R (IEC 60825-1), IIIa в США |
| Рабочее напряжение | 5 V (при 3,3 V тусклее) |
| Ток | Меньше 40 mA; отличается от платы к плате |
| Лазерная головка | Латунная трубка 6 мм |
| Пины | S (сигнал), средний пин (+5V или не подключён на большинстве плат), − (GND) |
| Рабочая температура | От −10 до 40 °C |
Безопасность для глаз: насколько опасен лазер на 5 mW?
Опасность лазера не в его общей мощности, а в том, что эта мощность собрана в крошечное пятно. Хрусталик глаза фокусирует параллельный луч в точку размером в микроны на сетчатке, поэтому 5 mW, безобидные на вид на стене, могут дать на сетчатке пятно ярче, чем если смотреть прямо на солнце. По этому риску лазеры делят на классы. У лазеров класса 2 мощностью ниже 1 mW обычно хватает защиты мигательного рефлекса (около 0,25 секунды). Класс 3R, к которому относится KY-008, допускает до пятикратного превышения этого предела: короткий случайный взгляд малоопасен, но смотреть в луч напрямую или через зеркало — значит рисковать повредить глаз.
| Класс | Мощность (видимый свет) | Риск |
|---|---|---|
| 1 | Очень низкая, или луч закрыт внутри устройства | Безопасен при обычном использовании |
| 2 | До 1 mW | Защищает мигательный рефлекс; не смотри в луч нарочно |
| 3R | 1–5 mW (KY-008) | Прямой взгляд может повредить глаз |
| 3B | 5–500 mW | Опасны прямой и отражённый от зеркала луч |
| 4 | Выше 500 mW | Опасность для глаз, кожи и риск пожара |
- Никогда не смотри в луч: не подноси выходной конец лазера к глазу и не размахивай включённым модулем в руке.
- Держи луч ниже уровня глаз: монтируй лучи сигнализации и счётчика на высоте 20–50 см от пола. Эта высота совпадает с уровнем глаз ползающего малыша, маленького ребёнка, кошки или собаки, поэтому не оставляй лазер включённым, когда они рядом.
- Берегись отражений: зеркала, стекло, циферблаты часов и блестящий металл отправляют луч в неожиданные стороны. Проверь путь луча от начала до конца и закончи его на матовой тёмной поверхности.
- Детям нельзя работать с лазером в одиночку: лазер — не игрушка; в школьных проектах и на научных выставках работай под присмотром взрослых.
- Не доверяй этикетке вслепую: реальная мощность дешёвых модулей может отличаться от указанной; считай любой модуль как минимум лазером класса 3R.
- Выравнивай с листом бумаги: держи белый лист перед лучом и следи за точкой на бумаге; никогда не заглядывай в луч, чтобы понять, куда он идёт.
Подключение KY-008: Arduino Uno, ESP32 и Raspberry Pi Pico
У KY-008 три пина: S — сигнальный, при HIGH включает лазер, − — это GND, а немаркированный средний пин на большинстве плат идёт на +5V или вообще никуда не подключён. Лазер стоит между S и −, поэтому пин Arduino, подключённый к S, напрямую питает лазер током. Для коротких тестов на Arduino Uno это нормально, но ток модуля отличается от платы к плате и может приближаться к 40 mA. Это абсолютный максимум ATmega328P на один пин; рекомендуемое значение — 20 mA. Если лазер будет гореть часами, если лазеров несколько или работа идёт с ESP32 или Pico, питай лазер от 5V и коммутируй его NPN-транзистором (BC547, S8050 или 2N2222). Распиновку транзистора можно проверить в утилите распиновки транзисторов.
| Подключение | Arduino Uno | ESP32 | Raspberry Pi Pico |
|---|---|---|---|
| KY-008 S | D7 | 5V (VIN) | VBUS (пин 40) |
| KY-008, средний пин | Не подключён (5V, если лазер не горит) | 5V (VIN) | VBUS (пин 40) |
| KY-008 − | GND | Коллектор NPN | Коллектор NPN |
| Эмиттер NPN | – | GND | GND (пин 38) |
| База NPN (через 1 kΩ) | – | GPIO 26 | GP15 (пин 20) |
| Ножка 1 LDR | 5V | 3V3 | 3V3(OUT) (пин 36) |
| Ножка 2 LDR + верхний вывод резистора | A0 | GPIO 34 | GP26 (пин 31) |
| Нижний вывод резистора (3,3–10 kΩ) | GND | GND | GND |
| Активный зуммер + | D8 | GPIO 25 | GP14 (пин 19) |
Прежде чем переходить к скетчам, проверь лазер и LDR вместе небольшой тестовой программой ниже. Она включает и выключает лазер каждые полсекунды и выводит показание в обоих состояниях в монитор порта. Чем больше разрыв между двумя значениями, тем надёжнее барьер: на Uno разрыв больше 300 — это очень хорошо; меньше 100 — значит, луч попадает на LDR не точно или внешний свет слишком сильный.
1// KY-008 + LDR ilk test: lazer açıkken ve kapalıyken değerleri gör / First test: see readings with the laser on and off
2const int LASER_PIN = 7; // KY-008 S pini / KY-008 S pin
3const int LDR_PIN = A0; // LDR bölücü çıkışı / LDR divider output
4
5void setup() {
6 Serial.begin(9600);
7 pinMode(LASER_PIN, OUTPUT);
8}
9
10void loop() {
11 digitalWrite(LASER_PIN, HIGH); // lazer açık / laser on
12 delay(500); // LDR'nin oturmasını bekle / let the LDR settle
13 int onValue = analogRead(LDR_PIN);
14
15 digitalWrite(LASER_PIN, LOW); // lazer kapalı / laser off
16 delay(500);
17 int offValue = analogRead(LDR_PIN);
18
19 Serial.print(F("Isin var / beam: "));
20 Serial.print(onValue);
21 Serial.print(F(" Isin yok / no beam: "));
22 Serial.print(offValue);
23 Serial.print(F(" Fark / gap: "));
24 Serial.println(onValue - offValue);
25}Как работает лазерный барьер на LDR?
Идея лазерного барьера проста: лазерный луч постоянно падает на LDR (фоторезистор) на другой стороне. Когда на нём лазерная точка, сопротивление LDR падает до нескольких сотен ом; когда луч прерывается, оно растёт до десятков килоом. Если включить LDR с постоянным резистором в делитель напряжения, это изменение сопротивления превращается в изменение напряжения на аналоговом пине: при схеме 5V → LDR → A0 → резистор → GND на A0 высокое значение, пока луч на месте, и низкое, когда он прерван. Как только кто-то проходит через луч, значение падает, и Arduino поднимает тревогу.



Быстрый расчёт показывает, почему лазер так сильно действует на LDR: когда луч мощностью 5 mW собран в точку диаметром около 3 мм, интенсивность получается примерно 700 W на квадратный метр. Это тот же порядок, что и полуденный солнечный свет, и в сотни раз ярче, чем в хорошо освещённой комнате. И всё же главный враг барьера — внешний свет: перед солнечным окном LDR остаётся освещённым, даже когда луч прерван, и сигнализация ничего не замечает. Решение очень дешёвое: помести LDR на дно чёрной трубки длиной 3–5 см (пустой корпус шариковой ручки, чёрная соломинка или термоусадочная трубка). Трубка пропускает только лазер, идущий прямо спереди, и закрывает внешний свет с боков.
| Ситуация | Сопротивление LDR (пример) | analogRead с 10 kΩ | analogRead с 3,3 kΩ |
|---|---|---|---|
| Лазер на LDR (в трубке) | 300 Ω | 993 | 938 |
| Луч прерван (в трубке) | 30 kΩ | 256 | 101 |
| Луч прерван (без трубки, светлая комната) | 3 kΩ | 787 | 536 |
| Разрыв между «есть луч» и «нет луча» (в трубке) | – | 737 | 837 |
Пошагово: лазерная сигнализация на Arduino
- Подключи пин S модуля KY-008 к D7, а пин − к GND; средний пин оставь неподключённым (если лазер не горит, подключи его к 5V).
- Одну ножку LDR подключи к 5V, другую — к A0; между A0 и GND поставь резистор 3,3 kΩ или 10 kΩ.
- Помести LDR на дно чёрной трубки и направь отверстие трубки на лазер.
- Подключи ножку + активного зуммера к D8, а ножку − к GND. Кнопку отключения звука поставь между D4 и GND; дополнительный резистор не нужен, потому что используется встроенная подтяжка.
- Закрепи лазер и LDR на прочном основании; сначала найди лазерную точку листом белой бумаги, затем заведи её в трубку.
- Загрузи скетч и открой монитор порта на скорости 9600 бод. При запуске перед лучом никого не должно быть: Arduino калибруется, один раз выключая и включая лазер.
1// KY-008 lazer + LDR ile lazerli alarm / Laser tripwire alarm with KY-008 + LDR
2const int LASER_PIN = 7; // KY-008 S pini ya da transistör bazı / KY-008 S pin or transistor base
3const int LDR_PIN = A0; // LDR bölücü çıkışı / LDR divider output
4const int BUZZER_PIN = 8; // aktif buzzer / active buzzer
5const int LED_PIN = 13; // kart üzerindeki LED / on-board LED
6const int RESET_PIN = 4; // susturma butonu, diğer ucu GND / silence button, other leg to GND
7
8const int BREAK_PCT = 40; // %40'ın altı = ışın kesik / below 40% = beam broken
9const int RESTORE_PCT = 60; // %60'ın üstü = ışın sağlam / above 60% = beam intact
10const unsigned long CONFIRM_MS = 30; // en az bu kadar kesik kalmalı / must stay broken this long
11const unsigned long ALARM_MS = 10000; // alarm süresi / alarm duration
12
13int onLevel = 0, offLevel = 0; // ışınlı ve ışınsız ölçüm / reading with and without the beam
14bool beamOk = true;
15bool alarmOn = false;
16unsigned long brokenSince = 0;
17unsigned long alarmStart = 0;
18
19int readAvg() {
20 long sum = 0;
21 for (int i = 0; i < 8; i++) sum += analogRead(LDR_PIN); // 8 ölçüm ortalaması / average of 8 readings
22 return sum / 8;
23}
24
25void calibrate() {
26 while (true) {
27 digitalWrite(LASER_PIN, LOW); // lazer kapalı = kesik ışın / laser off = broken beam
28 delay(300); // LDR yavaştır, bekle / the LDR is slow, wait
29 offLevel = readAvg();
30 digitalWrite(LASER_PIN, HIGH); // lazer açık = sağlam ışın / laser on = intact beam
31 delay(300);
32 onLevel = readAvg();
33 Serial.print(F("Isinli / beam: "));
34 Serial.print(onLevel);
35 Serial.print(F(" Isinsiz / no beam: "));
36 Serial.println(offLevel);
37 if (abs(onLevel - offLevel) >= 100) return; // yeterli fark var / enough difference
38 Serial.println(F("Isin LDR'ye ulasmiyor, hizala! / Beam misses the LDR, align it!"));
39 delay(2000);
40 }
41}
42
43// 100 = ışın tam, 0 = ışın yok; LDR'nin yönü fark etmez / 100 = full beam, 0 = no beam; LDR polarity doesn't matter
44int beamPercent() {
45 return (long)(readAvg() - offLevel) * 100 / (onLevel - offLevel);
46}
47
48void setup() {
49 Serial.begin(9600);
50 pinMode(LASER_PIN, OUTPUT);
51 pinMode(BUZZER_PIN, OUTPUT);
52 pinMode(LED_PIN, OUTPUT);
53 pinMode(RESET_PIN, INPUT_PULLUP);
54 calibrate();
55 Serial.println(F("Alarm hazir / Alarm armed"));
56}
57
58void loop() {
59 unsigned long now = millis();
60 int pct = beamPercent();
61
62 if (beamOk && pct < BREAK_PCT) { // ışın yeni kesildi / beam just broke
63 beamOk = false;
64 brokenSince = now;
65 } else if (!beamOk && pct > RESTORE_PCT) {
66 beamOk = true; // ışın geri geldi / beam restored
67 }
68
69 if (!beamOk && !alarmOn && now - brokenSince >= CONFIRM_MS) {
70 alarmOn = true;
71 alarmStart = now;
72 Serial.println(F("ALARM: isin kesildi! / beam broken!"));
73 }
74
75 if (alarmOn && (now - alarmStart >= ALARM_MS || digitalRead(RESET_PIN) == LOW)) {
76 alarmOn = false; // süre doldu ya da butona basıldı / timeout or button pressed
77 }
78
79 digitalWrite(LED_PIN, beamOk ? LOW : HIGH);
80 digitalWrite(BUZZER_PIN, (alarmOn && (now / 200) % 2) ? HIGH : LOW); // kesik siren / beeping siren
81}Скетч не использует фиксированный порог. При запуске calibrate() выключает лазер и измеряет значение LDR без луча, затем включает его и измеряет значение с лучом. beamPercent() по этим двум значениям переводит каждое показание в процент от 0 до 100: 100 означает, что луч приходит полностью, 0 — что он полностью перекрыт. Этому расчёту всё равно, стоит LDR вверху или внизу делителя: результат одинаков, растёт показание от света или падает. Если луч опускается ниже 40% и держится там не меньше 30 мс, сигнализация пищит 10 секунд, а кнопка на D4 выключает её раньше. Если луч по-прежнему прерван, тревога начинается снова, так что система не замолчит, пока кто-то стоит в луче.
Калибровка порога: почему не стоит вшивать число в код
Во многих примерах лазерной сигнализации в интернете используется фиксированный порог вроде «analogRead(A0) < 500». Вечером за столом на тесте это работает, но наутро, когда в комнату заглянет солнце, лазер сдвинется на пару миллиметров или LDR заменят, всё ломается. Правильный порог лежит ровно посередине между показаниями с лучом и без луча. Автоматическая калибровка, которая заново измеряет оба значения при каждом запуске, подстраивает систему под окружение. В скетче выше порог — не одно число, а два: луч считается прерванным, когда падает ниже 40%, и целым, когда поднимается выше 60%. Такой зазор называется гистерезисом; он не даёт сигнализации включаться и выключаться десятки раз в секунду, когда показание колеблется прямо у порога.
| Параметр | Значение в коде | Что делает? | Когда менять? |
|---|---|---|---|
| BREAK_PCT | 40 | Уровень, при котором луч считается прерванным | Подними до 60–70, чтобы ловить и полупрозрачные предметы (стекло, тонкую ткань) |
| RESTORE_PCT | 60 | Уровень, при котором луч снова считается целым | Всегда держи на 15–20 пунктов выше BREAK_PCT |
| CONFIRM_MS | 30 | Игнорирует короткие помехи (насекомые, пыль, вибрация) | При ложных срабатываниях подними до 100–200 мс; если быстрые проходы пропускаются, уменьши |
| Число усредняемых измерений | 8 | Сглаживает электрические помехи и мерцание ламп | На Uno одно измерение занимает около 0,1 мс; 8 измерений укладываются меньше чем в 1 мс |
| Калибровочный разрыв | 100 | Останавливает настройку, если луч не доходит до LDR | На очень больших расстояниях или в очень светлых комнатах можно снизить до 50 |
В системе, которая работает долго, внешний свет в течение дня медленно меняется. Простой способ это компенсировать — снова вызывать calibrate(), когда луч цел и сигнализация молчит (например, раз в час). Arduino сама выключает лазер, поэтому не примет эту короткую темноту за нарушителя. Следить за показаниями очень удобно в «Плоттере по последовательному соединению» (Serial Plotter) Arduino IDE: проведи рукой через луч — и увидишь, насколько падает график и сколько времени нужно, чтобы он восстановился.
Несколько лучей с зеркалами: сеть из одного лазера
Один луч легко обойти, пригнувшись под ним или перешагнув через него. Чтобы построить лазерную сеть, как в фильмах, не обязательно давать каждому лучу свой лазер и LDR: один лазер можно пустить зигзагом с помощью зеркал. Зеркала ставят друг напротив друга по обеим сторонам двери или коридора; луч отскакивает от стороны к стороне и в конце попадает на LDR. Если прерывается любой участок, LDR темнеет, так что один LDR и тот же скетч охраняют всю сеть. Угол отражения равен углу падения, поэтому каждое зеркало нужно лишь чуть-чуть наклонить, чтобы оно отправляло луч в центр следующего.
Каждое зеркало теряет часть света. Обычное зеркало для ванной отражает около 90% и даёт слабую вторую точку (паразитное отражение) от переднего стекла. Зеркала с внешним отражающим слоем, у которых отражающее покрытие нанесено поверх стекла, отражают 95% и больше и дают одну чистую точку. Потери перемножаются, поэтому с каждым новым зеркалом свет, доходящий до LDR, быстро уменьшается:
| Число зеркал | Обычное зеркало (90%) | Зеркало с внешним слоем (95%) |
|---|---|---|
| 1 | 90% | 95% |
| 2 | 81% | 90% |
| 4 | 66% | 81% |
| 6 | 53% | 74% |
| 8 | 43% | 66% |
Эта таблица показывает важную вещь: даже 43% более чем достаточно для откалиброванного скетча, потому что лазерная точка в сотни раз ярче внешнего света. Настоящая трудность не в потере мощности, а в юстировке. Чем длиннее путь луча, тем сильнее растёт маленькая ошибка угла: ошибка в 1 градус сдвигает точку примерно на 9 см на расстоянии 5 м. Луч дешёвого лазера к тому же немного расширяется по пути; через несколько метров точка может стать больше 5-миллиметровой поверхности LDR.
- Закрепи лазер, зеркала и трубку с LDR на прочных держателях (уголки на винтах, держатели, напечатанные на 3D-принтере, или деревянные бруски на термоклее); зеркало в руке никогда не держит юстировку.
- Притуши свет в комнате и найди лазерную точку, держа белый лист перед первым зеркалом.
- Выравнивай зеркала по порядку от начала к концу: наклоняй каждое совсем чуть-чуть, пока точка не попадёт в центр следующего зеркала.
- Направь последний луч в отверстие трубки с LDR и запусти тестовый скетч, чтобы найти положение, при котором разрыв в мониторе порта наибольший.
- Когда всё выровнено, зафиксируй угол зеркал термоклеем или стопорным винтом: даже лёгкий толчок стола может сбить юстировку.
- Проверь каждый участок сети, по очереди перекрывая его рукой, и убедись, что сигнализация каждый раз срабатывает.
Счётчик проходов: сколько прошло и за какое время?
Тот же барьер может считать людей, проходящих через дверь, коробки на конвейере или мячи, падающие в корзину. Хитрость в том, чтобы считать каждый проход один раз. Скетч ниже запоминает момент, когда луч прерывается, и засчитывает проход, когда луч возвращается, поэтому человек, который какое-то время стоит в луче, всё равно считается одним проходом. Прерывания короче 15 мс считаются пылью или электрическими помехами и игнорируются. Скетч также выводит, сколько времени луч был перекрыт: если известна длина предмета, это время сразу даёт его скорость (v = L / t). Например, если игрушечная машинка длиной 25 см перекрывает луч на 50 мс, её скорость 0,25 / 0,05 = 5 м/с.
1// Lazer bariyeriyle geçiş sayacı ve kesilme süresi / Laser barrier pass counter and block time
2const int LASER_PIN = 7; // KY-008 S pini / KY-008 S pin
3const int LDR_PIN = A0; // LDR bölücü çıkışı / LDR divider output
4const unsigned long MIN_BREAK_MS = 15; // daha kısa kesintiler parazittir / shorter breaks are noise
5const float OBJECT_LENGTH_M = 0.0; // cismin boyu biliniyorsa yaz (ör. 0.25) / object length if known (e.g. 0.25)
6
7int onLevel = 0, offLevel = 0;
8bool beamOk = true;
9unsigned long brokenSince = 0;
10unsigned long passCount = 0;
11
12int readAvg() {
13 long sum = 0;
14 for (int i = 0; i < 4; i++) sum += analogRead(LDR_PIN); // 4 ölçüm yeterli ve hızlı / 4 readings: enough and fast
15 return sum / 4;
16}
17
18void calibrate() {
19 while (true) {
20 digitalWrite(LASER_PIN, LOW);
21 delay(300);
22 offLevel = readAvg();
23 digitalWrite(LASER_PIN, HIGH);
24 delay(300);
25 onLevel = readAvg();
26 if (abs(onLevel - offLevel) >= 100) return;
27 Serial.println(F("Isin LDR'ye ulasmiyor / Beam misses the LDR"));
28 delay(2000);
29 }
30}
31
32int beamPercent() {
33 return (long)(readAvg() - offLevel) * 100 / (onLevel - offLevel);
34}
35
36void setup() {
37 Serial.begin(9600);
38 pinMode(LASER_PIN, OUTPUT);
39 calibrate();
40 Serial.println(F("Sayac hazir / Counter ready"));
41}
42
43void loop() {
44 unsigned long now = millis();
45 int pct = beamPercent();
46
47 if (beamOk && pct < 40) {
48 beamOk = false;
49 brokenSince = now; // kesilme anı / moment of the break
50 } else if (!beamOk && pct > 60) {
51 beamOk = true;
52 unsigned long blocked = now - brokenSince; // ışın ne kadar kesik kaldı / how long the beam was blocked
53 if (blocked >= MIN_BREAK_MS) {
54 passCount++;
55 Serial.print(F("Gecis / Pass #"));
56 Serial.print(passCount);
57 Serial.print(F(" Sure / Time: "));
58 Serial.print(blocked);
59 Serial.print(F(" ms"));
60 if (OBJECT_LENGTH_M > 0) {
61 float speed = OBJECT_LENGTH_M / (blocked / 1000.0); // v = L / t
62 Serial.print(F(" Hiz / Speed: "));
63 Serial.print(speed, 2);
64 Serial.print(F(" m/s"));
65 }
66 Serial.println();
67 }
68 }
69}Чтобы знать, в какую сторону идёт человек, используй два параллельных луча на расстоянии 10–15 см. Если сначала прерывается внешний луч A, а затем внутренний B, человек входит; если сначала B, а затем A — выходит. Если прерывается только A и луч возвращается, а B так и не прерывается, значит, человек подошёл к двери и передумал, и это не считается. Число людей внутри получается вычитанием выходов из входов, и его можно показать на 7-сегментном индикаторе или LCD-дисплее с I2C. Расстояние между лучами должно быть достаточно маленьким, чтобы тело человека перекрывало оба луча одновременно.
Таймер забега и измерение скорости
Измерение времени между двумя лазерными воротами даёт точный секундомер для бега, велосипеда, игрушечных машинок или физических опытов. Ворота старта и финиша ставят на известном расстоянии (d) друг от друга, а скорость равна v = d / t. Кто пробегает 10-метровую дорожку за 2,5 секунды, бежит со скоростью 4 м в секунду, то есть 14,4 км/ч. Чтобы включать и выключать оба лазера вместе, подключи пин − каждого к коллектору одного транзистора 2N2222 или S8050; пины S и средние пины соедини с 5V, эмиттер транзистора — с GND, а базу — с D7 через 1 kΩ. LDR ворот старта идёт на A0, а ворот финиша — на A1.
1// İki lazer kapılı yarış zamanlayıcı ve hız ölçer / Two-gate laser race timer and speed meter
2const int LASER_PIN = 7; // iki lazer tek transistörle / both lasers through one transistor
3const int START_LDR = A0; // başlangıç kapısı / start gate
4const int FINISH_LDR = A1; // bitiş kapısı / finish gate
5const int BUZZER_PIN = 8; // aktif buzzer / active buzzer
6const float DISTANCE_M = 10.0; // kapılar arası mesafe (metre) / distance between the gates (m)
7const unsigned long TIMEOUT_MS = 60000; // 60 sn'de bitmezse iptal / cancel after 60 s
8
9int onA, offA, onB, offB;
10enum State { READY, RUNNING, DONE };
11State state = READY;
12unsigned long startUs = 0;
13unsigned long startMs = 0;
14unsigned long doneMs = 0;
15
16int readAvg(int pin) {
17 long sum = 0;
18 for (int i = 0; i < 4; i++) sum += analogRead(pin);
19 return sum / 4;
20}
21
22bool isBroken(int pin, int on, int off) {
23 long pct = (long)(readAvg(pin) - off) * 100 / (on - off);
24 return pct < 40; // %40'ın altı = kesik / below 40% = broken
25}
26
27void calibrate() {
28 while (true) {
29 digitalWrite(LASER_PIN, LOW); // iki lazer de kapalı / both lasers off
30 delay(300);
31 offA = readAvg(START_LDR);
32 offB = readAvg(FINISH_LDR);
33 digitalWrite(LASER_PIN, HIGH); // iki lazer de açık / both lasers on
34 delay(300);
35 onA = readAvg(START_LDR);
36 onB = readAvg(FINISH_LDR);
37 if (abs(onA - offA) >= 100 && abs(onB - offB) >= 100) return;
38 Serial.println(F("Kapilardan biri hizali degil / One gate is misaligned"));
39 delay(2000);
40 }
41}
42
43void beep(int ms) {
44 digitalWrite(BUZZER_PIN, HIGH);
45 delay(ms);
46 digitalWrite(BUZZER_PIN, LOW);
47}
48
49void setup() {
50 Serial.begin(9600);
51 pinMode(LASER_PIN, OUTPUT);
52 pinMode(BUZZER_PIN, OUTPUT);
53 calibrate();
54 Serial.println(F("Hazir: baslangic kapisini gec / Ready: cross the start gate"));
55}
56
57void loop() {
58 if (state == READY && isBroken(START_LDR, onA, offA)) {
59 startUs = micros(); // zamanı hemen al / grab the time immediately
60 startMs = millis();
61 state = RUNNING;
62 beep(30);
63 } else if (state == RUNNING) {
64 if (isBroken(FINISH_LDR, onB, offB)) {
65 unsigned long elapsedUs = micros() - startUs;
66 float seconds = elapsedUs / 1000000.0;
67 float speed = DISTANCE_M / seconds; // v = d / t
68 Serial.print(F("Sure / Time: "));
69 Serial.print(seconds, 3);
70 Serial.print(F(" s Hiz / Speed: "));
71 Serial.print(speed, 2);
72 Serial.print(F(" m/s = "));
73 Serial.print(speed * 3.6, 1);
74 Serial.println(F(" km/h"));
75 beep(300);
76 state = DONE;
77 doneMs = millis();
78 } else if (millis() - startMs > TIMEOUT_MS) {
79 Serial.println(F("Zaman asimi / Timeout"));
80 state = READY;
81 }
82 } else if (state == DONE && millis() - doneMs > 3000 &&
83 !isBroken(START_LDR, onA, offA) && !isBroken(FINISH_LDR, onB, offB)) {
84 state = READY; // 3 sn sonra yeni tur / new run after 3 s
85 Serial.println(F("Hazir / Ready"));
86 }
87}Скетч работает как конечный автомат: в состоянии READY (готовность) он при прерывании стартового луча запоминает время через micros() и переходит в RUNNING (забег); при прерывании финишного луча выводит прошедшее время и скорость, а через 3 секунды готовится к следующему забегу. Если финишные ворота не пройдены за 60 секунд, забег отменяется. Честно о точности: сам код измеряет с разрешением лучше 1 мс, но LDR реагирует на темноту за 10–30 мс. К счастью, на обоих воротах стоит LDR одного типа, поэтому эта задержка возникает на обоих концах и в основном взаимно компенсируется; для бега и велосипеда результата более чем достаточно.
Уведомления на телефон с ESP32 (Telegram)
Чтобы сигнализация приносила пользу, когда дома никого нет, можно использовать ESP32: в момент прерывания луча она отправляет сообщение на телефон через Telegram. Подключение — как в таблице выше: лазер питается от 5V и коммутируется NPN-транзистором, подключённым к GPIO 26, а делитель с LDR питается от 3V3 и подключён к GPIO 34. Этот выбор не случаен: пины ADC2 у ESP32 не работают при включённом Wi-Fi, поэтому аналоговые датчики всегда подключай к пинам ADC1 (GPIO 32–39). АЦП ESP32 12-битный и возвращает значения от 0 до 4095, поэтому минимальный калибровочный разрыв в коде — 400. Как создать бота и получить токен и chat ID, мы подробно разобрали в руководстве Telegram-бот на ESP32; для скетча установи библиотеки UniversalTelegramBot и ArduinoJson через «Менеджер библиотек».
1// ESP32 lazer bariyeri + Telegram bildirimi / ESP32 laser tripwire + Telegram notification
2#include <WiFi.h>
3#include <WiFiClientSecure.h>
4#include <UniversalTelegramBot.h> // ArduinoJson kütüphanesi de gerekir / also needs the ArduinoJson library
5
6const char* WIFI_SSID = "WIFI_ADI"; // Wi-Fi ağının adı / your Wi-Fi name
7const char* WIFI_PASS = "WIFI_SIFRESI"; // Wi-Fi şifren / your Wi-Fi password
8#define BOT_TOKEN "1234567890:ABCdefGhIJKlmNoPQRstuVWXyz" // BotFather'dan / from BotFather
9#define CHAT_ID "123456789" // senin chat ID'n / your chat ID
10
11const int LASER_PIN = 26; // NPN transistör bazına 1 kΩ ile / to the NPN base via 1 kΩ
12const int LDR_PIN = 34; // ADC1 pini, Wi-Fi ile çalışır / ADC1 pin, works with Wi-Fi
13const int BUZZER_PIN = 25; // aktif buzzer / active buzzer
14const unsigned long CONFIRM_MS = 30; // en az bu kadar kesik kalmalı / must stay broken this long
15const unsigned long SIREN_MS = 5000; // siren süresi / siren duration
16const unsigned long COOLDOWN_MS = 30000; // iki deneme arası en az 30 sn / at least 30 s between attempts
17
18WiFiClientSecure client;
19UniversalTelegramBot bot(BOT_TOKEN, client);
20
21int onLevel = 0, offLevel = 0;
22bool beamOk = true;
23bool counted = false; // bu kesinti sayıldı mı / has this break been counted
24unsigned long brokenSince = 0;
25bool sirenOn = false;
26unsigned long sirenStart = 0;
27unsigned long breakCount = 0;
28bool pending = false; // gönderilecek bildirim var / a notification is waiting
29bool triedOnce = false;
30unsigned long lastTry = 0;
31
32int readAvg() {
33 long sum = 0;
34 for (int i = 0; i < 16; i++) sum += analogRead(LDR_PIN); // 16 ölçüm ortalaması / average of 16 readings
35 return sum / 16;
36}
37
38void calibrate() {
39 while (true) {
40 digitalWrite(LASER_PIN, LOW); // lazer kapalı = kesik ışın / laser off = broken beam
41 delay(300);
42 offLevel = readAvg();
43 digitalWrite(LASER_PIN, HIGH); // lazer açık = sağlam ışın / laser on = intact beam
44 delay(300);
45 onLevel = readAvg();
46 Serial.printf("Isinli / beam: %d Isinsiz / no beam: %d\n", onLevel, offLevel);
47 if (abs(onLevel - offLevel) >= 400) return; // 12 bit ölçekte yeterli fark / enough gap on the 12-bit scale
48 Serial.println("Isin LDR'ye ulasmiyor / Beam misses the LDR");
49 delay(2000);
50 }
51}
52
53int beamPercent() {
54 return (long)(readAvg() - offLevel) * 100 / (onLevel - offLevel);
55}
56
57void setup() {
58 Serial.begin(115200);
59 pinMode(LASER_PIN, OUTPUT);
60 pinMode(BUZZER_PIN, OUTPUT);
61 calibrate();
62
63 WiFi.mode(WIFI_STA);
64 WiFi.begin(WIFI_SSID, WIFI_PASS);
65 client.setCACert(TELEGRAM_CERTIFICATE_ROOT); // api.telegram.org kök sertifikası / root certificate
66 unsigned long start = millis();
67 while (WiFi.status() != WL_CONNECTED && millis() - start < 15000) { // en fazla 15 sn bekle / wait up to 15 s
68 delay(500);
69 Serial.print(".");
70 }
71 if (WiFi.status() == WL_CONNECTED) {
72 Serial.println(WiFi.localIP());
73 bot.sendMessage(CHAT_ID, "Lazer alarmi devrede / Laser alarm armed", "");
74 }
75}
76
77void loop() {
78 unsigned long now = millis();
79 int pct = beamPercent();
80
81 if (beamOk && pct < 40) { // ışın yeni kesildi / beam just broke
82 beamOk = false;
83 counted = false;
84 brokenSince = now;
85 } else if (!beamOk && pct > 60) {
86 beamOk = true; // ışın geri geldi / beam restored
87 }
88
89 if (!beamOk && !counted && now - brokenSince >= CONFIRM_MS) {
90 counted = true; // her kesinti bir kez sayılır / each break counts once
91 breakCount++;
92 pending = true;
93 sirenOn = true;
94 sirenStart = now;
95 Serial.printf("Kesinti / Break #%lu\n", breakCount);
96 }
97
98 if (sirenOn && now - sirenStart >= SIREN_MS) sirenOn = false;
99 digitalWrite(BUZZER_PIN, (sirenOn && (now / 200) % 2) ? HIGH : LOW); // kesik siren / beeping siren
100
101 if (pending && (!triedOnce || now - lastTry >= COOLDOWN_MS)) {
102 triedOnce = true;
103 lastTry = now;
104 if (WiFi.status() == WL_CONNECTED) {
105 String text = "Lazer bariyeri kesildi! / Laser beam broken! Toplam / Total: " + String(breakCount);
106 if (bot.sendMessage(CHAT_ID, text, "")) pending = false; // gitmezse 30 sn sonra yeniden / retry in 30 s if it fails
107 } else {
108 WiFi.reconnect(); // bağlantı koptuysa yeniden dene / reconnect if the link dropped
109 }
110 }
111}Каждое прерывание считается один раз, сирена звучит 5 секунд, а уведомление ставится в очередь. Отправка уведомлений пробуется не чаще раза в 30 секунд; новые прерывания за это время добавляются к общему числу и появляются в следующем сообщении. Так телефон не завалит сообщениями, пока кто-то ходит туда-сюда через луч. Если сообщение не отправилось, код пробует снова через 30 секунд, а если Wi-Fi отвалился, начинает переподключаться. sendMessage() занимает секунду-две, потому что открывает защищённое соединение HTTPS; в это время рисунок сирены замирает и луч не отслеживается — это нормально.
Лазерный барьер на Raspberry Pi Pico с MicroPython
На Raspberry Pi Pico ту же логику можно собрать в несколько строк MicroPython. Лазер питается от VBUS (USB 5 V, пин 40) и коммутируется NPN-транзистором, подключённым к GP15; делитель с LDR питается от пина 3V3(OUT) и подключён к GP26 (ADC0), а активный зуммер идёт на GP14. АЦП Pico через read_u16() возвращает значения от 0 до 65535, поэтому минимальный калибровочный разрыв — 6000. В коде тот же расчёт процентов, гистерезис и время подтверждения; он считает каждое прерывание, включает сирену на 5 секунд и выводит, сколько времени луч был перекрыт. Чтобы Pico работала без компьютера, сохрани файл на плату под именем main.py через Thonny.
1# Raspberry Pi Pico lazer bariyeri: alarm + geçiş sayacı / Laser barrier: alarm + pass counter
2from machine import Pin, ADC
3import time
4
5laser = Pin(15, Pin.OUT) # NPN transistör bazına 1 kΩ ile / to the NPN base via 1 kΩ
6ldr = ADC(26) # GP26 = ADC0, bölücü 3V3'ten / divider fed from 3V3
7buzzer = Pin(14, Pin.OUT) # aktif buzzer / active buzzer
8
9CONFIRM_MS = 30 # en az bu kadar kesik kalmalı / must stay broken this long
10SIREN_MS = 5000 # siren süresi / siren duration
11
12
13def read_avg(n=8):
14 total = 0
15 for _ in range(n):
16 total += ldr.read_u16()
17 return total // n
18
19
20def calibrate():
21 while True:
22 laser.value(0) # lazer kapalı = kesik ışın / laser off = broken beam
23 time.sleep_ms(300)
24 off = read_avg(32)
25 laser.value(1) # lazer açık = sağlam ışın / laser on = intact beam
26 time.sleep_ms(300)
27 on = read_avg(32)
28 print("Isinli / beam:", on, " Isinsiz / no beam:", off)
29 if abs(on - off) > 6000: # 16 bit ölçekte yeterli fark / enough gap on the 16-bit scale
30 return on, off
31 print("Isin LDR'ye ulasmiyor / Beam misses the LDR")
32 time.sleep(2)
33
34
35on_level, off_level = calibrate()
36beam_ok = True
37counted = False
38broken_at = time.ticks_ms()
39siren_until = time.ticks_ms()
40count = 0
41
42while True:
43 now = time.ticks_ms()
44 pct = (read_avg() - off_level) * 100 // (on_level - off_level) # 100 = ışın tam / full beam
45
46 if beam_ok and pct < 40: # ışın yeni kesildi / beam just broke
47 beam_ok = False
48 counted = False
49 broken_at = now
50 elif not beam_ok and pct > 60: # ışın geri geldi / beam restored
51 beam_ok = True
52 print("Kesik kaldigi sure / blocked for", time.ticks_diff(now, broken_at), "ms")
53
54 if not beam_ok and not counted and time.ticks_diff(now, broken_at) >= CONFIRM_MS:
55 counted = True # her kesinti bir kez sayılır / each break counts once
56 count += 1
57 siren_until = time.ticks_add(now, SIREN_MS)
58 print("Gecis / Pass:", count)
59
60 siren = time.ticks_diff(siren_until, now) > 0
61 buzzer.value(1 if siren and (now // 200) % 2 else 0) # kesik siren / beeping sirenИдеи проектов с лазерным модулем
- Лазерная домашняя сигнализация: объедини лучи у двери и в коридоре с PIR-датчиком движения и сведи все входы в один блок управления; подробности — в руководстве охранная сигнализация на Arduino.
- Лазерная арфа: поставь рядом 5–7 лучей и при прерывании каждого играй свою ноту на пассивном зуммере.
- Счётчик посетителей: раздели входы и выходы двумя лучами и показывай число людей внутри на дисплее.
- Измеритель скорости для научной выставки: двумя воротами измеряй скорость предметов, скатывающихся по наклонной плоскости, и сравнивай трение; больше идей — в руководстве идеи проектов для научных выставок.
- Игра «лазерный лабиринт»: построй зеркалами сеть лучей ниже уровня колена; засекай время игроков, которые пытаются пройти её, не задев, и начисляй штраф за каждое касание.
- Охрана ящика и сейфа: фиксируй прерывание луча при открывании крышки и отправляй уведомление на телефон с ESP32.
Частые ошибки и как их исправить
| Симптом | Вероятная причина | Решение |
|---|---|---|
| Лазер совсем не горит | Перепутаны S и −, или плата, у которой средний пин требует питания | Проверь маркировку пинов, подключи средний пин к 5V и попробуй ещё раз |
| Лазер очень тусклый | Модуль питается от 3,3 V, или пин не даёт достаточно тока | Питай лазер от 5V и коммутируй транзистором |
| Сообщение «Isin LDR'ye ulasmiyor» (луч не попадает на LDR) | Точка не на LDR, или внешний свет слишком сильный | Найди точку с помощью бумаги и помести LDR в чёрную трубку |
| Сигнализация звучит без остановки | Сбилась юстировка, шатается лазер или зеркало | Надёжно закрепи держатели и следи за показанием в плоттере |
| Сигнализация никогда не срабатывает | Даже при прерванном луче солнце или лампа держат LDR освещённым | Добавь трубку и отверни датчик от окна |
| Быстрые проходы не считаются | Время отклика LDR (10–30 мс) слишком велико | Уменьши CONFIRM_MS и MIN_BREAK_MS или используй фототранзистор |
| Arduino перезагружается или пин нагревается | Несколько лазеров питаются от одного пина | Питай лазеры от 5V и коммутируй транзистором |
| На ESP32 показание сбивается при запуске Wi-Fi | LDR подключён к пину ADC2 | Перенеси LDR на пин ADC1 в диапазоне GPIO 32–39 |
| Сообщения в Telegram не приходят | Неверный токен или chat ID, или боту ещё не писали | Отправь боту /start и ещё раз проверь токен и chat ID |
Лазерный модуль KY-008 открывает путь к одному из редких проектов, которые одной дешёвой деталью учат сразу оптике, делителям напряжения, калибровке и измерению времени. Начни с того, чтобы увидеть разрыв в тестовом скетче, затем собери сигнализацию и развивай проект зеркалами, счётчиком и таймером. В приложении BlueGrays есть пошаговые проекты «Лазерная сигнализация», «Лазерная охранная система», «Измерение освещённости с LDR», «Ночник на LDR» и «Сигнализация движения на ESP32 (PIR + зуммер + светодиод + Telegram)» со схемами и кодом.
Частые вопросы
Какое напряжение нужно лазерному модулю KY-008?
KY-008 рассчитан на 5 V, и благодаря токоограничивающему резистору модуля его можно подключать прямо к цифровому пину Arduino Uno. При 3,3 V он тоже светится, но тусклее. Никогда не подавай на модуль больше 5 V и никогда не питай лазерный диод без его резистора.
Может ли лазер на 5 mW повредить глаза?
Красный лазер на 5 mW относится к классу 3R: короткое случайное попадание в глаз обычно не оставляет стойких повреждений, но если смотреть в луч или поймать глазом отражение от зеркала, можно повредить сетчатку. Держи луч ниже уровня глаз, никогда не направляй его на людей или животных и в схемах с зеркалами проверяй весь путь луча.
На каком расстоянии работает лазерная сигнализация?
Расстояние ограничивают скорее юстировка и внешний свет, чем мощность лазера. С прочными держателями и LDR в чёрной трубке в помещении всё нормально работает на 10–20 м; на больших расстояниях точка растёт, и на LDR попадает меньше света. На улице солнце, туман и тряска от ветра заметно сокращают дальность.
Чем заменить LDR в лазерном барьере?
LDR дешёвый и простой, но его время отклика — 10–30 мс. Для таймеров и счётчиков, которым нужно более быстрое измерение, используют фототранзистор или фотодиод, которые реагируют за микросекунды. Есть и готовые модули лазерных приёмников: при попадании света они дают цифровой сигнал и подключаются прямо к цифровому пину.
Работает ли лазерный модуль с ESP32 и Raspberry Pi Pico?
Да, но вместо питания лазера прямо от пина лучше подавать на него 5V и коммутировать NPN-транзистором: пины Pico по умолчанию дают всего 4 mA. На этих платах питай делитель с LDR от 3V3, а на ESP32 используй пин ADC1 (GPIO 32–39), который работает вместе с Wi-Fi.
Инфракрасный барьер лучше лазерной сигнализации?
Профессиональные охранные системы используют невидимые инфракрасные лучи и приёмники модулированного сигнала: луч невозможно заметить, а модуляция делает его почти нечувствительным к солнечному свету. Красный лазер, напротив, виден, поэтому его очень легко выравнивать, и это делает его идеальным для учебных проектов, счётчиков и таймеров.
Что собрать после прочтения
Все проекты
Измерение освещённости с LDR
Хочешь увидеть яркость своей комнаты в виде числа? LDR — маленькая деталь, реагирующая на свет, — измеряет освещённость вокруг, а Arduino каждые полсекунды выводит значение на экран компьютера. Закрой

Ночник на LDR
Маленький ночник, который сам включается, когда темнеет, и гаснет, когда наступает утро. Датчик освещённости измеряет, насколько светло в комнате, и Arduino включает светодиод, когда достаточно темно,

Измерение освещённости модулем LDR на ESP32
Наблюдение в мониторе порта в реальном времени за сырым значением ADC модуля LDR и относительным процентом освещённости, вычисленным по опорным значениям темноты и света. Этот процент — не измерение в люксах. Учит логике калибровки модуля. Идеально выполнить его перед проектом 10, потому что он нужен для определения порога.

Лазерная сигнализация
Ты ставишь невидимую лучевую ограду, совсем как в кино. Лазер светит на датчик освещённости с другой стороны; как только кто-то проходит и прерывает луч, загорается светодиод и звучит зуммер, а когда

Лазерная охранная система
Совсем как сцены ограблений с лазерами в фильмах: лазерный луч светит на датчик освещённости, и как только кто-то входит и прерывает луч, загорается красный светодиод и начинает звучать зуммер. Сигнал

Ночник на LDR с ESP32
Ночник, в котором светодиод сам загорается в темноте и гаснет на свету. Используется пин D0 (цифровой выход) модуля датчика LDR — порог темноты задаётся маленьким потенциометром на модуле, а выход HIGH/LOW формирует сам модуль. ESP32 просто читает этот сигнал через digitalRead(), код очень простой.

Сигнализация движения на ESP32 (PIR + зуммер + светодиод + Telegram)
Умная охранная сигнализация, которая при обнаружении движения PIR-датчиком включает зуммер и светодиод и одновременно сразу отправляет владельцу уведомление в Telegram. В ней есть 30-секундная пауза, ограничивающая частоту повторных тревог; этот механизм не проверяет срабатывания PIR и не устраняет ложные тревоги. Подходит для дома, гаража, склада или мастерской. С ESP32-CAM можно добавить уведомления с фото, а с обычной ESP32 DevKit V1 приходят текстовые.
Другие статьи
Приложение BlueGrays
Собери эту схему шаг за шагом в телефоне
Схема подключения, код и список деталей на одном экране. 200+ проектов на Arduino, ESP32 и Pico готовы к сборке.
Наведи камеру телефона
Наведи камеру на код — откроется магазин приложений на телефоне.
или открыть магазин

