개발자/Arduino

Arduino Nano 33 IoT 개발 가이드 4 - 내부 RTC와 Sleep Mode

지구빵집 2020. 5. 13. 16:05
반응형

 

Arduino Nano 33 IoT 개발 가이드 4 - 내부 RTC와 Sleep Mode(6장)

 

여기에서는 Arduino 33 IoT의 내부 RTC 모듈에 대해 실습하기로 한다. IoT 보드는 스케치 프로그램에서 RTC 라이브러리를 사용해 접근할 수 있도록 내장 RTC 모듈을 제공한다. RTC 모듈 레퍼런스 링크를 참고하기 바란다. 

 

전체 강의자료와 소스코드는 아래를 참고하십시요. 소스코드의 출처는 위의 책 마지막 페이지에 소개되어 있습니다.

 

Arduino Nano 33 IoT 개발 가이드 1 - 개발환경

Arduino Nano 33 IoT 개발 가이드 2 - 기본적인 예제 살펴보기

Arduino Nano 33 IoT 개발 가이드 3 - WiFi Network 실습

Arduino Nano 33 IoT 개발 가이드 4 - 내부 RTC와 Sleep Mode

Arduino Nano 33 IoT 개발 가이드 5 - Arduino Cloud

Arduino Nano 33 IoT 개발 가이드 6 - Accelerator and Gyroscope

Arduino Nano 33 IoT 개발 가이드 7 - Bluetooth Low Energy (BLE)

실습에 필요한 전체 소스코드 다운

 

우선 RTC 라이브러리를 포함하기 위해 메뉴에서 -> 스케치 -> 아리브러리 포함하기 ->라이브러리 관리를 실행한다. 아래 이미지를 참고한다.

 

메뉴에서 -> 스케치 -> 아리브러리 포함하기 ->라이브러리 관리

라이브러리 관리 화면에서 검색창에 rtc로 검색한다. 맨위에 RTCZero 라이브러리가 보이는데 설치한다. 아래 화면에 rtc 검색후 RTCZero 라이브러리가 보인다.

 

 

RTCZero 라이브러리에서 setTime()과 setDate 함수를 사용해서 시간을 설정할 수 있고, 아래의 함수를 이용해서 시간 정보를 가져올 수 있다. 아래 데모 소스코드를 입력한다. 

- getYear()
- getMonth()
- getDay()
- getHours()
- getMinutes()
- getSeconds()

 

RTCdemo.ino 로 파일을 저장하고 툴 -> 보드 설정에서 "Arduino NANO 33 IoT" 로 선택하고, 포트를 연결된 포트 설정 COM63 등으로 선택하고 컴파일 업로드 한다.

 

#include <RTCZero.h>

RTCZero rtc;

void setup() 
{
	while (!Serial);
	Serial.begin(9600);
	rtc.begin(); // initialize RTC
	Serial.println("RTC set time");
	// set the current time 8 August 2019 7:23 AM
	rtc.setDate(10, 8, 19);
	rtc.setTime(7, 23, 00);
	delay(1000);
	Serial.println("RTC Read time");
	Serial.println("-------------------");
}

void loop() 
{
	Serial.print("RTC, Time = ");
	print2digits(rtc.getMinutes());
	Serial.write(':');
	print2digits(rtc.getHours());
	Serial.write(':');
	print2digits(rtc.getSeconds());
	Serial.print(", Date (D/M/Y) = ");
	Serial.print(rtc.getDay());
	Serial.write('/');
	Serial.print(rtc.getMonth());
	Serial.write('/');
	Serial.print(rtc.getYear());
	Serial.println();
	delay(1000);
}

void print2digits(int number) 
{
	if (number >= 0 && number < 10) 
    {
		Serial.write('0');
	}
	Serial.print(number);
}

 

시리얼 모니터를 실행하여 시간 데이터 출력을 확인한다. setup() 함수를 보면 시간 설정을 다음과 같은 코드로 한다. 데이터를 정확히 포맷에 맞춰 입력한다.

 

// set the current time 8 August 2019 7:23 AM
rtc.setDate(10, 8, 19);  ---> 5, 7, 20 
rtc.setTime(7, 23, 00);  ---> 11, 48, 00 으로 현재 시간을 바꾸어 설정하여 실습하기 바란다.

 

RTCdemo 실행화면

 

Connecting to Network Time Protocol (NTP) Server

 

아주 중요한 실습이므로 꼭 알아두어야 한다. 특정 시스템의 시간을 바로 현재 시간으로 동기화 할 필요가 있다. 이러한 경우에 NTP 프로토콜을 사용한다.

 

