Mega Servidor Arduino e Relógio em Tempo Real

imagem

Neste artigo, você aprenderá como o Arduino Mega Server funciona com o tempo e como criar projetos no Arduino vinculados em tempo real, independentemente de terem ou não um módulo RTC "de ferro" instalado. Todas as questões relacionadas ao trabalho em tempo real no Arduino serão discutidas em detalhes e, depois de ler este artigo, você se tornará um verdadeiro "relojoeiro".


Essência da questão


Qualquer projeto mais ou menos sério no Arduino deve ter uma idéia do tempo real atual. Por exemplo, as leituras do sensor devem ter limite de tempo (caso contrário, seria impossível criar estatísticas e até gráficos elementares), o controlador deve executar determinadas ações, dependendo da hora atual do dia, fins de semana, feriados etc. Se o seu controlador não tiver idéia em tempo real, ele se transforma em uma máquina simples que só pode executar ações básicas em um programa rigidamente definido.

Desde o Arduino Mega Servercomo é um sistema poderoso e desenvolvido, esse estado de coisas (falta de trabalho em tempo real) não pode ser adequado para mim e para todos os outros usuários do sistema. Portanto, a questão da integração no sistema RTC foi uma das primeiras da agenda.

Relógio virtual em tempo real


Tudo ficaria bem, mas nem eu, nem a maioria dos usuários de AMS possuímos o mesmo módulo RTC "de ferro", por isso foi decidido fazer um "passeio a cavalo" e, como medida temporária, organizar relógios em tempo real trabalhando dentro do sistema, sem módulo físico real. Qual foi implementado com sucesso.

Então, como organizar um RTC virtual, sem um módulo real. Existe uma maravilhosa Biblioteca do Tempo, que faz a maior parte do trabalho de nos fornecer um tempo preciso. Para começar a trabalhar com ele, é necessário fazer o download, descompacte-o e coloque-o no local padrão de todas as bibliotecas do ambiente Arduino, a saber, na pasta:

\rduino\libraries\

Depois disso, todas as possibilidades de trabalhar com o tempo que ele fornece se tornam disponíveis para nós.

Como funciona


O princípio é muito simples. A biblioteca “lança” o relógio virtual “dentro” do controlador e fornece a capacidade de sincronizá-los de várias maneiras, para escolher. Você pode escolher o método que melhor lhe convier. Como o Mega Server do Arduino é um dispositivo de rede, foi escolhida a opção de sincronizar o relógio através da rede com a hora exata dos servidores. Podem ser servidores na Internet ou servidores na rede local na qual o serviço correspondente está sendo executado. Por exemplo, na versão básica do AMS, o relógio é sincronizado com o servidor MajorDoMo , e você não precisa configurar nada para isso, tudo funciona imediatamente .

Portanto, para que isso funcione, você precisa conectar as bibliotecas apropriadas no início do esboço.

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

O arquivo Time.h é realmente uma biblioteca para trabalhar com o tempo, e o restante dos arquivos é necessário para trabalhar com a rede e para sincronizar o tempo usando o protocolo NTP (a biblioteca Ethernet também deve estar instalada no seu computador).

Em seguida, você precisa especificar o endereço IP do servidor com o qual deseja sincronizar o horário

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

e porta correspondente

unsigned int localPort = 8888;

mas há um ponto: a porta 8888 é adequada para sincronização em uma rede local e a maioria dos servidores na Internet não responde a ela. Portanto, se você planeja sincronizar a hora com os servidores de hora exata na Internet, é melhor definir a porta 123:

unsigned int localPort = 123;

resta apenas indicar o fuso horário

const int timeZone = 4;

e crie um objeto EthernetUDP

EthernetUDP Udp;

Com isso, as operações preparatórias podem ser consideradas concluídas e você pode descrever a funcionalidade necessária para trabalhar com o tempo. Função de inicialização:

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

Aqui você precisa prestar atenção à função

setSyncProvider(getNtpTime);

Esta função define a fonte de sincronização da hora (neste caso, sincronização NTP através da rede). Mas poderia ser qualquer outra fonte, por exemplo, o módulo RTC físico. O desempenho desta função leva à instalação de uma fonte de sincronização (para o futuro) e, ao mesmo tempo, à própria sincronização de tempo através dessa fonte. É no momento de executar esta função que a hora exata “aparece” em seu sistema.

