[아두이노] 아두이노 논문 검색

IOT/아두이노|2019. 8. 15. 09:00

[아두이노] 아두이노 논문 검색 



오늘은 아두이노 관련 논문을 검색하는 방법을 살펴보도록 하죠. 국내 아두이노를 이용한 연구 자료들을 쉽게 찾을 수 있고 깊게 공부하고 싶은 분들에게 이용 할 만한 사이트으로 RISS 통합 검색과 국회전자도서관을 소개합니다. 무료로 논문 자료들을 공개되어 있어서 온라인상에서 쉽게 자료를 다운 받을 수 있습니다. 구지 학교도서관이나 근처 지역도서관에 방문하셔서 논문을 찾을 필요 없이 인터넷에서 쉽게 논문 자료를 수집할 수 있습니다. 대학생들이라면 레포트 쓸 때 참고자료용으로 많이 이용하시고 계시겠죠. 그리고 꼭 아두이노가 아니더라도 다른 학문 분야에 관심 있는 분들도 한번쯤은 위 논문 사이트를 이용해 보셨을 거라 생각됩니다.

왜! 논문 검색을 소개 할까요. 논문은 좀 더 전문적인 지식을 습득하기에 많은 도움을 줍니다. 그 뿐 아니라 현재의 국내 아두이노의 흐름을 이해하기에 무척 좋은 자료가 되기 때문에 여러분들에게 상상과 응용의 필요한 지식을 제공해 줍니다.

그러면 간단히 어떻게 논문을 검색하는지 살펴보도록 하겠습니다.

1. RISS 통합 검색 사이트



위 링크된 사이트에 가시면 아래와 같은 창이 뜨고 검색어는 "아두이노"만 치시면 됩니다.


관련 자료는 단어 형태로 검색하시면 아두이노 뿐 아니라 다른 분야의 논문들을 쉽게 찾을 수 있습니다.

아두이노라는 단어로 현재 학위논문이 131편이고 국내학술지논문 521편이네요.


무료 or 유료 or 기관내 무료 형태로 논문을 제공하는데 무료로 많이 제공하니깐 한번씩 읽어보시는 것도 재미 있습니다. 현재 국내 학생과 연구자들이 어떤 창의적인 아이템으로 논문을 섰는지 한눈으로 확인할 수 있고 많은 참고 지식을 얻을 수 있기 때문에 읽어보시면 지식적 측면에서 꽤! 아두이노에 대한 시야가 넓어질거라 생각 됩니다.

실제로 특정 논문을 클릭하시면 기본 정보를 제공하고 원문보기를 누르시면 보시는 것처럼 확인이 가능합니다.


경우에 따라 로그인이 필요한 경우에는 구글ID로 로그인하시면 됩니다.

2. 국회전자도서관



위 링크된 사이트에 가시면 아래와 같은 창이 뜨고 검색어는 "아두이노"만 치시면 됩니다.


여기서 "아두이노" 검색어로 치면 아래와 같은 창이 뜹니다.


여기서, 학위논문과 학술지 자료가 있네요. 특정 논문 자료를 선택 해봤네요.


위 그림처럼 특정 논문 하나를 선택했네요. 아래 "원문보기"를 누르면 국회전자도서관은 논문을 볼 수 있게 프로그램을 설치해야 합니다. 이게 좀 불편하네요. 논문 내용은 보기 편하게 되어 있는데 그걸 하기 위해서는 프로그램을 설치해야 한다는 불편함이 있습니다.

마무리


자주 사용하는 사이트는 RISS 통합 검색 사이트를 이용 합니다. 여러분들도 시간이 날때 마다 한번 아두이노를 가지고 어떤 실험들을 국내에서 하고 있는지 읽어 보셨으면 합니다. 아두이노 시야가 한단계 더 업그레이드 될 거라 생각됩니다.

댓글()

[아두이노] 시각장애인을 위한 스마트 지팡이 원리 이해

IOT/아두이노|2019. 8. 14. 11:11

[아두이노] 시각장애인을 위한 스마트 지팡이 원리 이해



지난 시간에 진동모터에 대해 살펴보았습니다. 진동모터는 촉각을 느끼는 출력 부품입니다. 즉, 어떤 상황이 발생했을 때 그 상황에 대해 진동으로 인간에게 알릴 수 있습니다. 일상에서 진동모터는 어디에서 사용 할까요. 바로 스마트폰의 진동모드입니다. 스마트폰의 진동으로 전화나 문자가 왔을을 쉽게 알 수 있습니다. 또다른 응용사례가 뭐가 있을까요. 구글 검색을 통해 살펴보니 시각장애인을 위한 스마트 지팡이가 있더군요. 이 주제를 한번 가상시뮬레이터로 테스트 해보고 싶어지더군요. 참고로 제가 새로운 뭔가를 하루만에 창조하고 실험까지 하기는 시간적 여유가 없기 때문에 기존에 구현한 주제를 가지고 한번 가상시뮬레이터로 모방 실험을 통해 진동모터를 이해하는 시간을 갖고자 합니다.

1. 진동모터의 응용 사례 구글 검색



진동모터에 대한 응용 예제로 위 출처에 가시면 각 대학교 학생들이 만든 공모전 작품들에 대한 소개가 잘 나와 있습니다. 자세한 내용과 코딩은 읽지 않았습니다. 그 이유는 상상코딩에 방해가 되기 때문에 저만의 코딩을 할 수 없을 것 같아서 어떻게 스마트 지팡이를 구성했는지만 간단히 이미지를 참조하여 가상시뮬레이터에서 표현가능한 부품만을 이용하여 재구성해 보았습니다.

쉽게말해서, 이미지만 보고 한번 모방 표현을 해 보았는데 어떻게 했는지 본격적으로 살펴 볼까요. 참고로 스마트지팡이의 이미지는 출처에 가셔서 직접 보시기 바랍니다.

2. 스마트 지팡이 모방 재구성 이미지



  • 조도센서 : 보행 주변 조명 상태 확인
  • LED : 어두울 때 조명을 켜서 보행자을 알림등으로 활용
  • 초음파센서 : 3개의 초음파 센서로 벽감지 초음파센서는 전방에 벽을 확인하고 보행 초음파센서는 보행자의 보폭에 따른 장애- 물을 감지하고 바닥 초음파센서는 계단과 같은 하단의 장애물을 감지한다.
  • 진동모터 : 초음파센서를 통해 장애물이 감지되면 진동으로 장애물 감지를 알림

3. 스마트 지팡이 회로도


  • 준비물 : 초음파센서 3개, 조도센서 1개, 1k옴 2개, 220옴 1개, 진동모터 1개, 아두이노우노
  • 내용 : 진동모터는 3번핀, 초음파센서는 5,6,7번핀, LED은 13번핀, 조도센서 A0핀에 연결하시오.

구글 검색을 통해 재해석한 표현입니다. 실험에 사용한 주제는 원작은 위 링크 출처의 자료의 이미지를 모방한 실험으로 제작품은 아닙니다. 가상시뮬레이터에서 원리를 실험하는게 목적인 post 입니다.


상단 초음파센서는 벽(장애물) 감지이고 중앙 초음파센서는 보행(장애물) 감지이고 하단 초음파센서는 바닥(장애물) 감지합니다. 장애물이 감지되면 진동모터가 작동하여 진동으로 보행자에게 알린다. 조도센서는 스마트지팡이의 조명을 담당하면 주변이 어두울 때 지팡이에 조명이 켜지고 보행자을 식별할 수 있게 한다.

2. 코딩


설계 :

  • 조도센서를 통해서 LED 조명을 제어한다.
  • 3개의 초음파센서를 통해 3가지 장애물 진동 패턴을 다르게 한다.

먼저 조도센서를 통해 LED 조명을 제어해 볼까요.

조도센서를 읽기

int cds=analogRead(CDSPin);  

조도센서의 값이 100(임의값)미만이면 LED(조명)가 켜지고 100이상이면 LED(조명)가 꺼진다.

if(어두운지) 조명 켜기;
else 조명끄기;

위 if문을 코딩화 하면 다음과 같습니다.

if(cds<100) digitalWrite(LedPin,HIGH);   
else digitalWrite(LedPin,LOW);  

이제는 3개의 초음파센서를 통해 3가지 장애물 감지와 진동 패턴을 출력 해 볼까요.

가상시뮬레이터의 초음파센서는 3핀입니다. newPing 라이브러리를 이용하면 편한데 가상시뮬레이터에서 제공되지 않고 쓸려면 라이브러리 파일 안에 코딩을 전부 가상시뮬레이터로 복사해야 하기 때문에 수작업 코딩으로 실험했네요.

초음파 센서 읽기(사용자함수로 표현)

float UltrasonicDistance(int m_pin){
  pinMode(m_pin,OUTPUT); 
  digitalWrite(m_pin, LOW); 
  delayMicroseconds(2); 
  digitalWrite(m_pin,HIGH); 
  delayMicroseconds(10); 
  digitalWrite(m_pin,LOW); 
  
  pinMode(m_pin,INPUT);    
  float duration = pulseIn(m_pin, HIGH);  
  return duration / 57.5;  
}

3개의 초음파센서이니깐 위 사용자정의함수로 표현하면 loop()함수 안에서는 다음과 같이 3줄로 초음파센서 값을 읽으면 됩니다.

float v1=UltrasonicDistance(5);
float v2=UltrasonicDistance(6);
float v3=UltrasonicDistance(7);

각 초음파센서의 값을 읽었으면 진동을 출력해야 겠죠. 출력하기 전에 출력한 진동 패턴은 다음과 같이 설정 해보았습니다.

  if(v3<20){
    if(state == false){ //처음 장애물 감지때만 진입
          하단 초음파 초기상태;
    }    
    timeState = 200;  //진동 간격
  }
  else if(v2<30){
    if(state == false){      
      중앙 초음파 초기상태;
    }
    timeState = 500; //진동 간격
  }                    
  else if(v1<50) {
    if(state == false){      
      상단 초음파 초기상태;
    }
    timeState = 1000; //진동간격
  }
  else { //장애물 감지 안되면 초기화 시킴
    state = false;  
    digitalWrite(VibrationPin,LOW); 
  } 

위에서 초기상태는 장애물 감지 된 후의 동작이니깐 state 출력 상태로 바꾸고 초기 진동상태값은 true로 하고 이전시간값을 진동 간격만큼 처음 빼줍니다. 그 이유는 처음에는 무조건 진동을 먼저 시작하고 반복되게 하기 위해서 입니다. 참고로, Setup()함수에서는 강제적으로 delay(1000)하면 음수에 대한 오류는 발생하지 않습니다.

    if(state == false){ //처음 장애물 감지때만 진입
      state = true; //진동 출력 상태
      VibrationState=true; //진동모터의 상태
      timeVal = millis()-200; //진동이전값(처음에 무조건 진동해야하기 때문 -200을 함)
    }    

위 내용을 합치면,

  if(v3<20){
    if(state == false){ //처음 장애물 감지때만 진입
      state = true; //진동 출력 상태
      VibrationState=true; //진동모터의 상태
      timeVal = millis()-200; //진동이전값
    }    
    timeState = 200;  //진동 간격
  }
  else if(v2<30){
    if(state == false){      
      state = true;   
      VibrationState=true;
      timeVal = millis()-500;
    }
    timeState = 500;
  }                    
  else if(v1<50) {
    if(state == false){      
      state = true;   
      VibrationState=true;
      timeVal = millis()-1000;
    }
    timeState = 1000;    
  }
  else { 
    state = false;  
    digitalWrite(VibrationPin,LOW); 
  } 

이제 출력 상태를 지정을 했으니 출력을 해볼까요.

  if(state == true){ //출력 상태 확인
    if(millis()-timeVal>=timeState){ //출력 간격 체크
      digitalWrite(VibrationPin,VibrationState); //진동 출력
      VibrationState=!VibrationState; //진동출력상태 반전
      timeVal=millis(); //이전시간값
    }   
  } 

위와 같이 코딩함으로 진동을 울린건지 state값을 통해서 진동을 울리게 됩니다. 진동 울리는 간격은 millis()함수를 이용하여 딜레이함수 없이 딜레이 효과를 주었습니다. 왜! 이 원리를 이용했냐면 스마트지팡이에는 진동모터만 있는게 아닙니다. 다른 부품들도 실시간으로 동작을 처리해야 합니다. 그렇기 때문에 millis()함수를 이용하여 delay()효과를 주었네요. 이렇게 표현하면 조도센서에 대한 LED(조명) 변화는 동시에 처리할 수 있습니다.

종합해보면,

int timeState = 0;
boolean state = false;
boolean VibrationState = true;
unsigned long timeVal = 0;


const byte LedPin = 13;
const byte VibrationPin = 3;
const byte CDSPin = A0;

void setup(){
  
  //Serial.begin(9600);  
  pinMode(LedPin,OUTPUT);
  pinMode(VibrationPin,OUTPUT);  
  
  delay(1000);
}
void loop(){
  int cds=analogRead(CDSPin);  
  
  if(cds<100) digitalWrite(LedPin,HIGH);   
  else digitalWrite(LedPin,LOW);  
  
  
  float v1=UltrasonicDistance(5);
  float v2=UltrasonicDistance(6);
  float v3=UltrasonicDistance(7);
  
  
  if(v3<20){
    if(state == false){      
      state = true;
      VibrationState=true;
      timeVal = millis()-200;
    }    
    timeState = 200;  
  }
  else if(v2<30){
    if(state == false){      
      state = true;   
      VibrationState=true;
      timeVal = millis()-500;
    }
    timeState = 500;
  }                    
  else if(v1<50) {
    if(state == false){      
      state = true;   
      VibrationState=true;
      timeVal = millis()-1000;
    }
    timeState = 1000;    
  }
  else { 
    state = false;  
    digitalWrite(VibrationPin,LOW); 
  } 
  
  if(state == true){
    if(millis()-timeVal>=timeState){
      digitalWrite(VibrationPin,VibrationState);      
      VibrationState=!VibrationState;
      timeVal=millis();
    }   
  } 
 }
float UltrasonicDistance(int m_pin){
  pinMode(m_pin,OUTPUT); 
  digitalWrite(m_pin, LOW); 
  delayMicroseconds(2); 
  digitalWrite(m_pin,HIGH); 
  delayMicroseconds(10); 
  digitalWrite(m_pin,LOW); 
  
  pinMode(m_pin,INPUT);    
  float duration = pulseIn(m_pin, HIGH);  
  return duration / 57.5;  
}

4. 결과


아래 결과 영상만 보면 좀 이해가 안 될 수 있습니다. 위에 지팡이 이미지를 보고시고 사용된 부품의 위치와 가상시뮬레이터의 부품과 일치시켜서 어느정도 상상을 하면서 보셔야 이해가 될 듯 싶네요.


5. 문제점



문제점은 위 그림에서 보는 것 처럼 보행을 할 때 세부분으로 나눠서 3꼭지점을 지면 바닥에 닿고 나머지 부분은 h(높이)로 반원의 곡선을 그리면서 지팡이를 움직이게 됩니다. 지면에는 딱 세번의 위치를 찍을 뿐 지팡이는 대부분 공중에 떠있게 됩니다. 그래서 지면과의 거리를 측정하는 센서가 추가하여 지면에서 얼마만큼 높이이고 장애물과의 거리가 정확히 위치를 잡아야 하는데 이 부분은 가상시뮬레이터에서 생략했습니다. 그래서 실제 구현한다면 스마트 지팡이의 곡선 움직으로 인한 장애물 측정의 약간 문제가 생길 수 있습니다. 즉, 위 코딩대로 라면 하단 초음파 센서가 곡선을 그릴 때 중간 장애물을 하닥 근접 장애물로 인식한 진동음으로 울릴 수 있으며 지팡이가 포물선으로 움직이기 때문에 각도의 변화가 일어나기 때문에 그 각도에 대한 계산 부분이 코딩에 담겨져 있지 않습니다. 그래서 정확한 측정은 좀 더 코딩을 수학적 계산 부분이 필요합니다. 위 코딩은 단순한 가상시뮬레이터에서의 스마트 지팡이의 단순 원리를 테스트 하기 위한 내용이니 실제 구현시에는 여러가지를 고려하야 정확한 장애물 위치에 대한 진동을 만드셔야 합니다.

원래는 초음파센서로만 스마트지팡이를 구성하고 장애물을 감지하는데에는 한계가 있습니다. 사실 초음파센서보다는 진짜 제대로 된 스마트 지팡이를 구현한다면 차라리 영상처리를 이용한 장애물 감지 기술을 공부하여 구현해보는 것이 좋습니다. 자율주행차에 대해 요즘 관심이 많고 대학들이 그와 관련된 연구를 하는 곳들이 많습니다. 이런 연구하는 대학에서 영상처리를 공부하는 학생들이 자율주행차보다 자율보행지팡이 쪽으로 연구가 이루어진다면 진짜 제대로 된 스마트 지팡이가 만들어 질거라 생각됩니다.

주변사물을 영상인식으로 통해 그 정보를 소리와 진동으로 알려준다면 제대로 된 보행을 할 수 있을 거라 생각됩니다. 자율주행차와 같은 원리를 자율보행안내 지팡이 연구를 한다면 그 기술은 자율주행차에도 응용 가능하기 때문에 연구의 가치가 있습니다.

마무리


오늘은 진동모터의 응용 사례를 구글 검색으로 통해서 장애인을 위한 스마트 지팡이이라는 아이템을 찾아 가상시뮬레이터에서 간단히 테스트를 해보았습니다. 이미지를 보고 바로 코딩을 하다보니깐 코딩이 별로 마음에 안들고 좀 길어서 보기 안좋습니다. 이미지를 응용하고 코딩을 만드는데 2시간정도 시간이 걸렸네요. 저녁에 실험하다보니 이번 post를 좀 연기할 까 했는데 그래도 완성은 시켰네요. 완성해놓고 보면 별거 없는데 몇가지 상상했던 코딩들 중에 하나를 선택해서 제가 의도한 방향으로 코딩을 하다보니 좀 시간이 길어져 버렸습니다. 하루만에 완성하려고 하니 볼품없는 post가 되었네요. 실제 제작을 해도 원하는 결과는 좀 얻기 힘들거라 생각됩니다. 대충 어떤 의미로 동작하는지 그 의미만 가상시뮬레이터로 실험한 post라고 이해 하셨으면 합니다.

다른 응용사례를 찾지 못했는데 혹시 찾으신다면 여러분들도 저처럼 이렇게 모방 실험을 한번 해보세요. 모방이라도 코딩을 보지 않고 이미지만 보고 응용하여 의미를 이해하고 자신의 상상으로 재해석해서 회로도를 만들어 보세요. 그리고 재해석된 회로도의 표현들을 자신의 상상력을 동원하여 코딩해 보세요. 위 코딩은 초벌 코딩으로 간단히 원리를 실험한 코딩이지만 위 코딩에서 좀 더 개선하면 좋은 코딩이 될거라 생각됩니다. 여러분들은 위 코딩을 그대로 하지 말고 원리만 이해하시고 여러분들 스스로가 여러분만의 코딩으로 재해석하여 코딩을 만들어 보셨으면 합니다.


댓글()

[아두이노] 진동센서(Vibration Sensor) 제어

IOT/아두이노|2019. 8. 13. 09:00

[아두이노] 진동센서(Vibration Sensor) 제어



지난시간에 다룬 진동모터는 진동을 외부로 출력하는 부품인데 반대로 진동을 감지하여 내부로 입력을 받는 진동센서 부품이 있습니다. 진동에 대한 전기 신호를 만들어 내는데 디지털 신호와 아날로그 신호로 만들어 냅니다. 아무튼 진동을 감지 할수 있는 이 센서는 참 재밌는 부품입니다. 만약에 이 진동센서를 특정위치에 부착하면 그 대상물에 진동이 발생했을 때 진동센서로 진동을 감지하여 전기 신호로 만들어 낼 수 있습니다. 가령 대상물에 누가 건들거나 또는 대상물 주변에 진동이 발생하는 상황에서 아두이노에서 그에 대한 특정한 명령을 내릴 수 있습니다. 즉, 진동 감지 원리를 이용하여 도난방지나 침입 방지를 위한 경보기와 같은 곳에서 응용할 수 있습니다. 예를 들면은 차량에 누가 터지하거나 충격을 주게 되면 차량에서 경보음이이 울리는 것으 많이 보셨을 꺼에요. 그런 것들을 연상하시면 될 듯 싶네요.

이제 진동센서를 알아봅시다.

1. 진동센서(Vibration Sensor)


진동센서는 진동을 감지하는 센서로 진동에 대해 디지털 신호 or 아날로그 신호를 만들어 냅니다. 진동센서는 3핀으로 VCC, GND, D0핀으로 구성된 모듈이 있는가 하면 아래 그림처럼 VCC, GND, D0, A0핀으로 구성된 모듈이 있습니다. 아래 그림을 보시면 진동에 대한 값을 십자모양의 가변저항으로 조절 할 수 있습니다.


진동센서는 2가지 형태로 출력 신호를 만들어 냅니다. 첫번째로 디지털 출력으로 0 or 1의 값을 만들어 내는데 진동센서에 대해 조사하니 초기상태값이 HIGH 상태이고 진동이 발생하면 LOW 상태가 된다고 나와 있습니다. 즉, 진동이 발생하면 진동센서는 LOW가 되고 아두이노우노에서 진동(LOW)에 대한 처리 동작을 설계하면 됩니다. 두가지 상태만 존재하기 때문에 스위치 역할을 수행하는 주제에 알맞습니다. 두번째로 아날로그 출력으로 0~1023의 값을 만들어 내겠죠. 디지털 출력는 진동에 대한 두가지 상태만 존재하기 때문이 때문에 두가지 상황만 만들어 낼 수 있지만 아날로그 출력은 진동의 강도값으로 진동의 신호를 만들어 내기 때문에 이 신호의 범위값을 나누면 여러 상황을 만들어 낼 수 있고 처리동작도 여러개의 형태로 처리동작을 만들어 낼 수 있습니다. 두가지 방식 중 여러분들이 설계하는 목적에 맞게 디지털로 읽을지 아날로그를 읽을지만 잘 정하고 알맞게 사용하시면 도비니다.

진동센서의 값을 읽는 방법은 다음과 같습니다.

  • 디지털 입력 : digitalRead(D0)
  • 아날로그 입력 : analogRead(A0)

위 함수를 통해서 읽은 값에 대한 처리 동작만 간단히 코딩하면 되기 때문에 진동센서는 어려운 부품이 아닙니다.

2. 진동센서 회로도


준비물 : 진동센서, LED 1개, 저항 220옴 1개, 아두이노우노
내용 : 진동센서의 D0핀을 7번에 연결하고 LED 핀은 12번에 연결하시오.



위 그림은 진동의 상태를 LED로 출력하기 위한 회로도 입니다.

3. 코딩


설계 : 진동이 발생하면 LED에 불이 들어오게 한다.

if(진동감지) LED 켜기;
else LED 끄기;

딱 두줄이면 됩니다. 실험에서는 디지털 입력을 받기 때문에 아래와 같습니다.

if(digitalRead(디지털핀)==LOW) digitalWrite(LED핀, HIGH);
else digitalWrite(LED핀, LOW);

종합해 보면,

const byte StatePin = 12;
const byte VibrationPin = 7;

void setup(){
 pinMode(StatePin, OUTPUT);
 pinMode(VibrationPin, INPUT); 
}

void loop(){
 if(digitalRead(VibrationPin)==LOW) digitalWrite(state, HIGH);
 else digitalWrite(state, LOW);
}

진동감지에 대한 간단히 코딩입니다. 실제 부품이 없어서 결과는 알 수 없지만 유사한 실험을 한다면 기울기센서로 대신 가상시뮬레이터에서 실험하시면 됩니다.



기울기 센서에서 기울기가 발생하면 LED에 불이 들어오는 실험이 있으닊나 진동센서를 기울기 센서로 감안하여 대신 실험을 할 수 있습니다.

4. 경보기 응용


진동센서와 피에조부저 같은 사운드 출력 센서를 이용하면 진동이 발생하면 소리로 결과를 출력 할 수 있습니다. 경보장치로 활용하면 좋습니다. 보안이 필요한 대상에 대해서 진동이 발생하면 경보음이 울리게 설정 할 수 있습니다.

1) 진동센서 경보기 회로도


준비물 : 진동센서, LED 1개, 저항 220옴 1개, 아두이노우노
내용 : 진동센서의 D0핀을 7번에 연결하고 LED 핀은 12번에 연결하시오.



