Общие сведения:
В этом уроке мы создадим ночник с управлением адресными светодиодами через датчик жестов.
Выполнив один из жестов, которые распознаёт датчик, вы сможете управлять режимом работы ночника, его яркостью, цветом и скоростью анимации.
Видео:
Нам понадобится:
- 1х Arduino / Piranha UNO;
- 1х Battery Shield;
- 1х Trema Shield;
- 8х Trema-модуль Адресный светодиод NeoPixel;
- 1х Trema-модуль Датчик жестов, приближения, освещённости, цвета;
- 6х ПВХ-конструктор;
Для реализации проекта нам необходимо установить библиотеки:
- SparkFun_APDS9960 — для работы с датчиком движений;
- iarduino_NeoPixel — для работы с адресными светодиодами NeoPixel;
- Wire — библиотека входит в базовый набор Arduino IDE и не требует установки;
О том, как устанавливать библиотеки, Вы можете ознакомиться на странице Wiki - Установка библиотек в Arduino IDE.
Описание работы ночника:
После подачи питания ночник готов к работе.
Для включения ночника проведите рукой над датчиком вправо или влево, после чего ночник включит один из режимов работы:
- Перелив всеми цветами радуги;
- Случайное включение случайным цветом;
- Смена одного цвета на другой;
- Имитация пламени свечи;
- Один цвет на выбор по порядку(красный, зелёный, синий, жёлтый, фиолетовый, голубой, белый);
Для того, чтобы увеличить или уменьшить скорость анимации, достаточно провести рукой вверх (ускорить) или вниз (замедлить) над датчиком.
Для того, чтобы изменить уровень яркости ночника достаточно приблизить руку к датчику и плавно её отдалить - ночник перейдёт в режим настройки:
- При входе в режим настройки яркости ночник однократно моргнёт светодиодами белого цвета, после чего в течении времени, заданном в переменной
waiting_time, будет принимать команды настройки яркости; - Приближая или отдаляя руку к/от датчика яркость будет плавно меняться от меньшего (приближая) к большему (отдаляя);
- По истечении времени ночник вновь однократно моргнёт светодиодами белого цвета и выйдет из режима настройки яркости;
Для того, чтобы выключить ночник, достаточно поднести руку над датчиком на расстоянии 10-15 см и плавно её приблизить. После этого ночник выключится.
Схема сборки:
Arduino / Piranha UNO:

Batery Shield:
Установите Battery Shield на Arduino / Piranha UNO:
Во время установки Battery Shield должен быть в выключенном состоянии.

Trema Shield:
На Battery Shield установите Trema Shield:

Датчик жестов:
Подключите Trema-модуль Датчик жестов к Trema Shield:

Адресные светодиоды:
Подключите адресные светодиоды NeoPixel к Trema Shield:

