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;
}