피에조부저만 6번에 추가로 연결하시면 됩니다.

2) 코딩


위 진동센서 회로도에서 피에조부저만 추가로 부착되었기 때문에 소리에 대한 부분만 표현하시면 됩니다.

  • 피에조부저 : tone(핀번호, 주파수, 출력시간), noTone(핀번호), delay(시간값)

간단히 한음의 소리를 울리게 표현을 하면 아래와 같습니다.

   tone(SoundPin,523,1000/8);     // 도음으로 8분음표(음길이)
   delay(1000/4*1.30);             
   noTone(SoundPin);         

이 코딩을 어디에 넣어야 할까요? 바로 진동이 발생하는 시점에 경보음이 울리게 하면 됩니다.

종합해보면,

const byte StatePin = 12;
const byte VibrationPin = 7;
const byte SoundPin = 6;

void setup(){
 pinMode(StatePin, OUTPUT);
 pinMode(VibrationPin, INPUT); 
}

void loop(){
 if(digitalRead(VibrationPin)==LOW) {
   digitalWrite(state, HIGH);
   tone(SoundPin,523,1000/8);     // 도음으로 8분음표(음길이)
   delay(1000/4*1.30);             
   noTone(SoundPin);         
 }
 else digitalWrite(state, LOW);
}

유사한 예제로는 아래 거리경보장치 실험을 참조하시면 됩니다. 초음파센서로 거리값으로 경보음이 울리는 예제인데 여기서 진동센서의 역할을 초음파센서가 대신한 걸로 상상하시면 됩니다.



마무리


진동센서에 대해서 간단히 살펴보고 응용 예제로 진동센서로 경보장치를 상상해 보았습니다. 이것 말고도 다양한 것을 상상 할 수 있습니다. 위에서는 디지털 입력을 다뤘지만 진동에 대한 아날로그 입력을 받을 수 있다면 진동의 강도에 따라서 다양한 명령을 내릴 수 있습니다. 디지털 입력의 경우는 진동 하냐/안하냐 두가지 상태지만 아날로그는 진동의 범위에 따라 내리는 명령을 다양하게 내릴 수 있어 진동의 강도에 따른 섬세한 제어가 가능하다는 걸 알아 두세요.

아두이노는 재밌는 부품이 많습니다. 진동을 발생하는 진동모터가 있는가 하면 진동을 감지하는 진동센서가 있습니다. 여러분들도 특정 부품에 대해 조사하고 실험을 할 때 어떤 A동작을 수행하는 부품이 있다면 반대의 B동작의 부품이 없는지 같이 찾아서 공부하시면 아두이노 공부에 많은 도움이 됩니다. 공부하실 때 같은 동작을 하는 부품을 찾거나 반대 동작하는 부품을 같이 찾아서 공부를 꼭 해주세요. 못찾으면 어쩔 수 없지만 찾으시면 따블로 공부 효과를 얻을 수 있기 때문에 꼭 제가 말한 방식으로 아두이노를 공부해 주세요. 그래야 나중에 뭔가를 표현하고 싶을 때 도움이 많이 됩니다.

댓글()

[아두이노] 진동모터(Vibration Motor) 제어

IOT/아두이노|2019. 8. 12. 09:00

[아두이노] 진동모터(Vibration Motor) 제어



오늘은 가상시뮬레이터에 제공되는 부품 중 한가지 진동모터 부분을 빼먹은 것 같아서 진동모터에 대해서 이야기를 하고자 합니다. 진동모터는 모터에서 진동을 일으키는 모터입니다. DC모터는 모터가 회전하는 반면에 진동모터는 회전이 아닌 진동입니다. 제어하는 방식은 DC모터와 동일하며 전류가 공급되면 진동이 발생하고 전류의 세기에 따라서 진동의 세기가 달라집니다. 따로 알아야 할 부분은 없으면 가볍게 진동모터에 대해서 이야기 하고자 합니다.

1. 진동모터(Vibration Motor)


진동모터는 모터의 진동을 발생하는 부품입니다. 진동을 발생은 인간에게 촉감으로 느끼게 하는는 부품이라고 할 수 있습니다. 대표적인 예로 스마트폰은 전화나 문자가 올 때 사운드(음악소리)로 기본적으로 알립니다. 여기서 진동모드로 바꾸면 사운드가 아닌 스마트폰의 진동으로 전화나 문자가 온 걸 알려주게 됩니다. 이처럼 소리가 아닌 진동으로 인간에게 특정한 상황을 알릴 수 있습니다. 즉, 진동응 인간이 촉감으로 느낄 수 있는 출력부품으로 어떤 상황을 진동으로 감지할 수 있게 하는 출력부품이라고 생각하시면 됩니다.

그리고 진동모터는 상상하실 때 이렇게 생각하시면 됩니다. LED, 스피커 등과 같은 다양한 출력 부품들의 표현들을 진동모터로 대신하여 표현 할 수 있기 때문에 진동모터로 순순하게 상상하실 필요 없이 기존의 다양한 출력 부품들을 진동모터로 대체하고 응용상상을 하시면 됩니다.

가상시뮬레이터에서 제공되는 진동모터는 5V 입니다. 일반적인 DC모터를 제어하던 방식과 비슷합니다.


실제 실험에 사용하는 진동모터는 소형진동모터로 모터의 종류에 따라서 필요 전압이 다릅니다. 아두이노우노는 3.3V와 5V를 공급해줍니다. 아두이노우노 핀에서 출력 전압이 5V인데 만약 3V대 진동모터라면 아래와 같이 저항을 붙여 줘야 겠지요.


여러분들이 구매하신 진동모터가 몇V인지를 꼭 확인하시고 회로도를 구성하시면 됩니다.

참고로 아두이노우노는 PWM 핀은 아날로그 출력을 0~255(0~5V)을 출력 제어할 수 있습니다. 즉, 아날로그 출력값에 따라서 진동의 세기를 조절 할 수 있습니다. 위 두개의 의 그림을 보시면 5V일 때 진동모터는 진동 이미지로 3개의 띠를 보여주지만 저항이 붙은 진동모터는 2개의 띠가 희미하게 보이실 꺼에요. 이처럼 V값에 따라서 진동의 세기가 달라집니다.

진동모터를 구매하실 때 진동모터 사양을 꼭 확인하시기 바랍니다. 소형진동모터는 대개 약3V정도 인 것 같더군요.


참고로 가상시뮬레이터에 있는 위 그림 형태의 진동모터를 제공합니다. 이것을 가지고 회로도를 구성한다면 아래와 같겠죠.


void setup(){
  pinMode(3, OUTPUT);
}
void loop(){
  analogWrite(3, 255); //진동세기 임의의 값
  delay(1000);
  analogWrite(3, 0);
  delay(1000);
}

이렇게 가상시뮬레이터에서 돌리면 전압이 너무 낮아서 진동하지 않네요. 뭔가 잘못된 건가 하고 실제 구현하신분들의 영상을 보면 정상적으로 동작하더군요. 가상시뮬레이터에서는 진동모터가 5V라서 아두이노 핀에서 출력되는 전압이 낮고 해서 진동이 발생하지 않았습니다. 잘못되었다기 보다는 진동 전압이 낮기 때문에 진동을 가상시뮬레이터에서 발생시키지 못하더군요. 저항을 제거하고 실험하셔도 전압이 낮더군요. 디지털핀에서 제공되는 전압에서는 모터가 가상시뮬레이터에서 반응이 없습니다. 아래 3.3V와 5V에서는 진동이 전상적으로 발생하지만 디지털 핀에서의 전원공급에는 좀 문제가 있습니다. 사실 디지털핀으로 전원을 공급한다는 설계 자체가 좀 문제가 있습니다. 진동모터도 모터의 일정인데 디지털핀에서 모터의 전원을 공급한다는 설정은 좀 그렇죠. 아무리 낮은 전압이라도 모터는 모터이기 때문에 될 수 있으면 디지털 핀같은 곳에서 모터의 전원을 공급하는 설정은 추천하지 않습니다. 제가 post에서 실험한 내용은 단지 예를 든 실험입니다.

아무튼 아래 그림처럼 가상시뮬레이터에서 이렇게 결과가 나오네요.


전압이 낮은 진동모터였다면 동작을 했겠죠. 위 그림처럼 실험 할 시에 저항은 진동모터 전압에 맞는 저항을 붙여줘야 합니다. 최대 진동을 기본으로 맞추고

analogWrite(3, 255); //진동세기 임의의 값

이렇게 255가 되었을 때 최대 진동이고 0~255사이의 값을 줌으로서 진동의 세기를 조절 할 수 있게 됩니다. 최소 전압도 필요하기 때문에 너무 낮은 값으로 출력하면 진동모터가 동작 안할 수 있습니다. 가상 시뮬레이터는 전압이 5V 진동모터라 아두이노에서 출력되는 전압이 약4.7V에다가 저항에 따라서 1.92V정도 나왔네요. 그래서 진동을 가상시뮬에터에서 발생하지 않지만 실제는 다릅니다.

참고로, 저항없이 바로 PWM(3번)핀에 연결하시면 됩니다. 그럴 경우는

analogWrite(3, 100); //진동세기 임의의 값

진동 세기값으로 실험하는 진동모터의 최대값 수치가 예를 들어 100이라고 가정하면 100이상만 안넘기게만 하면 됩니다. 위 진동모터는 고려해야 부분이 몇가지 있네요.

아래 사진처럼 모듈형태의 진동모터도 제공되고 고려할 부분은 모듈 전압만 확인 하시면 됩니다. 회로도 연결은 간단합니다.

그림 출처 : https://www.aliexpress.com

모듈형태의 진동모터는 제어가 훨씬 편합니다. SIG, VCC, GND 핀으로 구성되어 있고 SIG핀은 아두이노 PWM핀에 연결하여 값만 넣으시면 됩니다.

예) SIG (3번핀) 연결 되었을 경우


void setup(){
  pinMode(3, OUTPUT);
}
void loop(){
  analogWrite(3, 200); //진동세기 임의의 값
  delay(1000);
  analogWrite(3, 0);
  delay(1000);
}

대충 이런식으로 제어를 할 수 있겠죠.

이제는 가상시뮬레이터에서 동작하게끔 변형 시켜서 실험을 해보도록 하겠습니다.

2. 진동모터 회로도


준비물 : NPN트렌지스터, 저항 1k옴 1개, 진동모터 1개, 아두이노우노
내용 : 3번핀을 진동모터 핀으로 이용해 봤네요.



위 그림에서 다이오드와 캐피시터를 달아야 하는데 생략 했네요.

실제는 아래 그림처럼 해도 동작할 꺼에요.


위 처럼 가상시뮬레이터에서 실험하면 움직이지 않습니다. 문제가 있는가 하고 인터넷 실험 영상을 찾아보니깐 동작에는 문제가 없어 보이더군요. 전류를 살펴보니 너무 낮은 전압이라서 동작을 안한 것이더군요. 아무튼 실제로 하실 때는 .트렌지스터 사용하지 않고 간단히 표현하셔도 아마 될 듯 싶네요.

3. 코딩


PWM핀을 이용하는데 analogWirte()함수를 이용하셔도 되지만 그냥 기본 digitalWrite()함수를 이용하셔도 됩니다. 가상시뮬레이터에서는 digitalWrite()함수를 이용했습니다.

void setup()
{  
  pinMode(3, OUTPUT);
}

void loop()
{
  digitalWrite(3, HIGH);
  delay(1000); 
  digitalWrite(3, LOW);
  delay(1000); 
}

4. 결과



위 회로도를 보시면 어디서 많이 본 듯한 회로도이지 않나요. 예전에 DC Motor에서 다뤘던 회로도 입니다.



참고 post의 회로도에 약간 다른 방식으로 디자인 해 봤네요. DC MOTOR편을 보시고 참고하셔서 회로도를 디자인을 하셔도 됩니다.

마무리


위에서 표현한 방식으로 표현하실 필요는 없습니다. 실제로 실험 하실 때는 진동모터의 사양을 보시고 필요 전압이 몇인지에 따라서 아두이노에 직접 연결하실 때 저항을 붙여서 연결하시든지 아니면 그냥 연결하시고 나서 PWM핀에 analogWirte()함수를 이용하여 진동 모터의 세기 전압만 맞추셔도 됩니다.

간단한 진동모터를 좀 복잡하게 설명한 것 같네요. 아무튼 진동모터로 할 수 있는 것들이 뭐가 있는지 한번 여러분들이 찾아보시고 뭘 할 수 있을지 상상해 보세요.

댓글()

[아두이노] 시리얼 플로터 사용

IOT/아두이노|2019. 8. 9. 09:00

[아두이노] 시리얼 플로터 사용



아두이노를 실험 할 때 지금까지는 시리얼 모니터로 디지털 값으로만 확인 해 봤습니다. 사실 지금까지 시리얼 플로터를 사용하지 않는 이유는 시이얼 플로터로 결과를 확인이 필요한 경우가 없었는데 지난 시간에 Color Sensor를 사용하면서 시리얼 플로터로 결과를 확인할 필요성이 느껴져서 이번에 post 주제로 선정했네요. 시리얼 플로터는 아날로그 파형으로 그래프 형태로 결과를 시각적으로 보여주는 출력 장치입니다. 그럼 이제 시리얼 플로터가 어떤 느낌인지 자세히 살펴보도록 하죠.

1. 시리얼 플로터


시리얼 모니터는 출력값을 디지털 형태의 숫자로 결과를 출력을 하고 시리얼 플로터는 아날로그 형태의 그래프 형태로 시각화 출력을 합니다.

그러면 시리얼 모니터와 시리얼 플로터의 출력 형태를 비교해 볼까요.

[실험 소스]

int output = 1;
unsigned long timeVal = 0;

void setup()
{
  Serial.begin(9600);
}

void loop()
{   
   if(millis()-timeVal>=100){ //0.1초 단위로 1 or -1의 결과를 출력합니다.
    output=-output;
    timeVal=millis();    
   }
   Serial.println(output); //시리얼 출력
}

[시리얼모니터 결과]


[시리얼플로터 결과]


둘의 출력 형태가 어떤 느낌인지 아시겠지요.

2. 시리얼 플로터에 여러개의 데이터 출력


위에서는 한개의 데이터만 출력했습니다. 지난 시간에 실험한 Color Sensor은 3개의 RGB 데이터를 출력하는데 그러면 그 데이터는 한번에 같이 어떻게 출력 할까요.

아래와 같은 방식으로 코딩하시면 됩니다. 즉, 데이터와 데이터 사이를 공백으로 띄워서 데이터를 출력하면 나눠서 출력하게 됩니다.

int output = 1;
unsigned long timeVal = 0;

void setup()
{
  Serial.begin(9600);
}

void loop()
{   
   if(millis()-timeVal>=100){ //0.1초 단위로 1 or -1의 결과를 출력합니다.
    output=-output;
    timeVal=millis();    
   }
     
     //시리얼 출력
   Serial.print(output); //데이터 1번
   Serial.print(" "); 
   Serial.println(output*2); //데이터 2번
}

즉, output와 output*2의 값 사이에 공백을 넣어주면 두개의 데이터를 동시에 아래 결과처럼 출력하게 됩니다.

[결과]


3. Color Sensor 값을 시리얼 플로터로 출력



지난 시간의 회로도를 그대로 적용 합니다.

1) Color Sensor 회로도


위 회로도는 지난시간에 만들 회로도이고 코딩도 그래도 적용합니다.

2) 코딩


시리얼 출력 부문만 약간 수정합니다.

const byte s0_pin = 3;
const byte s1_pin = 4;
const byte s2_pin = 5;
const byte s3_pin = 6;
const byte out_pin = 7;

const byte r_pin = 9;
const byte g_pin = 10;
const byte b_pin = 11;


void setup() {
  Serial.begin(9600);
  
  pinMode(r_pin, OUTPUT);
  pinMode(g_pin, OUTPUT);
  pinMode(b_pin, OUTPUT);
  
  pinMode(s0_pin, OUTPUT);
  pinMode(s1_pin, OUTPUT);
  pinMode(s2_pin, OUTPUT);
  pinMode(s3_pin, OUTPUT);
  
  pinMode(out_pin, INPUT);
  
  digitalWrite(s0_pin,HIGH);
  digitalWrite(s1_pin,LOW);
}


void loop() {
  digitalWrite(s2_pin,LOW);
  digitalWrite(s3_pin,LOW);
  int red_color = pulseIn(out_pin, LOW);
  red_color = map(red_color, 25,72,255,0);
  red_color = constrain(red_color, 0, 255);
  delay(50);
  
  digitalWrite(s2_pin,HIGH);
  digitalWrite(s3_pin,HIGH);
  int green_color = pulseIn(out_pin, LOW);
  green_color = map(green_color, 30,90,255,0);
  green_color = constrain(green_color, 0, 255);
  delay(50);
  
  digitalWrite(s2_pin,LOW);
  digitalWrite(s3_pin,HIGH);
  int blue_color = pulseIn(out_pin, LOW);
  blue_color = map(blue_color, 25,70,255,0);
  blue_color = constrain(blue_color, 0, 255);
  delay(50);

  analogWrite(r_pin, red_color);
  analogWrite(g_pin, green_color);
  analogWrite(b_pin, blue_color);

  //시리얼 플로터 출력
  Serial.print(blue_color);
  Serial.print("  ");  
  Serial.print(red_color);
  Serial.print("  ");      
  Serial.println(green_color);
  
  delay(100);
}

시리얼 출력 부분에서 시리얼 플로터 출력으로 약간 수정 했네요. 시리얼 플로터에 색상에 맞추다 보니깐 B, R, G 순으로 했네요. 혹시 여러분들이 출력을 했는데 아래 결과의 색상이 다르면 위 코딩의 색상 출력 순서를 변경하시면 됩니다.

3) 결과



Color의 변화를 시각적으로 확인 할 수 있습니다. 자세히 보시면 노이즈로 인해 색값이 변화가 중간에 심하게 발생하는 보실 수 있을꺼에요. 센서 자체가 납땜이 안되어 있고 전선꼽아서 하기 때문에 손으로 만지면 전선과 센서 사이에 잡음이 발생 할 수 밖에 없네요.

아래는 색상이 측정되고 출력되는 결과를 기록해 놓았습니다.


마무리


시리얼 플로터는 특정한 값의 변화를 시각적으로 확인하기에 편합니다. Color Sensor와 같은 부품을 제어 할 때에 값의 변화가 수치보다는 시각적 챠트로 보는게 더 효과적이겠죠. 이 챠트를 보면 색의 주파수 범위를 나중에 색종이를 가지고 지정할 때 활용하면 좋겠죠.

오늘은 간단히 시리얼 플로터를 사용하는 방법을 살펴 보았네요.


댓글()

[아두이노] Color Sensor 제어

IOT/아두이노|2019. 8. 1. 09:00

[아두이노] Color Sensor 제어



Color Sensor(TCS3200)은 색을 감지할 수 있는 센서입니다. 빛에서 색을 주파수값으로 읽는 센서인데 이 주파수 값을 가지고 RGB색을 만들어 내게 됩니다. 참고로 주파수 값을 읽을 수 있다고 해서 색값으로 바로 만들어 지지 않습니다. 각 RGB색의 값에 대한 주파수 영역을 잡아 색값 0~255사이의 값으로 변환 시켜야 합니다. 그 색 주파수 영역을 통해서 진짜 RGB색을 만들어 내는데 최근에 구매한 부품이여서 정확히 RGB 주파수 영역을 직접 만들어 내지 못했네요. 색종이로 RGB색을 측정하여 주파수 영역을 잡고 조정 작업을 해야 하는데 이미 만들놓은 분의 RGB 주파수 영역을 인용하여 간단히 실험을 하였습니다. 시간적 여유가 되시는 분들은 꼭 직접 RGB 주파수 영역을 본인이 가지고 있는 Color Sensor의 값을 측정하면서 원시값을 토대로 직접 그 영역을 만들어 보세요. 영유가 안된다면 저처럼 인용을 통해 간단히 실험하시면 됩니다.


1. Color Sensor




위 그림처럼 해당 핀에 대해서 살펴봅시다.

S0, S1 핀을 통해서 출력되는 주파수 크기값을 지정 합니다.


퍼센트 수치가 클수록 출력되는 숫자의 값은 작아 집니다. (L, H)은 숫자의 크기가 너무 큰 수가 나오고 (H,H)은 너무 작은 숫자값이 나오기 때문에 RGB색을 추출하기에는 좀 불편합니다. 그나마 색(0~255)을 만들기 위해서는 적당한 출력 수치값으로 (H,L)로 출력하면 적당한 주파수 크기로 출력됩니다. 실험에서는 (H,L)로 20% 크기로 출력됩니다.

RGB 각 색의 주파수 값을 얻기 위해서는 S2, S3의 상태로 해당 색상의 주파수 값을 얻을 수 있습니다.


위 표를 통해 RGB 색의 주파수 값을 얻게 됩니다.

결론은 S0, S1은 출력되는 주파수 크기를 지정하고 S2,S3은 해당 색을 지정해 줍니다.

2. Color Sensor 회로도




Color 센서에서 측정된 RGB 값을 3색 LED로 출력하는 회로도 입니다.

3. 코딩


위 데이터시트에의 나온 표를 기반으로 Color Sensor의 색을 읽기

Red 색 읽기

digitalWrite(s0_pin,HIGH); //출력 크기
digitalWrite(s1_pin,LOW);

digitalWrite(s2_pin,LOW); //Red 색
digitalWrite(s3_pin,LOW);
int Red = pulseIn(out_pin, LOW); //Red 주파수 값

Green 색 읽기

digitalWrite(s0_pin,HIGH);
digitalWrite(s1_pin,LOW);
digitalWrite(s2_pin,HIGH); //Green 색
digitalWrite(s3_pin,HIGH);
int Green = pulseIn(out_pin, LOW);

Blue 색 읽기

digitalWrite(s0_pin,HIGH);
digitalWrite(s1_pin,LOW);
digitalWrite(s2_pin,LOW); //Blue 색
digitalWrite(s3_pin,HIGH);
int Blue = pulseIn(out_pin, LOW);

3가지 색을 이렇게 읽게 됩니다.

스케일 20%로 고정이니깐 출력 크기는 setup()함수로 빼내고 나머지 각 색은 Loop함수에서 지정해 주면 되겠죠.

R,G,B 색값을 읽으면 그 색값이 색으로 보여지지 않습니다. 데이터시트에 가시면 주파수 챠트가 있는데 그걸 보고 색의 추출 범위를 지정해야 합니다.

색종이 같은 걸로 Red, Green, Blue 색의 주파수 최소 최대 범위를 지정하면 좋은데 색종이가 없어서 색의 범위 지정하기 어려워서 색의 범위를 만든 post를 겨우 찾아서 색의 범위를 지정할 수 있게 되었네요.



위 post 출처에서 Red, green, blue의 범위를 인용하여 실험했네요.

red_color = map(red_color, 25,72,255,0);
green_color = map(green_color, 30,90,255,0);
blue_color = map(blue_color, 25,70,255,0);

제가 보유한 Color Sensor는 깔금하게 색을 만들어 내지 못하더군요. 그리고 색 값이 0~255 범위를 벗어나는 값이 나오기 때문에 constrain()함수로 0~255사이로 묶어 두었습니다. 0보다 작은 값은 0에 고정되게 하고 255값을 벗어나면 255에 고정되게 만들었네요.

Red을 기준으로 표현하면

int Red = pulseIn(out_pin, LOW);
red_color = map(red_color, 25,72,255,0);
red_color = constrain(red_color, 0, 255);

나머지 색도 위 코딩과 같이 코딩하시면 됩니다.

Color 값을 만들었다면 그 값을 3색 LED로 핀은 PWM 핀으로 아날로그값을 출력하기 때문에 아래와 같이 코딩하면 됩니다.

RGB LED 출력

analogWrite(r_pin, red_color);
analogWrite(g_pin, green_color);
analogWrite(b_pin, blue_color);

종합해 보면,

const byte s0_pin = 3;
const byte s1_pin = 4;
const byte s2_pin = 5;
const byte s3_pin = 6;
const byte out_pin = 7;

const byte r_pin = 9;
const byte g_pin = 10;
const byte b_pin = 11;


