Управление адресной светодиодной лентой через ИК пульт

Mr.Master

✩✩✩✩✩✩✩
30 Сен 2023
5
0
Здравствуйте. Пытаюсь сделать управление светодиодной лентой через ИК пульт и наткнулся на такую проблему: хочу чтобы при нажатии на цифры на пульте включались разные статичные цвета по всей ленте(это работает), а при нажатии на звездочку включался режим переливающейся радуги(это тоже работает), но вот обратно с радуги на статичный цвет переключиться не получается, программа перестает видеть нажатия на пульт(хотя приемник моргает). На медленной радуге все работает (и то на пульт надо нажать иногда по 5 раз), но хочется чтобы она быстро двигалась, и чтобы не приходилось на пульт по несколько раз нажимать. Подскажите пожалуйста что можно сделать. Свою программу прикрепил.
 

Вложения

poty

★★★★★★✩
19 Фев 2020
3,230
940
Метод show класса FastLed блокирует все прерывания. Не знаю зачем, но в радуге он вызывается максимально часто (изменяется цвет одного диода и сразу отображается, при этом все диоды окрашиваются в один цвет). Перепишите это так же, как окрашивание ленту статичным цветом и сделайте частоту перерисовывания разумной, но не с помощью delay, а с помощью millis.
 
  • Лойс +1
Реакции: Sana956

Mr.Master

✩✩✩✩✩✩✩
30 Сен 2023
5
0
@poty, "сделать разумную частоту перерисовывания" вы имеете в виду замедлить радугу, т.е. на максимальной скорости не получиться сделать? Я пытался сделать на millis() но у меня не получилось, я не понял как это сделать
 

poty

★★★★★★✩
19 Фев 2020
3,230
940
@Mr.Master, причём здесь скорость радуги? В чём заключается Ваш эффект, которого Вы собираетесь достигнуть? Вы же не видите, наверняка, как лента меняет свой цвет на один тон hue подиодно? Воспринимаете это как единовременную смену тона по всей ленте?
 

Mr.Master

✩✩✩✩✩✩✩
30 Сен 2023
5
0
@poty, Вот я написал частоту перерисовывания на millis(). Но меньше 100мс не получается поставить так как перестают срабатывать кнопки на пульте, а 100мс это медленная скорость получается. Либо я не понял что вы предлагаете сделать
 

Вложения

poty

★★★★★★✩
19 Фев 2020
3,230
940
@Mr.Master, уже лучше, но логика страдает.
Во-первых, при переключении на радугу переменные time, i, counter нужно инициализировать:
C++:
      case IR_STAR:
        rainbow = 1;                                //включаем радугу
        time = millis();
        i = counter = 0;
Во-вторых, что будет в случае, когда выполнится условие if (i == LED_NUM - 1), но не выполнится if (millis() - time > 100)?
В-третьих, последний светодиод так никогда и не будет зажжён (последний светодиод имеет индекс i==LED_NUM-1, как только в результате i++ он таким станет сработает условие if (i == LED_NUM - 1) и i обнулится.
Поэтому код я бы переписал, например, так:
C++:
#define    REL_SPEED    10

/* .... остальной код .... */

    } else if (rainbow == 1) {                            //радуга
        if (i >= LED_NUM) {
            if (millis() - time >= REL_SPEED) {
                time += REL_SPEED;
                counter++;
                i = 0;
                FastLED.show();
            }
        } else {
            leds[i] = CHSV(counter + i * 2, 255, 255);  // HSV. Увеличивать HUE (цвет)
            i++;
        }
      }
 

Mr.Master

✩✩✩✩✩✩✩
30 Сен 2023
5
0
@poty, Спасибо насчет последнего светодиода, не замечал. Но опять же при включении радуги, невозможно переключиться на статичные цвета если REL_SPEED меньше 100
 

Вложения

poty

★★★★★★✩
19 Фев 2020
3,230
940
Куда подключен IR диод (или модуль)? Случайно не инвертирован сигнал?
 

Bruzzer

★★★✩✩✩✩
23 Май 2020
473
133
невозможно переключиться на статичные цвета если REL_SPEED меньше 100
Скорее всего вы упираетесь в возможности совместной работы библиотек. Прием данных по ИК занимает примерно 70 мс и в это время запрещать прерывания надолго в нельзя.
Если вы используете UNO подобную плату, то можно немного изменить библиотеку, чтобы можно было считывать факт прихода START последовательности и в этом случае останавливать вывод на ленту до окончания приема (примерно 70 мс). Так же может потребоваться изменить временные константы.
Возможно есть уже готовые, более подходящие библиотеки для ИК, я не в курсе.
 

poty

★★★★★★✩
19 Фев 2020
3,230
940
@Mr.Master, можно попробовать вот так:
Код на флагах:
#include "FastLED.h"
#include <NecDecoder.h>

#define LED_PIN        13       // пин управления лентой
#define LED_NUM        60       // количество светодиодов в ленте
#define REL_SPEED      25       // период смены кадра
#define IR_TO          14       // максимальный кадр протокола NEC (преамбула (9+4,5)мс)

#define IR_0         0x98       // команды пульта
#define IR_1         0xA2
#define IR_2         0x62
#define IR_3         0xE2
#define IR_4         0x22
#define IR_5         0x02
#define IR_6         0xC2
#define IR_7         0xE0
#define IR_8         0xA8
#define IR_9         0x90
#define IR_STAR      0x68
#define IR_HASH      0xB0
#define IR_OK        0x38
#define IR_UP        0x18
#define IR_LEFT      0x10
#define IR_RIGHT     0x5A
#define IR_DOWN      0x4A

