Реструктурировать код для многозадачности Neopixel + ИК-пульт + ардуино

Во-первых, я совершенно новичок как в arduino, так и в программировании. Во-вторых, как для хорошего новичка, я пытаюсь построить довольно сложный проект. Итак, любая помощь и предложение приветствуются. Цель: осветить полку двумя светодиодными лентами по 1 метру с двумя разными анимациями «огонь». И дистанционно управляйте Arduino, чтобы переключаться между эффектами и цветами.

Состояние проекта на данный момент: ИК-с-неопикселем

Я узнал, что одного Arduino недостаточно для декодирования ИК-сигналов и управления светодиодными лентами. Итак, я использую два. Успешно слил 2 скетча: тот, который получает, декодирует и отправляет ИК-сигнал на другой ардуино.

И на основе руководства https. ://learn.adafruit.com/multi-tasking-the-arduino-part-3/put-it-all-together-dot-dot-dot

Мне удалось управлять светодиодами с помощью ИК-пульта дистанционного управления. Вот код, который используется в настоящее время (немного раздут, так как он содержит все эффекты из учебника.)

Вот пока код:

#include <Adafruit_NeoPixel.h>
#include <Wire.h>
#define I2C_SLAVE_ADDRESS 8

// Коды кнопок пульта дистанционного управления
const unsigned int BUTTON_1 = 0xFD08F7; 
const unsigned int BUTTON_2 = 0xFD8877;
const unsigned int BUTTON_3 = 0xFD48B7;
const unsigned int BUTTON_4 = 0xFD28D7;
const unsigned int BUTTON_5 = 0xFDA857;
const unsigned int BUTTON_6 = 0xFD6897;
const unsigned int BUTTON_7 = 0xFD18E7;
const unsigned int BUTTON_8 = 0xFD9867;
const unsigned int BUTTON_9 = 0xFD58A7;
const unsigned int BUTTON_UP = 0xFD50AF;
const unsigned int BUTTON_DOWN = 0xFD10EF;
const unsigned int BUTTON_VOL = 0xFD906F;

int t = 0; // переменная для переключения светодиодных колец с пульта
int b = 255; // начальное значение яркости
// ИК полученный код
unsigned int irCode = 0;            //здесь заканчивается код
// Поддерживаемые типы шаблонов:
enum  pattern { NONE, RAINBOW_CYCLE, THEATER_CHASE, COLOR_WIPE, SCANNER, FADE, FIRE };
// Поддерживаемые направления патерна:
enum  direction { FORWARD, REVERSE };

// Класс NeoPattern — производный от класса Adafruit_NeoPixel
class NeoPatterns : public Adafruit_NeoPixel
{
    public:

    // Переменные-члены:
    pattern  ActivePattern;  // какой паттерн работает
    direction Direction;     // направление запуска паттерна

    unsigned long Interval;   // миллисекунды между обновлениями
    unsigned long lastUpdate; // последнее обновление позиции

    uint32_t Color1, Color2;  // Какие цвета используются
    uint16_t TotalSteps;  // общее количество шагов в паттерне
    uint16_t Index;  // текущий шаг в паттерне

    void (*OnComplete)();  // Обратный вызов по завершению паттерна

    // Конструктор - вызывает конструктор базового класса для инициализации полосы
    NeoPatterns(uint16_t pixels, uint8_t pin, uint8_t type, void (*callback)())
    :Adafruit_NeoPixel(pixels, pin, type)
    {
        OnComplete = callback;
    }

    // Обновить шаблон
    void Update()
    {
        if((millis() - lastUpdate) > Interval) // время обновления
        {
            lastUpdate = millis();
            switch(ActivePattern)
            {
                case RAINBOW_CYCLE:
                    RainbowCycleUpdate();
                    break;
                case THEATER_CHASE:
                    TheaterChaseUpdate();
                    break;
                case COLOR_WIPE:
                    ColorWipeUpdate();
                    break;
                case SCANNER:
                    ScannerUpdate();
                    break;
                case FADE:
                    FadeUpdate();
            case FIRE:
                    RainbowCycleUpdate();   
                    break;
                default:
                    break;
            }
        }
    }

    // Увеличиваем индекс и сбрасываем в конце
    void Increment()
    {
        if (Direction == FORWARD)
        {
           Index++;
           if (Index >= TotalSteps)
            {
                Index = 0;
                if (OnComplete != NULL)
                {
                    OnComplete(); // вызываем обратный вызов завершения
                }
            }
        }
        else // Направление == ОБРАТНОЕ
        {
            --Index;
            if (Index <= 0)
            {
                Index = TotalSteps-1;
                if (OnComplete != NULL)
                {
                    OnComplete(); // вызываем обратный вызов завершения
                }
            }
        }
    }

    // Обратное направление паттерна
    void Reverse()
    {
        if (Direction == FORWARD)
        {
            Direction = REVERSE;
            Index = TotalSteps-1;
        }
        else
        {
            Direction = FORWARD;
            Index = 0;
        }
    }

    // Инициализация для RainbowCycle
    void RainbowCycle(uint8_t interval, direction dir = FORWARD)
    {
        ActivePattern = RAINBOW_CYCLE;
        Interval = interval;
        TotalSteps = 255;
        Index = 0;
        Direction = dir;
    }


    // Обновить шаблон радужного цикла
    void RainbowCycleUpdate()
    {
        for(int i=0; i< numPixels(); i++)
        {
            setPixelColor(i, Wheel(((i * 256 / numPixels()) + Index) & 255));
        }
        show();
        Increment();
    }

    // Инициализация для театральной погони
    void TheaterChase(uint32_t color1, uint32_t color2, uint8_t interval, direction dir = FORWARD)
    {
        ActivePattern = THEATER_CHASE;
        Interval = interval;
        TotalSteps = numPixels();
        Color1 = color1;
        Color2 = color2;
        Index = 0;
        Direction = dir;
   }

    // Обновить паттерн «Театральная погоня»
    void TheaterChaseUpdate()
    {
        for(int i=0; i< numPixels(); i++)
        {
            if ((i + Index) % 3 == 0)
            {
                setPixelColor(i, Color1);
            }
            else
            {
                setPixelColor(i, Color2);
            }
        }
        show();
        Increment();
    }

    // Инициализация для ColorWipe
    void ColorWipe(uint32_t color, uint8_t interval, direction dir = FORWARD)
    {
        ActivePattern = COLOR_WIPE;
        Interval = interval;
        TotalSteps = numPixels();
        Color1 = color;
        Index = 0;
        Direction = dir;
    }


    // Обновить шаблон цветного вытеснения
    void ColorWipeUpdate()
    {
        setPixelColor(Index, Color1);
        show();
        Increment();
    }

    // Инициализация для СКАНЕРА
    void Scanner(uint32_t color1, uint8_t interval)
    {
        ActivePattern = SCANNER;
        Interval = interval;
        TotalSteps = (numPixels() - 1) * 2;
        Color1 = color1;
        Index = 0;
    }

    // Обновить шаблон сканера
    void ScannerUpdate()
    {
        for (int i = 0; i < numPixels(); i++)
        {
            if (i == Index)  // Сканируем пиксель вправо
            {
                 setPixelColor(i, Color1);
            }
            else if (i == TotalSteps - Index) // Сканируем пиксель влево
            {
                 setPixelColor(i, Color1);
            }
            else // Затухающий хвост
            {
                 setPixelColor(i, DimColor(getPixelColor(i)));
            }
        }
        show();
        Increment();
    }

    // Инициализация для Fade
    void Fade(uint32_t color1, uint32_t color2, uint16_t steps, uint8_t interval, direction dir = FORWARD)
    {
        ActivePattern = FADE;
        Interval = interval;
        TotalSteps = steps;
        Color1 = color1;
        Color2 = color2;
        Index = 0;
        Direction = dir;
    }

    // Обновить шаблон затухания
    void FadeUpdate()
    {
        // Вычислить линейную интерполяцию между Color1 и Color2
        // Оптимизировать порядок операций, чтобы свести к минимуму ошибку усечения
        uint8_t red = ((Red(Color1) * (TotalSteps - Index)) + (Red(Color2) * Index)) / TotalSteps;
        uint8_t green = ((Green(Color1) * (TotalSteps - Index)) + (Green(Color2) * Index)) / TotalSteps;
        uint8_t blue = ((Blue(Color1) * (TotalSteps - Index)) + (Blue(Color2) * Index)) / TotalSteps;

        ColorSet(Color(red, green, blue));
        show();
        Increment();
    }


    // Рассчитываем версию цвета с затемнением на 50% (используется ScannerUpdate)
    uint32_t DimColor(uint32_t color)
    {
        // Сдвигаем компоненты R, G и B на один бит вправо
        uint32_t dimColor = Color(Red(color) >> 1, Green(color) >> 1, Blue(color) >> 1);
        return dimColor;
    }

    // Задаем цвет всем пикселям (синхронно)
    void ColorSet(uint32_t color)
    {
        for (int i = 0; i < numPixels(); i++)
        {
            setPixelColor(i, color);
        }
        show();
    }

    // Возвращает красный компонент 32-битного цвета
    uint8_t Red(uint32_t color)
    {
        return (color >> 16) & 0xFF;
    }

    // Возвращает зеленый компонент 32-битного цвета
    uint8_t Green(uint32_t color)
    {
        return (color >> 8) & 0xFF;
    }

    // Возвращает синий компонент 32-битного цвета
    uint8_t Blue(uint32_t color)
    {
        return color & 0xFF;
    }

    // Введите значение от 0 до 255, чтобы получить значение цвета.
    // Цвета — это переход r — g — b — обратно к r.
    uint32_t Wheel(byte WheelPos)
    {
        WheelPos = 255 - WheelPos;
        if(WheelPos < 85)
        {
            return Color(255 - WheelPos * 3, 0, WheelPos * 3);
        }
        else if(WheelPos < 170)
        {
            WheelPos -= 85;
            return Color(0, WheelPos * 3, 255 - WheelPos * 3);
        }
        else
        {
            WheelPos -= 170;
            return Color(WheelPos * 3, 255 - WheelPos * 3, 0);
        }
    }

   // яркость, возможно, нужно доработать
   void incbrigtness() {
   if (b >= 255) {
      b = 255;
   setBrightness(b);}
   else {b = b + 10;   
   setBrightness(b);}
   }

   void decbrigtness() {
   if (b <= 5) {
      b = 5;
   setBrightness(b);}
   else {b = b - 10;   
   setBrightness(b);}
   }

};

void Ring1Complete();
void Ring2Complete();

void receiveEvent(int numBytes) {//декодировать ИК-сигнал
  byte byte1 = Wire.read();
  byte byte2 = Wire.read();
  irCode = byte2;
  irCode = irCode << 8;
  irCode = irCode | byte1;
}                                                                        

// Определяем несколько NeoPatterns для двух колец и палки
// а также некоторые подпрограммы завершения
NeoPatterns Ring1(24, 6, NEO_GRB + NEO_KHZ800, &Ring1Complete); // параметры светодиода
NeoPatterns Ring2(24, 7, NEO_GRB + NEO_KHZ800, &Ring2Complete);    

// Инициализируем все и готовимся к запуску
void setup()
{
   Serial.begin(9600);
   Serial.println("my-main-program starting!");
   Wire.begin(I2C_SLAVE_ADDRESS);
   Wire.onReceive(receiveEvent);
   Serial.println("my-main-program successfully started!");
 }
// Основной цикл
void loop()
{
    // Обновить кольца.
   if (irCode) {             //Читает ir-код
    handleButton(irCode);
    irCode = 0;
  }
 Ring1.Update();
 Ring2.Update();
 }