void setup() {
  Serial.begin(9600);
  
  pinMode(r_pin, OUTPUT);
  pinMode(g_pin, OUTPUT);
  pinMode(b_pin, OUTPUT);
  
  pinMode(s0_pin, OUTPUT);
  pinMode(s1_pin, OUTPUT);
  pinMode(s2_pin, OUTPUT);
  pinMode(s3_pin, OUTPUT);
  
  pinMode(out_pin, INPUT);
  
  digitalWrite(s0_pin,HIGH);
  digitalWrite(s1_pin,LOW);
}

void loop() {

  //Red Color Read
  digitalWrite(s2_pin,LOW);
  digitalWrite(s3_pin,LOW);
  int red_color = pulseIn(out_pin, LOW);
  red_color = map(red_color, 25,72,255,0);
  red_color = constrain(red_color, 0, 255);
  delay(50);
  
  //Green Color Read
  digitalWrite(s2_pin,HIGH);
  digitalWrite(s3_pin,HIGH);
  int green_color = pulseIn(out_pin, LOW);
  green_color = map(green_color, 30,90,255,0);
  green_color = constrain(green_color, 0, 255);
  delay(50);
  
  //Blue Color Read
  digitalWrite(s2_pin,LOW);
  digitalWrite(s3_pin,HIGH);
  int blue_color = pulseIn(out_pin, LOW);
  blue_color = map(blue_color, 25,70,255,0);
  blue_color = constrain(blue_color, 0, 255);
  delay(50);

  //3색 LED에 RGB 출력
  analogWrite(r_pin, red_color);
  analogWrite(g_pin, green_color);
  analogWrite(b_pin, blue_color);
  
  Serial.print("RED: ");
  Serial.print(red_color);
  Serial.print("  ");
  
  Serial.print("GREEN: ");
  Serial.print(green_color);
  Serial.print("  ");

  Serial.print("BLUE: ");
  Serial.print(blue_color);
  Serial.println("  ");
  
  delay(1000);
}

4. 결과


시리얼 모니터에 색값을 출력해 보았습니다.


스케일을 2%로 지정했다면 위 값은 천단위로 출력되고 스케일을 100% 했다면 일단위로 값이 출력됩니다. 위 결과처럼 20%가 가장 적당합니다. 참고로 위 결과는 20% 스케일의 map()함수를 이용하여 주파수 값을 색값으로 변환하고 다시 constrain()함수로 0~255사이로 고정시켜 나온 결과입니다.

아래 3색 LED에 Color Sensor로 측정한 값이 출력한 영상인데 깔끔하게 출력되지 않네요. Color Sensor를 딱 붙이지 않고 좀 높이를 어느정도 띄워서 측정하면 색이 좀 더 측정하는 색에 가깝게 출력되더군요. 한손으로 측정하고 한손으로 스마트폰 촬영을 하다 보니깐 높이를 제대로 맞추지 못해서 결과는 마음에 들지 않게 나왔네요.


마무리


Color Sensor부품은 처음에는 좀 쉽게 생각했는데 다루기가 좀 까다로운 부품이네요. 동작 코딩은 어렵지 않지만 정확하게 색의 값을 만들어 내는게 쉽지 않네요. 센서의 연결 부분에 노이즈가 발생하면 값의 변화가 크고 정확하게 RGB값으로 분리해 내기도 어렵습니다. 직접 사용하는 센서의 측정되는 RGB값의 범위를 일일히 잡아야 하는게 쉬운 부품이 아니네요. RGB 색값의 범위를 지정 할 수 있다면 나머지 부분은 간단하기 때문에 컨트롤 하기는 어렵지는 않는데 색 주파수 영역이 문제네요.


댓글()

[아두이노] 마그네틱도어센서+멜로디 트렌지스터 응용

IOT/아두이노|2019. 7. 31. 09:00

[아두이노] 마그네틱도어센서+멜로디 트렌지스터 응용



오늘은 지난시간에 다룬 멜로디 트렌지스터를 가지고 마그네틱도어센서에 연동하여 출입문 알림이를 상상해보는 시간을 가져 보았습니다. 일상에서 사용되는 여러가지 응용들을 중에 이 주제를 선택한 이유는 아주 간단한 원리로 동작하기 때문입니다. 실제 부품이 없는 상태에서도 충분히 상상코딩이 가능하기 때문입니다. 편의점 같은 장소에서 출입문을 열고 들어가면 멜로디가 나오는 상점들을 한번쯤은 들어보셨을 꺼에요. 그러면 어떻게 멜로디가 나오는 것일까요. 아주 간단합니다 .출입문 쪽에 마그네틱도어센서가 부착되어 출입문이 닫혀있을 때와 열렸을 때 마그네틱도어센서의 값은 1 or 0 의 상태값이 발생하고 그 값을 통해서 멜로디 IC를 작동시켜 출입문이 열렸을 때 멜로디가 연주 되게 회로도를 구성하면 됩니다.

그러면 마그네틱도어센서로 어떻게 멜로디 IC를 동작시키는지 아두이노로 간단히 상상 테스트를 해보도록 하죠.

1. 마그네틱도어센서



마그네틱도어센서는 일반 스위치와 같은 용도로 사용합니다. 위 그림에서 보는 것처럼 두개의 물체로 구성되어 있고 한쪽 물체에 두개의 전선 가닥이 연결되어 있습니다. 한쪽 전선에 전류를 Input(입력) 되면 다른 전선은 Output(출력) 역할을 합니다. 전선이 연결되어 있는 물체 몸체와 반대면 물체 몸체가 가까워지거나 멀어질 때 Output(출력) 상태는 1 or 0의 상태가 됩니다. 즉, 두 물체가 가까워지거나 멀어질 때 전선이 연결된 위치에 자력에 의해가 연결되거나 끊어지게 되는데 전류의 상태는 1 or 0 됩니다.

2. 마그네틱도어센서+Melody IC 회로도(도어상태알람)



마그네틱도어센서를 실제 가지고 있지 않아서 대충 상상력을 동원하여 회로도를 만들어 보았네요. 사전학습 post의 아두이노에 연결한 멜로디 IC를 간단히 표현했는데 거기에 마그네틱도어센서만 추가로 부착해 봤네요.


3. 코딩


실제로 실험을 못하기 때문에 코딩은 최대한 오류가 없는 방향으로 상상코딩을 해보도록 하겠습니다.

편의점 같은 장소에서 출입문을 열고 들어가면 잠깐 멜로디가 나오는 것을 들으신 적이 있을 꺼에요. 그걸 상상코딩을 해보도록 하겠습니다.

우선 마크네틱도어센서가 가까울 때(닫힘)는 "1" 상태이고 멀어질 때(열림) "0"상태라고 가정하겠습니다.

회로도의 센서값 읽기는 디지털 상태가 0 or 1을 읽는 digitalRead()함수를 사용하니깐 아래와 같이 코딩합니다.

int senserValue = digitalRead(마그네틱도어센서핀);

0 or 1상태에서 멀어질 때(열림) 멜로디 연주가 시작이 되어야 하기 때문에 다음과 같이 코딩 합니다. senserValue값에 따라서 해당 2번핀에 digitalWrite()함수로 전류를 출력하면 멜로디가 연주되고 전류를 차단하면 멜로디가 중단 되게 코딩을 하면 됩니다.

if(senserValue==LOW) digitalWrite(멜로디핀,HIGH);
else digitalWrite(멜로디핀,LOW);

종합해보면,

const byte melodyPin = 2;
const byte swPin = 3;

void setup(){
  pinMode(swPin,INPUT);
  pinMode(melodyPin,OUTPUT);
}
void loop(){
  int senserValue = digitalRead(swPin);
    
  if(senserValue==LOW) digitalWrite(melodyPin,HIGH);
  else digitalWrite(melodyPin,LOW);
    
  delay(50);
}

대충 위와 같은 코딩으로 표현 하면 될거라 생각 되네요. 따로 문제가 되는 부분은 없고 간단한 실험을 했기 때문에 회로도나 코딩에는 문제가 없을거라 생각됩니다.

마무리


마그네틱도어센서는 서로 전선이 연결되어 있지 않는 상태에서 자력의 원리를 이용하여 전류의 흐름을 제어할 수 있는 부품이라 참 재밌는 부품입니다. 이 부품으로 도어상태알람을 상상 실험을 했지만 이것 말고도 다양한 응용 분야에 사용되고 있습니다. 가령 창문에 연결하면 방범알람이로 디자인 할 수 있습니다. 여기에 추가로 Bluetooth or Wifi 같은 무선 모듈을 연결하면 스마트폰으로 방범 정보를 전송할 수 있게 됩니다. 집 같은 장소에다가 방범장치로 활용하면 원격으로 정보를 스마트폰으로 수신할 수 있어 꽤 유용하겠죠. 이걸 계속 업그레이드 하면 더 재밌는 것을 만들어 내실 수 있을 꺼에요.

그리고 마그네틱도어센서 대신에 근접센서나 인간감지센서와 같은 부품을 접목한 다양한 표현들이 현실에 존재합니다. 한번 어떤 것들이 있는지 찾아 보세요. 그리고 아두이노시각으로 그걸 한번 구현해 보세요.

댓글()

[아두이노] 멜로디 트렌지스터

IOT/아두이노|2019. 7. 30. 09:00

[아두이노] 멜로디 트렌지스터



오늘은 특정 멜로디 트렌지스터(UTC UM66TXXL) 부품에 대해 이야기를 하고자 합니다. 참 재밌는 부품으로 이 부품에 스피커를 연결하고 전류를 공급하면 멜로디가 흘러 나옵니다. 멜로디가 나오는 부품은 활용 분야가 엄청 많습니다. 여러분들도 일상에서 한번쯤은 접해 봤을 꺼에요. 편의점 문을 열때 멜로디가 나오거나 자동차 후진음으로 멜로디를 들어 보신분들이 많을꺼에요. 그리고 경보장치, 알람장치, 장난감 등에서 한번쯤은 들어 보셨을 거라 생각합니다. 이렇게 열거한 곳에서 멜로디 트렌지스터가 사용 됩니다.

이제 멜로디 트렌지스터를 어떻게 사용하는지 간단히 살펴보도록 하죠.

1. 멜로디 트렌지스터(데이터시트 내용을 간단히 정리)



링크 걸어 놓은 UTC UM66TXXL데이터시트에 가시면 자세히 이 부품에 대한 설명이 나와 있습니다. 데이터시트에 나온 내용 중 최소 알아야 할 부분만 발췌해서 아래와 같이 정리 했습니다. 자세히 보고 싶으시다면 위 출처에 가셔서 확인하시면 되겠습니다.


부품은 간단합니다. 그리고 데이터시트에 가셔서 보시면 2개의 형태의 회로도가 아래와 같습니다.

출처 : 데이터시트 (p4)



위 회로도는 데이터시트에 나와 있는 기본 회로도 입니다. 스위치 버턴을 이용하여 피에조부저와 스피커로 멜로디를 출력하는 회로도 이더군요. 그림2은 전자부품 쇼핑몰에 가시면 키트로 판매하더군요 약2천 얼마였던 것 같은데 잘 기억이 안나네요.

여기서 아두이노에 연결하여 실험한다면 스위치 부분만 아두이노우노의 핀에 연결하시면 아두이노 우노에서 제어가 가능해 집니다.

참고로, 이 부품은 한곡의 멜로디가 담겨져 있지만 부품 번호에 따라서 멜로디가 다릅니다. 데이터시트에 나온 멜로디 리스트는 다음과 같습니다.


"XX" 위치에 Number를 통해 곡이름을 알 수 있습니다.

참고로, 해당 부품의 가격은 개당 400~500원 사이의 가격정도 되더군요. 사실 실험을 하기 위해서 약 500원짜리 부품을 구매하기 위해서 택배로 주문 하는 것 자체가 너무 낭비인 것 같아서 실제 구매해서 실험을 해보고 싶었지만 아쉽게 못해봤네요.

2. 멜로디 트렌지스터 회로도


데이터시트에서 그림1번 회로도는 아래 그림과 같습니다.


위 그림의 회로도를 아두이노우노와 연결하면 다음과 같습니다.


이렇게 3번핀을 스위치버턴의 역활을 수행하면 됩니다. 참고로 아두이노우노의 출력값은 0~5V입니다. 그래서 5V로 전류를 보내기 보다는 멜로디 IC은 "1.5V~4.5V" 전류를 맞춰야 하기 때문에 저항을 붙여주셔야 겠죠.

3. 코딩


void setup() {  
  pinMode(3, OUTPUT);  
}
void loop(){
 digitalWrite(3, HIGH);  //3초 멜로디 연주 시작
 delay(3000);
 digitalWrite(3, LOW);  //2초 멜로디 중단
 delay(2000);
}

3번핀이 스위치 버턴 역활을 하기 때문에 결과는 3초동안 멜로디 IC의 곡이 연주되고 2초동안 멜로디 연주가 중단됩니다. 간단히 동작 테스트입니다. 실제 부품으로 실험 할 때는 한곡의 시간을 측정하시고 그 시간만큼 delay를 주고 멜로디를 연주해 보세요.

결과는 사실 멜로디 IC가 없기 때문에 확인 할 수 없습니다. 하지만 대충 동작 느낌은 아시겠지요.

4. 비슷한 예제(과제)



거리경보장치 응용 post에 가시면 초음파센서를 이용하여 장애물 감지가 되면 경보음이 울리는 예제입니다. 이 예제에서 수정하는 부분은 피에조부저 부분에 멜로디 IC을 연결하시면 됩니다.


위 회로도에서 화살표가 가리키는 녹색선에다가 멜로디 IC를 연결하시면 됩니다. 코딩은 post에서 거리에 따른 멜로디가 연주가 되어야 하는데 이 경우는 특정 거리일 때 연주가 되어야 합니다.

그러면 연주시작/중지는 특정 거리를 기준으로

  if(distance < 50)  digitalWrite(11, HIGH);  
  else  digitalWrite(11, LOW);  

이렇게 11번이 스위치 연할을 담당하여 초음파센서로 측정한 거리를 기준으로 전류를 공급과 차단을 통해서 제어를 하면 됩니다.

간단히 설명했지만 실제로 멜로디 IC가 있으시면 직접 제작해 보세요. 위 회로도와 똑같이 만들 필요가 없습니다. 대충 거리경보장치의 동작 원리와 비슷한 실생활의 유사한 장치를 찾으시고 그 모습을 아두이노로 표현하시면 됩니다.

마무리


간단히 멜로디 IC를 통해 특정곡이 연주하는 방법을 살펴 보았습니다. 원리를 간단합니다. 2번핀에 전류를 공급하고 3번핀에 멜로디 신호가 나오고 3번핀에 스피커를 연결하면 멜로디 IC에 담겨진 곡이 연주가 됩니다.

오늘 내용은 별거 없습니다. 사실 실생활의 멜로디 IC는 아두이노와 같은 곳에다 프로그램을 이식해서 사용하기 보다는 하드웨어적 전류의 상태를 이용하여 동작하게 합니다. 즉, 스위치 부분의 특정 센서나 제어 부품을 부착하고 타이머관련 부품을 연동하여 전류를 일정시간동안 유지하게 해서 멜로디가 흘러나오게 하는 경우가 많습니다. 구지 아두이노에 연결하여 동작하는 실험을 한 이유는 확장성 때문입니다. 사실 하드웨어적으로만 제어하면 여러가지의 부품을 연동 할 때 회로도를 구성하기가 복잡합니다. 비전문가분들은 사실 회로도를 만드는 자체가 어렵습니다. 하지만 이 부품을 아두이노에 연결하면 아두이노의 핀값을 프로그램을 통해 제어하기 때문에 오늘 배운 멜로디 IC 뿐아니라 다른 부품과의 연동에서 쉽게 회로도를 만들 수 있습니다. 오늘 내용는 아두이노 3번핀을 멜로디 IC의 스위치 버턴 역할을 수행할 수 있다는 것은 별거 아닌 내용이지만 무척 중요한 내용입니다. 예로 감지센서값을 아두이노에서 읽고 그 값을 기준으로 멜로디 IC를 작동 시킨다고 생각해보세요. 경보기, 알람기 등을 아두이노로 만들 수 있겠죠. 회로도도 사실 각 부품을 아두이노 핀에 연결만 하면 쉽게 만들 수 있습니다.

제가 지금까지 post한 것 중에 한번 이 멜로디 IC를 결합하여 만들 만 한 것이 있는지 한번 찾아보세요. 순간 떠오른 것은 아두이노 RC카에다가 후진할 때 멜로디가 연주되게 하면 자동차 후진음과 같과 같은 형태로 재밌게 만들 수 있을 것 같네요. 하지만 부품이 없어서 실제로 실험을 못하는게 아쉽습니다. 나중에 기회가 되면 한번 구매해서 실험해 보고 싶네요.

댓글()

[아두이노] 뽑기기계 횟수 락 원리

IOT/아두이노|2019. 7. 29. 09:00

[아두이노] 뽑기기계 횟수 락 원리



오늘은 횟수 락이 걸린 뽑기기계에 대한 아두이노시각으로 그 원리를 이해하는 시간을 가져보도록 하죠. 꼭 이렇게 뽑기 기계가 만들어진게 아니고 비슷한 느낌으로 아두이노에서 표현하여 살펴보도록 하죠.

1. 뽑기기계의 가상 설정



대충 위그림과 같은 뽑기 기계가 있다고 가정해 봅시다. 화살 막대기가 좌에서 우로 이동하면서 사각구멍 위치에 정확히 멈출 때 상품을 꺼낼 수 있게 됩니다. 이런류 게임을 해보신 분들은 뽑기기계가 락이 걸려서 일정 횟수 이상 돈을 넣고 도전을 해야 뽑을 수 있게 되어 있다는 것을 유튜버들의 뽑기영상을 보시면 간접 체험을 여러분들은 해보신 분들이 많을 꺼에요.

유튜버가 하는 말이 정확히 멈췄는데 약간 락이 풀릴 때까지 계속 멈춘 위치에서 좀더 이동해서 멈춘다고 이야기를 하는 소리를 많이 들었을 꺼에요. 계속 정확히 멈추는데 밀린다고 락이 걸려서 일정 횟수 이상 계속 해야지 뽑을 수 있다고 뽑기 락에 이야기를 많이 들었을 꺼에요.

여기서 횟수 락은 도대체 어떻게 하는 것일까 아두이노 시각으로 관찰하고 유사한 실험을 해볼까 합니다.

[특징]

  • 일정 횟수가 안되면 못 뽑는다.
  • 부정확한 위치에서는 그상태로 멈추지만 뽑는 위치에 멈추면 약간 밀리는 느낌으로 좀 더 이동한다.

이 특징을 가지고 이제 실험을 해 보겠습니다.

2. 횟수 락 회로도


  • 준비물 : Servo모터 1개, 스위치 버턴 2개, 아두이노 우노
  • 내용 : 스위치버턴은 인터럽트함수를 이용하기 위해 2,3번에 연결하고 Servo모터는 7번에 연결하시오.

실제 뽑기 기계에는 Stepper Motor로 움직이지만 가상시뮬레이터에서 실험을 간단히 하기 위해서 Servo모터로 대신 실험을 합니다.


횟수 락을 실험하기 때문에 스위치 버턴 2개로 Servo Motor를 제어하하면서 회전을 하는데 특정 각도에 범위에 대한 횟수 락 실험을 하게 됩니다. 그래서 복잡한 회로도 설계를 할 필요가 없고 이 회로도만으로 충분히 횟수 락 실험이 가능합니다.

3. 코딩


[설계]

  • 3번 스위치 버턴은 게임 시작
  • 2번 스위치 버턴은 멈춤(정지)
  • 2번 스위치 버턴을 눌러 멈춘 위치가 상품 범위에 들 때 횟수 카운트 하기
  • 횟수 카운트 할 때 상품 뽑기 횟수가 부족하면 멈춤 위치를 좀더 이동 시킴
  • 상품 뽑기 횟수에 도달하면 멈춤 위치가 상품을 뽑는 위치면 성공

스위치 버턴 동작
3번 스위치 버턴은 뽑기 시작버턴이고 2번 버턴은 뽑기 멈춤 버턴입니다. 인터럽트 함수를 이용하기 때문에 코딩은 다음과 같습니다.

const byte interruptPin1 = 3;
const byte interruptPin2 = 2;
boolean stateStart = false;
boolean stateStop = false;

void setup()
{
  pinMode(interruptPin1, INPUT_PULLUP);
  pinMode(interruptPin2, INPUT_PULLUP);  
  attachInterrupt(digitalPinToInterrupt(interruptPin1), exchange1, FALLING);  //인터럽트 발생 시 호출
  attachInterrupt(digitalPinToInterrupt(interruptPin2), exchange2, FALLING);  //인터럽트 발생 시 호출
}
void loop(){

  if(stateStart==true){
    뽑기시작;   
  }
 if(stateStop==true){ //뽑기정지
    뽑기정지;
 }
}
void exchange1() { //인터럽트스위치버턴  이벤트1
  stateStart=true;  
  stateStop=false;
}

void exchange2() { //인터럽트스위치버턴  이벤트2
  stateStart=false;
  stateStop=true; 
}

위 코딩을 보시면 stateStart, stateStop 변수의 상태 값으로 두가지 뽑기시작과 뽑기정지 버턴의 동작을 수행하게 됩니다.

뽑기시작 코딩은
Servo모터의 회전을 시작하도록 코딩이 이뤄집니다.

Servo servo;
const byte servoPin = 7; 

void setup(){
  servo.attach(servoPin);   
  servo.write(0);
  delay(1000);
}

위의 코딩은 Servo모터 초기상태입니다. 회전이 시작되기전 0도의 위치가 시작지점입니다.

0~180도로 왔다 갔다 회전을 하도록 뽑기시작 명령을 코딩하면 다음과 같습니다.

  if(stateStart==true){ //뽑기시작
    val+=stateNum;
    Serial.println(val);

    if(val==180)stateNum = -1;
    if(val==0)stateNum = 1;    
    
    servo.write(val);
    delay(50);
  }

val은 회전각입니다. stateNum은 180도가 되면 -1로 바뀌고 0도가 되면 1로 바뀌게 if문으로 제어해서 0~180도 회전이 1도씩 움직이게 됩니다. 이렇게 해서 뽑기가 시작되는 걸로 간주하시면 됩니다.

뽑기정지 코딩은
뽑기가 시작되면 이제 Servo모터를 정지시켜야 합니다. 뽑을 각도에 멈춰야 상품을 뽑을 수 있겠죠. 여기에 뽑기 횟수 락을 코딩을 하게 됩니다.

횟수 락은

    if(val>95 && val<105){ //뽑기 성공각도
      if(cnt<2){ //cnt 횟수 락
        cnt++;
        val=106;      
      }
      else cnt++;
    }
    else if(val>80 && val<120){ //상품 A의 횟수 증가 범위
      cnt++;
    } 
        servo.write(val);
    delay(100);

뽑기 성공하는 각도 범위는 95~105도 사이가 되면 뽑기가 성공됩니다. 하지만 횟수 락으로 cnt변수값이 3회이상이여야지만 뽑을 수 있게 락이 걸리게 됩니다. 즉, 아무리 95~105도 사이에 멈추더라도 cnt(횟수) 3회 미만이 되면 강제적으로 106도 각도로 움직이게 하여 약간 밀리는 느낌으로 뽑기 실패가 됩니다.

여기서 여러개의 상품이 있는데 A상품을 뽑는다고 가정한다면 A상품에 대한 뽑기 횟수가 증가해야 겠죠. B상품은 B상품에 대한 카운터를 해야 됩니다. 그렇기 때문에 A상품에 대한 횟수 락을 카운트 할 범위를 지정해 줘야 합니다. 위 코딩은 cnt는 A상품으로 여기면 됩니다.

만약, 여러개의 상품을 한다면 cntA, cntB, cntC 이런식으로 횟수 증가 범위를 개별적으로 증가시켜서 코딩하시면 됩니다.

아무튼 위 코딩으로 횟수락을 완성했네요. 3회 이상이 되지 않는 다면 아무리 완벽하게 성공 범위 지점에 정지시켜도 회적각이 밀리게 됩니다. 뽑기가 어떤 느낌인지 아시겠지요.

종합해보면,

#include <Servo.h>

Servo servo;
const byte servoPin = 7; 
const byte interruptPin1 = 3;
const byte interruptPin2 = 2;

boolean stateStart = false;
boolean stateStop = false;

int val = 0;
int cnt = 0;
int stateNum = 1;

