#define HC_ECHO 2 // пин Echo
#define HC_TRIG 3 // пин Trig
#define LED_MAX_MA 1500 // ограничение тока ленты, ма
#define LED_PIN 13 // пин ленты
#define LED_NUM 50 // к-во светодиодов
#define VB_DEB 0 // отключаем антидребезг (он есть у фильтра)
#define VB_CLICK 900 // таймаут клика
#include <VirtualButton.h>
bool state = 1; // 0 выкл, 1 вкл
byte mode = 0; // 0 цвет, 1 теплота, 2 огонь
byte bright[3] = {30, 30, 30}; // яркость
byte value[3] = {0, 0, 0}; // параметр эффекта (цвет...)
pinMode(HC_TRIG, OUTPUT); // trig выход
pinMode(HC_ECHO, INPUT); // echo вход
FastLED.addLeds<WS2812, LED_PIN, GRB>(leds, LED_NUM);
FastLED.setMaxPowerInVoltsAndMilliamps(5, LED_MAX_MA);
FastLED.setBrightness(255);
mem.begin(0, 'a'); // запуск и чтение настроек
applyMode(); // применить режим
mem.tick(); // менеджер памяти
if(data.state && data.mode == 2)fireTick(); // анимация огня
// таймер 50мс, опрос датчика и вся основная логика
if(millis() - tmr >= 50){
staticuint32_t tout; // таймаут настройки (удержание)
staticint offset_d; // оффсеты для настроек
int dist = getDist(HC_TRIG, HC_ECHO); // получаем расстояние
dist = getFilterMedian(dist); // медиана
dist = getFilterSkip(dist); // пропускающий фильтр
int dist_f = getFilterExp(dist); // усреднение
gest.poll(dist); // расстояние > 0 - это клик
// есть клики и прошло 2 секунды после настройки (удержание)
if(gest.hasClicks() && millis() - tout > 2000){
data.state = !data.state; // вкл/выкл
// если включена И меняем режим (0.. 2)
if(data.state && ++data.mode>= 3) data.mode = 0;
if(gest.click() && data.state){
pulse(); // мигнуть яркостью
// удержание (выполнится однократно)
if(gest.held() && data.state){
pulse(); // мигнуть яркостью
offset_d = dist_f; // оффсет расстояния для дальнейшей настройки
case 0: offset_v = data.bright[data.mode]; break; // оффсет яркости
case 1: offset_v = data.value[data.mode]; break; // оффсет значения
// удержание (выполнится пока удерживается)
if(gest.hold() && data.state){
// смещение текущей настройки как оффсет + (текущее расстояние - расстояние начала)
int shift = constrain(offset_v + (dist_f - offset_d), 0, 255);
case 0: data.bright[data.mode] = shift; break;
case 1: data.value[data.mode] = shift; break;
// получение расстояния с дальномера
#define HC_MAX_LEN 1000L // макс. расстояние измерения, мм
intgetDist(byte trig, byte echo){
digitalWrite(trig, HIGH);
// измеряем время ответного импульса
uint32_t us = pulseIn(echo, HIGH, (HC_MAX_LEN * 2 * 1000 / 343));
// считаем расстояние и возвращаем
return(us * 343L / 2000);
intgetFilterMedian(int newVal){
if(++count >= 3) count = 0;
return(max(buf[0], buf[1]) == max(buf[1], buf[2])) ? max(buf[0], buf[2]) : max(buf[1], min(buf[0], buf[2]));
#define FS_WINDOW 7 // количество измерений, в течение которого значение не будет меняться
#define FS_DIFF 80 // разница измерений, с которой начинается пропуск
intgetFilterSkip(int val){
if(!prev && val) prev = val; // предыдущее значение 0, а текущее нет. Обновляем предыдущее
// позволит фильтру резко срабатывать на появление руки
// разница больше указанной ИЛИ значение равно 0 (цель пропала)
if(abs(prev - val)> FS_DIFF || !val){
// счётчик потенциально неправильных измерений
}else count = 0; // сброс счётчика
// экспоненциальный фильтр со сбросом снизу
#define ES_EXP 2L // коэффициент плавности (больше - плавнее)
#define ES_MULT 16L // мультипликатор повышения разрешения фильтра
intgetFilterExp(int val){
if(val) filt += (val * ES_MULT - filt) / ES_EXP;
else filt = 0; // если значение 0 - фильтр резко сбрасывается в 0
// в нашем случае - чтобы применить заданную установку и не менять её вниз к нулю
case 0: led.setWheel8(data.value[0]); break;
case 1: led.setKelvin(data.value[1] * 28); break;
// плавная смена яркости при ВКЛЮЧЕНИИ и СМЕНЕ РЕЖИМА
if(prev_br != data.bright[data.mode]){
int shift = prev_br > data.bright[data.mode] ? -BR_STEP : BR_STEP;
while(abs(prev_br - data.bright[data.mode])> BR_STEP){
led.setBrightness(prev_br);
prev_br = data.bright[data.mode];
// плавная смена яркости при ВЫКЛЮЧЕНИИ
if(prev_br < 0) prev_br = 0;
led.setBrightness(prev_br);
mem.update(); // обновить настройки
FastLED.showColor(CRGB(led.R, led.G, led.B));
staticuint32_t rnd_tmr, move_tmr;
staticint rnd_val, fil_val;
// таймер 100мс, генерирует случайные значения
if(millis() - rnd_tmr > 100){
// таймер 20мс, плавно движется к rnd_val
if(millis() - move_tmr > 20){
// эксп фильтр, на выходе получится число 0..120
fil_val += (rnd_val * 10 - fil_val) / 5;
// преобразуем в яркость от 100 до 255
int br = map(fil_val, 0, 120, 100, 255);
// преобразуем в цвет как текущий цвет + (0.. 24)
int hue = data.value[2] + fil_val / 5;
for(int i = prev_br; i < prev_br + 45; i += 3){
led.setBrightness(min(255, i));
for(int i = prev_br + 45; i > prev_br; i -= 3){
led.setBrightness(min(255, i));