Multiples loop (u otra solución)

Buenas tardes,

Acabo de descubrir Arduino. No se donde había vivido yo toda la vida sin conocer este sistema...

Todo empezó por que quiero hacer un fotomatón casero para la boda de un familiar. Pensaba hacerlo con usa serie de relés de una manera muy simple y entonces encontré Arduino. En su día oí hablar de Arduino pero no me paré mirar ni que era.

Para este primer proyecto con Arduino, me he basado en el código de DIY Machines.

Básicamente el funcionamiento es el siguiente:

Tengo un botón, que al pulsarlo empieza una cuenta atrás de 10 segundos que se muestra en una pantalla led Matrix 8x32 con controlador MAX7219. Desués de esos 10 segundos activa un relé (que será el disparador de la cámara) para hacer otras 2 cuentas atrás de 5 segundos con el mismo procedimiento. En total se harán 3 fotos. Entre cada foto, se muestran diferentes mensajes. Este es el código:

/*
 * Arduino controlled Photo Booth - DIY Machines
This is an easy to build Arduino powered photo booth. You can customise the housing to suit your event/wedding and as it's controlled by an Arduino Nano you don't need anyone to 'man it' throughout the night.
==========
More info: https://diymachines.co.uk/
3D printed parts can be downloaded from here: https://www.thingiverse.com/DIY_Machines/designs
Project video: https://youtu.be/Fu5Gbpv4EYs 
==========
 * SAY THANKS:
Buy me a coffee to say thanks: https://ko-fi.com/diymachines
Support us on Patreon: https://www.patreon.com/diymachines
SUBSCRIBE:
■ https://www.youtube.com/channel/UC3jc4X-kEq-dEDYhQ8QoYnQ?sub_confirmation=1
INSTAGRAM: https://www.instagram.com/diy_machines/?hl=en
FACEBOOK: https://www.facebook.com/diymachines/
*/


int shutterPin = 12;
int buttonPin = 8;
int buttonVal = 0;  //somewhere to store the button state
int BUTTONLED = 9;

#define NUM_MAX 4
#define ROTATE 90

#define DIN_PIN 11  // 
#define CS_PIN  10  // 
#define CLK_PIN 13  //  

#include "max7219.h"
#include "fonts.h"


void setup() {
  // put your setup code here, to run once:
Serial.begin(9600); // open the serial port at 9600 bps:

pinMode(shutterPin, OUTPUT);
digitalWrite(shutterPin, HIGH);

pinMode(BUTTONLED, OUTPUT);
digitalWrite(BUTTONLED, HIGH);

pinMode(buttonPin, INPUT);
digitalWrite(buttonPin, HIGH);   //enable internal pull up resistor

initMAX7219();
  sendCmdAll(CMD_SHUTDOWN,1);
  sendCmdAll(CMD_INTENSITY,15);
}



void loop() {
  // put your main code here, to run repeatedly:
buttonVal = digitalRead(buttonPin);
Serial.println(buttonVal);
delay(200);



  
if (buttonVal == LOW) {
  digitalWrite(BUTTONLED, LOW);
  takePhoto1();
  printStringWithShift("OTRA",0);
  delay(2000);
  takePhoto2();
  printStringWithShift("Ultima",0);
  delay(2000);
  takePhoto2();
  printStringWithShift("LISTO ",0);
  delay(3000);
  printStringWithShift("           ",0);
  digitalWrite(BUTTONLED, HIGH);
}

                   
  
}


int showChar(char ch, const uint8_t *data)
{
  int len = pgm_read_byte(data);
  int i,w = pgm_read_byte(data + 1 + ch * len);
  for (i = 0; i < w; i++)
    scr[NUM_MAX*8 + i] = pgm_read_byte(data + 1 + ch * len + 1 + i);
  scr[NUM_MAX*8 + i] = 0;
  return w;
}

// =======================================================================

void printCharWithShift(unsigned char c, int shiftDelay) {
  if (c < ' ' || c > MAX_CHAR) return;
  c -= 32;
  int w = showChar(c, font);
  for (int i=0; i<w+1; i++) {
    delay(shiftDelay);
    scrollLeft();
    refreshAll();
  }
}

