12주차 ‐ ESP32 OLED NTP서버 활용 - boguuu/SmartDevice_2025-1 GitHub Wiki
ESP32를 사용하여 NTP(Network Time Protocol) 서버에서 시간을 가져오고, OLED 디스플레이에 현재 시간을 표시하는 디지털 시계를 제작
- ESP32
- ESP32 확장 실드
- OLED 디스플레이
- NTP(Network Time Protocol): 네트워크를 통해 컴퓨터 시스템 간 시간을 동기화하는 프로토콜
- 개발자: David L. Mills (1980년대)
- 활용처: 인터넷과 로컬 네트워크에서 광범위하게 사용됨
- NTP 서버는 GPS나 원자 시계로 정확한 시간 유지
- 클라이언트는 서버에 주기적으로 접속해 시간 정보를 받아 동기화
- 정밀도: 밀리초 단위의 높은 정확도 가능
- 네트워크 지연을 고려하여 시간 조정 수행
- Stratum 0: GPS, 원자 시계 등 (직접 네트워크 연결 X)
- Stratum 1: Stratum 0과 연결된 NTP 서버
- Stratum 2 이상: 상위 Stratum 서버로부터 시간 수신
- Stratum 15까지 존재, 숫자가 높을수록 정확도 낮아짐
- 클라이언트 → 서버로 시간 요청
- 서버 → 클라이언트로 시간 전송
- 클라이언트가 시간 조정
- 주기적으로 반복
- 보안 로그 정확성 확보
- 금융 거래 시간 신뢰성 보장
- 분산 시스템 동기화 유지
- 과학 실험 시간 기록에 필수
-
pool.ntp.org
: 전 세계 분산형 서버 -
time.nist.gov
: 미국 NIST 제공 -
time.windows.com
: Microsoft 기본 서버
- NTP는 시스템 간 정확하고 일관된 시간 유지를 가능하게 하여 보안, 거래, 시스템 운영의 기반을 강화하는 핵심 기술
Adafruit_SSD1306 라이브러리와 Adafruit_GFX 라이브러리 두 가지 라이브러리를 설치
#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);
}
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
- 🖼️ Adafruit_GFX: 폰트, 도형, 좌표 같은 그래픽 기능 담당
- 💻 Adafruit_SSD1306: OLED 디스플레이 제어 라이브러리 (SSD1306 칩 전용)
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 32
- 📏 OLED 디스플레이의 해상도 지정
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
- 🔌 I²C 통신으로 SSD1306 제어
- 🛠️ 리셋 핀은 사용 안 함 (
1
)
Serial.begin(9600);
- 🐞 시리얼 모니터 출력용 (디버깅용)
if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println("SSD1306 오류");
while (true);
}
⚠️ OLED 초기화 실패 시 무한 루프- 🧠 주소
0x3C
는 대부분의 I²C OLED에서 기본값
display.clearDisplay();
- 🧽 디스플레이 화면 초기화
display.clearDisplay();
- 🧼 화면 지우고 새로 그림
display.setTextSize(1);
display.setTextColor(WHITE);
display.setCursor(0, 0);
display.println("Hello, World!");
- 🔡 작은 폰트 (기본 크기)로 "Hello, World!" 출력
display.setTextSize(2);
display.setCursor(0, 15);
display.println("Hello, World");
- 🔠 더 큰 폰트 (2배 확대)로 같은 문구 출력
display.display();
delay(2000);
- 🖥️ 버퍼 내용을 실제 화면에 표시
- ⏱️ 2초 동안 화면 유지 후 반복
#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);
}
#include <WiFi.h>
#include <time.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
- 📡
WiFi.h
: ESP32를 WiFi에 연결할 수 있게 해줌 - 🕐
time.h
: NTP 서버에서 시간 가져올 때 사용 - 🖼️
Adafruit_GFX
+Adafruit_SSD1306
: OLED 그래픽 출력 담당
Adafruit_SSD1306 display = Adafruit_SSD1306(128, 32, &Wire, -1);
- 128×32 해상도의 OLED 객체 생성
- I²C 방식 사용 (
Wire
), 리셋 핀 없음 (-1)
const char* ssid = "Your_SSID";
const char* password = "Your_Password";
- 🔐 연결할 WiFi의 이름과 비밀번호 입력
int GMTOffset = 60 * 60 * 9;
int daylightOffset = 0;
- ⌛ 한국 기준 GMT+9
- 🌞 서머타임 미사용 (0)
const String weekDays[7] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
- 요일 문자열 배열
Serial.begin(115200);
- 시리얼 모니터 디버깅용
if (!display.begin(...)) {
Serial.println(F("SSD1306 초기화 실패"));
while (1);
}
- 디스플레이 초기화 시 실패하면 멈춤
delay(2000);
display.clearDisplay();
display.setTextSize(1);
display.setCursor(0, 0);
display.setTextColor(WHITE);
- 초기 설정 및 OLED 클리어
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.println("Connecting...");
}
Serial.println("Connected to Wi-Fi!");
- 📶 WiFi 연결 시도 → 성공할 때까지 대기
configTime(GMTOffset, daylightOffset, "pool.ntp.org", "time.nist.gov");
- 🌐 NTP 서버에서 시간 동기화 설정
time_t rawtime = time(nullptr);
struct tm* timeinfo = localtime(&rawtime);
- 시스템에서 현재 시간 정보 받아오기
Serial.print(timeinfo->tm_mday);
...
Serial.println(timeinfo->tm_sec);
- 📆 날짜와 ⏱️ 시간 시리얼에 출력
display.clearDisplay();
display.setTextSize(3);
display.setCursor(0, 0);
if (timeinfo->tm_hour < 10) display.print("0");
display.print(timeinfo->tm_hour);
display.print(":");
...
- 10보다 작을 땐 앞에 0 붙여서 두 자리로 표현
- 시, 분은 크게 (
setTextSize(3)
), 초는 작게 (setTextSize(2)
)
display.setTextSize(1);
display.setCursor(0, 25);
display.print(timeinfo->tm_mday);
...
display.print(weekDays[timeinfo->tm_wday]);
- 하단에 날짜와 요일 표시
display.display();
delay(1000);
- 화면 갱신하고 1초 대기
📚 라이브러리 포함
#include <WiFi.h> // ESP32의 Wi-Fi 기능 사용
#include <time.h> // 시간 관련 함수 사용 (NTP 동기화)
#include <Adafruit_GFX.h> // OLED 디스플레이를 위한 그래픽 라이브러리
#include <Adafruit_SSD1306.h> // SSD1306 OLED 디스플레이 라이브러리
- ESP32의 Wi-Fi 연결과 시간 동기화를 위해
WiFi.h
,time.h
를 포함 - OLED 화면 출력을 위해
Adafruit_GFX
와Adafruit_SSD1306
를 포함
🖥️ OLED 객체 초기화
Adafruit_SSD1306 display = Adafruit_SSD1306(128, 32, &Wire, -1);
- 128x32 픽셀 크기의 OLED 디스플레이 객체를 생성
- I2C 인터페이스를 사용(
Wire
), 리셋 핀은 -1로 설정(사용 안 함).
🌐 Wi-Fi 및 시간 설정
const char* ssid = "Your_SSID"; // Wi-Fi 네트워크 이름
const char* password = "Your_Password"; // Wi-Fi 비밀번호
int GMTOffset = 60 * 60 * 9; // UTC+9 (한국 표준시)
int daylightOffset = 0; // 서머타임 오프셋 (한국은 사용 안 함)
- 접속할 Wi-Fi의 SSID와 비밀번호를 지정
- 시간대를 한국(UTC+9)으로 설정하고 서머타임은 비활성화
요일 배열
const String weekDays[7] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
- tm 구조체의
tm_wday
값(0~6)을 문자열 요일로 매핑하기 위해 배열을 준비
🔧 setup() 초기 설정
Serial.begin(115200); // 디버깅용 시리얼 통신 시작
- ESP32가 컴퓨터와 시리얼 통신(속도 115200bps)으로 디버깅 메시지를 출력할 수 있게 설정
if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println(F("SSD1306 초기화 실패"));
while (1);
}
- OLED 초기화 시 실패하면 오류 메시지를 출력하고 무한 루프로 멈춤
delay(2000); // OLED 초기화 후 잠시 대기
display.clearDisplay(); // OLED 화면 초기화
display.setTextSize(1); // 텍스트 크기 지정
display.setCursor(0, 0); // 텍스트 위치 지정
display.setTextColor(WHITE); // 텍스트 색상 지정
- OLED를 초기화하고 글자 크기, 위치, 색상을 설정
WiFi.begin(ssid, password); // Wi-Fi 연결 시작
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.println("Connecting...");
}
Serial.println("Connected to Wi-Fi!");
- Wi-Fi 연결을 시도하며 연결 완료 시까지 "Connecting..." 출력 후 "Connected to Wi-Fi!" 출력
configTime(GMTOffset, daylightOffset, "pool.ntp.org", "time.nist.gov");
- 지정된 NTP 서버에서 현재 시간을 동기화하도록 설정
🔁 loop() 반복 동작
time_t rawtime = time(nullptr);
struct tm* timeinfo = localtime(&rawtime);
- 현재 시간을
time_t
형식으로 받아오고, 이를tm
구조체(연,월,일,시,분,초 등으로 분리)로 변환
Serial.print(timeinfo->tm_mday); ... Serial.println(timeinfo->tm_sec);
- 현재 날짜와 시간을 시리얼 모니터에 출력
display.clearDisplay(); // OLED 화면 지우기
display.setTextSize(3); // 시간 표시용 큰 글자
display.setCursor(0, 0);
- OLED 화면을 초기화하고 시간 표시용 큰 글자 크기로 설정
🕒 시/분/초 출력
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);
- 시와 분이 한 자리수일 때 앞에 0을 붙여 가독성을 높임
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]);
- 날짜(일/월/년)와 요일을 한 줄로 OLED 하단에 출력
display.display(); // OLED에 최종 출력
delay(1000); // 1초마다 갱신
- 변경된 내용을 OLED에 표시하고 1초마다 화면을 갱신
📌 요약
- Wi-Fi로 연결하여 인터넷 NTP 서버에서 시간 정보를 가져옴
- OLED에 현재 시각(시:분:초)과 날짜/요일을 1초 단위로 갱신해 출력