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

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


댓글()