Skip to content

Commit 5407a89

Browse files
authored
Merge pull request #6 from bcmi-labs/feature/ntp-time-sync
Use NTP to synchronize time of internal RTC for usage by OPC/UA server
2 parents 32830bd + 139228c commit 5407a89

File tree

7 files changed

+269
-2
lines changed

7 files changed

+269
-2
lines changed

examples/opcua_server/opcua_server.ino

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
#include "PortentaEthernet.h"
66
#include "Arduino_open62541.h"
7+
#include <mbed_rtc_time.h>
78

89
#ifndef ARDUINO_OPEN62541_O1HEAP_DEBUG
910
# define ARDUINO_OPEN62541_O1HEAP_DEBUG (0) /* Change to (1) if you want to see debug messages on Serial concerning o1heap memory calls. */
@@ -225,6 +226,19 @@ void setup()
225226
for (;;) { }
226227
}
227228

229+
/* Try and obtain the current time via NTP and configure the Arduino
230+
* Opta's onboard RTC accordingly. The RTC is then used inside the
231+
* open62541 Arduino wrapper to obtain the correct timestamps for
232+
* the OPC/UA server.
233+
*/
234+
EthernetUDP udp_client;
235+
auto const epoch = opcua::NTPUtils::getTime(udp_client);
236+
if (epoch > 0) {
237+
set_time(epoch); /* Directly set RTC of Arduino Opta. */
238+
} else {
239+
set_time(opcua::cvt_time(__DATE__)); /* Configure Arduino Opta with time at compile time as last time of defense. */
240+
}
241+
228242
/* Initialize heap memory. */
229243
o1heap_ins = o1heapInit(OPC_UA_SERVER_THREAD_HEAP.data(), OPC_UA_SERVER_THREAD_HEAP.size());
230244
if (o1heap_ins == nullptr) {

src/Arduino_open62541.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515

1616
#include "open62541.h"
1717
#include "o1heap/o1heap.h"
18+
#include "NTPUtils.h"
19+
#include "cvt_time.h"
1820

1921
#include "ArduinoOpta.h"
2022
#include "ArduinoOptaVariant.h"

src/NTPUtils.cpp

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/*
2+
* Copyright (c) 2024 Arduino
3+
*
4+
* SPDX-License-Identifier: MPL-2.0
5+
* This Source Code Form is subject to the terms of the Mozilla Public
6+
* License, v. 2.0. If a copy of the MPL was not distributed with this
7+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
8+
*/
9+
10+
/**************************************************************************************
11+
* INCLUDE
12+
**************************************************************************************/
13+
14+
#include "NTPUtils.h"
15+
16+
17+
/**************************************************************************************
18+
* NAMESPACE
19+
**************************************************************************************/
20+
21+
namespace opcua
22+
{
23+
24+
/**************************************************************************************
25+
* PUBLIC MEMBER FUNCTIONS
26+
**************************************************************************************/
27+
28+
unsigned long NTPUtils::getTime(UDP &udp)
29+
{
30+
udp.begin(NTP_LOCAL_PORT);
31+
32+
sendNTPpacket(udp);
33+
34+
bool is_timeout = false;
35+
unsigned long const start = millis();
36+
do
37+
{
38+
is_timeout = (millis() - start) >= NTP_TIMEOUT_MS;
39+
} while (!is_timeout && !udp.parsePacket());
40+
41+
if (is_timeout)
42+
{
43+
udp.stop();
44+
return 0;
45+
}
46+
47+
uint8_t ntp_packet_buf[NTP_PACKET_SIZE];
48+
udp.read(ntp_packet_buf, NTP_PACKET_SIZE);
49+
udp.stop();
50+
51+
unsigned long const highWord = word(ntp_packet_buf[40], ntp_packet_buf[41]);
52+
unsigned long const lowWord = word(ntp_packet_buf[42], ntp_packet_buf[43]);
53+
unsigned long const secsSince1900 = highWord << 16 | lowWord;
54+
unsigned long const seventyYears = 2208988800UL;
55+
unsigned long const epoch = secsSince1900 - seventyYears;
56+
57+
return epoch;
58+
}
59+
60+
/**************************************************************************************
61+
* PRIVATE MEMBER FUNCTIONS
62+
**************************************************************************************/
63+
64+
void NTPUtils::sendNTPpacket(UDP &udp)
65+
{
66+
uint8_t ntp_packet_buf[NTP_PACKET_SIZE] = {0};
67+
68+
ntp_packet_buf[0] = 0b11100011;
69+
ntp_packet_buf[1] = 0;
70+
ntp_packet_buf[2] = 6;
71+
ntp_packet_buf[3] = 0xEC;
72+
ntp_packet_buf[12] = 49;
73+
ntp_packet_buf[13] = 0x4E;
74+
ntp_packet_buf[14] = 49;
75+
ntp_packet_buf[15] = 52;
76+
77+
udp.beginPacket(NTP_TIME_SERVER, NTP_TIME_SERVER_PORT);
78+
udp.write(ntp_packet_buf, NTP_PACKET_SIZE);
79+
udp.endPacket();
80+
}
81+
82+
/**************************************************************************************
83+
* NAMESPACE
84+
**************************************************************************************/
85+
86+
} /* opcua */

src/NTPUtils.h

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/*
2+
* Copyright (c) 2024 Arduino
3+
*
4+
* SPDX-License-Identifier: MPL-2.0
5+
* This Source Code Form is subject to the terms of the Mozilla Public
6+
* License, v. 2.0. If a copy of the MPL was not distributed with this
7+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
8+
*/
9+
10+
#pragma once
11+
12+
/*
13+
This Utility Class is derived from the example code found here https://www.arduino.cc/en/Tutorial/UdpNTPClient
14+
For more information on NTP (Network Time Protocol) you can refer to this Wikipedia article https://en.wikipedia.org/wiki/Network_Time_Protocol
15+
*/
16+
17+
/**************************************************************************************
18+
* INCLUDE
19+
**************************************************************************************/
20+
21+
#include <Arduino.h>
22+
#include <Udp.h>
23+
24+
/**************************************************************************************
25+
* NAMESPACE
26+
**************************************************************************************/
27+
28+
namespace opcua
29+
{
30+
31+
/**************************************************************************************
32+
* CLASS DECLARATION
33+
**************************************************************************************/
34+
35+
class NTPUtils
36+
{
37+
public:
38+
39+
static unsigned long getTime(UDP &udp);
40+
41+
private:
42+
43+
static size_t const NTP_PACKET_SIZE = 48;
44+
static int const NTP_TIME_SERVER_PORT = 123;
45+
static int const NTP_LOCAL_PORT = 8888;
46+
47+
static unsigned long const NTP_TIMEOUT_MS = 1000;
48+
static constexpr const char *NTP_TIME_SERVER = "time.arduino.cc";
49+
50+
static void sendNTPpacket(UDP &udp);
51+
};
52+
53+
/**************************************************************************************
54+
* NAMESPACE
55+
**************************************************************************************/
56+
57+
} /* opcua */

src/arch/posix/ua_clock.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,11 @@ extern "C" {
1818

1919
int clock_gettime(clockid_t clk_id, struct timespec *tp)
2020
{
21-
tp->tv_sec = millis() / 1000;
22-
tp->tv_nsec = (millis() % 1000) * 1000000;
21+
/* Obtain time from RTC. */
22+
time_t const epoch = time(NULL);
23+
/* No nanosecond resolution. */
24+
tp->tv_sec = epoch;
25+
tp->tv_nsec = 0;
2326
return 0;
2427
}
2528

src/cvt_time.cpp

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/*
2+
* Copyright (c) 2024 Arduino
3+
*
4+
* SPDX-License-Identifier: MPL-2.0
5+
* This Source Code Form is subject to the terms of the Mozilla Public
6+
* License, v. 2.0. If a copy of the MPL was not distributed with this
7+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
8+
*/
9+
10+
/**************************************************************************************
11+
* INCLUDE
12+
**************************************************************************************/
13+
14+
#include "cvt_time.h"
15+
16+
#include <stdio.h>
17+
#include <string.h>
18+
19+
/**************************************************************************************
20+
* NAMESPACE
21+
**************************************************************************************/
22+
23+
namespace opcua
24+
{
25+
26+
/**************************************************************************************
27+
* FUNCTION DEFINITION
28+
**************************************************************************************/
29+
30+
time_t cvt_time(char const * time)
31+
{
32+
static time_t build_time = 0;
33+
34+
if (!build_time) {
35+
char s_month[5];
36+
int month, day, year;
37+
struct tm t =
38+
{
39+
0 /* tm_sec */,
40+
0 /* tm_min */,
41+
0 /* tm_hour */,
42+
0 /* tm_mday */,
43+
0 /* tm_mon */,
44+
0 /* tm_year */,
45+
0 /* tm_wday */,
46+
0 /* tm_yday */,
47+
0 /* tm_isdst */
48+
};
49+
static const char month_names[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
50+
51+
sscanf(time, "%s %d %d", s_month, &day, &year);
52+
53+
month = (strstr(month_names, s_month) - month_names) / 3;
54+
55+
t.tm_mon = month;
56+
t.tm_mday = day;
57+
t.tm_year = year - 1900;
58+
t.tm_isdst = -1;
59+
60+
build_time = mktime(&t);
61+
}
62+
63+
return build_time;
64+
}
65+
66+
/**************************************************************************************
67+
* NAMESPACE
68+
**************************************************************************************/
69+
70+
} /* opcua */

src/cvt_time.h

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* Copyright (c) 2024 Arduino
3+
*
4+
* SPDX-License-Identifier: MPL-2.0
5+
* This Source Code Form is subject to the terms of the Mozilla Public
6+
* License, v. 2.0. If a copy of the MPL was not distributed with this
7+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
8+
*/
9+
10+
#pragma once
11+
12+
/**************************************************************************************
13+
* INCLUDE
14+
**************************************************************************************/
15+
16+
#include <time.h>
17+
18+
/**************************************************************************************
19+
* NAMESPACE
20+
**************************************************************************************/
21+
22+
namespace opcua
23+
{
24+
25+
/**************************************************************************************
26+
* FUNCTION DECLARATION
27+
**************************************************************************************/
28+
29+
time_t cvt_time(char const * time);
30+
31+
/**************************************************************************************
32+
* NAMESPACE
33+
**************************************************************************************/
34+
35+
} /* opcua */

0 commit comments

Comments
 (0)