WS2812 Лестничное освещение — горят не все светодиоды

Я делаю проект лестничного освещения на основе Arduino, и у меня есть небольшая проблема с кодом. Я использовал код Саймона Джоветта с инструктируемого веб-сайта, который после пара настроек работает идеально. Помимо заключительного раздела, эффект водопада. В оригинальном проекте использовалось 117 светодиодов WS2812, я использую 188. Когда триггер белого светодиода бежит вверх или вниз по лестнице, они нормально работают на всех 188 светодиодах, также загораются светодиоды дыхания на первом и последнем светодиодах. Но эффект водопада останавливается на светодиоде 117. Может ли кто-нибудь увидеть, есть ли где-то еще, что мне нужно изменить количество светодиодов в коде, кроме этого Adafruit_NeoPixel strip = Adafruit_NeoPixel(188, ПИН, NEO_GRB+NEO_KHZ800);? Спасибо

// "Шик" вверх по лестнице Саймон Джоуэтт, ноябрь 2014 г.
// Благодаря библиотеке Neopxel от Adafruit


#include <Adafruit_NeoPixel.h>
//#определить LDRSensor 1
#define PIN 3

// Параметр 1 = количество пикселей в полосе
// Параметр 2 = номер вывода Arduino (большинство допустимо)
// Параметр 3 = флаги типа пикселя, суммируйте по мере необходимости:
// NEO_KHZ800 Битовый поток 800 кГц (большинство продуктов NeoPixel со светодиодами WS2812)
// NEO_KHZ400 400 кГц (классические 'v1' (не v2) пиксели FLORA, драйверы WS2811)
// Пиксели NEO_GRB подключены к битовому потоку GRB (большинство продуктов NeoPixel)
// Пиксели NEO_RGB подключаются к битовому потоку RGB (пиксели v1 FLORA, а не v2)
Adafruit_NeoPixel strip = Adafruit_NeoPixel(188, PIN, NEO_GRB + NEO_KHZ800);

// ВАЖНО: чтобы снизить риск выгорания NeoPixel, добавьте конденсатор 1000 мкФ
// провода питания пикселя, добавить резистор 300 - 500 Ом на ввод данных первого пикселя
// и минимизировать расстояние между Arduino и первым пикселем. Избегайте подключения
// на действующей цепи... если необходимо, сначала подключите GND.

// Настраиваем переменные
 unsigned long timeOut=60000; // временная метка для запоминания момента срабатывания PIR.
 int downUp = 0;              // переменная для запоминания направления движения вверх или вниз по лестнице
 int alarmPinTop = 5;        // ПИК наверху лестницы
 int alarmPinBottom =7;      // ПИК внизу лестницы
 int alarmValueTop = LOW;    // Переменная для хранения статуса PIR
 int alarmValueBottom = LOW; // Переменная для хранения статуса PIR
 int ledPin = 13;           // Светодиод на плате Arduino мигает при активации PIR
 int LDRSensor = A0;        // Светозависимый резистор
 int LDRValue = 0;          // Переменная для хранения значения LDR
 int colourArray[350];      // Массив для хранения значений RGB
 int change = 1;            // используется для «дыхания» светодиода
 int breathe = 0;           // используется для «дыхания» светодиода


void setup() {
   strip.begin();
   strip.show(); // Инициализируем все пиксели выключенными
   Serial.begin (9600);  // требуется только для отладки
   pinMode(ledPin, OUTPUT);  // инициализируем встроенный светодиод на контакте 13 в качестве индикатора
   pinMode(alarmPinTop, INPUT_PULLUP);     // для PIR наверху лестницы инициализируем входной контакт и используем внутренний резистор
   pinMode(alarmPinBottom, INPUT_PULLUP);  // для PIR у подножия лестницы инициализируем входной контакт и используем внутренний резистор
   delay (100); // датчику требуется 2 секунды, чтобы просканировать область вокруг него, прежде чем он сможет
   //обнаружение присутствия инфракрасного излучения.
   for (int i=0 ;i < 350; i++)  { // инициализируем массив colorArray нулем
   colourArray[i]=0; 
   } 
}

