Arduino Mega服务器和实时时钟

图片

在本文中,您将了解Arduino Mega Server随着时间的推移如何工作,以及如何在Arduino上创建实时链接的项目,而不管它们是否安装了“铁” RTC模块。将详细讨论在Arduino上实时工作的所有问题,阅读本文后,您将成为真正的“钟表匠”。


问题的实质


Arduino上的任何或多或少严肃的项目都应该对当前的实时性有所了解。例如,传感器读数应有时间限制(否则将无法建立任何统计信息甚至是基本图),控制器应根据一天中的当前时间,周末,节假日等执行某些操作。如果您的控制器不知道几乎是实时的,它变成了一台简单的机器,只能对严格定义的程序执行基本操作。

Arduino Mega Server开始它是一个功能强大且已开发的系统,因此这种事务状态(缺乏实时工作)不适合我以及该系统的所有其他用户。因此,整合到RTC系统中的问题是议程上的第一个问题。

虚拟实时时钟


一切都很好,但是我和大多数AMS用户都没有相同的“铁” RTC模块,因此决定进行一次“骑马”活动,并作为临时措施,在系统内部组织实时时钟,而无需实际的物理模块。已成功实施。

因此,如何在没有实际模块的情况下组织虚拟RTC。有一个很棒的时间库,可以为我们提供准确的时间。要开始使用它,您需要下载,解压缩并将其放在Arduino环境的所有库的标准位置,即文件夹中:

\rduino\libraries\

之后,我们可以利用它提供的所有时间进行工作。

怎么运行的


原理很简单。该库可在控制器内部“启动”虚拟时钟,并提供以多种方式同步虚拟时钟的功能,供您选择。您可以选择最适合您的方法。由于Arduino Mega Server是网络设备,因此选择了通过网络与精确时间服务器同步时钟的选项。这些可以是Internet上的服务器,也可以是运行相应服务的本地网络上的服务器。例如,在AMS的基本版本中,时钟与MajorDoMo服务器同步,并且您不需要为此进行任何配置,一切都可以直接使用。

因此,为了使其正常工作,您需要在草图的开头连接适当的库。

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

Time.h文件实际上是一个用于处理时间的库,其余文件对于使用NTP协议进行网络工作和同步时间是必需的(还必须安装以太网库)。

接下来,您需要指定要与之同步时间的服务器的IP地址

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

和相应的端口

unsigned int localPort = 8888;

但是有一点:端口8888适合在本地网络上同步,并且Internet上的大多数服务器都不响应它,因此,如果您打算与Internet上的精确时间服务器同步时间,最好设置端口123:

unsigned int localPort = 123;

它仅表示时区

const int timeZone = 4;

并创建一个EthernetUDP对象

EthernetUDP Udp;

这样,准备操作就可以视为完成了,您可以描述长时间使用所需的功能。初始化功能:

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

这里需要注意的功能

setSyncProvider(getNtpTime);

此功能设置时间同步源(在这种情况下,是通过网络的NTP同步)。但这可以是任何其他来源,例如物理RTC模块。此功能的执行导致(将来)安装同步源,并同时通过此源实现时间的高度同步。正是在执行此功能时,确切的时间“出现”在系统中。

该库本身还有另一个有趣的功能,

setSyncInterval(interval);

这样您就可以设置所需的两次同步间隔(以秒为单位设置,同步本身会自动发生,而无需您的任何参与)。

图片

现在,您可以在Arduino草图中使用确切的时间,例如,在串行监视器中显示事件并不容易,但是与特定的确切时间相关。这是使用timeStamp()函数完成的:

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

这是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());
}

在AMS Web界面中传输和显示时间的机制的分析超出了本文的讨论范围,值得单独撰写,如果有兴趣,我们可以编写续集并详细解释Arduino Mega Web界面中时间显示的“魔力”服务器

图片

实际上,仅此而已。这是在AMS之前(含0.12版)中如何组织虚拟实时时钟的方式,即使您没有用于实时时钟的物理模块,也可以在项目中以准确的时间来组织工作。但这不是故事的结局,而仅仅是开始。