// =======================================================================

void printStringWithShift(const char* s, int shiftDelay){
  while (*s) {
    printCharWithShift(*s++, shiftDelay);
  }
}

// =======================================================================

void takePhoto1() {
  printStringWithShift("          10          9           8          7         6         5        4         3        2         1         ",20);
  printStringWithShift("SONRIE!",0);
  delay(1000);
  digitalWrite(shutterPin, LOW);    // turn the LED off by making the voltage LOW
  printStringWithShift("            ",0);
  delay(1000);
  digitalWrite(shutterPin, HIGH);
}
void takePhoto2() {
    printStringWithShift("          5        4         3        2         1         ",20);
  printStringWithShift("SONRIE!",0);
  delay(1000);
  digitalWrite(shutterPin, LOW);    // turn the LED off by making the voltage LOW
  printStringWithShift("            ",0);
  delay(1000);
  digitalWrite(shutterPin, HIGH);
}

Como se puede ver, el loop está esperando a la orden del botón y cuando este se pulsa comienza a ejecutarse todo el código.

Lo que quiero hacer, es mostrar un mensaje mientras no se esté ejecutando el código y que al pulsarse el botón se ejecute el código como hasta ahora.

El problema es que mientras se muestra el mensaje de espera, no captura la pulsación del botón, ya que el procediminto del mensaje hace que no se esté ejecutando el loop.

¿Hay alguna manera para solucionar este "problema"?

No se si hay que hacer un loop múltiple o como se podría hacer. No he encontrado la solución tampoco con San Google.

Muchas gracias!

Ve a Documentación y lee sobre millis() y máquina de estados.
El problema es que la gran mayoría de los códigos de la Web usan delay() y delay() detiene la máquina por el tiempo que le indiques entre parentesis expresado en milisegundos.
Con millis() y algunas operaciones, no detienes al Arduino y responderá al botón y seguirá actualizando la pantalla mas alla del tiempo que esta demore en hacerlo.

Muchas gracais por la respuesta.

He probado algunas cosas con millis y no me ha funcionado, pero profundizaré un poco más en la documentación.

Gracias.

Insiste con los tutoriales.
millis() obliga a pensar de otro modo.
Ahora las cosas no pueden ser asi nomas como pasa con delay es decir, secuenciales.
Con millis() algo que comienza a actuar puede seguir mientras no se cumpla la condición de tiempo que afecta a millis().
Ejemplo : tienes Blink.ino donde prendes un led luego un delay de 1 seg en el que el no hace nada y vuelves a poner otra acción que apaga el led y de nuevo un delay de 1 seg donde de nuevo no hace nada.
Lo mismo en millis() es diferente.
Arrancas y pones la salida del led en high, y luego te tienes que quedar esperando a que se cumpla una condición para apagar el led y eso o haces por 1 seg mientras el loop se sigue ejecutando y puede ver sensors, estado de pultadores, actualizar LCD lo que quieras.
Antes con delay() no puede hacer nada mas que gastar tiempo, ese es el gran e importante cambio. Tal cambio da la sensación que Arduino hiciera muchas cosas pero sigue siendo secuencial solo que ahora no se queda de brazos cruzados.

Fijate si es lo que quieres

/*
   Arduino controlled Photo Booth - DIY Machines
  This is an easy to build Arduino powered photo booth. You can customise the housing to suit your event/wedding and as it's controlled by an Arduino Nano you don't need anyone to 'man it' throughout the night.
  ==========
  More info: https://diymachines.co.uk/
  3D printed parts can be downloaded from here: https://www.thingiverse.com/DIY_Machines/designs
  Project video: https://youtu.be/Fu5Gbpv4EYs
  ==========
   SAY THANKS:
  Buy me a coffee to say thanks: https://ko-fi.com/diymachines
  Support us on Patreon: https://www.patreon.com/diymachines
  SUBSCRIBE:
  ■ https://www.youtube.com/channel/UC3jc4X-kEq-dEDYhQ8QoYnQ?sub_confirmation=1
  INSTAGRAM: https://www.instagram.com/diy_machines/?hl=en
  FACEBOOK: https://www.facebook.com/diymachines/
*/

