친구들에게 깊은 인상을 남기고 싶었던 적이 있나요? 이제 나만의 이진법 시계를 직접 만들어 이진 코드로 시간을 확인할 수 있습니다.
먼저 회로도를 따라 부품을 올바르게 배치할 위치를 확인합니다. 저는 먼저 패드에 액체 납땜 플럭스를 바른 다음, 핀셋으로 각 부품을 올바른 위치에 조심스럽게 배치했습니다.
기판에 모든 부품을 납땜하고 나면 다음을 사용하여 시계를 프로그래밍할 수 있습니다. Arduino IDE.
드라이버가 설치되면 USB-C 케이블을 올바른 방향으로 연결하세요. Windows에서 연결음이 나면 CH340C USB-to-serial (TTL) chip이 성공적으로 감지되었다는 뜻입니다.
그런 다음 Arduino IDE를 열고 적절한 보드와 COM 포트를 선택한 후 다음 코드를 시계에 업로드하세요.
// 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;
}
이 시계는 오른쪽에 있는 두 개의 푸시 버튼으로 조작합니다.
시계의 각 행은 시간의 한 부분을 나타냅니다. 초, 분, 시. 각 행에는 LED 6개가 있으며, 각 LED는 이진값 하나를 나타냅니다. 값은 1, 2, 4, 8, 16, 32이므로 한 행에 표시할 수 있는 최댓값은 63입니다.
이 프로젝트를 재미있게 보셨기를 바랍니다! 이 시계 디자인의 개선된 버전을 개발할 계획입니다. 내부 8 MHz 발진기와 전력 MOSFET을 활용해 대기 전류를 더욱 줄이고 배터리 수명을 연장할 예정입니다. 아이디어나 피드백이 있으시면 언제든지 메시지를 보내 주세요.