void handleButton(unsigned int irCode) {
  switch (irCode) {
   case BUTTON_UP:      // кнопка вверх будет управлять светодиодной полосой 1, кнопка 2 будет управлять полосой 2, кнопка 3 обеими.
      t = 1;
     Serial.println(t);    
     break;
   case BUTTON_DOWN:
      t = 2;
     Serial.println(t);
      break;
   case BUTTON_VOL:
     t = 0;
      Serial.println(t);
     break;
   case BUTTON_1:
     if (t == 1) {    // здесь тоже нужны доработки
      Serial.println(t); 
     Ring1.TheaterChase(Ring1.Color(0,255,0), Ring1.Color(0,0,50), 100);
      }
     else if (t == 2) {
     Serial.println(t); 
     Ring2.TheaterChase(Ring1.Color(255,0,0), Ring1.Color(0,50,0), 100);
      }
     else {      // какие-то случайные эффекты для тестирования
     Serial.println(t); 
      Ring1.TheaterChase(Ring1.Color(0,0,255), Ring1.Color(50,0,0), 100);
     Ring2.TheaterChase(Ring1.Color(0,0,255), Ring1.Color(50,0,0), 100);
      }
   break;
    case BUTTON_2:
      Serial.println("2");
       Ring1.Scanner(Ring1.Color(255,0,0), 55);
     break;
    case BUTTON_3:
     Ring1.ColorWipe(Ring1.Color(0,255,255), 55);
     Serial.println("3");
      break;
    case BUTTON_4:
      Ring1.Scanner(Ring1.Color(0,255,255), 55);
     Serial.println("4");
      break;
    case BUTTON_5:
      Serial.println("5");
      break;
    case BUTTON_6:
      Serial.println("6");
      break;
    case BUTTON_7:
      Serial.println("7");
      break;
    case BUTTON_8:         // проверить яркость здесь.
   Ring1.incbrigtness();
   Serial.println(b);
     break;
    case BUTTON_9:
   Ring1.decbrigtness();
   Serial.println(b);
     break;
    default:
      Serial.print("Unrecognized code received: 0x");
      Serial.println(irCode, HEX);
      break;       
  }     
}


//------------------------------------------------ ------------
//Подпрограммы завершения - вызываются по завершению шаблона
//------------------------------------------------ ------------

// Обратный вызов завершения Ring1
void Ring1Complete() // действительно не нужно делать ничего конкретного после завершения цикла, поэтому следует вернуться к обновлению.
{Ring1.Update(); }

void Ring2Complete()
{Ring2.Update(); }

Проблема: Мне нужна помощь, как реструктурировать 2 разных эффекта. чтобы вписаться в приведенный выше код. Циклы заменены на millis() и задержку с помощью средства обновления.

вот первый пожарный код от danesparza:

#include <Adafruit_NeoPixel.h>
#define PIN 6

Adafruit_NeoPixel strip = Adafruit_NeoPixel(29, PIN, NEO_GRB + NEO_KHZ800);


void setup() {
  strip.begin();
  strip.setBrightness(brightness);
  strip.show(); // Инициализируем все пиксели выключенными
}

void loop() {

  // Раскомментируйте одно из этих значений RGB (красный, зеленый, синий) для
  // установить основной цвет пламени. Цвет будет мерцать
  // на основе начального базового цвета

  // Обычное (оранжевое) пламя:
  // int r = 226, g = 121, b = 35;

  // Фиолетовое пламя:
  // int r = 158, g = 8, b = 148;

  // Зеленое пламя:
  int r = 74, g = 150, b = 12;

  // Мерцание, основанное на наших исходных значениях RGB
  for(int i=0; i<strip.numPixels(); i++) {
    int flicker = random(0,55);
    int r1 = r-flicker;
    int g1 = g-flicker;
    int b1 = b-flicker;
    if(g1<0) g1=0;
    if(r1<0) r1=0;
    if(b1<0) b1=0;
    strip.setPixelColor(i,r1,g1, b1);
  }
  strip.show();

  // Отрегулируйте здесь задержку, если хотите. Прямо сейчас он рандомизирует
  // задержка переключения цвета для придания реалистичности
  delay(random(10,113));
}

И волновой эффект, который требует реструктуризации от suhajdab

#include <Adafruit_NeoPixel.h>
#define PIN 2
#define Pixels 24
#define BG 1
Adafruit_NeoPixel strip = Adafruit_NeoPixel(Pixels, PIN, NEO_GRB + NEO_KHZ800);