"Network Time Protocol의 약자로 Network 상에 연결된 장비와 장비 간에 시간 정보를 동기화하기 위한 프로토콜을 말합니다. NTP는 계층적인 구조를 가지는데 각각의 계층은 상위 계층으로부터 시간을 동기화합니다. 한국에서 운영되고 있는 NTP 서버에서 정보를 동기화하여 다른 장비들에게 서비스할 수 있습니다. NTP Client세팅으로 상위 계층에서 시간 정보를 받아 동기화할 수 있고, NTP Server세팅을 통해 다른 장비들에게 정보를 보내줄 수 있습니다." 상세 내용은 위키백과 네크워크 타임 프로토콜을 참조.

 

여기서 NTP를 통해 time.nist.gov의 시간을 가져온다. 아두이노 프로그램 예제를 이용한다. 아래 이미지를 참고해 예제를 연다. 메뉴에서 파일 -> 예제 -> WiFiNINA -> WifiUdpNtpClient 예제를 불러온다.

 

여기서 중요한 점은 arduino_secrets.h 파일에 있는 네트워크의 SSID 와 패스워드를 바꿔주는 일이다. 불러온 예제 파일, 즉 WifiUdpNtpClient 예제를 "다른 이름으로 저장" 을 선택하여 폴더를 지정하여 저장해 주면 그 폴더 아래에 arduino_secrets.h 파일이 생성된다. 이 파일을 열어 보면 아래와 같다. 참고로 WiFiUdpNtpClient.ino 소스코드를 올려둔다.

 

/*

 Udp NTP Client

 Get the time from a Network Time Protocol (NTP) time server
 Demonstrates use of UDP sendPacket and ReceivePacket
 For more on NTP time servers and the messages needed to communicate with them,
 see http://en.wikipedia.org/wiki/Network_Time_Protocol

 created 4 Sep 2010
 by Michael Margolis
 modified 9 Apr 2012
 by Tom Igoe

 This code is in the public domain.

 */

#include <SPI.h>
#include <WiFiNINA.h>
#include <WiFiUdp.h>

int status = WL_IDLE_STATUS;
#include "arduino_secrets.h" 
///////please enter your sensitive data in the Secret tab/arduino_secrets.h
char ssid[] = SECRET_SSID;        // your network SSID (name)
char pass[] = SECRET_PASS;    // your network password (use for WPA, or use as key for WEP)
int keyIndex = 0;            // your network key Index number (needed only for WEP)

unsigned int localPort = 2390;      // local port to listen for UDP packets

IPAddress timeServer(129, 6, 15, 28); // time.nist.gov NTP server

const int NTP_PACKET_SIZE = 48; // NTP time stamp is in the first 48 bytes of the message

byte packetBuffer[ NTP_PACKET_SIZE]; //buffer to hold incoming and outgoing packets

// A UDP instance to let us send and receive packets over UDP
WiFiUDP Udp;

void setup() {
  // Open serial communications and wait for port to open:
  Serial.begin(9600);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
  }

  // check for the WiFi module:
  if (WiFi.status() == WL_NO_MODULE) {
    Serial.println("Communication with WiFi module failed!");
    // don't continue
    while (true);
  }

  String fv = WiFi.firmwareVersion();
  if (fv < "1.0.0") {
    Serial.println("Please upgrade the firmware");
  }

  // attempt to connect to Wifi network:
  while (status != WL_CONNECTED) {
    Serial.print("Attempting to connect to SSID: ");
    Serial.println(ssid);
    // Connect to WPA/WPA2 network. Change this line if using open or WEP network:
    status = WiFi.begin(ssid, pass);

    // wait 10 seconds for connection:
    delay(10000);
  }

  Serial.println("Connected to wifi");
  printWifiStatus();

  Serial.println("\nStarting connection to server...");
  Udp.begin(localPort);
}

void loop() {
  sendNTPpacket(timeServer); // send an NTP packet to a time server
  // wait to see if a reply is available
  delay(1000);
  if (Udp.parsePacket()) {
    Serial.println("packet received");
    // We've received a packet, read the data from it
    Udp.read(packetBuffer, NTP_PACKET_SIZE); // read the packet into the buffer

    //the timestamp starts at byte 40 of the received packet and is four bytes,
    // or two words, long. First, esxtract the two words:

    unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
    unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);
    // combine the four bytes (two words) into a long integer
    // this is NTP time (seconds since Jan 1 1900):
    unsigned long secsSince1900 = highWord << 16 | lowWord;
    Serial.print("Seconds since Jan 1 1900 = ");
    Serial.println(secsSince1900);

    // now convert NTP time into everyday time:
    Serial.print("Unix time = ");
    // Unix time starts on Jan 1 1970. In seconds, that's 2208988800:
    const unsigned long seventyYears = 2208988800UL;
    // subtract seventy years:
    unsigned long epoch = secsSince1900 - seventyYears;
    // print Unix time:
    Serial.println(epoch);


    // print the hour, minute and second:
    Serial.print("The UTC time is ");       // UTC is the time at Greenwich Meridian (GMT)
    Serial.print((epoch  % 86400L) / 3600); // print the hour (86400 equals secs per day)
    Serial.print(':');
    if (((epoch % 3600) / 60) < 10) {
      // In the first 10 minutes of each hour, we'll want a leading '0'
      Serial.print('0');
    }
    Serial.print((epoch  % 3600) / 60); // print the minute (3600 equals secs per minute)
    Serial.print(':');
    if ((epoch % 60) < 10) {
      // In the first 10 seconds of each minute, we'll want a leading '0'
      Serial.print('0');
    }
    Serial.println(epoch % 60); // print the second
  }
  // wait ten seconds before asking for the time again
  delay(10000);
}

