Подсветка лестницы. Задания на доработку проекта

Alexey.Zakharyan

✩✩✩✩✩✩✩
13 Янв 2021
4
0
Решил сделать подсветку лестницы по схеме AlexGyver, вроде все собрал работает (это мой первый проект на Arduino) лента собрана в данный момент тестирую. В моем случае лестница имеет 2 пролета по 11 ступенек но ступеньки разной длины, 11 скупенек по 22 чипа WS2811 и еще 11 ступенек по 21 чип WS2811. соответственно программа AlexGyver не рассчитана на такое разнообразие ступенек, прошу помочь в разработке алгоритма для Arduino с разным количеством светодиодов на ступеньках. По финансовой части готов оплатить затраты на написание этого кода.photo_2021-01-24_18-19-13.jpg
 

Alexey.Zakharyan

✩✩✩✩✩✩✩
13 Янв 2021
4
0
Решил сделать подсветку лестницы по схеме AlexGyver, вроде все собрал работает (это мой первый проект на Arduino) лента собрана в данный момент тестирую. В моем случае лестница имеет 2 пролета по 11 ступенек но ступеньки разной длины, 11 скупенек по 22 чипа WS2811 и еще 11 ступенек по 21 чип WS2811. соответственно программа AlexGyver не рассчитана на такое разнообразие ступенек, прошу помочь в разработке алгоритма для Arduino с разным количеством светодиодов на ступеньках. По финансовой части готов оплатить затраты на написание этого кода.Посмотреть вложение 21356
Выкладываю фото монтажа ленты думаю кому-нибудь будет интересно.
photo_2021-01-25_00-24-52.jpgphoto_2021-01-25_00-24-57.jpgphoto_2021-01-25_00-25-46.jpgphoto_2021-01-25_00-26-02.jpgphoto_2021-01-25_00-26-06.jpgphoto_2021-01-25_00-26-10.jpgphoto_2021-01-25_00-26-16.jpgphoto_2021-01-25_00-26-19.jpgphoto_2021-01-25_00-26-25.jpgphoto_2021-01-25_00-26-36.jpgphoto_2021-01-25_00-26-40.jpgphoto_2021-01-25_00-26-43.jpg
 

IamNikolay

★★★✩✩✩✩
15 Янв 2020
820
175
Судя по фото - лента IP30. Когда ступеньки будут мыть вода затечет под выступ и замкнет электрику, последствия понятны.
Кроме того, даже если взять ленту IP65, то крепить нужно на водостойкий клей.
В идеале - купить IP67 (в силиконовом каркасе), т.к. при износе ленты (перегорании светодиодов) её можно будет легко заменить.
 

Alexey.Zakharyan

✩✩✩✩✩✩✩
13 Янв 2021
4
0
Судя по фото - лента IP30. Когда ступеньки будут мыть вода затечет под выступ и замкнет электрику, последствия понятны.
Кроме того, даже если взять ленту IP65, то крепить нужно на водостойкий клей.
В идеале - купить IP67 (в силиконовом каркасе), т.к. при износе ленты (перегорании светодиодов) её можно будет легко заменить.
Согласен с Вами, ленту покупал 5m 48 IP30 WS2811, думаю стоит обезопасить пайку термоклеем, и на будущее исключить мокрую уборку лестницы.
 

xof

★✩✩✩✩✩✩
24 Окт 2020
123
44
Добрый день! Можно тоже получить прошивку на разное количество диодов на ступеньку, для тестирования? Буду премного благодарен!!!
Чтобы не ждать у моря погоды куда проще дописать это самому. Тем более что это совсем не сложно. Конкретные вопросы в разделе помощи программирования можно спросить. ;)
А если хочется ничего не делать и получить, тогда в платный раздел обратиться.
 
  • Лойс +1
Реакции: bort707

ivor

✩✩✩✩✩✩✩
28 Окт 2019
7
0

Mils

✩✩✩✩✩✩✩
2 Дек 2018
4
1
Что-т я не понял с настройкой подсветки ночного режима в код. Стояло по умолчанию:
C++:
int16_t NIGHT_LIGHT_BIT_MASK = 0b0100100100100100;
Запускает только по 5 светодиодов на каждую ступень. Добавляю значения для нужных мне 27-ми светодиодов:
C++:
int16_t NIGHT_LIGHT_BIT_MASK = 0b0100100100100100100100100100100100100100100100100100100100100100100100100100100100;
Горит все те же по 5 светиков. Где еще нужно для этого менять значения в коде?
 

bort707

★★★★★★✩
21 Сен 2020
2,894
860
как думаете, такая строчка
0b0100100100100100100100100100100100100100100100100100100100100100100100100100100100;
поместится в 16 бит? :)
 

xof

★✩✩✩✩✩✩
24 Окт 2020
123
44
Что-т я не понял с настройкой подсветки ночного режима в код. Стояло по умолчанию:
C++:
int16_t NIGHT_LIGHT_BIT_MASK = 0b0100100100100100;
Я бы делал тут проще изначально если есть желание защищаться от выгорания диодов для чего это и было сделано - зажигал бы чётные\нечётные чтобы не париться со сдвигами, шириной и т.п.
 

Gen@

★★★✩✩✩✩
13 Апр 2020
129
110
Что-т я не понял с настройкой подсветки ночного режима в код. Стояло по умолчанию:
C++:
int16_t NIGHT_LIGHT_BIT_MASK = 0b0100100100100100;
это паттерн который должен циклически применяться ко всем диодам крайних ступенек.
Сделайте bug report на github я проверю и пофикшу, если это баг
 

