Mega Servidor Arduino y Reloj de Tiempo Real

imagen

En este artículo, aprenderá cómo Arduino Mega Server funciona con el tiempo y cómo puede crear proyectos en Arduino que estén vinculados en tiempo real, independientemente de si tienen un módulo RTC "hierro" instalado o no. Todos los problemas de trabajar con Arduino en tiempo real se analizarán en detalle y, después de leer este artículo, se convertirá en un verdadero "relojero".


Esencia de la pregunta


Cualquier proyecto más o menos serio sobre Arduino debería tener una idea del tiempo real actual. Por ejemplo, las lecturas del sensor deben estar limitadas en el tiempo (de lo contrario, sería imposible construir estadísticas e incluso gráficos elementales), el controlador debe realizar ciertas acciones dependiendo de la hora actual del día, fines de semana, días festivos, etc. Si su controlador no tiene idea En tiempo real, se convierte en una máquina simple que solo puede realizar acciones básicas en un programa rígidamente definido.

Desde Arduino Mega Serveres un sistema potente y desarrollado, entonces este estado de cosas (falta de trabajo en tiempo real) no podría satisfacerme a mí y a todos los demás usuarios del sistema. Por lo tanto, el tema de la integración en el sistema RTC fue uno de los primeros en la agenda.

Reloj virtual en tiempo real


Todo estaría bien, pero ni yo ni la mayoría de los usuarios de AMS teníamos el mismo módulo RTC "de hierro", por lo que se decidió hacer un "paseo a caballo" y, como medida temporal, organizar relojes en tiempo real que funcionen dentro del sistema, sin Módulo físico real. Que fue implementado con éxito.

Entonces, cómo organizar un RTC virtual, sin un módulo real. Hay una biblioteca de tiempo maravillosa que hace la mayor parte del trabajo de proporcionarnos el tiempo exacto. Para comenzar a trabajar con él, debe descargarlo, descomprimirlo y colocarlo en la ubicación estándar de todas las bibliotecas del entorno Arduino, es decir, en la carpeta:

\rduino\libraries\

Después de eso, todas las posibilidades de trabajar con el tiempo que proporciona se ponen a nuestra disposición.

Cómo funciona


El principio es muy simple. La biblioteca "lanza" el reloj virtual "dentro" del controlador y proporciona la capacidad de sincronizarlos de muchas maneras, para elegir. Puede elegir el método que más le convenga. Dado que Arduino Mega Server es un dispositivo de red, se eligió la opción de sincronizar el reloj a través de la red con los servidores de hora exactos. Estos pueden ser servidores en Internet o servidores en la red local en la que se ejecuta el servicio correspondiente. Por ejemplo, en la versión básica de AMS, el reloj está sincronizado con el servidor MajorDoMo , y no necesita configurar nada para esto, todo funciona de inmediato.

Entonces, para que esto funcione, debe conectar las bibliotecas apropiadas al comienzo del boceto.

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

El archivo Time.h es en realidad una biblioteca para trabajar con el tiempo, y el resto de los archivos son necesarios para trabajar con la red y para sincronizar la hora utilizando el protocolo NTP (la biblioteca Ethernet también debe estar instalada en su computadora).

A continuación, debe especificar la dirección IP del servidor con el que desea sincronizar la hora

IPAddress timeServer(192, 168, 2, 8); //   MajorDoMo  

y puerto correspondiente

unsigned int localPort = 8888;

pero hay un punto: el puerto 8888 es adecuado para la sincronización en una red local, y la mayoría de los servidores en Internet no responden, por lo que si planea sincronizar la hora con servidores de hora exactos en Internet, es mejor configurar el puerto 123:

unsigned int localPort = 123;

solo queda para indicar la zona horaria

const int timeZone = 4;

y crear un objeto EthernetUDP

EthernetUDP Udp;

En esto, las operaciones preparatorias se pueden considerar completadas y puede describir la funcionalidad que necesita para trabajar con el tiempo. Función de inicialización:

void rtcInit() {
  Udp.begin(localPort);
  Serial.println("Waiting for NTP sync...");
  setSyncProvider(getNtpTime);
}

Aquí debes prestar atención a la función

setSyncProvider(getNtpTime);

Esta función establece la fuente de sincronización horaria (en este caso, sincronización NTP a través de la red). Pero podría ser cualquier otra fuente, por ejemplo, el módulo RTC físico. El desempeño de esta función lleva a la instalación de una fuente de sincronización (para el futuro) y, al mismo tiempo, a la sincronización de tiempo a través de esta fuente. Es en el momento de realizar esta función que la hora exacta "aparece" en su sistema.

La biblioteca en sí tiene otra característica interesante,

setSyncInterval(interval);