void setup()
{
  Serial.begin(9600);
    
  pinMode(interruptPin1, INPUT_PULLUP);
  pinMode(interruptPin2, INPUT_PULLUP);  
  attachInterrupt(digitalPinToInterrupt(interruptPin1), exchange1, FALLING);  //인터럽트 발생 시 호출
  attachInterrupt(digitalPinToInterrupt(interruptPin2), exchange2, FALLING);  //인터럽트 발생 시 호출
  
  servo.attach(servoPin);   
  servo.write(0);
  delay(1000);
}

void loop()
{
  if(stateStart==true){ //뽑기시작
    val+=stateNum;
    Serial.println(val);

    if(val==180)stateNum = -1;
    if(val==0)stateNum = 1;    
    
    servo.write(val);
    delay(50);
  }
  
  if(stateStop==true){ //뽑기정지
    if(val>95 && val<105){ //뽑기 성공각도
      if(cnt<2){ //횟수 락
        cnt++;
        val=106;      
      }
      else cnt++;
    }
    else if(val>80 && val<120){ //상품 A의 횟수 증가 범위
      cnt++;
    }    
    servo.write(val);
    delay(100);
    
        //결과
    Serial.print("End Val : ");
    Serial.println(val);
    Serial.print("Count : ");
    Serial.println(cnt);
    if(cnt>2 && val>95 && val<105){ //성공 메시지와 cnt 초기화
      Serial.println("success!");
      cnt=0;
    }
    else Serial.println("failure!"); //실패 메시지
    
    //초기화
    val=0;
    servo.write(val);
    delay(200);    
    stateStop=false;    
  }  
}

void exchange1() { //인터럽트스위치버턴  이벤트1
  stateStart=true;  
  stateStop=false;
}

void exchange2() { //인터럽트스위치버턴  이벤트2
  stateStart=false;
  stateStop=true; 
}

4. 결과


원래 뽑기 성공범위는 좀 더 좁은 범위로 해야 하는데 녹화를 딱 맞게 하기 위해서 성공 범위를 좀 넓게 지정했네요. 횟수 락의 카운트 값은 2입니다. 3회부터서 성공 범위에 들면 뽑기 성공이 됩니다. 성공 범위가 아닌 뽑기 범위면 카운트만 세고 성공 범위인데 횟수가 부족하면 강제적으로 성공 범위를 벗어나게 하는 동작 소스입니다.

횟수 락에 의해서 성공 범위인 103각도에 멈췄지만 106도 강제적으로 이동시켜 실패하게 만들었네요.


횟수 락이 풀리고 성공 범위인 97각도에 멈춰고 97각도로 최종적으로 완료 되어 3회만에 성공이 되었네요.


만약에 3회 이후에 성공 범위에서 멈추지 못했다면 계속 성공할 때까지 도전해야 합니다.

아래 도전 영상을 올려 놓았습니다.


마무리


원래는 다른 post를 준비 중이였는데 문득 뽑기 횟수 락이 떠올라서 작성 중인 post를 멈추고 뽑기 횟수 락 실험을 하게 되었네요. 실제 뽑기가 이렇게 코딩이 되어졌다는 의미는 아니고요 관찰을 통해서 대충 이런 느낌이 아닐까 하고 그 느낌을 코딩화 했을 뿐입니다.
아두이노는 재밌는 것은 주변 환경을 관찰하고 그 관찰된 현상을 코딩화를 쉽게 할 수 있다는 점입니다. 최근 post들이 주변에 접할 수 있는 한번쯤은 경험했던 상황들을 아두이노적 시각으로 실험하고 있는데 여러분들도 이런 감각을 배우셨으면 합니다.


댓글()

[아두이노] 구부림(flex) 센서와 관절 제어

IOT/아두이노|2019. 7. 26. 09:00

[아두이노] 구부림(flex) 센서와 관절 제어



지난 시간까지 주변 사물을 관찰하는 실험을 하였는데 문득 손을 보고 손의 움직임을 관찰하다가 한번 아두이노 시각으로 표현하고 싶은더군요. 그래서 손가락 관절을 제어하는 실험에 대해 상상하다가 예전에 공부했던 손가락의 움직임을 감지 할 수 있는 센서가 떠올랐네요. 실제 구부림 센서가 없어 실제로 실험은 못하지만 구부림 센서을 대해 간단히 이야기만 하고 가상시뮬레이터로 구부림 센서를 대신 할 부품으로 가변저항을 이용하여 가상시뮬레이터에서 손가락 관절 제어 실험을 해 보겠습니다.

1. 구부림(flex) 센서


구부림 센서는 구부림의 정도에 따라서 저항값이 달라집니다. 이 값을 통해서 구부림의 각도를 계산해 낼 수 있습니다.

int val = analogRead(A0);
int angle = map(val,입력최소값,출력최대값,0,90);

위와 같이 설정하면 구부림 값에 따라 0~90도의 각도를 만들어 낼 수 있습니다. 이 값을 통해서 Servo모터의 회전 시킬 수 있습니다.

2. 손가락 관절 제어


관철의 관찰 대상은 인간의 손으로 하겠습니다.


위 사진을 보시면 집게손가락을 보시면 세개의 관절이 있습니다. 이 부분의 동작을 관찰해 볼까요.

이제 주먹을 쥐어 볼까요.



자세히 보시면 각 마디의 관절이 약 90도 각도로 회전되어 있는 걸 보실 꺼에요.

이 관찰을 통해서 여러분들은 아두이노시각으로 표현한다면 뭐가 있을까요. 각도를 제어할 수 있는 Servo모터가 떠오르지 않나요.

집게손가락의 관절의 각도를 손이 쫙 펴진 상태에서 0도라면 주먹이 쥐어지는 과정은 서서히 3개의 관절의 각도를 증가시켜 90도 회전을 하게되면 주먹이 쥐어지는 형태가 됩니다. 이 원리를 잘 이용하게 손가락의 관절 제어를 할 수 있게 되겠죠.

오늘 다루게 되는 구부림센서를 이용하여 구부림의 각도값을 통해 3개의 관절(Servo모터)을 이용하면 손가락을 움직이게 할 수 잇겠죠.

이제 좀 더 자세히 알아보도록 하죠.

3. 구부림센서를 이용한 손가락 관절 제어


  • 준비물 : Servo모터 3개, 구부림센서, 저항 1m옴, 아두이노 우노
  • 내용 : A0는 구부림센서값을 읽기 위한 핀이고, Servo모터는 5,6,7핀을 이용한다.

실제 구부림 센서가 없기 때문에 회로도만 간단히 소개합니다.


위 회로도로 만들게 된다면 구부림의 정도에 따라서 3개의 Servo모터를 회전시키면 손가락 관절을 회전시키는 결과를 얻게 됩니다.

4. 가상시뮬레이터에서 손가락 관절 제어 실험


  • 준비물 : Servo모터 3개, 가변저항 1개, 아두이노 우노
  • 내용 : A0는 구부림센서값을 읽기 위한 핀이고, Servo모터는 5,6,7핀을 이용한다.

가상시뮬레이터에서는 구부림 센서는 없습니다. 예전에 post로 이야기 했듯이 없는 부품은 유사한 같은 느낌의 부품을 이용해서 실험을 하라고 했죠. 이번에는 가변저항을 이용하여 구부림 센서의 값을 대신하면 유사한 실험을 할 수 있게 됩니다.

위 회로도에서 보는 것처럼 가변저항의 Vcc, Gnd 핀을 연결하면 가변저항을 회전시키면 1023~0의 아날로그 신호의 변화가 일어나고가 발생합니다. 반대로 연결하면 0~1023의 아날로그 신호 변화가 일어납니다. 이 부분을 주위해주세요.

5. 코딩


손가락 과절 제어는 가상시뮬레이터에서 가변저항의 값을 각도로 바꾸기 위해서는 1023~0의 아날로그 신호값을 0~90도의 각도값을 만들고 그 각도만큼 회전시키면 됩니다.

int angle = map(analogRead(A0),0,1023,0,90);  

map()함수로 0~1023의 아날로그값을 0~90의 각도값으로 변환시킵니다. angle값으로 Servo모터를 회전시키면 됩니다.

servo1.write(angle);     
servo2.write(angle);     
servo3.write(angle);     

이렇게 가변저항을 통해 얻은 회전각을 3개의 Servomotor의 회전값으로 실행시켜면 자연스럽게 손가락 관절을 움직이게 됩니다.

종합해보면,

#include <Servo.h>

Servo servo[3];
const byte servoPin[3] = {5,6,7};

void setup()
{ 
  for(int i=0;i<3;i++){
    servo[i].attach(servoPin[i]); 
    servo[i].write(0);
  }  
  delay(1000);
}

void loop()
{  
  int angle = map(analogRead(A0),0,1023,0,90);  
  for(int i=0;i<3;i++){
    servo[i].write(angle);     
  }
  delay(50);
}

손가락 관절 제어라고 했는데 거창한 코딩을 기대하고 post를 보셨다면 크게 실망하셨을 거라 생각됩니다. 이걸로 손가락 관절이 제어가 되는지 의구심을 갖을 수 있겠죠.

시뮬레이터 결과로 간단히 설명 드리겠습니다.

6. 결과





위 결과를 보면 저게 손가락 관절이야 하실 꺼에요. 손가락 위치로 한번 재 배치 해 볼까요.


위 그림이 어느정도 회전에 대한 손가락 관절을 이해하셨나요.


7. 구부림 센서의 한계


구부림 센서는 손가락 한개의 구부림 각도 한개만 만들어 냅니다. 즉, 3개의 관절을 같은 각도로 제어하기 때문에 단순한 움직임만 만들어 냅니다. 정교한 손가락 움직임을 만들어 낼 수 없습니다. 손가락 3개의 관절 중 2개의 관절만 구부려도 구부림 센서는 이 값을 가지고 3개의 관절을 동일한 각도로 회전시키는 문제가 발생합니다.

처음에는 구부림 센서와 같은 부품으로 간단히 손가락을 움직이게 하고 나중에 능력이 된다면 근육센서로 신경값을 분석하여 손가락을 움직이는 실험을 해보세요. 나중에 기회가 된다면 꼭 해보고 싶은 실험입니다. 문제는 근육센서가 비싸고 단순히 Servo모터로 손가락을 제어하는것도 좀 부자연 스럽고 어느정도 로봇손에 관련된 고급 모터 부품을 쓴다면 재미로 실험하기에는 가격 부담이 크네요.

마무리


구부림 센서를 이용한 손가락 관절 제어는 구글검색을 하시면 쉽게 post들을 찾아 볼 수 있습니다. 대개 장갑에 구부림 센서를 부착하여 장갑을 낀 상태로 주먹을 쥐면 주먹을 쥔 각도만큼 구부림 센서는 구부러지고 그 구부림 각도만큼 아두이노에서 센서값을 읽고 회전각을 만들어서 구부린 각도만큼 회전을 시키면 실제 센서가 부착된 장갑에서 손동작과 유사하게 로봇손을 움직이게 할 수 있게 됩니다.

실제로 Servo모터가 16개가 있고 다수 Servo모터를 컨트롤 할 수 있는 모터쉴드랑 다섯개의 구부림 센서가 있다면 로봇손 전부를 움직이게 하는 실험을 할 수 있을 꺼에요. 하지만 제가 가지고 있는 부품이 Servo모터 1개뿐이라 실제 관절 실험을 못해 봤네요. 하지만 가상시뮬레이터로 어느정도 유사한 실험을 할 수 있는 것에 만족합니다.

댓글()

[아두이노] 아두이노 주차장 출입구 차량차단장치

IOT/아두이노|2019. 7. 25. 09:00

[아두이노] 아두이노 주차장 출입구 차량차단장치



일상을 주제로 주변에 상황을 아두이노로 표현하는 실험을 계속 진행 중입니다. 오늘은 아파트나 일반 주차장 또는 특정 건물에 차량을 진입하는 입구 쪽에 차량 차단장치가 설치되어 있는 곳들이 많습니다. 이 차량 차단장치를 아두이노적 시각으로 한번 실험을 해보도록 하겠습니다.

1. 주차장 출입구 차량차단장치 설정



위와 같이 상황은 근접감지센서를 다른 것을 이용해야 하는데 실험에서는 초음파센서를 이용하여 근접감지를 하도록 상황을 만들고 차량이 출입구 쪽으로 다가오면 근접감지가 되고 그 감지되었을 때 차량차단막기 올라가게 됩니다. 차량차단막이 내려져 있을 때는 Red LED에 불이 들어와 있는 상태이고 차단막이 올라가면 Green LED에 불이 들어오고 차량이 통과된다는 설정입니다.

2. 아두이노 주차장 출입구 차량차단장치 회로도


  • 준비물 : 적색 LED 1개, 녹색 LED 1개, 저항 220옴 2개, 초음파센서, Servo모터, 아두이노우노
  • 내용 : 8,9번핀은 LED에 연결하고 초음파센서핀은 7번, Servo모터은 6번 핀에 연결하시오.

실제 초음파센서는 4핀이지만 가상시뮬레이터에서는 3핀 초음파센서로 7번핀으로 Trig, Echo 역활을 수행합니다.


회로도는 복잡하지 않죠. 위 회로도는 각 역활은 아래 그림으로 이해하시면 되겠습니다.


3. 코딩



사전학습으로 Servo모터와 초음파센서에 대해서 한번 읽고 오세요. 사용하실 수 있는 분들은 그냥 넘어가셔도 됩니다.

[설계]

  • 초기신호등은 적색LED에 불이 들어온 상태가 되면 차량 정지를 의미한다.
  • 초음파센서로 근접거리가 일정거리이하가 되면 차량감지로 간주한다.
  • 차량감지가 되면 차량차단막이 올라간다는 느낌으로 Servo모터를 90도 회전한다.
  • Servo모터가 90도 회전이 되었다가 간주한 시점에 "적색->녹색" 등으로 변환시킨다.
  • 차량은 통과하는데 통과가 완료되었다고 생각되는 감지 시간을 기준으로 차단만은 일정시간 유지한다.
  • 마지막 차량 감지된 시간을 기준으로 일정시간 차단막이 올라가 있다가 차량이 통과했다고 생각 되는 시간에 차단막이 내려온다는 느낌으로 Servo모터를 0도로 회전시킨다.
  • 0도 회전을 할 때 "녹색->적색"등으로 변환시킨다.(정지)

차량감지

거리를 측정은 위 초음파센서 post의 거리 계산를 함수로 거리값을 구하게 됩니다.

int CalDistance (int Pin){  //초음파센서(3핀) 예제를 그대로 외부함수로 빼냄
  pinMode(Pin,OUTPUT); //출력모드로  사용
  digitalWrite(Pin, LOW); 
  delayMicroseconds(2); 
  digitalWrite(Pin,HIGH); 
  delayMicroseconds(10); 
  digitalWrite(Pin,LOW);   
  
  pinMode(Pin,INPUT);    //입력모드로 사용
  int duration = pulseIn(Pin, HIGH);  
  int distance = duration / 57.5;  //가상시뮬레이션의 오차율을 줄이기 위해 이걸로 테스트 함.
  return distance; //거리값 리턴
}

거리를 구하게 되면 차량이 감지판정은 if문으로 만든다.

if(m_distance<50){ //50cm미터 미만일 때 차량 감지로 간주             
  차량감지 후 동작;
}

차량이 감지 후 동작은

  if(m_distance<50){   //50cm미만일 때 차단막 올리기 동작       
    if(state==false){  //초기값은 state=false상태로 처음에는 이 if조건문이 무조건 참이 된다.
      servo.write(90); //감지 되었기 때문에 Servo모터 90도 회전
      delay(2000);   //2초 동안 강제로 Servo모터가 회전하도록 설정
      state=true;     //다시 이 if조건문을 수행할 필요가 없기 때문에 state=true 설정     
      digitalWrite(rPin, LOW); //차단막이 올라갔으니 적색등 끄기
      digitalWrite(gPin, HIGH); //차량 통행하도록 녹색등 켜기
    }
    timeVal=millis(); //마지막 차량 감지된 시간을 저장함
  }  
  if(state==true){ //차단막 내리기 동작
    if(millis()-timeVal>=2000){ //마지막 차량 감지된 시간을 기준으로 차단막 유지시간이 지났는가 체크
      servo.write(0); //차단막 내리기
      state=false; //차단막이 내려졌으니 이 if조건문을 수행할 필요가 없기 때문에 state=fasle 설정
      digitalWrite(gPin, LOW); //차단막이 내려오니깐 녹색등은 끄기
      digitalWrite(rPin, HIGH); //차량 정지하도록 적색등 켜기
    }    
  } 

주석을 일일히 다 달아놓았으니깐 주석을 살펴보시기 바랍니다. 왜! if문을 state의 상태값으로 두가진 차단막 올리기/내리기 동작을 구분하였을 까요. 두 동작을 구분을 지어서 동작하게 하기 위해서요. 자세히 보시면 만약 차단막이 올라간 상태에서 계속 현재 차량이 감지되거나 계속 새로운 차량이 들어올 경우는 계속 차단막 올리는 명령을 내려야 합니다. 이미 올라갔는데 구지 반복 명령을 내릴 필요는 없이 현상태만 유지하면 됩니다. 그래서 if문으로 올라갔으면 내려가는 상황만 체크하면 되지 올라가는 상황을 동작할 필요없는 없습니다. 여기서, timeVal은 올라가는 상황 코딩 안에 다 넣지 않고 밖에다 빼낸 이유는 계속 초음파센서로 감지했을 때 마지막으로 감지된 시간을 기준으로 차단막의 유지시간을 결정하게 하기 위해서 입니다.

종합해보면,

#include <Servo.h>

Servo servo;
const byte servoPin = 6;
const byte pingPin = 7;
const byte gPin = 8;
const byte rPin = 9;
int m_distance=0;
boolean state = false;
unsigned long timeVal=0;

void setup()
{
  pinMode(gPin, OUTPUT);
  pinMode(rPin, OUTPUT);  

  servo.attach(servoPin); 
  servo.write(0);
  delay(1000);
  
  digitalWrite(rPin, HIGH);
}

void loop()
{
  m_distance=CalDistance(7);  //초음파센서로 거리계산함수  
  if(m_distance<50){            
    if(state==false){
      servo.write(90);
      delay(2000); 
      state=true;          
      digitalWrite(rPin, LOW);
      digitalWrite(gPin, HIGH);
    }
    timeVal=millis();
  }  
  if(state==true){
    if(millis()-timeVal>=2000){
      servo.write(0);
      state=false;
      digitalWrite(gPin, LOW);
      digitalWrite(rPin, HIGH);
    }    
  }  
}

int CalDistance (int Pin){  //초음파센서(3핀) 예제를 그대로 외부함수로 빼냄
  pinMode(Pin,OUTPUT); //출력모드로  사용
  digitalWrite(Pin, LOW); 
  delayMicroseconds(2); 
  digitalWrite(Pin,HIGH); 
  delayMicroseconds(10); 
  digitalWrite(Pin,LOW);   
  
  pinMode(Pin,INPUT);    //입력모드로 사용
  int duration = pulseIn(Pin, HIGH);  
  int distance = duration / 57.5;  //가상시뮬레이션의 오차율을 줄이기 위해 이걸로 테스트 함.
  return distance; //거리값 리턴
}

loop()함수 내 로직만 설계에 맞게 코딩만 하면 오늘 코딩의 전부입니다. loop()함수 내 코딩은 몇줄 안되지만 설계의 내용을 다 포함되어 있네요. 글보다 코딩이 더 짧은 케이스네요.

4. 결과


가상시뮬레이터에서 결과입니다.


5. 실제 테스트


정상적으로 동작하는지 실제로 구현해 봤네요.

1) 아두이노 주차장 출입구 차량차단장치 회로도


4핀짜리 초음파센서로 대충 실제 제작 모습과 동일하게 디자인 했네요.


위 회로도를 실제 배치하고 선을 연결하니깐 아래와 같은 모습으로 좀 지져분하게 되었습니다.


2) 코딩



위의 가상시뮬레이터 코딩에서 newPing 라이브러리를 사용해서 그부분만 수정했습니다. 그리고 초음파센서가 4핀이니 trig, echo 핀을 정확하게 지정해 줘야 합니다.

코딩은 가상시뮬레이터 소스에서 newPing 라이브러리를 이용한 방식과 4핀 초음파센서 부분만 수정하면 됩니다.
그리고 주의할 점은 실제 초음파센서에서는 거리 측정값이 오류가 발생합니다. 이 오류를 해결하기 위해서 초음파센서값에 대한 평균값을 구해서 오류값을 막을 수 도 있지만 간단하게 오류값을 무시하는 방식을 취했습니다.

  if(m_distance>오류값 && m_distance<감지거리){       
      동작;
  }

제가 쓰는 초음파센서는 가끔 0과 10이하의 값들이 찍히네요. 그래서 실험에서는 10이상의 값들만 거리 판정을 내렸네요. 만약 결과가 정상적으로 나오지 않는다면 거리측정값을 시리얼통신을 통해 시리얼모니터로 그 결과를 찍어보시고 여러분들이 원하는 값으로 수정해 주시면 됩니다.

수정해보면,

#include <NewPing.h>
#include <Servo.h>

Servo servo;
const byte servoPin = 7; 
const byte gPin = 8;
const byte rPin = 9;
int m_distance=0; //거리
boolean state = false; //차단막 제어를 위한 상태값
unsigned long timeVal=0;

NewPing sonar(6, 5, 200); // (Trig, Echo. 거리제한)

void setup() {
  
  Serial.begin(9600);
  pinMode(gPin, OUTPUT);
  pinMode(rPin, OUTPUT);  

  servo.attach(servoPin); 
  servo.write(0);
  delay(1000);
  
  digitalWrite(rPin, HIGH);
}

void loop() {  
  delay(50);            
  m_distance=sonar.ping_cm();  //초음파센서로 거리계산함수  
  Serial.println(m_distance);
  if(m_distance>10 && m_distance<20){  //20cm는 의미가 있는 것은 아닙니다.
    if(state==false){
      servo.write(90);
      delay(2000); 
      state=true;          
      digitalWrite(rPin, LOW);
      digitalWrite(gPin, HIGH);      
    }
    timeVal=millis();
  }  
  if(state==true){
    if(millis()-timeVal>=3000){
      servo.write(0);
      state=false;
      digitalWrite(gPin, LOW);
      digitalWrite(rPin, HIGH);
    }    
  } 
}

3) 결과


10cm이하는 차량감지로 판정을 하지 않습니다. 그리고 20cm미만일 때 차량감지 파전을 내렸네요. 스마트폰으로 촬영하기 위해서 거리값을 최대한 줄여서 실험 했네요.


마무리


최근에 계속 아두이노적 시각으로 주변환경을 관찰하면서 하나씩 post 주제를 선정하고 있네요. 지하주차장에 차량차단막이 되어 있는 곳을 들어 갈 때 차량을 감지하고 차단막이 올라가면서 옆에 신호등이 "적색->녹색" 등으로 바뀌는 상황을 보면서 "아! 이걸 주제로 표현해봐야지!"하고 이렇게 실험하게 되었네요.

여러분들도 한번 주변환경을 관찰하고 저처럼 실험을 해보세요.


댓글()

[아두이노] 아두이노 신호등과 횡단보도신호등

IOT/아두이노|2019. 7. 24. 09:00

[아두이노] 아두이노 신호등과 횡단보도신호등



지난 시간에 간단히 아두이노 신호등을 만들어 실험했는데 여기에 횡단보도신호등을 추가하여 같이 제어하는 실험을 해볼까 합니다. 특정 도로 상황을 만들고 그 상황에서 차량신호등과 횡단보도신호등의 동작을 설정하고 그 부분만 회로도로 구성하고 상황에 맞게 동작하도록 코딩을 해보았습니다.

1. 도로의 신호등과 횡단보도신호등의 상황 설정


우선 아래와 같은 도로의 상황을 설정해 보았습니다.


위 그림에서 A, B 지점의 신호등과 횡단보드신호등을 아두이노로 표현하여 실험을 하겠습니다.

상황은 차량신호등은 4차선 도로의 주행이 우선 시 하겠죠. 그러면 2차선에서 바라보는 신호등은 "녹->황->적" 순서에서 적색등이 다른 등보다 더 긴 시간을 유지합니다. 도로의 신호등의 시간은 도로의 상태에 따라 시간이 다르기 때문에 삼거리 도로에서는 4차선 도로쪽 녹색등 시간이 적색등보다 길고 2차선의 적색등이 녹색등보다 더 긴 시간을 유지하게 됩니다. 즉, 2차선 적색등일 때 4차선은 녹색등이니깐 두 신호등의 관계를 잘 이해해 주세요.

