지난 시간에 간단히 아두이노 신호등을 만들어 실험했는데 여기에 횡단보도신호등을 추가하여 같이 제어하는 실험을 해볼까 합니다. 특정 도로 상황을 만들고 그 상황에서 차량신호등과 횡단보도신호등의 동작을 설정하고 그 부분만 회로도로 구성하고 상황에 맞게 동작하도록 코딩을 해보았습니다.
우선 아래와 같은 도로의 상황을 설정해 보았습니다.
위 그림에서 A, B 지점의 신호등과 횡단보드신호등을 아두이노로 표현하여 실험을 하겠습니다.
상황은 차량신호등은 4차선 도로의 주행이 우선 시 하겠죠. 그러면 2차선에서 바라보는 신호등은 "녹->황->적" 순서에서 적색등이 다른 등보다 더 긴 시간을 유지합니다. 도로의 신호등의 시간은 도로의 상태에 따라 시간이 다르기 때문에 삼거리 도로에서는 4차선 도로쪽 녹색등 시간이 적색등보다 길고 2차선의 적색등이 녹색등보다 더 긴 시간을 유지하게 됩니다. 즉, 2차선 적색등일 때 4차선은 녹색등이니깐 두 신호등의 관계를 잘 이해해 주세요.
횡당보도신호등은 2차선에서 바라 봤을 때 적색등일 때 횡단보도신호등이 작동하게 코딩을 할까 합니다. 대충 2차선으로 바라봤을 때 "녹->황->적"순으로 바뀌는데 적색등으로 바뀔 때 횡단보드신호등의 적색등이 녹색등으로 바뀌고 횡단보드의 7-Segment Display가 카운트를 하게 됩니다. 그리고 0이 되면은 다시 적색등으로 바뀌는 형태로 코딩했습니다.
좀 상황을 설정하고 코딩을 완성하고 보니 상황이 약간 이상하게 설정했더군요. 다시 코딩을 수정하기 귀찮아서 이상태로 실험을 마무리했는데 이부분을 감안하시고 실험 내용을 보시기 바랍니다.
아래 회로도는 지난시간의 신호등 회로도에서 횡단보도신호등을 추가한 회로도 입니다. 두개의 횡단보드신호등을 만들 수 있었지만 너무 복잡하게 그려질 것 같아서 한쪽 횡단보드신호등만 표현했네요. 만약 두개를 만드신다면 횡단보드신호등의 연결된 선을 공유선으로 연결하시면 됩니다.
좀 복잡한 회로도인데 해당 표현은 아래 그림으로 대충 구분하시면 됩니다.
7-Segment Display 부품을 사용하는데 그냥 아두이노우노 핀에 적접 연결해도 되지만 복습차원으로 74HC595칩을 이용하여 제어하도록 하겠습니다.
74HC595칩 제어 :
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);
}
}
}
실제로 제작해 해봤는데 배선이 좀 지져분하게 만들어 졌네요.
결과는 가상시뮬레이터에서 실험한 결과와 같은 동작으로 결과가 출력 되었네요.
기존의 신호등 코딩에서 횡단보도신호등을 추가했을 때 기존의 코딩에 대해서 신경 쓸 필요 없고 횡단보도신호등에 대한 코딩만 신경쓰면 됩니다. millis()함수를 이용하여 시간을 제어하면 차량신호등의 동작과 횡단보도신호등의 동작을 동시에 수행 할 수 있게 됩니다.
오늘 실험은 신호등과 횡단보드신호등에 상황을 만들고 두 상황을 동시에 처리를 하였습니다. 오늘 코딩한 스타일을 잘 기억해 주세요. 여러 상황을 동시에 처리하거나 또는 여러 부품을 동시에 제어할 때 millis()함수를 이용하여 시간을 제어하면 각 상황을 코딩하고 나서 다른 상황을 코딩할 때 시간에 대한 어려움을 쉽게 해결 합니다.
그리고, 오늘 post에서 2가지 상황을 설정하고 그 상황에 맞게 아두이노에서 동시에 처리하는 코딩을 하였습니다. 여러분들도 꼭 신호등이 아니더라도 여러가지 상황을 만들고 그 상황을 동시에 처리하는 코딩 연습을 많이 하셨으면 합니다. 하나의 부품을 제어하는 것은 무척 싶습니다. 하지만 둘 이상의 부품을 제어하는 것과 둘 이상의 상황을 제어하는 방법은 쉽지 않습니다. 둘 이상의 조건을 만족하기 위해서는 서로 충돌이 발생하지 않게 상상 코딩을 해야 하기 때문에 이 능력은 꾸준히 반복 학습을 통해서 터득 할 수 있습니다. 그렇기 때문에 오늘 제가 했던 방식으로 일상의 상황들을 하나씩 찾아서 적용하여 아두이노로 표현해 보세요.