lo que le permite establecer el intervalo deseado entre sincronizaciones (establecido en segundos, la sincronización en sí ocurre automáticamente, sin ninguna participación de su parte).

imagen

Ahora puede usar el tiempo exacto dentro del boceto de Arduino, por ejemplo, mostrar eventos en el monitor de serie no es fácil, pero está vinculado a un tiempo exacto específico. Esto se hace usando la función timeStamp ():

void timeStamp() {
  serialRTC();
  Serial.print(" ");
}

que es un contenedor para la función serialRTC ():

void serialRTC() {
  Serial.print(year()); 
  Serial.print("-");
  printDigits(month());
  Serial.print("-");
  printDigits(day());
  Serial.print(" ");
  printDigits(hour());
  Serial.print(":");
  printDigits(minute());
  Serial.print(":");
  printDigits(second());
}

El análisis del mecanismo para transmitir y mostrar el tiempo en la interfaz web de AMS está más allá del alcance de esta historia y es digno de un artículo separado y, si hay interés, podemos escribir una secuela y explicar en detalle cómo se muestra la "magia" del tiempo en la interfaz web de Arduino Mega Servidor

imagen

En realidad, eso es todo. Así es como se organizaron los relojes virtuales en tiempo real en AMS hasta la versión 0.12 inclusive, y también puede organizar el trabajo con tiempo preciso en sus proyectos, incluso si no tiene un módulo físico para relojes en tiempo real. Pero este no es el final de la historia, sino el comienzo.

Código completo del módulo RTC de Arduino Mega Server 0.12
/*
Modul Virtual RTC
part of Arduino Mega Server project
*/

// Virtual RTC

IPAddress timeServer(192, 168, 2, 8);
unsigned int localPort = 8888; // local port to listen for UDP packets
EthernetUDP Udp;

const int timeZone = 4;
time_t prevDisplay = 0; // when the digital clock was displayed

void rtcInit() {
Udp.begin(localPort);
Serialprint(«Waiting for NTP sync… \n»);
setSyncProvider(getNtpTime);
modulRtc = 1;
}

void rtcWorks() {
if (timeStatus() != timeNotSet) {
if (now() != prevDisplay) { // update the display only if time has changed
setLifer();
prevDisplay = now();
//digitalClockDisplay();
}
}
}

void printDigits(int digits) {
if(digits < 10) {
Serial.print('0');
}
Serial.print(digits);
}

void serialRTC() {
Serial.print(year());
Serial.print("-");
printDigits(month());
Serial.print("-");
printDigits(day());
Serial.print(" ");
printDigits(hour());
Serial.print(":");
printDigits(minute());
Serial.print(":");
printDigits(second());
}

void timeStamp() {
serialRTC();
Serial.print(" ");
}

void printRTC(){
serialRTC();
Serial.println();
}

// NTP code

const int NTP_PACKET_SIZE = 48; // NTP time is in the first 48 bytes of message
byte packetBuffer[NTP_PACKET_SIZE]; //buffer to hold incoming & outgoing packets

#ifdef RTC_FEATURE

time_t getNtpTime() {
while (Udp.parsePacket() > 0); // discard any previously received packets
Serialprint(«Transmit NTP request\n»);
sendNTPpacket(timeServer);
uint32_t beginWait = millis();
while (millis() — beginWait < 1500) {
int size = Udp.parsePacket();
if (size >= NTP_PACKET_SIZE) {
Serialprint(«Receive NTP response\n»);
Udp.read(packetBuffer, NTP_PACKET_SIZE); // read packet into the buffer
unsigned long secsSince1900;
// convert four bytes starting at location 40 to a long integer
secsSince1900 = (unsigned long)packetBuffer[40] << 24;
secsSince1900 |= (unsigned long)packetBuffer[41] << 16;
secsSince1900 |= (unsigned long)packetBuffer[42] << 8;
secsSince1900 |= (unsigned long)packetBuffer[43];
return secsSince1900 — 2208988800UL + timeZone * SECS_PER_HOUR;
}
}
Serialprint(«No NTP response\n»);
return 0; // return 0 if unable to get the time
}

// send an NTP request to the time server at the given address
void sendNTPpacket(IPAddress &address) {
// set all bytes in the buffer to 0
memset(packetBuffer, 0, NTP_PACKET_SIZE);
// Initialize values needed to form NTP request
// (see URL above for details on the packets)
packetBuffer[0] = 0b11100011; // LI, Version, Mode
packetBuffer[1] = 0; // Stratum, or type of clock
packetBuffer[2] = 6; // Polling Interval
packetBuffer[3] = 0xEC; // Peer Clock Precision
// 8 bytes of zero for Root Delay & Root Dispersion
packetBuffer[12] = 49;
packetBuffer[13] = 0x4E;
packetBuffer[14] = 49;
packetBuffer[15] = 52;
// all NTP fields have been given values, now
// you can send a packet requesting a timestamp:
Udp.beginPacket(address, 123); //NTP requests are to port 123
Udp.write(packetBuffer, NTP_PACKET_SIZE);
Udp.endPacket();
}