횡당보도신호등은 2차선에서 바라 봤을 때 적색등일 때 횡단보도신호등이 작동하게 코딩을 할까 합니다. 대충 2차선으로 바라봤을 때 "녹->황->적"순으로 바뀌는데 적색등으로 바뀔 때 횡단보드신호등의 적색등이 녹색등으로 바뀌고 횡단보드의 7-Segment Display가 카운트를 하게 됩니다. 그리고 0이 되면은 다시 적색등으로 바뀌는 형태로 코딩했습니다.

좀 상황을 설정하고 코딩을 완성하고 보니 상황이 약간 이상하게 설정했더군요. 다시 코딩을 수정하기 귀찮아서 이상태로 실험을 마무리했는데 이부분을 감안하시고 실험 내용을 보시기 바랍니다.

2. 아두이노 신호등+횡단보도신호등 회로도


  • 준비물 : 적색 LED 2개, 오렌지색 LED 1개, 녹색 LED 2개, 저항 220옴 6개, 7-Segment Display 1개, 74HC595칩 1개, 아두이노우노
  • 내용 : 신호등 5,6,7번 핀을 LED 출력핀으로 연결하고 횡단보도신호등 11,12번 핀을 LED 출력핀으로 연결한다. 그리고 74HC595칩은 9,10,11번을 이용하고 7-Segment Display의 Vcc핀을 8번핀으로 사용합니다.

아래 회로도는 지난시간의 신호등 회로도에서 횡단보도신호등을 추가한 회로도 입니다. 두개의 횡단보드신호등을 만들 수 있었지만 너무 복잡하게 그려질 것 같아서 한쪽 횡단보드신호등만 표현했네요. 만약 두개를 만드신다면 횡단보드신호등의 연결된 선을 공유선으로 연결하시면 됩니다.


좀 복잡한 회로도인데 해당 표현은 아래 그림으로 대충 구분하시면 됩니다.


3. 코딩



7-Segment Display 부품을 사용하는데 그냥 아두이노우노 핀에 적접 연결해도 되지만 복습차원으로 74HC595칩을 이용하여 제어하도록 하겠습니다.

74HC595칩 제어 :

  • latchPin, clockPin, dataPin을 pinMode( )로 선언
  • digitalWrite(결쇠핀, 열림/잠금)으로 74HC595칩에 입력할때 열림/잠금을 제어
  • shiftOut(데이터입력핀, 클럭핀, MSBFIRST, 전송값)으로 이걸 통해서 역순으로 데이터가 배치
const byte latchPin = 10; //ST_CP Pin
const byte clockPin = 11; //SH_CP Pin
const byte dataPin = 9; //DS Pin

byte data[]={
0B01000000, //0
0B01111001, //1
0B00100100, //2
0B00110000, //3
0B00011001, //4,
0B00010010, //5
0B00000010, //6
0B01111000, //7
0B00000000, //8
0B00010000  //9
};

위의 74HC595칩을 제어하는 3개의 ST_CP, SH_CP, DS Pin과 숫자는 1byte값안에 각 bit가 7-Segment Display의 각 핀에 해당됩니다. data[]배열변수에 숫자 패턴을 정의해 놓습니다. 0~9까지의 숫자 패턴을 만들어 놓았는데 실제로 0~3까지의 숫자만 사용됩니다.

그 값을 아래 3줄로 해서 출력하면 7-Segment Display의 숫자가 출력합니다.

digitalWrite(latchPin, LOW); //열림
shiftOut(dataPin, clockPin, MSBFIRST, data[cnt]); //출력
digitalWrite(latchPin, HIGH); //닫힘

위 내용은 쉬프트레지스터(74HC595) 제어(아두이노) post에 가셔서 복습하시고 오셔야 이해가 되실 꺼에요. 이해가 안되시는 분들은 읽고 오시면 되겠습니다.

횡단보도신호등 동작 : 7-Segment Display가 "3-2-1-0"순서로 카운트하게 하려면 다음과 같습니다

if(cnt>0 && millis()-timeSeg>=1000){
    cnt--; 
    digitalWrite(latchPin, LOW);
    shiftOut(dataPin, clockPin, MSBFIRST, data[cnt]);
    digitalWrite(latchPin, HIGH);                       
    timeSeg=millis();      
 }

timeSeg변수는 7-Segment Display에 출력에 대한 이전시간값을 가지고 있습니다. ">=1000"으로 체크하면 1초 단위로 if문이 참이 됩니다. 여기서 cnt는 숫자패턴의 위치값이 됩니다. 즉, 1초 단위로 체크하는데 숫자패턴이 0보다 커야한다고 락을 걸어놓은 if문입니다. "cnt--"으로 현재 숫자패턴위치값을 하나 감소 하면서 출력된다고 생각하시면 됩니다.

횡단보도신호등의 "적색->녹색" 변환

 digitalWrite(TransversboardLight[1], LOW); //적색 LOW
 digitalWrite(TransversboardLight[0], HIGH); //녹색 HIGH

횡단보도신호등의 "녹색->적색" 변환

 digitalWrite(TransversboardLight[0], LOW); //녹색 LOW
 digitalWrite(TransversboardLight[1], HIGH); //적색 HIGH

위 두가지 횡단보도신호등 변환은 차량신호등이 적색이 되는 순간의 코딩 부분에 "적색->녹색"변환 코딩을 하고 7-Segment Display가 카운트 0이 되는 순간의 코딩 부분에 "녹색->적색"변환 코딩을 삽입하면 됩니다.

이제 위에서 이야기한 횡단보드신호등 코딩 부분을 지난시간에 만든 차량신호등에 합쳐 봅시다.

[신호등 기본소스]

const byte trafficLight[3] = {5,6,7}; //신호등 핀
int lightTime[3] = {5,2,3}; //신호등 유지시간
unsigned long timeVal = 0; //이전시간
int indexVal = 0; //신호등 위치

void setup()
{
  for(int i=0;i<3;i++){
    pinMode(trafficLight[i], OUTPUT);
  }  
    //초기상태
  digitalWrite(trafficLight[indexVal], HIGH); //녹색등
}

void loop()
{  
  if(millis()-timeVal>=lightTime[indexVal]*1000){ //신호등 동작 trafficLight[3]순서대로
    digitalWrite(trafficLight[indexVal], LOW);  //이전등 끄기
    indexVal++; //신호등위치 증가
    if(indexVal==3)indexVal=0; // 신호등위치가 3이 되면 다시 0으로 처음위치로 돌아감
    digitalWrite(trafficLight[indexVal], HIGH); //새로운등 켜기
    timeVal=millis();
  }
}

어디서 부터 접근해야 할까요. 위 loop()함수는 신호등 "녹색->오렌지색->적색"순서로 깜박입니다. 여기서 적색등이 되는 순간에 횡단보도신호등이 바뀐다고 했죠. 그러면 적색등일 때 "indexVal==2"일때의 횡단보도신호등의 조건문을 만들어 주면 됩니다.

if(millis()-timeVal>=lightTime[indexVal]*1000){ //신호등 동작 trafficLight[3]순서대로
  신호등명령문;
    if(indexVal==2){ //적색등일때
      횡단보도신호등 초기상태;
    }
}

횡단보도신호등 초기상태는

 state=true;
 timeSeg=millis(); //7-Segment Display 이전시간값
 digitalWrite(TransversboardLight[1], LOW); //적색 LOW
 digitalWrite(TransversboardLight[0], HIGH); //녹색 HIGH

 digitalWrite(SegPower, HIGH);  //7-Segment Display 전원 공급
 //3 숫자 패턴 출력
 digitalWrite(latchPin, LOW);
 shiftOut(dataPin, clockPin, MSBFIRST, data[cnt]);
 digitalWrite(latchPin, HIGH);   

대충 indexVal이 2일 때 적색으로 바뀌는 순간으로 그 때 횡단보도신호등의 초기상태를 위와 같이 코딩하게 됩니다. state은 다음 코딩의 체크문으로 사용하는 변수값입니다.

if(state==true){
  횡단보도신호등동작;
}

위와 같이 state문으로 횡단보도신호등을 동작시킬지 결정하는 if문입니다.

횡단보도신호등 동작은

 if(cnt>0 && millis()-timeSeg>=1000){ //"2-1-0"출력함 3은 초기상태에서 출력했기 때문에 나머지 숫자 출력함
    cnt--; 
    digitalWrite(latchPin, LOW);
    shiftOut(dataPin, clockPin, MSBFIRST, data[cnt]); //숫자 출력
    digitalWrite(latchPin, HIGH);                       
    timeSeg=millis();  //이전시간변수에 현재시간 저장    
 }
 if(cnt==0 && millis()-timeSeg>=1000){ //0을 출력하고 1초동안은 0인 상태를 유지하기 위함
    cnt=3; //숫자패턴 3으로 초기화
    state = false; //횡단보도신호등 동작 중단      
        
    digitalWrite(SegPower, LOW); //7-Segment Display 전원 공급 중단
    digitalWrite(TransversboardLight[0], LOW); //녹색 LOW
    digitalWrite(TransversboardLight[1], HIGH); //적색 HIGH
 }

횡단보도신호등의 7-Segment Display의 숫자를 카운트하고 카운트가 끝나면 다시 "녹색->적색" 등으로 바뀌게 됩니다. 횡단보도신호등이 끝나면 다시 처음 원위치 상태로 돌아가야 합니다.

종합해보면,

byte data[]={
0B01000000, //0
0B01111001, //1
0B00100100, //2
0B00110000, //3
0B00011001, //4,
0B00010010, //5
0B00000010, //6
0B01111000, //7
0B00000000, //8
0B00010000  //9
};

const byte latchPin = 10; //ST_CP Pin
const byte clockPin = 11; //SH_CP Pin
const byte dataPin = 9; //DS Pin
const byte SegPower = 8;
const byte TransversboardLight[2] = {12,13};
int cnt = 3;
boolean state = false;

const byte trafficLight[3] = {5,6,7};
const byte lightTime[3] = {5,2,7};
unsigned long timeVal = 0;
unsigned long timeSeg = 0;
int indexVal = 0;


void setup()
{
  pinMode(latchPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(dataPin, OUTPUT);
  pinMode(SegPower, OUTPUT);
  
  pinMode(TransversboardLight[0], OUTPUT);
  pinMode(TransversboardLight[1], OUTPUT);
  
  for(int i=0;i<3;i++){
    pinMode(trafficLight[i], OUTPUT);
  }  
  digitalWrite(trafficLight[indexVal], HIGH);
  digitalWrite(TransversboardLight[1], HIGH);
}

void loop()
{  
  if(millis()-timeVal>=lightTime[indexVal]*1000){
    digitalWrite(trafficLight[indexVal], LOW);  
    indexVal++;
    if(indexVal==3)indexVal=0;
    digitalWrite(trafficLight[indexVal], HIGH);
    timeVal=millis();    
    
    if(indexVal==2){
      state=true;
      timeSeg=millis();
      digitalWrite(TransversboardLight[1], LOW);
      digitalWrite(TransversboardLight[0], HIGH);
      
      digitalWrite(SegPower, HIGH);      
      digitalWrite(latchPin, LOW);
      shiftOut(dataPin, clockPin, MSBFIRST, data[cnt]);
      digitalWrite(latchPin, HIGH);   
    }
  }
  if(state==true){
    if(cnt>0 && millis()-timeSeg>=1000){
      cnt--; 
      digitalWrite(latchPin, LOW);
      shiftOut(dataPin, clockPin, MSBFIRST, data[cnt]);
      digitalWrite(latchPin, HIGH);                       
      timeSeg=millis();      
    }
    if(cnt==0 && millis()-timeSeg>=1000){
      cnt=3;
      state = false;      
      digitalWrite(SegPower, LOW);
      digitalWrite(TransversboardLight[0], LOW);
      digitalWrite(TransversboardLight[1], HIGH);
    }
  }  
}

4. 결과



실제로 제작해 해봤는데 배선이 좀 지져분하게 만들어 졌네요.


결과는 가상시뮬레이터에서 실험한 결과와 같은 동작으로 결과가 출력 되었네요.


마무리


기존의 신호등 코딩에서 횡단보도신호등을 추가했을 때 기존의 코딩에 대해서 신경 쓸 필요 없고 횡단보도신호등에 대한 코딩만 신경쓰면 됩니다. millis()함수를 이용하여 시간을 제어하면 차량신호등의 동작과 횡단보도신호등의 동작을 동시에 수행 할 수 있게 됩니다.

오늘 실험은 신호등과 횡단보드신호등에 상황을 만들고 두 상황을 동시에 처리를 하였습니다. 오늘 코딩한 스타일을 잘 기억해 주세요. 여러 상황을 동시에 처리하거나 또는 여러 부품을 동시에 제어할 때 millis()함수를 이용하여 시간을 제어하면 각 상황을 코딩하고 나서 다른 상황을 코딩할 때 시간에 대한 어려움을 쉽게 해결 합니다.

그리고, 오늘 post에서 2가지 상황을 설정하고 그 상황에 맞게 아두이노에서 동시에 처리하는 코딩을 하였습니다. 여러분들도 꼭 신호등이 아니더라도 여러가지 상황을 만들고 그 상황을 동시에 처리하는 코딩 연습을 많이 하셨으면 합니다. 하나의 부품을 제어하는 것은 무척 싶습니다. 하지만 둘 이상의 부품을 제어하는 것과 둘 이상의 상황을 제어하는 방법은 쉽지 않습니다. 둘 이상의 조건을 만족하기 위해서는 서로 충돌이 발생하지 않게 상상 코딩을 해야 하기 때문에 이 능력은 꾸준히 반복 학습을 통해서 터득 할 수 있습니다. 그렇기 때문에 오늘 제가 했던 방식으로 일상의 상황들을 하나씩 찾아서 적용하여 아두이노로 표현해 보세요.


댓글()

[아두이노] 아두이노 신호등 만들기와 코딩

IOT/아두이노|2019. 7. 23. 09:00

[아두이노] 아두이노 신호등 만들기와 코딩



오늘은 어떤 post를 할까 고민하다가 관찰 실험으로 신호등을 보면서 저걸 주제로 post를 해볼까 하는 생각에 신호등을 주제로 선정했네요. 신호등의 경우는 너무 단순해서 LED 기초편에서 간단히 글로만 설명하고 넘어갔던 것 같은데 이걸 post 해도 좋을지 고민하다가 두가지 코딩으로 간단하게 그냥 LED를 순서대로 on/off 하는 방식과 좀 복잡하지만 delay()함수 없이 LED를 패턴 배열변수로 만들어서 LED를 순서대로 on/off하는 방식으로 코딩하는 원리를 설명할까 합니다. 두 코딩의 차이점이 뭐고 어떻게 코딩하는게 더 나은 코딩인지에 대해서 직접 느껴보셨으면 하는 맘으로 post를 작성 했네요.

1. 아두이노 신호등 기초


신호등을 아두이노로 만들려면 신호등의 동작을 알아야 겠죠. 실험은 3등 신호등으로 간단히 테스트 하겠습니다.


3등 신호등의 순서는 "녹->황->적"순서로 움직입니다. 왜! 이순서로 신호등이 바뀌냐면 차량이 녹색일 때 주행을 하고 바로 적색으로 바뀔 때 정지를 한다고 가정을 해봅시다. 교차로 같은 곳에서 차량이 교차로 안에서 신호등이 바뀌면 그자리에서 정지를 하면 교차로에서 교착상태(Dead lock) 현상이 발생할 수 있습니다. 그래서 이 문제를 해결하기 위해 두 신호등 사이에 황색등을 있습니다. 이 황색등의 의미는 녹색등에 의해 주행되는 차량이 적색등으로 정지하기 전에 황색등으로 교차로에 이미 진입한 차량은 그상태로 계속 주행을 하고 교차로에 진입하려는 차량은 교차로에 진입하지 말고 정지하라는 신호의 의미로 사용됩니다. 즉, 교차로에서 차량이 적색으로 바뀌기 전 현재 녹색등에 의해 주행중인 차량을 통제하기 위한 등이 황색등이라고 생각하시면 됩니다.

그래서, 녹->황->적 순으로 움직입니다.

2. 아두이노 신호등 회로도


  • 준비물 : 적색, 오렌지색, 녹색 LED, 저항 220옴 3개, 아두이노우노
  • 내용 : 5,6,7번 핀을 LED 출력핀으로 연결하시오.


회로도 구성은 간단합니다.

3. 코딩


실제로 신호등 시간을 적용하기는 시간이 너무 길기 때문에 동작 테스트로 녹색 5초, 오렌지색 2초, 적색3초로 가정하고 실험 코딩을 해보겠습니다.

1) 기본 3색 신호등


한개의 신호등 동작

digitalWrite(신호등, HIGH); 
delay(시간);
digitalWrite(신호등, LOW);

이렇게 하면 한개의 등이 켜지고 일정시간 유지했다가 다시 꺼지게 됩니다. 위 동작 명령으로 LED 등이 일정시간 켜졌다 꺼지게 됩니다.

신호등을 순서대로 녹색 5초, 오렌지색 2초, 적색 3초 순으로 반복 깜박이게 하려면 아래와 같이 코딩이 됩니다.

void setup()
{
 pinMode(5, OUTPUT);
 pinMode(6, OUTPUT);
 pinMode(7, OUTPUT);
}
void loop(){
  digitalWrite(5, HIGH); //녹색
  delay(5000);
  digitalWrite(5, LOW);
  digitalWrite(6, HIGH); //오렌지색
  delay(2000);
  digitalWrite(6, LOW);
  digitalWrite(7, HIGH); //적색
  delay(3000);
  digitalWrite(7, LOW);
}

위 코딩은 단순하게 녹색/오렌지색/적색의 LED를 깜박이는 동작입니다. 이걸로 끝내면 신호등 post를 할 이유가 없겠죠. 아두이노로 신호등을 만든다면 위 코딩으로 단순하게 코딩을 하면 안됩니다.

[위 코딩의 문제점]
위 코딩은 순서대로 신호등이 깜박이지만 강제적인 순서대로 진행되고 delay()함수로 해당 LED가 독점 사용되기 때문에 중간에 어떤 부품도 개입할 수 없습니다. 물론 인터럽트함수를 이유한다면 개입은 가능하겠지만 위 코딩은 무조건 LED 신호등 명령구간의 처리시간동안 아두이노를 독점하게 됩니다. "녹색(5초) -> 오렌지색(2초) -> 적색(3초)" 순으로 동작하면서 10초동안은 다른 부품이 개입 할 수 없습니다. 여기서, 다른 부품을 추가하여 제어하려면 복잡해 질 수 밖에 없습니다.

그러면 이 문제를 어떻게 해결해야 할까요. 바로, 예전 post에서 이야기한 delay()함수 없이 delay효과를 부여하는 millis()함수를 이용하면 쉽게 해결 됩니다. 그런데 millis()함수로 delay()함수 효과를 낼 수 있다는 것을 알아도 코딩상으로 어떻게 코딩을 해야 할지 잘 연상이 안 될꺼에요.

어떤식으로 그 효과를 표현하는지 살펴보도록 하죠.

2) delay()함수를 이용하지 않는 신호등


신호등 변수와 신호등 시간 변수 만들기

const byte trafficLight[3] = {5,6,7}; //신호등 핀
int lightTime[3] = {5,2,3}; //신호등 유지시간

이렇게 신호등의 핀과 해당 신호등의 유지하는 시간을 변수로 만든 이유는 loop함수내에서 신호등 동작 코딩을 틀로 만들어 놓고 그 틀에 위 변수로 선언한 부분을 대입하여 순서대로 신호등이 "녹색->오렌지색->적색"으로 on/off되도록 만들기 위해서 입니다.

신호등 동작

void steup(){
  digitalWrite(trafficLight[indexVal], HIGH); //초기 녹색등으로 시작
}
  if(millis()-timeVal>=lightTime[indexVal]*1000){ //신호등 동작 trafficLight[3]순서대로
    digitalWrite(trafficLight[indexVal], LOW);  //이전등 끄기
    indexVal++; //신호등위치 증가
    if(indexVal==3)indexVal=0; // 신호등위치가 3이 되면 다시 0으로 처음위치로 돌아감
    digitalWrite(trafficLight[indexVal], HIGH); //새로운등 켜기
    timeVal=millis();
  }
    

위 동작 코딩을 보시면 처음 setup()함수에서 초기 녹색등으로 시작합니다.

그리고,

 if(millis()-timeVal>=lightTime[indexVal]*1000){
    신호등 동작; 
    timeVal=millis();   
 }

위 if문으로 lightTime[]은 indexVal(위치)값으로 현재 신호등 유지시간을 체크합니다. timeVal(이전시간)값은 현재 신호등이 바뀐 시간값을 저장됩니다. 이런식으로 if문에서 신호등을 바뀐 timeVal(이전시간)값을 기준으로 다음 신호등이 유지되는 시간값을 비교해야 유지시간이 끝나면 다시 신호등이 바뀌는 방식으로 신호등의 유지시간을 순서대로 비교하면서 신호등이 바뀌게 됩니다.

종합해 보면,

const byte trafficLight[3] = {5,6,7}; //신호등 핀
int lightTime[3] = {5,2,3}; //신호등 유지시간
unsigned long timeVal = 0; //이전시간
int indexVal = 0; //신호등 위치

void setup()
{
  for(int i=0;i<3;i++){
    pinMode(trafficLight[i], OUTPUT);
  }  
    //초기상태
  digitalWrite(trafficLight[indexVal], HIGH); //녹색등
}

void loop()
{  
  if(millis()-timeVal>=lightTime[indexVal]*1000){ //신호등 동작 trafficLight[3]순서대로
    digitalWrite(trafficLight[indexVal], LOW);  //이전등 끄기
    indexVal++; //신호등위치 증가
    if(indexVal==3)indexVal=0; // 신호등위치가 3이 되면 다시 0으로 처음위치로 돌아감
    digitalWrite(trafficLight[indexVal], HIGH); //새로운등 켜기
    timeVal=millis();
  }
}

이렇게 하면 해당 신호등이 유지한 시간이 만족 할 때마다 신호등이 "녹색->오렌지색->적색" 등으로 바뀌는 동작을 수행합니다.

이 방식의 코딩은 복잡해 보일 수 있지만 원리를 이해하면 그렇게 어렵지 않습니다. 그리고 단순한 코딩이 가지고 있는 문제점을 해결한 방식으로 신호등이 동작하는 시간 사이에 다른 부품의 다른 동작을 수행하는 명령코딩을 하기 쉽습니다.

4. 결과


첫번째 코딩과 두번째 코딩의 결과는 같기 때문에 두번째 결과만 올려 놓습니다.


5. delay()함수 없이 신호등 만드는 목적


신호등이 "녹색(5초) -> 오렌지색(2초) -> 적색(3초)" 순으로 동작하면서 10초동안의 공백이 사라집니다. 그 사이에 원하는 동작을 수행할 수 있게 됩니다. 신호등 동작을 if문으로 묶어서 신호등이 바뀔 조건이 될 때마다 if문 안에 신호등 on/off 명령만 수행되기 때문에 다른 부품의 loop() 코딩이 자유롭게 됩니다.

즉,

void loop(){

}

위와 같은 상태에서 다른 부품을 코딩한다고 생각하시면 될 듯 싶네요.

여기에 횡단보드 부분을 추가한다면 횡단보드에 대한 동작 코딩만 생각하고 코딩하면 됩니다. delay()함수를 이용하면 기존의 코딩에서는 시간에 대해 민감하지만 millis()함수를 이용한 코딩은 새로운 코딩이 결합했을때 발생하는 delay 문제가 없기 때문에 코딩 결합이 쉬워집니다.

그리고, 변수로 신호등 핀과 신호등 유지시간을 분리해 낸 이유는 배열변수의 들어있는 값은 신호등 패턴입니다. 즉, 패턴을 배열변수로 미리 지정 할 수 있습니다.

const byte trafficLight[3] = {5,6,7}; //신호등 핀
int lightTime[3] = {5,2,3}; //신호등 유지시간

이렇게 패턴을 배열변수로 지정하면 나중에 수정할 때 배열변수의 값만 바꾸면 신호등을 원하는 형태로 동작할 수 있게 자유롭게 변경이 됩니다. 즉, loop()함수를 직접적으로 코딩을 수정할 필요가 없이 신호등 패턴으로 자유롭게 신호등을 제어 할 수 있다는 의미인 셈이죠.

