메뉴 전체보기

회원메뉴

괴짜들을 위한 시계: 바이너리 워치 > 게시판

본문 바로가기

쇼핑몰 검색

NO. 3011
제목 : 괴짜들을 위한 시계: 바이너리 워치
2026-08-15 11:30

소개: 괴짜들을 위한 시계: 바이너리 워치


소개: 괴짜들을 위한 시계: 바이너리 워치


친구들에게 깊은 인상을 남기고 싶었던 적이 있나요? 이제 나만의 이진법 시계를 직접 만들어 이진 코드로 시간을 확인할 수 있습니다.


전체 제작 영상 링크 : https://youtu.be/bifUcp3U8ec




STEP 1. 준비물


준비물


다음은 이 PCB 바이너리 시계 제작에 필요한 주요 부품 목록입니다.


주요 구성 요소

  1. 1 × ATMEGA328P-AU — U1
  2. 1 × DS3231MZ RTC — U2
  3. 1 × CH340C USB-시리얼 변환 IC — U3
  4. 1 × TP4056-42-ESOP8 리튬폴리머(LiPo) 충전기 IC — U4
  5. 1 × CSTCE16M0V53-R0 16 MHz 공진기 — Y1

LED

  1. 18 × WS2812B-V6 어드레서블 RGB LED — D1–D18
  2. 1 × PMEG4010BEA 쇼트키 다이오드 — D19
  3. 1 × KT-0805G 녹색 LED — D20
  4. 1 × KT-0805R 적색 LED — D21

저항기

  1. 1 × 1.2 kΩ 저항기 — BAT1
  2. 4 × 4.7 kΩ 저항기 — R2–R5
  3. 1 × 10 kΩ 저항기 — R6
  4. 2 × 1 kΩ 저항기 — R7–R8
  5. 1 × 2.7 kΩ 저항기 — R9

커패시터

  1. 4 × 0.1 µF (100 nF) 커패시터 — C1–C4

커넥터 및 제어 장치

  1. 1 × USB-C 16핀 리셉터클 (USB 2.0) — J2
  2. 2 × EVQPUJ02K 촉감식 푸시 버튼 — SW1–SW2
  3. 1 × 리튬폴리머 배터리 — BT1




STEP 2. PCB 기판 제조


PCB 기판 제조


PCB 기판 제조


저는 PCB 제작을 JLCPCB에 의뢰했습니다. 이 회사는 제가 캐나다와 중국에서 모두 이용해 본 곳입니다. 웹사이트가 매우 사용자 친화적이며, PCB 제조, SMT 조립, PCB 스텐실 등 다양한 서비스를 제공합니다.

PCB 제조에 필요한 모든 Gerber 파일을 업로드했습니다. 또한 BOM (자재 명세서)부품 배치/위치 파일 도 포함했습니다. JLCPCB에 부품 조립을 맡기고 싶다면 사용할 수 있습니다. JLCPCB에서는 PCB 조립을 주문할 때 BOM과 배치 파일을 직접 업로드할 수 있습니다.

JLCPCB 웹사이트:

https://jlcpcb.com/




STEP 3. PCB 조립


PCB 조립


PCB 조립


PCB 조립


PCB 조립


PCB 조립


먼저 회로도를 따라 부품을 올바르게 배치할 위치를 확인합니다. 저는 먼저 패드에 액체 납땜 플럭스를 바른 다음, 핀셋으로 각 부품을 올바른 위치에 조심스럽게 배치했습니다.

마이크로칩에는 실크스크린에 흰색 점이 있는데, 이는 핀 1의 위치를 나타냅니다. 납땜하기 전에 칩의 방향을 올바르게 맞추십시오.

모든 부품을 올바른 위치에 배치한 후, 저는 열풍 리워크 건 으로 납땜재를 가열하여 부품을 제자리에 납땜했습니다.




STEP 4. 코드 업로드하기


코드 업로드하기


기판에 모든 부품을 납땜하고 나면 다음을 사용하여 시계를 프로그래밍할 수 있습니다. Arduino IDE.

