반응형

제목이 좀 거창하지만 심플하게 ATtiny85 MCU 칩과 서보모터를 이용해서 지금 키우고 있는 금붕어의 사료를 자동으로 줄 수 있도록 장치를 만들어 보았다. 당분간은 그럴 일이 없지만 몇 일 동안 집을 비우게 되면 이 장치가 작동해서 금붕어 들의 밥을 챙겨 줄 수 있을 것이다. 금붕어는 현재 4마리를 키우고 있으며 하루에 두 번 12시간 간격으로 12알 정도의 사료를 주고 있다. 이를 자동화 하는 것이 목표이다. 


하지만 정확히 12알을 주는 것은 아래의 방법으로는 불가능하고 12알씩 따로 컨테이너에 담아두고 회전하면서 구멍으로 떨어뜨리는 장치를 만들어야 하는데 좀 복잡해진다. 뭐 금붕어가 10알 떨어진다고 불평하진 않을테니... 아무튼 그래서 이런 저런 생각 끝에 복잡하게 하지 않고 유튜브 동영상을 참고하여 비슷하게 만들어 보았다.


예전의 글에서 말했듯이 이런 간단한 장치에 아두이노를 사용하는 것은 어떻게 보면 낭비(?)일 것이다. 그런 의미에서 ATtiny 라는 싸고 저렴한 칩 하나를 이용해서 기능을 구현한다는 것이 의미가 있다고 생각한다.


사료통으로 집안에 굴러다니는 조그만 플라스틱 약병을 이용하였다. 서보모터와 붙이기 위해 약병 뚜껑의 꼭지 부분을 잘랐다.


글루건으로 서보모터 브라켓하고 약병 뚜껑을 접착해 주었다.


그리고 약통에 사료가 나올 수 있게 구멍을 뚫어주고 약통뚜껑과 접착된 브라켓을 서보모터에 끼워주면 된다. 이러면 사료주는 장치는 다 만든거나 다름이 없다. 엄청 심플하다.


그리고 이제 ATtiny85 칩에 프로그래밍을 하면 된다. 예전의 글에서도 말 했듯이 아두이노의 기본 Servo 라이브러리는 ATtiny 에서 작동하지 않았다. 그래서 SoftwareServo 라는 라이브러리를 이용하였다. 그리고 코딩도 아두이노의 그것과는 미묘하게 다른 면이 있다.


회로의 연결은 아래와 같다. ATtiny 서보모터 제어 글에서 연결했던 것과 같다. 참고로 서보제어 단자는 ATtiny85의 PB1 단자에 연결하였다.




■ 소스

※ ATtiny 프로그래밍 환경 참고 : http://deneb21.tistory.com/455http://deneb21.tistory.com/462

※ ATtiny 서보모터 제어 & 라이브러리 참고 : http://deneb21.tistory.com/463

SoftwareServo.zip

※ 소스 참고 : http://natemcbean.com/bin/FISHFEEDEERMAXIMUMEFFECTIVENESS.pde


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#include <SoftwareServo.h> 
 
SoftwareServo myservo;  // create servo object to control a servo 
 
int pos = 0;    // variable to store the servo position 
 
long FEEDINGDELAY = 43200000// 12 hours
//long FEEDINGDELAY = 57600000; // 16 hours
//long FEEDINGDELAY = 60000;  //for Test
 
long endtime; 
long now;
 
void setup() 
  myservo.attach(1);  // attaches the servo on pin 1 to the servo object 
  myservo.write(0);
  delay(15);
  
  
  // FEED THE FISH..
  for(pos = 0; pos < 180; pos += 1)  // goes from 0 degrees to 180 degrees 
  {                                  // in steps of 1 degree 
    myservo.write(pos);              // tell servo to go to position in variable 'pos' 
    SoftwareServo::refresh();        // must call at least once every 50ms or so to keep your servos updating
    delay(15);                       // waits 15ms for the servo to reach the position 
  } 
  for(pos = 180; pos>=1; pos-=1)     // goes from 180 degrees to 0 degrees 
  {                                
    myservo.write(pos);              // tell servo to go to position in variable 'pos' 
    SoftwareServo::refresh();
    delay(15);                       // waits 15ms for the servo to reach the position 
  } 
 
 