#endif

// Duration

void showDuration(time_t duration) {
// prints the duration in days, hours, minutes and seconds
Serialprint(" (duration ");
if(duration >= SECS_PER_DAY){
Serial.print(duration / SECS_PER_DAY);
Serialprint(" day ");
duration = duration % SECS_PER_DAY;
}
if(duration >= SECS_PER_HOUR){
Serial.print(duration / SECS_PER_HOUR);
Serialprint(" hour ");
duration = duration % SECS_PER_HOUR;
}
if(duration >= SECS_PER_MIN){
Serial.print(duration / SECS_PER_MIN);
Serialprint(" min ");
duration = duration % SECS_PER_MIN;
}
Serial.print(duration);
Serialprint(" sec) \n");
}

void checkEvent(time_t* prevEvent) {
time_t duration = 0;
time_t timeNow = now();

if (*prevEvent > 0) {
duration = timeNow — *prevEvent;
}
if (duration > 0) {
showDuration(duration);
}
*prevEvent = timeNow;
}



Una sorpresa agradable


No hubiera estado ocupado integrando módulos RTC en el sistema durante mucho tiempo (también hay suficientes otras tareas urgentes), pero aquí, en el marco de la cooperación tecnológica con nuestro proyecto, CHIPSTER proporcionó equipos para pruebas e integración en AMS, entre los que se encontraban módulos Ethernet basados ​​en el chip W5500 y ... el módulo de reloj en tiempo real en el chip DS3231 , que resultó ser el más oportuno y sirvió como un ímpetu para la integración de módulos RTC en el sistema.

Resultó que la empresa CHIPSTER no solo vende equipos electrónicos, sino que también desarrolla sus propios productos para Arduino y automatización bajo la marca Geegrowy tiene grandes planes para el futuro en esta dirección, en particular, tiene un proyecto para el lanzamiento de una versión especializada del Arduino Mega 2560 con funciones avanzadas y "afiladas" específicamente para el Arduino Mega Server. Y, si se lanza esta placa, será un evento muy interesante. Pero volvamos al reloj en tiempo real.

Reloj en tiempo real


Como el módulo RTC estaba al alcance de mi mano, sería un pecado no integrarlo en el sistema. Afortunadamente, esto resultó ser bastante simple gracias a la misma Biblioteca de tiempo. Pero lo primero es lo primero.

Para aquellos que no saben, hay dos tipos de módulos en tiempo real: "ordinario" (generalmente en un chip DS1307) y "avanzado" (en un chip DS3231, que obtuve). La diferencia entre los dos es que los primeros no son muy precisos y pueden "escapar" muy rápidamente y con mucha fuerza, y el segundo es un reloj de alta precisión con un cuidado normalizado de no más de dos minutos al año, es decir, realmente aplicable en la práctica. Y la precisión se logra gracias a un diseño de circuito más complejo y compensación térmica incorporada.

Pero programáticamente, ambas versiones de los módulos son compatibles y ambas funcionarán con la biblioteca y el código. La diferencia solo estará en la precisión.

Y, por supuesto, una de las principales propiedades de un reloj en tiempo real es la capacidad de funcionar cuando la alimentación está apagada, debido a la batería incorporada.

Conexión física


Ahora hablemos sobre cómo conectar físicamente el módulo RTC al Arduino Mega Server o a su proyecto Arduino. Debo decir que esto es muy simple y que solo necesitará dos resistencias y algunos cables.

La conexión es trivial: necesita encontrar cuatro contactos en su módulo: GND (tierra), VCC (voltaje de alimentación), SCL (señal de reloj), SDA (datos). Se usan otros contactos en casos raros y específicos y puede ignorarlos.

imagen

Entonces, conectamos el pin GND a tierra, el pin VCC a la tensión de alimentación del controlador. Aquí todo es simple y no deben surgir preguntas.

El resto de las conclusiones no son mucho más complicadas. El módulo RTC se comunica con el controlador a través de la interfaz I2C, que tiene solo dos cables: sincronización y datos, y los controladores Arduino ya tienen contactos para conectar esta interfaz. El Arduino Uno es A4 (SDA) y A5 (SCL), mientras que el Arduino Mega es D20 (SDA) y D21 (SCL).

La única sutileza es que los pines SCL y SDA necesitan ser "atraídos" hacia la fuente de energía a través de resistencias de 4.7 kΩ. Si no tiene exactamente esta clasificación, puede usar resistencias del rango de 2 KOhm - 10 KOhm.

