5mm 폴리카보네이트 조각으로 약 6cm 원형 디스크를 프렛톱으로 잘라냈습니다. 중앙에 8mm 구멍을 뚫었습니다(피더 축 두께. 이 플라스틱 축은 패들 휠에서 분리 가능).
외곽에 3mm 구멍 6개 뚫었습니다(자석 두께). 패들 휠에 고무 패들 6개가 있어 6개 자석을 사용, 디스크 가장자리에 60도 간격으로 배치. 봉 자석이 딱 맞게 들어갑니다.
홀 센서가 6개 자석을 모두 감지해 패들 휠 위치를 정확히 알고 정지할 수 있습니다. 서보 정지 지연 시간 변수는 급식 후, 서보 정지 전 지연을 주어 패들 휠 정확한 위치를 미세 조정합니다. '정지' 위치가 틀리면 사료가 패들 휠에서 완전히 안 나옵니다. 홀 센서 위치가 다를 수 있으니 이 변수로 조정하세요.
이제 패들 휠 축을 폴리카보네이트 디스크에 접착합니다.
흰색 원형 서보 혼을 M3 볼트 3개로 폴리카보네이트 디스크에 볼트 고정. 너트를 스페이서로 사용하고 과도하게 조이지 마세요.
이제 서보가 피더 어셈블리 일부가 되었으니 나무 마운트에 놓고 서보 아래 나무 조각 높이를 측정합니다.
홀 센서
사용한 홀 센서는 단극성(UNIPOLAR) 타입입니다. 자석이 가까우면 LOW 출력, 자석이 멀어지면 역극성 없이도 자동으로 HIGH로 리셋됩니다. 단극성 홀 센서가 훨씬 편합니다.
!!! 홀 센서에는 10kΩ 풀업 저항이 필요합니다.
0805 SMD 저항을 홀 센서 위에 직접 납땜했지만 본인 방식대로 하셔도 됩니다. 이 저항 빠뜨리지 마세요!
구형 볼펜 플라스틱 몸체를 홀 센서 홀더로 활용했습니다.
전자부품 하우징
해먼드 ABS 하우징(플랜지 포함) 120 x 65 x 40mm 사용.
외부 연결용으로 저가 중국산이 아닌 정밀 소켓 커넥터(암놈) 사용. 모든 프로젝트에 이 커넥터 쓰는데, 불량 연결로 인한 찾기 힘든 고장만큼 나쁜 건 없으니까요.
아두이노 프로그래밍 편의를 위해 외부 FTDI 연결도 마련:
전면 패널
저렴하고 멋진 전면 패널 만들려고 여러 방법 시도...
모드 포지로 레이저 프린트 종이를 하우징 전면에 붙이고 위에 모드 포지 덧칠 → 실패
폴리우레탄 아크릴레이트 분산액 기반 투명 바니시. 종이 잘 붙이고 상단 바니시 칠하니 멋졌지만 레이저 프린터 토너 부분엔 접착 안 됨 → 실패
전면 패널 프린트 상단만 라미네이팅 PVC로 코팅. 멋진 마감, 색상 선명. 라미네이트 종이를 바니시로 하우징에 붙임. 바니시가 종이 스며들어 방수 되길 기대했으나 두꺼운 종이라 안 되고 물 테스트하니 종이 말려서 떨어짐 → 부분 실패, 추가 연구 필요
레이저 프린트용 특수 알루미늄 호일 구매. 장당 2.25유로. 결과 좋았지만 토너 보호용 투명 필름이 실패(사진 1의 결점 참조).
다시 투명 바니시로 보호 시도. 알루미늄 호일이 초고광택! 하지만 바니시가 토너에 안 붙어 바니시 영구 포기...
최종적으론... 보호 필름 없이 호일 프린트 그대로 사용. 자주 만지면 지문 자국 남음(이 페이지 상단 사진의 BACK 버튼 아래, 닭 사료 있는 하우징 부분 참조) 이상적이진 않지만 제겐 충분.
더 많은 사진은 업로드 제한으로 생략.
이상입니다... 즐거운 제작 되세요!
코드
Pet Feed-O-Matic v 1.1 - 20-02-2018 (Arduino)
// c++ stuff...
//#include <Arduino.h>
//#line 1
//#line 1 "/Users/Erik/Documents/PlatformIO/Projects/180202-151127-uno/src/Pet_Feeder_1_1_ENG.cpp"
/*
아스키 아트 생략...
Erik de Ruiter
-----------------------------------------------------------------------------
To do:
- 원격 급식?
- 피더 정지 지연을 메뉴 편집 항목으로
last change: 2018-02-20 17:20:28
FEATURES:
- *정확한 분량* 매번! (홀 센서 활용)
- 하루 한 번 두 급식 시간
- *최고 정확도* RTC (정품 DS3231 칩만)
- 예정된 급식 개별 취소 (디스플레이/LED 표시). 시간 경과 후 자동 복구
- 수동 급식 (버튼당 1분량)
- 급식 시간별 분량 조절 (1-9분량)
- 메인 화면에서 모든 설정 한눈에
- 쉬운 메뉴 탐색
- 급식 성공 LED 표시
- LCD 백라이트 타이머 (30초 후 끔, 버튼 누르면 켬)
- 홀 센서 고장 시 백업 기능
- 홀 센서 고장 시 자정까지 LED 깜빡임
- 시간 등 설정이 EEPROM에 안전 저장
WEBSITES:
LCD HD44780 캐릭터 생성기: https://omerk.github.io/lcdchargen/
아두이노 스케치 큰 글자 주석 생성기: https://patorjk.com/software/taag/#p=display&c=c++&f=Big&t=Comment
Revisions: 2018-01-28 20:17:10
*/
////////////////////////////////////////////////////////////////////////////////
// 사용자 변경 가능 변수
////////////////////////////////////////////////////////////////////////////////
// 홀 센서 고장 백업 기능용. 간격은 피더 60도 회전(1분량)보다 약간 크게
#define HALL_SENSOR_BACKUP_VALUE 300
// 급식 후 서보 정지 전 지연. 패들 휠이 올바른 위치에서 멈추게 조정
#define FEEDER_STOP_DELAY 100
// 버튼 누른 후 LCD 백라이트 켜진 시간
#define LCD_BACKLIGHT_ON_TIME 30000
// 저장 없이 메뉴 종료 타임아웃 (ms)
#define MENU_TIMEOUT_VALUE 7000
////////////////////////////////////////////////////////////////////////////////
// 라이브러리
////////////////////////////////////////////////////////////////////////////////
// https://github.com/fdebrabander/Arduino-LiquidCrystal-I2C-library
#include <LiquidCrystal_I2C.h>
// https://github.com/JChristensen/Button
#include <Button.h>
// https://github.com/JChristensen/DS3232RTC
#include <DS3232RTC.h>
// https://www.arduino.cc/playground/Code/Time
#include <Time.h>
// https://arduino.cc/en/Reference/Wire (아두이노 IDE 기본 포함)
#include <Wire.h>
#include <EEPROM.h>
// 연결:
// LCD (I2C 모듈):
// SCL - A5
// SDA - A4
// VCC
// GND
// 홀 센서 인터럽트 핀
#define HALL_SENSOR_PIN 3
#define BUTTON_BACK_PIN 4
#define BUTTON_UP_PIN 5
#define BUTTON_DOWN_PIN 6
#define BUTTON_SELECT_PIN 7
#define BUTTON_CANCEL1_PIN 8
#define BUTTON_CANCEL2_PIN 9
#define BUTTON_MANUAL_PIN 10
#define LED_CANCEL1_PIN A0
#define LED_CANCEL2_PIN A1
#define LED_SUCCESS1_PIN A2
#define LED_SUCCESS2_PIN A3
// 피더 서보 출력 핀
#define SERVO_OUTPUT_PIN 12
// 홀 센서 입력 핀
#define LED_HALL_SENSOR_PIN 13
// LCD 주소 0x27 또는 0x3F
LiquidCrystal_I2C lcd(0x3F, 16, 2);
// 버튼 라이브러리 설정
// 20ms 디바운스면 택트 스위치에 보통 적합
#define DEBOUNCE_MS 20
// 롱프레스 반복 전 대기 시간
#define REPEAT_FIRST 1000
// 롱프레스 반복 간격
#define REPEAT_INCR 200
// 아두이노 내부 풀업 저항 사용
#define PULLUP true
#define INVERT true
// 버튼 선언
Button buttonSelect(BUTTON_SELECT_PIN, PULLUP, INVERT, DEBOUNCE_MS);
Button buttonUp(BUTTON_UP_PIN, PULLUP, INVERT, DEBOUNCE_MS);
Button buttonDown(BUTTON_DOWN_PIN, PULLUP, INVERT, DEBOUNCE_MS);
Button buttonBack(BUTTON_BACK_PIN, PULLUP, INVERT, DEBOUNCE_MS);
Button buttonCancel1(BUTTON_CANCEL1_PIN, PULLUP, INVERT, DEBOUNCE_MS);
Button buttonCancel2(BUTTON_CANCEL2_PIN, PULLUP, INVERT, DEBOUNCE_MS);
Button buttonManual(BUTTON_MANUAL_PIN, PULLUP, INVERT, DEBOUNCE_MS);
// 조절되는 숫자
int count;
int lastCount = -1;
unsigned long rpt = REPEAT_FIRST;
unsigned long timeoutValue = 0;
boolean manualCancelFeed1 = false;
boolean manualCancelFeed2 = false;
boolean manualFeed = false;
int feedAmount1 = 1;
int feedAmount2 = 1;
bool feederSuccess = false;
int portions = 0;
int turns = 0;
// 입력 액션 열거형
enum {btnSELECT, btnUP, btnDOWN, btnBACK, btnCANCEL1, btnCANCEL2, btnMANUAL, trigTIMEOUT};
// 유한 상태 머신(FSM) 상태
enum STATES {
MAIN, MENU_EDIT_FEEDTIME1, MENU_EDIT_FEEDTIME2, MENU_EDIT_FEEDAMOUNT,
MENU_EDIT_TIME, MENU_EDIT_DATE, MENU_EDIT_SETTINGS,
EDIT_FEED_TIME1_HOUR, EDIT_FEED_TIME1_MINUTE, EDIT_FEED_TIME1_ON_OFF,
EDIT_FEED_TIME2_HOUR, EDIT_FEED_TIME2_MINUTE, EDIT_FEED_TIME2_ON_OFF,
EDIT_FEED_AMOUNT1, EDIT_FEED_AMOUNT2,
EDIT_HOUR, EDIT_MINUTE, EDIT_DAY, EDIT_MONTH, EDIT_YEAR,
EDIT_SERVO_STOP_DELAY, EDIT_SERVO_BACKUP_DELAY
};
STATES state;
int8_t userInput;
int8_t trigger;
int Second, Minute, Hour, Day, Month, Year;
int8_t DoW;
String day_of_week;
unsigned char address, data;
int testt = 0;
int feed_time1_hour, feed_time1_minute;
bool feed_time1_active = false, alarm1Activated = false;
int feed_time2_hour, feed_time2_minute;
bool feed_time2_active = false, alarm2Activated = false;
// 블링크 함수용
uint32_t blink_interval = 500, blink_previousMillis = 0, blink_currentMillis = 0;
boolean blink_state = false;
// 스피닝 휠 표시용
uint32_t spinningWheel_interval = 170, spinningWheel_previousMillis = 0, spinningWheel_currentMillis = 0;
int spinningWheelSymbol = 0;
// 홀 센서 고장 백업용
uint32_t hallSensorBackup_interval = HALL_SENSOR_BACKUP_VALUE;
uint32_t hallSensorBackup_currentMillis = 0;
boolean hallSensorFail = false;
// LCD 백라이트 타이머용
uint32_t lcdBacklight_interval = LCD_BACKLIGHT_ON_TIME;
uint32_t lcdBacklight_currentMillis = 0;
boolean RTC_error = true;
boolean long_press_button = false;
// LCD 커스텀 심볼 정의
byte bell_symbol_Char[8] = { B00100, B01110, B01110, B01110, B11111, B00100, B00000, B00000 };
byte inverted_one_Char[8] = { 0b11111, 0b11011, 0b10011, 0b11011, 0b11011, 0b11011, 0b10001, 0b11111 };
byte inverted_two_Char[8] = { 0b11111, 0b11011, 0b10101, 0b11101, 0b11011, 0b10111, 0b10001, 0b11111 };
byte arrow_up_Char[8] = { 0b00100, 0b01110, 0b11111, 0b01110, 0b01110, 0b01110, 0b01110, 0b00000 };
byte arrow_down_Char[8] = { 0b00000, 0b01110, 0b01110, 0b01110, 0b01110, 0b11111, 0b01110, 0b00100 };
byte inverted_p_Char[8] = { 0b11111, 0b10001, 0b10101, 0b10001, 0b10111, 0b10111, 0b11111, 0b00000 };
byte backslash_Char[8] = { 0b00000, 0b10000, 0b01000, 0b00100, 0b00010, 0b00001, 0b00000, 0b00000 };
byte thickDash_Char[8] = { 0b00000, 0b00000, 0b11111, 0b11111, 0b11111, 0b00000, 0b00000, 0b00000 };
volatile boolean hallSensorActivated = false;
// 인터럽트 1: 홀 센서
void HallSensorIsr() {
hallSensorActivated = true;
digitalWrite(LED_HALL_SENSOR_PIN, HIGH);
}
// Atom 에디터 + PlatformIO 애드온으로 편집해서 C++ 파일로 만들기 위한 선언
void HallSensorIsr(); void setup(); void loop(); void change_states(); void check_inputs();
void transition(int trigger); void check_alarm(); void check_manual_feed();
void display_menu_option_set_feedtime1(); void display_menu_option_set_feedtime2();
void display_menu_option_set_feed_amount(); void display_menu_option_set_time();
void display_menu_option_set_date(); void midnight_reset(); void display_time();
void displayFeedingAmouts(); void displayFeedingTimes(); void set_feedAmount(); void set_time();
void set_date(); void set_feeding1_time(); void set_feeding2_time(); void get_time(); void get_date();
void write_time(); void write_date(); void write_feeding_time1(); void write_feeding_time2();
void write_feedamount(); void get_feedamount(); void get_feed_time1(); void get_feed_time2();
void check_RTC(); byte decToBcd(byte val); byte bcdToDec(byte val); void leading_zero(int digits);
void blinkFunction(); void displaySpinningWheel(); void startFeederServo(); void stopFeederServo();
void activateFeeder(int portions); void check_LcdBacklight(); void lcd_backlight_ON();
void ledsAndLcdDisplayStartup(); void hallSensorCheck();
void setup() {
lcd.begin();
lcd_backlight_ON();
Wire.begin();
pinMode(HALL_SENSOR_PIN, INPUT_PULLUP);
pinMode(LED_HALL_SENSOR_PIN, OUTPUT);
pinMode(SERVO_OUTPUT_PIN, OUTPUT);
pinMode(LED_CANCEL1_PIN, OUTPUT);
pinMode(LED_CANCEL2_PIN, OUTPUT);
pinMode(LED_SUCCESS1_PIN, OUTPUT);
pinMode(LED_SUCCESS2_PIN, OUTPUT);
digitalWrite(LED_CANCEL1_PIN, LOW);
digitalWrite(LED_CANCEL2_PIN, LOW);
digitalWrite(LED_SUCCESS1_PIN, LOW);
digitalWrite(LED_SUCCESS2_PIN, LOW);
digitalWrite(LED_HALL_SENSOR_PIN, LOW);
lcd.createChar(0, thickDash_Char);
lcd.createChar(1, bell_symbol_Char);
lcd.createChar(2, backslash_Char);
lcd.createChar(3, inverted_p_Char);
lcd.createChar(4, inverted_one_Char);
lcd.createChar(5, inverted_two_Char);
lcd.createChar(6, arrow_up_Char);
lcd.createChar(7, arrow_down_Char);
stopFeederServo();
Wire.begin();
setSyncProvider(RTC.get);
setSyncInterval(60);
RTC.squareWave(SQWAVE_NONE);
attachInterrupt(INT1, HallSensorIsr, FALLING);
ledsAndLcdDisplayStartup();
state = MAIN;
get_feed_time1();
get_feed_time2();
}
void loop() {
change_states();
check_inputs();
check_alarm();
check_manual_feed();
midnight_reset();
display_time();
displayFeedingAmouts();
displayFeedingTimes();
set_feedAmount();
set_time();
set_date();
set_feeding1_time();
set_feeding2_time();
get_time();
get_date();
write_time();
write_date();
write_feeding_time1();
write_feeding_time2();
write_feedamount();
get_feedamount();
get_feed_time1();
get_feed_time2();
check_RTC();
// 이하 생략... 전체 코드는 다운로드 필요
}
// 이 파일은 잘렸습니다. 전체 내용은 다운로드하세요.
회로도
아두이노 기반 펫 피더
아두이노 IDE 1.8.2 사용. 라이브러리 폴더에 같은 라이브러리의 두 버전이 있지 않도록 주의하세요!!