만약에, 4등 신호등 일 경우면 신호등 핀과 신호등 유지시간만 4등 신호등에 맞게 배열변수값을 수정하시면 loop()문에 신호등 동작은 4등 신호등에 맞게 동작하게 됩니다. 어떤 의미인지 아시겠지요.

처음 delay()함수를 이용한 순차적인 단순 신호등 코딩은 그 순간만 편하지만 새로운 부품이 추가되면 그때부터 코딩은 복잡해 집니다. 하지만 두번째 millis()함수는 처음에 변수를 선언하고 패턴을 만들고 신호등 동작 명령도 복잡해 보일 수 있는 코딩이지만 이 코딩은 새로운 부품이 추가 되더라고 코딩은 더이상 복잡해지지 않습니다.

이처럼 코딩하는 방식에 따라서 결과가 달라집니다.

추가로, 오늘 실험한 코딩을 기준으로 교차로 신호등을 업그레이드 해보세요. 교차로 신호등을 코딩해서 보여드리면 오늘 전달하고자 하는 의미가 제대로 전달되지 않고 코딩량만 좀 더 늘어나고 복잡해 보일 수 있기 때문에 단순하게 한개 신호등으로 코딩학습의 내용을 담아 의미만 전달하고자 간단히 표현 했네요.

마무리


오늘은 신호등이라는 주제로 두개의 코딩으로 접근해 보았습니다. 시간에 대한 millis()함수를 이용한 좀 복잡한 코딩으로 신호등을 제어한 코딩을 중심적으로 이해해 주셨으면 합니다. 이 원리는 지금까지 post 했던 글들에서 꽤 많이 사용했던 기법입니다.

if(millis()-timeVal>=1000){ 1초단위
  동작;
  timeVal=millis();
}

이 의미만 제대로 이해하시면 두개 이상의 부품을 사용하실 때 아두이노 코딩이 무척 쉬워 집니다.

그리고 패턴 배열변수의 선언하는 의미도 잘 이유해 주세요. 사실 신호등의 동작 원리는 과거 post의 피에조부저를 이용한 멜로디 만드는 코딩의 원리를 응용해서 신호등으로 표현했습니다.

위 참고 post에 가셔서 피에조부저로 곰세마리멜로디를 만든 코딩 원리를 살펴 봐주세요. 이번 신호등 코딩이 이 원리를 이요해서 만든 응용 코딩인 셈이죠. 변수와 동작처리 코딩 스타일이 동일한 원리입니다. 어떤 것을 응용했는지 살펴보세요.

코딩이라는 것은 재미 있습니다. 어떤 원리를 배우고 이해했을 때 전혀 다른 곳에서 그 원리를 적용하여 결과물을 얻을 수 있다는 것은 코딩을 공부하는 재미 중에 하나입니다. 저는 가끔 다른 분야의 코딩들을 자주 살펴 봅니다. 거기서 얻은 지식이 아두이노에 쓰이는 경우가 종종 있습니다. 물론 아두이노에서 배운 지식이 다른 분야에서 써먹기도 하고요. 이런것들이 코딩의 재미가 아니겠습니까요.

여러분들도 다양한 분야의 코딩들을 시간 날 때마다 기초 코딩이라도 학습하셨으면 합니다. 그래야 코딩의 능력이 향상됩니다.

댓글()

[아두이노] 아두이노 선풍기 만들기

IOT/아두이노|2019. 7. 22. 09:00


[아두이노] 아두이노 선풍기 만들기



오늘은 일상에서의 주제를 찾다가 여름이고 해서 선풍기를 주제로 아두니로를 실험을 해 보았습니다. 선풍기의 동작을 관찰하고 각 특징을 기록하고 그 특징들을 아두이노 시각으로 정리 하여 아두이노 상에서 선풍기의 원리를 최대한 유사하게 표현하여 실험을 진행했습니다. 실제로 제작하지 않았고 가상시뮬레이터에서 여러분들도 같이 체험할 수 있게 가상시뮬레이터에서 실험이 이루어집니다.

자! 아두이노 상에서 선풍기를 한번 실험해 볼까요.

1. 선풍기의 동작 관찰


우선 선풍기를 아두이로 만들려면 선풍기의 특징들을 우선 알아야 합니다.

간단히 선풍기의 특징을 정리해 볼까요.


  • 선풍기는 각 단계별로 바람의 세기를 조절 할 수 있다.
  • 선풍기는 180도 정도의 각으로 좌/우 회전을 시킬 수 있다.
  • 선풍기는 동작을 제어하는 스위치버턴이 있으며 현재 상태를 LED로 표시하는 선풍기가 있다.

3가지 특징으로 접근해 보면, 선풍기의 바람(DC모터)을 단계 별로 세기를 조절하는 스위치버턴와 좌/우회전(Servo모터) 시키는 스위치버턴 그리고 스위치를 누를 때 스위치 상태를 7-Segment Display로 표현하면 대충 선풍기의 느낌으로 제작이 가능해 집니다.

2. 아두이노 선풍기 회로도


  • 준비물 : DC모터, 서보모터, 저항 1K옴 1개, 저항 220옴 1개, npn 1개, 다이오드 1개, 스위치버턴 2개, 7-Segment Display 1개, 아두이노우노, 외부전원
  • 내용 : 5번핀이 DC모터 제어핀으로 사용, 9번핀은 서보모터 제어핀으로 사용, 스위치버턴은 2,3번을 사용, 7-Segement Display 4,6,7,8,10,11,12 핀으로 제어하게 됩니다.


위 그림과 같은 느낌으로 제어를 한다는 가정하에서 회로도를 만들면 다음과 같습니다.


좀 복잡하실꺼에요. 아래 코딩에 사전학습 post들을 보시고 개별적으로 해당 부품에 대한 이해하시고 회로도를 보시면 회로도가 어느정도 이해하실 거라 생각됩니다.

3. 코딩



실험에 사용되는 부품들에 대한 사전학습이 필요합니다.

  • 스위치버턴 - 인터럽트함수를 이용하여 제어하게 됨
  • 7 Segment LED - 선풍기 바람의 세기의 단계를 숫자로 표시
  • 서보모터 : 몸체의 회전
  • DC모터 : 바람의세기 제어

기본적으로 위 내용을 우선 원리를 이해하셨으면 본격적으로 코딩에 들어갑시다.

1)선풍기 바람 세기 제어


DC모터 속도를 제어 합니다.

int motorPin = 5;
...
pinMode(motorPin, OUTPUT); 
...
analogWrite(motorPin, 속도);

DC모터는 5번핀을 이용하고 핀모드는 출력 모드입니다. 그리고 analogWrite()함수로 DC모터의 속도를 조절할 수 있습니다.

0,1,2,3 단계로 4개의 상태로 나눠서 바람의 세기를 제어를 하면 아래와 같습니다.

  switch(step){
    case 0:
     analogWrite(motorPin, 0);
     break;
    case 1:
     analogWrite(motorPin, 85);
     break;
    case 2:
     analogWrite(motorPin, 170);
     break;
    case 3:
     analogWrite(motorPin, 255);
     break;
  }

step은 단계 변수로 step의 값에 따라서 DC모터의 세기가 결정됩니다.

바람의 세기버턴을 누르면 step의 값이 0,1,2,3으로 변환하게 하려면 아래와 같이 코딩하면 됩니다.

attachInterrupt(digitalPinToInterrupt(interruptPin1), exchange1, FALLING);
void exchange1() {
  step++;
  if(step==4) step=0;
    
     switch(step){   
       ... 생략
     }  
}

스위치 버턴은 인터럽트함수를 이용하여 인터럽트 스위치 버턴을 누르면 현재 선풍기의 바람의 세기를 변경할 수 있게 됩니다.

2) 바람의 세기 단계를 7-Segment Display(애노드형) 출력


현재 step 단계를 7-Segment Display로 출력하면 됩니다.

해당 숫자를 출력하기 위해서 0.1.2.3 숫자의 패턴을 만들어야 합니다. 애노드형이니깐 아래와 같이 표현할 수 있습니다.

int segValue[4][7] = {
  {0,0,0,0,0,0,1}, //0
  {1,0,0,1,1,1,1}, //1
  {0,0,1,0,0,1,0}, //2
  {0,0,0,0,1,1,0}, //3
};

이 패턴을 가지고 step의 숫자가 출력되는 위치는 어디 일까요. 바로 스위치가 눌러질 때 그 때가 바로 숫자가 변경되는 시점이 됩니다.

void exchange1() {
  step++;
  if(step==4) step=0;
  for(int j=0;j<7;j++){ //setp 숫자 출력
     digitalWrite(segPin[j], segValue[step][j]);        
  }

3) 좌/우 회전시키기


서보모터로 좌/우 회전을 시키기 위해서는 어떻게 해야 할까요. 대충 0~180도 사이를 왔다 갔다 회전을 시키면 됩니다. 회전 최소 간격을 10도기준 0.1초로 해서 회전시키는 방식을 취했습니다.

    if(millis()-timeVal>=100){
        angle=angle+addAngle; //angle(각) 변화값 
        if(angle==180){ 
          addAngle=-10;
        }
        else if(angle==0){
          addAngle=10;
        }  
        servo1.write(angle); //회전각
        timeVal = millis();
    }

위 코딩을 자세히 보시면 addAngle이 초기값으로 10도로 세팅되어 있고 10도씩 0.1초 간격으로 증가하게 됩니다. 이때 180도가 되면 addAngle은 -10으로 바뀌고 -10도씩 0.1초 간격으로 감소하게 됩니다. 이때 0도가 되면 다시 addAngle은 10으로 바큅니다. 0~180도 사이를 왔다 갔다 회전을 하게 하는 동작 코딩이 됩니다.

그러면, 회전을 시킬려면 스위치 버턴을 누르면 회전을 시킬지 정지할지를 정하면 됩니다. 이경우도 인터럽트함수를 이요합니다. 왜냐면 어떤 명령을 수행중에 특정 상태로 강제로 변환 시키기 위해서는 인터럽트함수만한게 없습니다. 회전을 하는 중에서도 강제로 중단시켜야 할 때 유영하게 사용 할 수 있기 때문입니다.

attachInterrupt(digitalPinToInterrupt(interruptPin2), exchange2, FALLING);
void exchange2() {
  sw_state=!sw_state;
  timeVal = millis();
}

인터럽트스위치버턴이 눌러지먼 sw_state가 반전시킵니다.

void loop(){
  if(step>0 && sw_state==true){
    회전명령;
  }
}   

이렇게 코딩하면 sw_state 상태값에 따라서 서보모터의 회전을 결정하게 됩니다.

추가로"step>0" 이 표현이 넣었습니다. 그 이유는 회전은 선풍기가 돌때만 회전되어야지 돌지도 않는데 선풍기화 회전할 필요는 없겠죠. 그래서 선풍기 바람의 세기가 0일때는 회전 할 필요가 없다는 의미로 추가했습니다.

결론은 선풍기 바람의 세기가 0이상이고 선풍기회전스위치버턴이 눌러서 sw_state가 true일때마 회전하라는 의미입니다.

종합해 보면,

#include <Servo.h> 

Servo servo1; 
int servoPin1 = 9;
int motorPin = 5;

unsigned long timeVal = 0; //이전시간

int segValue[4][7] = {
  {0,0,0,0,0,0,1}, //0
  {1,0,0,1,1,1,1}, //1
  {0,0,1,0,0,1,0}, //2
  {0,0,0,0,1,1,0}, //3
};
int segPin[8]={4,6,7,8,10,11,12}; //a,b,c,d,e,f,g 핀


int interruptPin1 = 2; //바람 세기 스위치버턴
int interruptPin2 = 3; //회전 스위치버턴
int step = 0; //바람의세기 단계
boolean sw_state = false; //회전스위치 상태값
int angle = 0; //회전각
int addAngle = 10; //회전조절각

void setup() 
{ 
  pinMode(motorPin, OUTPUT); //DC모터
  servo1.attach(servoPin1); //서보모터
    
  for(int i=0;i<7;i++){ //7-Segment Display핀 모드를 선언과 동시체 초기 0단계 표시
    pinMode(segPin[i], OUTPUT);
    digitalWrite(segPin[i], segValue[0][i]);
  }
  
    //인터럽트함수 선언
  pinMode(interruptPin1, INPUT_PULLUP);
  pinMode(interruptPin2, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(interruptPin1), exchange1, FALLING); 
  attachInterrupt(digitalPinToInterrupt(interruptPin2), exchange2, FALLING);
  

  //초기 세팅
  analogWrite(motorPin, 0); //바람세기 = 0
  servo1.write(angle); //회전각 = 0
  delay(1000);
}

void loop() 
{   
  if(step>0 && sw_state==true){
    if(millis()-timeVal>=100){
        angle=angle+addAngle;
        if(angle==180){
          addAngle=-10;
        }
        else if(angle==0){
          addAngle=10;
        }  
        servo1.write(angle);        
        timeVal = millis();
    }
  }   
}
void exchange1() {
  step++;
  if(step==4) step=0;
  for(int j=0;j<7;j++){
     digitalWrite(segPin[j], segValue[step][j]);        
  }
  switch(step){
    case 0:
     analogWrite(motorPin, 0);
     break;
    case 1:
     analogWrite(motorPin, 85);
     break;
    case 2:
     analogWrite(motorPin, 170);
     break;
    case 3:
     analogWrite(motorPin, 255);
     break;
  }
}
void exchange2() {
  sw_state=!sw_state;
  timeVal = millis();
}

4. 결과



위그림에서 회전과 바람세기 스위치버턴을 누르면 아래 동영상처럼 동작을 하게 됩니다.


한가지 특수 기능을 표현했습니다. 단순하게 회전과 바람세기을 명령을 내리는 것이 아니라 둘간의 상황이 잘 어울리게 코딩을 했습니다. 바람의 세기가 0일때는 회전상태일지라도 정지가 되고 바람의 세기가 0이 아닌 1이상일 때는 회전정지 상태가 아니면 회전되게 코딩을 표현 했는데 최대한 진짜 선풍기처럼 표현한 부분입니다.

if(sw_state==true){
  선풍기회전;
}

위 코딩에서 "step>0"이란 조건문 한개로 방금 말한 특수 기능을 수행합니다.

if(step>0 && sw_state==true){
  선풍기회전;
}

단순한 한개의 조건을 체크 했을 뿐인데 재밌는 기능을 수행하게 되었네요.

마무리


오늘은 아두이노 가상시뮬레이터에서 코딩적 관점에서 회로도를 만들고 실험이 이루어졌습니다. 실제로 선풍기를 제작한다면 DC모터로 선풍기를 만들지 않고 AC모터로 만들고 좀 더 큰 사이즈의 형태로 실제 선풍기와 같은 형태로 제작을 했다면 좀 더 멋졌겠지만 그러지는 못했네요.

오늘 실험은 일상에서 흔히 볼 수 있는 특정 사물 동작 하나를 관찰하고 그것을 아두이노 시각으로 풀이해 보았습니다. 여러분들도 이런 실험을 한번 해보세요. 이런 학습을 하다보면 처음에는 모방 실험이 되겠지만 그 노하우가 쌓이면 사물의 관찰에서 인간과 같은 생물의 동작을 관찰하고 그것을 아두이노 시각으로 풀이해 낼 수 있게 됩니다. 즉, 상상이 현실이 됩니다. 단지 여러분들에게 필요한 것은 상상입니다.

오늘 실험에서 리모콘이나 Bluetooth를 연동하면 추가로 원격 제어를 할 수 있기 때문에 한번 상상코딩을 해보셨으면 합니다. 리모콘과 Bluetooth를 그동안 자주 거론되고 실험에 사용한 부품입니다. 그리고 라이브러리를 이용하면 간단히 제어를 할 수 있습니다. 함수 몇개만 사용하시면 쉽게 제어가 되니깐 위 코딩에서 어떻게 이 부품들을 적용할지만 코딩 로직만 잘 상상하시면 됩니다 한번 도전해 보세요. 그리고 하실 수 있으시다면 DC모터가 아닌 AC모터를 이용해서 실제 선풍기처럼 제작을 해보셔도 됩니다. AC모터와 AC모터를 제어할 모터쉴드를 구하시고 아두이노로 AC모터를 제어코딩만 하시면 실제 선풍기를 만드실 수 있을 꺼에요.


댓글()

[아두이노] Neopixel을 이용한 7-Segment Display

IOT/아두이노|2019. 7. 18. 21:50


[아두이노] Neopixel을 이용한 7-Segment Display



아두이노 디지털 시계를 유연히 유튜브에서 NeoPixel을 이용한 디지털 시계가 만들어 놓은 영상을 보다가 재밌을 것 같아서 한번 Neopixel로 7-Segment Display를 만들어 보는 실험을 해보았습니다. 7-Segment Display의 원리가 8개의 LED를 구성된 부품입니다. 사실 LED로 수작업으로 7-Segment Display 모양을 직접 만들면 선 연결이 복잡해지고 시계같은 것을 LED로 만들게 되면 귀찮은 부분들이 많은데 Neopixel를 이용하면 의외로 선 연결이 간단하게 설계 할 수 있습니다. LED로만 7-Segment Display를 생각했는데 왜 NeoPixel로 7-Segment Display로 표현 할 생각을 왜! 못했을까 하는 생각에 부품의 원리를 알고 있어도 실제 상상할 때 이런 것을 가끔 놓치는 경우가 발생하네요.

오늘은 Neopixel를 이용한 7-Segment Display를 어떻게 표현한느지 간단히 실험을 통해 살펴보도록 하겠습니다.

1. 7-Segment Display



7-Segment Display 부품은 아래 그림 과 같습니다. led 제어 a,b,c,d,e,f,g,dp핀으로 해당 위치에 LED에 불이 on/off 상태로 숫자와 문자를 만들어 냅니다.


위 모습을 좀 더 자세하게 나타내면은 아래 그림과 같습니다.


위 그림 처럼 8개의 LED로 구성되고 LED가 7-Segment Display 모양 형태로 배치만 하면 아래와 같습니다.


위 그림을 보면 좀 지져분하게 되었지만 어떤 느낌인지 아시겠지요. 일렬로 된 그림은 보기 편하게 되어 있는 것이고 실제로 7-Segment Display를 표현하려면 위와 같이 배치를 하면 됩니다. 참고로 선 모양은 깔끔하게 표현을 못했고 그냥 대충 막 선을 연결했네요. 선 관리가 일반 발광다이오드(LED)에서는 좀 불편 합니다. 그래서 아두이노 디지털 시계를 만들때 이 표현을 사용하지 않았습니다. 이 표현대로 4-Digit 7-Segment Display 표현을 수작업으로 직접 디자인 한다면 공유선까지 하면 답이 안보이고 꽤 머리를 써서 디장인설계를 해야 합니다.

하지만 NeoPixel를 이용한 회로도 설계는 단순화 디자인을 할 수 있는데 어떻게 표현하는지 한번 살펴 볼까요.

2. Neopixel 7-Segment Display 만들기



7-Segment Display의 a,b,c,d,e,f,g 핀 위치를 그대로 동일하게 표현하면 위 그림처럼 되는데 약간 선 방향이 연결에서 좀 불편하게 연결하게 되더군요. 이 상태에서 한세트 더 7-Segment Display를 연결하면 선이 좀 지져분 해지고 선 연결이 답이 안보이겠더군요.

그래서, 아래와 같이 선연결을 S자 방향으로 자연스럽게 이여지도록 표현 했네요. 대부분 실험을 하시는 분들이 S자 방향으로 선을 많이 연결하시더군요. 그리고 현재 선 방향을 반대 S자 방향으로 연결해도 됩니다. 정해진 것은 없고 S자 방향선을 연결하면 꽤 깔끔하게 연결이 되니깐 참고하시고 선을 연결하시면 됩니다.


위 그림으로는 7-Segment Display라고 잘 연상이 안되죠. 아래 그림을 보고 이해하시면 됩니다.


그리고, 추가로 7-Segment Display를 만든다면 아래와 같이 늘릴 수 있습니다.


별로 복잡하지가 않죠.

3. Neopixel 7-Segment Display 실험


1) Neopixel 7-Segment Display 회로도


  • 준비물 : Neopixel 7개, 아두이노우노
  • 내용 : 3번핀을 Neopixel 제어핀으로 사용한다.


2) 코딩


  • 설계 : 0~9까지 카운터 테스트

[Neopixel 함수]

#include <Adafruit_NeoPixel.h>

const byte neopixelPin = 3; 
Adafruit_NeoPixel neopixel = Adafruit_NeoPixel(7, neopixelPin, NEO_GRB + NEO_KHZ800);
  • neopixel.begin() : 네오픽셀 시작
  • neopixel.setPixelColor(위치,R,G,B) : 네오픽셀 위치에 색상 세팅
  • opixel.show() : 세팅된 값을 출력
  • neopixel.clear() : 네오픽셀 초기화

0~9까지 카운터 세기 위해서는 0~9까지의 숫자 패턴을 만들어야 합니다.

 byte segValue[10][7] = {
   {1,1,1,0,1,1,1}, //0
   {1,0,0,0,1,0,0}, //1
   {1,1,0,1,0,1,1}, //2
   {1,1,0,1,1,1,0}, //3
   {1,0,1,1,1,0,0}, //4
   {0,1,1,1,1,1,1}, //5
   {0,0,1,1,1,1,1}, //6
   {1,1,0,0,1,0,0}, //7
   {1,1,1,1,1,1,1}, //8
   {1,1,1,1,1,1,0}  //9   
 };

아두이노 시계의 패턴을 그대로 적용하려고 했지만 배선 순서가 바뀌어서 어쩔 수 없이 segValue[10][7]값을 수정했네요.

0~9까지 순서대로 출력해야 코딩을 만들어 봅시다.

for(0~9까지 반복){
  for(해당숫자패턴 배열을 출력){
      네오픽셀 숫자패턴 세팅;
  }
  네오픽셀 출력;
  delay(1000);
}

대충 이렇게 틀을 잡으면 됩니다.

코딩으로 하면,

  for(int i=0;i<10;i++){  //0~9까지 10개의 숫자를 반복
    for(int j=0;j<7;j++){ //한개의 숫자 당 7개의 네오픽셀 상태임으로 7번 반복
      if(segValue[i][j]==1){ //해당 숫자의 패턴 상태 체크
        neopixel.setPixelColor(j, 255, 0, 0); // 1이면 해당 네오픽셀에 Red color로 표현           
      }else{
        neopixel.setPixelColor(j, 0, 0, 0);  // 0이면 무색으로 표현       
      }
    }
    neopixel.show();  //7개의 패턴 상태가 세팅되면 수자가 만들어지게 되고 그 숫자를 출력      
    delay(1000); //딜레이 시간 1초을 줌
  }  

1초 단위로 0~9까지 숫자가 출력됩니다. 카운터 효과가 되겠죠.

종합해 보면,

#include <Adafruit_NeoPixel.h>

const byte neopixelPin = 3; 
Adafruit_NeoPixel neopixel = Adafruit_NeoPixel(7, neopixelPin, NEO_GRB + NEO_KHZ800);


 byte segValue[10][7] = {
   {1,1,1,0,1,1,1}, //0
   {1,0,0,0,1,0,0}, //1
   {1,1,0,1,0,1,1}, //2
   {1,1,0,1,1,1,0}, //3
   {1,0,1,1,1,0,0}, //4
   {0,1,1,1,1,1,1}, //5
   {0,0,1,1,1,1,1}, //6
   {1,1,0,0,1,0,0}, //7
   {1,1,1,1,1,1,1}, //8
   {1,1,1,1,1,1,0}  //9   
 };

void setup() {
  neopixel.begin();  
}

void loop() {      
  for(int i=0;i<10;i++){  //0~9까지 10개의 숫자를 반복
    for(int j=0;j<7;j++){ //한개의 숫자 당 7개의 네오픽셀 상태임으로 7번 반복
      if(segValue[i][j]==1){ //해당 숫자의 패턴 상태 체크
        neopixel.setPixelColor(j, 255, 0, 0); // 1이면 해당 네오픽셀에 Red color로 표현           
      }else{
        neopixel.setPixelColor(j, 0, 0, 0);  // 0이면 무색으로 표현       
      }
    }
    neopixel.show();  //7개의 패턴 상태가 세팅되면 수자가 만들어지게 되고 그 숫자를 출력      
    delay(1000); //딜레이 시간 1초을 줌
  }  
    
  //초기화  
  neopixel.clear(); 
  neopixel.show();
  delay(100);     
}