int color;
int center = 0;
int step = -1;
int maxSteps = 16;
float fadeRate = 0.6;
int diff;

//фоновый цвет
uint32_t currentBg = random(256);
uint32_t nextBg = currentBg;

void setup() {
  strip.begin();
  strip.show(); // Инициализируем все пиксели выключенными
}

void loop () {
  ripple();
}

void ripple() {
  if (BG){
    if (currentBg == nextBg) {
      nextBg = random(256);
    } 
    else if (nextBg > currentBg) {
      currentBg++;
    } else {
      currentBg--;
    }
    for(uint16_t l = 0; l < Pixels; l++) {
      strip.setPixelColor(l, Wheel(currentBg, 0.1));
    }
  } else {
    for(uint16_t l = 0; l < Pixels; l++) {
      strip.setPixelColor(l, 0, 0, 0);
    }
  }


  if (step == -1) {
    center = random(Pixels);
    color = random(256);
    step = 0;
  }



  if (step == 0) {
    strip.setPixelColor(center, Wheel(color, 1));
    step ++;
  } 
  else {
    if (step < maxSteps) {
      strip.setPixelColor(wrap(center + step), Wheel(color, pow(fadeRate, step)));
      strip.setPixelColor(wrap(center - step), Wheel(color, pow(fadeRate, step)));
      if (step > 3) {
        strip.setPixelColor(wrap(center + step - 3), Wheel(color, pow(fadeRate, step - 2)));
        strip.setPixelColor(wrap(center - step + 3), Wheel(color, pow(fadeRate, step - 2)));
      }
      step ++;
    } 
    else {
      step = -1;
    }
  }

  strip.show();
  delay(50);
}


int wrap(int step) {
  if(step < 0) return Pixels + step;
  if(step > Pixels - 1) return step - Pixels;
  return step;
}



// Введите значение от 0 до 255, чтобы получить значение цвета.
// Цвета — это переход r — g — b — обратно к r.
uint32_t Wheel(byte WheelPos, float opacity) {

  if(WheelPos < 85) {
    return strip.Color((WheelPos * 3) * opacity, (255 - WheelPos * 3) * opacity, 0);
  } 
  else if(WheelPos < 170) {
    WheelPos -= 85;
    return strip.Color((255 - WheelPos * 3) * opacity, 0, (WheelPos * 3) * opacity);
  } 
  else {
    WheelPos -= 170;
    return strip.Color(0, (WheelPos * 3) * opacity, (255 - WheelPos * 3) * opacity);
  }
}

Итак, может ли кто-нибудь помочь мне, как включить эти 2 эффекта, основанные на руководстве (к сожалению, не очень помогающие при моем нынешнем уровне навыков) в первый код? Большое спасибо.

, 👍1


1 ответ


1

Во второй части вы видите void ripple(){, который похож на void loop(){, но запускается только при его вызове.

Вы можете объединить эти коды, поместив оператор if после вашего void loop(){, чтобы проверить, нужно ли запускать цикл или пульсацию. Вы можете просто использовать логическое значение для этого. Например:`

void loop(){
  if(state == true){  //я вызвал логическое состояние
    ripple();  // это вызывает функцию ripple и запускает void ripple()
  }

  // вот остальная часть цикла, она выполняется, если состояние != true.
  // вы можете изменить состояние где угодно.
}

Вы даже можете создать новую функцию на основе того, что в данный момент находится в вашем цикле. Пример:

void loop(){
  if(state == 1){
    originalLoop(); //то, что изначально было в вашем цикле, но теперь как отдельная функция
  }
  else if(state == 2){
    ripple();
  }

  //остальная часть цикла, в котором вы можете изменить состояние на 1 или 2, как хотите
}

Надеюсь, это немного поможет :)

,

Кроме того, почему вы используете два Arduinos? Просто используйте внешний источник питания для питания NeoPixels и используйте один Arduino, чтобы делать практически все., @Len

Поскольку есть много тем, как декодирование ИК-сигнала и Neopixel не будут работать вместе, потому что IR основан на прерываниях, а Neopixel его отключает. И в основном из-за того, что я не смог найти хороший пример, когда один arduino запускает оба., @shdow