Все используемые в уроке Trema-модули NeoPixel соединены друг c другом, а первый модуль можно подключить к любому выводу Arduino. Номер вывода указывается в скетче (в примере используется вывод D10). Чем больше модулей в цепи, тем больше тока она потребляет, по этому в схеме используется стабилизированный источник питания Battery Shield на 5В постоянного тока.
Код программы (скетч):
// Подключаем библиотеки:
#include <Wire.h> // Для работы с шиной I2C
#include <SparkFun_APDS9960.h> // Для работы с датчиком APDS-9960
#include <iarduino_NeoPixel.h> // Подключаем библиотеку iarduino_NeoPixel для работы со светодиодами NeoPixel
uint8_t neo_pin = 10; // Вывод, к которому подключены модули NeoPixel
uint16_t modul_number = 8; // Количество модулей
iarduino_NeoPixel led(neo_pin, modul_number*4 ); // Объявляем объект LED указывая (№ вывода Arduino к которому подключён модуль NeoPixel, количество используемых светодиодов)
SparkFun_APDS9960 apds = SparkFun_APDS9960(); // Определяем объект apds, экземпляр класса SparkFun_APDS9960
// Объявляем переменные:
uint8_t j; // Объявляем переменную для хранения значения сдвига спектра цветов для всех светодиодов (от 0 до 255)
uint8_t k; // Объявляем переменную для хранения положения сдвига спектра цвета для каждого светодиода на спектре j (зависит от количества светодиодов)
uint8_t r, g, b; // Объявляем переменную для хранения цветов RGB для каждого светодиода
uint8_t z = 1; // Определяем константу указывающую задержку в мс (чем выше значение, тем медленнее перелив цветов)
uint8_t flg, setting = 0; // Флаг включения/выключения света
uint8_t mode = 0; // Флаг режима работы светодиодов
uint8_t proximityData = 0; // Переменная, хранящая значение расстояния до модуля APDS9960
uint8_t brightness = 255; // Переменная значения яркости
uint32_t waiting_time = 7000; // Время ожидания при настройке яркости
uint32_t work_time; // Счётчик времени при настройке яркости
byte on_off = 0; // Триггер включения/выключения света
void setup() {
// Инициализируем адресные светодиоды:
led.begin();
// Инициализируем модуль жестов:
apds.init(); // Если инициализация модуля жестов прошла успешно, то ...
// Устанавливаем коэффициент усиления приёмника: // Доступные значения: 1х, 2х, 4х, 8х (GGAIN_1X, GGAIN_2X, GGAIN_4X, GGAIN_8X). Чем выше коэффициент тем выше чувствительность
apds.setGestureGain(GGAIN_4X); // Если установлен коэффициент усиления приёмника в режиме обнаружения жестов, то ...
// Устанавливаем силу тока драйвера ИК-светодиода: // Доступные значения: 100мА, 50мА, 25мА, 12.5мА (LED_DRIVE_100MA, LED_DRIVE_50MA, LED_DRIVE_25MA, LED_DRIVE_12_5MA). Чем выше сила тока, тем выше чувствительность.
apds.setGestureLEDDrive(LED_DRIVE_100MA); // Если устанавлена сила тока драйвера (яркость) ИК-светодиода для обнаружения жестов, то ...
// Разрешаем режим обнаружение жестов:
apds.enableGestureSensor(false); // Если инициализация режима обнаружения жестов прошла успешно, то
led.setColor(NeoPixelAll,0, 0, 0); // Выключаем светодиоды
led.write(); //
// Ждём завершение инициализации и калибровки:
delay(500); // Задержка 0,5с
}
void loop() {
// Проверяем наличие движения
if (apds.isGestureAvailable()) { // Если зафиксировано движение, то ...
switch (apds.readGesture()) { // Сверяем значение соответствующее жесту ...
// Проверяем жест:
//==========================================================//
// "СЕВЕР" - УВЕЛИЧЕНИЕ СКОРОСТИ АНИМАЦИИ
case DIR_UP:
if(z>=1 && z <=11) {z += 5;} else // Если параметр скорости задан от 1мс до 11мс, то шаг равен 5мс
if(z > 11 && z <=101) {z += 20;}else // Если параметр скорости задан от 11мс до 101мс, то шаг равен 20мс
if(z > 101 && z <=500){z += 50;}else // Если параметр скорости задан от 101мс до 500мс, то шаг равен 50мс
if(z > 500) {z = 1;} // При превышении значения шаг сбрасывается
break;
//==========================================================//
// "ЮГ" - УМЕНЬШЕНИЕ СКОРОСТИ АНИМАЦИИ
case DIR_DOWN:
if(z<1) {z = 500;}else // При выходе из заданного промежутка значений шаг сбрасывается
if(z >=1 && z <=11) {z -= 5;} else // Если параметр скорости задан от 1мс до 11мс, то шаг равен 5мс
if(z > 11 && z <= 101){z -= 20;}else // Если параметр скорости задан от 1мс до 11мс, то шаг равен 5мс
if(z > 101 && z <=500){z -= 50;} // Если параметр скорости задан от 1мс до 11мс, то шаг равен 5мс
break;
//==========================================================//
// "ЗАПАД" - ЛИСТАЕМ РЕЖИМЫ ВНИЗ
case DIR_LEFT:
mode--; // Уменьшаем номер режима на единицу
if(mode < 1){mode = 11;} // Если значение меньше допустимого, то переходим к максимально большому значению
break;
//==========================================================//
// "ВОСТОК" - ЛИСТАЕМ РЕЖИМЫ ВВЕРХ
case DIR_RIGHT:
mode++; // Увеличиваем номер режима на единицу
if(mode > 11){mode = 1;} // Если значение больше допустимого, то переходим к минимальному значению
break;
//==========================================================//
// "ОТДАЛЕНИЕ" - ПЕРЕХОД В РЕЖИМ ЯРКОСТИ
case DIR_FAR:
setting = 1; // Ставим флаг перехода в режим настройки яркости
flg = 1; // Ставим флаг смены работы режима датчика жестов - с чтения жестов на чтение приближения
work_time = millis(); // Обновляем счётчик
break;
//==========================================================//
// "ПРИБЛИЖЕНИЕ" - ПЕРЕХОД В РЕЖИМ ВЫКЛЮЧЕНИЯ СВЕТА
case DIR_NEAR:
mode = 12; // Устанавливаем режим выключения света
break;
}
}
// РЕЖИМЫ РАБОТЫ СВЕТОДИОДОВ:
switch (mode) { // Сверяем значение соответствующее режиму работы
//==========================================================//
// ПЕРЕЛИВ ВСЕХ ЦВЕТОВ РАДУГИ
case 1:
j++; // Смещаем спектр цветов для всех светодиодов
for(uint16_t i=0; i<led.count(); i++){ // Проходим по всем светодиодам
k=((uint16_t)(i*256/led.count())+j); // Определяем положение очередного светодиода на смещённом спектре цветов
if(k<85) { b=0; r=k*3; g=255-r;}else // Перелив от зелёного к красному, через жёлтый
if(k<170){k-=85; g=0; b=k*3; r=255-b;}else // Перелив от красного к синему , через фиолетовый
{k-=170; r=0; g=k*3; b=255-g;} // Перелив от синего к зелёному, через голубой
led.setColor(i, // Устанавливаем выбранный цвет для очередного светодиода
map(r, 0, 255, 0, brightness), // Задаём зависимость яркости красного от параметра brightness, который определяется в настройках яркости
map(g, 0, 255, 0, brightness), // Задаём зависимость яркости зелёного от параметра brightness, который определяется в настройках яркости
map(b, 0, 255, 0, brightness)); // Задаём зависимость яркости синего от параметра brightness, который определяется в настройках яркости
}
led.write(); // Записываем цвета в светодиоды
delay(z); // Устанавливаем задержку
break;
//==========================================================//
// ВКЛЮЧЕНИЕ СЛУЧАЙНЫМ ЦВЕТОМ
case 2:
for(uint16_t t=0; t<led.count(); t++){ // Проходим по всем светодиодам
int8_t i=random(3); // Определяем какой из трех составляющих цветов будет отсутствовать R, G, или B
if(i==0){r=0;}else{r=random(0xFF);} // Определяем случайное значение для переменной r
if(i==1){g=0;}else{g=random(0xFF);} // Определяем случайное значение для переменной g
if(i==2){b=0;}else{b=random(0xFF);} // Определяем случайное значение для переменной b
led.setColor(t, // Устанавливаем цвет r,g,b для случайного светодиода
map(r, 0, 255, 0, brightness), // Задаём зависимость яркости красного от параметра brightness, который определяется в настройках яркости
map(g, 0, 255, 0, brightness), // Задаём зависимость яркости зелёного от параметра brightness, который определяется в настройках яркости
map(b, 0, 255, 0, brightness)); // Задаём зависимость яркости синего от параметра brightness, который определяется в настройках яркости
}
led.write(); // Записываем цвета в светодиоды
delay(z); // Устанавливаем задержку
break;
//==========================================================//
// ВСЕ СВЕТОДИОДЫ ЗАЖИГАЮТСЯ ОДНИМ ЦВЕТОМ
case 3:
uint16_t f; // Определяем переменную как коэффициент яркости
if(f==0 ){f=42; }else // f=60° (0 ... 42 ... 255 = 0° ... 60° ... 360°)
if(f==42 ){f=85; }else // f=120° (0 ... 85 ... 255 = 0° ... 120° ... 360°)
if(f==85 ){f=127;}else // f=180° (0 ... 127 ... 255 = 0° ... 180° ... 360°)
if(f==127){f=170;}else // f=240° (0 ... 170 ... 255 = 0° ... 240° ... 360°)
if(f==170){f=212;}else // f=300° (0 ... 212 ... 255 = 0° ... 300° ... 360°)
if(f==212){f=0; } // f=360° (0 ... 255 ... 255 = 0° ... 360° ... 360°)
if(f<85) { b=0; r=f*3; g=255-r;}else // Перелив от зелёного к красному, через жёлтый
if(f<170){f-=85; g=0; b=f*3; r=255-b;}else // Перелив от красного к синему , через фиолетовый
{f-=170; r=0; g=f*3; b=255-g;} // Перелив от синего к зелёному, через голубой
led.setColor(NeoPixelAll, // Устанавливаем выбранный цвет для очередного светодиода
map(r, 0, 255, 0, brightness), // Задаём зависимость яркости красного от параметра brightness, который определяется в настройках яркости
map(g, 0, 255, 0, brightness), // Задаём зависимость яркости зелёного от параметра brightness, который определяется в настройках яркости
map(b, 0, 255, 0, brightness)); // Задаём зависимость яркости синего от параметра brightness, который определяется в настройках яркости
led.write(); // Записываем цвета в светодиоды
delay(z*10); // Устанавливаем задержку
break;
//==========================================================//
// ГОРИТ СВЕЧА
case 4:
for(uint16_t m=0; m<led.count(); m++){ // Проходим по всем светодиодам
if(m%2==0){ // Каждый второй светодиод настраиваем гореть красным
r = random(10,50); // Определяем случайное значение яркости красного
led.setColor(m, // Устанавливаем выбранный цвет для очередного светодиода
map(r, 0, 255, 0, brightness), // Задаём зависимость яркости красного от параметра brightness, который определяется в настройках яркости
0, // Устанавливаем зелёный равным 0
0); // Устанавливаем синий равным 0
}else { // Оставшиеся светодиоды настраиваем гореть жёлтым
r = random(50,256); g = r; // Определяем случайное значение яркости жёлтого
led.setColor(m, // Устанавливаем выбранный цвет для очередного светодиода
map(r, 0, 255, 0, brightness), // Задаём зависимость яркости красного от параметра brightness, который определяется в настройках яркости
map(g, 0, 255, 0, brightness), // Задаём зависимость яркости зелёного от параметра brightness, который определяется в настройках яркости
0); // Устанавливаем синий равным 0
}
}
led.write(); // Записываем цвета в светодиоды
delay(random(10,100)); // Устанавливаем случаное значение времени задержки для имитации горения
break;
//==========================================================//
// ГОРИТ КРАСНЫМ
case 5:
r = 255; g = 0; b = 0; // Выставляем значение RGB-спектра в красный цвет
led.setColor(NeoPixelAll, // Устанавливаем выбранный цвет для всех светодиодов
map(r, 0, 255, 0, brightness), // Задаём зависимость яркости красного от параметра brightness, который определяется в настройках яркости
g, // Устанавливаем зелёный равным 0
b); // Устанавливаем синий равным 0
led.write(); // Записываем цвета в светодиоды
break;
//==========================================================//
// ГОРИТ ЗЕЛЁНЫМ
case 6:
r = 0; g = 255; b = 0; // Выставляем значение RGB-спектра в зелёный цвет
led.setColor(NeoPixelAll, // Устанавливаем выбранный цвет для всех светодиодов
r, // Устанавливаем красный равным 0
map(g, 0, 255, 0, brightness), // Задаём зависимость яркости зелёного от параметра brightness, который определяется в настройках яркости
b); // Устанавливаем синий равным 0
led.write(); // Записываем цвета в светодиоды
break;
//==========================================================//
// ГОРИТ СИНИМ
case 7:
r = 0; g = 0; b = 255; // Выставляем значение RGB-спектра в синий цвет
led.setColor(NeoPixelAll, // Устанавливаем выбранный цвет для всех светодиодов
r, // Устанавливаем красный равным 0
g, // Устанавливаем зелёный равным 0
map(b, 0, 255, 0, brightness)); // Задаём зависимость яркости синего от параметра brightness, который определяется в настройках яркости
led.write(); // Записываем цвета в светодиоды
break;
//==========================================================//
// ГОРИТ ЖЁЛТЫМ
case 8:
r = 255; g = 255; b = 0; // Выставляем значение RGB-спектра в жёлтый цвет
led.setColor(NeoPixelAll, // Устанавливаем выбранный цвет для всех светодиодов
map(r, 0, 255, 0, brightness), // Задаём зависимость яркости красного от параметра brightness, который определяется в настройках яркости
map(g, 0, 255, 0, brightness), // Задаём зависимость яркости зелёного от параметра brightness, который определяется в настройках яркости
b); // Устанавливаем синий равным 0
led.write(); // Записываем цвета в светодиоды
break;
//==========================================================//
// ГОРИТ ФИОЛЕТОВЫЙ
case 9:
r = 255; g = 0; b = 255; // Выставляем значение RGB-спектра в фиолетовый цвет
led.setColor(NeoPixelAll, // Устанавливаем выбранный цвет для всех светодиодов
map(r, 0, 255, 0, brightness), // Задаём зависимость яркости красного от параметра brightness, который определяется в настройках яркости
g, // Устанавливаем зелёный равным 0
map(b, 0, 255, 0, brightness)); // Задаём зависимость яркости синего от параметра brightness, который определяется в настройках яркости
led.write(); // Записываем цвета в светодиоды
break;
//==========================================================//
// ГОРИТ ГОЛУБЫМ
case 10:
r = 0; g = 255; b = 255; // Выставляем значение RGB-спектра в голубой цвет
led.setColor(NeoPixelAll, // Устанавливаем выбранный цвет для всех светодиодов
r, // Устанавливаем красный равным 0
map(g, 0, 255, 0, brightness), // Задаём зависимость яркости зелёного от параметра brightness, который определяется в настройках яркости
map(b, 0, 255, 0, brightness)); // Задаём зависимость яркости синего от параметра brightness, который определяется в настройках яркости
led.write(); // Записываем цвета в светодиоды
break;
//==========================================================//
// ГОРИТ БЕЛЫМ
case 11:
r = 255; g = 255; b = 255; // Выставляем значение RGB-спектра в белый цвет
led.setColor(NeoPixelAll, // Устанавливаем выбранный цвет для всех светодиодов
map(r, 0, 255, 0, brightness), // Задаём зависимость яркости красного от параметра brightness, который определяется в настройках яркости
map(g, 0, 255, 0, brightness), // Задаём зависимость яркости зелёного от параметра brightness, который определяется в настройках яркости
map(b, 0, 255, 0, brightness)); // Задаём зависимость яркости синего от параметра brightness, который определяется в настройках яркости
led.write(); // Записываем цвета в светодиоды
break;
//==========================================================//
// ВЫКЛЮЧЕНО
case 12:
r = 0; g = 0; b = 0; // Выставляем значение RGB-спектра в чёрный цвет (включено)
led.setColor(NeoPixelAll, // Устанавливаем выбранный цвет для всех светодиодов
r, // Устанавливаем красный равным 0
g, // Устанавливаем зелёный равным 0
b); // Устанавливаем синий равным 0
led.write(); // Записываем цвета в светодиоды
break;
}
//==========================================================//
// РЕЖИМ НАСТРОЙКИ ЯРКОСТИ:
if(setting){ // Если стоит флаг входа в режим настройки, то проверяем...
if(work_time+waiting_time > millis()){ // с момента входа в режим настройки не прошло ли время waiting_time, и если не прошло то...
if(flg){ // проверяем, стоит ли флаг смены режима работы датчика жестов. Если стоит, то
flg = 0; // сбрасываем флаг,
apds.disableGestureSensor(); delay(500); // выключаем режим чтения жестов, ждём 0,5с,
apds.enableProximitySensor(false); // включаем режим чтения приближения,
led.setColor(NeoPixelAll,0 ,0 ,0 ); led.write(); delay(500); // выключаем светодиоды, ждём 0,5с,
led.setColor(NeoPixelAll,25 ,25 ,25 ); led.write(); delay(500); // включаем светодиоды, ждём 0,5с,
led.setColor(NeoPixelAll,0 ,0 ,0 ); led.write(); // выключаем светодиоды.
}
if(apds.readProximity(proximityData)){ // Если датчик считывает приближение, то...
brightness = map(proximityData,0, 255,255,10); // регулируем яркость, "перевернув" диапазон значений, приходящий с датчика
}
} else { // Если время waiting_time прошло, то
setting = 0; // сбрасываем флаг режима настройка яркости,
apds.disableProximitySensor(); delay(500); // выключаем режим чтения приближения, ждём 0,5с,
apds.enableGestureSensor(false); // включаем режим чтения жестов,
led.setColor(NeoPixelAll,0 ,0 ,0 ); led.write(); delay(500); // выключаем светодиоды, ждём 0,5с,
led.setColor(NeoPixelAll,25 ,25 ,25 ); led.write(); delay(500); // включаем светодиоды, ждём 0,5с,
led.setColor(NeoPixelAll,0 ,0 ,0 ); led.write(); // выключаем светодиоды.
}
}
delay(100); // Задержка, чтобы не перегружать шину I2C постоянными запросами
}
Алгоритм работы скетча:
До кода void setup() определяются переменные, подключаются необходимые библиотеки.
В коде void setup() инициализируется датчик жестов, адресные светодиоды NeoPixel, выключаются светодиоды, если до этого они горели.
Код void loop() делится на несколько частей:
- Проверяется, определил ли датчик жестов какой-то жест;
- Если жест определён, тогда выполняется одно из действий, назначенное на этот жест: изменение режима работы светодиодов, изменение скорости анимации, изменение яркости, выключение;
- После того, как жест определён, в зависимости от того, какой был жест, скетч переходит к выбору режима работы (переменная
mode):- Работать в режиме плавного перелива RGB-цветами;
- Работать в режиме случайного выбора цветов;
- Работать в режиме случайного включения светодиодов случайным цветом;
- Работать в режиме переключения цветов;
- Работать в режиме имитации горения пламени свечи;
- Работать в режиме одного цвета (красный, зелёный, синий, жёлтый, фиолетовый, голубой, белый);
- Работать в режиме настройки яркости;
- Выключить свет;
- Если выбран режим настройки яркости, тогда:
- Перед входом в режим однократное включение светодиодов белым оповестит вас о том, что вы вошли в режим;
- Датчик будет переведён из режима чтения жестов в режим чтения расстояния до руки;
- В течении фиксированного времени
waiting_timeвы можете настраивать яркость работы светодиодов (поднесите руку к датчику и движением вверх-вниз регулируйте яркость); - По окончании режима настройки светодиоды снова однократно моргнут белым и ночник вернётся в рабочий режим;
- Если необходимо изменить скорость работы анимации, тогда:
- По умолчанию скорость анимации указана в переменной
z, а в скетче есть алгоритм изменения скорости:- Если
zпринимает значение от 1 до 10, то каждое движение вверх или вниз будет увеличивать или уменьшать его значение на 5; - Если
zпринимает значение от 10 до 100, то каждое движение вверх или вниз будет увеличивать или уменьшать его значение на 20; - Если
zпринимает значение от 100 до 500, то каждое движение вверх или вниз будет увеличивать или уменьшать его значение на 50;
- Если
- По умолчанию скорость анимации указана в переменной
- Если выбран режим выключения света, тогда все светодиоды потухнут, а для того, чтобы их снова зажечь, достаточно будет просто выполнить жест смены режима работы;

Обсуждение