imagen

Soporte de software


Ahora solo queda agregar soporte para el módulo en el código AMS o su proyecto. Como dije, será muy simple porque la misma Biblioteca de tiempo funcionará con el módulo. Es cierto que necesitaremos agregar otra biblioteca, a saber, la Biblioteca DS1307RTC . También lo desempaquetamos y lo colocamos en la carpeta de la biblioteca estándar:

\rduino\libraries\

Agregue las siguientes líneas a su código de boceto

#include <Wire.h>
#include <DS1307RTC.h>

Ahora estamos totalmente equipados y podemos comenzar a escribir el código para el bosquejo mismo, trabajando con el módulo físico RTC. En función

void rtcInit() {
  Udp.begin(localPort);
  Serial.println("Waiting for NTP sync...");
  setSyncProvider(getNtpTime);
}

reemplazar cuerda

setSyncProvider(getNtpTime);

sobre el

setSyncProvider(RTC.get);

y la hora interna del Arduino Mega Server (o su controlador) se sincronizará con el controlador RTC "iron", y no con los servidores en Internet o en la red local. Por lo tanto, al llamar a las funciones setSyncProvider (getNtpTime) y setSyncProvider (RTC.get), puede manipular las fuentes de sincronización de tiempo y sincronizar el tiempo como lo desee, dependiendo de varias condiciones.

Otra función que necesita saber es

if (timeStatus() != timeNotSet) {

}

que le permite averiguar si la hora está sincronizada y, según esta condición, tomar las medidas necesarias.

Momento sutil


Debe distinguir entre dos cosas: el tiempo que pasa en el módulo RTC "iron" y el tiempo que pasa en su controlador. Esto no es lo mismo. Lo "principal" para usted es el tiempo en el controlador, y el tiempo en el módulo es solo una fuente de sincronización.

¡Pero! Dado que el tiempo en el RTC físico también se está agotando gradualmente, también debe ajustarse mediante la sincronización con fuentes más precisas, por ejemplo, servidores en Internet.

Por lo tanto, el algoritmo óptimo debería ser este: si es posible, sincronice todos los relojes con los servidores en Internet, si la red no está disponible, comenzamos a sincronizar la hora en el controlador con el módulo RTC, tan pronto como aparezca la red, vuelva a la sincronización a través de Internet.

Si se encuentra en condiciones extremas, sin acceso a ninguna fuente de sincronización, puede ajustar manualmente el curso del reloj de hierro de vez en cuando.

Consideremos, por ejemplo, la función de sincronización del reloj interno del controlador y el módulo RTC a través de la red:

void rtcSync() {
  setSyncProvider(getNtpTime);
  Serial.println("...getNtpTime...");
  if (timeStatus() != timeNotSet) {
    Serial.println("...set!...");
    time_t t = getNtpTime();
    RTC.set(t);
    setSyncProvider(RTC.get);
  }
}

Aquí primero tenemos la hora exacta a través de la red.

setSyncProvider(getNtpTime);

luego, si tiene éxito, instálelo en el módulo RTC

RTC.set(t);

y luego desde este módulo establecemos el tiempo del controlador

setSyncProvider(RTC.get);

Lanzamiento inicial


Pero eso no es todo. También existe el problema de la puesta en marcha inicial, cuando el módulo RTC solo está conectado, pero el tiempo no está configurado y, por lo tanto, es imposible sincronizarlo. Necesitas establecer de alguna manera el momento adecuado. Hay dos formas de resolver este problema en el Arduino Mega Server: puede sincronizar el RTC físico a través de la red (si hay servidores de hora exactos disponibles) o usar la utilidad Arduino Serial Commander.

Para configurar la hora en el módulo RTC, simplemente ... haga clic en el botón. Todo lo demás lo harán por usted dos jóvenes llamados Arduino Mega Server y Arduino Serial Commander. Si no usa AMS, pero está desarrollando su propio proyecto, puede tomar el código del kit de distribución Arduino Mega Server (el código está disponible y es completamente gratuito) o buscar una solución a este problema en Internet (hay varias soluciones).

imagen

Versión con soporte RTC real


Arduino Mega Server, a partir de la versión 0.13, admite RTC "iron". Puede descargar la última versión actual del sitio web oficial del proyecto , y puede hacer sus preguntas en el foro .

Y, por supuesto, agradezco a CHIPSTER por la cooperación y el equipo proporcionado para las pruebas y la integración (en uno de los siguientes artículos le contaré sobre el módulo W5500 y la aceleración de la operación de la red AMS).

Además . Un canal de Youtube está abierto y aquí hay un video promocional del Arduino Mega Server, que muestra cómo trabajar con un sistema real.

Source: https://habr.com/ru/post/es385349/


All Articles