Dimmer sin delay, no funciona (SOLUCIONADO)

Hola: Estoy haciendo la regulacion de un motor de escobillas y para la prueba uso una bombilla.

Estoy usando un Dimmer Zero crosing de este tipo

|500x378

Tengo que detectar el paso por cero y luego dar la senal. Todo funciona bien usando delay. Pego aqui el codigo usando delay:

const byte ZCP = 2;
const unsigned int dim =5;   //brillo de la bombilla al 50%


void setup() {

  pinMode(ZCP, INPUT);
  pinMode(3, OUTPUT);
  digitalWrite(3, LOW);

}

void loop() {

  if (digitalRead(ZCP) == HIGH){    
    Zero_Cross();    
  }
}

void Zero_Cross() {

digitalWrite(3, LOW);
delay(dim);
digitalWrite(3, HIGH);
}

Pero yo no quiero usar delay en mis proyectos, asi que pruebo con millis(). Pero no hay manera a regular, la bombilla me luce siempre al 100%.

Os pego aqui el codigo usando millis():

const byte ZCP = 2;
unsigned long previousMillis = 0;
unsigned long currentMillis = 0;
const long interval = 5;  //brillo de la bombilla al 50%


void setup() {
  pinMode(ZCP, INPUT);
  pinMode(3, OUTPUT);
  digitalWrite(3, LOW);

}

void loop() {
  if (digitalRead(ZCP) == HIGH){    
    Zero_Cross();    
  }

}

void Zero_Cross() {
  currentMillis = millis();
  if (currentMillis - previousMillis >= interval) {
    digitalWrite(3, HIGH);
    previousMillis = currentMillis;
  }else{
    digitalWrite(3, LOW);
  }
  
}

Fijo que es un error tonto. Alguna idea. Gracias

Y pretendes detectar el paso de cruce x cero usando una simple instrucción digitalRead?

Eso si es tarea para las interrupciones xq debes detectar el cruce e inmediatamente tomar el tiempo para tu acción de control de brillo.

Ahora si buscas Arduino phase control encontrarás la rutina hecha.

surbyte: Y pretendes detectar el paso de cruce x cero usando una simple instrucción digitalRead?

Eso si es tarea para las interrupciones xq debes detectar el cruce e inmediatamente tomar el tiempo para tu acción de control de brillo.

ok ok, por eso veia en los ejemplos todos que usaban la interrupcion. ¿pero por algun motivo? ¿por la rapidez de lectura? No usaba la interrupcion por miedo a que interfiriese en el resto de sketch, pues este que puse aqui es solo para el control del motor.

Muchas gracias, voy trastear un poco mas.

Justamente una interrupcion lo que no hace es insterferir siempre que respetes el hecho que debe ser veloz. Eso significa, nada de enviar Serial.print o cosas por BT o cuestiones lentas. No poner delay(). Es ir, leer, sumar algo, cambiar un pin y salir Se puede hacer mucho mas, solo generalizo.

cuando veas el ejemplo que te dije, verás todo lo que se hace. Ese ejemplo puede resultar complejo, pero puedes buscar lo mismo en google y encontrarás cosas mucho mas fáciles de usar.

Aqui ando de nuevo peleando. He estado mirando Arduino Phase control, pero me cuesta entender el codigo.. mis conocimietos son bastante limitados.

He seguido peleandome con el mio.

const byte ZCP = 2;
unsigned long previousMillis = 0;
unsigned long currentMillis;
unsigned long tiempo = 5;    //brillo al 50%
int contador;


void setup() {
  Serial.begin(9600);
  pinMode(ZCP, INPUT_PULLUP);
  pinMode(3, OUTPUT);
  digitalWrite(3, LOW);
  attachInterrupt(0,Zero_Cross,FALLING);

}

void Zero_Cross() {
  contador++;  
}

void loop() {  
  if (contador >0){
    unsigned long currentMillis = millis();
    if (currentMillis - previousMillis >= tiempo){
    Serial.println("HIGH");
    previousMillis = currentMillis;
    contador=0;   
  }
  }else{
    Serial.println("LOW");
  }
  
  }

Tengo que decir que en el monitor serial me aparece bien un HIGH y un LOW (mas bien un HIGH y dos LOW).... pero vaya... que si meto la salida a la bombilla esta esta siempre al 100% de luminosidad.

¿Acaso es complicado hacer Phase control con millis?

No entiendes como funciona esto. Primero has verificado que tienes un puslo cada 10 mseg?

El zero cross interrupt esta disparando la interrupción cada 10 mseg. Entonces tu contador se incrementa pero para hacerlo antes hay que ponerlo a 0. Y cuando lo pongo a 0, cuando cumplo lo que quiero hacer, o sea a los 5 mseg.

void Zero_Cross() {
  contador++; 
}

Pero para que eso ocurra contador tiene que ser una variable que tenga la etiqueta

volatile

debe lucer asi

volatile int contador;

Prueba de este modo.

Hola; Pues aqui estoy de nuevo.. y no para bien. Pues no tengo manera de hacer el control de fase con un millis(), y hay que reconocer el codigo no parece demasiado complejo.

Consigo monitorizar la fase y sus correspondientes pasos por cero, es decir que eso funciona correcto. Pero el problema es que a partir de ahi quiero regular la intensidad de una bombilla y no hay manera. O brilla al 100% o parpadea como una loca.

El codigo se base en detectar el pase por cero y sumar a un contador. A partir de ahi temporizo para dar señal.

|500x341

Aqui el codigo

const byte ZCP = 2;
unsigned long currentMillis;
unsigned long previosMillis;
unsigned long tiempo;
volatile int contador;


void setup() {
  Serial.begin(9600);
  pinMode(ZCP, INPUT_PULLUP);
  pinMode(3, OUTPUT);
  digitalWrite(3, LOW);
  attachInterrupt(0,Zero_Cross,FALLING);
  contador=0;
}

void Zero_Cross(){
  contador++;  
}

void loop() {
  tiempo=currentMillis-previosMillis;
  Serial.println(tiempo);
  
  if (contador==1){ //detecto el pase por cero
    currentMillis=millis();
    if (currentMillis-previosMillis>= 5){  // Puesto que la onda completa son 10ms, doy señal en la mital de la onda.. Esto quiere decir que el brillo de la bombilla sera al 50%
      Serial.println("HIGH");                    
    }else{
      Serial.println("LOW");         //simpre que no este en HIGH estara en LOW
    }
  }
  if (contador >= 2){  //cuando completa una onda completa, volvermos empezar repitiendo el ciclo
    previosMillis = currentMillis;  //resetamos el conometro
    contador=1;  //volvemos a la "if" de arriba
  }   
}

He visto alguna que otra libreria por ahi, pero desconozco si usan delay en sus rutinas...pues es justo lo que no quiero. En el proyecto este tengo que usar millis() o micros().

Muchas gracias

Para comenzar tu código esta mal enfocado.
Si usas interrupciones, la idea esque estas hagan la tarea.

Bueno desempolvé un código que tenia de demostración para mis alumnos donde iba y volvía haciendo un dimmer

#include  <TimerOne.h>          // http://www.arduino.cc/playground/Code/Timer1
volatile int i = 0;             // Variable contador volatile porque se usa con la interrupción
volatile boolean zero_cross=0;  // Esto nos dice si estamos o no en crossed zero
int AC_pin    = 3;              // Salida al Triac
int dim       = 64;              // Nivel de Dimming (0-128)  0 = on, 128 = 0ff
int inc       = 1;              // Contador up o down, 1=up/ascendente, -1=down/descendente

int freqStep = 75;    // Estes es el retardo para el paso del brillo en microsegundos.
                      // Calculado en función de la frecuencia de la red (50Hz) para 60 hz se debe usar otro valor
// 
// Hay que entender que hay dos cruces x cero por ciclo, lo que significa
// que el curce ocurre a 100Hz para una alimentación de 50Hz o 20 mseg de períoodo. 

// Para calcula la freqStep dividir la longitud de una onda senoidal en useg x el numero de pasos de brillo
// 100Hz = 10000uSeg 10000 uSeg/ 128 steps = 75uS/step