중요: USB-C 포트를 여러 기능에 사용하도록 변경했으므로, USB-C 케이블을 연결하는 올바른 방법은 한 가지뿐입니다 . 프로그래밍하려면 케이블을 기판에 올바른 방향으로 연결해야 합니다.

먼저 컴퓨터에 CH340C USB-to-serial drivers 가 설치되어 있는지 확인하세요. 공식 Windows 드라이버는 여기에서 다운로드할 수 있습니다: CH340/CH341 Windows Driver — WCH

드라이버가 설치되면 USB-C 케이블을 올바른 방향으로 연결하세요. Windows에서 연결음이 나면 CH340C USB-to-serial (TTL) chip이 성공적으로 감지되었다는 뜻입니다.

그런 다음 Arduino IDE를 열고 적절한 보드와 COM 포트를 선택한 후 다음 코드를 시계에 업로드하세요.

참고!: 코드를 업로드하기 전에 다음 라이브러리를 Arduino IDE Library Manager를 통해 설치하세요:

  1. RTClib — by Adafruit
  2. Adafruit NeoPixel — by Adafruit


// BinaryWatch
// By Newson's Electronics
// August, 2026

#include <Wire.h>
#include <RTClib.h>
#include <Adafruit_NeoPixel.h>
#include <avr/sleep.h>
#include <avr/power.h>

#define LED_PIN 4
#define NUM_LEDS 18
#define BUTTON_UP 2
#define BUTTON_NEXT 3

int brightness = 1;

Adafruit_NeoPixel strip(NUM_LEDS, LED_PIN, NEO_GRB + NEO_KHZ800);
RTC_DS3231 rtc;

// ================= COLORS =================

uint32_t RED = 0xFF0000;
uint32_t GREEN = 0x00FF00;
uint32_t YELLOW = 0xFFFF00;
uint32_t BLUE = 0x0000FF; // low voltage

// ================= EDIT MODE =================

bool changeMode = false;

bool charge = false;

int editMode = 0;
// 0 = seconds
// 1 = minutes
// 2 = hours

int editSeconds;
int editMinutes;
int editHours;

// ================= BUTTON =================

unsigned long buttonTimer = 0;
bool buttonHeld = false;

// ================= FLASH =================

unsigned long flashTimer = 0;
bool flashState = true;

// ================= SLEEP =================

unsigned long lastActivity = 0;
const unsigned long sleepDelay = 60000; //ms
const unsigned long flashDelay = 100; //ms

unsigned long d2SleepTimer = 0;
bool d2SleepHeld = false;


// ================= WAKE FLAG =================

volatile bool wakeFlag = false;

void setup() {

Serial.begin(9600);


long voltage = readVcc();





pinMode(BUTTON_UP, INPUT_PULLUP);
pinMode(BUTTON_NEXT, INPUT_PULLUP);
//digitalWrite(BUTTON_UP, HIGH);
//digitalWrite(BUTTON_NEXT, HIGH);


strip.begin();
strip.setBrightness(brightness);
strip.show();

if (!rtc.begin()) {
Serial.println("RTC NOT FOUND");
}

// Set RTC from computer upload time
// COMMENT THIS AFTER FIRST UPLOAD

//rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));

// D2 wakes from sleep

attachInterrupt(digitalPinToInterrupt(BUTTON_UP), wakeUp, FALLING);

lastActivity = millis();
}

void loop() {

// Enter sleep after 60 seconds idle

if (!changeMode && millis() - lastActivity > sleepDelay) {
enterSleep();
}


// Always check buttons

if (changeMode == false) {
checkBrightness();
checkEnterMode();
checkD2Sleep();
} else {
checkD2Increment();
checkD3Next();
checkD3Save();
}


// Update clock only once per second

static unsigned long clockTimer = 0;

if ((millis() - clockTimer >= 1000)||changeMode == true) {

clockTimer = millis();

DateTime now = rtc.now();




if (changeMode == false) {

displayClock(
now.hour(),
now.minute(),
now.second());


printTime(
now.hour(),
now.minute(),
now.second());

} else {

displayEditMode();


printTime(
editHours,
editMinutes,
editSeconds);
}
}

delay(10);
}


// ================= D2 HOLD TO SLEEP =================

void checkD2Sleep()
{
// Only work in normal mode
if (changeMode)
return;

bool d2 = digitalRead(BUTTON_UP);

// D2 is pressed
if (d2 == LOW)
{
// Start timing the hold
if (!d2SleepHeld)
{
d2SleepTimer = millis();
d2SleepHeld = true;
}

// Held for 1 second
if (millis() - d2SleepTimer >= 1000)
{
d2SleepHeld = false;

Serial.println("D2 HELD - GOING TO SLEEP");
strip.clear();
strip.show();
d2=0;
delay(2000);

// Reset activity timer
lastActivity = millis();

enterSleep();
}
}
else
{
// Button released
d2SleepHeld = false;
}
}

// ================= BATTERY VOLTAGE =================
// Reads LiPo voltage using ATmega328P internal 1.1V reference
// Returns voltage in millivolts

long readVcc()
{
// Save current ADC settings
uint8_t oldADMUX = ADMUX;
uint8_t oldADCSRA = ADCSRA;

// Enable ADC power
power_adc_enable();

// Enable ADC
ADCSRA |= _BV(ADEN);

// Select internal 1.1V reference
ADMUX = _BV(REFS0) |
_BV(MUX3) |
_BV(MUX2) |
_BV(MUX1);

// Allow internal reference to stabilize
delay(10);

// Clear ADC interrupt flag
ADCSRA |= _BV(ADIF);

// Start conversion
ADCSRA |= _BV(ADSC);

// Wait for conversion to finish
while (ADCSRA & _BV(ADSC))
{
}

// Read ADC result
uint8_t low = ADCL;
uint8_t high = ADCH;

uint16_t result = ((uint16_t)high << 8) | low;

// Disable ADC
ADCSRA &= ~_BV(ADEN);

// Disable ADC power
power_adc_disable();

// Restore previous ADC settings
ADMUX = oldADMUX;
ADCSRA = oldADCSRA;

// Calculate Vcc in millivolts
long vcc = 1125300L / result;

Serial.print("Battery Voltage: ");
Serial.print(vcc / 1000.0);
Serial.println(" V");


charge = (vcc < 3400); // low battery below 3.4V

//return vcc;
}

// ================= BRIGHTNESS ADJUST NORMAL MODE =================

void checkBrightness() {

static bool lastPressed = false;

bool d2 = digitalRead(BUTTON_UP);
bool d3 = digitalRead(BUTTON_NEXT);

// Both buttons pressed together

if (d2 == LOW && d3 == LOW) {

if (!lastPressed) {

brightness += 1;

if (brightness > 20)
brightness = 1;

strip.setBrightness(brightness);
strip.show();

Serial.print("Brightness: ");
Serial.println(brightness);

lastActivity = millis();
}

lastPressed = true;

} else {

lastPressed = false;
}
}

// ================= DISPLAY CLOCK =================

void displayClock(int h, int m, int s) {

strip.clear();

if (charge == true) { strip.setPixelColor(17, BLUE); }


// seconds

displayBinary(
s,
0,
GREEN);

// minutes

displayBinary(
m,
6,
YELLOW);

// hours

displayBinary(
h,
12,
RED);

strip.show();
}
// ================= ENTER EDIT MODE =================

void checkEnterMode() {

if (digitalRead(BUTTON_NEXT) == LOW) {

if (!buttonHeld) {

buttonTimer = millis();
buttonHeld = true;
}

if (millis() - buttonTimer > 1000) {

DateTime now = rtc.now();

editHours = now.hour();
editMinutes = now.minute();
editSeconds = now.second();

editMode = 0;

changeMode = true;

buttonHeld = false;

lastActivity = millis();

Serial.println("EDIT MODE");
}

} else {

buttonHeld = false;
}
}

// ================= EDIT DISPLAY =================

void displayEditMode() {

if (millis() - flashTimer > flashDelay) {

flashTimer = millis();

flashState = !flashState;
}

strip.clear();

// Display rows not being edited

if (editMode != 0)
displayBinary(editSeconds, 0, GREEN);

if (editMode != 1)
displayBinary(editMinutes, 6, YELLOW);

if (editMode != 2)
displayBinary(editHours, 12, RED);

// Flash selected row

if (flashState) {

if (editMode == 0)
displayBinary(editSeconds, 0, GREEN);

if (editMode == 1)
displayBinary(editMinutes, 6, YELLOW);

if (editMode == 2)
displayBinary(editHours, 12, RED);
}

strip.show();
}

// ================= D2 INCREMENT =================

void checkD2Increment() {

static bool lastState = HIGH;

bool current = digitalRead(BUTTON_UP);

if (lastState == HIGH && current == LOW) {

lastActivity = millis();

if (editMode == 0) {

editSeconds++;

if (editSeconds > 59)
editSeconds = 0;
}

if (editMode == 1) {

editMinutes++;

if (editMinutes > 59)
editMinutes = 0;
}

if (editMode == 2) {

editHours++;

if (editHours > 23)
editHours = 0;
}
}

lastState = current;
}

// ================= D3 NEXT =================

void checkD3Next() {

static bool lastState = HIGH;

bool current = digitalRead(BUTTON_NEXT);

if (lastState == HIGH && current == LOW) {

lastActivity = millis();

editMode++;

if (editMode > 2)
editMode = 0;

if (editMode == 0)
Serial.println("EDIT SECONDS");

if (editMode == 1)
Serial.println("EDIT MINUTES");

if (editMode == 2)
Serial.println("EDIT HOURS");
}

lastState = current;
}

// ================= D3 SAVE =================

void checkD3Save() {

if (digitalRead(BUTTON_NEXT) == LOW) {

if (!buttonHeld) {

buttonTimer = millis();

buttonHeld = true;
}

if (millis() - buttonTimer > 1000) {

DateTime now = rtc.now();

rtc.adjust(DateTime(
now.year(),
now.month(),
now.day(),
editHours,
editMinutes,
editSeconds));

changeMode = false;

buttonHeld = false;

lastActivity = millis();

Serial.println("TIME SAVED");
}

} else {

buttonHeld = false;
}
}

// ================= BINARY LED DISPLAY =================

void displayBinary(int value, int startLED, uint32_t color) {

for (int bit = 0; bit < 6; bit++) {

if (value & (1 << bit)) {

strip.setPixelColor(
startLED + bit,
color);
}
}
}

// ================= SERIAL TIME =================

void printTime(int h, int m, int s) {

Serial.print("Time: ");

if (h < 10)
Serial.print("0");

Serial.print(h);

Serial.print(":");

if (m < 10)
Serial.print("0");

Serial.print(m);

Serial.print(":");

if (s < 10)
Serial.print("0");

Serial.print(s);

// display time to sleep
unsigned long sleepCountdown = (sleepDelay - (millis() - lastActivity)) / 1000;

Serial.print(" Sleep in: ");
Serial.print(sleepCountdown);
Serial.println(" seconds");
}

// ================= DEEP SLEEP =================

void enterSleep()
{
// Make sure buttons are released before sleeping
if (digitalRead(BUTTON_UP) == LOW ||
digitalRead(BUTTON_NEXT) == LOW)
{
return;
}

Serial.println("GOING TO SLEEP");

strip.clear();
strip.show();

// Disable peripherals
power_adc_disable();
power_spi_disable();
power_timer1_disable();
power_timer2_disable();
power_twi_disable();

// Do NOT disable Timer0 because millis() and delay() need it
//power_timer0_disable();

set_sleep_mode(SLEEP_MODE_PWR_DOWN);

// Wait for button noise to settle
delay(50);

// Check again before sleeping
if (digitalRead(BUTTON_UP) == LOW ||
digitalRead(BUTTON_NEXT) == LOW)
{
power_all_enable();
return;
}

// Clear any pending interrupt
EIFR |= (1 << INTF0);

sleep_enable();

// Enter sleep
sleep_mode();

// Execution continues here after wake

sleep_disable();

// Clear interrupt again
EIFR |= (1 << INTF0);

// Restore peripherals
power_all_enable();

delay(10);

// Wait until wake button is released
while (digitalRead(BUTTON_UP) == LOW)
{
delay(10);
}

lastActivity = millis();

Serial.println("AWAKE");
readVcc();


}