A própria biblioteca tem outro recurso interessante,

setSyncInterval(interval);

que permite definir o intervalo desejado entre as sincronizações (definido em segundos, a sincronização ocorre automaticamente, sem a participação de sua parte).

imagem

Agora você pode usar a hora exata dentro do esboço do Arduino, por exemplo, exibir eventos no monitor serial não é fácil, mas vinculado a uma hora exata específica. Isso é feito usando a função timeStamp ():

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

que é um wrapper para a função 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());
}

A análise do mecanismo de transmissão e exibição de tempo na interface da Web do AMS está além do escopo desta história e é digna de um artigo separado. Se houver interesse, podemos escrever uma sequela e explicar em detalhes como a “mágica” do tempo é exibida na interface da web do Arduino Mega. Servidor

imagem

Na verdade, é tudo. É assim que os relógios virtuais em tempo real foram organizados no AMS até a versão 0.12, inclusive, e você também pode organizar o trabalho com tempo preciso em seus projetos, mesmo se você não tiver um módulo físico para relógios em tempo real. Mas este não é o fim da história, mas apenas o começo.

Código completo do módulo RTC do 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;
}



Uma surpresa agradável


Eu não estaria ocupado integrando módulos RTC ao sistema por um longo tempo (também existem outras tarefas urgentes), mas aqui, no âmbito da cooperação tecnológica com nosso projeto, a CHIPSTER forneceu equipamentos para teste e integração ao AMS, entre os quais havia módulos Ethernet baseados no chip W5500 e ... o módulo de relógio em tempo real no chip DS3231 , que se mostrou o mais oportuno e serviu de impulso para a integração dos módulos RTC no sistema.

Verificou-se que a empresa CHIPSTER não apenas vende equipamentos eletrônicos, mas também desenvolve seus próprios produtos para Arduino e automação sob a marca Geegrowe tem grandes planos para o futuro nessa direção, em particular, ela tem um projeto para o lançamento de uma versão especializada do Arduino Mega 2560 com recursos avançados e "aprimorados" especificamente para o Arduino Mega Server. E, se este fórum for lançado, será um evento muito interessante. Mas voltando ao relógio em tempo real.

Relógio de tempo real


Como o módulo RTC estava ao meu alcance, seria pecado não integrá-lo ao sistema. Felizmente, isso acabou sendo muito simples, graças à mesma biblioteca de tempo. Mas as primeiras coisas primeiro.

Para quem não sabe, existem dois tipos de módulos em tempo real - "comum" (geralmente em um chip DS1307) e "avançado" (em um chip DS3231, que eu recebi). A diferença entre os dois é que os primeiros não são muito precisos e podem "fugir" muito rapidamente e com muita força, e o segundo é um relógio de alta precisão com atendimento normalizado de não mais de dois minutos por ano, ou seja, realmente aplicável na prática. E a precisão é alcançada graças a um projeto de circuito mais complexo e compensação térmica integrada.

Porém, programaticamente, ambas as versões dos módulos são compatíveis e funcionam com a biblioteca e o código. A diferença estará apenas na precisão.

E, é claro, uma das principais propriedades de um relógio em tempo real é a capacidade de trabalhar quando a energia é desligada, devido à bateria embutida.

Conexão física


Agora vamos falar sobre como conectar fisicamente o módulo RTC ao Mega Server do Arduino ou ao seu projeto do Arduino. Devo dizer que isso é muito simples e você precisará de apenas dois resistores e alguns fios.

A conexão é trivial: você precisa encontrar quatro contatos em seu módulo - GND (terra), VCC (tensão de alimentação), SCL (sinal de relógio), SDA (dados). Outros contatos são usados ​​em casos raros e específicos e você pode ignorá-los.

imagem

Assim, conectamos o pino GND ao terra, o pino VCC à tensão de alimentação do controlador. Tudo é simples aqui e nenhuma pergunta deve surgir.

O restante das conclusões não é muito mais complicado. O módulo RTC se comunica com o controlador por meio da interface I2C, que possui apenas dois fios: sincronização e dados, e os controladores Arduino já possuem contatos para conectar essa interface. O Arduino Uno é A4 (SDA) e A5 (SCL), enquanto o arduino Mega é D20 (SDA) e D21 (SCL).

