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;
}
@DapurDan, 👍0
Обсуждение2 ответа
Эта строка в коде:
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
Я только что попробовал отредактированный код, но теперь светодиоды вообще не работают?? Я уверен, что я сделал что-то не так.
#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
- Как объявить массив переменного размера (глобально)
- Программирование Arduino с использованием Python, а не C/C ++
- Загрузка Arduino Nano дает ошибку: avrdude: stk500_recv(): programmer is not responding
- Как справиться с rollover millis()?
- Является ли использование malloc() и free() действительно плохой идеей для Arduino?
- Можно ли сделать несколько функций loop() с помощью Arduino Uno?
- Какие накладные расходы и другие соображения существуют при использовании структуры по сравнению с классом?
- устаревшее преобразование из строковой константы в 'char*'
Добавлена ссылка на веб-сайт Instructable. Пожалуйста, прокомментируйте, если не прав., @st2000
Есть ли конкретная причина, по которой вы используете массив int для значений цвета светодиодов вместо типизированного массива CRGB, который используется во всех примерах NeoPixel?, @chrisl