void loop() {

  LDRValue = analogRead(LDRSensor);
  Serial.println(LDRValue);

  if (timeOut+57000 < millis()) {        // состояние простоя — «дышите» верхним и нижним светодиодами, чтобы показать, что программа зацикливается

     breathe = breathe + change;
     strip.setPixelColor(0,0,0,breathe);
     strip.setPixelColor(187,0,0,breathe);
     strip.show();
     if (breathe == 100 || breathe == 0) change = -change;      // вдыхаем светодиод от 0 = выключено до 100 = достаточно ярко
     if (breathe == 100 || breathe == 0) delay (300);           // Пауза в начале и в конце каждого вдоха
     delay(25); 
  }

  if (LDRValue > 1010) {        // включайте светодиоды только ночью, когда LDR определяет условия низкой освещенности - возможно, вам придется изменить число в зависимости от ваших обстоятельств!

    alarmValueTop = digitalRead(alarmPinTop);        // Постоянно опрашиваем PIR наверху лестницы
    alarmValueBottom = digitalRead(alarmPinBottom);  // Постоянно опрашиваем PIR внизу лестницы

    if (alarmValueTop == HIGH && downUp != 2)  {      // 2-й член позволяет постоянно сбрасывать timeOut, если кто-то задерживается наверху лестницы перед спуском, но не позволяет нижнему PIR сбрасывать timeOut, когда вы пройти мимо него.
      timeOut=millis();  // Временная метка при срабатывании PIR. После этого начнется цикл светодиодов.
      downUp = 1;
      topdown();         // подсвечиваем полосу сверху вниз
    }

    if (alarmValueBottom == HIGH && downUp != 1)  {    // 2-й член позволяет постоянно сбрасывать timeOut, если кто-то задерживается внизу лестницы перед спуском, но не позволяет верхнему PIR сбрасывать timeOut, когда вы пройти мимо него.
      timeOut=millis();    // Временная метка при срабатывании PIR. После этого начнется цикл светодиодов.
      downUp = 2;
      bottomup();         // подсвечиваем полосу снизу вверх
    }
    if (timeOut+10000 < millis() && timeOut+15000 < millis()) {    // выключаем светодиоды в направлении движения.
       if (downUp == 1) {
          colourWipeDown(strip.Color(0, 0, 0), 50); // Выключенный
       }
       if (downUp == 2)  {
        colourWipeUp(strip.Color(0, 0, 0), 50);   // Выключенный
       }
      downUp = 0;
      // for (int i=0 ;i < 350; i++) { // В зависимости от ваших предпочтений вы можете включить этот цикл для очистки colorArray
      // colorArray[i]=0;
      // }
    }

    if (timeOut+15000 < millis() && timeOut+54999 > millis()) waterfall();    // Эффект водопада, который будет воспроизводиться между этими моментами времени после срабатывания PIR.

    if (timeOut+55000 < millis() && timeOut+56999 > millis()) fade();   // Гасить/выключать светодиоды
  }
}

 void topdown() {
    Serial.println ("detected top");                // Полезное отладочное сообщение
    colourWipeDown(strip.Color(50, 50, 30), 10);    // Теплый белый
    for(int i=0; i<3; i++) {                        // Полезная индикация отладки дважды мигает светодиодом на плате Arduino
      digitalWrite(ledPin,HIGH);
      delay(200);
      digitalWrite(ledPin,LOW);
      delay(200);
    }
 }

 void bottomup() {
    Serial.println ("detected bottom");          // Полезное отладочное сообщение
    colourWipeUp(strip.Color(50, 50, 30), 10);   // Теплый белый
    for(int i=0; i<3; i++) {                     // Полезная индикация отладки дважды мигает светодиодом на плате Arduino
      digitalWrite(ledPin,HIGH);
      delay(200);
      digitalWrite(ledPin,LOW);
      delay(200);
    }
  }

 // Заливаем точки одну за другой цветом
 void colourWipeDown(uint32_t c, uint8_t wait) {
   for(uint16_t i=0; i<strip.numPixels(); i++) {
      strip.setPixelColor(i, c);
      strip.show();
      delay(wait);
  }
 }

 // Заливаем точки одну за другой цветом
 void colourWipeUp(uint32_t c, uint8_t wait) {
   for(uint16_t i=strip.numPixels(); i < -1; i--) {
      strip.setPixelColor(i, c);
      strip.show();
      delay(wait);
  }
 }

 // Воспроизведение эффекта водопада
 void waterfall(){
    for(int i= 347; i>-1; i--) {            // Сдвигаем цвета RGB вниз в colorArray[]
        colourArray[i+3]=colourArray[i];
        }
      colourArray[0] = random(0,20);        // Генерируем красный компонент 1-го светодиода наверху лестницы
      colourArray[1] = random(0,40);        // Генерируем зеленый компонент 1-го светодиода наверху лестницы
      colourArray[2] = random(10,70);       // Генерируем синий компонент 1-го светодиода наверху лестницы

      for (int k=0; k<351; k=k+3)   {       // Устанавливаем и отправляем цвета на полосу
       uint32_t c = strip.Color(colourArray[k],colourArray[k+1],colourArray[k+2]);
       strip.setPixelColor(((k+3)/3)-1,c);
       }

      strip.show();      // и отображаем результат
      delay(35);         // задержка для имитации проточной воды
 }

 // Воспроизвести затухание светодиода
 void fade(){
 for (int j = 0; j <70; j++) {
   for(int i=350; i>-1; i--) {
     colourArray[i]=colourArray[i]-1;      // уменьшить интенсивность света на 1
     if (colourArray[i] <= 0 ) colourArray[i] = 0;
   }
   for (int k=0; k<351; k=k+3)   {
     uint32_t c = strip.Color(colourArray[k],colourArray[k+1],colourArray[k+2]);
     strip.setPixelColor(((k+3)/3)-1,c);
   }
  strip.show();
  delay(60);
  }
 breathe = 0;
 change = 1; 
 }