来自Arduino Mega Server 0.12的完整RTC模块代码
/*
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;
}



一个惊喜


我不会长时间忙于将RTC模块集成到系统中(还有其他紧迫的任务),但是在这里,在与我们项目的技术合作框架中,CHIPSTER提供了用于测试和集成到AMS的设备,其中包括基于W5500芯片的以太网模块和... DS3231芯片上的实时时钟模块,这是最合适的选择,并且是将RTC模块集成到系统中的动力。

事实证明,CHIPSTER公司不仅销售电子设备,而且还以Geegrow品牌命名为Arduino和自动化开发自己的产品。并在这个方向上为未来制定了宏伟的计划,特别是,她有一个计划发布具有高级功能并专门针对Arduino Mega Server进行“锐化”的Arduino Mega 2560的专用版本。而且,如果该板发布,这将是非常有趣的事件。但是回到实时时钟。

实时时钟


由于RTC模块触手可及,因此不将其集成到系统中将是一个罪过。幸运的是,由于有了相同的时间库,事实证明这很简单。但是首先是第一件事。

对于那些不知道的人,有两种类型的实时模块-“普通”(通常在DS1307芯片上)和“高级”(在我的DS3231芯片上)。两者之间的区别在于,第一只手表的准确性不高,可以非常快速,非常强劲地“逃跑”,第二只手表是一款高精度手表,每年的护理时间不超过两分钟,也就是说,实际上可以实际使用。更加复杂的电路设计和内置的热补偿功能可实现精度。

但是通过编程,这两个模块的版本都是兼容的,并且都可以与库和代码一起使用。区别仅在于准确性。

当然,由于内置电池的存在,因此实时时钟的主要特性之一就是在电源关闭时能够工作的能力。

物理连接


现在让我们讨论如何将RTC模块物理连接到Arduino Mega Server或您的Arduino项目。我必须说,这非常简单,您将只需要两个电阻和几根电线。

连接很简单:您需要在模块上找到四个触点-GND(接地),VCC(电源电压),SCL(时钟信号),SDA(数据)。其他联系人仅在极少数情况下使用,您可以忽略它们。

图片

因此,我们将GND引脚接地,将VCC引脚连接至控制器的电源电压。这里的一切都很简单,不会出现任何问题。

其余结论并不复杂。RTC模块通过I2C接口与控制器通信,该接口只有两条线:同步和数据,而Arduino控制器已经具有用于连接此接口的触点。Arduino Uno是A4(SDA)和A5(SCL),而arduino Mega是D20(SDA)和D21(SCL)。

唯一的妙处是,需要通过4.7kΩ电阻将SCL和SDA引脚“拉”到电源。如果没有确切的额定值,则可以使用2 KOhm-10 KOhm范围内的电阻。

图片

软件支援


现在,仅需在AMS代码或您的项目中添加对模块的支持即可。如我所说,这将非常简单,因为同一时间库将与该模块一起使用。确实,我们需要添加另一个库,即DS1307RTC库我们也将其解压缩并放入库的标准文件夹中:

\rduino\libraries\

将以下行添加到您的草图代码中

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

现在我们已经配备齐全,并且可以开始使用RTC物理模块编写草图本身的代码。在功能上

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

替换字符串

setSyncProvider(getNtpTime);



setSyncProvider(RTC.get);

并且Arduino Mega Server(或您的控制器)的内部时间将与“铁” RTC控制器同步,而不与Internet或本地网络上的服务器同步。因此,通过调用函数setSyncProvider(getNtpTime)和setSyncProvider(RTC.get),可以根据各种条件来操纵时间同步源并根据需要同步时间。

您需要了解的另一个功能是

if (timeStatus() != timeNotSet) {

}

这样您就可以确定时间是否同步,并根据这种情况采取必要的措施。

微妙的时刻


您需要区分两件事:“铁” RTC模块中花费的时间和控制器中所花费的时间。这不是同一回事。对您来说,“主要”是控制器中的时间,而模块中的时间只是同步的来源。

但!由于物理RTC中的时间也在逐渐“耗尽”,因此还需要通过与更精确的资源(例如Internet上的服务器)同步来进行调整。

因此,最佳算法应该是这样的:如果可能,然后将所有时钟与Internet上的服务器同步,如果网络不可用,则我们开始将控制器中的时间与RTC模块同步,一旦网络出现,便立即通过Internet返回到同步。

如果您处于极端条件下,而无法访问任何同步源,则可以不时手动调整熨斗时钟的进程。

例如,让我们考虑通过网络的控制器和RTC模块的内部时钟的同步功能:

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

在这里,我们首先获得通过网络的确切时间。

setSyncProvider(getNtpTime);

然后,如果成功,则将其安装在RTC模块中

RTC.set(t);

然后从该模块中设置控制器时间

setSyncProvider(RTC.get);

初次发布


但这还不是全部。当仅连接RTC模块但未设置其时间时,还存在初始启动的问题,因此无法与之同步。您需要以某种方式在其中设置正确的时间。有两种方法可以解决Arduino Mega Server中的此问题:您可以通过网络(如果有确切的时间服务器)同步物理RTC(或使用Arduino Serial Commander实用程序)。

要在RTC模块中设置时间,只需...单击按钮。其他一切将由两名名叫Arduino Mega Server和Arduino Serial Commander的年轻人为您完成。如果您不使用AMS,而是在开发自己的项目,则可以从Arduino Mega Server分发工具包中获取代码(该代码可用并且完全免费),或者在Internet上寻找针对此问题的解决方案(有几种解决方案)。

图片

具有实际RTC支持的版本


从0.13版开始的Arduino Mega Server支持“铁” RTC。您可以从项目官方网站上下载最新版本也可以论坛上提问

并且,当然,我要感谢CHIPSTER的合作以及为测试和集成提供的设备(我将在以下文章之一中向您介绍W5500模块和AMS网络运行的加速)。

加法一个Youtube频道已打开,这是Arduino Mega Server 促销视频,演示了如何在实际系​​统上工作。

Source: https://habr.com/ru/post/zh-CN385349/


All Articles