void setup() {                                     
  pinMode(AC_pin, OUTPUT);                          // Configuro el pin del Triac
  attachInterrupt(0, zero_cross_detect, RISING);    // La interupcion en el pin 2 para detectar el cruce x cero
  Timer1.initialize(freqStep);                      // Inicializo TimerOne library para la frecuencia que usaremos
  Timer1.attachInterrupt(dim_check, freqStep);      
  // La libreria TimerOne usa el TIMER1 de modo que generará una interrupción (dim_check) que apunta a la función
  // que usaremos para chequear si es el momento adecuado para disparar el triac
  // Esto ocurre cada 75 useg en 50hz.

void zero_cross_detect() {    
  zero_cross = true;               // pone en 1 cuando hay un cruce x cero
  i=0;
  digitalWrite(AC_pin, LOW);       // apago el TRIAC
}                                 

// Disparo el TRIAC en el momento adecuado
void dim_check() {                   
  if (zero_cross) { // si esta en 1 entonces
      if (i>=dim) {                     
          digitalWrite(AC_pin, HIGH); // Enciendo las luces
          i = 0;    // reseteo el contado de tiempo
          zero_cross = false; // reseteo el detector de cruce x cero
      } 
      else {
          i++; // incremento el contador de tiempo
      }                                
  }                                  
}                                   

void loop() {    
  dim = 64; // 64 es 128/2 o sea 50% del brillo.
}

En mi loop tenia esto

  dim += inc;
  if ((dim>=128) || (dim<=0))
      inc*=-1;
  delay(18);

No me preguntes porque delay(18) solo debes ser un retardo para poder apreciar el cambio de brillos de mi demo de dimmer.
El código que te puse debería mostrar el 50% de brillo. Verifica que los pines usados para el triac coincide con el tuyo.

Muchas gracias por la respuesta. Probare y te comento. Saludos

exit status 1 'zero_cross_detect' was not declared in this scope

Da error al compilar. No entiendo porque da este error. La interrupcion esta bien declarada.

Y no sabes resolverlo por tu cuenta?

cambia de lugar zero_cross_detect()!!

Quedó entre setup y loop y mi compilador Platform.IO lo permite pero el IDE no. Ubicala donde el IDE quiere que esten las cosas.

Hola:
Siento mucho mi ignorancia, pero me cuesta a veces ver esto claro.

Resulta que no daba con la solucion, segun tu me comentabas la interrupcion tenia que estar colocada en un lugar determinado. La coloque en todos los sitios posibles pero no funcionaba.

Resulta que al final lo que era es que en el codigo que me pasaste faltaba la llave “}” de cierre en void setup… que cosas… lo que no entiendo es porque marcaba ese error.

Copio y pego aqui el codigo corregido.

#include  <TimerOne.h>          // http://www.arduino.cc/playground/Code/Timer1
volatile int i = 0;             // Variable contador volatile porque se usa con la interrupción
volatile boolean zero_cross=0;  // Esto nos dice si estamos o no en crossed zero
int AC_pin    = 3;              // Salida al Triac
int dim       = 64;              // Nivel de Dimming (0-128)  0 = on, 128 = 0ff
int inc       = 1;              // Contador up o down, 1=up/ascendente, -1=down/descendente

int freqStep = 75;    // Estes es el retardo para el paso del brillo en microsegundos.
                      // Calculado en función de la frecuencia de la red (50Hz) para 60 hz se debe usar otro valor
//
// Hay que entender que hay dos cruces x cero por ciclo, lo que significa
// que el curce ocurre a 100Hz para una alimentación de 50Hz o 20 mseg de períoodo.

// Para calcula la freqStep dividir la longitud de una onda senoidal en useg x el numero de pasos de brillo
// 100Hz = 10000uSeg 10000 uSeg/ 128 steps = 75uS/step

void setup() {                                     
  pinMode(AC_pin, OUTPUT);                          // Configuro el pin del Triac
  attachInterrupt(0, zero_cross_detect, RISING);    // La interupcion en el pin 2 para detectar el cruce x cero
  Timer1.initialize(freqStep);                      // Inicializo TimerOne library para la frecuencia que usaremos
  Timer1.attachInterrupt(dim_check, freqStep);     
  // La libreria TimerOne usa el TIMER1 de modo que generará una interrupción (dim_check) que apunta a la función
  // que usaremos para chequear si es el momento adecuado para disparar el triac
  // Esto ocurre cada 75 useg en 50hz.
}

void zero_cross_detect() {   
  zero_cross = true;               // pone en 1 cuando hay un cruce x cero
  i=0;
  digitalWrite(AC_pin, LOW);       // apago el TRIAC
}                                 

// Disparo el TRIAC en el momento adecuado
void dim_check() {                   
  if (zero_cross) { // si esta en 1 entonces
      if (i>=dim) {                     
          digitalWrite(AC_pin, HIGH); // Enciendo las luces
          i = 0;    // reseteo el contado de tiempo
          zero_cross = false; // reseteo el detector de cruce x cero
      }
      else {
          i++; // incremento el contador de tiempo
      }                               
  }                                 
}                                   

void loop() {   
  dim = 64; // 64 es 128/2 o sea 50% del brillo.
}

Lo probare estos dias, muchas gracias otra vez