Gen@

★★★✩✩✩✩
13 Апр 2020
129
110
Возможно-ли доработать прошивку так, чтобы подсветка выключалась как у Алекса, по срабатыванию конечного датчика?
За ваши деньги - любой каприз. Ну а если серьёзно то именно выключение по датчику многими хаялось в прошивке Алекса. То кот забежит Или кто то сдругого конца на лестинцу заходит и идёшь в темноте.
 

Gen@

★★★✩✩✩✩
13 Апр 2020
129
110
Я бы делал тут проще изначально если есть желание защищаться от выгорания диодов для чего это и было сделано - зажигал бы чётные\нечётные чтобы не париться со сдвигами, шириной и т.п.
кому то возможно будет сильно ярко светить половиной диодов. Поэтому и сделал битовым шаблоном. Просто надо будет пару примеров добавить для простых смертных
 

Старик Похабыч

★★★★★★★
14 Авг 2019
4,185
1,280
Москва
Вот! Поэтому я выключение сделал иначе.. долго ломал голову как. Расскажу
2 датчкика, первый и второй
Заходит человек по первому, лестница загорается плавно, определенное время и после этого еще сколько то горит . Если второй датчик сработал во время пока лестница еще не загорелась вся, то она начинает загораться со второго края, и после этого горит оставшееся время. Если лестница загорелась полностью, то срабатывание 2го датчика не учитывается, как будто человек вышел, гореть продолжает до нужного тайм-аута.
Если лестница загорелась полностью, и при этом прошел человек через второй датчик, то он не учитывается, НО разумно предположить, что 1-ый человек должен выйти с лестницы через 2-ой датчик. и он сработает как включение лестницы, но уже со 2-го к 1-ому, и какое то время будет не активен 1-ый датчик.
Вроде бы не запутался,...
 
  • Лойс +1
Реакции: ivor

Gen@

★★★✩✩✩✩
13 Апр 2020
129
110
ну вот именно поэтому я и поставил таймаут. Ставьте его побольше и не морочьте людям голову.
 

ivor

✩✩✩✩✩✩✩
28 Окт 2019
7
0
Вот! Поэтому я выключение сделал иначе.. долго ломал голову как. Расскажу
2 датчкика, первый и второй
Заходит человек по первому, лестница загорается плавно, определенное время и после этого еще сколько то горит . Если второй датчик сработал во время пока лестница еще не загорелась вся, то она начинает загораться со второго края, и после этого горит оставшееся время. Если лестница загорелась полностью, то срабатывание 2го датчика не учитывается, как будто человек вышел, гореть продолжает до нужного тайм-аута.
Если лестница загорелась полностью, и при этом прошел человек через второй датчик, то он не учитывается, НО разумно предположить, что 1-ый человек должен выйти с лестницы через 2-ой датчик. и он сработает как включение лестницы, но уже со 2-го к 1-ому, и какое то время будет не активен 1-ый датчик.
Вроде бы не запутался,...
а можно на вашу прошивку взглянуть или она платная?
 

Старик Похабыч

★★★★★★★
14 Авг 2019
4,185
1,280
Москва
@ivor, Я ее делал под заказ, и она очень специфическая.
Она на меге (так уже была распаяна и вшита), пиродатчиках, без эффектов (дежурная подсветка и основной свет, плавное включение и угасание), на разное кол-во диодов на ступенях.
 

bort707

★★★★★★✩
21 Сен 2020
2,894
860
кому то возможно будет сильно ярко светить половиной диодов. Поэтому и сделал битовым шаблоном. Просто надо будет пару примеров добавить для простых смертных
@Gen@,, у вас в работе с битовыми шаблонами ошибочка вкралась - точнее она будет, если длина ступеньки больше длины шаблона.
В функции fillStepWithBitMask
в строке
C++:
if (bitRead(bitMask, i % STEP_LENGTH)) {
надо добавить нормирование на длину битмаски
C++:
if (bitRead(bitMask, (i % STEP_LENGTH)%16) {
и все будет работать правильно со ступеньками любой длины

ЗЫ @Mils - указаннное выше - решение вашей проблемы, описанной в #84
 
Изменено:
  • Лойс +1
Реакции: Gen@

Старик Похабыч

★★★★★★★
14 Авг 2019
4,185
1,280
Москва
@ivor,
С позволения заказчика ( Gren_76 ) выкладываю результаты:
Старый вариант :

Новый вариант:

C++:
// определения для работы, настраиваются пользователем

#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 );
      }
    }
  }
}
 

Slenk

★★★★★★✩
21 Янв 2020
382
588
34
Краснодар
А с "объёмниками" никто не экспериментировал? Зажигать как зажигается, а тушить когда "объёмник" перестанет возбуждаться
 

Gen@

★★★✩✩✩✩
13 Апр 2020
129
110
спасибо добавлю патч на днях

трассировочные огни в режиме ожидания прикольная фича. возьму на заметку. Тут @ivor просит добавить кнопку. Посмотрим что за функции ему там нужны. Потом добавлю на голосование. Если будет интерес - добавлю.
 
  • Лойс +1
Реакции: LexxExpert, MAXGRU и ivor

Gen@

★★★✩✩✩✩
13 Апр 2020
129
110
За идею спасибо @Gren_76 . А код мне не нужет. Сам напишу что угодно.