void loop() 
  // process times
  now = millis();
  endtime = now + FEEDINGDELAY;
  
  while(now < endtime) {
   myservo.write(0);
   delay(20000);
   now = millis();   
  }
  
  // FEED THE FISH..
  for(pos = 0; pos < 180; pos += 1)  // goes from 0 degrees to 180 degrees 
  {                                  // in steps of 1 degree 
    myservo.write(pos);              // tell servo to go to position in variable 'pos' 
    SoftwareServo::refresh();        // must call at least once every 50ms or so to keep your servos updating
    delay(15);                       // waits 15ms for the servo to reach the position 
  } 
  for(pos = 180; pos>=1; pos-=1)     // goes from 180 degrees to 0 degrees 
  {                                
    myservo.write(pos);              // tell servo to go to position in variable 'pos' 
    SoftwareServo::refresh();
    delay(15);                       // waits 15ms for the servo to reach the position 
  } 
}
cs


소스는 위와 같다. 자신이 원하는 시간을 FEEDINGDELAY 변수에 적용하면 된다. 나의 경우는 12시간 간격으로 줄 것이기 때문에 위와 같이 선택했다. 일단 칩에 전원이 공급되면 무조건 서보가 한 번 움직여서 사료를 주게 된다. 그 후 loop() 의 while 문에서 설정한 시간만큼 지연시키게 되고 시간이 경과하면 while문을 빠져나오게 되고 for 문이 작동 되면서 서보가 움직이게 되고 사료를 주게 된다. 이것이 무한반복 된다.


어느 정도 각도에서 내가 원하는 만큼의 사료가 나오는지 테스트를 많이 해 보아야 한다. 정확히는 맞지 않더라도 근사치의 사료양이 공급되어야 어항도 깨끗하게 유지되고 금붕어도 굶거나 너무 과식하는 현상이 없어질 것이다.


어항 언저리에 서보모터를 양면테이프로 붙여서 테스트 해 보았다. 적당량보다 조금 많은 양의 사료가 나왔다. 이럴 경우 약병을 약간 돌려서 구멍 있는 쪽이 조금 덜 기울어지게 하면 밑으로 떨어지는 사료가 줄어든다. 제일 알맞은 각도가 나오면 나중에도 그 각도를 맞출 수 있도록 네임펜으로 표시를 해 주었다.


최종적으로 미니 브레드보드에 회로를 구성하고 AA건전지 4개를 이용해서 6V 전원을 공급해 주었다. ATtiny 데이터시트에 보면 5.5V 까지가 권장 공급전압인데 6V 를 인가해 주어도 이상 없이 동작한다.


이제 잘 작동하는지 관찰하면 되겠다. 

이로써 집을 비울 경우 물고기 밥주는 문제는 해결되었다고 생각한다. 단, 개선의 여지는 많이 있어 보인다.



※ 추가내용 (2016.12.17) : 몇 일을 관찰해 보니 아주 잘 작동하지만 현재 작동하고 있는지 아닌지 확인할 방법이 없어서 1분 마다 깜빡이도록 LED 를 하나 추가로 달아주었다. millis() 를 이용한 기존의 소스를 delay() 로 바꿔주고 전원을 기존의 배터리에서 안 쓰는 핸드폰 어댑터 5V, 1A 짜리로 교체해 주었다.


ATtiny_Fish_Feeder.fzz


■ 변경된 소스
- delay() 를 이용하여 12시간(720분) 간격으로 서보가 동작하여 물고기 밥을 주도록 변경하였다. 
- PB0 에 LED 하나를 추가 하여 1분 마다 깜빡이도록 수정 하였다.
- 향후 개선 아이디어 : 먹이 주는 시간에 주변이 어두울 경우 금붕어가 먹이를 잘 먹을 수 있도록 LED 가 10분 정도 수족관을 밝혀주는 것도 괜찮을 것 같다. (CDS 센서, 고휘도 LED 필요) , 먹이 주는 시간이 되면 알람이 울리게 하는 것도 괜찮을 듯 하다. 금붕어의 파블로프의 개化? (피에조 부저 필요)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include <SoftwareServo.h> 
 
SoftwareServo myservo;  // create servo object to control a servo 
 
int pos = 0;    // variable to store the servo position 
int term = 720// 12hours = 43200sec = 720minutes
int led = 0;  // LED Pin (PB0)
 
