Привіт! Сьогодні складатимемо простий прототип детектора перешкод. "Мозком" системи буде плата Arduino Nano, до АЦП входу якого буде підключено датчик CNY70. В якості індикатора я налаштую RGB-світлодіод через ШІМ так, щоб плавно змінювати колір світіння, в залежності від даних, прийнятих з датчика.
Для початку зберемо схему індикації. Для цього візьмемо RGB-світлодіод, такий світлодіод має в одному корпусі 3 діоди різних кольорів: червоний (R), зелений (G), синій (B). Вони мають 4 виводи - один з них загальний катод (самий довгий), інші - аноди кожного окремого діода.

Тут будьте уважні - тут використані світлодіод з загальним катодом, але також існують з загальним анодом. Вони працюють однаково, але полярність обернена.
Отже, встановлюю світлодіод на плату таким чином щоб катод був на "-" шині макетної плати, а аноди окремо один від одного. Одразу підключаю "-" шину до виводу GND Arduino-плати:
Від кожного аноду вивожу по резистору: від зеленого та синього 100 Ом, від червоного 220 Ом (крайній зі сторони плати):

У моєму випадку, напруга червоного діода менша за напруги інших, тому резистор з більшим опором, але пристрої можуть відрізнятись - дивіться документацію конкретного приладу.
Тепер нам потрібно всі три виводи діодів підключити до плати, але не до звичайних цифрових виводів, а до тих що підтримують ШІМ, щоб ми могли плавно змінювати навантаження на кожен колір. Нижче представлено скріншот розпіновки плати Arduino Nano з офіційного сайту документації:

Нас цікавлять виводи D3, D5, D6. Позначка "~" перед назвою піна означає що ці виводи підтримують ШІМ, а отже підійдуть для наших цілей. Під'єднаємо аноди світлодіодів до них:

Наша схема індикації зібрана, і тепер напишемо код для тестування її роботи. Запускаємо Visual Studio Code, переходимо у вкладку PlatformIO і створюємо новий проєкт з такими налаштуваннями:

Відкриємо файл main.cpp і замінимо код на такий:
#include <Arduino.h>void setup() {
//встановлюємо відповідні піни в режим виводуpinMode(PD3, OUTPUT);pinMode(PD5, OUTPUT);pinMode(PD6, OUTPUT);
//встановлюємо значення потужності 50 для червоного (максимальне значення - 255)
analogWrite(PD3, 50);
//очікуємо 2 секунди
delay(2000);
//встановлюємо значення потужності 50 для зеленого
analogWrite(PD5, 50);
//затримка 2 секунди
delay(2000);
//50 для синього
analogWrite(PD6, 50);
//2 секунди
delay(2000);}void loop() {
}
Як видно, спочатку код в методі setup() встановлює режим виводу для пінів D3, D5, D6, потім почергово подає живлення на світлодіоди. Значення 50 обрано навмання, для нашого тесту можна використати будь яке значення до 255 включно. Головне щоб не нуль, бо колір не засвітиться.
Після компіляції коду (кнопка Build) і завантаження на плату маємо такий результат:
Чудово! Наша індикація працює, але в нас ще є робота. Тепер треба підключити датчик перешкод, який при наближенні до поверхні що відбиває світло передає інформацію про відстань до цього об'єкта на мікроконтролер. Для цього підійде CNY70.
Його принцип роботи досить простий: випромінювач випускає невидиме інфрачервоне світло. Якщо на поверхні є об'єкт, світло відбивається, потрапляє на фототранзистор, і той пропускає струм. Якщо поверхня темна або відсутня, відбитого світла менше, і фототранзистор генерує слабший сигнал.

Розпіновка приладу і схема підключення представлена на малюнку нижче:

На корпусі датчику не вказані виводи, тому треба уважно продивитись де який вивід перед підключенням. Орієнтуйтесь по стороні з написом. Як видно зі схеми, інфрачервоний діод підключається як звичайний, через резистор 220 Ом, напруга живлення - 5 Вольт. Світлотранзистор також живиться від 5В, через резистор 10 кОм, між його колектором і резистором вихід сигналу, який підключається до аналогового входу плати. На Arduino Nano аналогові входи називаються на "А": A0, A1, A2 і т.д. (див. розпіновку на малюнку плати вище).
Отже, нам потрібні 5 вольт для живлення датчику, виведемо їх на шину "+" макету від піну 5V плати мікроконтролера:

Тепер до пінів датчика приєднаємо дроти, щоб можна було підключити до схеми, при цьому залишити можливість рухати датчиком. Я підключив червоний і чорний дроти до пінів діода, відповідно червоний - до аноду, чорний - до катоду. Коричневий (емітер) і білий (колектор) з'єднані з виводами транзистора:

Підключаємо до плати: емітер і анод до "+" шини, а катод і колектор на окремі шинки для підключення резисторів:

Тепер, згідно схеми, через відповідні резистори під'єднаємо катод і колектор до шини "-" (зліва 220 Ом, справа - 10к):
Тепер потрібно під'єднати вихід фототранзистора з ADC входом плати мікроконтролера. Згідно схеми виводимо дріт між резистором 10к і білим проводом (колектор транзистора), і підводимо його до найближчого (або найзручнішого) аналоговому піну плати (в моєму випадку це A6):

На цьому збір схеми завершений, тож можна переходити до нашого коду. Я використаю лише два кольори діода-індикатора так, що коли датчик не бачить перешкоди горить зелений, і по мірі наближення до перешкоди зелений затухає, червоний починає світитись. Тож, замінимо код файла main.cpp на такий:
#include <Arduino.h>
#define RED_LED PD3
#define GREEN_LED PD5
#define DETECTOR A6//об'являємо допоміжну функціюint getLEDLightnessFrom(int sensorValue);void setup() {//встановимо піни PD3 (червоний), PD5(зелений) в режим виводуpinMode(RED_LED, OUTPUT);pinMode(GREEN_LED, OUTPUT);//пін А6 - в режим вводуpinMode(DETECTOR, INPUT);//встановимо початкові значення для кольорівanalogWrite(GREEN_LED, 0);analogWrite(RED_LED, 255);}void loop() {//зчитуємо значення що передає датчик з піну А6int sensorVal = analogRead(DETECTOR);//записуємо нові значення у піни світлодіодів//для цього використовуємо допоміжну функціюint redLightness = getLEDLightnessFrom(sensorVal);int greenLightness = getLEDLightnessFrom(25 - sensorVal);analogWrite(GREEN_LED, redLightness);analogWrite(RED_LED, greenLightness);}//допоміжна функція для розрахунку рівня//світіння світлодіода в залежності//від значення, отриманого з датчикаint getLEDLightnessFrom(int sensorValue) {if(sensorValue < 2)return 0;return sensorValue * 10;}
Сенсор CNY70 видає значення від 0 до 25 (я попередньо дізнався ці значення експериментальним шляхом, через UART-консоль). Рівень яскравості встановлюється функцією analogWrite(pin, value), яка очікує що аргумент value матиме значення від 0 до 255. Допоміжна функція getLEDLightnessFrom(sensorValue) повертає 0 якщо значення сенсору менше 2, інакше повертає значення сенсору помножений на 10. Таким чином якщо сенсор видає 25, то функція видасть яскравість 250 (майже максимум), і 0 якщо сенсор ледь уловлює перешкоди. Зверніть увагу що для зеленого значення обернене (25 - sensorVal) так, що чим більше видає сенсор, тим меншу значення передається допоміжній функції. Як результат - чим ближче до сенсора перешкода, тим яскравіше світиться червоний світлодіод, і навпаки - чим далі перешкода, тим яскравіше зелений:
Зверніть увагу!
Я поставив білий зарядний пристрій, тому що білий колір краще відбиває світло. Якщо "детектувати" на темні поверхні, то значення на виході датчика буде меншим.
Згладжування значень детектора
На відео видно, що коли я наближую датчик до предмету, то індикатор змінює колір не рівномірно, а стрибками. Це тому що сам сенсор видає рівень нестабільно. Це звичайна справа для сенсорів, але я хотів трошки покращити результат, тож напишемо простий фільтр для стабілізації індикатора:
#include <Arduino.h>#define RED_LED PD3#define GREEN_LED PD5#define DETECTOR A6int getLEDLightnessFrom(int sensorValue);//об'являємо функцію сгладжуванняint smooth(int rawValue);void setup() {pinMode(RED_LED, OUTPUT);pinMode(GREEN_LED, OUTPUT);pinMode(DETECTOR, INPUT);analogWrite(GREEN_LED, 0);analogWrite(RED_LED, 255);}void loop() {int sensorVal = analogRead(DETECTOR);//згладжуємо вхідний сигналsensorVal = smooth(sensorVal);int redLightness = getLEDLightnessFrom(sensorVal);int greenLightness = getLEDLightnessFrom(25 - sensorVal);analogWrite(GREEN_LED, redLightness);analogWrite(RED_LED, greenLightness);}int getLEDLightnessFrom(int sensorValue) {if(sensorValue < 2)return 0;return sensorValue * 10;}bool initialized = false;int lastVal = 0;
double koef = 0.5;int smooth(int rawValue) {if(!initialized) {initialized = true;lastVal = rawValue;} else {lastVal = lastVal + koef * (rawValue - lastVal);}return lastVal;}
Тут додано функцію smooth(rawValue), яка бере значення, і просто знижує різницю між попереднім і даним в два рази. Змінюючи коефіцієнт згладжування koef можна налаштовувати реакцію на зміни. Ось такий результат отримуємо:
