Dormir al arduino de dos maneras diferentes !?

Hola.
Estoy intentando hacer un riego automático con arduino y baterías. Todo correcto, en principio. Para cargar las baterías pretendo utilizar energía solar. He construido un seguidor solar... sin problemas.
Para ahorrar energía pretendo que el arduino se duerma:
_ Toda la noche
_ Cada minuto durante el día.
Funciona perfectamente cada una de ellas por separado...mi problema aparece cuando intento que duerma toda la noche y cada minuto durante el día.

Código:

#include <SolarCalculator.h>
#include <TimeLib.h>
#include <Wire.h>
#include <RTClibExtended.h>
#include <AccelStepper.h>
#include <DHT.h>
#include <DHT_U.h>
#include <LowPower.h>
#include <Adafruit_PCF8574.h>

const int wakePin = 2;    // Usa la interrupción 0 (pin 2) y ejecuta la funcion cuando pin 2 está a LOW

const int dirAzim = 3;    //CW+
const int stepAzim = 13;  //CLK+
const int dirElev = 14;   //CW+
const int stepElev = 15;  //CLK+

const int pinIN1 = 5;       //encendido Válvula 1
const int pinIN2 = 6;       //apagado Válvula 1
const int pinIN3 = 7;       //encendido Válvula 2
const int pinIN4 = 8;       //apagado Válvula 2
const int pinIN5 = 9;       //encendido Válvula 3
const int pinIN6 = 10;      //apagado Válvula 3
const int pinIN7 = 11;      //encendido Válvula 4
const int pinIN8 = 12;      //apagado Válvula 4
const int pinHSuelo1 = A6;  //control Humedad Suelo zona Válvula 1
const int pinHSuelo2 = A7;  //control Humedad Suelo zona Válvula 2
const int pinHSuelo3 = A2;  //control Humedad Suelo zona Válvula 3
const int pinHSuelo4 = A3;  //control Humedad Suelo zona Válvula 4

Adafruit_PCF8574 pcf;

// Location
double latitude = 48.1654743206;
double longitude = -20.5030256541;
int utc_offset;  //cambio automático con utc_ofset_verano_invierno

// Declaramos un RTC DS3231
RTC_DS3231 rtc;

byte AlarmFlag = 0;


//-------------------------------------------------
// Función que se ejecuta al despertar el Arduino
//-------------------------------------------------
void wakeUp() {
  AlarmFlag++; // Fija la bandera para indicar que ha saltado la alarma
}
//-------------------------------------------------
// Función que limpia las alarmas al salir del modo sleep
//-------------------------------------------------
void clearAlarmFunction1() {
  // Elimina cualquier alarma pendiente
  rtc.armAlarm(1, false);
  rtc.clearAlarm(1);
  rtc.alarmInterrupt(1, false);
}
void clearAlarmFunction2() {
  rtc.armAlarm(2, false);
  rtc.clearAlarm(2);
  rtc.alarmInterrupt(2, false);
}

/*Las alarmas del DS3231 pueden generar interrupciones mediante un pin de salida destinado a este propósito.

  Las alarmas pueden configurarse de varias formas, la alarma 1 puede configurarse de 6 formas distintas:

  E_ALM1_EVERY_SECOND – alarma cada segundo.
  E_ALM1_MATCH_SECONDS – los segundos coinciden (una vez por minuto): GFRTC.setAlarm(E_ALM1_MATCH_SECONDS, 0, 0, 10, 1);
  E_ALM1_MATCH_MINUTES – los minutos y segundos coinciden (una vez por hora): GFRTC.setAlarm(E_ALM1_MATCH_MINUTES, 0, 15, 0, 1);
  E_ALM1_MATCH_HOURS – las horas, los minutos y segundos coinciden (una vez por dia): GFRTC.setAlarm(E_ALM1_MATCH_HOURS, 07, 0, 0, 1);
  E_ALM1_MATCH_DAY – el día de la semana, las horas, los minutos y segundos coinciden (semanal).
  E_ALM1_MATCH_DATE – el día del mes, las horas, los minutos y segundos coinciden (una vez por mes).

  Mientras tanto, la alarma 2 puede configurarse de 5 formas:

  E_ALM2_EVERY_MINUTE – alarma cada minuto.
  E_ALM2_MATCH_MINUTES – los minutos coinciden (una vez por hora).
  E_ALM2_MATCH_HOURS – las horas y los minutos coinciden (una vez por día).
  E_ALM2_MATCH_DAY – el día de la semana, las horas y los minutos coinciden (una vez por semana).
  E_ALM2_MATCH_DATE – el día del mes, las horas y los minutos coinciden (una vez por mes).
*/
//-------------------------------------------------
// Función que fija una nueva alarma
//-------------------------------------------------
void setAlarmFunction2() {

  rtc.setAlarm(ALM2_EVERY_MINUTE, 0, 0, 0);
  //rtc.setAlarm(ALM2_MATCH_MINUTES, 0, 30, 0); //cada HORA en el minuto 30. esto no funciona
  rtc.alarmInterrupt(2, true);
}
void setAlarmFunction1() {
  rtc.setAlarm(ALM1_MATCH_MINUTES, 0, 10, 0, 1); //cada hora a los 10 segundos
  //rtc.setAlarm(ALM1_MATCH_HOURS, HoraAlarma, 0, 0, 1); //cada dia a la hora que marque el InicioAlarma.ino
  //rtc.setAlarm(ALM1_MATCH_HOURS, 54, 17, 0);  //cada día a las 18 horas para prueba1 PARA PRUEBA DOS CAMBIAR COMENTARIO
  rtc.alarmInterrupt(1, true);
}



void setup() {
  Serial.begin(115200);
  Wire.begin();
  rtc.begin();

  pinMode(wakePin, INPUT);
  pinMode(stepAzim, OUTPUT);
  pinMode(dirAzim, OUTPUT);
  pinMode(stepElev, OUTPUT);
  pinMode(dirElev, OUTPUT);

  if (!rtc.begin()) {
    Serial.println("No hay un módulo RTC");
    while (1)
      ;
  }

  setSyncProvider(time_provider);
  if (timeStatus() != timeSet) {
    Serial.println("Unable to sync with the RTC");
  } else {
    Serial.println("RTC has set the system time");
  }

  // Fijar a fecha y hora específica. En este ejemplo el 2021-01-01 a las 00:00:00
  //rtc.adjust(DateTime(2024, 1, 6, 20, 44, 0));

  // Set system time to compile time
  //setTime(toUtc(compileTime()));
  // Set time manually (hr, min, sec, day, mo, yr)
  //setTime(15, 01, 00, 5, 1, 2024);
  //set time with RTC. El RTC ha sido puesto en la hora UTC
  DateTime now = rtc.now();

  InitialTrack();

  // Limpia cualquier alarma pendiente
  clearAlarmFunction1();
  clearAlarmFunction2();

  // Fija el pin SQW del RTC a OFF (por defecto está fijado a 1Hz)
  // La salida del pin INT del DS3231 está conectada a este pin
  // Debe conectarse al pin D2 de arduino para despertarse
  rtc.writeSqwPinMode(DS3231_OFF);
}

void loop() {

  //On first loop we enter the sleep mode
  if (AlarmFlag == 0) {

    setAlarmFunction1();
    attachInterrupt(0, wakeUp, LOW);                      //use interrupt 0 (pin 2) and run function wakeUp when pin 2 gets LOW
    LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF);  //arduino enters sleep mode here
    detachInterrupt(0);

    SolarTrack();
 
    
  }
  //clearAlarmFunction1();
  //AlarmFlag = 0;
}

Más Código

// Motor interface type must be set to 1 when using a driver:
#define motorInterfaceType1 1
#define motorInterfaceType2 1

// el sol se mueve 1º cada cuatro minutos. 1 minuto = 0.25º
// Refresh interval, in seconds. 120 seg = 2 min = 0.5º
//int interval = 120;

const double PA = 2205.0;   //pasos por grado Azimut
const double h = 2.0 * PA;  //holgura máquina Azimut = 2º
const double a = 0.125;     //adelanto para intervalo de 60 seg
const double PE = 2635.0;   //pasos por grado Elevación

//create a new instance of the AccelStepper class para cada motor
AccelStepper stepperAzim = AccelStepper(motorInterfaceType1, stepAzim, dirAzim);
AccelStepper stepperElev = AccelStepper(motorInterfaceType2, stepElev, dirElev);

byte Alarm = 0;
void wakeUp1(){
  Alarm++;
}

void InitialTrack() {
  // Set the maximum speed and acceleration:
  stepperAzim.setMaxSpeed(3000L);
  stepperAzim.setSpeed(1000L);         // pasos/segundo
  stepperAzim.setAcceleration(1000L);  // pasos/segundo al cuadrado
  stepperElev.setMaxSpeed(3000L);
  stepperElev.setSpeed(1000L);
  stepperElev.setAcceleration(1000L);
  //iniciamos motores en 0º; mirando al sur y en la horizontal
  stepperElev.runToNewPosition(0);
  stepperAzim.runToNewPosition(0);
}

void SolarTrack() {
 Serial.println("Al curre");
  double az, el;

  CambioHora();

  setSyncProvider(time_provider);


  time_t utc = now();

  // Calculate the solar position, in degrees
  calcHorizontalCoordinates(utc, latitude, longitude, az, el);

  // Print results
  if (el > 0) {

    if (Alarm == 0) {
      
      setAlarmFunction2();
      attachInterrupt(0, wakeUp, LOW);                      //use interrupt 0 (pin 2) and run function wakeUp when pin 2 gets LOW
      LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF);  //arduino enters sleep mode here
      detachInterrupt(0);

      Serial.println("A trabajar");

      //ejecutamos movimiento azimut
      stepperAzim.runToNewPosition(((az - 180 + a) * PA) + h);  //para interval=120
      //ejecutamos movimiento elevación
      stepperElev.runToNewPosition(el * PE);

      Serial.print("fecha y hora local: ");
      Serial.print(day(utc));
      Serial.print("/");
      Serial.print(month(utc));
      Serial.print("/");
      Serial.print(year(utc));
      Serial.print(" ");
      Serial.print(hour(utc) + utc_offset);
      Serial.print(":");
      Serial.print(minute(utc));
      Serial.print(":");
      Serial.println(second(utc));

      Serial.print("azimut: ");
      Serial.print(az);
      Serial.print(" ==> ");
      Serial.print(az - 180.0);

      Serial.print(" elevación: ");
      Serial.println(el);

      //SENSORES Y DEMÁS AQUÍ
      Serial.println("descanso");
      clearAlarmFunction2();
      Alarm = 0;
    }
    

  } else {

    //ejecutamos movimiento: centramos la placa al sur
    stepperAzim.runToNewPosition(0);
    stepperElev.runToNewPosition(0);

    Serial.print("fecha y hora local: ");
    Serial.print(day(utc));
    Serial.print("/");
    Serial.print(month(utc));
    Serial.print("/");
    Serial.print(year(utc));
    Serial.print(" ");
    Serial.print(hour(utc) + utc_offset);
    Serial.print(":");
    Serial.print(minute(utc));
    Serial.print(":");
    Serial.println(second(utc));

    Serial.print("azimut: ");
    Serial.print(az);
    Serial.print(" ==> ");
    Serial.print(az - 180.0);

    Serial.print(" elevación: ");
    Serial.println(el);

    clearAlarmFunction1();
    AlarmFlag = 0;
  }
}


time_t toUtc(time_t local) {
  return local - utc_offset * 3600L;
}

// Code from JChristensen/Timezone Clock example
time_t compileTime() {
  const uint8_t COMPILE_TIME_DELAY = 8;
  const char *compDate = __DATE__, *compTime = __TIME__, *months = "JanFebMarAprMayJunJulAugSepOctNovDec";
  char chMon[4], *m;
  tmElements_t tm;

  strncpy(chMon, compDate, 3);
  chMon[3] = '\0';
  m = strstr(months, chMon);
  tm.Month = ((m - months) / 3 + 1);

  tm.Day = atoi(compDate + 4);
  tm.Year = atoi(compDate + 7) - 1970;
  tm.Hour = atoi(compTime);
  tm.Minute = atoi(compTime + 3);
  tm.Second = atoi(compTime + 6);
  time_t t = makeTime(tm);
  return t + COMPILE_TIME_DELAY;
}

time_t time_provider() {
  return rtc.now().unixtime();
}

El segundo código es un archivo .ino separado.
Cualquier sugerencia sería bienvenida.
Gracias de antemano

Xosé

Hola @grilo!!.

1ra pregunata:
No es que el rtc a traves de su interrupcion externa (alarm irq) esta despertando a tu Arduino?

2da pregunta:
Porque repites esto en distintos lugares del código?

      setAlarmFunction2();  // aveces le pones setAlarmFunction1
      attachInterrupt(0, wakeUp, LOW);                      //use interrupt 0 (pin 2) and run function wakeUp when pin 2 gets LOW
      LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF);  //arduino enters sleep mode here
      detachInterrupt(0);

no es buena práctica tenerlo en diferentes lugares.
Tiene que ser un solo sitio y donde puedas controlar las condiciones que lo pongan a dormir.

hola Surbyte.
Gracias por tu pronta respuesta.
El código es diferente:
1.- "setAlarmFunction1()"
2.- "setAlarmFunction2()"
Entiendo que cada alarma es diferente, una para despertar cada hora, en el código, y la otra cada minuto.

con respecto a tu primera pregunta:
-arduino despierta.
-ejecuta el código una vez, cuando tendría que ejecutarlo repetidamente hasta el anochecer, y...
se queda ahí.

Entendí que es diferente lo que no entendí porque no lo invocas a un mismo punto con diferentes tiempos lo que es lo mismo visto de otro modo pero al llamar a la misma función tienes mas control de lo que esta sucediendo.
Supongamos que setAlarmFunction2 lo pone a dormir por 1hora y setAlarmFunction1 lo pone a dormir por 3 hs
Entonces usas setAlarmFunction(unsigned long tiempo) donde tiempo es la variable con el tiempo que deseas que duerma y listo.
Que cambia? Qué desde mi punto de vista tienes en una sola todos los llamados.
Que sabes de donde vienes para las tareas de debug. Puedes en las tuyas hacerlo, claro que si, pero duplicarás cosas.

Lo veré con me detalle para darte una mejor respuesta.

1 Like

Tal cual, usa una sola alarma pero prográmala diferente según el horario.
Por ejemplo, a las 23:59 la programas para que dispare la interrupción a las 8:00:00.
Cuando despierta a las 8:00 la programas para que dispare cada minuto.

gracias a ambos por la respuesta.
El problema es un poco más serio. Despertarlo cada minuto no tiene problema. El problema es que la que tiene que despertar cada día (estaba haciendo pruebas cada hora) no tiene una hora fija de despertarlo. Tiene que despertar al amanecer y cada día es a una hora diferente.
Intentaré lo que decís este fin de semana y os cuento como va.
un saludo
Xosé

Este tema de @victorjam puede serte de utilidad

Okay se despierta con la salida del sol por ejemplo. Cual es el problema? usas una libreria que la calcule según tu lat y long, y la estableces antes de ponerlo a dormir

La librería que uso es "Solar Calculator". Funciona perfectamente.
Si no duermo al arduino todo funciona.
Si duermo al arduino y lo hago despertar cada minuto, funciona perfectamente.
Si lo duermo, y lo hago despertar a una hora cualquera del día, también funciona.
La idea es que desde el amanecer al ocaso (mientras hay sol) se despierte cada minuto, coloque el seguidor en las coordenadas correctas y vuelva a dormir, además, al ponerse el sol deberá irse a dormir hasta el día siguiente.
Cuando pongo dos alarmas diferentes para despertar al arduino, no funciona ninguna.
este es el problema.

como se puede ver en la captura de pantalla, después de anochecer sigue repitiéndose el código cada minuto.
¿comó pongo al arduino a dormir toda la noche?

En el minuto y hora que quieras que "pernocte":
Desactivas la alarma de cada minuto.
Seteas y activas alarma del amanecer.

Agradezco vuestros esfuerzos.
La realidad es que eso que me contáis ya lo he hecho, porque, cuando pregunto, ya le he dado todas las vueltas posibles.
Seguí probando, una y otra vez hasta el aburrimiento... y cambié la idea.
¿Y si el problema no estaba realmente en despertar y dormir al arduino?
Funcionó!!!!
Cambié el if por un while y SOLUCIONADO.
No tengo acabado el proyecto, aunque ya funciona a la perfección.
Cuando lo acabe, lo incluiré en "Proyectos" por si a alguien le sirve.
Un saludo

Xosé

Moderador:

No incluyas en proyectos esto porque no es un proyecto solo es la respuesta a tu pregunta/consulta que puede servirle a los demás.

Lee en la sección Proyectos, qué cosas se consideran proyectos. Te pondrían un link pero allí esta.

Si cada uno de los que aqui postea, luego lo volviera a postear en Proyectos, primero incurres en un doble posteo (sancionable con 1 dia de baneo) y segundo con qué autoridad lo haces, si viniste al foro, preguntado cómo resolver algo.

Ya tu hilo quedó resuelto. El que venga y quiera buscar lo va a encontrar.
El tema es que todos vienen y hacen la fácil, no encuentro, busqué 1 semana y no encontré nada, y resulta que todas o casi el 98% de las preguntas de este foro ya han sido respondidas.
Hoy la gente es muy cómoda y no se esfuerza.
Y no hablo de este tema ni de ti, hablo de los demás.

Entonces tu proyecto ya esta difundido y es público.
Déjalo asi y acá.

A sus ordenes!!!
1.- evidentemente, lo posteado aquí no es in proyecto. solo unos trozos de código que me daban problemas.
2.- el proyecto completo es un sistema de riego basado en energía solar, con un sistema de seguimiento del sol basado en cálculos de trayectoria solar.
3.- no entiendo la prohibición, pero lo dicho: !!A sus ordenes!!

un saludo

Xosé

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