Seguidor Solar con LDR y RTC

Hola!!! Mi proyecto consta de un seguidor solar, es con una lupa proyectando un haz de luz, ahora el dispositivo debe seguir la trayectoria del sol de 12:00 a 1:00pm ya tengo el programa, mi problema es que al correrlo no avanza los grados que quiero, (avanza 1 grado cada 4 min.).
Y al desconectar el arduino y al volverlo conectar no hace lo que quiero. :slight_smile:

#include <Wire.h>
#include "Sodaq_DS3231.h"
# include <Servo.h>
Servo panel;

unsigned long time;
float tiempo = 240000.0;//¬† AVANZA UN GRADO cada 4 min, HASTA LLEGAR A LOS 67¬į
unsigned long  t =0;

int pos_default =53;
int pos_default_add=53;
boolean act_1,act_2=false;
char DiaSemana[][0] = {"Dom", "Lun", "Mar", "Mie", "Jue", "Vie", "Sab" };

    // La linea fija la fecha, hora y dia de la semana, se debe suprimir la linea en la segunda carga
    //  dia 1-Lunes (0=Dom, 1=Lun, 2=Mar, 3=Mie, 4=Jue, 5=Vie, 6=Sab)
//DateTime dt(2019, 8, 4, 15, 0, 0, 0);

void setup () 
{
    Serial.begin(9600);
    Wire.begin();
    rtc.begin();
   // La linea fija la fecha, hora y dia de la semana, se debe suprimir la linea en la segunda carga 
//rtc.setDateTime(dt);
  
  panel.attach(2);
  panel.write(53);
}

void loop () 
{
 
  
  int LDR = analogRead(A0);
  
 Serial.print("SENSOR_LDR ");
 Serial.println(LDR);
 
    DateTime now = rtc.now();
    Serial.print(now.year(), DEC);
    Serial.print('/');
    Serial.print(now.month(), DEC);
    Serial.print('/');
    Serial.print(now.date(), DEC);
    Serial.print(' ');
    Serial.print(now.hour(), DEC);
    Serial.print(':');
    Serial.print(now.minute(), DEC);
    Serial.print(':');
    Serial.print(now.second(), DEC);
    Serial.print(' ');
    Serial.print(DiaSemana[now.dayOfWeek()]);
    Serial.println();
   delay(1000); // Se actualiza cada segundo

   if (LDR <=600){
    panel.write(pos_default);
}
if (now.hour()>=12&& now.minute()>=0&& now.second()>=0){
  act_1=true;
 
}

  
if ( act_1==true){
       
         time=millis();
        if(time-t > tiempo){
         t=time;
         pos_default_add++;
          }
        if(LDR > 800){
       panel.write (pos_default_add);
       Serial.print("GRADOS SERVO");
       Serial.println(pos_default_add);
        }
}

if(pos_default_add >=67){
  act_1=false;
}
if(pos_default_add >=67&& now.hour()>=13&& now.minute()>=0){     
         pos_default_add =53;
         panel.write (pos_default);
         
}
}

Hola Brandonsaank31. Hay un par de cosas que no has explicado o no me han quedado claras.

Una es los grados 53 y 67. Parece como que quieres que si no es est√° entre las 12:00 y las 13:00 el servo ha de ponerse a 53 grados y que el m√°ximo que quieres que alcance es 67 grados, porque a las 13:00 ha de volver a la posici√≥n de inicio. ¬ŅEs eso as√≠? ¬ŅA las 12:00 en punto a cuantos grados ha de estar a 53 o a 54? ¬ŅY a las 13:00 en punto ha de ‚Äúregresar‚ÄĚ a los 53 grados?

La otra cosa es el uso de la LDR. Si la lectura es igual o menor de 600 entonces pones el servo a 53 grados. Y si el valor es mayor que 600 y menor o igual que 800 no mueves el servo. El servo lo mueves si la lectura de la LDR es mayor que 800. ¬ŅQu√© se supone que quieres hacer con la LDR?

Un detalle: no declares la variable tiempo como float. Has de declararla como unsigned long y a dem√°s has de indicar que el 240000 es un unsigned long poniendo a ese n√ļmero el sufijo UL de tal forma que sea 240000UL. La l√≠nea de c√≥digo ha de quedar tal que as√≠:

unsigned long tiempo = 240000UL;//¬† AVANZA UN GRADO cada 4 min, HASTA LLEGAR A LOS 67¬į

Seguramente al principio la ten√≠as declarada como unsigned long pero te ‚Äúhac√≠a cosas raras‚ÄĚ porque pusiste 240000 en lugar de 240000UL. Si no pones el UL el compilador en lugar de considerarlo como un entero de 32 bits lo interpreta como un entero de 16 bits y lo ‚Äútrunca‚ÄĚ, dando un valor ‚Äúextra√Īo‚ÄĚ menor o igual que 65535.

En cuanto a hacer que tu programa funcione correctamente, para este caso en concreto, yo enfocaría el programa de una forma diferente a como lo has hecho. Trabajaría con minutos del día. Por ejemplo: calcularía las doce del mediodía como (12 * 60). Y para calcular el minuto del día sería algo así como:

    int minutoActual = now.hour() * 60 + now.minute();

Una vez calculado minutoActual: si se supone que antes de las 12:04 ha de estar a 53 grados y que cada 4 minutos que pase de las 12:00 ha de incrementar un grado hasta un máximo de 67 grados, y que superado este ángulo ha de volver a los 53 grados hasta las 12:00 del siguiente día, yo calcularía el ángulo así:

    int angulo = 53 + (minutoActual - (12 * 60)) / 4; // Calculamos los minutos que pasan de las 12:00 horas los dividimos entre 4 y se lo sumamos a los 53 grados iniciales
    if ((angulo < 53) || (angulo > 67)) { // Sólo ha de tener un valor entre 53 y 67 grados
        angulo = 53;                      // En caso contrario su valor se establece a 53 grados
    }

Tendr√≠a guardado la ‚Äúposici√≥n anterior‚ÄĚ del servo la comparar√≠a con la posici√≥n ahora calculada que debe tener el servo en funci√≥n de la hora del d√≠a y del LDR (si depende del LDR, que su dependencia no la conozco). Y si la nueva posici√≥n calculada es diferente a la anterior entonces actualizo el servo y guardo esa nueva posici√≥n como la ‚Äúposici√≥n anterior‚ÄĚ.

En principio no necesitarías ni tan siquiera usar las funciones delay() ni millis(). Simplemente basta con leer continuamente la hora del RTC y actualizar la posición del servo.

Lo que sigo sin tener clara es lo que quieres hacer con la LDR. Pero creo que te bastar√≠a con que una vez calculado el √°ngulo seg√ļn la hora del d√≠a lo ajustes seg√ļn el valor de la LDR.

Lo que quiero que haga el programa es que, empezara en una posicion inicial que son los 53¬į cuando llegue a las 12:00pm empezara a moverse un grado cada 4 min.(empezando desde los 53¬į) hasta llegar a la 13:00pm que son los 67¬į .

El LDR no se muy bien como ocuparlo aca, sinceramente me fueron asesorando pero no conozco muy bien a arduino, pero la intencion del LDR es que si captura menos de 600 regresa a su estado por default que son los 53¬į

Cuando captura mas de 800 que es una luz solar moderada, empezara a moverse, pero el LDR no hace que mueva al servo, en si es el RTC, el LDR solo lo ocupo para regresar a posicion inicial si no hay luz solar, pero cuando detecta, regresa a los grados que debe llevar respecto al RTC(no se si me explico).

Sinceramente me gusta mucho la programacion, pero me falta mucho por aprender y en este proyecto me quedo perdido.

Agradeceria mucho tu ayuda..... :slight_smile:

Tu dispositivo funcionará bien para cierta posición del sensor, pero basta con que se mueva el sensor solo un poco para que los resultados no sean repetitivos. Tenlo muy presente!!

Seguramente esto te parecer√° demasiado complejo pero lo mejor hoy en dia es leer la posici√≥n del Sol de las paginas Web que lo hacen y de ese modo te aseguras coherencia durante todo el a√Īo.

El LDR si no lo usas de modo diferencial se comportar√° de maneras que considerar√°s err√°ticas y es como todo lo que se lee de modo com√ļn.
Imagina un dia soleado vs un dia nublado.. cual crees que ser√° el comportamiento del LDR en ambas condiciones?

Antes que nada, si est√°s usando el pin 2 en un Arduino UNO para el servo, eso no te va a funcionar. Tienes que conectarlo a uno que soporte PWM. Estos est√°n se√Īalados con el s√≠mbolo ~ al lado del n√ļmero. En el Arduino UNO son los pines 3, 5, 6, 9, 10 y 11. En este caso he usado el 9. As√≠ que conecta el servo al pin 9 o cambia la definici√≥n de la constante PIN_SERVO en el programa si pones el servo en otro pin con PWM (si tu Arduino soporta el PWM en el pin 2 entonces pon el 2).

Lamento no haber puesto antes mi propuesta y que esté poco documentada. Pero no dispongo de tanto tiempo libre como quisiera, para poder trastear con el Arduino.

Como no tengo ni la librer√≠a ni el RTC, para las pruebas y no tener que estar esperando mucho, me he ‚Äúinventado‚ÄĚ el minuto del d√≠a entre las 11:55 y las 13:03. Durando s√≥lo dos segundos cada minuto ‚Äúsimulado‚ÄĚ. Si observas por el monitor serie ver√°s que te va indicando la hora y minuto del d√≠a (simulado si no est√° activado el RTC) y si est√° ‚ÄúACTIVO‚ÄĚ porque le llega luz suficiente al LDR.

En cuanto al LDR, he creído que los valores de lectura de 600 y 800 son para dejar una franga de histéresis de 200. Se activa si el valor es superior a 800 y se desactiva si es inferior a 600, entre esos dos valores conserva el estado.

He quitado la parte de las ‚Äúpruebas‚ÄĚ del RTC. Si quieres que funcione con el RTC has de comentar una l√≠nea y descomentar otra, en el c√≥digo hay una l√≠nea en el medio con un comentario que te lo indica. As√≠ simula el tiempo:

    int minutoDelDia = (((HORA_DE_INICIO) * 60 + (MINUTO_DE_INICIO)) - 5) + ((millis() / 2000) % (((MAX_GRADOS) - (MIN_GRADOS) + 2) * (MINUTOS_POR_GRADO) + 5));
    // Comentar la línea anterior y descomentar la siguiente línea para usar el RTC
    //int minutoDelDia = now.hour() * 60 + now.minute();

Y as√≠ va seg√ļn el RTC:

    //int minutoDelDia = (((HORA_DE_INICIO) * 60 + (MINUTO_DE_INICIO)) - 5) + ((millis() / 2000) % (((MAX_GRADOS) - (MIN_GRADOS) + 2) * (MINUTOS_POR_GRADO) + 5));
    // Comentar la línea anterior y descomentar la siguiente línea para usar el RTC
    int minutoDelDia = now.hour() * 60 + now.minute();

Lo he probado sin la parte del RTC. Con lo del RTC no lo he podido probar por falta de librer√≠as y el propio RTC, as√≠ que no garantizo que compile y funcione tal como est√° el c√≥digo ya que lo del RTC lo he a√Īadido ‚Äúa ciegas‚ÄĚ.

Sin más, aquí tienes el código:

#include <Wire.h>
#include "Sodaq_DS3231.h"
#include <Servo.h>