3) 결과


숫자 모양 틀로 표현했다면 아래 움짤이 숫자로 보였을 텐데 이상한 모양처럼 보이네요. 마지막에 9 다음에 0으로 다시 넘어갈 때는 0~9까지 1초 단위로 숫자가 바뀌다가 9 이후 초기화로 전부 지우는 동작을 한번 수행하기 때문에 약간 보시기에 부자연스러울 수 있을 꺼에요. 초기화 명령라인들을 지우고 프로그램을 실행시켜면 자연스러워질 꺼에요. 초기화 명령라인을 넣은 이유는 나중에 추가로 수정할 때 초기화 작업이 필요 할 수 있으니깐 이렇게 Neopixel를 초기화 할 수 있다는 것을 보여 드리기 위해서 초기화 명령라인을 남겨 뒀네요. 이부분을 감안하시고 보시기 바랍니다.


4. Neopixel 디지털 시계 디자인




대충 디지털 시계 디자인은 위와 같이 표현하시면 "00:00"으로 "시:분"으로 표현 할 수 있습니다. 여기서 초까지 출력되게 하고 싶다면 Neopixel를 더 추가해야 겠죠.

코딩은 위에서 숫자를 한개의 숫자를 출력하는데 0~6위치의 네오픽셀에 7개의 패턴을 만들어 출력했는데 이 원리를 기반으로 지난 시간에 아두이노 시계 코딩을 가지고 수정하시면 됩니다.

위 코딩에서는 일자의 자리 0~9까지를 카운터 했는데 십의 자리를 출력하고 싶다면

for문에서 0~6 Neopixel은 1의 자리이고 7~13 Neopixel은 10의 자리가 됩니다.

그러면 12라는 숫자를 출력하기 위해서는

7~13 Neopixel => 1
0~6 Neopixel => 2

이렇게 각 범위의 숫자를 만들어 내면 됩니다. 그러면 1의 자리 for문과 10의 자리 for문을 Neopixel 위치값을 지정해서 숫자를 만들어 내면 됩니다.

위의 사전학습에서 시계 코딩을 분석해 보고 오늘 post에서 숫자 카운터를 한 것을 기반으로 직접 상상 코딩을 통해 디티러 시계 코딩에 도전해 보세요.

이 코딩은 여러분들의 상상에 맡기고 소스는 공개하지 않겠습니다.

마무리


오늘은 Neopixel를 이용하여 7-Segment Display를 만드는 방법을 실험을 하였습니다. 직업 제작을 한다면 꽤 재밌는 것들을 만들 수 있습니다. 마지막에 디지털 시계 디자인 한 것처럼 시계를 만들 수 있습니다. 일반 발광다이오드(LED)로 디자인 하는 것보다 Neopixel 디자인이 좋은 점은 회로선을 연결하는데 편하고 아두이노에서 사용하는 핀은 단 한개로 제어가 가능합니다. 더 중요한 것은 바로 다양한 Color 표현이 가능하다는 점입니다. 3색 LED로 표현하면 Color를 만들 수 있지만 이경우는 회로도 선 배치가 단색 LED보다 더 복잡해지고 제어하는데 꽤 애먹습니다. 그러니 NeoPixel로 디자인하는게 훨 편하겠죠.

우연히 아두이노 디지털 시계 유튜드 영상을 보다가 "아! Neopixel이 있었지!"하고 Neopixel에 대해 잘 알고 있었는데 이걸 상상을 못했다는것이 참 아직도 아두이노의 실력이 부족하다는 것을 느낄 수 있었네요. 그래도 잠깐 몇초 본 영상으로 대충 느낌 아니깐 회로도 만드는 것은 어렵지 표현 했습니다.

아무튼 재밌는 실험을 했네요.

댓글()

[아두이노] 같은 원리 다른 부품(복습)

IOT/아두이노|2019. 7. 16. 09:00

[아두이노] 같은 원리 다른 부품(복습)



오늘은 아두이노를 실험을 할 때 사용되는 부품들 중에 같은 원리로 동작하지만 다른 부품들이 어떤 것들이 있는지 복습하는 시간을 가져볼까 합니다. 살펴 볼 원리는 3x3x3 CUBE 출력 원리이며 이 원리가 사용되는 다른 부품들을 살펴보는 시간을 가져보도록 하겠습니다.

1. LED의 3x3x3 CUBE 출력 원리



참고 자료의 CUBE 원리는 다음과 같습니다.

위 그림에서 보는 것 처럼 가로축선과 세로축선들 끼리 공유선으로 묶으면서 특정 위치에 LED에 불이 들어오는 방법은 가로와 세로의 특정 위치의 +,-의 상태값에 따라 불이 들어오게 됩니다. 공유선을 사용하기 때문에 같은 공유선에서 서로 다른 LED에 불이 들어오기 위해서는 시간차 전원 +,- 상태로 LED를 동시에 서로 다른 공유된 LED에 불이 들어올 수 있게 합니다. 이 원리는 참고자료에 가시면 자세히 나와 있습니다.

이 원리를 사용하는 다른 부품들을 한번 살펴볼까요.

2. 동일한 LED의 3x3x3 CUBE 출력 원리를 사용하는 다른 부품


이 출력 원리를 이용하는 부품을은 대부분 다수의 LED를 이용하는 부품에서 사용됩니다. 다수의 LED에 사용되는 부품들은 이 원리를 이용하여 부품을 제작합니다. 다수의 LED를 개별적으로 제어하려면 많은 핀을 사용해야 하는데 그렇게 부품을 제작하면 부품도 부품이지만 그 부품을 제어 한다면 아두이노 같은 보드에서 제어하기 위해서 많은 핀들이 필요로 하고 비효율적인 부품이 되겠죠. 그래서 다수의 LED를 사용하는 부품들은 각 LED를 제어하기 위해 공유선을 이용하여 LED를 제어하는 방식을 사용하여 적은 핀으로 많은 LED를 제어할 수 있게 제작이 됩니다. 그러면 이 원리를 이용하는 부품들을 살펴보도록 하죠.

1) 4-Digit 7-Segment Display



7-Segment를 여러개 사용하기 위해서 4개의 7-Segment을 공유선을 연결하여 출력 원리를 사용합니다.

위 그림의 부품의 내부 회로도는 아래 그림과 같습니다. 한개의 7-Segment Display를 제어하기 위해서는 총 8핀이 필요하는데 4개의 7-Segment Display를 제어한다면 개별적일 경우 32개 핀이 필요 합니다. 하지만 공유선을 이용한 실제 이 부품은 12개의 핀으로 4개의 7-Segment Display를 제어할 수 있게 제작 되었습니다.

2) 8x8 도트매트릭스



3x3x3 CUBE은 배치 위치가 3x3x3일뿐 실제 내부 회로도를 보면 3x9로 공유선을 이용하여 연결합니다. 8x8 도트매트릭스은 3x9 LED 회로도에서 8x8형태로 표현한 부품입니다.

아래 내부 회로도 모습을 보시면 3x3x3 CUBE랑 같은 모습인 것을 확인 할 수 있을 꺼에요.

3. 동일한 LED의 3x3x3 CUBE 출력 원리의 반대 입력 부품


다수의 LED를 제어하는 출력 원리를 반대로 입력 원리로 사용하는 부품이 있습니다. 대표적으로 키패드를 들 수 있습니다. 다수의 LED에 불이 들어오게 하기 위해서 시간차로 특정 위치에 불이 들어오도록 LED의 출력 상태를 만들었다면 반대로 스위치버턴에서 시간차로 특정 위치의 +,-상태로 전류가 공급되고 특정 위치에 스위치버턴이 눌러지면 전류가 흐르고 그 전류값을 아두이노가 읽고 그 읽은 위치값으로 눌러진 스위치 위치를 알게 됩니다. LED은 이 원리를 출력으로 이용했고 스위치버턴은 이 원리를 입력으로 이용했습니다.

Keypad



위 그림을 보면 LED 위치에 스위치버턴으로 바뀌었고 출력 대신에 입력으로 변경되었을 뿐 동작 원리는 동일합니다.

마무리


동일한 원리를 이용하는 부품들이 많은데 그 중 지금까지 다룬 post들 기준으로 몇개만 나열했네요. 이것 말고도 더 많은 부품들이 동일한 원리로 동작합니다. 각 부품들에 대해서 간단히 복습 차원으로 살펴보는 시간을 가졌습니다. 자세한 부품의 설명은 해당 post를 링크 걸어 놓았으니깐 한번 살펴보셨으면 합니다.

왜! 이런 post를 썼을까요. 그 이유는 아두이노 실험을 쭉 하시다 보면 수 많은 부품들을 다루게 됩니다. 그런데 재밌는 것은 부품만 다르지 부품의 동작원리는 같은 경우가 많습니다. 오늘 post에서 다룬 것처럼 LED와 스위치버턴을 이용하여 만든 부품들이 전부 동일한 원리로 동작합니다. 만약, 3x3x3 CUBE 원리를 알고 있었으면 8x8 도트매트릭스나 4-Digit 7-Segment Display나 키패드 모듈을 쉽게 이해하고 제어할 수 있었을 거라 생각됩니다. 각 부품들을 개별적으로 생각하지 마시고 어떤 특징들이 유사한지 한번 여러분들이 알고 있는 부품들간의 특징들을 비교해서 정리를 해보셨으면 합니다. 오늘 post한 부품들은 동일한 원리의 부품들입니다. 즉, 한개를 알게 되면 위에서 거론한 다른 부품들도 다룰 수 있다는 의미인 것이죠. 이처럼 부품들 간의 특징들을 서로 비교하고 같은 원리의 부품들을 정리하시면 아두이노 학습에 무척 도움 됩니다. 이번에 한번 이런 부품들 별로 정리하는 시간을 가져보셨으면 하는 맘으로 post를 작성 했네요.

"같은 느낌 다른 표현"의 부품들을 찾는 재미과 반복 복습학습으로 아두이노 능력이 향상될 거라 생각 됩니다.

댓글()

[아두이노] 부품이 없을 때 아두이노 실험 팁

IOT/아두이노|2019. 7. 15. 09:00

[아두이노] 부품이 없을 때 아두이노 실험 팁



수 많은 부품들과 오픈 라이브러리 소스를 이용하여 아두이노로 쉽게 실험을 할 수 있습니다. 하지만 많은 부품들을 실험할 때마다 구매하기는 사실 부담이 됩니다. 부품을 구매해서 실험을 하면 좋지만 꼭 구매해야만 실험 할 수 있는 것은 아닙니다. 제가 post를 쓰면서 자주 온라인 가상시뮬레이터에서 실험을 해왔는데 이 가상시뮬레이터를 이용하여 실험하고 싶은 주제의 결과물을 얻을 수 있습니다. 하지만 가상시뮬레이터도 만능은 아닙니다. 모든 전자부품을 제공되지 않습니다. 기초적인 기본 부품만을 제공합니다. 없는 부품을 실험 할 때는 가상시뮬레이터에서도 제약이 따릅니다. 제공되지 않는 부품의 경우는 실험을 못하느냐고 묻는다면 충분히 실험을 할 수 있습니다. 실제 구매해야 하는 부품을 가상시뮬레이터에서 해당 부품의 특징과 비슷한 부품을 대체해서 코딩상으로 해당 주제에 대한 실험을 할 수 있습니다. 즉, 부품의 특징과 원리만 잘 이해하고 있다면 충분히 다른 부품을 사용하더라고 실험하고 싶은 내용으로 실험을 충분히 할 수 있습니다.

그러면, 어떻게 실험을 하는지 그 방법에 대해 살펴보도록 하죠.

1. 온라인 가상시뮬레이터를 이용한 실험


온라인 가상시뮬레이터 tinkercad에서 제공되는 기본 아두이노우노와 부품들은 몇개 되지 않습니다. 하지만 많은 것들을 실험할 수 있습니다. 기본적으로 제공되는 각 부품들의 특징들만 잘 이해하고 있으면 제공되지 않는 부품들도 어느정도 유사한 실험을 할 수 있습니다. 그러면 어떻게 유사한 실험을 할 수 있는지 알아봅시다.

1) 가상시뮬레이터에서 제공되는 부품의 특징


크게 읽기/출력 부품으로 나뉩니다. 읽기는 디지털 읽기와 아날로그 읽기가 있으며 출력도 디지털 출력과 아날로그 출력으로 나뉩니다.

  • 디지털 읽기 : 0 or 1 신호를 읽습니다. (0-0V, 1-5V)
  • 디지털 출력 : 0 or 1 신호를 출력합니다.
  • 아날로그 읽기 : 0~1023 신호를 읽습니다.
  • 아날로그 출력(PWM핀) : 0~255 신호를 출력합니다.
  • 시간값(HIGH->LOW 변환시간, LOW->HIGH변환시간) 외 신호 펄스 관련 제어

2) 실험 할 부품의 특징 파악 후 대체 부품 찾기


실험에 사용 할 부품이 가상시뮬레이터에서 제공되지 않는 부품일 때 해당 부품은 디지털/아날로그 신호를 읽어오는지 출력하는지의 부품의 특징을 우선 알아야 합니다. 그 특징과 유사한 부품의 특징을 가진 가상시뮬레이터의 부품으로 실험 목적에 맞는 유사 실험을 진행 할 수 있습니다.

예를들면, 지난시간에 실험한 토양수분센서를 실험하고 싶을 때 아래 부품을 구매를 해야 실험을 할 수 있습니다. 가상시뮬레이터에서도 제공되지 않는 부품이라서 구매하지 않으면 실험을 할 수 없겠죠.


토양수분센서가 없다고 해서 실험을 할 수가 없는것은 아닙니다. 바로 대체 부품을 찾으면 됩니다. 토양수분센서는 0~1023의 아날로그 신호를 만들어 냅니다. 그러면 0~1023의 신호를 발생하는 부품을 찾으면 되겠죠. 가상시뮬레이터에서 0~1023의 신호를 발생하는 부품은 여러개가 있는데 그 대표적으로 조도센서가 있습니다. 가스센서를 이용해도 되고 가변저항기를 이용해도 됩니다. 0~1023의 아날로그 신호를 발생하는 부품이면 토양수분센서를 대신 할 가상부품으로 여기고 실험을 수행할 수 있습니다.


위 조도센서를 이용하여 토양수분센서의 역할을 수행 할 있습니다.

3) 실험 할 부품이 원리를 파악 후 대체 부품 만들기


실험에 사용 할 부품이 가상시뮬레이터에서 제공되지 않을 경우 해당 부품을 직접 만들 수 있습니다. 원리만 잘 이해하면 그 원리에 맞게 가싱시뮬레이터에서 디자인을 할 수 있습니다.

예를들면, 4-Digit 7-Segment Display는 가상시뮬레이터에서 제공되지 않습니다.


직접 만든다면 4-Digit 7-Segment Display의 원리를 데이터시트를 보고 파악을 해야 겠죠. 가상시뮬레이터로 대체 부품을 만들면 아래와 같습니다.


응용해서 추가로 만든다면은 다음과 같이 늘릴 수 도 있겠죠.


대체 부품을 만들기 위해서는 해당 부품의 데이터시트를 보고 동작 원리에 맞게 부품을 만들어야 하는 어려움이 있지만 어느정도 LED 원리를 터득하고 7-Segment Display를 터득했다면 복잡해 보일뿐 위 표현은 충분히 하실 수 있습니다. 다른 부품의 경우도 이와 유사합니다.

4) 실험 할 부품을 대체 부품을 못 구할 시 Serial 통신 이용


대체한 부품을 찾지 못하거나 부품을 만들 수 없을 때는 Serial 통신을 이용하여 가상 입력과 출력를 표현하는 방법입니다. 즉, 어떤 특정한 부품이 있을 때 부품의 읽는 값을 대신해서 시리얼통신에서 가상 데이터를 입력하고 그 값을 코딩 상에서 가상 데이터를 처리하고 그 결과를 대체 부품이 있으면 출력시키고 출력을 대체 할 부품이 없을 때는 시리얼모니터로 출력시켜서 부족한 부분을 Serial 통신으로 대체하면 됩니다.

예) 시간변환 예제

만약 대체 부품을 구했다면 아래의 post처럼 직접 만들어서 실험을 할 수 있겠죠.

이처럼 가상시뮬레이터에서 대체 할 부품이 있다면 그 부품으로 이용하고 대체 할 부품을 만들 수 있다면 직접 디자인하고 이것도 저것도 아니면 Serial 통신을 이용해서 가상데이터를 입력 받게 하고 출력도 Serial 모니터로 대신 동작하게 할 수 있습니다.

위에 열거한 방법들을 잘 기억해 놓고 아두이노 실험을 하시기 바랍니다.

2. 대체 부품을 이용 시 코딩에 중점을 두자


아두이노를 실험하는 것은 회로도를 만드는 것도 중요한 목적이지만 코딩에 더 집중 하셔야 합니다. 우리가 아두이노를 실험하는 큰 이유가 아두이노를 통해 어떤 특정한 동작 명령을 내려 그 동작을 수행하게 하기 위해서 입니다. 하드웨어에 명령을 내리기 위해서는 코딩을 해야 하고 이부분은 무척 중요합니다.

현재 실험에서 부품이 없더라도 유사 느낌으로 코딩상에서 동작을 제어할 수 있습니다.

지난시간의 자동토양물주기를 토양수분센서와 water pump가 없었지만 충분히 가상시뮬레이터에서 실험을 할 수 있었습니다. 대체 할 부품으로 조도센서와 DC모터를 사용하고 실제 자동토양물주기 동작 명령 코딩에 중점을 두고 실험을 했기 때문입니다. 이 실험은 코딩 상에서 조도센서의 값을 토양수분센서로 인식하고 DC모터를 water pump로 인식하고 코딩을 했고 그 처리 동작도 자동토양물주기의 동작명령을 코딩했기 때문에 실제 다른 부품을 사용했지만 정상적인 결과물을 얻을 수 있었습니다.

아두이노를 실험할 때는 실험 할 부품이 있으면 실감나게 실험을 할 수 있지만 없더라도 대체 부품으로 실험 목적의 맞는 내용으로 코딩을 하면 원하는 결과를 얻을 수 있기 때문에 아두이노 실험에서는 코딩이 역할이 무엇보다 중요합니다. 그래서, 틈틈히 코딩 연습을 해주세요.

아두이노는 부품없이 코딩만으로도 실험을 할 수 있습니다. 부품이 있으면 좋고 없으면 대체부품을 찾고 그것도 없다면 부품을 디자인하고 그게 힘들면 Serial 통신으로 대체하더라도 결과물을 얻을 수 있습니다. 뭔가를 실험하기 위해서 꼭 부품이 필요한 것이 아니니깐 코딩적 관점으로 실험을 하셔도 됩니다.

3. 시간 날 때마다 부품의 이해


부품은 각각 고유의 특징을 지니고 있습니다. 기울기 센서는 기울기에 따라서 0 or 1의 신호를 발생하고 조도센서는 빛의 영향으로 아날로그 신호가 발생하고 LED은 0 or 1의 전류 상태로 불이 on/off 상태가 되고 초음파센서는 전방에 장애물에 거리를 초음파로 벽에 부딛치고 되돌아오는 시간값으로 거리를 측정하고 스위치버턴은 스위치를 누르면 스위치버턴에 연결된 전류의 선이 이여지는 등의 새로운 부품들의 특징을 시간 날 때마다 원리를 머리속에 담아 두세요. 그래야 새로운 부품으로 실험을 해야 하는데 그 부품이 없는 경우에 그 부품의 특징과 기존에 공부한 부품과의 유사 부품으로 대체해서 원하는 실험을 할 수 있게 됩니다.

그리고, 전자부품 쇼핑몰에 가셔서 시간 날 때마다 어떤 부품들이 있는지 부품을 검색하세요. 그 부품의 동작 원리와 특징들을 알아 두셨으면 합니다. 그러면 특정 부품을 가지고 실험을 할 때 같이 연동할 부품을 상상하기 편하고 상상을 할 때 공부 해놓은 부품들이 많으면 다양한 상상설계에 도움이 됩니다.

부품 공부는 전자부품 쇼핑몰에서 찾은 부품을 다시 구글검색에서 해당 부품으로 실험한 자료들을 수집하거나 데이터시트를 검색하셔서 동작 원리를 공부해 주세요. 상상코딩에 무척중요한 부분입니다.

상상코딩의 예를 들면 영화나 애니메이션에서 SF 장면을 보면 제 경우 그 한 장면 장면들이 아두이노적 시각으로 상상을 할 수 있습니다. SF영화에서 특수 장갑을 끼고 VR 화면을 조정하거나 실제 로봇을 조정하는 장면이 있다면 이 부분을 아두이노로 표현한다면 어떻게 할까요. 다양한 방법이 있지만 간단하게 소개하면 구부림센서가 있습니다. 구부림의 각도에 따라서 로봇의 손을 움직일 수 있습니다. 그리고 VR화면을 조정할 수 있습니다. 아니면 근육신경센서를 이용해서 근육의 움직임센서값을 이용해서 로봇의 손을 움직일 수 있습니다. 뇌신경센서로 로봇을 조정할 수 있습니다. 이런것들을 상상하기 위해서는 여러분들이 다양한 많은 부품들을 알고 있어야 합니다. 각 부품들의 특징만이라도 알고 있으면 특정 부품을 공부하거나 상상 실험을 할 때 응용할 수 있는 상상력의 범위가 넓어집니다.

꼭 전자부품 쇼핑몰에 가셔서 특수 부품들을 살펴보시고 해당 부품의 데이터시트를 보시고 동작을 이해하는 공부를 꼭 해주세요. 상상은 그냥 되는게 아닙니다. 여러분들이 얼마나 많은 부품을 체험해 보았느냐에 따라 남들과 다른 상상을 할 수 있습니다.

마무리


오늘은 좀 두서없는 이야기를 한 것 같네요. 꼭 하고 싶은말은 특정 실험을 할 때 꼭 실제 부품이 필요하지 않습니다. 아두이노로 실험할 것들은 엄청 많습니다. 그 많은 실험을 하기 위해서 모든 부품을 다 사용할 수 없습니다. 왠만큼 실험하기 위해서는 금전적 부담이 크기 때문에 특수한 실험의 경우는 부품 하나의 가격이 만만치 않기 때문에 실험을 하기 부담 됩니다. 그럴 때는 꼭 부품을 구해서 실험을 할 필요는 없습니다. 그 부품을 대신 할 부품으로 원하는 동작을 수행하게 할 명령 코딩만 잘 짜시면 됩니다.

실험에 사용되는 부품을 대체 할 부품을 찾기, 대체 할 부품이 없을 때 비슷한 동작 부품 만들기, 부품 없을 때 Serial 통신으로 대체하기 등 이 세가지를 이용하여 아두이노로 다양한 실험을 하시고 많은 상상을 하셨으면 합니다.

댓글()

[아두이노] 자동토양물주기 원리

IOT/아두이노|2019. 7. 12. 22:07

[아두이노] 자동토양물주기 원리



지난 시간에 토양수분센서 측정기를 만들어 보았습니다. 오늘은 토양수분센서를 이용한 자동토양물주기 원리에 대해 살펴보고자 합니다. 실제 구현은 아래와 같은 모습이지만 모터펌프가 없고 DC모터로 간단히 동작을 실험하려고 했는데 차라리 가상시뮬레이터로 실험을 하여 post를 읽는 분들도 직접 실험에 참여할 수 있게 하는 방향으로 나가는게 좋을 것 같아서 간단히 가상시뮬레이터로 자동토양물주기 원리를 실험하는 것이 좋을 것 같아서 post의 방향을 변경했습니다.


위 그림처럼 디자인하고 제작했다면 좀 더 좋았을 텐데 준비물 화분과 워터펌프가 없어서 실제 구현은 못했네요.

오늘 post 실험은 자동토양물주기 원리를 이해하기인데 여기서 사용되는 부품은 조도센서와 DC모터입니다. 조도센서는 토양수분센서이고 DC모터는 모터펌프라는 가정하에 실험이 가상시뮬레이터에서 이루어 집니다. 실제로 가상시뮬레이터에서는 토양수분센서와 모터펌프를 제공하지 않기 때문에 조도센서와 DC모터를 통해 자동토양물주기 원리를 이해하는 시간이니깐 조도센서와 DC모터를 상상으로 토양수분선세와 모터펌프라고 머리속에 담아놓고 post를 보시기 바랍니다.

