스마트 디바이스 12주 ntp서버 - yubiine/25-1_smartdevice GitHub Wiki

⭐️ 목표

ESP32를 이용하여 NTP 서버로부터 시간 정보를 가져와 OLED 디스플레이에 표시하는 방법을 학습한다.

📌OLED란?

  • 유기 발광 다이오드 - 백라이트가 필요없어 더 얇고 전력소모가 적으며 선명한 화질 구현 가능
  • 크기: 일반적으로 0.96인치, 1.3인치, 1.5인치 등이 널리 사용된다.
  • 해상도: 128x64, 128x32 해상도가 대표적이다.
  • 색상: 단색(흰색, 파랑 등) 또는 컬러 OLED가 있으며, 본 실습에서는 단색 OLED를 사용한다.

📌I2C 인터페이스란?

직렬 통신 방식으로 아두이노와 같은 마이크로 컨트롤러와 센서, 디스플레이, 메모리 등 여러 장치를 두개의 선(SCL, SDA) 만으로 연결할 수 있게 해주는 통신 규격이다.

🔹I2C 인터페이스의 주요 특징

항목 설명
선 개수 2개 (SCL: 클록 신호, SDA: 데이터 신호)
통신 방식 마스터-슬레이브 방식
속도 일반적으로 100kbps (표준), 400kbps (패스트 모드), 최대 3.4Mbps (하이스피드)
주소 방식 각 장치에 고유 주소 부여 (7비트 또는 10비트 주소)
장점 선이 적고 구조가 단순해 공간 절약 및 배선 간소화
단점 속도가 상대적으로 느리고 거리 제한이 있음

🔹통신 원리 요약

  1. ESP32는 SDA/SCL 라인을 통해 OLED에 명령과 데이터를 전송한다.
  2. OLED는 받은 데이터를 디스플레이에 출력한다.
  3. I2C 주소(기본값 0x3C)로 슬레이브를 식별한다.

🔹주의사항

  • I2C 통신 문제(데이터 출력 오류, "SSD1306 오류") 발생 시, SDA/SCL 연결 및 I2C 주소를 점검.
  • I2C 주소는 OLED 모듈에 따라 다를 수 있으므로 데이터시트를 확인.
  • 전원 공급은 안정적인 3.3V 또는 5V를 사용.

📌NTP(Network Time Protocol)란?

  • 정의: 인터넷 상에서 컴퓨터 시계를 동기화하는 프로토콜
  • 목적: 네트워크에 연결된 장치들의 시간을 정확하게 맞춰 로그·통신·보안 정합성 유지

🔹동작원리

  1. 클라이언트 NTP 서버에 시간 요청
    UDP 패킷으로 시간 요청
  2. 서버 → 클라이언트 응답
    UTC 기반 시각을 전달
  3. 지연 시간 보정
    클라이언트는 왕복 지연(RTT)을 계산해 실제 시각 보정

🔹Stratum 계층 구조

  • Stratum 0: 원자시계, GPS 수신기 등 최상위 시간 소스
  • Stratum 1: Stratum NTP 서버
  • Stratum 2+: 상위 Stratum 서버로부터 동기화받음 (일반서버) (일반적으로 Stratum 2~4 사용)

🔹예시

상황 이유
로그 기록 정확한 시간 기록이 필수 (보안 분석 등)
분산 시스템 서로 다른 서버 간 시간 오차를 줄여야 함
인증 시스템 시간 기반 토큰 등의 정확성 유지 필요
금융 시스템 거래 시각이 정확해야 법적 효력이 있음

📌실습

image

ESP32 OLED

Adafruit_SSD1306 라이브러리와 Adafruit_GFX 라이브러리 설치
image

🔹실습 코드

#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 32

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);

void setup() {
  Serial.begin(9600);

  // OLED 디스플레이 초기화
  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    Serial.println("SSD1306 오류");
    while (true);
  }

  // OLED 디스플레이 클리어
  display.clearDisplay();
}

void loop() {
  // "Hello, World!"를 크기가 다른 세 개의 텍스트로 표시
  display.clearDisplay();
  
  // 크기 6의 폰트로 텍스트 표시
  display.setTextSize(1);
  display.setTextColor(WHITE);
  display.setCursor(0, 0);
  display.println("Hello, World!");

  // 크기 8의 폰트로 텍스트 표시
  display.setTextSize(2);
  display.setTextColor(WHITE);
  display.setCursor(0, 15);
  display.println("Hello, World");

  display.display();
  delay(2000);
}

