Реструктурировать код для многозадачности 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 эффекта, основанные на руководстве (к сожалению, не очень помогающие при моем нынешнем уровне навыков) в первый код? Большое спасибо.
@shdow, 👍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, как хотите
}
Надеюсь, это немного поможет :)
- Использовать timer0, не влияя на millis() и micros().
- Торговый автомат Arduino для мониторинга ввода монет в слот во время ожидания ввода пользователя
- Arduino Мигает двумя светодиодами без задержки (количество повторений)
- Сброс Arduino с помощью ПО (каждый день)
- Странное поведение со светодиодной лентой WS2812B RGB
- Та же кнопка одним кликом и двойным кликом
- Какая хорошая альтернатива Arduino Nano, которую можно использовать с Neopixels?
- Как сбросить millis()?
Кроме того, почему вы используете два Arduinos? Просто используйте внешний источник питания для питания NeoPixels и используйте один Arduino, чтобы делать практически все., @Len
Поскольку есть много тем, как декодирование ИК-сигнала и Neopixel не будут работать вместе, потому что IR основан на прерываниях, а Neopixel его отключает. И в основном из-за того, что я не смог найти хороший пример, когда один arduino запускает оба., @shdow