, 👍0

Обсуждение

Добавлена ссылка на веб-сайт Instructable. Пожалуйста, прокомментируйте, если не прав., @st2000

Есть ли конкретная причина, по которой вы используете массив int для значений цвета светодиодов вместо типизированного массива CRGB, который используется во всех примерах NeoPixel?, @chrisl


2 ответа


1

Эта строка в коде:

 int colourArray[350];      // Массив для хранения значений RGB

...содержит 3 значения для каждого светодиода. А вот 350/3 это 116,67. Это значение, вероятно, должно было быть 117 * 3 = 351 для исходного кода. А для размещенного кода с использованием 188 светодиодов это, вероятно, должно быть 188 * 3 = 546.

Эта же проблема с индексацией в colorArray повторяется еще несколько раз в коде, где массив очищается, присваиваются случайные значения и используются для управления светодиодами. Обычно хорошо написанный код использует директиву «#define» для установки таких значений один раз в начале кода, что позволяет компилятору вносить коррективы при компиляции/загрузке скетча Arduino.

Та же самая "#define" для скетчей Arduino как используется в программировании на C. (Поскольку скетчи Arduino на самом деле являются программами на C.) Обычно определения находятся в начале файла. Определения — это способ замены значимого имени на что-то, что сначала трудно понять. Мы даже можем использовать немного математики, чтобы избежать ошибок, поскольку компилятор решит их за нас перед загрузкой кода:

#define LED_NUMBER_OF (3*188)

Позже в коде мы можем использовать определение:

int colourArray[LED_NUMBER_OF];      // Массив для хранения значений RGB

Еще позже в коде мы можем снова использовать определение:

for (int i=0 ;i < LED_NUMBER_OF; i++)  { // инициализируем массив colorArray нулем
  colourArray[i]=0; 
} 
,

Спасибо, st2000, я изменил код, как вы предложили, мне нужно будет сделать то же самое для раздела водопада?, @DapurDan

Вероятно. Быстрый (и, может быть, достаточно хороший) подход состоит в том, чтобы найти число 350 и решить, можно ли добиться того, что вы хотите, заменив 546 или определить LED_NUMBER_OF того, что вы пошли по этому пути., @st2000


0

Я только что попробовал отредактированный код, но теперь светодиоды вообще не работают?? Я уверен, что я сделал что-то не так.

#include <Adafruit_NeoPixel.h>

#define PIN 3

#define LED_NUMBER_OF (3*188)

// Параметр 1 = количество пикселей в полосе
// Параметр 2 = номер вывода Arduino (большинство допустимо)
// Параметр 3 = флаги типа пикселя, суммируйте по мере необходимости:
// NEO_KHZ800 Битовый поток 800 кГц (большинство продуктов NeoPixel со светодиодами WS2812)
// NEO_KHZ400 400 кГц (классические 'v1' (не v2) пиксели FLORA, драйверы WS2811)
// Пиксели NEO_GRB подключены к битовому потоку GRB (большинство продуктов NeoPixel)
// Пиксели NEO_RGB подключаются к битовому потоку RGB (пиксели v1 FLORA, а не v2)
Adafruit_NeoPixel strip = Adafruit_NeoPixel(188, PIN, NEO_GRB + NEO_KHZ800);

// ВАЖНО: чтобы снизить риск выгорания NeoPixel, добавьте конденсатор 1000 мкФ
// провода питания пикселя, добавить резистор 300 - 500 Ом на ввод данных первого пикселя
// и минимизировать расстояние между Arduino и первым пикселем. Избегайте подключения
// на действующей цепи... если необходимо, сначала подключите GND.

