Несколько независимых шаблонов светодиодов

У меня есть проблема, которая, на первый взгляд (и будучи новичком в Arduino), показалась мне идеальным приложением для Arduino. Однако после неудачной попытки реализовать это я сомневаюсь в себе!

Просто — мне нужно независимо управлять множеством светодиодов, многие из которых будут иметь свои индивидуальные шаблоны — например «5 секунд горит — 5 секунд выключается». «непрерывные вспышки» — или такие последовательности, как «2 вспышки, пауза, 1 вспышка». Очевидно, что без такой роскоши, как темы, я немного застреваю. Было бы приятно узнать, а) Arduino — лучший выбор и б) если да, то как мне это сделать!

Заранее спасибо :)

, 👍9

Обсуждение

Вы изучали [protothreads](http://dunkels.com/adam/pt/)? Существует несколько библиотек Arduino, которые позволяют легко включать протопотоки в ваш проект., @sachleen

Используйте библиотеку Arduino [NonBlockingSequece](https://github.com/AhmedYousryM/NonBlockingSequence). Там есть много ярких примеров, позволяющих ответить на этот конкретный вопрос., @user1774936


3 ответа


Лучший ответ:

9

Одновременная обработка нескольких шаблонов, безусловно, возможна с помощью такой платформы, как Arduino, и есть несколько способов сделать это.

Один из методов, который я бы рассмотрел, — это написание функций, которые эффективно представляют каждый шаблон математически. Вы просто передаете ему общее время, прошедшее в вашей программе до сих пор, и он выполняет соответствующее действие для этого конкретного момента времени. Он немедленно возвращается после этого (без задержек или чего-либо еще).

Чтобы сделать это, вам сначала нужно узнать, как долго длится один цикл шаблона. Затем вы можете использовать оператор modulo, чтобы выяснить, как далеко вы продвинулись в текущем цикле. После этого вам нужно будет только задать некоторые условия if, чтобы определить, что делать в любой момент времени.

Вот как это может выглядеть для вашего шаблона «5 секунд работы, 5 секунд отдыха»:

function pattern5on5off(unsigned long totalTime)
{
  // Рассчитаем, как далеко мы продвинулись в текущем цикле
  const unsigned long cycleTime = totalTime % 10000;

  // Если мы находимся в первых 5 секундах цикла, то включаем свет.
  // В противном случае выключите его.
  if (cycleTime < 5000)
    digitalWrite(3, HIGH);
  else
    digitalWrite(3, LOW);
}

По общему признанию, постоянно вызывать digitalWrite(), когда вам это технически не нужно, не очень эффективно. Это не должно причинить никакого вреда, и его довольно легко оптимизировать при необходимости.

Чтобы использовать приведенный выше пример в скетче, вам просто нужно вызвать его в loop() и передать число, полученное из millis(); например:

void loop()
{
  const unsigned long totalTime = millis();

  pattern5on5off(totalTime);

  // здесь можно вызвать другие шаблоны...
}

Другие шаблоны будут более сложными, но будут следовать тому же принципу. Вам просто нужно будет использовать соответствующие if операторы для выражения вашей логики.

Главное, что нужно помнить, это то, что функция представляет собой определенный момент времени. Она никогда не должна останавливать или задерживать программу, иначе это помешает выполнению других шаблонов.

Изменить: Время в первом цикле
Как заметил jfpoilpret в комментариях, самый первый цикл начнется в случайной точке. Это потому, что при первом вызове millis() в loop() он не начнется с 0 (устройство уже будет работать некоторое время до вызова loop()). Однако это легко решить, если необходимо.

Вы бы сделали это, сместив значение totalTime на любое значение, которое вы получили в самый первый раз вокруг loop(). Например:

unsigned long g_startTime = 0;

void loop()
{
  unsigned long totalTime = 0;

  if (g_startTime == 0) {
    // Это первый цикл.
    // Сохраняем время начала, чтобы можно было компенсировать его позже.
    g_startTime = millis();

  } else {
    // Это не первый цикл.
    // Компенсируем время начала.
    totalTime = millis() - g_startTime;
  }

  pattern5on5off(totalTime);
  // и т. д..
}
,

Спасибо большое - все очень понятно! Я определенно бился головой об стену с неправильным подходом... :), @Nickos

Проблема подхода % заключается в том, что с первого раза время не будет точным, так как поначалу оно будет случайным., @jfpoilpret

@jfpoilpret Это правда. Но это легко исправить, поэтому я добавил это в свой ответ. :), @Peter Bloomfield

Другой вариант — вместо того, чтобы запускать millis один раз в цикле и передавать его значение в качестве параметра каждой функции led, сделать каждую функцию без параметров и запускать millis внутри каждой из них. Это позволило бы каждой функции получить более точное значение, которое может быть или не быть важным в зависимости от того, сколько времени занимает выполнение каждой функции в цикле, а также от требований приложения к корректности синхронизации., @heltonbiker


4

Arduino — прекрасный выбор для этой задачи, с ним легко начать. Главное — написать неблокирующий код. Вы можете взглянуть на пример BlinkWithoutDelay.

Я сделал предложение для вашего задания:

// Временные последовательности для светодиодов в миллисекундах
// Первое значение — время включения, второе значение — время выключения,
// третье значение по времени и т.д. (до 10 значений)
// Одна строка для каждого светодиода
unsigned int led_timing[][10] = {
  {5000, 5000},
  {100, 1000},
  {100, 100, 100, 1500, 100, 1500}
};

// Контакты, к которым подключены светодиоды
byte led_pins[] = {11, 12, 13};

// Отслеживайте временную последовательность
// Размер массива взят из led_pins
unsigned long last_change[sizeof(led_pins)/sizeof(led_pins[0])];
byte timing_i[sizeof(led_pins)/sizeof(led_pins[0])];

void setup()
{
  // Инициализируем светодиоды как выход
  for (byte i = 0; i < sizeof(led_pins)/sizeof(led_pins[0]); i++)
  {
    pinMode(led_pins[i], OUTPUT);
    digitalWrite(led_pins[i], HIGH);
  }
}


void loop()
{
  // Текущая временная метка
  unsigned long now = millis();

  // Отслеживайте последовательность для каждого светодиода
  for (byte i = 0; i < sizeof(led_pins)/sizeof(led_pins[0]); i++)
  {
    if (now - last_change[i] >= led_timing[i][timing_i[i]])
    {
      digitalWrite(led_pins[i], !digitalRead(led_pins[i]));
      timing_i[i]++;

      // Начать заново с конца временной последовательности
      timing_i[i] %= sizeof(led_timing[i])/sizeof(led_timing[i][0]);

      last_change[i] = now;
    }
  }
}
,

1

Я знаю, что пост старый, но я проверил пример с подходом на основе массива и, по моему мнению:

sizeof(led_timing[i])/sizeof(led_timing[i][0])

всегда будет возвращать выделенный размер (количество элементов) массива - в данном случае 10. Поэтому отсчет времени не будет возобновлен, пока вы не достигнете «конца» массива, используя неопределенные элементы. Для светодиода «0»:

int led_timing[0][10]:
5000,5000, <undefined>, <undefined>, <undefined>, <undefined>, <undefined>, <undefined>, <undefined>, <undefined>

Приветствия Томми

,