void setup(){
  pinMode(led, OUTPUT);
  myservo.attach(1);  // attaches the servo on PB1 to the servo object 
  myservo.write(0);
  delay(15);
  feedFish();
}
 
void loop(){
    for(unsigned int i=0;i<=term;i++)
    {
      delay(59000);  // 59 sec
      digitalWrite(led,HIGH);
      delay(1000);  // 1 sec , 59+1 = 1 minute
      digitalWrite(led,LOW);
    }
    feedFish();
}
 
// FEED THE FISH..
void feedFish()
{
  for(pos = 0; pos < 180; pos += 1)  // goes from 0 degrees to 180 degrees 
  {                                  // in steps of 1 degree 
    myservo.write(pos);              // tell servo to go to position in variable 'pos' 
    SoftwareServo::refresh();        // must call at least once every 50ms or so to keep your servos updating
    delay(15);                       // waits 15ms for the servo to reach the position 
  } 
  for(pos = 180; pos>=1; pos-=1)     // goes from 180 degrees to 0 degrees 
  {                                
    myservo.write(pos);              // tell servo to go to position in variable 'pos' 
    SoftwareServo::refresh();
    delay(15);                       // waits 15ms for the servo to reach the position 
  } 
}
cs






※ 추가내용 (2016.12.18) : 어떤 이유인지 확실히는 모르겠는데 새로 업로드한 소스에서 12시간에 6분 정도가 빨리 흐른다. 아마도 ATtiny 에 내장된 타이머가 정밀하지 않은 탓은 아닐까? 하는데 term 변수인 720분에 6분을 더해서 오차를 보정해 주었다.


1
2
3
//int term = 720; 
int term = 726;
 
cs




※ 추가내용 (2016.12.19) : 먹이를 주기전 Beep 음이 3번 울리도록 피에조 스피커를 추가하고 소스를 변경해 주었다. Beep 음은 아두이노에 있는 tone() 함수를 이용했는데 이 함수는 순수한 생짜의 ATtiny 에서는 지원되지 않는다. 사용하려면 http://deneb21.tistory.com/455 의 맨 아래 추가내용에 관련 내용(ATtiny Core 적용)을 적어 놓았다.  참고로 tone() 사용 시 음의 주파수를 500을 주었더니 소리가 제대로 나지 않았다. 아래는 이제까지 수정사항을 반영한 연결과 소스이다.


ATtiny_Fish_Feeder2.fzz



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include <SoftwareServo.h> 
 
SoftwareServo myservo;  // create servo object to control a servo 
 
int pos = 0;    // variable to store the servo position 
//int term = 720; // 12hours = 43200sec = 720minutes
int term = 726// 12hours = 43200sec = 720minutes
int led = 0;  // LED Pin (PB0)
int speaker = 2//speaker Pin (PB2)
 
void setup(){
  pinMode(led, OUTPUT);
  myservo.attach(1);  // attaches the servo on PB1 to the servo object 
  myservo.write(0);
  delay(15);
  feedFish();
}
 
void loop(){
    for(unsigned int i=0;i<=term;i++)
    {
      delay(59000);  // 59 sec
      digitalWrite(led,HIGH);
      delay(1000);  // 1 sec , 59+1 = 1 minute
      digitalWrite(led,LOW);
    }
    feedFish();
}
 
// FEED THE FISH..
void feedFish()
{
  //feeding alarm
  for(unsigned int i=0;i<3;i++)
  {
    tone(speaker,2000,1000);
    delay(2000);
  }
  
  //feeding (servo move)
  for(pos = 0; pos < 180; pos += 1)  // goes from 0 degrees to 180 degrees 
  {                                  // in steps of 1 degree 
    myservo.write(pos);              // tell servo to go to position in variable 'pos' 
    SoftwareServo::refresh();        // must call at least once every 50ms or so to keep your servos updating
    delay(15);                       // waits 15ms for the servo to reach the position 
  } 
  for(pos = 180; pos>=1; pos-=1)     // goes from 180 degrees to 0 degrees 
  {                                
    myservo.write(pos);              // tell servo to go to position in variable 'pos' 
    SoftwareServo::refresh();
    delay(15);                       // waits 15ms for the servo to reach the position 
  } 
}
cs


반응형

+ Recent posts