// Настраиваем переменные
 unsigned long timeOut=60000; // временная метка для запоминания момента срабатывания PIR.
 int downUp = 0;              // переменная для запоминания направления движения вверх или вниз по лестнице
 int alarmPinTop = 5;        // ПИК наверху лестницы
 int alarmPinBottom =7;      // ПИК внизу лестницы
 int alarmValueTop = LOW;    // Переменная для хранения статуса PIR
 int alarmValueBottom = LOW; // Переменная для хранения статуса PIR
 int ledPin = 13;           // Светодиод на плате Arduino мигает при активации PIR
 int LDRSensor = A0;        // Светозависимый резистор
 int LDRValue = 0;          // Переменная для хранения значения LDR
 int colourArray[LED_NUMBER_OF];      // Массив для хранения значений RGB
 int change = 1;            // используется для «дыхания» светодиода
 int breathe = 0;           // используется для «дыхания» светодиода


void setup() {
   strip.begin();
   strip.show(); // Инициализируем все пиксели выключенными
   Serial.begin (9600);  // требуется только для отладки
   pinMode(ledPin, OUTPUT);  // инициализируем встроенный светодиод на контакте 13 в качестве индикатора
   pinMode(alarmPinTop, INPUT_PULLUP);     // для PIR наверху лестницы инициализируем входной контакт и используем внутренний резистор
   pinMode(alarmPinBottom, INPUT_PULLUP);  // для PIR у подножия лестницы инициализируем входной контакт и используем внутренний резистор
   delay (100); // датчику требуется 2 секунды, чтобы просканировать область вокруг него, прежде чем он сможет
   //обнаружение присутствия инфракрасного излучения.
   for (int i=0 ;i < LED_NUMBER_OF; i++)  { // инициализируем массив цветов нулем
   colourArray[i]=0; 
   } 
}