A única sutileza é que os pinos SCL e SDA precisam ser "puxados" para a fonte de energia através de resistores de 4,7 kΩ. Se você não tiver exatamente essa classificação, poderá usar resistores na faixa de 2 KOhm a 10 KOhm.

imagem

Suporte de software


Agora, resta apenas adicionar suporte ao módulo no código AMS ou no seu projeto. Como eu disse, será muito simples, porque a mesma biblioteca de tempo funcionará com o módulo. É verdade que precisaremos adicionar outra biblioteca, a biblioteca DS1307RTC . Também o descompactamos e o colocamos na pasta da biblioteca padrão:

\rduino\libraries\

Adicione as seguintes linhas ao seu código de esboço

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

Agora estamos totalmente equipados e podemos começar a escrever o código para o próprio esboço, trabalhando com o módulo físico RTC. Em função

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

substituir string

setSyncProvider(getNtpTime);

no

setSyncProvider(RTC.get);

e o horário interno do Arduino Mega Server (ou do seu controlador) será sincronizado com o controlador RTC "iron", e não com os servidores na Internet ou na rede local. Assim, chamando as funções setSyncProvider (getNtpTime) e setSyncProvider (RTC.get), você pode manipular as fontes de sincronização de tempo e sincronizar o tempo como desejar, dependendo de várias condições.

Outra função que você precisa conhecer é

if (timeStatus() != timeNotSet) {

}

que permite descobrir se a hora está sincronizada e, dependendo dessa condição, executar as ações necessárias.

Momento sutil


Você precisa distinguir entre duas coisas: o tempo que passa no módulo RTC “ferro” e o tempo que passa no seu controlador. Isto não é a mesma coisa. A coisa “principal” para você é o tempo no controlador e o tempo no módulo é apenas uma fonte para sincronização.

Mas! como o tempo no RTC físico também está gradualmente se esgotando, ele também precisa ser ajustado pela sincronização com fontes mais precisas, por exemplo, servidores na Internet.

Portanto, o algoritmo ideal deve ser este: se possível, sincronize todos os relógios com os servidores na Internet, se a rede estiver indisponível, então começaremos a sincronizar o tempo no controlador com o módulo RTC, assim que a rede aparecer, volte para a sincronização pela Internet.

Se você estiver em condições extremas, sem acesso a nenhuma fonte de sincronização, poderá ajustar manualmente o curso do relógio de ferro de tempos em tempos.

Vamos, por exemplo, considerar a função de sincronização do relógio interno do controlador e do módulo RTC via rede:

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

Aqui, obtemos o tempo exato pela rede.

setSyncProvider(getNtpTime);

se for bem-sucedido, instale-o no módulo RTC

RTC.set(t);

e, a partir deste módulo, definimos o tempo do controlador

setSyncProvider(RTC.get);

Lançamento inicial


Mas isso não é tudo. Há também o problema da inicialização inicial, quando o módulo RTC está conectado apenas, mas o tempo não está definido e, portanto, é impossível sincronizar com ele. Você precisa, de alguma forma, definir a hora certa. Há duas maneiras de resolver esse problema no Mega Server do Arduino: você pode sincronizar o RTC físico pela rede (se houver servidores de horário exato) ou usar o utilitário Arduino Serial Commander.

Para definir a hora no módulo RTC, basta ... clicar no botão. Tudo o mais será feito por você por dois jovens chamados Arduino Mega Server e Arduino Serial Commander. Se você não usa o AMS, mas está desenvolvendo seu próprio projeto, pode pegar o código no kit de distribuição do Arduino Mega Server (o código está disponível e totalmente gratuito) ou procurar uma solução para esse problema na Internet (existem várias soluções).

imagem

Versão com suporte RTC real


O Arduino Mega Server, começando na versão 0.13, suporta RTC "iron". Você pode fazer o download da versão atual mais recente no site oficial do projeto e fazer suas perguntas no fórum .

E, é claro, expresso minha gratidão à CHIPSTER pela cooperação e pelo equipamento fornecido para teste e integração ( falarei sobre o módulo W5500 e a aceleração da operação da rede AMS em um dos seguintes artigos).

Adição . Um canal do Youtube está aberto e aqui está um vídeo promocional do Arduino Mega Server, que demonstra como trabalhar com um sistema real.

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


All Articles