Ws2812b меняем ленту на гирлянду

Нашёл ещё один вариант как мне кажется устойчивый к многократным перемонтажам … и того пока три:

А пенопластовые шарики из подушек или кресел-подушек ? :slight_smile:

Селикогель и пенопласт совсем разные хим свойства.

Они непрозрачные

Делал новогоднюю гирлянду на дом из 100 светодиодных модулей.
[https://www.youtube.com/watch?v=UDHte7VaFYU]

Красиво.

VID_20241127_152919

Спойлер
/////////////////////////////////////
/// примеры для ws2812b на ESP32
////////////////////////////////////
#define plus 17 //
#define minus 25 //
int IK_PIN = 26;        //Пин подключения выходного сигнала с ИК-приёмника
       


#include "Adafruit_NeoPixel.h"
#define line_ 96 //число пикселей в ленте
uint8_t dataPin = 5;// вывод для подключения
Adafruit_NeoPixel strip = Adafruit_NeoPixel(line_,dataPin , NEO_BRG + NEO_KHZ800);// Создаем переменную strip для управления нашей лентой.

volatile word keycode_ = 0;// переменная хранения кода кнопки

byte R=0; byte G=0; byte B=0;//составляющие цвета пикселя
int t=70;// пауза между кадрами
int effekt=1;// номер эффекта
int M=10;//коэффициент мощности свечения пикселя 
long Y=0;//переменная хранения моментов времени
byte cvet=0;//
long Y1=0;//переменная хранения моментов времени
int ti = 0;
int ni = 0;
bool FL_=true;//переключение автоматического режима на управление с пульта ИК
bool FL_show=true;//
//
byte kanal_1[8]={0,24,44,60,74,83,89,95};// выделение светодиодов в группы-каналы
byte kanal_2[8]={4,28,48,63,76,81,88,95}; 
byte kanal_3[8]={8,31,50,65,77,85,91,95};  
byte kanal_4[8]={12,34,52,67,78,87,93,95};

// 
///RGB
void setup() {
pinMode(plus,OUTPUT);//
digitalWrite(plus,HIGH);// плюс питания ик приёмника
pinMode(minus,OUTPUT);//
digitalWrite(minus,LOW);// минус питания ик приёмника  
pinMode(IK_PIN, INPUT);  // ir recv
attachInterrupt(26, irread, FALLING);
 strip.begin();
Serial.begin(9600); 

}
void loop() {
 
 ///
 
 //автоматический перебор программ каждые 30 секунд
 if(millis()-Y>30000ul&&FL_){
 Y=millis();
 t=70*random(1,4);//псевдослучайный выбор длительности паузы между кадрами в эффекте
 effekt--; if(effekt==0){effekt=22;}//
 M=random(3,11);//псевдослучайный выбор яркости свечения
 cvet=random(0,6);//псевдослучайный выбор компонент цвета 
 }
 
 /// 
 // проигрывание программы эффекта
   for ( ti = 0; ti < line_; ti++) {//перебор номеров кадров в ленте
    for ( ni = 0; ni < line_; ni++) {//перебор номеров пикселей в ленте
     cvet_(ti,ni);// функция определения цвета пикселя в зависимости от его номера в ленте и момента времени (кадра анимации)   
     if(cvet==0){strip.setPixelColor(ni, R*M, G*M, B*M);}if(cvet==1){strip.setPixelColor(ni, G*M, B*M, R*M);}if(cvet==2){strip.setPixelColor(ni, B*M, R*M, G*M);}
     if(cvet==3){strip.setPixelColor(ni, R*M, B*M, G*M);}if(cvet==4){strip.setPixelColor(ni, G*M, R*M, B*M);}if(cvet==5){strip.setPixelColor(ni, B*M, G*M, R*M);}
    }
  if(FL_show){strip.show();}  
  // delay_(t);
  delay(t);keypressed();
    }
 ///
 }
///////////////////////////////////////
void cvet_ (int it,int in){
 
 switch (effekt) {
     case 0:
     R=0;G=0;B=0;//выключение ёлки
     break;
    case 1:
     R=7;G=1;B=0;
     if(in==it){R=0;G=14;B=2;}//бегущий по оранжевой ленте -> цвета бирюзового пиксель ->
      break;
    case 2:
     { byte mnogo=9; R=5*((in+it%mnogo)%mnogo);G=5*((mnogo-1-(in+it%mnogo)%mnogo));B=0;}// бегущая по ленте <-палитра от зелёного до красного (переход из 9 штук)<-
      break;
    case 3:
      if(in<=it){R=5;G=0;B=10;}else{R=0;G=10;B=0;}// ->полоса фиолетового цвета набегает на полосу зелёного ->
      break;
     case 4:
      if(in%(it+1)==0){R=15;G=15;B=10;}else{R=7;G=1;B=1;}// -> выстрел белыми пикселями с их взаиморазлётом -> на розовом фоне 
      break;
      case 5:
        if(in==line_-it||in+1==line_-it||in+2==line_-it||in+3==line_-it||in+4==line_-it){R=15;G=3;B=0;}else{R=0;G=0;B=7;}//бегущий по синей ленте -> цвета оранжевого 5 пикселей ->
      break;
    case 6:
     { byte mnogo=10; R=5*((in+it%mnogo)%mnogo);G=0;B=5*((mnogo-1-(in+it%mnogo)%mnogo));}// бегущая по ленте <-палитра от синего до красного (переход из 10 штук)<-
      break;
    case 7:
      if(in<=line_-it){R=10;G=10;B=0;}else{R=0;G=7;B=7;}// <-полоса голубого цвета набегает на полосу жёлтого <-
      break;
     case 8:
      if((line_-in)%(it+1)==0){R=15;G=15;B=10;}else{if(in%(it+3)==0){R=10;G=3;B=0;}else{R=0;G=2;B=2;}}// -> выстрел белыми пикселями и оранжевыми с их взаиморазлётом -> на голубом фоне 
      break;
    case 9:
     { byte mnogo=32; R=3;G=1*(mnogo-1-(it%mnogo));B=1*((it%mnogo));t=200;}// плавный переход цвета ленты от "зеленоватого к синеватому"
      break;  
    case 10:
     { byte mnogo=32; R=1*(mnogo-1-(it%mnogo));G=1;B=1*((it%mnogo));t=200;}// плавный переход цвета ленты от "красноватого к синеватому"
      break; 
    case 11:
     { byte mnogo=7; R=0*((mnogo-1-(in+it%mnogo)%mnogo));G=2*((mnogo-1-(in+it%mnogo)%mnogo));B=5*((mnogo-1-(in+it%mnogo)%mnogo));}// бегущая по ленте <-поток метеоров (переход из 7 штук)<-
      break; 
     case 12:
     {if(in==it){R=0;G=1;B=0;}if(in+1==it){R=10;G=0;B=0;}if(in+2==it){R=10;G=3;B=0;}if(in+3==it){R=10;G=10;B=0;}if(in+4==it){R=0;G=10;B=0;}
     if(in+5==it){R=0;G=10;B=10;}if(in+6==it){R=0;G=0;B=15;}if(in+7==it){R=3;G=0;B=10;} }// бегущая по ленте ->радуга (переход из 7 штук)->
      break;
     case 13:  
     {if(in<=line_-it){R=0;G=1;B=0;}if(in+2==line_-it){R=10;G=0;B=0;}if(in+4==line_-it){R=10;G=3;B=0;}if(in+6==line_-it){R=10;G=10;B=0;}if(in+8==line_-it){R=0;G=10;B=0;}
     if(in+10==line_-it){R=0;G=10;B=10;}if(in+12==line_-it){R=0;G=0;B=15;}if(in+14==line_-it){R=3;G=0;B=10;} }// бегущая по ленте <-радуга (переход из 7 штук через 1)<-
      break; 
      case 14:  
    {if(in-2<(line_+2*it)/2&&in+2>(line_-2*it)/2&&it<line_/2){R=0;G=7;B=7;}else{if((in>it||in<line_-it)&&it>=line_/2){R=0;G=7;B=7;}else{R=5;G=1;B=5;}} }//<-разбегание от центра поочерёдное полосы синеватого и сиреневого цветов - подобие волны->
      break; 
   case 15:  
    {if(in-2<(line_+2*it)/2&&in+2>(line_-2*it)/2&&it<line_/2){R=0;G=7;B=1;}else{if((in>it||in<line_-it)&&it>=line_/2){R=0;G=7;B=1;}else{R=15;G=2;B=0;}} }//<-разбегание от центра поочерёдное полосы зеленоватого и оранжевого цветов - подобие волны->
      break; 
     case 16:  
    { if((in>=it-3&&in<=it+3)||(in>=it-3+8&&in<=it+3+8)){R=0;G=0;B=0;}else{ R=2*(it%2);G=2*((it%4)/2);B=2*((it%8)/4);} t=70;}//-> бег тень 6 пикселей, светящиеся 1 пиксель, тень 6 пикселей -> на фоновом радужном переливе 
      break;
       case 17:
     if(in%(it/(line_/3)+1)==0){R=random(0,(in+it)%7+2); G=random(0,in%10+2); B=random(0,it%10+2);}if(R==0&&G==0&&B==0){R=5;G=5;B=5;}// рандом пикселей 
      break; 
       case 18:
     {R=(in/16+it)%2;G=((in/16+it)/2)%2;B=((in/16+it)/4)%2;M=40;if(R==0&&G==0&&B==0){R=7;G=1;B=0;M=5;}t=140;}//радужное движение групп по 16 пикселей с добавкой оранжевого
      break; 
       case 19:
     {R=((it+in)/(5))%2;G=(((it+in)/(5))/2)%2;B=(((it+in)/(5))/4)%2;M=40;if(R==0&&G==0&&B==0){R=7;G=1;B=0;M=5;}t=70;}//радужное движение групп по 16 пикселей с добавкой оранжевого 2 вариант
      break; 
       case 20:
      if(in%2==0&&it%3==0){R=1;G=2;B=3;}else{R=2;G=3;B=1;}  
     for( byte w=0;w<8;w++){
      if(in==kanal_1[w]||in==kanal_2[w]||in==kanal_3[w]||in==kanal_4[w]){R=1;G=0;B=0;}// маятник эффект
      if(it%6==0&&in==kanal_1[w]){R=8;G=5;B=5;}
      if(it%6==1&&in==kanal_2[w]){R=8;G=5;B=5;}
      if(it%6==2&&in==kanal_3[w]){R=8;G=5;B=5;}
      if(it%6==3&&in==kanal_4[w]){R=8;G=5;B=5;}
      if(it%6==4&&in==kanal_3[w]){R=8;G=5;B=5;}
      if(it%6==5&&in==kanal_2[w]){R=8;G=5;B=5;}
       }
     break; 
       case 21:
   if(it%4==0){R=1;G=1;B=0;}else{R=2;G=3;B=0;}  
    if(it<8&&(in==kanal_1[7-it%8]||in==kanal_4[it%8])){R=8;G=5;B=5;}//бегущие точки по каналам
    if(it>=8&&it<16&&(in==kanal_2[it%8]||in==kanal_3[7-it%8])){R=8;G=8;B=5;}
    if(it>=16&&it<24&&(in==kanal_3[7-it%8]||in==kanal_2[it%8])){R=8;G=8;B=5;}
    if(it>=24&&it<32&&(in==kanal_4[it%8]||in==kanal_1[7-it%8])){R=8;G=8;B=5;} 
    if(it>=32&&it<40&&(in==kanal_1[7-it%8]||in==kanal_4[it%8])){R=8;G=8;B=5;}
    if(it>=40&&it<48&&(in==kanal_2[it%8]||in==kanal_3[7-it%8])){R=8;G=8;B=5;}
    if(it>=48&&it<56&&(in==kanal_3[7-it%8]||in==kanal_2[it%8])){R=8;G=8;B=5;}
    if(it>=56&&it<64&&(in==kanal_4[it%8]||in==kanal_1[7-it%8])){R=8;G=8;B=5;}
    if(it>=64&&it<72&&(in==kanal_1[7-it%8]||in==kanal_4[it%8])){R=8;G=8;B=5;}
    if(it>=72&&it<80&&(in==kanal_2[it%8]||in==kanal_3[7-it%8])){R=8;G=8;B=5;}
    if(it>=80&&it<88&&(in==kanal_3[7-it%8]||in==kanal_2[it%8])){R=8;G=8;B=5;}
    if(it>=88&&it<96&&(in==kanal_4[it%8]||in==kanal_1[7-it%8])){R=8;G=8;B=5;}
    if(in==it){R=14;G=3;B=0;}//бегущие точки по спирали ёлки
    if(in==line_-it){R=3;G=14;B=0;}//
    break; 
     case 22:
    R=1;G=2;B=it%5;//отрисовка пикселей ёлки 
     if(it%16<8){
     for( int w=0;w<8;w++){
      if(in==kanal_1[w]||in==kanal_2[w]||in==kanal_3[w]||in==kanal_4[w]){R=1;G=0;B=0;}//отрисовка пикселей каналов 
      if(it%8==w&&(in==kanal_1[w]||in==kanal_2[w]||in==kanal_3[w]||in==kanal_4[w])){R=8;G=5;B=5;}
       }
     }
    else{
      for( int w=0;w<8;w++){
      if(in==kanal_1[w]||in==kanal_2[w]||in==kanal_3[w]||in==kanal_4[w]){R=1;G=0;B=0;}//отрисовка пикселей каналов 
      if(w!=0&&(line_-it)%8==w&&(in==kanal_1[w]||in==kanal_2[w]||in==kanal_3[w]||in==kanal_4[w])){R=8;G=5;B=5;}
       }
    }
     break; 
    default:
      { R=0;G=0;B=0;}//выключаем пиксели если не выбран эффект
  } 
}
/////////////////////////////////////////
void irread() {
volatile static byte n = 0;
volatile static word keycode = 0;
volatile static unsigned long tm = 0;
volatile static unsigned long last = 0;

  tm = micros() - last; 
  last = micros();
  n += 1;
  if (tm > 3000) {
    keycode = 0;
    n = 0;
    }
  if (n >8&&n<=32) {
    if (tm > 1500) {keycode += 1;}
    keycode = keycode << 1;
  }
  if(n==32){keycode_ = keycode;keycode = 0;n=0;}
}
//////////////////////////////////////////
void keypressed() {
 if(keycode_!=0){
  Serial.println(keycode_, HEX);// печатаем код кнопки 
  //код по обработке значений keycode_
   switch (keycode_) {
    case 0x15906E:
    effekt++;//выбор - номер эффекта
    if(effekt>19){effekt=1;}
      break;
    case 0x1451AE:
    effekt--;//выбор - номер эффекта
    if(effekt<1){effekt=22;}
      break;
   case 0x1550AE:
     M=M+2;if(M>10){M=10;}//выбор яркости свечения 
      break;
    case 0x14D12E:
      M=M-2;if(M<0){M=1;}//выбор яркости свечения
      break; 
      case 0x15C03E:
      cvet++;if(cvet>5){cvet=0;}//выбор компонент цвета  
      break; 
       case 0x14A15E:
       t=t+30;if(t>210){t=30;}//выбор длительности паузы между кадрами в эффекте 
      break;
      case 0x14817E:
       FL_=! FL_;
      break; 
      case 0x15D02E:
       FL_show=! FL_show;//переключение с автоматического перебора на ручное эффектов
      break;
       case 0x15609E:
       effekt=0;//выключение ёлки
      break;  
      default:
      ti=ni=line_;      
    }
  keycode_ = 0;delay(100);
   }  
 }
//////////////////////////////////////////////
/////////////////////////////////////////////////
// функция временной задержки с проверкой поступающих данных
void delay_(int T1)
{
Y1=millis();  
while(millis()-Y1<T1){
 
// keypressed();
    
}
}
////////////////////////////////////////////////

 

Текущая версия.
…Может кто подкинет описание интересных эффектов?
Тут правда своеобразная логика создания эффекта.


Бег пикселя снизу вверх будет записан так:

case 1:
     R=7;G=1;B=0;//фоновая закраска ленты по всем кадрам оранжевым 
     if(in==it){R=0;G=14;B=2;}//бегущий по ленте -> цвета бирюзового пиксель ->
      break;

:slight_smile:
Ещё бы генерацию эффектов придумать.

Что такое - генерация эффектов?

По типу как я делал в узорах. Там правда проще в логике работы – псевдослучайный набор в четверти узора, а дальше повторение в отражении или повороте.


гиф

Переделал на голосовое управление гирлянду. Концепт - отдельные команды: шестнадцать, очень ярко , быстро, цвет пять. Вроде управление не хуже чем при ИК.

//ФУНКЦИИ ПРИЁМА И ОБРАБОТКИ КОМАНД-ДАННЫХ
void CheckSerial()
{
  //////////////////////////////////////////////////////////////////////////////
  while (SerialBT.available())//считываем строку данных пока они поступают
  {
    char inChar = (char)SerialBT.read();
    if (inChar == '\n')     //если окончание строки или запрос на загрузку
    {
      MakeCmd();//обрабатываем её данной функцией
      break;
    }
    else inputString += inChar;// иначе удлиняем строку на один символ
  }
 
}
/////////////////////////////////////////////////////////////////////////////////
void MakeCmd()
{
  int y = inputString.length(); // присваиваем переменной у число символов в строке
  String cmd = inputString; // передаём строку другой переменной
  inputString = "";//"обнуляем переменную"
  //////
// Serial.print(cmd);
if(cmd=="СТОП КАДР"){FL_show=false;//стоп-кадр
    }
if(cmd=="ПУСК КАДР"){FL_show=true;//стоп-кадр
    }
 if(cmd=="АВТОМАТ ДА"){FL_=true;//автоматический перебор
    }
 if(cmd=="АВТОМАТ НЕТ"){FL_=false;//ручной перебор
    }
 if(cmd=="ЦВЕТ 0"){ cvet=0;//перебор сочетаний цветов
    }
  if(cmd=="ЦВЕТ 1"){ cvet=1;//перебор сочетаний цветов
    } 
  if(cmd=="ЦВЕТ 2"){ cvet=2;//перебор сочетаний цветов
    }
  if(cmd=="ЦВЕТ 3"){ cvet=3;//перебор сочетаний цветов
    }
   if(cmd=="ЦВЕТ 4"){ cvet=4;//перебор сочетаний цветов
    }
  if(cmd=="ЦВЕТ 5"){ cvet=5;//перебор сочетаний цветов
    }          
 if(cmd=="ЯРКО"){ M=5;//выбор яркости свечения 
    } 
 if(cmd=="ТЕМНО"){ M=3; //выбор яркости свечения 
    }
 if(cmd=="ОЧЕНЬ ЯРКО"){ M=10;//выбор яркости свечения 
    } 
 if(cmd=="ОЧЕНЬ ТЕМНО"){ M=1; //выбор яркости свечения
    }   
 if(cmd=="ОЧЕНЬ БЫСТРО"){ t=30;//выбор длительности паузы между кадрами в эффекте 
 }
 if(cmd=="ОЧЕНЬ МЕДЛЕННО"){ t=210;//выбор длительности паузы между кадрами в эффекте 
 } 
  if(cmd=="БЫСТРО"){ t=60;//выбор длительности паузы между кадрами в эффекте 
 }
 if(cmd=="МЕДЛЕННО"){ t=100;//выбор длительности паузы между кадрами в эффекте 
 }    
 if(cmd=="ВЫКЛЮЧИ"){ effekt=0;//выключение ёлки 
 }
 else{//выбор - номер эффекта
 int pos=cmd.toInt();//преобразовать строку в число 
 if(pos>0&&pos<=23){effekt=pos;}
     }                           
  //////
}

//////////////////////////////////////////////////////////////

Но хотелось бы одной фразой (не факт, что так будет удобнее :slight_smile:
Типа : Эффект шестнадцать яркость три скорость два цвет четыре
Но как тогда разбирать проще строку? Ведь в ней уже четыре числа надо извлечь.

Моя жена не одобрит такое нововведение :sleepy:

Да почему? В смартфон можно спокойно говорить, он уловит.
Сейчас модно (по жене вижу) наговаривать сообщения. Можно считать это сообщением…ёлке.

Дошло до меня как сделать вместо отдельных команд полное задание ёлке. Надо так:

Спойлер
//ФУНКЦИИ ПРИЁМА И ОБРАБОТКИ КОМАНД-ДАННЫХ
void CheckSerial()
{
  //////////////////////////////////////////////////////////////////////////////
  while (SerialBT.available())//считываем строку данных пока они поступают
  {
    char inChar = (char)SerialBT.read();
    if (inChar == '\n'||inChar == ' ')     //если окончание строки или пробел
    {
      MakeCmd();//обрабатываем её данной функцией
      break;
    }
    else inputString += inChar;// иначе удлиняем строку на один символ
  }
 
}
/////////////////////////////////////////////////////////////////////////////////
void MakeCmd()
{
  int y = inputString.length(); // присваиваем переменной у число символов в строке
  String cmd = inputString; // передаём строку другой переменной
  inputString = "";//"обнуляем переменную"
  //////
// Serial.print(cmd);
 if(cmd=="ВЫКЛЮЧИ"){ effekt=0;//выключение ёлки 
 }
 //
  if(cmd=="ЭФФЕКТ"||cmd=="EFFECT"){komanda_=1;}
  if(cmd=="ЯРКОСТЬ"){komanda_=2;}
  if(cmd=="ПАУЗА"){komanda_=3;}
  if(cmd=="ЦВЕТ"||cmd=="СВЕТ"){komanda_=4;}
  if(cmd=="КАДР"){komanda_=5;}
  if(cmd=="АВТОМАТ"){komanda_=6;}
//
 int pos=cmd.toInt();//преобразовать строку в число 
 if(komanda_==1&&pos>0&&pos<=23){effekt=pos;pos=0;komanda_=0;}
 if(komanda_==2&&pos>0&&pos<=10){M=pos;pos=0;komanda_=0;}
 if(komanda_==3&&pos>=30&&pos<=210){t=pos;pos=0;komanda_=0;}
 if(komanda_==4&&pos>0&&pos<6){cvet=pos-1;pos=0;komanda_=0;}
 if(komanda_==5&&cmd=="СТОП"){FL_show=false;pos=0;komanda_=0;}
 if(komanda_==5&&cmd=="ПУСК"){FL_show=true;pos=0;komanda_=0;}
 if(komanda_==6&&cmd=="ДА"){FL_=true;pos=0;komanda_=0;}
 if(komanda_==6&&cmd=="НЕТ"){FL_=false;pos=0;komanda_=0;}                                
//
}

Тогда задание: Эффект четыре, яркость пять, пауза сто, цвет три - будет выполнено сразу.
… Ну иногда распознаватель речи капризничает и выдаёт числа текстом, а не цифрами, путает слова тоже иногда :slight_smile:


Сделал вторую версию рассеивателя света на основе плафона от старых люминесцентных ламп.



Получил монолитный поликарбонат с призмой в рассеивателе. Своеобразное восприятие при разных углах.

Решил добавить к дистанционным вариантам управления сенсорное, на 4 “кнопки” самодельных:

Ни фига не работают сенсоры вместе с лентой, а только без :slight_smile:
Придётся одной кнопкой как то управлять пробовать :slight_smile: