Manejo de tiempo de encendido en semáforo con RTC

Estimados, buenas noches, estoy haciendo dos semáforo de 3 vías con dos Megas 2560 y quiero controlar el tiempo de encendido entre rojo, amarillo y verde con los segundos que me entrega un RTC DS3231, o sea que yo pueda configurar la cantidad de segundos que quiero permanezcan encendidos y usar el RTC por su exactitud, probé con la función (millis), pero tengo el problema que empiezan a desincronizar en poco minutos entre los dos semáforos. Realmente no entiendo como podría usar la función (second) para configurar el encendidos de los leds y poder lograr que queden sincronizados los dos semáforos. Agradezco si alguien puede ayudarme.

Para sincronizar dos dispositivos distintos tienes que proveer una señal entre ambos que sirva de sincronismo. No puedes pretender que dos elementos aislados se sincronicen solo porque tu crees que lo harán. Entonces crea una secuencia tal que en el arranque uno encienda y envie una señal al segundo, el segundo o esclavo de éste, en su arranque estará esperando la aparición de esta señal. Cuando lo haga, toma ese momento usando millis() y devuelve una respuesta al primero. En ese momento ambos estarán sincronizados por algunos pocos milisegundos. Cual es la diferencia.

Llamemos A1 al arduino 1 y A2 al esclavo.

A1 => (envía) una señal o sea pone un pin en HIGH, tomas ese momento como T1 en el A1. A2 espera ese cambio en un pin, cuando lo detecte tomas ese momento como T1 en A2 y contesta con un pin en alto tmb. A1 va ahora a esperar ese cambio. cuando lo detecte tendras ese T2.

Bien T2-T1 ha sido el tiempo de reacción de todo el conjunto viste desde la perspectiva de A1. Si fuera menor a 1 eso coincidiría con tu error ya que no puede medir nada mejor que 1 milisegundo.

O sea que podrias tener sincronizados los dos Arduinos dentro de ese milisegundo.

Para ser mas precisos podrías repetir todo usando micros() que es la versión 1/1000 de milis(). Repetir la prueba y ver cuantos useg demora en ir y volver la confirmación. Si te da menos de 1000 useg entonces estas dentro de lo que puedes manejar. Pero a la larga esto fallaría también, ahora que lo pienso mejor.

Ahora podrias siempre usar esos pines para de algun modo mantener el sincronismo entre ambos y olvidarte de arrastrar errores, ya que si suponermos tienes 1 mseg de error, en 1000 ciclos tu error sera de 1000 milisegundos o 1 seg, que ya no es poco, y sigues sumando y fallando.

Entonces lo mejor es que el sincronismo lo de una señal. Cuando comienza la secuencia cambias el esado del pin (low a high) y el segundo Arduino sabrá que desde ahi debe hacer su secuencia del mismo modo que A1 pero claro con otros colores.

Entonces habrá algunos useg entre ambos pero siempre serán esos useg.

Estimado, muchas gracias por su respuesta. Tengo el problema de que los semáforos van a estar a 800 metros uno de otro, por lo que se me complica sincronizar de ese modo, por eso pensé en un RTC, y luego remplazar este por un GPS en cada equipo, con eso ya tendría en cada placa dos datos exactamente iguales, la hora, pero no se como poder usar ese dato y configurar para que cuente los segundos que yo programe, y encienda o apague cada salida. Si yo tengo los segundos exactos, los que me provee los GPS, en el semáforo 1 , por ejemplo, 10:00:00, también lo tendré igual en el semáforo 2, la idea seria, prende el verde desde el segundo :00 hasta el segundo :15 y cambia al amarillo, el semáforo 2 debería comportarse de la misma manera, en teoría.

por eso pensé en un RTC

Y como sincronizas algo con un RTC? Imposible. Un GPS para un semáforo?

coloca una radio y sincroniza enviando una señal entre ellos. Tendrá el mismo efecto que usar una sincronización por pin + delay de mseg pero de nuevo, esos mseg puedes medirlos y hacer que el ajuste sea mas fino.

Asi lo haría yo.

Usa LoRA, o nRF24 con antena.

Nuevamente, muchas gracias por la respuesta. Las placas profesionales de control para semáforos que son 4 las empresas que las fabrican en mi país, todas usan como sincronismo el horario de un Modulo GPS en la placa, con eso logran el desfasaje entre ellos en segundos para lograr la onda verde, o sea hacen el calculo velocidad sobre distancia, lo que le da el tiempo, ese dato lo agregan al semáforo numero 2, entiendo que seria así, semáforo 1 enciende el verde, para el segundo semáforo se hace el calculo mencionado anteriormente, como velocidad se toma 60 Kmh (es una avenida) y distancia 800 mts lo que me da 48 segundos, al conectar el semáforo 2 espera en intermitente 48 segundos y enciende el verde, así comenzaría el ciclo. Seguiré investigando, cuando encuentre como puedo usar los segundos para controlar el encendido de los leds lo publicare por si a alguien le sirve, por ahora lo único que se me ocurre pasar la hora a formato UNIX y usar la función if.

Bueno usa un GPS entonces. Tu preguntaste en el post inciial y yo no se si eres profesional o un amateur haciendo una tarea escolar. Mi primera sugerencia fue considerando que era una maqueta. La segunda considerando que puedes hacerlo pero si lo hacen con GPS, será asi.

Si quieres te digo como se usan los segundos pero indica que GPS vas a usar. Un ublox NEO 6m es un buen modelo precio/perfomance link link de tutorial con el GPS NEO 6M

Un GPS entrega sentencias NMEA.

$GPGGA,110617.00,41XX.XXXXX,N,00831.54761,W,1,05,2.68,129.0,M,50.1,M,,*42

Esta comienza justamente con la hora

110617.00 es la hora a la que la locación fue tomada 11:06:17 UTC los 17 son tus segundos. Asi que ahi tienes tu solución.

Disculpe, no soy profesional, si se refiere a ingeniero, no lo soy, simplemente Técnico electrónico, programe PIC por casi 18 años y hace un par de años que empece con Arduino, me cuesta aun el C, ya que estuve muchos años programando en assembler. La idea es crear la lógica en los semáforos de una avenida, sacar la placa existente, ya obsoleta, y usar solo la parte de potencia. Tengo 2 Megas 2560 con un ethernet shield y un DS3231, este pronto a ser remplazado por el GPS que tu mencionas, el Neo. Genere una pequeña pagina en la que puedo configurar el tiempo de encendidos de cada luz, Verde, Amarillo y Rojo de cada Via que en un principio son 2. Use la función (millis) para cada tiempo de encendido de cada color, pero me encontré que en las placas, el encendido en un principio eran iguales, pero luego se iban desfasando el encendido. Ya tengo los segundos en el programa, los que me da el RTC, lo que no comprendo como hacer es remplazar esa función milllis por los segundos. Pido disculpas si no supe manejarme en la pagina a pesar de que leí las reglas del foro, aunque puede que, a los 55 años se me hayan escapado algunas cosas.

A ver si es por la edad yo tengo 2 mas que tu. Asi que no hay problema.
Como dije, cuando alguien llega al foro como generalmente no plantean el contexto uno no sabe con quien habla, ingeniero o técnico, amateur o profesional.

Si tienes la hora del RTC, la pregunta es como sabes que sera la misma en un mes o 2 meses.
YO te cuento que tuve problemas con un DS3231.

Ahora no se que quieres hacer. Si quieres usar el RTC, pues lees la hora y debes usar algo que te sirva como sincronismo, digamos cada 10 min tomas el 0 de los segundos para sincronizar.
Dije cada 10 min por lo seiguiente pero apenas estoy pensando en el problema a ver si a alguien se le ocurre alguna idea mejor :
Pero la pregunta es que haces cuando se corta la luz y luego vuelve? si tienes la desgracia que vuelva recien iniciada la hora entonces deberias esperar hasta esos 10 min para que todos se pongan en línea.

No se es algo rápido que he pensado.

Ya te digo yo no confio en el DS3231. Tengo un en marcha y me dio mucos dolores de cabeza. Los he reemplazado porque podía por NTC usando WIFI. Pero no es tu caso.

GeraJose: Tengo 2 Megas 2560 con un ethernet shield y un DS3231, este pronto a ser remplazado por el GPS que tu mencionas, el Neo.

Si tiene Ethernet Shield, puedes hacer una sincronización de tiempo con un servidor NTC y almacenarla en el DS3231. La cosa es que esas ethernet shield tengan salida a internet o puedan conectar a un servidor de tiempo de internet.

Comparto la idea. Si tienes los Shield W5100 o 5500 usa NTC para sincronizar tiempos.
Hay unejemplo disponible en la librería Ethernet para conectarte a un servidor NTC.

Este es un ejemplo

//sample code originated at http://www.openreefs.com/ntpServer
//modified by Steve Spence, http://arduinotronics.blogspot.com

#include <SPI.h>
#include <Ethernet.h>
#include <EthernetUdp.h>
#include <Time.h>

/* ******** Ethernet Card Settings ******** */
// Set this to your Ethernet Card Mac Address
byte mac[] = { 0x90, 0xA2, 0xDA, 0x00, 0x23, 0x36 };

/* ******** NTP Server Settings ******** */
/* us.pool.ntp.org NTP server
   (Set to your time server of choice) */
IPAddress timeServer(216, 23, 247, 62);

/* Set this to the offset (in seconds) to your local time
   This example is GMT - 4 */
const long timeZoneOffset = -14400L; 

/* Syncs to NTP server every 15 seconds for testing,
   set to 1 hour or more to be reasonable */
unsigned int ntpSyncTime = 3600;       


/* ALTER THESE VARIABLES AT YOUR OWN RISK */
// local port to listen for UDP packets
unsigned int localPort = 8888;
// NTP time stamp is in the first 48 bytes of the message
const int NTP_PACKET_SIZE= 48;     
// Buffer to hold incoming and outgoing packets
byte packetBuffer[NTP_PACKET_SIZE]; 
// A UDP instance to let us send and receive packets over UDP
EthernetUDP Udp;                   
// Keeps track of how long ago we updated the NTP server
unsigned long ntpLastUpdate = 0;   
// Check last time clock displayed (Not in Production)
time_t prevDisplay = 0;           

void setup() {
   Serial.begin(9600);
  
   // Ethernet shield and NTP setup
   int i = 0;
   int DHCP = 0;
   DHCP = Ethernet.begin(mac);
   //Try to get dhcp settings 30 times before giving up
   while( DHCP == 0 && i < 30){
     delay(1000);
     DHCP = Ethernet.begin(mac);
     i++;
   }
   if(!DHCP){
    Serial.println("DHCP FAILED");
     for(;;); //Infinite loop because DHCP Failed
   }
   Serial.println("DHCP Success");
  
   //Try to get the date and time
   int trys=0;
   while(!getTimeAndDate() && trys<10) {
     trys++;
   }
}

// Do not alter this function, it is used by the system
int getTimeAndDate() {
   int flag=0;
   Udp.begin(localPort);
   sendNTPpacket(timeServer);
   delay(1000);
   if (Udp.parsePacket()){
     Udp.read(packetBuffer,NTP_PACKET_SIZE);  // read the packet into the buffer
     unsigned long highWord, lowWord, epoch;
     highWord = word(packetBuffer[40], packetBuffer[41]);
     lowWord = word(packetBuffer[42], packetBuffer[43]); 
     epoch = highWord << 16 | lowWord;
     epoch = epoch - 2208988800 + timeZoneOffset;
     flag=1;
     setTime(epoch);
     ntpLastUpdate = now();
   }
   return flag;
}

// Do not alter this function, it is used by the system
unsigned long sendNTPpacket(IPAddress& address)
{
  memset(packetBuffer, 0, NTP_PACKET_SIZE);
  packetBuffer[0] = 0b11100011;
  packetBuffer[1] = 0;
  packetBuffer[2] = 6;
  packetBuffer[3] = 0xEC;
  packetBuffer[12]  = 49;
  packetBuffer[13]  = 0x4E;
  packetBuffer[14]  = 49;
  packetBuffer[15]  = 52;                 
  Udp.beginPacket(address, 123);
  Udp.write(packetBuffer,NTP_PACKET_SIZE);
  Udp.endPacket();
}

// Clock display of the time and date (Basic)
void clockDisplay(){
  Serial.print(hour());
  printDigits(minute());
  printDigits(second());
  Serial.print(" ");
  Serial.print(day());
  Serial.print(" ");
  Serial.print(month());
  Serial.print(" ");
  Serial.print(year());
  Serial.println();
}

// Utility function for clock display: prints preceding colon and leading 0
void printDigits(int digits){
  Serial.print(":");
  if(digits < 10)
    Serial.print('0');
  Serial.print(digits);
}

// This is where all the magic happens...
void loop() {
    // Update the time via NTP server as often as the time you set at the top
    if(now()-ntpLastUpdate > ntpSyncTime) {
      int trys=0;
      while(!getTimeAndDate() && trys<10){
        trys++;
      }
      if(trys<10){
        Serial.println("ntp server update success");
      }
      else{
        Serial.println("ntp server update failed");
      }
    }
  
    // Display the time if it has changed by more than a second.
    if( now() != prevDisplay){
      prevDisplay = now();
      clockDisplay(); 
    }
}