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

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. 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에 대해 잘 알고 있었는데 이걸 상상을 못했다는것이 참 아직도 아두이노의 실력이 부족하다는 것을 느낄 수 있었네요. 그래도 잠깐 몇초 본 영상으로 대충 느낌 아니깐 회로도 만드는 것은 어렵지 표현 했습니다.

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

댓글()