// ================= WAKE INTERRUPT =================

void wakeUp() {

wakeFlag = true;
}






STEP 5. 시계의 작동 원리


시계의 작동 원리


시계의 작동 원리


시계의 작동 원리


시계의 작동 원리


시계의 작동 원리


이 시계는 오른쪽에 있는 두 개의 푸시 버튼으로 조작합니다.

시계 켜기 및 끄기

시계의 상단 버튼 을 누르면 시계가 켜집니다. 시계를 절전 모드로 전환하려면 상단 버튼을 약 1초동안 길게 누릅니다.

시간 설정

시간을 설정하려면 하단 버튼을 1초 동안길게 누릅니다. 시계에서 현재 편집 중인 행이 깜박이기 시작합니다:

  1. 첫 번째 행:
  2. 두 번째 행:
  3. 세 번째 행:

선택한 행의 값을 증가시키려면 상단 버튼 을 누릅니다.

정확한 시간이 설정되면 하단 버튼을 1초 동안 길게 눌러 시간을 저장하고 일반 시계 모드로 돌아갑니다.

LED 밝기 조절

일반 시계 모드 인 상태에서빠르게 두 버튼을 동시에누릅니다. 누를 때마다 LED 밝기가 증가합니다. 최대 밝기에 도달하면 설정이 다시 가장 어두운 밝기 단계로 돌아갑니다.

배터리 부족 표시

시계가 절전 모드로 전환될 때마다 LiPo 배터리의 전압을 확인합니다.

배터리 전압이 너무 낮은 것으로감지되면, 시계 왼쪽 상단 모서리의 파란색 LED 가 배터리를 재충전해야 함을 알리기 위해 켜집니다.




STEP 6. 시간을 읽는 법은?


시간을 읽는 법은?


시간을 읽는 법은?


시간 읽는 법

시계의 각 행은 시간의 한 부분을 나타냅니다. 초, 분, 시. 각 행에는 LED 6개가 있으며, 각 LED는 이진값 하나를 나타냅니다. 값은 1, 2, 4, 8, 16, 32이므로 한 행에 표시할 수 있는 최댓값은 63입니다.


행에 표시되는 숫자를 확인하려면 켜진 모든 LED의 값을 더합니다.

예: LED에 1 + 4 + 8 = 13이 표시되면 해당 행은 13을 나타냅니다.


따라서 예를 들어 시계의 13 이 첫 번째 행에, 42 가 두 번째 행에, 18 이 세 번째 행에 표시된다면 시간은 18:42:13입니다.


이 프로젝트를 재미있게 보셨기를 바랍니다! 이 시계 디자인의 개선된 버전을 개발할 계획입니다. 내부 8 MHz 발진기와 전력 MOSFET을 활용해 대기 전류를 더욱 줄이고 배터리 수명을 연장할 예정입니다. 아이디어나 피드백이 있으시면 언제든지 메시지를 보내 주세요.



원문: https://www.instructables.com/A-Watch-for-Nerds-the-Binary-Watch/

좋아요 0
게시판

회사명 공방유니언 주소 각 공방 주소 참조
사업자 등록번호 0000 대표 공방유니언 전화 019-60105846 팩스 0000 이메일 gongbangunion@gmail.com
통신판매업신고번호 0000 개인정보관리책임자 공방유니언
Copyright © 2017 공방유니언. All Rights Reserved.