1. 자동토양물주기 회로도


  • 준비물 : LED 2개, 저항 200옴 2개, 저항 10k옴 1개, 조도센서, 릴레이모듈, 토양수분센서, 아두이노우노, 외부전원
  • 내용 : 아두이노우노 A0핀을 조도센서의 A0에 연결하고 LED를 각각 11,12핀에 연결한다. 그리고 릴레이모듈핀으로 7번에 연결한다.

아두이노 가상시뮬레이터에서는 토양수분센서를 제공하지 않습니다. 가상시뮬레이터에 실험하기 위해서는 대신 할 센서가 필요합니다. 실험에 사용할 센서는 조도센서입니다. 조도센서가 토양수분센서라는 가정하에서 가상으로 실험을 하기 때문에 감안하시고 글을 읽어주시기 바랍니다.


3. 코딩



LED, 조도센서, 릴레이모듈 등을 사용하기 위해서는 위 참고 자료를 한번 살펴보시 오시면 됩니다.

회로도의 기본 동작원리를 유사 post와 거의 90%이상 유사합니다. 차이점은 화분자동물주라는 코딩부분만 약간 차이가 있을 뿐 기본 베이스 코딩은 동일합니다. 아무튼 복습차원으로 이번 post를 공부하시기 바랍니다.

화분에 자동토양물주기를 가정하고 실험 코딩을 해보도록 하겠습니다.

[설계]

  1. 조도센서의 값의 따라 motor on/off 설정(조도센서을 토양수분센서로 가정)
  2. led를 이용하여 토양의 현재 상태를 시각적으로 표현
  3. 릴레이모듈을 이용하여 외부전원을 이용하여 motor를 회전(일반모터를 모터펌프로 가정)

설계는 3가지로 크게 나눠서 생각하게 되었습니다. 동작 원리는 가령 토양수분센서가 건조하여 수분이 없을 때 일정 센서의 일정 수치가 되면 모터펌프로 물을 공급을 하고 토양의 일정한 수분값을 갖게 되면 모터펌프를 중단하는 기본 동작이 코딩입니다. 이때 현재상태로 토양수분상태가 양호하면 Green LED가 켜지고 토양수분이 부족하면 Red LED에 불이 들어오게 하여 현재 상태를 시각적으로 보여주도록 하는 간단한 테스트입니다.

참고로, 모터는 릴레이모듈을 이용하여 motor의 외부전원을 사용하도록 하였습니다. 릴레이모듈은 스위치 역활을 하고 아두이노에서 스위치를 누르면 모터를 동작하고 해제하면 모터를 중단하는 느낌이라고 이해하시면 됩니다.

자~! 이제부터 설계할 3부분을 자세히 살펴 볼까요.

1) 조도센서의 값의 따라 motor on/off 설정


물을 자동으로 주기 위해서는 물을 주기위한 센서값에 도달했을 때 모터펌프로 화분에 물을 주도록 코딩해야 합니다. 어떻게 해야 할까요. 처음에는 정교한 코딩을 할 필요는 없습니다. 단순하게 생각하고 코딩하시면 됩니다.

m_cds = analogRead(cdsPin); //Sensor Read
if(m_cds>=물주기상태값){
  digitalWrite(motorPin, HIGH);
}else{
  digitalWrite(motorPin, LOW);
}

위와 같이 if문으로 간단히 코딩을 할 수 있습니다. 여기서, 조금 개선 시켜볼까요.

if(m_cds>=물주기상태값 && state==false){ //모터동작 체크
    state=true;
    digitalWrite(motorPin, state);
        timeVal=millis();
}else if(m_cds<=물중단상태값 && state==true){ //모터중지 체크
    if(millis()-timeVal>=2000){ //모터동작 최소유지시간 
      state=false;
      digitalWrite(motorPin, state);
    }           
}

위와 같이 timeVal(이전시간값)의 현재시간 millis()함수로 저장됩니다. 그 시간을 기준으로 모터가 최소 동작하는 시간을 2초로 잡았는데 이경우는 실제 실험에서는 적당한 시간을 잡아 주시면 됩니다. 가상 실험이니깐 대충 2초정도로 motor 동작하게 설정했습니다.

참고로, 모터동작이 시작되면 다음 loop()함수가 수행될 때 물공급 상태값과 물중단 상태값을 체크할 때 state를 조건문으로 추가했는데 이 조건문은 모터가 동작했을 때에 모터중단 동작을 수행하게 하기 위해서 추가한 조건문입니다. 물공급은 모터상태가 false(중지) 상태일 때 가동해야 하니깐 false인지 확인하고 물중단이면 state은 가동중단 해야하니깐 true인지 확인해야 합니다. 물공급/물중단 상태를 나타내는 토양수분센서값으로 비교하지만 같이 현재 Motor상태도 비교하면 중복 반복되는 동작을 줄일 수 있습니다.

2) led를 이용하여 토양의 현재 상태를 시각적으로 표현


센서의 값으로 모터를 동작을 제어했지만 시각적으로 현재 상태를 확인하기가 애매합니다. 그래서 LED 2개로 현재 토양상태를 표현해 봅시다.

[토양수분충분상태]

digitalWrite(redPin, LOW);
digitalWrite(greenPin, HIGH);

[토양수분부족상태]

digitalWrite(redPin, HIGH);
digitalWrite(greenPin, LOW);

위 두가지 조건을 어디에 코딩해야 할까요.

if(m_cds>=물주기상태값 && state==false){ //모터동작 체크
  토양수분충분상태;
}else if(m_cds<=물중단상태값 && state==true){ //모터중지 체크
  토양수분부족상태;
}

위 코딩 위치에 위 상태 코딩을 넣으시면 됩니다.

3) 릴레이모듈을 이용하여 외부전원을 이용하여 motor를 회전


모터는 아두이노에서 전원으로 돌리는 것은 문제가 있습니다. 그래서 외부전원을 사용하여 motor를 동작시켜야 하는데 이경우는 여러가지 방법이 있습니다.

첫번째, 트랜지스터를 이용하여 motor를 동작시키는 방법

두번째, 모터쉴드를 이용하여 motor를 동작시키는 방법

세번째, 릴레이모듈을 이용하여 motor를 동작시키는 방법

세번째의 경우는 전구 대신에 motor로 교체하시면 됩니다. 릴레이모듈을 제어하기 위해서 릴레이핀의 상태값으로 motor를 제어하게 됩니다.

digitalWrite(motorPin, HIGH); //동작

digitalWrite(motorPin, LOW); //정지

릴레이 모듈이 좀 햇갈리시면 위 링크 걸어놓은 post에 가셔서 한번 원리를 이해하시고 오세요.

위 설계된 내용을 종합하여 코딩하면,

const byte cdsPin = A0;
const byte redPin = 12;
const byte greenPin = 11;
const byte motorPin = 7;

unsigned long timeVal = 0; //이전시간
unsigned long checkTime = 0;
boolean state = false; //모터상태값

int m_cds =0; //센서값 변수

void setup()
{
  Serial.begin(9600);
  pinMode(redPin, OUTPUT);
  pinMode(greenPin, OUTPUT);
  pinMode(motorPin, OUTPUT);
  
  //초기상태
  digitalWrite(redPin, LOW);
  digitalWrite(greenPin, HIGH);
}

void loop()
{
  if(millis()-checkTime>=1000){ //1초단위로 센서읽기
    m_cds = analogRead(cdsPin); //Sensor Read
    Serial.println(m_cds);
    checkTime=millis();
  }
  
  if(m_cds>=960 && state==false){ //모터동작 체크
    state=true;
    digitalWrite(motorPin, state);
    digitalWrite(redPin, HIGH);
    digitalWrite(greenPin, LOW); 
    timeVal=millis();
  }else if(m_cds<=60 && state==true){ //모터중지 체크
    if(millis()-timeVal>=3000){ //모터동작 최소유지시간 
      state=false;
      digitalWrite(motorPin, state);
      digitalWrite(redPin, LOW);
      digitalWrite(greenPin, HIGH); 
    }
  }
}

센서값을 모터 동작값으로 960이상으로 가상으로 설정했고, 모터 중지값으로 60이하로 설정했습니다. 나머지는 위에 설명한 부분을 합쳐놓은 코딩이니깐 어렵지 않을거라 생각합니다.

4. 결과


조도센서를 토양수분센서로 가정했기 때문에 조도센서를 토양수분센서라고 상상하시고 동작을 이해해 주시기 바랍니다. 조도센서가 960이상이되면 Red LED에 불이 들어고 DC모터가 동작하게 하게 됩니다. 그리고 일정시간(3초)동안은 최소 동작하게 하고 조도센서값이 60이하이고 최소 동작시간이 지나게 되면 Green LED에 불이 들어오고 DC모터가 정지됩니다.

위 코딩에서 모터동작/정지에 사용되는 제어값 960과 60은 가정하에서 설정한거라 실제 토양에서의 수분값은 측정해보고 물주는 토양수분상태값을 정하시고 위 코딩에서 수정하시면 됩니다. 그리고 모터동작을 최소 3초동안 동작하게 했는데 이 경우도 토양에 물주는게 딱 3초로 할 수 없습니다. 실제로는 일정이상의 시간으로 물을 줘야하는데 water pump의 물을 끌어올리는 양을 기준으로 테스트해보고 이정도의 시간으로 물을 주면 되겠다 싶은 정도의 시간을 water pump로 실제 물을 주면서 시간을 측정해 놓고 위 코딩에서 시간값을 수정하시면 됩니다.

그리고, 위 코딩에서 센서값을 읽은 if문안에 나머지 모터동작 코딩을 넣으셔도 됩니다. 센서의 읽은 값을 기준으로 모터의 동작을 결정하게 되기 때문입니다. 외부로 뺀 이유는 따로 몇가지 추가할 경우 별도의 동작을 체크할 일을 감안하여 외부로 모터 동작 if문을 뺐놓은 것일뿐 여러분들은 안에서 넣을지 뺄지는 알아서 판단하시고 코딩을 배치하시면 됩니다.


5. 진짜 자동토양물주기 회로도




대충 지금까지 다뤘던 것들을 종합해서 회로도로 만들어 보았네요. 토양수분 측정기의 회로도에 추가로 bluetooth를 연결해 보았고 모터펌프는 이미지를 못찾아서 그냥 DC모터로 표현했네요. [아두이노] 토양수분 측정기의 회로도와 오늘 배웠던 회로도를 결합해 보았네요.

위 회로도는 토양수분센서 값을 따라 Motor를 동작을 제어를 기본으로 하는데 추가로, 토양수분센서의 값을 LCD16x2에 출력할 수 있고 현재 상태를 LED로 확인이 가능하고 Bluetooth로 스마트폰에 센서값을 출력이 가능합니다.

코딩은 오늘 배운 코딩과 토양수분측정기 코딩을 합치고 시리얼통신 코딩 부분을 bluetooth 통신 코딩형태로 변경하면 됩니다.

코딩부분은 post에 올리지 않겠습니다. 한번 오늘 배웠던 내용을 기반으로 LCD16x2 I2C모듈과 Bluetooth 모듈만 추가 되었으니깐 그 부분만 상상해서 코딩을 하시면 됩니다. 난이도를 살작 올렸습니다. 이번 post에서 회로도만 보시면 코딩을 상상할 수 있는지 보기 위해서 코딩부분은 생략했습니다. 각 부품이 주어질 때 이 부품은 어떻게 제어할 수 있는지와 이 부품에 어떤 출력을 할 것인지는 여러분들의 상상에 맡기겠습니다.

마무리


오늘은 가상시뮬레이터에서 조도센서를 이용하여 Motor 동작을 제어하는 실험을 하였습니다. 가상시뮬레이터로 실험한 이유는 여러분들도 한번 동작 원리를 체험을 해보셨으면 하는 맘으로 표현 했네요. 위 실험을 실제로 실험해 봐야 하는데 모터펌프가 없네요. DC모터로 대신해서 실험해도 되지만 RC카에 조립된 DC모터를 다시 분해해서 만들기가 좀 그래서 구현된 실제 모습은 없습니다. 실제 구현을 안하더라고 가상시뮬레이터로 충분히 의미를 전달할 수 있기 때문에 오늘 내용은 가상시뮬레이터로만 실험을 마무리 하겠습니다.

오늘 내용은 조도센서는 토양수분센서이고 DC Motor는 모터펌프라고 가정하에 실험한 post 입니다. 간단하게 동작 제어만을 실험한 것이라 추가로 좀더 상황을 만들어 업그레이드를 해야 좀 더 완벽한 자동토양물주기 시스템이 도비니다. 이부분은 여러분들의 상상력을 발휘하는 부분으로 남겨 놓겠습니다. 상상은 전류 공급문제, 자동토양물주기 모형틀, 추가 연동 할 부품 등을 여러분들이 상상해서 한번 업그레이드 시켜 보세요.


댓글()

[아두이노] 토양수분 측정기

IOT/아두이노|2019. 7. 9. 09:00

[아두이노] 토양수분 측정기



지난 시간에 토양수분센서 값을 간단히 출력하는 실험을 하였습니다. 오늘은 토양수분센서로 측정한 값을 어떤 출력방식을 상상할지 고민하다가 LCD16x2에 센서값을 출력하도록 하여 간단히 토양수분 측정기를 만들어보는 실험을 해 보겠습니다.


1. 토양수분 측정기 회로도


  • 준비물 : LCD16x2 I2C 모듈, 토양수분센서, 아두이노우노
  • 내용 : 아두이노우노 A0핀을 토양수분센서의 A0에 연결하고 LCD16x2 I2C 모듈에 SDA(A4), SCL(A5)핀에 연결하시오
  • 참고 : [아두이노] LCD16x2 I2C(LiquidCrystal_I2C) 제어

LCD16x2 I2C 모듈에서 I2C통신에 사용되는 아두이노우노 A4, A5핀에 주의해서 연결하시면 나머지는 연결하는데 어려움이 없습니다.


3. 코딩



[함수]

LiquidCrystal_I2C lcd(0x3F,16,2);  // 0x27 or 0x3F
  • 초기화 : lcd.init()
  • 배경 : lcd.backlight()
  • 커서위치 : lcd.setCursor(0, 0)
  • 출력 : lcd.print("문자열")

추가 함수는 참조 링크된 곳에 가셔서 "LiquidCrystal_I2C.h"파일에 가셔서 함수명들을 살펴보시기 바랍니다.

[기존소스]

void setup() {  
  Serial.begin(9600);
}
void loop() {
  int val = analogRead(A0);  //토양수분센서값 읽기
  Serial.println(val);
  delay(1000);
}

LCD16x2 I2C모듈에 결과를 출력하는 코딩을 추가하시면 됩니다.

종합해보면,

#include <Wire.h> 
#include <LiquidCrystal_I2C.h>
 
LiquidCrystal_I2C lcd(0x3F,16,2);  // 0x27 or 0x3F

void setup() {    
  lcd.init(); //초기화
  lcd.backlight(); //배경불켜기
  lcd.setCursor(0, 0);
  lcd.print("Sensor Start!");
  delay(1000);
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Moisture Sensor");
  lcd.setCursor(0, 1);
  lcd.print("value : ");
}
void loop() {
  int val = analogRead(A0);   
  lcd.setCursor(9, 1);
  lcd.print(val);    
  delay(1000);
  lcd.setCursor(9, 1);
  lcd.print("    "); //센서값0~1023사이가 값이 출력되기 때문에 4칸을 공백처리하면 센서값만 지울 수 있음.
}

lcd.setCursor(0, 0)은 첫줄의 0번째 위치에 커서가 위치합니다. 이 함수 뒤에 lcd.print(값)함수로 출력함수를 표현하면 커서의 위치에서부터 값이 출력됩니다. lcd.setCursor(9, 1)함수는 두번째 줄의 9번째칸의 커서가 위치하고 그 9번째 칸부터 lcd.print(값)함수의 출력값이 LCD16x2에 출력됩니다.

혹시, LCD16x2 I2C모듈에 관한 위 코딩이 이해가 안되신다면 사전학습으로 링크 걸어놓은 post를 한번 읽고 오세요.

4. 결과


이번 실험에서도 토양에 직접 토양수분센서를 꼽지 않고 손에 물을 묻혀서 측정하는 방식을 선택했습니다. 그리고 PC USB로 전원을 공급 받았는데 외부 건전지를 이용해서 아두이노우노를 자유롭게 분리해 냈습니다. 이동식 토양수분측정기로 만들었는데 모양을 이쁘게 케이스로 만들었다면 손전등같은 형태로 측정기를 만들었을텐데 그러지는 못했네요.

아무튼 손으로 토양수분센서를 만지면 LCD16x2 I2C 모듈에 측정된 센서값이 아래 동영상에서 보는 것처럼 정상적으로 출력되었네요.


5. 추가 출력 방식


1) Bluetooth 응용


Bluetooth를 이용하여 스마트폰으로 토양수분센서를 출력할 수 있습니다.

위 링크된 조도센서값을 Bluetooth를 이용하여 스마트 폰에 센서값을 출력한 예제에서 토양수분센서 부분만 수정하면 됩니다.

스마트폰으로 토양수분센서값을 전송 받을 수 있게 된다면 많은 것들을 할 수 있습니다. 가령 화분에 토양수분센서가 꼽아져 있으면 화분에 수분상태를 실시간으로 스마트폰으로 확인이 가능합니다. 화분에 물주는 시기를 스마트폰으로 확인이 가능해 지겠죠. 그리고, 토양수분상태를 확인하고 물을 줘야 할 상황이면 자동으로 물을 줄 수 있으며 아니면 스마트폰으로 물주는 명령을 아두이노 수동으로 명령을 내릴 수 도 있습니다. Bluetooth 대신에 wifi모듈을 이용한다면 화분이 있는 위치에서 벗어난 외부에서도 화분 토양수분상태를 확인이 가능해지고 외부에서 화분에 물을 줄지를 원격으로 제어할 수 있습니다.

2) LED or 피에조부저 응용


위 링크된 LED와 피에조부저를 이용하면 토양의 현재 수분상태를 LED Color로 시각적으로 표현이 가능합니다. 그리고 피에조부저를 이용하면 수분상태를 소리로도 표현이 가능합니다. 어떻게 이용하느냐에 따라서 다양한 응용이 가능합니다.

마무리


오늘은 토양수분 측정기를 만들어 보았습니다. 측정기를 만들었지만 이 토양수분센서를 통신에 이용하면 더 많은 것들을 할 수 있습니다. 응용으로는 식물을 재배하는 곳에 설치하면 모터펌프를 이용해서 자동으로 토양에 물을 줄 어 식물재배에 이용할 수 있습니다. 또는 주기적으로 토양수분상태를 측정하게 되면 이 데이터로 토양의 수분상태의 정보를 수집하여 그 정보를 토대로 토양수분변화를 분석하는 통계 자료로 활용할 수 있습니다. 즉, 토양수분값을 어떤 방향으로 상상하느냐에 따라서 그 활용도는 넓습니다.

여러분들은 토양수분센서가 있다면 무엇을 하고 싶은지 상상의 나래를 펼쳐 보세요.


댓글()

[아두이노] 토양수분센서 제어

IOT/아두이노|2019. 7. 8. 09:00

[아두이노] 토양수분센서 제어



오늘은 토양수분센서를 이용한 실험을 할까 합니다. 토양수분센서는 흙 속의 수분의 상태를 측정 할 수 있는 센서인데 식물을 키울 때 유용하게 활용 할 수 있습니다. 즉, 흙 속의 수분 상태에 따라 식물에 물 주는 시기를 결정할 수 있기 때문에 적절할 시기에 자동으로 식물에 물을 줄 수 있게 됩니다. 주변의 흙의 수분 상태를 측정할 수 있는 측정기를 만드는 데 이 센서를 이용하면 좋겠죠.


이제 간단하게 토양수분센서를 실험을 합시다.

1. 토양수분센서


아래 사진처럼 두개의 부분으로 나뉩니다. 토양수분센서는 여러가지 종류가 있는데 하나로 된 일체형 센서가 있거나 아래와 같이 두개로 나눠진 형태의 선세거 있습니다. 제가 사용하는 토양수분센서는 아래 사진처럼 두개로 나누어져 있습니다.


첫번째 사진을 보시면 왼쪽의 +, - 부분이 있는데 두번째 사진의 실제 흙속에 수분을 탐지하는 센서 부분으로 핀이 두개 있는데 앞에 첫번재 사진의 모듈 부분에 +.- 부분을 순서없이 그냥 연결하시 됩니다.

첫번재 사진에 오른쪽에 A0(아날로그신호), D0(디지털신호), GND, VCC로 4개의 핀이 있는데 정교하게 수분상태를 측정할려면 A0(아날로신호)핀을 사용하면 됩니다. 두번째 탐지센서부분에 전류를 공급해서 수분에 따라서 전류의 신호값이 변화가 일어나는데 수분이 전혀 없다면 1023값이 되고 수분의 상태에 따라서 수치는 작아집니다.

수분에 따라 0~1023 사이의 신호를 발생하게 됩니다.(이부분만 기억하시면 됩니다.)

토양수분센서는 아날로그신호를 읽기 때문에 다음과 같이 읽으시면 됩니다.

analogRead(A0);

디지털신호를 읽기를 사용할 경우는 다음과 같겠죠.

digitalRead(D0);

2. 토양수분센서 회로도


  • 준비물 : 토양수분센서, 아두이노우노
  • 내용 : 아두이노우노의 A0핀을 토양수분센서의 A0핀에 연결하시오.
  • 이미지 출처 : FC-28 Soil Hygrometer Module : https://github.com/Teutatis/Fritzing-Parts
    • FC-28 Soil Hygrometer Module.fzpz
    • FC-28 Soil Hygrometer Module_-_Probe.fzpz

fritzing 이미지 찾느라고 고생했는데 겨우 찾아서 회로도 그림을 완성했네요.


토양수분센서 모듈의 A0핀을 아두이노우노 A0핀에 연결만 제대로 하시면 됩니다. 나머지는 Vcc, Gnd 핀은 해당 핀에 맞춰서 연결하시면 됩니다.

3. 코딩


간단히 토양수분센서를 통해서 수분값을 읽는 명령만 수행하는 코딩으로 실험합니다.

void setup() {  
  Serial.begin(9600);
}
void loop() {
  int val = analogRead(A0);  //토양수분센서값 읽기
  Serial.println(val);
  delay(1000);
}

analogRead()함수를 이용하여 토양수분센서의 값을 읽고 시리얼모니터로 토양수분센서의 값을 출력합니다.

4. 결과


실험은 토양에 직접 꼽아서 토양의 수분상태를 측정해야 하는데 적당한 흙이 없어서 그냥 손으로 물을 묻히고 수분을 측정해 보았습니다. 아래 동영상은 손이 흙이라고 가정하여 손에 묻은 수분 측정을 하는 실험 영상입니다.


마무리


오늘은 간단히 토양수분센서를 이용하여 수분을 측정하는 실험을 하였습니다. 부품을 몇개 더 추가해서 좀 더 그럴싸하게 만들어 실험을 해 볼까도 했지만 순수하게 토양수분센서에 대한 의미만 전달하는 것이 좋을 것 같아서 간단히 실험했네요.

토양수분센서를 analogRead(A0), digitalRead(D0)로 측정을 간단히 할 수 있게 되었으니 다음 post에서 부품을 어떤 것을 추가해서 좀 더 그럴싸하게 표현을 해보는 실험을 연구를 좀 해 봐야 겠네요. LCD16x2로 토양수분센서값을 출력시키는 토양수분측정기를 만들지 아니면 Bluetooth에 연결하여 스마트폰으로 토양수분센서값을 전달 받아 스마트폰에 제어권을 넘길지 고민을 좀 해야 봐겠네요. wifi 모듈이 있으면 좀 더 확장해서 라즈베리파이에 서버를 만들고 서로 통신하는 방법을 취하면 멋질 것 같은데 wifi 모듈이 없네요. 나중에 하나 장만을 해서 실험을 해봐야 겠네요.

아마도 복습차원으로 Bluetooth은 최근 주제에서 너무 많이 연동한 실험을 했기 때문에 LCD16x2로 토양수분센서값을 출력하는 측정기를 만들 것 같기는 한데 아직은 결정하지 않았고 결정되면 둘 중 하나를 선택해서 좀 더 개선된 실험을 해보도록 하겠습니다.


댓글()