WS2812 + Apds-9960
- Войдите на сайт для отправки комментариев
Всем привет.
Хочу сделать себе светодиодное освещение из 300 светодиодов (WS2812) с переключением эффектов жестами (с помощью датчика Apds-9960).
Сами эффекты беру тут: https://www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/#LEDEffects
Сам тоже писал немного, поосваивал, так сказать, программирование WS2812.
Пока испытываю на 16 светодиодах это дело (ну, жесты, то есть), дабы потом без проблем залить скетч под 300 штук. Начал с простого: с пары эффектов (влево - один эффект, вправо - другой, вниз и вверх - выключить ленту).
Собственно, столкнулся с проблемой прерываний. Если зациклить какой-нибудь эффект, то из него уже не выйти, а если сделать конечным, то он просто пройдет один раз и остановится. Сами эффекты находятся в циклах for(). Как реализовать выход из циклов по событию (при смене жеста)?
Сам код:
#include <Adafruit_NeoPixel.h> #include <Wire.h> // Для работы с шиной I2C #include <SparkFun_APDS9960.h> // Для работы с датчиком APDS-9960 #define PIN 6 #define NUM_LEDS 16 #define APDS9960_INT 2 // Needs to be an interrupt pin. We should be able to use the Arduino's pin 3 as well. Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUM_LEDS, PIN, NEO_GRB + NEO_KHZ800); SparkFun_APDS9960 apds = SparkFun_APDS9960(); // Initialize a SparkFun_APDS9960 object. This does all the magic incl. i2c communication with the sensor. volatile int isr_flag = 0; // interrupt flag, triggered when a gesture has been dectected. Used to detect gesture detection in the interrupt handler void setup() { // Инициализация RGB pinMode(PIN, OUTPUT); strip.begin(); strip.show(); // Initialize all pixels to 'off' // APDS Initialization code pinMode(APDS9960_INT, INPUT); // Set interrupt pin as input. @@Note: this should be handled my the library. But it isn't attachInterrupt(0, interruptRoutine, FALLING); // Initialize interrupt service routine. Basicly it'll call our interrupt routine if (apds.init()) // Initialize APDS-9960 (configure I2C and initial values) { // @@NOTE: original value is two. But it looks like the modern gesture sensor or more sensitive. 1 does it for me apds.setGestureGain( 1 ); } if (apds.enableGestureSensor(true)) // Start running the APDS-9960 gesture sensor engine. Sensor support more functions than gesture detection only. { //strip.begin(); strip.show(); } } void loop() { if(isr_flag == 1) { detachInterrupt(0); handleGesture(); isr_flag = 0; attachInterrupt(0, interruptRoutine, FALLING); } } void interruptRoutine() { isr_flag = 1; } void handleGesture() { if (apds.isGestureAvailable()) { switch (apds.readGesture()) { case DIR_LEFT: RGBLoop(); break; case DIR_RIGHT: CylonBounce(0, 0xff, 0, 4, 10, 50); break; case DIR_UP: strip.clear(); strip.show(); break; case DIR_DOWN: strip.show(); strip.clear(); break; } } } // ---> here we define the effect function <--- // *** REPLACE TO HERE *** void RGBLoop() { for(;;) { for(int j = 0; j < 3; j++ ) { // Fade IN for(int k = 0; k < 256; k++) { switch(j) { case 0: setAll(k,0,0); break; case 1: setAll(0,k,0); break; case 2: setAll(0,0,k); break; } showStrip(); //delay(3); } // Fade OUT for(int k = 255; k >= 0; k--) { switch(j) { case 0: setAll(k,0,0); break; case 1: setAll(0,k,0); break; case 2: setAll(0,0,k); break; } showStrip(); //delay(3); } } //break; } } void CylonBounce(byte red, byte green, byte blue, int EyeSize, int SpeedDelay, int ReturnDelay) { for(;;) { for(int i = 0; i < NUM_LEDS-EyeSize-2; i++) { setAll(0,0,0); setPixel(i, red/10, green/10, blue/10); for(int j = 1; j <= EyeSize; j++) { setPixel(i+j, red, green, blue); } setPixel(i+EyeSize+1, red/10, green/10, blue/10); showStrip(); delay(SpeedDelay); } delay(ReturnDelay); for(int i = NUM_LEDS-EyeSize-2; i > 0; i--) { setAll(0,0,0); setPixel(i, red/10, green/10, blue/10); for(int j = 1; j <= EyeSize; j++) { setPixel(i+j, red, green, blue); } setPixel(i+EyeSize+1, red/10, green/10, blue/10); showStrip(); delay(SpeedDelay); } delay(ReturnDelay); //break; } } void showStrip() { #ifdef ADAFRUIT_NEOPIXEL_H // NeoPixel strip.show(); #endif #ifndef ADAFRUIT_NEOPIXEL_H // FastLED FastLED.show(); #endif } void setPixel(int Pixel, byte red, byte green, byte blue) { #ifdef ADAFRUIT_NEOPIXEL_H // NeoPixel strip.setPixelColor(Pixel, strip.Color(red, green, blue)); #endif #ifndef ADAFRUIT_NEOPIXEL_H // FastLED leds[Pixel].r = red; leds[Pixel].g = green; leds[Pixel].b = blue; #endif } void setAll(byte red, byte green, byte blue) { for(int i = 0; i < NUM_LEDS; i++ ) { setPixel(i, red, green, blue); } showStrip(); }
Где нужно ставить isr_flag = 0? Что только не перепробовал... Примеров внятных нет по внешним прерываниям циклов for или while. Всё что находил - мигание светодиодом (в библиотеке apds тоже есть примеры с прерываниями, но так же не цикличны), поэтому мне сложно понять как работает прерывание.
Если рассуждать логически, то нужно сначала зациклить в бесконечность какой-нибудь эффект, а когда сработал жест, выйти из цикла и начать следующее действие.
И, да, я сознательно закомментировал delay в эффекте RGBLoop, потому что эта задержка, возможно, мешает чтению жестов. Как делать с millis() тоже пока не разобрался, как им заменить delay, но это ладно, потом уже. Сейчас главное читать датчик во время выполнения эффекта.
Заменял ещё for(;;)
на
while(!apds.readGesture()) в 71 и 106 строках. Но работает по прошествии всего цикла, а не в любой момент. Есть долгоработающие эффекты и ждать прошествия цикла ну как-то не вариант.Если отвечать на поставленный вами вопрос - как выйти из цикла по прерыванию, то можно, например, сказать, что нужно проверять isr_flag внутри самого "глубокого" цикла (например в районе строк 78, 90 и тп.) и делать return, если он равен ненулевой.
Если же делать правильно, то вам нужно код каждого спецэффекта заменить на неблокирующий и тогда проблема почти что разрешиться сама собой.
Что для вас проще - я не знаю.
Если же делать правильно, то вам нужно код каждого спецэффекта заменить на неблокирующий и тогда проблема почти что разрешиться сама собой.
В смысле без циклов, одними условиями if-else?
А не подскажете ещё по millis()? Где можно подробнее почитать, кроме примеров со светодиодом "мигаем blink без delay"? Этот пример мало что даёт, например, не объясняет как использовать его в глубинах циклов, при чтении каких-либо датчиков...
И ещё интересно... Подключал до apds модуль радиоканала. Управлял с пульта. Самое интересное, что там никакая пропитка выходами из цикла не нужна, переключение работает и так. Но тут другая проблема, после первого чтения команды с пульта, последующие переключения работают плохо, пульт нужно подносить очень близко, срабатывает не сразу. Может кто что посоветовать/объяснить как это исправить? Из-за задержек, насколько я понимаю? Привожу код.
В смысле без циклов, одними условиями if-else?
А не подскажете ещё по millis()? Где можно подробнее почитать, кроме примеров со светодиодом "мигаем blink без delay"? Этот пример мало что даёт, например, не объясняет как использовать его в глубинах циклов, при чтении каких-либо датчиков...
Слишком абстрактный вопрос задаете. Наврядли ответ на него вам даст понимание. Приведите простой случай который вам близок. Могу на вареном яйце объяснить, к примеру.
Слишком абстрактный вопрос задаете. Наврядли ответ на него вам даст понимание. Приведите простой случай который вам близок. Могу на вареном яйце объяснить, к примеру.
Ну вот более простой пример, написал такую вот мигалку:
delay здесь явно лишние для чтения каких-либо датчиков... Заменить на millis вообще не понял как.
Поправьте, что не так делаю?
То есть всё-таки рекомендуете заменить циклы и заменять готовые эффекты на подобное чтобы получилось как надо с millis? Эххх... Очень всё непросто выходит. Спасибо, буду пробовать что-нибудь.
А по прерываниям на радиоканале что посоветуете? Почему пульт плохо срабатывает когда нужно выйти из подпрограммы?
Мм... Проблема всё же в прерываниях. Убрал все attachInterrupt из Setup и Loop - работает всё так же до конца цикла. Не работают прерывания, в общем. Может кто показать как их применить в моём примере?
Теперь более понятно почему с пульта работает почти как надо. В библиотеке RCSwitch есть такая замечательная функция
mySwitch.enableReceive(0);
// Receiver on interrupt 0 => that is pin #2
С ней прерывания работают почти как надо (выходит из цикла for(;;) в любой момент по нажатию кнопки), за исключением того что во время цикла эффекта почему-то плохо работает пульт (но это, вероятно, из-за задержек (?)).
А на APDS-9960 без понятия как использовать прерывания... Нужны ли флаги (isr_flag)? Пытался сделать как здесь: http://mysensors.ru/build/gesture-controller/ взяв пример DimmableLight_v2.ino. Не работают прерывания если делать как здесь, заменяя функции на свои.
Проблема в том, что вы начинаете метаться, перебирая компоненты и программные приемы, не понимая, как они работают и какие у них типовые ограничения.
Например, известно, что для вывода на WS2811/2812/etc библиотеки, как правило, запрещают обработку прерываний. Так что всё, что прилетает на ардуину извне имеет ненулевой шанс быть упущенным.
...или пытаетесь свести два устройство в одно. По ссылке - там ардуина, связанная с сенсором, не занимается сложной обработкой и выводами на страйп, поэтому у нее достаточно ресурсов, чтобы ловить движения на сенсоре. Она опросила сенсор, решение приняла, десять байт выплюнула по nrf24l01 и дальше готова к работе.
Например, известно, что для вывода на WS2811/2812/etc библиотеки, как правило, запрещают обработку прерываний. Так что всё, что прилетает на ардуину извне имеет ненулевой шанс быть упущенным
Ну как-то же делают наверняка такое. Те же китайцы прилагают готовые запрограммированные пульты к таким лентам. Ардуина не справляется разве с таким?
Проблема в том, что вы начинаете метаться, перебирая компоненты и программные приемы, не понимая, как они работают и какие у них типовые ограничения.
Вы правы, но как-то ж надо попробовать... Выходит, да, что надо знать и тонкости ардуины, и тонкости библиотек компонентов.
Ну как-то же делают наверняка такое. Те же китайцы прилагают готовые запрограммированные пульты к таким лентам. Ардуина не справляется разве с таким?
В общем, что хотел сделать - пожалуй, удалось.) Поделюсь, может, кому пригодится. Взял за основу готовые эффекты AlexGyver'а.
https://www.youtube.com/watch?v=_h-Vj8Z6hqs
Прикрутил модули радиоканала и bluetooth'а. Дописал вот такой код:
Ну, собственно, и всё. Пульт, конечно, работает немного не так, как хотелось бы - всему виной задержки в кодах эффектов. Но переписывать это дело под millis задача просто невозможная, имхо. Да и "не так, как хотелось бы" заключается лишь в небольшом удерживании кнопки, около полусекунды, а хотелось бы чтобы при одном коротком нажатии.
Синезубый приятель же работает без отказов - идеально. Программу для управления рекомендую Bluetooth Electronics. В ней настраиваются не только сами кнопки, но и их количество, а ещё можно приделать всякие слайдеры и переключатели.
Из личного минуса - не работает айфон с этими модулями (HR-05) bluetooth, но это уже "спасибо" эпплу с его политикой:
ios does not support most bluetooth profiles, because apple wants a slice of peripheral profits through its MFi program. If you can't dump ios, you will have to use BLE (which is unrestricted and not part of MFi).
Товарищ андройд - наше всё.)
Все что Вы искали, уже выложено в открытом доступе https://youtu.be/C4QELMzOpiA
Все что Вы искали, уже выложено в открытом доступе https://youtu.be/C4QELMzOpiA
реклама? "Все что вы искали" - кому это? - тем более что ТС ничего и не искал
Ссылку не открывал, что там - не знаю, но уж очень агрессивно подается. На уровне "самые красивые сиськи - в нашем видео"
А Вы полностью прочитали сообщение топикстартера?
Цитирую:
"Собственно, столкнулся с проблемой прерываний. Если зациклить какой-нибудь эффект, то из него уже не выйти, а если сделать конечным, то он просто пройдет один раз и остановится. Сами эффекты находятся в циклах for(). Как реализовать выход из циклов по событию (при смене жеста)?"
Ссылку не открывал, что там - не знаю, но уж очень агрессивно подается. На уровне "самые красивые сиськи - в нашем видео"
А Вам , то что от этого плохо стало или другие мотивы?
А Вы полностью прочитали сообщение топикстартера?
А вы всю ветку читали? - Автору было предложено порядка пяти разных способов решения его проблемы...
Не говоря уж о том, что проблема эта обсуждается каждым вторым новичком и решения для нее приведены в сотнях источников в гугле.
Вы уверены. что ваша ссылка спустя год хоть что-то добавляет к обсуждению? :)))
Я уверен на 100%, что мое решение избавит от всех проблем, присущих этому датчику. Вы его скорее всего не использовали никогда и не в курсе о его нестабильной работе