#define NUM_MAX 4
#define ROTATE 90

#define DIN_PIN 11  // 
#define CS_PIN  10  // 
#define CLK_PIN 13  //  

#include "max7219.h"
#include "fonts.h"

#define SHUTTER_PIN 12
#define BUTTON_PIN 8
#define BUTTON_LED 9

byte buttonVal = 1;  //somewhere to store the button state
bool buttonEn = true;

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600); // open the serial port at 9600 bps:

  pinMode(SHUTTER_PIN, OUTPUT);
  digitalWrite(SHUTTER_PIN, HIGH);

  pinMode(BUTTON_LED, OUTPUT);
  digitalWrite(BUTTON_LED, HIGH);

  pinMode(BUTTON_PIN, INPUT_PULLUP);  //enable internal pull up resistor  

  initMAX7219();
  sendCmdAll(CMD_SHUTDOWN, 1);
  sendCmdAll(CMD_INTENSITY, 15);
}

void loop() {
  printStringWithShift("ACA VA TU MENSAJE           ", 50);
  bool dummy = buttonPressed();
  delay(50);
  if (buttonVal == 0) {
    buttonVal = 1;
    digitalWrite(BUTTON_LED, LOW);
    takePhoto1();
    printStringWithShift("OTRA ", 0);
    delay(2000);
    takePhoto2();
    printStringWithShift("ULTIMA", 0);
    delay(2000);
    takePhoto2();
    printStringWithShift("LISTO ", 0);
    delay(3000);
    printStringWithShift("           ", 0);
    digitalWrite(BUTTON_LED, HIGH);
    buttonEn = true;
  }
}

bool buttonPressed() {
  if (!buttonEn) return !buttonVal;
  if (buttonVal) {
    buttonVal = digitalRead(BUTTON_PIN);
    buttonEn = buttonVal;
    return !buttonVal;
  }
  return false;
}

int showChar(char ch, const uint8_t *data)
{
  int len = pgm_read_byte(data);
  int i, w = pgm_read_byte(data + 1 + ch * len);
  for (i = 0; i < w; i++){
    scr[NUM_MAX * 8 + i] = pgm_read_byte(data + 1 + ch * len + 1 + i);
  }
  scr[NUM_MAX * 8 + i] = 0;
  return w;
}

// =======================================================================

void printCharWithShift(unsigned char c, int shiftDelay) {
  if (c < ' ' || c > MAX_CHAR) return;
  c -= 32;
  int w = showChar(c, font);
  for (int i = 0; i < w + 1; i++) {
    if (buttonPressed()) break; // sale si el boton fue pulsado
    delay(shiftDelay);
    scrollLeft();
    refreshAll();
  }
}

// =======================================================================

void printStringWithShift(const char* s, int shiftDelay) {
  while (*s) {
    if (buttonPressed()) break; // sale si el boton fue pulsado
    printCharWithShift(*s++, shiftDelay);
  }
}

// =======================================================================
void takePhoto() {
  printStringWithShift("SONRIE!", 0);
  delay(1000);
  digitalWrite(SHUTTER_PIN, LOW);    // turn the LED off by making the voltage LOW
  printStringWithShift("            ", 0);
  delay(1000);
  digitalWrite(SHUTTER_PIN, HIGH);
}

void takePhoto1() {
  printStringWithShift("          10          9           8          7         6         5        4         3        2         1         ", 20);
  takePhoto();
}
void takePhoto2() {
  printStringWithShift("          5        4         3        2         1         ", 20);
  takePhoto();
}

Te dejo el link a la simulación

Mil gracias!! funciona a la perfección.

Teniendo en cuenta que de Arduino he programado... nada, no lo habría resuelto.

Gracias de nuevo!

A la perfección no creo aunque te queda resolver la parte de buttonVal que tiene varios delay() y que van a frenar todo lo anterior cuando valga 0.

buttonVal en realidad sería booleana, si no está en 1 está en 0 así que no entiendo de que hablás, qué se frenaría. Justamente cuando buttonVal se pone a 0 (porque se lee un LOW del pin del botón), dispara la cámara. ¿Qué se frena?