#define RB_ON        0x80       // радуга включена
#define S1_DONE      0x40       // массив светодиодов сдвинут
#define S2_DONE      0x20       // массив светодиодов заполнен
#define IR_CHK       0x02       // флаг таймера таймаута
#define IR_TICK      0x01       // флаг запрета отображения

CRGB leds[LED_NUM];             // массив светодиодной ленты
NecDecoder ir;                  // класс ИК-управления
byte counter;                   // кадр анимации
volatile byte flags = 0;        // биты флагов
uint16_t time_rel;              // время кадра
uint16_t time_ir;               // таймаут ИР-управления

void setup() {
  //Serial.begin(9600);
  pinMode(LED_PIN, OUTPUT);
  attachInterrupt(0, irIsr, FALLING);
  FastLED.addLeds<WS2812, LED_PIN, GRB>(leds, LED_NUM);
  FastLED.setBrightness(50);
  FastLED.clear();
  FastLED.showColor(CRGB(0, 0, 0));//сброс ленты(выключение)
  FastLED.show();
}
void irIsr() {
  ir.tick();                    // обработка импульса
  flags |= IR_TICK;             // запрет отображения
  flags &= ~IR_CHK;             // отменяем таймаут
}

void loop() {
  if (ir.available()) {
                                // снятие запрета отображения
    flags &= ~(IR_CHK | IR_TICK);
    switch (ir.readCommand()) {
      case IR_1:
        flags &= ~(RB_ON | S1_DONE | S2_DONE);      // выключаем радугу
        FastLED.showColor(CRGB(255, 0, 0));         // включаем статичный красный свет на всей ленте
        break;
      case IR_2:
        flags &= ~(RB_ON | S1_DONE | S2_DONE);      // выключаем радугу
        FastLED.showColor(CRGB(0, 255, 0));         // включаем статичный зеленый свет на всей ленте
        break;
      case IR_STAR:
        flags |= RB_ON;                             // включаем радугу
        time_rel = (uint16_t)millis();              // включаем таймер кадра
        counter = 0;                             
    }
  } else if (flags & RB_ON) {                       // радуга
    if ((flags & IR_TICK) && ((flags & IR_CHK) == 0)) {
                                                    // обнаружили IR-передачу
      time_ir = (uint16_t)millis();                 // включаем таймер таймаута
      flags |= IR_CHK;                              // поднимаем флаг таймаута
    }
    if (flags & S2_DONE) {                          // кадр готов
      if ((flags & IR_CHK) && ((uint16_t)millis() - time_ir >= IR_TO)) {
                                                    // если сработал таймаут
        flags &= ~(IR_CHK | IR_TICK);               // отменяем запрет отображения, сбрасываем таймаут
      }
      if ((uint16_t)millis() - time_rel >= REL_SPEED) { // кадр завершился
        if ((flags & IR_TICK) == 0) {               // отображение разрешено
          FastLED.show();                           // отображаем
          counter += 2;                             // переход на следующий кадр (через 2 для оптимизации)
          flags &= ~(S1_DONE | S2_DONE);            // для подготовки следующего кадра
        }
        time_rel += REL_SPEED;
      }
    } else if (flags & S1_DONE) {                   // расчёт последнего светодиода
      leds[LED_NUM-1] = CHSV(counter + (LED_NUM - 1) * 2, 255, 255);
      flags |= S2_DONE;                             // кадр подготовлен
    } else {                                        // смещение картинки в сторону первого светодиода
      memmove(leds, (leds+1), sizeof(CRGB)*(LED_NUM-1));
      flags |= S1_DONE;                             // смещение произведено
    }
  }
}
 

Сотнег

★★★★★★★
15 Янв 2020
4,364
1,494
Если в этой библиотеке обработчик сигнала от пульта на прерываниях, то никакие "на флагах" ситуацию не исправят.
Судя по всему, пульт регулярно попадает в промежутки, когда обработчик прерываний выключен
(в момент выполнения FastLED.show).

Другие проекты каким-то образом успевают работать и с пультом и с гораздо более длинными лентами.
Может, попробовать обойтись тут без attachInterrupt, просто добавив ir.tick() в основной цикл?
Или вообще другую библиотеку взять типа такой:
 

poty

★★★★★★✩
19 Фев 2020
3,230
940
@Сотнег, ну, вероятность несработки есть, но это именно вероятность, а не гарантированная штука.
Любые два процесса, кодированные основанным на временных интервалах кодом не смогут на одном процессоре работать правильно на 100%.
Эта библиотека работает с помощью измерения временных интервалов, поэтому вынесение tick полностью угробит её функционирование.
 

Bruzzer

★★★✩✩✩✩
23 Май 2020
473
133
@poty,
Я сам не работал с ИК пультами, так что не знаю насколько часто там бывают ложные импульсы (и бывают ли вообще), поэтому и предлагал ловить старт импульс. Если ложных импульсов нет (или ими можно пренебречь), то ваш метод должен работать. Возможно стоит подкорректировать временные константы для стартового импульса в библиотеке, чтобы он надежнее ловился.
 

Vlad70

✩✩✩✩✩✩✩
24 Мар 2024
1
0
Здравствуйте. К светомузыке на адресной ленте duino_light_show хочу прикрутить ИК пульт с библиотекой NecDecoder, проблема та же. Кадры на ленту выходят часто, в этот момент запрещаются прерывания и прием ИК невозможен. В NecDecoder есть флаг _start. Хочу в теле основной программы ввести проверку флага и в случае уст. ждать его сброса. За это время будет пропущено всего 2-3 кадра. Думаю визуализация не пострадает. Но ардуино ругается, что он не описан в данной области (was not declared in this scope).
Подскажите как использовать данный флаг из библиотеки в основном скетче.