// send an NTP request to the time server at the given address
unsigned long sendNTPpacket(IPAddress& address) {
  //Serial.println("1");
  // 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)
  //Serial.println("2");
  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;

  //Serial.println("3");

  // 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
  //Serial.println("4");
  Udp.write(packetBuffer, NTP_PACKET_SIZE);
  //Serial.println("5");
  Udp.endPacket();
  //Serial.println("6");
}


void printWifiStatus() {
  // print the SSID of the network you're attached to:
  Serial.print("SSID: ");
  Serial.println(WiFi.SSID());

  // print your WiFi shield's IP address:
  IPAddress ip = WiFi.localIP();
  Serial.print("IP Address: ");
  Serial.println(ip);

  // print the received signal strength:
  long rssi = WiFi.RSSI();
  Serial.print("signal strength (RSSI):");
  Serial.print(rssi);
  Serial.println(" dBm");
}

 

arduino_secrets.h 파일을 열어 "" 안에 무선 WiFi의 SSID와 password를 알맞게 넣어준다.

 

#define SECRET_SSID ""
#define SECRET_PASS ""

 

실행하면 결과는 아래 화면과 같다. 각 메시지와 시간을 확인한다. 시간이 3:42:33초 이렇게 나오는데 이것은 그리니치 천문대 세계 표준 시간이므로 해당하는 나라의 지역 시간으로 바꾸어야 한다. 한국은 9시간을 더하면 12:43분으로 정확하게 맞는다. 확인했다. 와우! 참고로 리눅스 타임은 시작될 때부터 1초씩 시간을 잰 횟수를 말한다. 그래서 정확히 표준시로 이용하게 되었다. 

 

NTP 서버 시간 동기화 결과 화면

 

우리는 Arduino Nano 33 IoT 를 RTCZero 라이브러리의 함수 stanbyMode()를 사용해서 sleep mode 로 설정할 수 있다. 보드를 깨우기 위해 setAlarmTime() 을 설정한다. callback function and enableAlarm() 함수를 이용해 실행을 하도록 한다.

 

실습을 위해 setup() 함수에서 sleep mode로 실행하는 간단한 프로그램을 만든다. 보드가 깨어나면 loop() 를 실행한다. 아래 코드를 입력한다.

 

RTCAlarm.ino 파일

 

#include <RTCZero.h>
int led = 13;
RTCZero rtc;
void setup() {
while (!Serial);
Serial.begin(9600);
pinMode(led, OUTPUT);
digitalWrite(led, LOW);
rtc.begin(); // initialize RTC
Serial.println("RTC set time");
// set the current time 11 August 2019 7:23 AM
// 2020년 5월 7일 12시 53분이니까
rtc.setDate(7, 5, 20);
rtc.setTime(12, 54, 00);
delay(1000);
Serial.println("RTC Read time");
Serial.println("-------------------");
Serial.println("set standby");
rtc.setAlarmTime(12, 55, 00);
rtc.enableAlarm(rtc.MATCH_HHMMSS);
rtc.attachInterrupt(wakeup);
rtc.standbyMode();
}
void loop() 
{
// this program will be executed
// after program wakes up
Serial.println(">> do something");
delay(800);
}
void wakeup()
{
Serial.println("Wake up...");
digitalWrite(led, HIGH);
}

아래는 결과 화면이다. 설정은 잘 되고 1분 후에 정확히 13번 핀의 내장 LED 가 불이 켜지며 실행하는 것으로 확인은 되지만 loop 함수를 실행하는 메세지가 표시가 안되었다. 통신 모드가 sleep 으로 영향을 받는지도 모른다. 여하는 다른 결과를 확인하신 분은 답글 부탁드린다.

 

NTP 알람 실습 결과

 

여기까지 하고 놀러나가야지. ^^

 

초한지 천하대전. 유방은 항우의 여인 우미인이 잃어버린 비파를 찾아준다.

 

 

 

 

반응형