🔹실습 결과

image

ESP32 OLED NTP서버 활용

🔹실습 코드

#include <WiFi.h>  // WiFi 통신을 위한 라이브러리
#include <time.h>  // 시간과 관련된 함수를 위한 라이브러리

#include <Adafruit_GFX.h>      // 디스플레이를 위한 그래픽 라이브러리
#include <Adafruit_SSD1306.h>  // SSD1306 OLED 디스플레이를 위한 라이브러리
// SSD1306 디스플레이 객체 초기화
Adafruit_SSD1306 display = Adafruit_SSD1306(128, 32, &Wire, -1);

const char* ssid = "Your_SSID";          // 여기에 사용하는 WiFi 네트워크 이름 (SSID)을 입력하세요
const char* password = "Your_Password";  // 여기에 사용하는 WiFi 네트워크 비밀번호를 입력하세요

int GMTOffset = 60 * 60 * 9;  // 시간 오프셋 설정, 한국은 UTC/GMT +9입니다.
int daylightOffset = 0;       // 국가에서 서머타임을 사용하는 경우 오프셋 값을 설정하세요.

const String weekDays[7] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };  // 요일 이름 배열

void setup() {
  Serial.begin(115200);  // 디버깅을 위한 시리얼 통신 시작

  // SSD1306 디스플레이 초기화
  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    Serial.println(F("SSD1306 초기화 실패"));
    while (1)
      ;  // 디스플레이 초기화에 실패하면 프로그램 실행 중지
  }

  delay(2000);                  // 2초 동안 대기
  display.clearDisplay();       // 디스플레이 지우기
  display.setTextSize(1);       // 텍스트 크기를 1로 설정
  display.setCursor(0, 0);      // 커서 위치를 디스플레이 왼쪽 위 모서리로 설정
  display.setTextColor(WHITE);  // 텍스트 색상을 흰색으로 설정

  WiFi.begin(ssid, password);  // 제공된 SSID와 비밀번호를 사용하여 WiFi 네트워크에 연결
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Connecting...");
  }

  Serial.println("Connected to Wi-Fi!");
  // 시간 설정을 위한 NTP 서버 설정
  configTime(GMTOffset, daylightOffset, "pool.ntp.org", "time.nist.gov");
}


void loop() {

  // 현재 시간 가져오기
  time_t rawtime = time(nullptr);
  struct tm* timeinfo = localtime(&rawtime);

  // 시리얼 모니터에 날짜 출력
  Serial.print(timeinfo->tm_mday);
  Serial.print("/");
  Serial.print(timeinfo->tm_mon + 1);
  Serial.print("/");
  Serial.print(timeinfo->tm_year + 1900);

  Serial.print(" ");

  // 시리얼 모니터에 시간 출력
  Serial.print("Time: ");
  Serial.print(timeinfo->tm_hour);
  Serial.print(":");
  Serial.print(timeinfo->tm_min);
  Serial.print(":");
  Serial.println(timeinfo->tm_sec);

  // OLED 디스플레이 초기화
  display.clearDisplay();
  display.setTextSize(3);
  display.setTextColor(WHITE);
  display.setCursor(0, 0);

  // 시간 출력
  if (timeinfo->tm_hour < 10)
    display.print("0");
  display.print(timeinfo->tm_hour);

  display.print(":");

  if (timeinfo->tm_min < 10)
    display.print("0");
  display.print(timeinfo->tm_min);

  display.print(":");

  // 초 출력
  display.setTextSize(2);
  display.setCursor(102, 5);

  if (timeinfo->tm_sec < 10)
    display.print("0");
  display.print(timeinfo->tm_sec);

  // 날짜 출력
  display.setTextSize(1);
  display.setCursor(0, 25);
  display.print(timeinfo->tm_mday);
  display.print("/");
  display.print(timeinfo->tm_mon + 1);
  display.print("/");
  display.print(timeinfo->tm_year + 1900);

  display.print(" ");
  display.print(weekDays[timeinfo->tm_wday]);

  // 디스플레이에 표시
  display.display();

  delay(1000);
}```
### 🔹실습 결과

https://github.com/user-attachments/assets/2b175b64-eaab-4acc-a6b3-4a33ee5ff0a1


⚠️ **GitHub.com Fallback** ⚠️