#define MIN_GRADOS                 53   // Grado inicial o mínimo
#define MAX_GRADOS                 67   // Grado final o máximo

#define HORA_DE_INICIO             12   // Hora del día en que comienza (con el grado mínimo)
#define MINUTO_DE_INICIO            0   // Minuto del día en que comienza
#define MINUTOS_POR_GRADO           4   // Cada cuantos minutos se incrementa un grado
#define LECTURA_LDR_ACTIVO        800
#define LECTURA_LDR_INACTIVO      600

#define PIN_LDR                    A0
#define PIN_SERVO                   9

int minutoDelDiaAnterior = -1;  // Lo inicializamos a un valor incorrecto para asegurarnos que nada más empezar detecta un cambio
int anguloAnterior = -1;        // Lo inicializamos a un valor incorrecto para asegurarnos que nada más empezar detecta un cambio
boolean activo = false;
Servo panel;

void setup() {
    Serial.begin(9600);
    Wire.begin();
    rtc.begin();
    // La linea fija la fecha, hora y dia de la semana, se debe suprimir la linea en la segunda carga 
    //rtc.setDateTime(dt);

    panel.attach(PIN_SERVO);
}

void loop() {
    DateTime now = rtc.now();

    int minutoDelDia = (((HORA_DE_INICIO) * 60 + (MINUTO_DE_INICIO)) - 5) + ((millis() / 2000) % (((MAX_GRADOS) - (MIN_GRADOS) + 2) * (MINUTOS_POR_GRADO) + 5));
    // Comentar la línea anterior y descomentar la siguiente línea para usar el RTC
    //int minutoDelDia = now.hour() * 60 + now.minute();

    int hora = minutoDelDia / 60;
    int minuto = minutoDelDia % 60;
    int angulo = MIN_GRADOS;                // Por defecto asumimos que va a tener el mínimo ángulo posible. Si no está activo se mantendrá este ángulo
    int activoAnterior = activo;
    int valorLDR = analogRead(PIN_LDR);
    if (valorLDR > LECTURA_LDR_ACTIVO) {
¬† ¬† ¬† ¬† activo = true; // Hay luz suficiente, as√≠ que se activa (si a√ļn no lo estaba)
    }
    if (valorLDR < LECTURA_LDR_INACTIVO) {
¬† ¬† ¬† ¬† activo = false; // Hay poca luz, as√≠ que se desactiva (si a√ļn lo estaba)
    }
    if (activo) { // Si está activo...
¬† ¬† ¬† ¬† // Calcluamos el √°ngulo que ha de tener seg√ļn el minuto del d√≠a
        angulo = MIN_GRADOS + (((hora * 60 + minuto) - ((HORA_DE_INICIO) * 60 + (MINUTO_DE_INICIO))) / (MINUTOS_POR_GRADO));
        if ((angulo < MIN_GRADOS) || (angulo > MAX_GRADOS)) {
            // Si el ángulo calculado está fuera del rango permitido entonces le asignamos el ángulo mínimo
            angulo = MIN_GRADOS;
        }
    }
    if ((minutoDelDia != minutoDelDiaAnterior) || (angulo != anguloAnterior) || (activo != activoAnterior)) {
¬† ¬† ¬† ¬† // Como ha habido alg√ļn cambio de la hora, del √°ngulo o del estado activo: mostramos los datos en el monitor serie
        Serial.print((hora < 10) ? F("0") : F(""));
        Serial.print(hora);
        Serial.print((minuto < 10) ? F(":0") : F(":"));
        Serial.print(minuto);
        Serial.print(F("  "));
        Serial.print(angulo);
        Serial.print(F(" grados"));
        Serial.println(activo ? F("  ACTIVO") : F(""));
    }
    if (angulo != anguloAnterior) {
        // Si ha cambiado el ángulo: actualizamos la posición del servo
        panel.write(angulo);
    }
    minutoDelDiaAnterior = minutoDelDia;
    anguloAnterior = angulo;
}