// определения для работы, настраиваются пользователем
#define GO_UP_BTN_PIN 11 // нижний датчик прохода
#define GO_DOWN_BTN_PIN 10 // верхний датчик прохода
#define LED_PIN 6 // пин ленты
#define WORK_BRIGHTNESS 100 // яркость подсветки лестнецы для рабочего режима
#define DUTY_BRIGHTNESS 40 // яркость подсветки лестнецы для дежурного режима
#define NUM_STEP 20 // количество ступеней лестницы
//#define BRI_STEP 20 // количество ступеней яркости, для медленного и плавного включения/выключения.
#define BRI_STEP 10 // количество ступеней яркости
#define NUM_LEDS 516 //количество светодиодов ленты, нужно вычислить самостоятельно
#define ON_TIME 7 // время полного загорания/затухания подсветки. Время сильно примерное для малых величин (меньше 5), т.к. не учитываются доп операции и реально будет больше
#define ON_DELAY 3 // время задержки после полного влючения. Время выключения отсчитается от последнего срабатывания датчика, равно ON_TIME+ON_DELAY
#define DUTY_TIME 500 // время прохода полного цикла огней, в мс. 500 близко к минимуму. Минимальное значение зависит от кол-ва ступеней яркости и общего числа ступеней.
#define K_OUT 0.94 // коэффициент затухания дежурных огней при переходе на рабочий режим
#define DUTY_LEN 3 // количество одновременных дежурных огней
#define DUTY_DELAY 3 // задержка между циклами дежурных огней
#include <FastLED.h>
#define COLOR_ORDER GRB
#define CHIPSET WS2811
#include "GyverButton.h" // работа с датчиками прохода как с кнопками
// если датчики работают как то "наоборот", то поставить последний параметр NORM_CLOSE
GButton up_Btn(GO_UP_BTN_PIN , HIGH_PULL, NORM_CLOSE);
GButton down_Btn(GO_DOWN_BTN_PIN , HIGH_PULL, NORM_CLOSE);
// кол-во светодиодов на каждую ступень, настраивается пользователем
uint8_t stairs_step_leds[NUM_STEP] =
{
25, 25, 25, 25, 25,
25, 25, 25, 25, 25,
26, 26, 26, 28, 28,
28, 28, 25, 25, 25
};
// рабочие цвета подсветки, настраивается пользователем
CRGB work_Color = CRGB(255, 255, 255);
CRGB fly_Color = CRGB(20, 15, 255);
// глобальные переменные не требующие изменений
// массив светодиодов
CRGB leds[NUM_LEDS];
// задержка между изменениями цвета
uint8_t step_delay = 20;
uint8_t duty_delay = 20;
// градации яркости для шагов. всего BRI_STEP шагов.
// для длительных загораний более плавное включение
/*
uint8_t led_brightness[BRI_STEP] =
{
0, 5, 10, 15, 20, 25, 30, 35, 40, 52,
65, 77, 90, 102, 115, 125, 135, 145, 180, 255
};*/
// для коротких загораний - быстрое включение
uint8_t led_brightness[BRI_STEP] =
{
0, 10, 20, 40, 60,
90, 130, 170, 210, 255
};
// текущие ступени, снизу вверх, сверху вниз, и на выключение, дежурный
int8_t cur_step[4] =
{
0, NUM_STEP, NUM_STEP, 0
};
// текушая яркость ступени, от 0 до 9
uint8_t br_all[NUM_STEP];
bool goDown = false; // огни загораются сверху вниз
bool goUp = false; // огни загораются снизу вверх
bool lights_out = false; // огни выключаются
bool go_direction = false; // направление выключения false сверху вниз, true снизу вверх
bool goDuty = true; // работает режим дежурных онней
bool needClear = false; // надо почистить таблицу яркости!
bool ignor_top = false; // надо игнорировать одно срабатывание верхнего датчика
bool ignor_bottom = false; // надо игнорировать одно срабатывание нижнего датчика
uint32_t off_tmr = 0;
void setup() {
Serial.begin(115200);
delay(1000);
FastLED.addLeds<CHIPSET, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS).setCorrection(TypicalSMD5050);
FastLED.setBrightness(DUTY_BRIGHTNESS);
ClearBRI();
ClearLed();
FastLED.show();
step_delay = get_step_delay();
Serial.println(step_delay);
duty_delay = get_duty_delay();
Serial.println(duty_delay);
}
void loop() {
down_Btn.tick();
if (down_Btn.isPress())
{
if (!ignor_top)
{
Serial.println("Идем вниз!");
goDown = true;
cur_step[2] = NUM_STEP;
lights_out = false;
go_direction = false;
off_tmr = millis();
if (goDuty)
{
ClearBRI();
FastLED.setBrightness(WORK_BRIGHTNESS);
needClear = true;
}
goDuty = false;
}
else
{
ignor_top = false;
Serial.println("Игнор верхнего датчика!");
}
}
if (goDown)
{
lights_out = false;
goDown = stairs_fire_on_down();// если есть что зажигать свверху вниз, то зажигаем
if (goDown) ClearMidle();
else
{
ignor_bottom = true;
Serial.println("Нужен игнор нижнего датчика!");
}
}
up_Btn.tick();
if (up_Btn.isPress())
{
if (!ignor_bottom)
{
Serial.println("Идем вверх!");
goUp = true;
cur_step[2] = 0;
lights_out = false;
go_direction = true;
off_tmr = millis();
if (goDuty)
{
ClearBRI();
FastLED.setBrightness(WORK_BRIGHTNESS);
needClear = true;
}
goDuty = false;
}
else
{
ignor_bottom = false;
Serial.println("Игнор верхнего датчика!");
}
}
if (goUp)
{
lights_out = false;
goUp = stairs_fire_on_up(); // если есть что зажигать снизу вверх, то зажигаем
if (goUp) ClearMidle();
else
{
ignor_top = true;
Serial.println("Нужен игнор верхнего датчика!");
}
}
if ((!goDown) && (!goUp) && (!goDuty)) lights_out = true;
if (lights_out)
{
if (millis() - off_tmr > 1000 * (ON_TIME + ON_DELAY))
{
ignor_bottom = false;
ignor_top = false;
if (go_direction) lights_out = stairs_fire_off_up();
else lights_out = stairs_fire_off_down();
goDuty = !lights_out;
if (goDuty)
{
FastLED.setBrightness(DUTY_BRIGHTNESS);
cur_step[3] = 0;
}
}
}
if (goDuty)
{
stairs_duty_fire();
ignor_bottom = false;
ignor_top = false;
}
redraw_leds();
//ShowFPS();
}
// функции
void ClearLed()
{
for (uint16_t i = 0; i < NUM_LEDS; i++) leds[i] = 0;
}
uint16_t first_led_on_step(uint8_t n) // номер 1-го светодиода в ступени n
{
uint16_t result = 0;
for (uint8_t i = 0; i < n; i++) result = result + stairs_step_leds[i];
return result;
}
void set_step_color(uint8_t n, CRGB c)
{
uint16_t ns = first_led_on_step(n);
for (uint16_t i = ns; i < (ns + stairs_step_leds[n]); i++)
leds[i] = c;
}
CRGB remapColor(CRGB c, uint8_t br)
{
return CRGB(map(c.r, 0, 255, 0, led_brightness[br]), map(c.g, 0, 255, 0, led_brightness[br]), map(c.b, 0, 255, 0, led_brightness[br]));
}
bool fire_on_step(uint8_t n, CRGB c)
{
if (br_all[n] == (BRI_STEP - 1)) return true;
br_all[n]++;
set_step_color(n, remapColor(c, br_all[n]));
return false;
}
bool fire_off_step(uint8_t n, CRGB c)
{
if (br_all[n] == 0) return true;
br_all[n]--;
set_step_color(n, remapColor(c, br_all[n]));
return false;
}
bool stairs_fire_on_up()
{
static uint32_t f_tmr = millis();
if (millis() - f_tmr < step_delay) return true;
f_tmr = millis();
if (fire_on_step(cur_step[0], work_Color)) cur_step[0]++;
if (cur_step[0] == NUM_STEP)
{
cur_step[0] = 0;
return false;
}
return true;
}
bool stairs_fire_off_up()
{
static uint32_t f_tmr = millis();
if (millis() - f_tmr < step_delay) return true;
f_tmr = millis();
if (fire_off_step(cur_step[2], work_Color)) cur_step[2]++;
if (cur_step[2] == NUM_STEP)
{
cur_step[2] = 0;
return false;
}
return true;
}
bool stairs_fire_on_down()
{
static uint32_t f_tmr = millis();
if (millis() - f_tmr < step_delay) return true;
f_tmr = millis();
if (fire_on_step(cur_step[1] - 1, work_Color)) cur_step[1]--;
if (cur_step[1] == 0)
{
cur_step[1] = NUM_STEP;
return false;
}
return true;
}
bool stairs_fire_off_down()
{
static uint32_t f_tmr = millis();
if (millis() - f_tmr < step_delay) return true;
f_tmr = millis();
if (fire_off_step(cur_step[2] - 1, work_Color)) cur_step[2]--;
if (cur_step[2] == 0)
{
cur_step[2] = NUM_STEP;
return false;
}
return true;
}
void redraw_leds()
{
static uint32_t r_tmr = millis();
if (millis() - r_tmr < (step_delay)) return;
r_tmr = millis();
FastLED.show();
}
uint8_t get_duty_delay()
{
return (DUTY_TIME) / NUM_STEP / BRI_STEP;
}
uint8_t get_step_delay()
{
return (1000.0 * ON_TIME) / NUM_STEP / BRI_STEP;
}
void ShowFPS()
{
static uint32_t tm_m = 0;
static uint32_t cnt_m = 0;
cnt_m++;
if ((millis() - tm_m) > 1000)
{
Serial.print("loop per sec: "); Serial.println(cnt_m);
cnt_m = 0;
tm_m = millis();
}
}
void set_duty_step_color(uint8_t n, CRGB c)
{
uint16_t ns = first_led_on_step(n);
leds[ns] = c;
for (uint16_t i = ns + 1; i < ns + stairs_step_leds[n] - 1; i++)
leds[i] = CRGB(0, 0, 0);
leds[ns + stairs_step_leds[n] - 1] = c;
}
bool duty_fire_on_step(uint8_t n, CRGB c)
{
if (br_all[n] == (BRI_STEP - 1)) return true;
br_all[n]++;
set_duty_step_color(n, remapColor(c, br_all[n]));
return false;
}
bool duty_fire_off_step(uint8_t n, CRGB c)
{
if (br_all[n] == 0) return true;
br_all[n]--;
set_duty_step_color(n, remapColor(c, br_all[n]));
return false;
}
void stairs_duty_fire()
{
static uint32_t f_tmr = millis();
static uint32_t delay_tmr = millis();
if (millis() - f_tmr < duty_delay) return true;
f_tmr = millis();
int16_t on_step = cur_step[3];
int16_t off_step = cur_step[3] - (DUTY_LEN - 1);
bool f_off = false;
bool f_on = false;
if (off_step > -1 && off_step < NUM_STEP) f_off = duty_fire_off_step(off_step, fly_Color);
else f_off = true;
if (on_step > -1 && on_step < NUM_STEP) f_on = duty_fire_on_step(on_step, fly_Color);
else f_on = true;
if (cur_step[3] == (NUM_STEP + DUTY_LEN - 1)) delay_tmr = millis();
if ((cur_step[3] == (NUM_STEP + DUTY_LEN)) && (millis() - delay_tmr < (1000 * DUTY_DELAY))) f_on = false;
if (f_off && f_on) cur_step[3] = (cur_step[3] + 1) % (NUM_STEP + DUTY_LEN + 1);
}
void ClearBRI()
{
for (uint8_t i = 0; i < NUM_STEP; i++)br_all[i] = 0;
}
void ClearMidle()
{
if (!needClear) return;
static uint32_t f_tmr = millis();
if (millis() - f_tmr < step_delay) return;
f_tmr = millis();
needClear = false;
int16_t on_step = cur_step[3];
int16_t off_step = cur_step[3] - (DUTY_LEN - 1);
if (off_step < 0) off_step = off_step + NUM_STEP;
if (off_step > on_step) on_step = on_step + NUM_STEP;
for (int8_t i = off_step; i < on_step + 1; i++)
{
int8_t pz = i % NUM_STEP;
if (pz >= cur_step[0] && pz <= cur_step[1])
{
uint16_t ns = first_led_on_step(pz);
if (CRGB(0, 0, 0) != leds[ns])
{
needClear = true;
leds[ns] = CRGB(K_OUT * leds[ns].r , K_OUT * leds[ns].g , K_OUT * leds[ns].b );
}
ns = ns + stairs_step_leds[pz] - 1;
if (CRGB(0, 0, 0) != leds[ns])
{
needClear = true;
leds[ns] = CRGB(K_OUT * leds[ns].r , K_OUT * leds[ns].g , K_OUT * leds[ns].b );
}
}
}
}