Múltiples patrones de LED independientes

8

Tengo un problema, que a primera vista (y siendo nuevo en Arduino), pensé que era una aplicación perfecta para un Arduino. Sin embargo, después de intentarlo y no implementarlo, ¡estoy dudando de mí mismo!

Simplemente, necesito controlar muchos LED de forma independiente, muchos de los cuales tendrán sus propios patrones individuales, por ejemplo, "5 segundos encendido - 5 segundos apagado". "flashes continuos" - o secuencias como "2 flashes, pausa, 1 flash". Obviamente, sin el lujo de los hilos, me estoy despegando un poco. Sea genial saber si a) Arduino es la mejor opción yb) si es así, ¿cómo puedo hacer esto?

Gracias por adelantado :)

Nickos
fuente
1
¿Has mirado en protothreads ? Hay un par de bibliotecas Arduino que le permiten incorporar fácilmente protothreads en su proyecto.
sachleen

Respuestas:

9

Manejar múltiples patrones al mismo tiempo es ciertamente posible con una plataforma como Arduino, y hay varias maneras de hacerlo.

Un método que consideraría es escribir funciones que representen efectivamente cada patrón matemáticamente. Simplemente le pasará el tiempo total que ha transcurrido en su programa hasta el momento, y realizará la acción adecuada para ese momento específico en el tiempo. Volverá inmediatamente después (sin demoras ni nada).

Para hacer eso, primero necesitará saber cuánto dura un solo ciclo del patrón. Luego puede usar el operador de módulo para averiguar qué tan avanzado está el ciclo actual. A partir de ahí, todo lo que necesita hacer es tener algunas ifcondiciones para determinar qué hacer en un momento dado.

Esto es lo que podría parecer para su patrón "5 segundos encendido, 5 segundos apagado":

function pattern5on5off(unsigned long totalTime)
{
  // Calculate how far through the current cycle we are
  const unsigned long cycleTime = totalTime % 10000;

  // If we're in the first 5 seconds of the cycle then turn the light on.
  // Otherwise, turn it off.
  if (cycleTime < 5000)
    digitalWrite(3, HIGH);
  else
    digitalWrite(3, LOW);
}

Es cierto que llamar constantemente digitalWrite()cuando técnicamente no es necesario no es muy eficiente. Sin embargo, no debería causar ningún daño y es bastante fácil de optimizar si es necesario.

Para usar el ejemplo anterior en un boceto, solo debe llamarlo loop()y pasar el número que obtiene millis(); p.ej:

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

  pattern5on5off(totalTime);

  // call other patterns here...
}

Otros patrones serán más complejos, pero siguen el mismo principio. Simplemente usaría ifdeclaraciones apropiadas para expresar su lógica.

Lo vital para recordar es que la función representa un momento específico en el tiempo. Nunca debe pausar o retrasar el programa, de lo contrario evitará que se ejecuten los otros patrones.

Editar: Tiempo en el primer ciclo
Como jfpoilpret señaló en los comentarios, el primer ciclo comenzará en un punto aleatorio. Esto se debe a que la primera vez que se llama millis()en loop(), no empezará a 0 (el dispositivo ya han estado funcionando durante un corto tiempo antes de que loop()obtiene la llamada). Sin embargo, es fácil de resolver, si es necesario.

Lo harías compensando el totalTimevalor por el valor que obtuviste la primera vez loop(). Por ejemplo:

unsigned long g_startTime = 0;

void loop()
{
  unsigned long totalTime = 0;

  if (g_startTime == 0) {
    // This is the first cycle.
    // Store the start time so we can compensate later.
    g_startTime = millis();

  } else {
    // This is not the first cycle.
    // Compensate for the start time.
    totalTime = millis() - g_startTime;
  }

  pattern5on5off(totalTime);
  // etc..
}
Peter Bloomfield
fuente
¡Muchas gracias, tiene mucho sentido! Definitivamente he estado golpeando mi cabeza contra una pared con el enfoque equivocado ... :)
Nickos
1
El problema con el %enfoque es que el tiempo no será correcto la primera vez, ya que será aleatorio al principio.
jfpoilpret
1
@jfpoilpret Eso es cierto. Sin embargo, es fácil de arreglar, así que lo agregué a mi respuesta. :)
Peter Bloomfield
Otra opción sería, en lugar de ejecutar millisuna vez en el ciclo y pasar su valor como parámetro a cada función led, tener cada función como una sin parámetros y ejecutar millis dentro de cada una. Eso permitiría que cada función obtenga un valor más preciso, que podría o no ser importante dependiendo de cuánto tiempo lleva ejecutar cada función en el bucle, y también los requisitos de corrección de tiempo de la aplicación.
heltonbiker
4

Arduino es una buena opción para la tarea, fácil de comenzar. La clave es escribir código sin bloqueo. Puedes echar un vistazo al ejemplo de BlinkWithoutDelay.

Hice una sugerencia para su tarea:

// Timing suquences for the LED's in milliseconds
// First value is on time, second value is off time,
// third value on time and so on (up to 10 values)
// One row for each LED
unsigned int led_timing[][10] = {
  {5000, 5000},
  {100, 1000},
  {100, 100, 100, 1500, 100, 1500}
};

// The pins the LED's are connected to
byte led_pins[] = {11, 12, 13};

// Keep track of timing sequence
// Array size taken from 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()
{
  // Initialize LED's as output
  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()
{
  // Current timestamp
  unsigned long now = millis();

  // Keep track of sequence for each LED
  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]++;

      // Start over at the end of timing sequence
      timing_i[i] %= sizeof(led_timing[i])/sizeof(led_timing[i][0]);

      last_change[i] = now;
    }
  }
}
usuario2973
fuente
0

Sé que la publicación es antigua, pero revisé el ejemplo con el enfoque basado en matriz y, en mi opinión:

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

siempre producirá el tamaño asignado (número de elementos) de la matriz, en este caso 10. Por lo tanto, el tiempo no se reiniciará hasta que llegue al "final" de la matriz, utilizando elementos indefinidos. Para LED "0":

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

Saludos tommy

TommyL
fuente