




🧭 작동 요약
- 평상시
- LCD 1행: 현재 시각
- LCD 2행: 급이 시각
- 급이 시각 설정
- * → 시 입력
- 숫자 입력
- # → 분 입력
- 숫자 입력
- # → 설정 완료
- 하루 1회만 자동 급이
#include <ThreeWire.h>
#include <RtcDS1302.h>
#include <Servo.h>
#include <LiquidCrystal_I2C.h>
#include <Keypad.h>
void handleKeypad();
void updateLCD(const RtcDateTime& now);
void checkFeedTime(const RtcDateTime& now);
void print2(int num);
void feedFish();
/* ================== RTC (DS1302) ================== */
#define DS1302_CLK 12
#define DS1302_DAT 11
#define DS1302_RST 10
ThreeWire myWire(DS1302_DAT, DS1302_CLK, DS1302_RST);
RtcDS1302<ThreeWire> Rtc(myWire);
/* ================== SERVO ================== */
Servo feederServo;
const int SERVO_PIN = 13;
const int REST_ANGLE = 0;
const int DROP_ANGLE = 180;
/* ================== LCD ================== */
LiquidCrystal_I2C lcd(0x27, 16, 2);
/* ================== KEYPAD ================== */
const byte ROWS = 4;
const byte COLS = 4;
char keys[ROWS][COLS] = {
{'1','2','3','A'},
{'4','5','6','B'},
{'7','8','9','C'},
{'*','0','#','D'}
};
byte rowPins[ROWS] = {9, 8, 7, 6};
byte colPins[COLS] = {5, 4, 3, 2};
Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS);
/* ================== FEED TIME ================== */
int feedHour = 17;
int feedMinute = 13;
/* ================== STATE ================== */
int lastFeedMinute = -1;
enum Mode { NORMAL, SET_HOUR, SET_MIN };
Mode mode = NORMAL;
/* ================== SETUP ================== */
void setup() {
Serial.begin(9600);
feederServo.attach(SERVO_PIN);
feederServo.write(REST_ANGLE);
Rtc.Begin();
if (!Rtc.GetIsRunning()) {
Rtc.SetIsRunning(true);
}
// 최초 1회만 사용
//Rtc.SetDateTime(RtcDateTime(__DATE__, __TIME__));
lcd.init();
lcd.backlight();
lcd.clear();
Serial.println("구피 자동 급이기 시작");
}
/* ================== LOOP ================== */
void loop() {
RtcDateTime nowUTC = Rtc.GetDateTime();
// UTC -> KST 변환
int hourKST = nowUTC.Hour() + 9;
int dayoffset = 0;
if (hourKST >= 24) {
hourKST -= 24;
dayoffset = 1;
}
RtcDateTime nowKST(
nowUTC.Year(),
nowUTC.Month(),
nowUTC.Day() + dayoffset,
hourKST,
nowUTC.Minute(),
nowUTC.Second()
);
handleKeypad();
updateLCD(nowKST);
checkFeedTime(nowKST);
delay(500); // 타이밍 안정성 확보
}
/* ================== FUNCTIONS ================== */
// 키패드 입력 처리
void handleKeypad() {
char key = keypad.getKey();
if (!key) return;
if (mode == NORMAL && key == '*') {
mode = SET_HOUR;
feedHour = 0;
lcd.clear();
}
else if (mode == SET_HOUR && isDigit(key)) {
feedHour = feedHour * 10 + (key - '0');
if (feedHour > 23) feedHour = 23;
}
else if (mode == SET_HOUR && key == '#') {
mode = SET_MIN;
feedMinute = 0;
lcd.clear();
}
else if (mode == SET_MIN && isDigit(key)) {
feedMinute = feedMinute * 10 + (key - '0');
if (feedMinute > 59) feedMinute = 59;
}
else if (mode == SET_MIN && key == '#') {
mode = NORMAL;
lastFeedMinute = -1; // 재설정 즉시 반영
lcd.clear();
}
}
// LCD 기본 화면 표시
void updateLCD(const RtcDateTime& now) {
lcd.setCursor(0,0);
lcd.print("Now ");
print2(now.Hour());
lcd.print(":");
print2(now.Minute());
lcd.print(":");
print2(now.Second());
lcd.setCursor(0,1);
if (mode == SET_HOUR) {
lcd.print("Set Hour: ");
print2(feedHour);
}
else if (mode == SET_MIN) {
lcd.print("Set Min : ");
print2(feedMinute);
}
else {
lcd.print("Feed ");
print2(feedHour);
lcd.print(":");
print2(feedMinute);
}
}
// 급이 시각 체크
void checkFeedTime(const RtcDateTime& now) {
// 자정 지나면 하루 리셋
if (now.Hour() == 0 && now.Minute() == 0) {
lastFeedMinute = -1;
}
// 같은 분에 처음 진입했을 때만 급이
if (now.Hour() == feedHour &&
now.Minute() == feedMinute &&
lastFeedMinute != now.Minute()) {
feedFish();
lastFeedMinute = now.Minute();
}
}
// 실제 급이 + 성공 메시지 표시
void feedFish() {
// 1. 급이 시작 표시
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Feeding...");
// 2. 서보 동작
const unsigned long FEED_HOLD_TIME = 1500; // 4초 (원하는 만큼 조절)
feederServo.write(DROP_ANGLE);
delay(FEED_HOLD_TIME); // ← 급이량을 결정하는 핵심
feederServo.write(REST_ANGLE);
delay(1000);
// 3. 급이 완료 표시
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Feed done");
// 사람이 인지할 수 있도록 대기
delay(2000);
// 4. 화면 초기화 (다음 loop에서 기본 화면 표시)
lcd.clear();
}
// 2자리 출력
void print2(int num) {
if (num < 10) lcd.print("0");
lcd.print(num);
}
반응형
'창의공작소' 카테고리의 다른 글
| 아두이노_구피 사료 자동 공급기 (0) | 2025.12.01 |
|---|---|
| 조도 센서를 이용한 자동 야간 조명등 만들기 (2) | 2024.07.15 |
| (대충, 허접)차박용 led 조명등 만들기 (0) | 2022.10.01 |
| 재활용품 활용한 LED 무드등 (0) | 2022.09.17 |
| 레고 시티 기차 레일_3D 프린트 (0) | 2020.07.18 |