Возможное применение, пример:
Заход в комнату/выход из комнаты. Допустим, есть ванная комната. В начальном состоянии там освещение выключено. Заходит один человек – освещение включается. Устройство считает 0+1=1. Он начинает принимать душ в душевой кабине. Заходит другой человек 1+1=2, моет руки. Заходит третий, допустим, побриться 2+1=3. Тот, кто помыл руки выходит 3-1=2. Потом, тот кто брился 2-1=1. И, наконец, первый завершает принимать душ, выходит 1-1=0, освещение выключается.
Говорю сразу – датчик движения вместо этого устройства не прокатит. Нет, если кому-то нравится махать руками время от времени – дело хозяйское, я через это уже прошёл.
Описание:
Генератор прямоугольных импульсов с выходом на IR-светодиод устанавливается на одной половине дверного проёма. Генератор собран на NE555 и имеет форму сигнала - меандр, и частоту примерно 20 кГц.
https://cloud.mail.ru/public/DyZ6/nJsxoqqdb
На другой половине дверного проёма устанавливаются два IR-светоприёмника, с некоторым разнесением по горизонтали (у меня примерно 2,5 см). Каждый светоприёмник имеет свой отдельный канал. На выходе фотодиода сигнал приблизительно 5-20 мВ, в зависимости от дальности, и, примерно, треугольная форма. Далее идёт каскад усиления на двух транзисторах VT1, VT2 (VT3, VT4), которые усиливают сигнал и доводят его до логических уровней. Каждый канал собран на отдельной плате. На схеме пунктирными линиями отображены отдельные платы.
https://cloud.mail.ru/public/zXoN/JaaE5YAuy
Затем идут ждущие мультивибраторы с перезапуском на микросхемах 176ЛA7 (2И-НЕ) и CD4013 (D-триггер), которые превращают факт наличия или отсутствия сигнала в 0 или 1. Настройка мультивибраторов сводится к подбору R9, C5 (R20, C11), чтобы их постоянная времени была раза в 3-4 больше периода сигнала. Светодиоды HL1, HL2 установлены в отладочных целях и информируют о том, когда сигнал на каком канале есть, когда нет. Мультивибраторы собраны на одной плате, так как логические микросхемы общие у обоих каналов. И, наконец, эти значения поступают на входы D2, D3 Arduino.
Arduino управляет нагрузкой 220 вольт с помощью типовой схемы с оптроном MOS3063M и симистором BT12-600BW. R10 подобран так, чтобы входной ток MOS3063M был номинальным, примерно 5 мА. Нагрузка у меня заведомо не индуктивная, поэтому снабберную цепь я не ставил. На этой же плате расположена сама Arduino, и миниатюрный блок питания Hi-link на 5 Вольт для питания низковольтной части.
Все пять плат на фото:
https://cloud.mail.ru/public/6MnN/2C5vYT4Sn
Описание алгоритма скетча (в сильно упрощенном виде).
Скетч анализирует эти входы, устанавливает, на каком из них сигнал изменился раньше, и делает вывод о направлении пересечения барьера. При наличии пересечения, он увеличивает или уменьшает внутренний счетчик, который отображается на светодиодах HL3, HL4 (выходы D6, D8) в двоичном коде (при желании, можно сделать цифровой индикатор). Состояния счетчика – в начальном положении счётчик равен 0. Если пересечение барьера происходит в «обратном» направлении, то счётчик не меняется и ничего не происходит. Если в «прямом», то счётчик увеличивается на 1, и схема включает нагрузку, подав 1 на выход Arduino D4, и, в конечном итоге, включив симистор Т1. Далее счётчик может увеличиваться или уменьшаться. При достижении максимального числа 3, схема далее ничего не делает. При достижении числа 0, нагрузка выключается, и схема переходит в исходное состояние. После любого события пересечения, устанавливается пауза в 2 секунды, когда скетч ничего не делает, эту паузу можно изменить.
Текст скетча
Код: Выделить всё
//
// Shirokov V.V. aka Massaraksh7, 06.06.2026
//
#define IN1 2
#define IN2 3
#define FIRE 4
#define B0 6
#define B1 8
#define R_WAIT 0
#define R_DIR 1
#define R_BACK 2
#define R_DO_DIR 3
#define R_DO_BACK 4
#define WAIT_MS 2000 //---Задержка, мс, после события
#define WAIT_MKS 300 //---Задержка, мкс, между опросами, 3 опроса
#define MAX_VIS 3 //---Максимальное число посетителей
int count,rate;
int m1[3],m2[3];
//---Прочитать состояние входов и записать в буфер
void ReadAndShift() {
int n,mt1,mt2;
n=digitalRead(IN1);if (n==LOW) mt1=0; else mt1=1;
n=digitalRead(IN2);if (n==LOW) mt2=0; else mt2=1;
m1[2]=m1[1];m1[1]=m1[0];m1[0]=mt1;
m2[2]=m2[1];m2[1]=m2[0];m2[0]=mt2;
}
//---Если для индикации числа посетителей используется 7-сегментный индикатор,
//---то эту подпрограмму надо переписать
void ShowCount() {
if (count==0){digitalWrite(B0,HIGH);digitalWrite(B1,HIGH);}
if (count==1){digitalWrite(B0,HIGH);digitalWrite(B1, LOW);}
if (count==2){digitalWrite(B0, LOW);digitalWrite(B1,HIGH);}
if (count==3){digitalWrite(B0, LOW);digitalWrite(B1, LOW);}
}
//---Очистка буфера
void clearBuf() {
int i;
for(i=0;i<3;i++){m1[i]=0;m2[i]=0;}
}
//---Подключить нагрузку
void fireON() {
digitalWrite(FIRE,HIGH);
}
//---Отключить нагрузку
void fireOFF() {
digitalWrite(FIRE,LOW);
}
//---Инициализация
void setup() {
pinMode(IN1,INPUT);pinMode(IN2,INPUT);
pinMode(FIRE,OUTPUT);pinMode(B0,OUTPUT);pinMode(B1,OUTPUT);
digitalWrite(FIRE,LOW);digitalWrite(B0,HIGH);digitalWrite(B1,HIGH);
delay(WAIT_MS);
count=0;rate=R_WAIT;
clearBuf();
}
//---Главный цикл
void loop() {
int i;
if (rate==R_WAIT) //---Режим ожидания основной
{
ReadAndShift();
if (m1[0]==0 && m1[1]==0 && m1[2]==0 && m2[0]==1 && m2[1]==1 && m2[2]==1){rate=R_BACK;exit;}
if (m1[0]==1 && m1[1]==1 && m1[2]==1 && m2[0]==0 && m2[1]==0 && m2[2]==0){rate=R_DIR;exit;}
delayMicroseconds(WAIT_MKS);exit;
}
if (rate==R_DIR) //---Режим возможного пересечения вперёд
{
ReadAndShift();
if (m1[0]==0 && m1[1]==0 && m1[2]==0 && m2[0]==0 && m2[1]==0 && m2[2]==0){rate=R_WAIT;exit;}
if (m1[0]==1 && m1[1]==1 && m1[2]==1 && m2[0]==1 && m2[1]==1 && m2[2]==1){rate=R_DO_DIR;exit;}
delayMicroseconds(WAIT_MKS);exit;
}
if (rate==R_BACK) //---Режим возможного пересечения назад
{
ReadAndShift();
if (m1[0]==0 && m1[1]==0 && m1[2]==0 && m2[0]==0 && m2[1]==0 && m2[2]==0){rate=R_WAIT;exit;}
if (m1[0]==1 && m1[1]==1 && m1[2]==1 && m2[0]==1 && m2[1]==1 && m2[2]==1){rate=R_DO_BACK;exit;}
delayMicroseconds(WAIT_MKS);exit;
}
if (rate==R_DO_DIR) //---Точно пересечение вперёд
{
if (count<MAX_VIS) count++;
ShowCount();
if (count==1) fireON();
delay(WAIT_MS);clearBuf();
rate=R_WAIT;
exit;
}
if (rate==R_DO_BACK) //---Точно пересечение назад
{
if (count>0) count--;
ShowCount();
if (count==0) fireOFF();
delay(WAIT_MS);clearBuf();
rate=R_WAIT;
exit;
}
}Видео полного теста устройства.