Y algunos no buscamos la perfección, nos conformamos con solucionarle un problema a otro usuario. Claro que si es perfecto mejor que mejor pero no conozco a nadie perfecto por aquí.

Se frena lo que tiene adentro

 printStringWithShift("OTRA ", 0);
    delay(2000);
    takePhoto2();
    printStringWithShift("ULTIMA", 0);
    delay(2000);
    takePhoto2();
    printStringWithShift("LISTO ", 0);
    delay(3000);
    printStringWithShift("           ", 0);

o que crees que hará esta parte del código cuando se ejecute?
Y yo no criticaba a vos @anon90500195, decia que falta analizar esta parte para que no sea bloqueante, se entiende?

Bueno, lo hace. listo. No dije nada.
El OP esta feliz.. a otra cosa mariposa.

Y cuál es el problema si son esperas entre tomas de fotos?
No hay otra cosa que hacer que esperar a que el objetivo haga algún monigote.

@ion_sc Te explico lo que hice:
El programa tiene una función para mostrar una cadena de texto en el display por medio de otra función que va mostando uno a uno cada caracter de esa cadena.
Hasta que no termina una no puede terminar la otra, entonces recién se puede leer el estado del pulsador.
Lo que hice fue entre cada caracter a mostrar chequear si se pulsó el botón y si fue pulsado sale inmediatamente de ambas funciones.
Por eso es un poco "extraña" la función buttonPressed(), para que funcione tanto en una como en la otra función, porque si se detecta la pulsación mientras se presenta un caracter, salga inmediatamente y a la vez salga de la llamó (o sea la que muestra la cadena completa). Y que a su vez sirva para poder verificar si hubo pulsación en esta otra función.
Fijate que incluso se puede usar en el loop().
Te repito, es "rara" pero no es muy difícil de entender.

Aclaración:
Como los condicionales se cumplen cuando la condición es verdadera

if (buttonVal)

equivale a (y es la forma abreviada de )

if (buttonVal == true) // ó HIGH ó 1 ó cualquier valor distinto de 0

y análogamente

if (!buttonEn)  // la negación del valor de la variable

equivale a

if (buttonEn == false)  // 0, LOW

o

if (buttonEn != true)

Saludos

Y esto así no es mas simple?

void loop() {
  printStringWithShift("ACA VA TU MENSAJE           ", 50);
  buttonPressed();

  if (!buttonEn) {
    buttonVal = 1;
    digitalWrite(BUTTON_LED, LOW);
    takePhoto1();
    printStringWithShift("OTRA ", 0);
    delay(2000);
    takePhoto2();
    printStringWithShift("ULTIMA", 0);
    delay(2000);
    takePhoto2();
    printStringWithShift("LISTO ", 0);
    delay(3000);
    printStringWithShift("           ", 0);
    digitalWrite(BUTTON_LED, HIGH);
    buttonEn = true;
  }
}

bool buttonPressed() {
    buttonVal = digitalRead(BUTTON_PIN);
    if (buttonVal)
        buttonEn = false;
    else
        buttonEn = true;
    return buttonEn;
}
1 Like

printStringWithShift() y su dependencia printCharWithShift() son bloqueantes, salir de ellas al pulsar el botón era el problema a resolver.

Si cumple la función, si es más simple.

"Para depurar siempre hay tiempo" nos decía un profesor.

Saludos

Efectivamente,
El problema con el que me conontré era que se bloqueaba en el printCharWithShift().

Respecto al resto de los delay están como tienen que estar. A parte de los que están entre las tomas de fotografías, los que habéis mostrado arriba, están los delay dentro de takePhoto().

Los delays en la función takePhoto() hacen que mientras se muestra la palabra "Sonríe" en el Matrix Led se activa el relé de disparo de la cámara. Que por las pruebas que he hecho, tendré que añadir un relé con más tiempo para realizar el enfoque, ya que con 2 segundos hay veces que no alcanza a enfocar y disparar.

Por otro lado, como se comentaba arriba, están los delays entre fotos. No hay nada más que hacer mientras se espera a la segunda y tercera foto.

Muchas gracias por la solución. como he dicho, me funciona perfectamente, además optimizando el código.

Salud!

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.