기본 콘텐츠로 건너뛰기

ESP8266 + TM1637 WIFI 인터넷 시계 만들기

ESP8266 과 TM1637 으로 인터넷 시계 만들기

원래는 다이소에서 판매하는 5000원짜리 시계 뜯어서 만드려고 했는데, 시계를 뜯어보니, 자체 MCU를 통해 제어를 하고 있어 TM1637 로 구현하고 나중에 케이스를 만드는게 나을 것 같아서 아래와 같이 구현(chatGPT)했다.

  1. 30분마다 NTP 가져오기
  2. WIFI가 끊기더라도 RTC 유지
  3. WIFI가 끊기면 1분에 한 번씩 재접속 시도
  4. 버튼을 3초 누르면, AP 설정 모드 (88:88 표시)

#include <ESP8266WiFi.h>
#include <WiFiUdp.h>
#include <TM1637Display.h>
#include <WiFiManager.h> // https://github.com/tzapu/WiFiManager

#define CLK D5  // TM1637 CLK
#define DIO D6  // TM1637 DIO
#define CONFIG_BUTTON_PIN D7  // 설정 진입 버튼

TM1637Display display(CLK, DIO);

// 시간 관련 변수
time_t currentEpochTime = 0;
unsigned long lastNtpMillis = 0;
unsigned long checkTermMillis = 1800000UL; // 30분마다 NTP 다시 받아옴 // Wi-Fi 재접속 관련
unsigned long lastWiFiReconnectAttempt = 0;
const unsigned long wifiReconnectInterval = 60000; // 1분

// 버튼 관련
unsigned long buttonPressedTime = 0;
bool buttonWasHeld = false;
const unsigned long CONFIG_HOLD_TIME = 3000; // 3초

bool isInConfigMode = false;

const char* ntpServer = "pool.ntp.org";
const int timeZone = 9; // 한국은 +09:00

WiFiUDP udp;
unsigned int localPort = 2390;

void setup() {
  Serial.begin(115200);
  display.setBrightness(0x0f);
  display.showNumberDecEx(0, 0b11100000, true); // 00:00 표시
  pinMode(CONFIG_BUTTON_PIN, INPUT);

  WiFiManager wm;
  if (!wm.autoConnect("ESPClockAP")) {
    Serial.println("WiFi 연결 실패");
  } else {
    Serial.println("WiFi 연결됨: " + WiFi.SSID());
  }

  connectNTP();
}

void connectNTP() {
  Serial.println("NTP 시간 동기화 시도");
  udp.begin(localPort);
  sendNTPpacket(ntpServer);

  delay(1000);

  if (udp.parsePacket()) {
    byte packetBuffer[48];
    udp.read(packetBuffer, 48);

    unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
    unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);
    unsigned long secsSince1900 = highWord << 16 | lowWord;

    currentEpochTime = secsSince1900 - 2208988800UL + timeZone * 3600;
    lastNtpMillis = millis();

    Serial.println("NTP 동기화 완료: " + String(currentEpochTime));
  } else {
    Serial.println("NTP 응답 없음");
  }
}

void sendNTPpacket(const char* address) {
  byte packetBuffer[48] = { 0 };
  packetBuffer[0] = 0b11100011;
  packetBuffer[1] = 0;
  packetBuffer[2] = 6;
  packetBuffer[3] = 0xEC;
  udp.beginPacket(address, 123);
  udp.write(packetBuffer, 48);
  udp.endPacket();
}

void enterConfigPortal() {
  isInConfigMode = true;
  display.clear();
  display.showNumberDecEx(8888, 0b11100000, true); // 88:88 표시

  WiFiManager wm;
  wm.setConfigPortalTimeout(180); // 3분 제한

  // 스마트폰으로 WifiClock/8888 로 접속해서 WiFi SID 설정
  bool res = wm.startConfigPortal("WiFiClock", "8888");

  if (res) {
    Serial.println("WiFi 설정 완료!");
  } else {
    Serial.println("설정 시간 초과");
  }

  delay(1000);
  ESP.restart();
}

void loop() {
  static int prevMinute = -1;
  static unsigned long lastNtpCheck = 0;
  unsigned long nowMillis = millis();

  if (isInConfigMode) return;

  // 1. 버튼 3초 이상 누르면 설정 모드 진입
  int buttonState = digitalRead(CONFIG_BUTTON_PIN);
  if (buttonState == HIGH) {
    if (buttonPressedTime == 0) {
      buttonPressedTime = nowMillis;
    } else if (!buttonWasHeld && nowMillis - buttonPressedTime >= CONFIG_HOLD_TIME) {
      buttonWasHeld = true;
      Serial.println("버튼 3초 이상 눌림 - 설정 모드 진입");
      enterConfigPortal();
    }
  } else {
    buttonPressedTime = 0;
    buttonWasHeld = false;
  }

  // 2. WiFi 끊긴 경우 1분마다 재접속
  if (WiFi.status() != WL_CONNECTED && nowMillis - lastWiFiReconnectAttempt >= wifiReconnectInterval) {
    lastWiFiReconnectAttempt = nowMillis;
    WiFi.reconnect();
    Serial.println("WiFi 재접속 시도");
  }

  // 3. 30분마다 NTP 재동기화
  if (nowMillis - lastNtpCheck >= checkTermMillis) {
lastNtpCheck = nowMillis; connectNTP(); } // 4. 소프트 RTC 기반으로 시간 표시 time_t now = currentEpochTime + (nowMillis - lastNtpMillis) / 1000; int hours = (now % 86400L) / 3600; int minutes = (now % 3600) / 60; if (minutes != prevMinute) { prevMinute = minutes; int displayTime = hours * 100 + minutes; display.showNumberDecEx(displayTime, 0b11100000, true); // ':' on Serial.printf("Time: %02d:%02d\n", hours, minutes); } delay(100); }

댓글

이 블로그의 인기 게시물

Winget 해시 무시하기

가끔씩 Winget 에서 패키지를 다운로드 했을 때, "설치 관리자 해시가 일치하지 않습니다." 오류가 뜰 때가 있다. 보안 이슈가 있지만, 그냥 무시하고 싶을 때, 아래 순서로 무시해준다. 관리자 권한 winget settings --enable InstallerHashOverride 설치 winget install --ignore-security-hash --id NirSoft.NirCmd

Windows 11 기존 컨텍스트 메뉴 사용

Windows 11 에서, 컨텍스트 메뉴가 지저분한게 싫었는지, 모던 컨텍스트 메뉴라고 따로 필요한 것만 정리해서 보여준다. 그러나 이게 좀 불편하고, 기존의 꼭 필요한 메뉴가 보이지 않아 굳이 한 번 더 기존 메뉴를 불러오는데, 모든 앱들이 모던 컨텍스트 메뉴로 옮길 때까지는 기존 컨텍스트 메뉴를 기본으로 볼 수 있는 방법이 있다. REM 관리자 권한 REM 기존 컨텍스트 메뉴 reg.exe add "HKCU\Software\Classes\CLSID\{86ca1aa0-34aa-4e8b-a509-50c905bae2a2}\InprocServer32" /f /ve REM 모던 컨텍스트 메뉴로 되돌리기 reg.exe delete "HKCU\Software\Classes\CLSID\{86ca1aa0-34aa-4e8b-a509-50c905bae2a2}" /f 이거 하고 탐색기를 재시작한다. 참조:  Restore old Right-click Context menu in Windows 11