void loop() {

  LDRValue = analogRead(LDRSensor);
  Serial.println(LDRValue);

  if (timeOut+57000 < millis()) {        // состояние простоя — «дышите» верхним и нижним светодиодами, чтобы показать, что программа зацикливается

     breathe = breathe + change;
     strip.setPixelColor(0,0,0,breathe);
     strip.setPixelColor(187,0,0,breathe);
     strip.show();
     if (breathe == 100 || breathe == 0) change = -change;      // вдыхаем светодиод от 0 = выключено до 100 = достаточно ярко
     if (breathe == 100 || breathe == 0) delay (300);           // Пауза в начале и в конце каждого вдоха
     delay(25); 
  }

  if (LDRValue > 1010) {        // включайте светодиоды только ночью, когда LDR определяет условия низкой освещенности - возможно, вам придется изменить число в зависимости от ваших обстоятельств!

    alarmValueTop = digitalRead(alarmPinTop);        // Постоянно опрашиваем PIR наверху лестницы
    alarmValueBottom = digitalRead(alarmPinBottom);  // Постоянно опрашиваем PIR внизу лестницы

    if (alarmValueTop == HIGH && downUp != 2)  {      // 2-й член позволяет постоянно сбрасывать timeOut, если кто-то задерживается наверху лестницы перед спуском, но не позволяет нижнему PIR сбрасывать timeOut, когда вы пройти мимо него.
      timeOut=millis();  // Временная метка при срабатывании PIR. После этого начнется цикл светодиодов.
      downUp = 1;
      topdown();         // подсвечиваем полосу сверху вниз
    }

    if (alarmValueBottom == HIGH && downUp != 1)  {    // 2-й член позволяет постоянно сбрасывать timeOut, если кто-то задерживается внизу лестницы перед спуском, но не позволяет верхнему PIR сбрасывать timeOut, когда вы пройти мимо него.
      timeOut=millis();    // Временная метка при срабатывании PIR. После этого начнется цикл светодиодов.
      downUp = 2;
      bottomup();         // подсвечиваем полосу снизу вверх
    }
    if (timeOut+10000 < millis() && timeOut+15000 < millis()) {    // выключаем светодиоды в направлении движения.
       if (downUp == 1) {
          colourWipeDown(strip.Color(0, 0, 0), 50); // Выключенный
       }
       if (downUp == 2)  {
        colourWipeUp(strip.Color(0, 0, 0), 50);   // Выключенный
       }
      downUp = 0;
      // for (int i=0 ;i < 350; i++) { // В зависимости от ваших предпочтений вы можете включить этот цикл для очистки colorArray
      // colorArray[i]=0;
      // }
    }

    if (timeOut+15000 < millis() && timeOut+54999 > millis()) waterfall();    // Эффект водопада, который будет воспроизводиться между этими моментами времени после срабатывания PIR.

    if (timeOut+55000 < millis() && timeOut+56999 > millis()) fade();   // Гасить/выключать светодиоды
  }
}

 void topdown() {
    Serial.println ("detected top");                // Полезное отладочное сообщение
    colourWipeDown(strip.Color(50, 50, 30), 10);    // Теплый белый
    for(int i=0; i<3; i++) {                        // Полезная индикация отладки дважды мигает светодиодом на плате Arduino
      digitalWrite(ledPin,HIGH);
      delay(200);
      digitalWrite(ledPin,LOW);
      delay(200);
    }
 }

 void bottomup() {
    Serial.println ("detected bottom");          // Полезное отладочное сообщение
    colourWipeUp(strip.Color(50, 50, 30), 10);   // Теплый белый
    for(int i=0; i<3; i++) {                     // Полезная индикация отладки дважды мигает светодиодом на плате Arduino
      digitalWrite(ledPin,HIGH);
      delay(200);
      digitalWrite(ledPin,LOW);
      delay(200);
    }
  }

 // Заливаем точки одну за другой цветом
 void colourWipeDown(uint32_t c, uint8_t wait) {
   for(uint16_t i=0; i<strip.numPixels(); i++) {
      strip.setPixelColor(i, c);
      strip.show();
      delay(wait);
  }
 }

 // Заливаем точки одну за другой цветом
 void colourWipeUp(uint32_t c, uint8_t wait) {
   for(uint16_t i=strip.numPixels(); i < -1; i--) {
      strip.setPixelColor(i, c);
      strip.show();
      delay(wait);
  }
 }

 // Воспроизведение эффекта водопада
 void waterfall(){
    for(int i= 350; i>-1; i--) {            // Сдвигаем цвета RGB вниз в colorArray[]
        colourArray[i+3]=colourArray[i];
        }
      colourArray[0] = random(0,20);        // Генерируем красный компонент 1-го светодиода наверху лестницы
      colourArray[1] = random(0,40);        // Генерируем зеленый компонент 1-го светодиода наверху лестницы
      colourArray[2] = random(10,70);       // Генерируем синий компонент 1-го светодиода наверху лестницы

      for (int k=0; k<350; k=k+3)   {       // Устанавливаем и отправляем цвета на полосу
       uint32_t c = strip.Color(colourArray[k],colourArray[k+1],colourArray[k+2]);
       strip.setPixelColor(((k+3)/3)-1,c);
       }

      strip.show();      // и отображаем результат
      delay(35);         // задержка для имитации проточной воды
 }

 // Воспроизвести затухание светодиода
 void fade(){
 for (int j = 0; j <70; j++) {
   for(int i=350; i>-1; i--) {
     colourArray[i]=colourArray[i]-1;      // уменьшить интенсивность света на 1
     if (colourArray[i] <= 0 ) colourArray[i] = 0;
   }
   for (int k=0; k<351; k=k+3)   {
     uint32_t c = strip.Color(colourArray[k],colourArray[k+1],colourArray[k+2]);
     strip.setPixelColor(((k+3)/3)-1,c);
   }
  strip.show();
  delay(60);
  }
 breathe = 0;
 change = 1; 
 }
~~~~~~
,

Вероятно, вам нужно было использовать определение LED_NUMBER_OF, где бы вы ни обращались к массиву colorArray. Я вижу, вы изменили 350 на 351 в некоторых местах. Возможно, если вы попытаетесь использовать LED_NUMBER_OF вместо 351. Также обратите внимание, что есть одна строка кода, где вам нужно использовать (LED_NUMBER_OF - 3), потому что вы добавляете 3 к индексу цикла FOR внутри цикла. И плохо обращаться к местоположениям после конца любого массива!, @st2000

Старайтесь не публиковать повторно свой вопрос в качестве ответа. Помните, что веб-сайты по обмену стеком/переполнению стека пытаются стать списком доступных для поиска вопросов и ответов. Это действительно сбивает с толку людей с похожими проблемами., @st2000

если ваша программа работает, вы можете сделать 2 вещи? 1. Удалите этот ответ, и если вам нужно отредактировать свой вопрос, если вы хотите добавить дополнительную информацию, чтобы сделать вопрос лучше. 2. Если ответ правильный (то есть, если ответ помогает устранить проблему в вопросе), отметьте его правильным, чтобы другие могли увидеть, что сработало для вас. Спасибо., @st2000