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

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. 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를 작성 했네요.

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

댓글()

[아두이노] LED를 통한 전류의 흐름 이해

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

[아두이노] LED를 통한 전류의 흐름 이해



오늘은 코딩이 없는 간단한 LED 제어를 통해 전류의 흐름을 배우는 시간을 갖도록 하겠습니다. 실험할 회로도는 Tinkercad에서 제공하는 RGB LED 예제를 기반으로 하겠습니다. Tinkercad 예제를 우연히 보다가 공식적인 실험 예제를 발견하고 이 예제로 실험하면 전류의 흐름을 좀 더 쉽게 이해하는 할 수 있을 것 같아 이번 포스팅 주제로 결정을 했네요.

1. RGB LED 스위치



위 사진은 RGB LED를 사용해야 하는데 Blue LED가 없어서 그 위치에 어쩔 수 없이 Yellow LED를 배치했네요. 스위치를 누르면 해당 Color Led에 불이 들어오고 3색 LED에 해당 Color가 출력되는 회로도입니다. 두개 이상을 누르면 두가지 Color가 혼합된 Color로 3색 LED에 불이 들어오게 됩니다. Blue LED가 없어서 Yellow LED에 불이 들어오지만 해당 위치는 Blue이기 때문에 3색 LED에는 Blue가 출력되도록 세팅 했네요.

2. RGB LED


1) Tinkercad의 공식 RGB LED 회로도

출처 : Tinkercad 예제

해당 회로도에서는 9V의 건전지를 사용했더군요. 그러면 LED의 저항은 9V에 맞게 다시 계산되어야 합니다. "V=IR" 공식에서 R의 저항값을 다시 계산기로 계산해야 하는데 귀찮아서 회로도의 저항 속성창을 통해 저항을 확인해보니 480옴이더군요.

위 회로도는 뭘 표현한건지 대충 아니깐 직접 안보고 만들어 보도록 하겠습니다. 혹시 원리를 모르시는 분들을 위해 간단히 설명을 하면 스위치 버턴을 누르면 해당 스위치에 연결된 선을 전류가 흐릅니다. 여기서 Red, Blue, Green Led가 각 스위치마다 연결되어 있습니다. 해당 스위치를 누르면 각 color의 Led에 불이 들어옵니다. 여기까지 이해하셨으면 여기서 스위치에 선이 추가되어 3색 LED에 연결하면 어떻게 될까요. Red Led에 연결된 선이 3색 LED의 Red핀에 연결시키고 Blue Led에 연결된 선은 Blue핀에 연결하고 Green Led에 연결된 선을 Green 핀에 연결시켜 놓으면 해당 스위치를 누르면 개별 LED와 3색 LED에 해당된 color가 불이 들어오겠죠. 3색 LED에 해당 RGB핀이 아래 Red, Blue, Green LED랑 한몸이 되어서 같이 동작하는 회로도입니다.

재밌는것은 가상시뮬에이터에서 할 수 없지만 두개 이상의 스위치를 누르면 색이 3색 LED에 혼합된 Color로 출력됩니다. 즉, Red 스위치랑 Blue 스위치를 누르면 해당 LED불이 들어오고 3색 LED에는 Red+Blue가 혼합된 Color가 출력됩니다. Color를 공부하는 아이들에게 좋은 학습자료가 될 수 있겠죠.

참고로 여기서는 스위치로 표현되었지만 가변저항기를 스위치 자리에 교체하면 가변저항기를 돌릴때마다 Color 값을 조절 할 수 있겠죠. 그러면 더 많은 Color를 만들어내는 장치가 될 수 있습니다.

2) 직접 만들기


  • 준비물 : 9V 건전지, 저항 480옴 4개, Red, Blue, Green 각각 1개, 3색 LED 1개, 스위치 3개
  • 내용 : RGB LED 스위치

스위치를 이용해 RGB LED를 제어했구나 하고 대충 몇초만 보고 쉬운 예제이니깐 그 의미에 맞게 동일하게 만들어 봤네요


썩 마음에 들게 만들지 못했네요.

3. 전류의 흐름


이제는 전류가 어떤식으로 흘러가서 LED에 불이 들어오는지 살펴보도록 하겠습니다.


위 그림처럼 Led에 전류를 공급하면 불이 들어옵니다. 전류는 '+'에서 '-'로 이동 합니다.


그런데 Vcc쪽에 스위치를 달게 되면 초기 상태는 끊어진 상태로 전류가 흐리지 않습니다. 여기서, 스위치를 누르면 스위치 내부에 선이 서로 연결되어서 Vcc(+) 전류가 Led에 공급되면서 Led에 불이 들어오게 됩니다. 우리가 일상에서 흔히 형광등을 켤때 벽에 스위치가 붙어 있잖아요. 그걸 상상하시면 됩니다.


위의 그림처럼 3개의 Led를 연결했을 때 각 스위치 중에 누른 스위치는 전류가 공급되고 해당 Led에 불이 들어 옵니다. 이부분을 잘 기억해 두세요. Vcc(+)와 Gnd(-)의 선이 어떻게 연결되었고 여기서 해당 선에 전류가 흐르게 되면 어느 방향으로 전류가 흘러갈지를 머리속에서 잘 그려주세요.


위 그림에서 빨간선이 전류의 진행 방향입니다. Green 스위치를 누르게 되면 전류의 진행 방향에 따라서 흐르게 되고 Green Led에 불이 들어오게 됩니다.



위 그림을 보시면 3색 LED만 추가 되면은 Green Led와 3색 LED로 두갈래로 전류가 공급되고 Green Color의 불이 들어오게 됩니다. 대충 어떻게 전류가 흘러가는지 아시겠지요.

4. 두개의 스위치를 누른 효과 만들기


이 예제는 한개의 스위치 뿐이 누를 수 없습니다. 가상에서도 두개의 스위치를 누르고 싶은 욕망이 생기더군요. 오늘 이야기 하고 싶은 포스트 내용으로 들어가겠습니다. 이 걸 하기 위한 연습단계였네요. 어떻게 하면 두개의 스위치를 누른 효과를 만들 수 있을까요. 임의의 스위치를 만들어서 2개의 LED에 불이 들어오게 전류를 공급하면 됩니다.


위 그림처럼 새로운 스위치를 만들어서 Blue, Green Led 선에 연결을 했을 경우에 스위치를 누르면 2개의 Color에 불이 들어오고 위 3색 LED에 혼합된 색이 출력됩니다. 정상적으로 동작하는 것처럼 보이지만 전류의 흐름에 문제가 있습니다. 과연 뭘까요. 처음의 3개의 스위치를 눌러보세요. Green 스위치를 누르게 되면 Green, Blue에 불이 들어오게 됩니다.


위 그림에서 빨간원을 잘 보시기 바랍니다. Green 스위치를 누르면 해단 선에 전류가 공급되는데 빨간 원모양은 새로운 스위치에 purple 선으로 전류가 흘러가게 되고 purple에 연결된 Blue Led에도 전류가 공급되는 현상이 발생하게 됩니다. 결론은 Blue, Green은 서로 연결되었다는 의미가 됩니다. 그래서 어느 스위치를 누르 든 2개의 LED에 불이 들어오는 현상이 발생합니다.

이 문제를 어떻게 해결 할까요. 전자부품에는 다이오드라는 게 있습니다. 전류를 특정 방향으로만 흘러가도록 하는 부품인데 역전류 현상을 막는 부품이기도 합니다. 아래 그림에서 보는 것처럼 한쪽으로만 진행되고 마이너스 쪽에서 전류가 들어온다면 +쪽으로 전류가 흐르지 못하도록 차단하는 부품입니다.


이 부품을 연결을 아래와 같이 표현하면 Green 스위치를 누를 경우 새로 만든 스위치로 전류가 흘러가지 않게 다이오드가 차단 합니다.


참 재밌는 부품이지요. Green 스위치를 누른 결과인데 Green 스위치를 누르면 새로 만든 pulple선에 연결된 다이오드에 의해서 전류가 흘러가지 못하게 차단이 됩니다. Blue스위치를 눌러도 마찬가지이고요.

회로도 재구성


  • 준비물 : 9V 건전지, 저항 480옴 4개, Red, Blue, Green 각각 1개, 3색 LED 1개, 스위치 7개, 다이오드 9개
  • 내용 : RGB LED 스위치


세개의 Color가 동시에 눌러 졌을 때 상황을 표현한 스위치를 한개 더 추가했습니다. 그리고 그 결과는 위 그림에서 화살표가 가리키는 스위치를 눌렀을 때의 결과 이미지 입니다. 좀 지져분하게 되었지만 위에서 다이오드 연결한 것처럼 추가로 몇개만 더 만드시면 됩니다. 보기만 지져분할 뿐 동작은 잘 됩니다. 위에 공개 회로도로 링크 걸어놓았으니깐 가셔서 시뮬레이터를 돌려 보세요.

다른 방법으로 제어하는 방법도 있겠지만 순간 떠오르는 방법이고 다이오드를 소개하기 딱 좋을 아이템이라고 생각하여 이렇게 표현 했네요. 전류의 흐름을 잘 이해하셨으면 합니다.

5. 실제로 구현


여기서 끝나면 재미 없어서 한번 실제로 만들어 봤네요. 전원부분은 건전지를 사기가 싫어서 아두이노 5V 전원공급으로 처리했습니다. 그래서 저항은 그냥 220옴으로 수정 되었네요. 참고로 실제 구현에서는 스위치를 동시에 누를 수 있기 때문에 구지 복잡한 다이오드 연결을 하고 새로운 스위치를 만들 필요는 없었습니다.

1) 회로도 구성


  • 준비물 : 저항 220옴 4개, Red, Blue, Green 각각 1개, 3색 LED 1개, 스위치 3개, 아두이노우노, 뻥판
  • 내용 : RGB LED 스위치


위 회로도는 깔금한데 실제 구현은 선때문에 보기 지져분합니다. 실제 모습을 포스트 첫 이미지 사진을 보시면 되겠습니다. 좀 지져분하여 이해를 돕기 위해서 가상 시뮬레이터로 먼저 표현을 했네요. 이것도 위에 공개회로도에 링크를 걸어 놓았으니깐 시뮬레이터를 돌려 보세요.

2) 결과

실제 실험 영상에서는 Blue Led가 없어서 해당 위치에 Yellow Led를 넣었네요. 참고로 Yellow LED이지만 3색 LED에서는 Blue로 정확히 출력됩니다. 감안하시고 보세요.


마무리


쓰다보니깐 간단히 포스팅 할려다가 또 길어졌네요. 짧게 표현하는 능력이 부족해서 글이 좀 길어지는 경향이 있네요. 암튼 이번 실험을 통해서 전류가 어떻게 흘러가는지 이해하는 시간이 되었으면 합니다.

마무리는 깔끔하게 여기서 마칩니다.


댓글()

[아두이노] 8x8 도트매트릭스 제어

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

[아두이노] 8x8 도트매트릭스 제어 



이건 조이스틱 제어 한뒤에 다룰려고 했던 주제인데 3x3x3 CUBE LED랑 같기 때문에 이번에 같이 소개하는 게 좋을 것 같아서 바로 도트매트릭스에 대해서 살펴보도록 하겠습니다.

1. 8x8 도트매트릭스


제가 가지고 있는 모델입니다. 뒷면에 핀이 8x8핀으로 구성되어 있는데 그냥 이상태에서 실험하시면 딱 이 부품만 사용하면 아두이노에서 사용할 수 있는 핀은 몇개 안남습니다. 여기에 따로 모듈을 붙여서 사용핀을 줄여서 실험하셔야 다른 부품들과 같이 다양한 실험을 할 수 있습니다. 모듈화 된 제품을 구하시는게 좋고요. Max7219 칩인가 그걸로 도트매트릭스 핀을 다 제어가 가능합니다. 아니면 74HC595를 2개 사용하여 8핀씩 제어하는 방법도 있고요. 그냥 모듈화 된 것을 구매하셔서 라이브러리로 쉽게 제어하는게 편하겠지요.


오늘 다루는 내용은 사실 실제부품과는 차이가 있고 원리를 설명하며 가상시뮬레이터에서 실험을 통해거 그 결과를 보기 위한 내용으로 꾸며질 꺼에요. 그 이유는 도트매트릭스 부품이 가상시뮬레이터에서는 없기 때문이고 해당 8x8 도트매트릭스 부품은 핀 번호가 좀 복잡합니다. 단순하에 8x8핀이 있다고 해서 대충 연결해서는 원하는 결과를 얻을 수 없습니다.

실제로 실험하실려면은 아래 아두이노 튜토리얼를 참고 하시기 바랍니다.

이것만 보시면 안되고 앞쪽과 뒷쪽을 구분해야 합니다. 앞쪽에 볼록 약간 뛰어 나온 모양이 있는데 그 부분이 앞쬭인데 부품이름이 적혀있는 면일 꺼에요. 가장 중요한 핀번호를 알아야 합니다.

여기 블로그 게시물을 쓴분의 8x8 도트매트릭스 뒷면에 핀번호 사진이 있습니다. 그걸 보시고 핀번호를 확인하시면 됩니다. 그러면 아두이노 공식 홈페이지에 나온 튜토리얼 예제를 보시고 핀 연결한 뒤에 실제로 돌려보시면 아마 될꺼에요.

이걸 다 다룰려면 포스트가 또 길어지고 실제 아두이노를 다루는 것에 초점을 둔게 아니라 가상시뮬레이터 실험을 통해서 아두이노를 이해하는게 목적임으로 간단히 8x8 도트매트릭스 원리를 이해하는 내용으로 전개하겠습니다.

그러면 직접 LED로 제작해야죠. 도트매트릭스 회로도를 만들어 봅시다. 다시 노가다의 길이 시작 되었네요.

2. 8x8 도트매트릭스 회로도 구성


  • 준비물 : Yellow LED 64개, 저항 220옴 8개, 아두이노우노
  • 내용 : 8x8 도트매트릭스 형태를 표현하자.


아마도 지난시간에 3x3x3 CUBE LED를 같다고 생각하시는 분들이 있을거에요. 맞습니다. 도트매트릭스가 이런 구조로 된 부품입니다. 이 회로도를 만드는 중에 난 지금 뭐하고 있는거지 하는 생각이 들정도로 엄청 노가다 작업을 했네요. 동작 원리는 동일합니다. 층과 호실의 개념으로 접근하시면 됩니다. 이건 지난시간에 많이 설명을 해서 더이상 반복하지 않게습니다.

스피드하게 다음 코딩작업으로 갈까요. 참고로 A0~A4핀은 아날로그 입력핀인데 pinMode()를 선언하면 디지털핀 용으로 사용이 가능합니다. 그래서, 16개 핀이 필요한데 겨우 배치했네요. 참고로 0,1핀은 통신핀으로 일부러 사용하지 않았습니다. 사용할 수 있지만 나중에 시리얼통신을 할 때 적용해야 하기 때문에 습관을 0,1핀을 될 수 있으면 사용안하시는게 좋습니다.

3. 코딩



복습

  • pinMode(사용핀, 사용모드) : 사용핀을 INPUT/OUTPUT/INPUT_PULLUP 모드중 뭘로 사용할지를 정함.
  • digitalWrite(사용핀, 출력형태) : 사용핀을 HIGH(5V) or LOW(0V) 중 출력 형태를 정함.
  • delay(1000) : 1초(1000)를 대기한다.

int layer[8] = {10,11,12,13,A0,A1,A2,A3};
int room[8] = {2,3,4,5,6,7,8,9};
int m_layer = 8;  // 층 수
int m_room = 8; // 호실 수

void setup()
{  
  for(int i=0;i<m_layer;i++){
    pinMode(layer[i], OUTPUT); // 층 선언
    pinMode(room[i], OUTPUT); //호실 선언
    digitalWrite(layer[i], HIGH);  // 각층 닫기
    digitalWrite(room[i], LOW);  // 각호실 닫기   
  }   
}
void loop()
{
  for(int i=0;i<m_layer;i++){
    digitalWrite(layer[i], LOW);
    for(int j=0;j<m_room;j++){      
      digitalWrite(room[j], HIGH);
      delay(100);
      digitalWrite(room[j], LOW);
    }
    digitalWrite(layer[i], HIGH);
  }
}  

4. 패턴글자 출력 코딩


참고소스 : [아두이노] 3x3x3 CUBE LED 패턴 코딩 완성본


참고소스는 각 층별로 패턴을 저장했다면 이번 경우는 패턴을 기준으로 각층별의 패턴을 묶어서 저장했네요. 약간의 차이가 있습니다.
'STEEM'이란 글자를 만들기 위해서 8x8에서 'S'를 하나의 패턴으로 층별로 값을 묶었습니다. 지난시간에는 1층이면 1층만의 패턴을 하나의 배열변수에 저장했다면 이번에는 'S'자를 만드는 1~8층의 정보를 하나의 배열변수에 저장했다고 생각하시면 됩니다. 약간은 차이가 있고 이런식으로도 패턴을 저장하는구나를 보여주기 위해서 입니다. 사실, 이방식으로 패턴을 저장하는게 더 옳은 방법입니다. 지난 포스팅에 패턴은 층별 개별적인 패턴을 만들기 때문에 층별 배열변수로 저장이 이뤄졌고요. 이번에는 전체 층을 합산한 패턴을 만들기 때문에 패턴에 대한 전체층의 값을 묶어서 저장해서 좋더 효율으로 패턴을 만들고 출력할 수 있다는 점을 생각하시고 코드를 보시기 바랍니다.

int layer[8] = {10,11,12,13,A0,A1,A2,A3};
int room[8] = {2,3,4,5,6,7,8,9};
int m_layer = 8;  // 층 수
int m_room = 8; // 호실 수

unsigned int layerPattern[5][8] ={ //패턴
  //S :   
  {0x3C,0x66,0x06,0x1C,0x38,0x60,0x66,0x3C},
  //T : 
  {0x18,0x18,0x18,0x18,0x18,0x18,0xFF,0xFF},  
  //E : 
  {0x7E,0x7E,0x60,0x7E,0x7E,0x60,0x7E,0x7E},
  //E : 
  {0x7E,0x7E,0x60,0x7E,0x7E,0x60,0x7E,0x7E},
  //M : 
  {0xC3,0xC3,0xC3,0xC3,0xDB,0xFF,0xE7,0xC3}  
};

void setup()
{  
  for(int i=0;i<m_layer;i++){
    pinMode(layer[i], OUTPUT); // 층 선언
    pinMode(room[i], OUTPUT); //호실 선언
    digitalWrite(layer[i], HIGH);  // 각층 닫기
    digitalWrite(room[i], LOW);  // 각호실 닫기
   
  }  
 
}
void loop()
{
  for(int i=0;i<5;i++){  //순차적으로 패턴수만큼 동작
    for(int j=0;j<30;j++){  //m_delay 시간만큼 해당 패턴을 유지한다.
      for(int p=0;p<8;p++){ //층 별 패턴
        LEDSetting(layer[p], layerPattern[i][p]);  //1층 i번째 패턴        
      }
    }
    delay(500);
  }  
}  
void LEDSetting(int layer, unsigned int state){
  digitalWrite(layer, LOW); //층 개방
  for(int i=0;i<8;i++){    
    digitalWrite(room[i],bitRead(state, i)); //호실 개방     
  }
  delay(5);
  for(int i=0;i<8;i++){     
    digitalWrite(room[i], LOW);   //호실 닫힘   
  }
  digitalWrite(layer, HIGH); //층 닫힘    
}

5. 결과


가상시뮬레이터 결과는 순차적으로 릴레이 출력과 글자 패턴을 출력하는 영상입니다. 따로 만드는 과정은 생략했고요. 그냥 어떻게 동작하는지만 살펴보세요. 그리고 글자 패턴은 'STEEM' 글자가 출력되게 했네요. 가상시물레이터가 뚜렷하게 출력되지는 않네요.

마무리


코딩에 대한 설명은 생략했습니다. 지난시간에 순차적으로 릴레이하는 코딩을 그대로 인용하였고, 패턴 만들기로 2편의 포스팅으로 대충 감을 잡으셨을꺼에요. 따로, 8x8 도트 매트릭스를 설명 드릴 것은 없네요. 3x3x3 CUBE LED나 8x8 도트매트릭스는 다 동일한 원리니깐요. 오늘은 복습 차원으로 생각하시면 되겠습니다.

사실 이 포스트는 지난 3x3x3 CUBE LED할 때 완성했는데 따로 Steem API가 재미 있어서 그쪽으로 맛보기로 포스팅하다 보니깐 이제서야 올리게 되었네요. 1일 1포스트를 하다 보니깐 뭔가 빨리 올리고 싶어서 밀리게 되네요.

아! Steem API 몇개 함수를 이용해서 또 재밌는 걸 만들려고 구상중이네요. 그건 포스팅을 안하겠지만요. 아두이노하다거 포스트 정체성이 상실하니깐 아두이노를 당분간 계속 올려야겠네요. 혹시 재밌는 표현이 완성된 것 있으면 주말에나 한번씩 포스팅을 해봐야 겠네요.


댓글()

[아두이노] 3x3x3 CUBE LED 패턴 놀이

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

[아두이노] 3x3x3 CUBE LED 패턴 놀이 



3x3x3 CUBE LED로 한주를 그냥 보내 버렸네요. LED 제어에 가장 재밌는 부분이라서 설명해야 할 것이 산더미 인데 더이상 했다가는 너무 한 주제로 무리한 것 같아서 다음에 비슷한 주제 나오면 또 신나게 달려볼까 합니다. 주말이라서 오늘은 편하게 휴식하는 맘으로 지난시간에 완성한 함수를 이용해서 패턴만 만들어서 재밌게 표현하고 놀았습니다. 위에 링크된 공개된 회로도를 여러분들의 계정에 복사하셔서 패턴만 워드나 기타 문서 편집기에서 표로 만들어서 표현하고 싶은 패턴을 색칠하시고 패턴을 완성하시면 그걸 시뮬레이터로 테스트 해보세요. 그냥 오늘은 표로 책실하는 놀이나 하고 그 결과를 시뮬레이터로만 돌려 보면 휴식을 취합니다.

1. 3x3x3 CUBE LED 회로도 구성


  • 준비물 : Red, Blue, Green LED 각각 9개, 저항 220옴 3개, 아두이노우노
  • 내용 : 3x3x3 CUBE LED 형태를 표현하자.


완성 좀 더 그럴싸 하게 표현하려고 좀 노력했네요. 일자모양의 LED를 제어하니깐 느낌이 안 살고해서 큐브 모양을 한번 제대로 보여드리기 위해서 최대한 큐브모양에 가깝게 표현 했네요.

2. 패턴 만들기





위 표를 책칠한 위치의 자리를 잘 생각하셔서 2진수로 표현하고 그 것을 여기다 에 올리면 너무 길어질 것 같아서 10진수로 다시 표현했네요.

unsigned int layerPattern1[6] ={325,170,16,341,186,511}; //1층
unsigned int layerPattern2[6] ={7,56,448,73,146,292}; //2층
unsigned int layerPattern3[6] ={273,146,84,56,63,511}; //3층

세줄로 위 표를 색칠한 패턴을 완성했습니다.


3. 코드 수정


  • layerPattern1[],layerPattern2[],layerPattern2[] 이부분은 패턴이 저장된 배열변수로 베이스 소스에서 이부분만 수정

  • int m_pattern = 6; : 패턴수가 6개라서 6으로 수정

  • int m_delay[9] ={30,30,30,30,30,30}; //유지시간은 패턴 6개이고 30이라는 시간만큼 유지

끝! 지난시간의 소스에서 이부분만 수정하면 나머지 그대로 사용하시면 됩니다. 즉, 3x3x3 CUBE LED은 위 세개만 변경하시면 원하는 패턴을 다 만들 수 있습니다.


[완성 소스]

int layer[3] = {A0,A1,A2};
int room[9] = {2,3,4,5,6,7,8,9,10};
int m_layer = 3;  // 층 수
int m_room = 9; // 호실 수


//패턴 정수로 표현
unsigned int layerPattern1[6] ={325,170,16,341,186,511}; //1층
unsigned int layerPattern2[6] ={7,56,448,73,146,292}; //2층
unsigned int layerPattern3[6] ={273,146,84,56,63,511}; //3층

int m_pattern = 6; //패턴수
int m_delay[9] ={30,30,30,30,30,30}; //패턴유지시간

void setup()
{  
  for(int i=0;i<m_layer;i++){
    pinMode(layer[i], OUTPUT); // 층 선언
    digitalWrite(layer[i], HIGH);  // 각층 닫기
  }
  
  for(int i=0;i<m_room;i++){
    pinMode(room[i], OUTPUT); //호실 선언
  }
}
void loop()
{
  for(int i=0;i<m_pattern;i++){  //순차적으로 패턴수만큼 동작
    for(int j=0;j<m_delay[i];j++){  //m_delay 시간만큼 해당 패턴을 유지한다.
      LEDSetting(layer[0], layerPattern1[i]);  //1층 i번째 패턴        
      LEDSetting(layer[1], layerPattern2[i]);  //2층 i번째 패턴        
      LEDSetting(layer[2], layerPattern3[i]);  //3층 i번째 패턴        
    }  
  }   
}    
void LEDSetting(int layer, unsigned int state){
  digitalWrite(layer, LOW); //층 개방
  for(int i=0;i<9;i++){    
    digitalWrite(room[i],bitRead(state, i)); //호실 개방     
  }
  delay(5);
  for(int i=0;i<9;i++){     
    digitalWrite(room[i], LOW);   //호실 닫힘   
  }
  digitalWrite(layer, HIGH); //층 닫힘    
}

4. 결과


[ 가상시뮬레이터 결과 ]
가상시뮬레이터로 돌리니깐 느낌이 아직도 잘 안사네요. 실제 제작한분 있으면 거기에 이 소스를 대입하면 좀 화려할 듯 싶네요.



마무리


주말이라서 새로운 주제로 나가기 좀 그래서 휴식하는 맘으로 지난시간에 완성한 소스에서 재밌는 패턴을 만들어서 시뮬레이션하는걸로 끝냈습니다.

사실 주말에 Steem API가 땡겨서 맨땅에 해당 하면서 javascript로 데이터를 불러오는 것 까지는 성공했는데 함수들을 정확히 다 이해를 못해서 구글 검색을 통해 예제만 신나게 찾고 있네요. 함수 래퍼런스가 너무 부실해서 C언어 지식으로 대충 때려 맞추고 있네요. 라즈베리파이에 파이썬 3.5가 깔려서 Steem 라이브러리를 설치하고 정보 불러오는 것 까지도 성공했는데 파이썬으로 접근하는 함수나 명령문들이 아직은 자료가 부족해서 기본정보만 접근하는 것만 성공하고 때려치우고 javascript로 공부하느라 아두이노를 잠시 주말은 외도하고 간단히 패턴 놀이로 포스팅 합니다.


댓글()

[아두이노] 3x3x3 CUBE LED 패턴 코딩 완성본

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

[아두이노] 3x3x3 CUBE LED 패턴 코딩 완성본



지난 시간의 포스팅으로 끝낼려고 했는데 왠지 이렇게 마무리 하면 좀 찜찜한 기분이 들어서 좀 더 완벽한 코딩을 만들어 놓고 마무리 짓는게 더 나을 것 같아서 추가로 포스팅을 합니다. 3x3x3 CUBE LED를 마무리 하려다가 문득 코딩이 순간 머리속에서 스쳐 지나가서 간단하게 완성본을 만들어 봤네요.


1. 3x3x3 CUBE LED 회로도 구성


  • 준비물 : Red, Blue, Green LED 각각 9개, 저항 220옴 3개, 아두이노우노, 뻥판
  • 내용 : 3x3x3 CUBE LED 형태를 표현하자.


2. 패턴 코딩 시행 착오


만든 함수

  • pattern(사용층1, 사용호수1, 유지시간)
  • pattern(사용층1, 사용층2, 사용호수1, 사용호수2, 유지시간)
  • pattern(사용층1, 사용층2, 사용층3, 사용호수1, 사용호수2, 사용호수3, 유지시간)
    이렇게 표현해서 원하는 사용층만 사용하도록 바꿨습니다.
void pattern(int layer1,int room1,int m_delay){
  for(int i=0;i<m_delay;i++){ //for문이 delay()함수 효과    
    LEDSetting(layer1,room1);   
  }  
}

void pattern(int layer1,int layer2,int room1, int room2, int m_delay){
  for(int i=0;i<m_delay;i++){ //for문이 delay()함수 효과    
    LEDSetting(layer1,room1);
    LEDSetting(layer2,room2); 
  }  
}

void pattern(int layer1,int layer2,int layer3,int room1,int room2, int room3,int m_delay){
  for(int i=0;i<m_delay;i++){ //for문이 delay()함수 효과    
    LEDSetting(layer1,room1);
    LEDSetting(layer2,room2);    
    LEDSetting(layer3,room3);    
  }  
}
  • LEDSetting(사용층, 사용호수) : 기본적으로 LED에 전원을 공급해서 전류가 0.01초 만큼 공급되도록 세팅했습니다.
void LEDSetting(int layer, int room){
  digitalWrite(layer, LOW); //층 개방
  digitalWrite(room, HIGH); //호실 개방 
  delay(10);
  digitalWrite(room, LOW);   //호실 닫힘
  digitalWrite(layer, HIGH); //층 닫힘  
}

위와 같은 함수를 만들었네요. 사용층 수에 따라서 인수가 달라질 것을 감안하여 오버로드 함수로 표현을 이용했습니다.

로직을 짜면,

void loop()
{
  //Red LED만 순차적 릴레이
  for(int i=0;i<9;i++){ 
    pattern(layer[0],room[i],10);    
  }     
  
  //RED, BLUE만 동시 서로 역순 순차적 릴레이
  for(int i=0;i<9;i++){ 
    pattern(layer[0],layer[1],room[i],room[8-i],20);    
  }  
  
  //RED, BLUE, GREEN LED 모두 순차적 릴레이 
  for(int i=0;i<9;i++){ 
    pattern(layer[0],layer[1],layer[2],room[8-i],room[i],room[i],5);    
  }  
}

[ 결과 ]



결과는 짧은 동영상에서 보는 것과 같이 만족스럽게 다양한 패턴으로 동작의 결과를 얻었습니다.

완성되었다 싶었는데 문제점이 발견 되더군요. 이 방식을 사용하면 동시에 제어를 하더라도 각층의 LED은 1개만 컨트롤 할 수 밖에 없다는게 문제점이네요. 여기서, 딜레이 시간을 짧게해서 연속으로 다른 호실에 불이 들어오게 하면 되는데 이렇게 되면 또, LEDSetting()함수안에 delay(10)이 같은 층이여도 반복되니깐 그 시간이 누적되면 동시에 불이 들어오는 착시현상이 사라지게 되는 문제에 봉착 됐네요. 그래서 다시 원점에서 코딩을 시작하게 되었네요.

2. 완성된 패턴 코딩 설명


배열변수에 패턴 저장

각층의 패턴을 아예 16bit로 만들었네요. '0B' 표시는 2진수라는 표현입니다. 시각적으로 불이 들어오는 위치를 '1'이고 불이 안들어오는 위치를 '0'으로 나눴네요. bit로 쪼개면 변수 크기을 최소화 하고 효율적인 코딩을 할 수 있어서 사용 하였습니다. 가령, '0'과 '1'을 개별적으로 저장 변수를 만들경우 패턴이 늘어날수록 변수저장공간이 부족한 아두이노에게는 좀 문제가 있겠죠. 그래서 bit로 쪼갠 정보를 많이 이용합니다. LED의 상태를 '0'과 '1'로만 표현이 가능하니 가장 이상적인 표현이라고 할 수 있습니다.

아래 그림만 보면 '1'이 나타내는 곳에 LED가 불이 들어오니깐요. 대충 어떤식으로 LED가 켜지는지 쉽게 알 수 가 있겠죠.

unsigned int layerPattern1[9] ={ //1층 패턴
  0B0000000000000001, 
  0B0000000000000010,
  0B0000000000000100,
  0B0000000000001000,
  0B0000000000010000,
  0B0000000000100000,
  0B0000000001000000,
  0B0000000010000000,
  0B0000000100000000
};
unsigned int layerPattern2[9] ={ //2층 패턴
  0B0000000100000000, 
  0B0000000010000000,
  0B0000000001000000,
  0B0000000000100000,
  0B0000000000010000,
  0B0000000000001000,
  0B0000000000000100,
  0B0000000000000010,
  0B0000000000000001
};
unsigned int layerPattern3[9] ={ //3층 패턴
  0B0000000100000001, 
  0B0000000010000010,
  0B0000000001000100,
  0B0000000000101000,
  0B0000000000010000,
  0B0000000000101000,
  0B0000000001000100,
  0B0000000010000010,
  0B0000000100000001
};

또는, 2진수 계산법을 아신다면 표현은 인간이 표현하기 쉬운 숫자로 표기가 가능합니다.

//1층 패턴
unsigned int layerPattern1[9] ={1,2,4,8,16,32,64,128,256};

//2층 패턴
unsigned int layerPattern2[9] ={256,128,64,32,16,8,4,2,1};

//3층 패턴
unsigned int layerPattern3[9] ={258,130,68,40,16,40,58,130,258};

2진수로 표현된 것을 10진수로 변환하면 우리가 사용하는 일반 숫자로 나타낼 수 있어요. 3x3x3 CUBE LED은 한층의 9개의 LED를 사용하기 때문에 비트 한자리씩 총 9개의 비트자리만 사용하면 됩니다. 그래서 표현 숫자는 0~511 숫자로 9개의 LED 패턴을 만들어 낼 수 있습니다. 중요한것은 어느 숫자가 어느 LED인지는 쉽게 알 수 없습니다. 10진수 정수를 2진수로 변환해야지 그 위치에 LED에 불이 들어오는 구나 생각되는 것이죠. 10진수 형태로 나타낸 패턴은 포털 검색에서 2진수 변환이라는 키워드를 치셔서 왜 저렇게 숫자로 나타내는지 찾아보세요. 진수변환이 주 목적이 아니니깐 위에 2진수 표현한 배열변수를 그대로 사용하겠습니다.

잘 이해가 안가는 분들을 위해서 아래 그림을 참고하시면 됩니다.


그림에서 아래 1부터 9까지의 숫자는 비트 자릿수 입니다. 그냥 1호실부터 9호실로 생각하시면 돼요. 참고로 역순으로 표현한 이유는 나중에 bitRead()함수를 이용해서 1비트씩 읽는데 bitRead(숫자,읽을위치)로 하는데 읽을 위치가 '0'이면 숫자의 첫번째자리의 비트를 읽어오기 때문입니다. "나는 정순으로 배치하고 '8'번째 위치부터 역순으로 읽으면 되잖아!" 하시는 분들은 그렇게 하셔도 됩니다. 이 표현은 제가 코딩하는 스타일의 편의 상 이렇게 한것뿐인점 참고해 주세요.

그런데 앞에 '0'의 갯수가 더 많은 이유가 뭐지하고 생각하시는 분들도 있을꺼에요. 1byte는 8bit로 구성되어 있고 2byte은 16bit가 됩니다. unsigned int 자료형을 2byte의 자료 공간을 갖기 때문에 표기는 전체 16bit를 다 표현해야겠죠. 그래서 9bit의 값만 사용하지만 앞에 사용하지않는 비트는 전부 '0'으로 채우게 됩니다. 쉽게 생각하시면 오른족 9bit만 3x3x3 CUBE LED에 이용된다고 생각하시면 됩니다.

그리고 패턴을 만들때 워드창에다가 표를 만들어서 위 그림처럼 색칠하기를 해보세요. 그리고 색이 칠해진 자리는 '1'로 표기하고 색이 안칠해진 자리는 '0'으로 표기하여 16bit를 표현하시면 됩니다. 어렵지 않겠죠.

1(10진수) => 0B0000000000000001(2진수)와 같습니다. 둘 다 똑같은 값인점 참고해주세요. 10진수는 인간이 이해하기 쉬운 숫자라면 2진수는 컴퓨터가 이해하기 쉬운 숫자라고 생각하시면 됩니다.

예) 표에서 녹색 1패턴은 어떻게 표현 할까요.

0B0000000100000001

이렇게 해서 패턴을 만드는 것은 어렵지 않겠죠


만든 함수

  • LEDSetting(사용층핀, 사용층패턴) : 사용층의 모든 호실의 패턴를 인수로 넘겨줘서 출력한다.

여기서 층 개방/닫힘은 위아래로 묶어준뒤에 그안에 for문으로 9개 LED의 상태를 개방하고 0.01초 동안 불이 들어오게 한뒤에 다시 for문을 통해 9개 LED를 닫습니다. LED에 불이 들어올때 착시효과 만들기 위한 LED 불이 최소한 켜져있도록 만든 기준 함수입니다. 쉽게 말해서 특정 위치에 LED가 최소한으로 전류가 공급될 시간을 주기 위한 함수라고 생각하시면 됩니다.

void LEDSetting(int layer, unsigned int state){
  digitalWrite(layer, LOW); //층 개방
  for(int i=0;i<9;i++){    
    digitalWrite(room[i],bitRead(state, i)); //패턴 호실 개방     
  }
  delay(10);
  for(int i=0;i<9;i++){     
    digitalWrite(room[i], LOW);   //모든 호실 닫힘    
  }
  digitalWrite(layer, HIGH); //층 닫힘  
  
}

아두이노 IDE에서 코딩할 때 기본적으로 라이브러리에서 제공되는 bitRead()함수를 1bit씩 추출을 쉽게 할 수 있어서 편하게 표현을 할 수 있었네요.

x은 읽을값이고 n은 읽을 위치입니다. 참고로 읽는 순서는 오른쪽에서 부터입니다. 그래서 1호실 개방하는데 '0B0000000000000001'로 표현했다면 bitRead(0B0000000000000001,0) 함수로 0번째(첫번째) 위치 1을 추출하게 됩니다.


로직을 수행하면,

int m_pattern = 9; //패턴수
int m_delay = 20; //유지시간

void loop()
{

  for(int i=0;i<m_pattern;i++){     
    for(int j=0;j<m_delay;j++){
      LEDSetting(layer[0], layerPattern1[i]);          
      LEDSetting(layer[1], layerPattern2[i]);          
      LEDSetting(layer[2], layerPattern3[i]);          
    }
  
  }
}

1층(layerPattern1[9]), 2층(layerPattern2[9]), 3층(layerPattern3[9])에 기록된 9개 패턴들을 간단히 출력하는 로직입니다.

3. 패턴 코딩 완성본


int layer[3] = {A0,A1,A2};
int room[9] = {10,9,8,7,6,5,4,3,2};
int m_layer = 3;  // 층 수
int m_room = 9; // 호실 수

unsigned int layerPattern1[9] ={ //1층 패턴
  0B0000000000000001, 
  0B0000000000000010,
  0B0000000000000100,
  0B0000000000001000,
  0B0000000000010000,
  0B0000000000100000,
  0B0000000001000000,
  0B0000000010000000,
  0B0000000100000000
};
unsigned int layerPattern2[9] ={ //2층 패턴
  0B0000000100000000, 
  0B0000000010000000,
  0B0000000001000000,
  0B0000000000100000,
  0B0000000000010000,
  0B0000000000001000,
  0B0000000000000100,
  0B0000000000000010,
  0B0000000000000001
};
unsigned int layerPattern3[9] ={ //3층 패턴
  0B0000000100000001, 
  0B0000000010000010,
  0B0000000001000100,
  0B0000000000101000,
  0B0000000000010000,
  0B0000000000101000,
  0B0000000001000100,
  0B0000000010000010,
  0B0000000100000001
};
int m_pattern = 9; //패턴수
int m_delay[9] ={20,20,20,20,20,20,20,20,20}; //패턴유지시간

void setup()
{  
  for(int i=0;i<m_layer;i++){
    pinMode(layer[i], OUTPUT); // 층 선언
    digitalWrite(layer[i], HIGH);  // 각층 닫기
  }
  
  for(int i=0;i<m_room;i++){
    pinMode(room[i], OUTPUT); //호실 선언
  }
}

void loop()
{

  for(int i=0;i<m_pattern;i++){  //순차적으로 패턴수만큼 동작
    for(int j=0;j<m_delay[i];j++){  //m_delay 시간만큼 해당 패턴을 유지한다.
      LEDSetting(layer[0], layerPattern1[i]);  //1층 i번째 패턴        
      LEDSetting(layer[1], layerPattern2[i]);  //2층 i번째 패턴        
      LEDSetting(layer[2], layerPattern3[i]);  //3층 i번째 패턴        
    }  
  }
}
    
void LEDSetting(int layer, unsigned int state){
  digitalWrite(layer, LOW); //층 개방
  for(int i=0;i<9;i++){    
    digitalWrite(room[i],bitRead(state, i)); //호실 개방     
  }
  delay(10);
  for(int i=0;i<9;i++){     
    digitalWrite(room[i], LOW);   //호실 닫힘   
  }
  digitalWrite(layer, HIGH); //층 닫힘    
}

4. 결과


[ 가상시뮬레이터 결과 ]



[실제 아두이노 결과 ] 3층은 선이 부족해서 3층 패턴이 맘에 들어서 2층으로 옮겨왔고 1층 패턴은 그대로 뒀네요.


실제 결과는 LEDSetting()함수내 최소한 불이 켜져있을 시간을 delay(10)으로 했는데 3층의 딜레이 시간이 총 0.03초 정도가 되더군요. 그외 명령문들 수행결과까지 하면 딜레이가 좀 길어저 깜박거림이 좀 심해져 눈의 피로도를 유발하더군요. 어쩔 수 없이 delay(10)을 delay(5~7)사이로 돌렸네요. 그렇게 되면 패턴이 돌아가는 시간이 빨라져서 유지시간 m_delay[9] 배열변수의 값들을 약간씩 늘여서 깜박이는 속도를 다시 보정 하였습니다.

마무리


어제 포스팅을 올리고 나서 뭔가가 너무 아쉬워서 다른 각도로 접근하여 완성본을 만들어 봤네요.
상단에 layerPattern1[], layerPattern2[], layerPattern3[] 배열변수만 나중에 원하는 패턴을 '0'과 '1'로만 만들어 내고 패턴수와 그 패턴을 유지하는 시간만 만들어 내면 나머지는 그대로 사용하시면 됩니다. 완성본이라고 하지만 아직은 즉흥적으로 코딩한 거라 좀 아쉬운 점이 많습니다.

좀 더 코딩부분을 다뤄서 패턴 클래스를 만들고 하면 체계적으로 제어가 되지 않을까 생각이 들었는데 더이상 가는건 아닌것 같아서 너무 깊게 들어가는 것보다 다른 주제로 넘어가는게 좋을 것 같아서 여기서 멈추겠습니다.

이제는 장시간에 걸쳐서 다룬 3x3x3 CUBE LED를 마무리하도록 하겠습니다.


댓글()

[아두이노] 3x3x3 CUBE LED 패턴 코딩 II

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

[아두이노] 3x3x3 CUBE LED 패턴 코딩 II



지난 시간에 3x3x3 CUBE LED 패턴 포스팅에서 패턴을 만드는 것과 그걸 일정시간 유지하는 방법을 배웠습니다. 그리고 2가지 패턴을 교차로 LED를 깜박이게 하는 것까지 했었죠. 이제는 패턴을 좀 더 길게 만들고 중복되는 코딩 부분을 제거하여 코딩을 최소화하는 과정을 배워 보겠습니다.


1. 3x3x3 CUBE LED 회로도 구성


  • 준비물 : Red, Blue, Green LED 각각 9개, 저항 220옴 3개, 아두이노우노, 뻥판
  • 내용 : 3x3x3 CUBE LED 형태를 표현하자.


2. 패턴 코딩 과정


패턴 코딩 과정의 두번째 시간입니다. 지난시간의 코드에서 연장선상에서 이여가겠습니다.

[ 1층1호실2층2호실과 1층2호실2층1호실 교차 깜박이기]

int layer[3] = {A0,A1,A2};
int room[9] = {10,9,8,7,6,5,4,3,2};
int m_layer = 3;  // 층 수
int m_room = 9; // 호실 수

void setup()
{
  for(int i=0;i<m_layer;i++){
    pinMode(layer[i], OUTPUT);
    digitalWrite(layer[i], HIGH);    
  }
  
  for(int i=0;i<m_room;i++){
    pinMode(room[i], OUTPUT);
  }
}

void loop()
{
    //1층1호실과 2층2호실
     for(int i=0;i<30;i++){ //for문이 delay()함수 효과
         digitalWrite(layer[0], LOW);  // 1층 개방
         digitalWrite(room[0], HIGH);  // 1호실 개방
         delay(10); //0.01초 동안 불 켜기
         digitalWrite(room[0], LOW);  // 1호실 닫힘
         digitalWrite(layer[0], HIGH);  // 1층 닫힘
  
         digitalWrite(layer[1], LOW);  // 2층 개방
         digitalWrite(room[1], HIGH);  // 2호실 개방
         delay(10); //0.01초 동안 불 켜기
         digitalWrite(room[1], LOW);  // 2호실 닫힘
         digitalWrite(layer[1], HIGH);  // 2층 닫힘
    }
    
    //1층2호실과 2층1호실
    for(int i=0;i<30;i++){ //for문이 delay()함수 효과
        digitalWrite(layer[0], LOW);  // 1층 개방
        digitalWrite(room[1], HIGH);  // 2호실 개방
        delay(10); //0.01초 동안 불 켜기
        digitalWrite(room[1], LOW);  // 2호실 닫힘
        digitalWrite(layer[0], HIGH);  // 1층 닫힘
  
        digitalWrite(layer[1], LOW);  // 2층 개방
        digitalWrite(room[0], HIGH);  // 1호실 개방
        delay(10); //0.01초 동안 불 켜기
        digitalWrite(room[0], LOW);  // 1호실 닫힘
        digitalWrite(layer[1], HIGH);  // 2층 닫힘
    }
}

여기서, 어느게 중복되고 있나요. 바로 for문 이하의 안에 명령문들이 두번 중복되는 것을 보실거에요. 2가지여서 2번 중복되었는데 10가지라면 10번 for문을 코딩해야한다면 정말 노가다 코딩이 되겠죠.

[ 중복코드를 외부함수로 ]

이제는 그 중복되는 부분을 차라리 외부로 빼서 호출하여 명령을 수행하도록 하여 코딩하는 양을 줄이도록 하겠습니다.

void pattern(int layer1, int layer2, int room1, int room2){
  for(int i=0;i<30;i++){ //for문이 delay()함수 효과
    digitalWrite(layer1, LOW); 
    digitalWrite(room1, HIGH);  
    delay(10);
    digitalWrite(room1, LOW);  
    digitalWrite(layer1, HIGH); 
  
    digitalWrite(layer2, LOW); 
    digitalWrite(room2, HIGH);  
    delay(10);
    digitalWrite(room2, LOW);  
    digitalWrite(layer2, HIGH); 
  }  
}

새롭게 함수를 만들었습니다. 그렇게 이 로직은 사실 기존의 코딩한 로직의 연장 선상으로 표현한 것입니다. 사실은 이렇게 하면 안되고 내부 로직도 변경하고 명령문도 새롭게 만들어야 합니다. 혼동을 최소화하기 위해서 이전 코드를 그대로 인용하여 표현했다는 점을 감안하시고 보셨으면 해요.

그러면, 어떻게 loop()함수에서 코딩이 될까요.

void loop(){
    pattern(layer[0],layer[1],room[0],room[1]);  //1층1호실, 2층2호실
    pattern(layer[0],layer[1],room[1],room[0]);  //1층2호실, 2층1호실
}

  • pattern(1층,2층,1층호실,2층호실) : 2층과 2호실에 제어.

코딩이 딱 두줄로 바뀌었지요. 그리고 loop()함수만 봐도 대충 의미를 이해하실꺼에요.

좀 길게 스토리를 만들어 볼까요. 가령 1층은 1호실부터 9호실까지 순차적으로 깜박이고 2층은 9호실부터 1호실까지 순차적으로 깜박이게 명령을 내려 볼까요.

void loop(){
    pattern(layer[0],layer[1],room[0],room[8]);  
    pattern(layer[0],layer[1],room[1],room[7]);  
    pattern(layer[0],layer[1],room[2],room[6]);  
    pattern(layer[0],layer[1],room[3],room[5]);  
    pattern(layer[0],layer[1],room[4],room[4]);  
    pattern(layer[0],layer[1],room[5],room[3]);  
    pattern(layer[0],layer[1],room[6],room[2]);  
    pattern(layer[0],layer[1],room[7],room[1]);  
    pattern(layer[0],layer[1],room[8],room[0]);     
}

그런데, 또 문제가 생겼네요. 중복 제거 코딩을 했는데 또 중복이 발생했네요. 불필요한 코딩이 늘어났는데 다시 해결해 볼까요. 이번에도 순차적이니깐 for문으로 처리해 버리죠.

void loop(){
  for(int i=0;i<9;i++){
    pattern(layer[0],layer[1],room[i],room[8-i]);    
  }
}

간단히 loop()함수 내 3출 코딩으로 줄어 들었네요. 이제 더 나아가 반대로 역순으로 깜박이게 하고 싶은 충동이 안느껴지나요. 위 for문에서 room[i],room[8-i]만 서로 바꿔주면 됩니다. 그래서 아래와 같은 완성된 코드만 만들어집니다.

void loop(){
 
  //for문으로 한줄 명령으로 릴레이  
  for(int i=0;i<9;i++){
    pattern(layer[0],layer[1],room[i],room[8-i]);    
  }  
    
  //역순  
  for(int i=0;i<9;i++){
    pattern(layer[0],layer[1],room[8-i],room[i]);    
  }
}

만약에 이 명령문들을 일일히 수작업으로 명령을 다 코딩했다고 생각해보세요. pattern()함수를 릴레이 패턴 for문 당 9번 총 pattern()함수 내부의 코드를 18번 일일히 수정하면서 코딩해야 합니다. pattern()함수 내부 명령문들도 많은데 그 많은 명령문을 18번이나 중복해서 코딩해야 한다고 상상해보세요. 진정한 노가맨이 되겠죠.

프로그램 문법을 좀 배우면 표현력의 한계는 없고 남들보다 더 쉬운 코딩을 할 수 있습니다.

loop()함수안의 코드는 의미전달과 최소화만 해야합니다. 뭘 하는지만 전달해주고 세부적인 명령문들은 외부함수로 빼서 제어해야합니다. 그래야 나중에 코드를 수정하거나 다른사람들이 보기에도 편해집니다. 수작업으로 모든 명령문들을 일일히 loop() 함수에 다 집어 넣게 되면은 도대체 loop()함수 내에서 무슨 동작하는지 머리만 복잡해 집니다.

위 loop()함수 내 코드만 보더라도 pattern()함수를 for문으로 해서 순차적으로 뭘 표현했구나하고 쉽게 이해할 수 있습니다. 정확히 어떤 동작인지는 모르지만 pattern()함수라는 뭔지는 모르지만 9번 순차적으로 반복이 이루어졌고 그걸 또 역순으로 9번 순차적으로 진행했구나 정도는 쉽게 loop() 함수만 보면 알 수 있습니다. loop()함수의 동작은 pattern()함수는 아직은 뭔지 모르더라도 그걸 순차적으로 반복하고 또 역으로 반복했구나 쉽게 loop()함수의 동작을 이해 할 수 있습니다.

3. 소스


int layer[3] = {A0,A1,A2};
int room[9] = {10,9,8,7,6,5,4,3,2};
int m_layer = 3;  // 층 수
int m_room = 9; // 호실 수

void setup()
{
  for(int i=0;i<m_layer;i++){
    pinMode(layer[i], OUTPUT);
    digitalWrite(layer[i], HIGH);    
  }

  for(int i=0;i<m_room;i++){
    pinMode(room[i], OUTPUT);
  }
}

void loop(){

  //for문으로 한줄 명령으로 릴레이  
  for(int i=0;i<9;i++){
    pattern(layer[0],layer[1],room[i],room[8-i]);    
  }  

  //역순  
  for(int i=0;i<9;i++){
    pattern(layer[0],layer[1],room[8-i],room[i]);    
  }
}

void pattern(int layer1, int layer2, int room1, int room2){
  for(int i=0;i<30;i++){ //for문이 delay()함수 효과
    digitalWrite(layer1, LOW); 
    digitalWrite(room1, HIGH);  
    delay(10);
    digitalWrite(room1, LOW);  
    digitalWrite(layer1, HIGH); 
  
    digitalWrite(layer2, LOW); 
    digitalWrite(room2, HIGH);  
    delay(10);
    digitalWrite(room2, LOW);  
    digitalWrite(layer2, HIGH); 
  }  
}

4. 결과


어떻게 패턴 코딩을 진행했는지 녹화를 해 놓았으니깐 살펴보시고 마지막에 시뮬레이션 결과를 확인해 보세요. 실제 아두이노에서도 실행한 장면도 들어 있으니깐 보시고 결과물이 어떻게 나왔는지 확인해 주세요.

가상시뮬레이터에서 실험한 기록 일지

실제아두이노에서 실험한 영상

마무리


지난시간의 내용을 기반으로 오늘은 연장선상에서 이여갔습니다. 사실, 오늘 코딩한 것에도 문제점이 많습니다. 그것은 강제적으로 1층과 2층에 꼭 불이 하나는 들어와야 한다는 전재 조건이 있습니다. 그 부분을 수정하자면 pattern()함수 내부를 완전 수정해야하기 때문에 그냥 수정 안하고 그 문제점을 가지고 오늘 포스팅을 했네요. 오늘은 전달하고자 하는 바는 중복코드를 제거하는 방법입니다. 그리고 loop()함수의 명령문들은 뺄 수 있으면 외부 함수로 빼내고 하나의 묶음으로 묶어서 loop()내에서는 의미 전달코딩을 하면 보다 효율적인 코딩을 할 수 있다는 것을 보여 드리는게 목적입니다.

결론, loop()함수는 최소 동작의 의미만 표현하며 중복되는 부분은 외부함수로 빼서 표현의 최소화 입니다. 지난시간에는 서로다른 층의 다른 호실의 LED에 불이 들어오게 하는 방법과 패턴 LED가 일정시간 유지될 수 있도록 하는 방법을 배웠습니다. 이 두가지를 꼭 기억하셔서 다른 곳에서도 활용해 보세요.

마지막으로 상단에 가상 시뮬레이터에서 실험한 회로도가 공개 시켰네요. 혹시 회로도 만들기 귀찮은 분들을 위해서 패턴만 만들어보시라고 공개 했네요. 가셔서 계정이 있으면 복사하시면 본인 계정으로 회로도가 복사 됩니다. 거기서 편하게 편집하시면 됩니다.


댓글()

[아두이노] 3x3x3 CUBE LED 패턴 코딩 I

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

[아두이노] 3x3x3 CUBE LED 패턴 코딩 I



지난 시간에 3x3x3 CUBE LED 코딩을 간소화 하는 방법을 살펴보았습니다. 오늘은 좀 더 깊게 들어가서 LED를 깜박이는 패턴을 만들어보는 시간을 갖도록 하겠습니다. 그냥 순차적으로 깜박이는 것은 for문 하나면 해결되지만 서로 다른 층과 서로 다른 호실에 동시에 불이 들어오게 하는 방법은 좀 다른 관점의 코딩이기 때문입니다. 그 이유는 동시에 서로 다른 층의 다른 호실에 불이 들어오게 할 수 없습니다. 왜냐면 각 호실은 같은 층의 연결되어 있고 각층은 동일 호실과 선이 연결되어 있기 때문입니다. 만약, 1층 1호실과 2층 2호실에 불이 들어오게 하려고 1층과 2층을 동시에 개방하면 1층 1,2호실과 2층 1,2호실에 LED가 켜지게 됩니다. 이 부분에 대해서 간단히 살펴보고 그걸 해결하기 위한 방법을 설명 드리겠습니다. 사실 힘들게 뻥판 노가다로 전선을 연결했는데 다시 원위치 시키기 아쉬워서 패턴이라는 내용으로 포스팅을 한편 더 작성하네요.


1층과 2층의 LED만 사용합니다. 전선부족으로 지난시간에 2,3층 3개의 LED에 선을 연결못했는데 3층선 일부를 2층선으로 옮겨와서 1,2층 선을 모두 연결한 상태에서 실험했네요.

1. 서로 다른 층과 호실에 동시에 불을 켤 수 없는가?


1층 1호실과 2층 2호실에 동시에 불이 들어오게 한다고 가정해 봅시다. 그러면 1층과 2층을 Gnd(-)로 개방해야하잖아요. 그런데 1층(Gnd)와 1호실(Vcc)로 해서 1층 1호실에 불이 들어옵니다. 하지만 2층 2호실의 경우도 동시에 2층(Gnd)와 2호실(Vcc)을 하게 된다면 아래와 같은 현상이 발생합니다.


1층 1,2호실과 2층 1,2호실에 불이 전부 들어오게 됩니다. 선이 층을 기준으로 모든 호실이 하나로 선(Gnd)이 연결되어 있고 호실을 기준으로 모든 층이 선(Vcc)로 연결되어 있기 때문입니다. 그래서 동시에 불이 들어오게는 불가능 합니다. 그럼 해결책이 없느냐고 물으신다면 바로 딜레이 시간을 줘서 1층과 2층을 짧은 시간에 개방하고 닫고 해서 1층1호실에 불이 들어오게 한뒤에 2층 2호실에 불이 들어오게 함으로써 착시현상이 일어나게 처리하면 해결 할 수 있습니다.


위 그림에서 A동작을 수행시 불이 충분히 켜질 시간(delay) 주고 난 뒤에 바로 B동작을 수행합니다. 참고로 A동작과 B동작의 딜레이 시간은 아주 짧아야 합니다. 착시현상으로 동시에 불이 들어오는 것처럼 느끼게 됩니다.


대충 위 그림처럼 착시로 동시에 불이 들어오게 보여야 합니다. 이제 어떻게 하는지 알아보도록 하겠습니다.

2. 3x3x3 CUBE LED 회로도 구성


  • 준비물 : Red, Blue, Green LED 각각 9개, 저항 220옴 3개, 아두이노우노, 뻥판
  • 내용 : 3x3x3 CUBE LED 형태를 표현하자.


힘들게 디자인 했으니 그대로 활용하겠습니다.

3. 패턴 코딩 과정


  • 사용함수 : pinMode(), digitalWrite(), delay()
  • 내용 : 간단히 2x2x2 CUBE LED를 순차적으로 깜박이기
  • 참고 : [아두이노] LED 제어

복습

  • pinMode(사용핀, 사용모드) : 사용핀을 INPUT/OUTPUT/INPUT_PULLUP 모드중 뭘로 사용할지를 정함.
  • digitalWrite(사용핀, 출력형태) : 사용핀을 HIGH(5V) or LOW(0V) 중 출력 형태를 정함.
  • delay(1000) : 1초(1000)를 대기한다.

3x3x3 CUBE LED 순차적 깜박이는 코드

int layer[3] = {A0,A1,A2};
int room[9] = {10,9,8,7,6,5,4,3,2};
int m_layer = 3;  // 층 수
int m_room = 9; // 호실 수

void setup()
{
  for(int i=0;i<m_layer;i++){
    pinMode(layer[i], OUTPUT);
    digitalWrite(layer[i], HIGH);    
  }
  
  for(int i=0;i<m_room;i++){
    pinMode(room[i], OUTPUT);
  }
}

void loop()
{
  
  for(int i=0;i<m_layer;i++){
    digitalWrite(layer[i], LOW);  
    for(int j=0;j<m_room;j++){
      digitalWrite(room[j], HIGH);  
      delay(100);
      digitalWrite(room[j], LOW);  
    }
    digitalWrite(layer[i], HIGH);  
  }
}

여기서, 다른 부분은 그냥두고 이부분만 수정하시면 됩니다.

  for(int i=0;i<m_layer;i++){
    digitalWrite(layer[i], LOW);  
    for(int j=0;j<m_room;j++){
      digitalWrite(room[j], HIGH);  
      delay(100);
      digitalWrite(room[j], LOW);  
    }
    digitalWrite(layer[i], HIGH);  
  }

이 명령은 순차적으로 깜박이는 로직입니다. 여기서 1층 1호실과 2층 2호실에 불이 동시에 들어오게 하려면 어떻게 해야 할까요. 1,2층 개방/닫힘 명령과 1,2호실 개방/닫힘 명령이 필요합니다.

  • 층 개방 : digitalWrite(해당층핀, LOW);
  • 층 닫힘 : digitalWrite(해당층핀, HIGH);
  • 호실 개방 : digitalWrite(호실핀, HIGH);
  • 호실 닫힘 : digitalWrite(호실핀, LOW);

[ 1층1호실과 2층2호실 불 들어오게 하기 ]

위 코드에서 layer[0]은 1층, layer[1]은 2층, layer[2]은 3층이고 room[0]은 1호실, room[1]은 2호실, room[2]은 3호실, room[3]은 4호실입니다. 배열변수로 그렇게 선언했기 때문에 이점을 생각하고 코딩을 해보도록 할까요.

먼저, 1층 1호실을 불이 들어오게 명령을 내려 볼까요.

digitalWrite(layer[0], LOW);  // 1층 개방
digitalWrite(room[0], HIGH);  // 1호실 개방
delay(10); //0.01초 동안 불 켜기
digitalWrite(room[0], LOW);  // 1호실 닫힘
digitalWrite(layer[0], HIGH);  // 1층 닫힘

다음 2층 2호실에 불이 들어오게 명령을 내려 볼까요.

digitalWrite(layer[1], LOW);  // 2층 개방
digitalWrite(room[1], HIGH);  // 2호실 개방
delay(10); //0.01초 동안 불 켜기
digitalWrite(room[1], LOW);  // 2호실 닫힘
digitalWrite(layer[1], HIGH);  // 2층 닫힘

결과는 다음과 같습니다.


[ 소스 ]

int layer[3] = {A0,A1,A2};
int room[9] = {10,9,8,7,6,5,4,3,2};
int m_layer = 3;  // 층 수
int m_room = 9; // 호실 수

void setup()
{
  for(int i=0;i<m_layer;i++){
    pinMode(layer[i], OUTPUT);
    digitalWrite(layer[i], HIGH);    
  }
  
  for(int i=0;i<m_room;i++){
    pinMode(room[i], OUTPUT);
  }
}

void loop()
{
  digitalWrite(layer[0], LOW);  // 1층 개방
  digitalWrite(room[1], HIGH);  // 1호실 개방
  delay(10); //0.01초 동안 불 켜기
  digitalWrite(room[1], LOW);  // 1호실 닫힘
  digitalWrite(layer[0], HIGH);  // 1층 닫힘
  
  digitalWrite(layer[1], LOW);  // 2층 개방
  digitalWrite(room[0], HIGH);  // 2호실 개방
  delay(10); //0.01초 동안 불 켜기
  digitalWrite(room[0], LOW);  // 2호실 닫힘
  digitalWrite(layer[1], HIGH);  // 2층 닫힘
}

[ 결과 ]



[ 1층2호실과 2층1호실 불 들어오게 하기 ]

위 코드를 반대로 표현하면 됩니다.

digitalWrite(layer[0], LOW);  // 1층 개방
digitalWrite(room[1], HIGH);  // 2호실 개방
delay(10); //0.01초 동안 불 켜기
digitalWrite(room[1], LOW);  // 2호실 닫힘
digitalWrite(layer[0], HIGH);  // 1층 닫힘

다음 2층 2호실에 불이 들어오게 명령을 내려 볼까요.

digitalWrite(layer[1], LOW);  // 2층 개방
digitalWrite(room[0], HIGH);  // 1호실 개방
delay(10); //0.01초 동안 불 켜기
digitalWrite(room[0], LOW);  // 1호실 닫힘
digitalWrite(layer[1], HIGH);  // 2층 닫힘

결과는 다음과 같습니다.


[ 소스 ]

... 생략

void loop()
{
  digitalWrite(layer[0], LOW);  // 1층 개방
  digitalWrite(room[1], HIGH);  // 2호실 개방
  delay(10); //0.01초 동안 불 켜기
  digitalWrite(room[1], LOW);  // 2호실 닫힘
  digitalWrite(layer[0], HIGH);  // 1층 닫힘
  
  digitalWrite(layer[1], LOW);  // 2층 개방
  digitalWrite(room[0], HIGH);  // 1호실 개방
  delay(10); //0.01초 동안 불 켜기
  digitalWrite(room[0], LOW);  // 1호실 닫힘
  digitalWrite(layer[1], HIGH);  // 2층 닫힘
}

[ 결과 ]



[ 1층1호실과 2층2호실의 불을 일정시간 단위로 깜박이기 ]

하나의 패턴은 그냥 loop()함수로 돌리면 그 패턴의 LED만 불이 들어옵니다. 둘 이상의 경우는 해당 패턴의 모양이 일정시간 유지해야 합니다. 하지만 연속해서 표현하면 아주 짧게 딜레이를 줬기 때문에 해당 패턴모양이 원하는 시간동안 유지하지 못하는 현상이 발생합니다. 또는 두가지 패턴이 하나의 패턴으로 겹치는 현상도 발생합니다. 그래서 하나의 패턴을 일정시간 유지시키기 위해서는 딜레이 함수와 같은 효과를 코딩으로 표현해야 합니다.

딜레이를 아주 짧게 줬기 때문에 짧게 패턴을 일정횟수만큼 반복하면 반복된 시간만큼이 해당 패턴의 모양을 유지하는 시간으로 만들어 낼 수 있습니다.

가령,

 for(int i=0;i<30;i++){ //for문이 delay()함수 효과
   digitalWrite(layer[0], LOW);  // 1층 개방
   digitalWrite(room[0], HIGH);  // 1호실 개방
   delay(10); //0.01초 동안 불 켜기
   digitalWrite(room[0], LOW);  // 1호실 닫힘
   digitalWrite(layer[0], HIGH);  // 1층 닫힘
  
   digitalWrite(layer[1], LOW);  // 2층 개방
   digitalWrite(room[1], HIGH);  // 2호실 개방
   delay(10); //0.01초 동안 불 켜기
   digitalWrite(room[1], LOW);  // 2호실 닫힘
   digitalWrite(layer[1], HIGH);  // 2층 닫힘
}

표현을 하면 1층1호실은 0.01초만큼 불이들어온 후에 2층2호실은 0.01초만큼 불이 들어오게 됩니다. 총 합산하면 0.02초만큼 1층1호실과 2층2호실의 시간이 소요됩니다. 그걸 30회 반복한다고 했죠. 보는 수치상으로 계산하면 0.6초여야 동안 같은 패턴을 유지하게 된다고 생각하시면 됩니다. 그런데 실제로 돌리시면 0.06초동안 유지되는게 아니라 명령어 수행시간도 합산하면 미세하지만 좀 늘어나겠죠. 신경 쓸 필요는 없는 부분이고요. 30번 반복이란 예를 든 것일뿐 다른 값으로 어느정도 시간이 유지되는지를 테스트 해보고 원하는 시간으로 보정을 해보세요. for문 안에 delay(10)도 그냥 제 아두이노로 실험했을때 대충 정한 값이라 이 값이 크면 착시효과가 낮으니깐 어느정도 다른 값들을 대입해보면서 원하는 딜레이시간으로 보정해 해보세요.

[ 1층1호실과 2층2호실 패턴과 1층2호실과 2층1호실 패턴을 교차 ]

위 for문에 반대패턴을 코딩한걸 삽입하면 되겠죠.

[ 소스 ]

... 생략

void loop()
{
    //1층1호실과 2층2호실
     for(int i=0;i<30;i++){ //for문이 delay()함수 효과
         digitalWrite(layer[0], LOW);  // 1층 개방
         digitalWrite(room[0], HIGH);  // 1호실 개방
         delay(10); //0.01초 동안 불 켜기
         digitalWrite(room[0], LOW);  // 1호실 닫힘
         digitalWrite(layer[0], HIGH);  // 1층 닫힘
  
         digitalWrite(layer[1], LOW);  // 2층 개방
         digitalWrite(room[1], HIGH);  // 2호실 개방
         delay(10); //0.01초 동안 불 켜기
         digitalWrite(room[1], LOW);  // 2호실 닫힘
         digitalWrite(layer[1], HIGH);  // 2층 닫힘
    }
    
    //1층2호실과 2층1호실
    for(int i=0;i<3[](http://)0;i++){ //for문이 delay()함수 효과
        digitalWrite(layer[0], LOW);  // 1층 개방
        digitalWrite(room[1], HIGH);  // 2호실 개방
        delay(10); //0.01초 동안 불 켜기
        digitalWrite(room[1], LOW);  // 2호실 닫힘
        digitalWrite(layer[0], HIGH);  // 1층 닫힘
  
        digitalWrite(layer[1], LOW);  // 2층 개방
        digitalWrite(room[0], HIGH);  // 1호실 개방
        delay(10); //0.01초 동안 불 켜기
        digitalWrite(room[0], LOW);  // 1호실 닫힘
        digitalWrite(layer[1], HIGH);  // 2층 닫힘
    }
}

[ 결과 ]

위 그림에서 보는것처럼 교차로 깜박이게 됩니다. 이런식으로 해서 패턴을 만들고 그 패턴을 일정시간동안 유지시킬 수 있게 되면은 다양한 패턴을 만들어 낼 수 있겠죠.

마무리


이번 포스팅은 원래 패턴 코딩을 한편으로 제작할려고 했던 내용입니다. 그런데 포스팅의 글을 쓰다가 보니 아직 제대로 코딩하는 법으로 들어가지 못했는데 분량이 장난 아니게 늘어났네요. 어쩔 수 없이 다음편으로 연장해야 할 듯 싶네요. 너무 길게 쓰면은 피로감이 밀려올 수 있으니 이번 포스팅은 패턴을 만들고 그 패턴을 일정시간 유지하는 것에서 마무리 하도록 하겠습니다.

다음편에서는 이렇게 패턴을 만들고 for문을 통해서 일정시간을 유지시키는 방법으로 매 패턴들을 표현한다면 코딩량이 엄청나겠죠. 단 두가지 패턴을 코딩했는데도 이정도인데 10개이상 되는 패턴을 만든다면 노가다 코딩이 되겠죠.

그래서 중복되는 부분을 제거하는 코딩을 다음 포스팅에 연재 하도록 하겠습니다. 오늘은 이렇게 패턴을 만드는 것과 일정시간 유지하는 걸 배웠으니 어떤 패턴을 만들지 머리속에서 구상해보세요. 사실 전체과정을 동영상으로 기록하고 실제 아두이노에서도 결과까지 다 준비 됐는데 그건 다음에 올리도록 하겠습니다.

댓글()

[아두이노] 3x3x3 CUBE LED 제어 III

IOT/아두이노|2019. 3. 21. 09:00

[아두이노] 3x3x3 CUBE LED 제어 III



오늘은 좀 더 프로그램 코딩에 관해서 이야기 하도록 하겠습니다. 그리고 제목을 3x3x3 CUBE LED를 다룬다고 해놓고 지난시간에 2x2x2 CUBE LED를 실험해서 좀 찜찜한 기분이 들어서 노가다를 감수하고 코딩이 간소화 되니깐 3x3x3 CUBE LED를 해도 되겠다 싶어서 회로도 디자인을 했네요. 우선 배치도를 구상하고 최대한 보기 편하게 만들려고 노력했네요. 전개는 우선 지난시간에 코딩한 2x2x2 CUBE LED 코딩을 기반으로 프로그래머 스타일의 문법을 가미하면서 간소화 시키는 것을 설명드리고 그다음 간소화한 코딩을 3x3x3 CUBE LED 회로도에 맞게 변경하여 실험하는 결과를 보여드리도록 하겠습니다.

1. 2x2x2 CUBE LED 코드 간소화


아래 코드만 봐도 머리가 아프죠. 이걸 프로그램 문법을 이용해서 간소화 하도록 하겠습니다.

int layer1 = 11;
int layer2 = 12;

int room1 = 2;
int room2 = 3;
int room3 = 4;
int room4 = 5;

void setup()
{
  pinMode(layer1, OUTPUT);
  pinMode(layer2, OUTPUT);

  pinMode(room1, OUTPUT);
  pinMode(room2, OUTPUT);
  pinMode(room3, OUTPUT);
  pinMode(room4, OUTPUT);

  //초기화 각 층을 막아놓습니다.
  digitalWrite(layer1, HIGH);
  digitalWrite(layer2, HIGH);
}

void loop()
{
  //1층 점등
  digitalWrite(layer1, LOW);  
  digitalWrite(room1, HIGH);  
  delay(1000);
  
  digitalWrite(room1, LOW);  
  digitalWrite(room2, HIGH);  
  delay(1000);
  
  digitalWrite(room1, LOW);  
  digitalWrite(room2, HIGH);  
  delay(1000);
  
  digitalWrite(room2, LOW);  
  digitalWrite(room3, HIGH);  
  delay(1000);

  digitalWrite(room3, LOW);  
  digitalWrite(room4, HIGH);  
  delay(1000);
  
  digitalWrite(room4, LOW);  
  digitalWrite(layer1, HIGH);  
  delay(1000);
  
  //2층 점등
  
  digitalWrite(layer2, LOW);  
  digitalWrite(room1, HIGH);  
  delay(1000);
  
  digitalWrite(room1, LOW);  
  digitalWrite(room2, HIGH);  
  delay(1000);
  
  digitalWrite(room1, LOW);  
  digitalWrite(room2, HIGH);  
  delay(1000);
  
  digitalWrite(room2, LOW);  
  digitalWrite(room3, HIGH);  
  delay(1000);

  digitalWrite(room3, LOW);  
  digitalWrite(room4, HIGH);  
  delay(1000);
  
  digitalWrite(room4, LOW);  
  digitalWrite(layer2, HIGH);  
  delay(1000);

}

1) 변수를 배열변수로 바꾼다.

기존수정
int layer1 = 11; int layer2 = 12;int layer[2] = {11, 12};
int room1 = 2; int room2 = 3; int room3 = 4; int room4 = 5;int room[4] = { 2, 3, 4, 5}




2) for문을 사용하여 중복 코드를 제거한다.

기존 코드

  pinMode(layer1, OUTPUT);
  pinMode(layer2, OUTPUT);
  //초기화 각 층을 막아놓습니다.
  digitalWrite(layer1, HIGH);
  digitalWrite(layer2, HIGH);

수정 코드

    for(int i=0;i<2;i++){
        pinMode(layer[i], OUTPUT);
        digitalWrite(layer[i], HIGH);
  }

기존 코드

  pinMode(room1, OUTPUT);
  pinMode(room2, OUTPUT);
  pinMode(room3, OUTPUT);
  pinMode(room4, OUTPUT);

수정 코드

    for(int i=0;i<4;i++){
        pinMode(room[i], OUTPUT);
    }

3) void loop() 안의 코드를 간소화

수정 코드

  for(int i=0;i<2;i++){
    digitalWrite(layer[i], LOW);              
    for(int j=0;j<4;j++){
           digitalWrite(room[j], HIGH);  
           delay(1000);
           digitalWrite(room[j], LOW);  
    }    
    digitalWrite(layer[i], HIGH);      
  }

1,2번의 수정은 코드만 보면 대충 뭘 줄였는지 알 수 있지만 3번 코드 간소화 경우는 로직이라서 간단히 설명을 드리겠습니다. for문은 순차적으로 반복하는 문법입니다.

예)

for(int i=0;i<3;i++) { 
    Serial.println("Hellow" );
}

for문은 i가 0시작해서 i<3보다 작을때까지 반복합니다. 뒤에 i++은 증감연산자로 'i=i+1'로 생각하시면 됩니다. for문이 한번씩 돌때마다 i++로 i가 1씩 증가하는데 그렇게 되면 결과는 다음과 같습니다.

결과 :

Hellow
Hellow
Hellow

이렇게 i가 0부터 0,1,2까지 참임으로 for문 안의 명령어 Serial.println()함수를 수행하지만 i가 3이되면 거짓으로 for문을 빠져 나옵니다.

이제 for문을 이해했으면 왜 수정코드를 이렇게 코딩했을까요.

2x2x2 cube led은 1층 4개, 2층 4개의 LED로 구성되어 있잖아요. 여기서 1층부터 순차적으로 1,2,3,4호실에 불이 들어와야 하니깐 4번을 순차적으로 같은 동작을 수행해야 합니다. 그래서 for문으로 4번을 순차적으로 반복할려면 아래와 같은 구조가 됩니다.

for(j=0;j<4;j++){
  명령문;
}

1호실 부터 1초 단위로 불이 들어올려면 1호실만 HIGH 상태가 1초동안 대기 되었다가 다시 LOW로 바뀌고 2호실, 3호실, 4호실 순으로 반복되어야 겠죠.

digitalWrite(layer[i], LOW);  // 해당 i층 Gnd 상태 (해당층 개방)
for(int j=0;j<4;j++){
  digitalWrite(room[j], HIGH);  //해당 j호실 전원 공급
  delay(1000);
  digitalWrite(room[j], LOW);  //해당 j호실 전원 차단  
}
digitalWrite(layer[i], HIGH);  //해당 i층 HIGH 상태 (해당층 닫힘)

이렇게 되는 것이죠. 그리고 Vcc와 Gnd은 한쌍이라고 했죠. 1층을 Gnd(-)로 하려면 1층을 LOW상태로 만들어 합니다. 그래서 for문 밖에다가 1층을 LOW상태로 만들었네요. 왜 안에다가 안넣고 밖에다가 표현했을까요. 그 이유는 아두이노가 1층을 Gnd 상태로 만들었는데 또다시 Gnd 상태 만들어라고 반복 명령을 내리면 비효율적인 코딩이 되잖아요. 구지 반복 명령을 할 필요없이 한번만 해도 되는 문장은 for문에 넣을 필요는 없습니다.

여기서 for문이 2중 for문으로 구성되었는데 그 이유는 1층 1,2,3,4호실을 깜박였으면 2층 1,2,3,4호실 깜박여야 하잖아요. 각 호실을 깜박이는 동작을 for문으로 만들었는데 그 for문을 2층이니깐 2번 반복해야 하니깐 2중 for문을 만들어야 합니다.

for(int i=0;i<2;i++){ // i=0,1 일때까지만 2번 반복 (층 반복)
 해당층 개방;
 for(int j=0;j<4;j++){ // j=0,1,2,3 일때까지만 4번 반복 (호실 반복)
    명령문;
 }
 해당층 닫힘;
}

그래서 아래와 같은 로직이 나오게 된 것이죠.

    for(int i=0;i<2;i++){
        digitalWrite(layer[i], LOW);  // 해당 i층 Gnd 상태 (해당층 개방)
        for(int j=0;j<4;j++){
            digitalWrite(room[j], HIGH);  //해당 j호실 전원 공급
            delay(1000);
            digitalWrite(room[j], LOW);  //해당 j호실 전원 차단  
        }
     digitalWrite(layer[i], HIGH);  //해당 i층 HIGH 상태 (해당층 닫힘)
    }

전체적으로 종합해보면

int layer[2] ={11,12};
int room[4] = {2,3,4,5};


void setup()
{
  for(int i=0;i<2;i++){
    pinMode(layer[i], OUTPUT);
    digitalWrite(layer[i], HIGH);
  }
  
  for(int i=0;i<4;i++){
    pinMode(room[i], OUTPUT);
  }
}

void loop()
{
    for(int i=0;i<2;i++){
        digitalWrite(layer[i], LOW);  // 해당 i층 Gnd 상태 (해당층 개방)
        for(int j=0;j<4;j++){
            digitalWrite(room[j], HIGH);  //해당 j호실 전원 공급
            delay(1000);
            digitalWrite(room[j], LOW);  //해당 j호실 전원 차단  
        }
     digitalWrite(layer[i], HIGH);  //해당 i층 HIGH 상태 (해당층 닫힘)
    }
}

2. 3x3x3 CUBE LED 회로도 구성


  • 준비물 : Red, Blue, Green LED 각각 9개, 저항 220옴 3개, 아두이노우노, 뻥판
  • 내용 : 3x3x3 CUBE LED 형태를 표현하자.


입체적 이미지를 평면적으로 뻥판에 나타내면 아래와 같은 회로도가 그려집니다.


실제로 제작을 한다면


위 그림처럼 철사를 사각형으로 만들고 빨간 꼭지점에 LED의 '-' 쪽을 각 꼭지점에 납땜하시면 됩니다. 그러면 총 9개가 하나의 극으로 연결이 되는데 이걸 1층이라고 생각하시면 됩니다. 이걸 3개를 만들어서 LED의 '+'쪽을 세로로 연결하시면 3층이 완성되어 전체 이미지는 3x3x3 CUBE LED가 됩니다. 실제로 만들면 무지 간단한데 이걸 가상시뮬레이터에서 표현하고 뻥판에 배치하니깐 무지 복잡해 보이네요.

최대한 보기 쉽게 표현 한건데 이것 역시 완성하고 나니깐 지난시간의 회로도보다 더 복잡하네요.

3. 코딩


  • 사용함수 : pinMode(), digitalWrite(), delay()
  • 내용 : 간단히 2x2x2 CUBE LED를 순차적으로 깜박이기
  • 참고 : [아두이노] LED 제어

복습

  • pinMode(사용핀, 사용모드) : 사용핀을 INPUT/OUTPUT/INPUT_PULLUP 모드중 뭘로 사용할지를 정함.
  • digitalWrite(사용핀, 출력형태) : 사용핀을 HIGH(5V) or LOW(0V) 중 출력 형태를 정함.
  • delay(1000) : 1초(1000)를 대기한다.

2x2x2 CUBE LED 간소화 코딩을 3x3x3 CUBE LED 형태로 수정

int layer[3] = {A0,A1,A2};
int room[9] = {10,9,8,7,6,5,4,3,2};
int m_layer = 3;  // 층 수
int m_room = 9; // 호실 수

void setup()
{
  for(int i=0;i<m_layer;i++){
    pinMode(layer[i], OUTPUT);
    digitalWrite(layer[i], HIGH);    
  }
  
  for(int i=0;i<m_room;i++){
    pinMode(room[i], OUTPUT);
  }
}

void loop()
{
  
  for(int i=0;i<m_layer;i++){
    digitalWrite(layer[i], LOW);  
    for(int j=0;j<m_room;j++){
      digitalWrite(room[j], HIGH);  
      delay(100);
      digitalWrite(room[j], LOW);  
    }
    digitalWrite(layer[i], HIGH);  
  }
}

층수와 호실수를 변수로 따로 빼서 setup(), loop() 함수 로직은 이제 수정하지 않는 방향으로 변경했네요. 이렇게 하면 위에 변수 선언부분만 수벙하시면 2x2x2 CUBE LED로 바꿀 수 있고 3x3x3 CUBE LED로도 쉽게 변경이 가능해집니다.

4. 결과


지난 시간에 가상시뮬레이터 결과가 지연 렉 때문에 정상적으로 결과가 안나와서 실제 아두이노로 표현했는데 브라우저 문제였네요. 오늘 크롬에서 가상시뮬레이터를 돌리니깐 어제 1초단위로 깜박이는 동작이 깔끔하게 보여줬네요. 오늘도 가상시뮬레이터로 돌린 결과와 현실 아두이노에서 돌린 결과를 실제 촬영해서 기록했네요.

그런데 실제 아두이노에서는 전선이 부족하여 정상적으로 동작은 하지만 일부 LED은 연결할 수 없어서 일부 LED에 불이 안들어온 점은 감안하고 보세요. 최대한 선들을 구햇지만 선 부족으로 1층은 9개 연결을 다 했지만 2,3층은 6개까지 연결하고 나니깐 더이상 선이 없어서 2,3층 3개의 LED은 돌릴 수 없었네요. 그래도 전달하고자하는 실험 내용의 결과는 정상적으로 얻었으며 안켜진 LED은 오류가 아니니깐 감안하시고 보세요.

추가 내용

마지막 동영상 장면에서 delay()시간차 연결을 몇초 담았는데요. 혹시 궁금하실분이 잇을 것같아서 만약 1층1호실 2층 2호실을 같은 시간대 불이 들어오게 할려면 어떻게 해야할지 의문을 품는 분들이 아마 있을꺼에요. 큐브 자체가 회로도를 저리 구성하기 때문에 물리적으로는 사실 어렵습니다. 하지만 인간의 시각을 이용한 방법으로 착시 효과로 표현은 가능합니다. 이 말은 인간의 눈은 두 LED가 있으면 delay()시간을 아주 짧게 주면 동시에 불이 들어오는 것처럼 착시 현상이 발생합니다. 아두이노에서는 그 착시효과를 이용해서 delay()시간으로 각 층에 LED에 불을 넣으면 됩니다.

  digitalWrite(layer[0], LOW);  
  digitalWrite(room[0], HIGH);   
  delay(20);
  digitalWrite(room[0], LOW);
  digitalWrite(layer[0], HIGH);

  digitalWrite(layer[1], LOW);  
  digitalWrite(room[2], HIGH);   
  delay(20);
  digitalWrite(room[2], LOW);   
  digitalWrite(layer[1], HIGH); 

즉, 1층 1호실에 불이 0.02(20)초 동안 전원 공급했다가 차단하고 바로 2층 2호실에 0.02(20)초 동안 정원 공급하면 어떻게 될까요. 분명 코딩상으로 차이가 나지만 시각적으로 보면 delay(20)초 간격으로 1층 1호실과 2층 2호실이 동시에 불이 들어오는 것처럼 착시효과가 발생합니다. 이러한 방법으로 서로 다른층의 LED를 동시에 불을 들어오게 합니다.

위 그림을 보듯이 짧게 딜레이를 줘서 각 층의 원하는 위치에 거의 동시에 불이들어오게 착시효과를 나타내고 잇네요.

마무리


원래는 한개의 포스팅으로 3편의 내용을 함축하고 싶었는데 그냥 회로도 그리고 코딩하고 결과만 딸랑 보여주면 별로 의미가 없을 것 같아서 최대한 많은 것을 설명할려고 노력했는데 빠진 부분이 아직도 너무 많네요. RGB LED를 이용한 CUBE LED도 표현하면 좋은데 실제 보유한 RGB LED가 한개 뿐이고 가상시뮬레이터로 표현하자니 2핀짜리 LED 선연결도 이리 복잡한데 4핀짜리 RGB LED를 큐브 모양으로 만들 엄두가 안나네요. 원리를 이번 포스팅에 설명한 원리랑 같기 때문에 도전하고 싶은 분들은 도전해보세요. 지금 표현한 것에 3배 노가다를 하시면 충분히 표현이 가능하실꺼에요.
전 도저히 못하겠네요. 도전하실분들은 RGB LED를 큐브로 만들어 보세요.


댓글()

[아두이노]3x3x3 CUBE LED 제어 II

IOT/아두이노|2019. 3. 20. 09:00

[아두이노]3x3x3 CUBE LED 제어 II



지난 시간에 간단히 3x3x1 LED를 제어 해봄으로써 3x3x3 CUBE LED의 원리를 이해 했습니다. 오늘 실험에서는 3x3x3 CUBE LED를 실험할 예정이였지만 실제 구현까지 할려고 하니 총 27개 LED를 다뤄야 하고 뻥판에 배치하자니 지져분해지고 오히려 의미 전달이 잘 안될 것 같아요. 2x2x2 CUBE LED 간단히 소개할까 합니다.

1. 3x3x3 CUBE LED


모습은 아래 그림처럼 구성되어 있습니다. 가로로 보시면 Gnd(-) 부위에 녹색선으로 사각형 모양을 만들어 하나로 선을 만듭니다. 그게 세로로 보는 것 처럼 3층으로 이루어진 것을 3x3x3 CUB LED라고 부릅니다.

[ 세로 ][ 가로 ]


여기서, 무조건 Gnd(-)로 9개 LED를 하나로 묶어을 필요는 없습고 반대로 Vcc(+)로 9개 LED를 하나로 묶을 수 있습니다. 그냥 선택사항일 뿐이죠. 저는 9개 LED를 Gnd로 묶어서 하나의 선으로 만든 것일 뿐이죠.

이걸 실험할려면 총 27개의 LED를 제어를 해야하고 실제로 회로도를 만들면 좀 복잡해 질 수 있어서 의미 전달보다 시각적으로 복잡하게 느낄 수 있어서 차라리 2x2x2 CUBE LED를 실험하는게 의미 전달하는데 더 나을 것 같아서 수정하였습니다.

2. 2x2x2 CUBE LED


2x2x2 CUBE LED 모습인데 가상시뮬레이터에서 그릴려고 하니깐 모양이 참 애매하네요. 대충 이런식으로 배치한다고 생각하시면 됩니다.


실제로 CUBE 모양을 만들려면 납땡을 하고 철사가 필요한데 납땜 도구를 찾아서 제작을 할까 고민을 하다가 CUBE를 만드는데 LED를 다쓰면 나중에 다른 실험을 할때 LED가 부족할 것 같았서 뻥판에 배치하여 그 의미만 전달하는 걸로 표현했습니다.


[ 실행 결과 ]


녹색은 1층을 나타내고 노랑은 2층으로 생각하시면 됩니다. 위 그림에서 각 호실은 오렌지 1호실, 블루 2호실, 엘로우 3호실, 화이트 4호실를 가리킵니다. 여기서, 오렌지 1호실은 2층 1호실과 연결되고요. 블루, 엘로우, 화이트 들은 각층에 해당 호실과 선이 연결됩니다. 각층 Gnd(-)은 아두이노의 Gnd(-)에 연결 되었고, 각 오렌지, 블루, 엘로우, 화이트 선은 아두이노의 Vcc(5V)에 연결되었습니다. 결론은 각 층은 Gnd(-) 연결하고 각호실에 Vcc(5V)에 모두 연결되었기 때문에 전부 불이 들오게 됩니다.

만약, 오렌지 1호실만 Vcc(5V)에 연결하고 각층 Gnd(-)은 전부 연결했다며 아래 그림처럼 1층 1호실, 2층 1호실에 불이 들어 오겠죠. 다시 정리하자면, Vcc의 전류가 엘로우 1호선만 공급되는데 1층, 2층 중 Gnd(-)로 모두 연결되었기 때문에 둘다 전류가 흘러가기 때문에 각 층 1호실에 불이 들어오게 됩니다. 만약 1층이 Gnd(-)와 연결이 끊어졌고 2층만 Gnd(-)에 연결되었다면 2층 1호실만 불이 들어 오겠죠.


이렇게 Vcc(5V)가 연결된 선을 따라서 최종 목적인 Gnd(-)로 연결된 곳만 전류가 정상적으로 흘러가게 됩니다. 이것만 이해하시면 됩니다.


[ 참고 ] 전류의 흐름

[예로 1층 1호실에 불이 들어왔다면]


전류의 흐름을 빨간선으로 표시한 부분이 정상적으로 연결되어 전류가 흘러가는 표시 이고요. 위 그림처럼 화살표 방향으로 흘러간다는 것을 참고해 주세요. 대충 어떤 느낌이신지 아시겠지요.


아두이노에서는 디지털 핀에 연결하여 제어를 하게 되는데 그 과정을 배워보도록 하겠습니다.

3. 회로도 구성


  • 준비물 : Green LED 4개, Yellow LED 4개, 저항 220옴 2개, 아두이노우노, 뻥판
  • 내용 : 2x2x2 CUBE LED 형태를 표현하자.


총 6개의 핀을 사용하게 됩니다. 2,3,4,5번 핀은 각 호실을 나타내는 핀이고 11,12번 핀은 층을 나타내는 핀입니다. 지금까지 LED 제어를 할때 Vcc(+) 방향에 저항을 붙여왔습니다. 하지만 Gnd(-)방향에 저항을 붙여 놓아도 상관 없습니다. 저항으로 흐르는 전류가 제어가 되기 때문에 어느 방향으로 저항을 붙이든 상관이 없다는 점을 참고해 주세요.

4. 코딩


  • 사용함수 : pinMode(), digitalWrite(), delay()
  • 내용 : 간단히 2x2x2 CUBE LED를 순차적으로 깜박이기
  • 참고 : [아두이노] LED 제어

복습

  • pinMode(사용핀, 사용모드) : 사용핀을 INPUT/OUTPUT/INPUT_PULLUP 모드중 뭘로 사용할지를 정함.
  • digitalWrite(사용핀, 출력형태) : 사용핀을 HIGH(5V) or LOW(0V) 중 출력 형태를 정함.
  • delay(1000) : 1초(1000)를 대기한다.

설계

이전 시간에 직접 전류를 선으로 Vcc(+), Gnd(-)를 연결해서 해당 위치의 불이 들어오게 만들었습니다. 그렇다면 아두이노에서는 어떻게 명령을 내려야 할까요.

1층 1호실에 불이 들어올려면 오레지(2)핀에 Vcc(5V)가 공급되고 1층(11)핀이 Gnd(-)가 되면 되겠죠. 직접 선을 연결한다는 생각을 하시면 됩니다.

그걸 코딩화 하면은 digitalWrite()함수를 사용해야 합니다.

  • 오렌지(2)핀이 Vcc(5V)가 공급 => digitalWrite(2, HIGH);
  • 1층(11)핀이 Gnd(0V)가 되어야 하니깐 => digitalWrite(11, LOW);

이렇게 표현하시면 됩니다.

1층을 순차적으로 깜박이게 하려면 어떻게 해야 할까요.

층별로는 1층(11)핀이 Gnd 상태이고 2층은 Gnd 상태가 아니면 되죠.

  • digitalWrite(11, LOW);
  • digitalWrite(12, HIGH);

이렇게 표현합니다. 왜 2층에 HIGH를 공급했을까요. 디지털 출력은 HIGH or LOW 두가지 상태만 존재합니다. 그리고 LED가 불이 들어올려면 Vcc와 Gnd가 연결해야 전류가 흘러서 LED에 불이 들어온다고 했죠. 가령 해당 LED가 Vcc쪽으로 5V를 공급하고 Gnd쪽으로 5V 상태면 서로 충돌하게 되잖아요. 그러면 전류가 흐를 수 없게 됩니다. 같은 극이 되면은 전류를 흐르지 않습니다. 이런 원리를 이용해서 LED를 제어하게 됩니다.

1층만 순차적으로 LED가 1,2,3,4 호실에 불이 들어올려면

digitalWrite(12, HIGH);

2층이 HIGH 상태에서 1층1호실을 불이 들어올려면

digitalWrite(11, LOW); 
digitalWrite(2, HIGH);
delay(1000);

2번 Vcc, 11번 Gnd로 1층 1호실에 불이 들어오고 1초간 대기한다는 의미입니다.

그러면 1층 2호실이면 같은 문장으로.

// 2층 잠금
digitalWrite(12, HIGH);

// 1층 개방
digitalWrite(11, LOW); 

// 1층 1호실 켜기
digitalWrite(2, HIGH);
delay(1000);

// 1층 1호실 끄고 2호실 켜기
digitalWrite(2, LOW);
digitalWrite(3, HIGH);
delay(1000);

.....

주석에서 설명하는 것처럼 켠 LED은 끄고 다음 LED은 켜고 이 문장을 반복해서 표현하면 됩니다.

2층으로 넘어갈때는

// 1층 잠금
digitalWrite(11, HIGH); 

// 2층 개방
digitalWrite(12, LOW); 

// 2층 1호실 켜기
digitalWrite(2, HIGH);
delay(1000);

// 2층 1호실 끄고 2층 2호실 켜기
digitalWrite(2, LOW);
digitalWrite(3, HIGH);
delay(1000);

.....

이렇게 반대로 생각하시면 됩니다.

전체적으로 코딩을 하면

int layer1 = 11;
int layer2 = 12;

int room1 = 2;
int room2 = 3;
int room3 = 4;
int room4 = 5;


void setup()
{
  pinMode(layer1, OUTPUT);
  pinMode(layer2, OUTPUT);

  pinMode(room1, OUTPUT);
  pinMode(room2, OUTPUT);
  pinMode(room3, OUTPUT);
  pinMode(room4, OUTPUT);

  //초기화 각 층을 막아놓습니다.
  digitalWrite(layer1, HIGH);
  digitalWrite(layer2, HIGH);
}

void loop()
{
  //1층 점등
  digitalWrite(layer1, LOW);  
  digitalWrite(room1, HIGH);  
  delay(1000);
  
  digitalWrite(room1, LOW);  
  digitalWrite(room2, HIGH);  
  delay(1000);
  
  digitalWrite(room1, LOW);  
  digitalWrite(room2, HIGH);  
  delay(1000);
  
  digitalWrite(room2, LOW);  
  digitalWrite(room3, HIGH);  
  delay(1000);

  digitalWrite(room3, LOW);  
  digitalWrite(room4, HIGH);  
  delay(1000);
  
  digitalWrite(room4, LOW);  
  digitalWrite(layer1, HIGH);  
  delay(1000);
  
  //2층 점등
  
  digitalWrite(layer2, LOW);  
  digitalWrite(room1, HIGH);  
  delay(1000);
  
  digitalWrite(room1, LOW);  
  digitalWrite(room2, HIGH);  
  delay(1000);
  
  digitalWrite(room1, LOW);  
  digitalWrite(room2, HIGH);  
  delay(1000);
  
  digitalWrite(room2, LOW);  
  digitalWrite(room3, HIGH);  
  delay(1000);

  digitalWrite(room3, LOW);  
  digitalWrite(room4, HIGH);  
  delay(1000);
  
  digitalWrite(room4, LOW);  
  digitalWrite(layer2, HIGH);  
  delay(1000);

}

코딩만 길게 표현이 된 것일뿐 LED을 켜고 끄고 이렇게 순차적으로 반복되는 문장일뿐 길다고 복잡하게 생각하실 필요는 없어요.

아래 두 표현으로만 코딩한 내용을 순차적으로 깜박이는 패턴을 만들려고 하니깐 전체적 코딩에서 길어지게 보였을분 아래 두 표현만 이해하시면 됩니다. 순차적으로 깜박이는게 중요한게 아니라 어떻게 전류를 흐르게 하고 차단을 하는지를 이해하시면 됩니다.

  digitalWrite(layer1, HIGH); //1층 잠금   
  digitalWrite(layer1, LOW); //1층 열림
  

  digitalWrite(room1, HIGH); //켜기 
  digitalWrite(room1, LOW);  //끄기

오늘 코딩한 표현은 흐름의 동작을 직설적으로 다 일일히 코딩으로 보여준거고요. 프로그램 문법을 사용하면 간단히 줄일 수 있습니다. 프로그램 문법보다는 일일히 동작하는지를 살펴보는게 주 목적입니다. for문을 사용하면 아주 간단하게 축소 시킬 수 있지만 오늘은 그 부분을 다루지 않을께요.

5. 결과


가상시뮬레이터에 부품이 많이 배치되면은 온라인상에서 실험하는거랑 렉이 좀 발생하네요. 실험 소스대로 하면 컴사양과 인터넷 사양에 따라 달라지겠지만 제 컴에서는 1초가 꽤 길게 느껴지더군요. 아주 짧은 시간으로 돌렸는데 첫음에는 빠르게 동작하다가 나중에는 거의 1초 단위로 순차적으로 깜작이더군요. 마지막에 실제 아두이노로 실험한 결과를 올려놨으니깐 젤 마지막 부분인 14분 20초부터 영상을 보시고 동작을 살펴보세요.

마무리


원래는 3x3x3 CUBE LED를 할려고 계획했는데 실제로 뻥판에 구현하는 장면까지 넣을려고 하니깐 뻥판이 너무 지져분해지고 의미 전달이 안될 것 같아서 2x2x2 CUBE LED로 변경했네요. 이 경우는 총 8개 LED만 제어하면 되기 때문에 코딩량도 적고 회로도 배치하는 것도 복잡하지 않고 실제 제작에서도 간단하게 실험할 수 있어서 좋은 것 같더군요.
개인적으로 3x3x3 CUBE LED를 실제로 납땜하면서 구현해보는게 가장 이상적이라고 생각되네요. 문제는 딱 제가 보유한 LED 개수가 딱 색상별로 10개씩 있어서 만약에 3x3x3 CUBE LED를 실제로 만들면 색상별로 1개씩 밖에 남지않아서 다음 실험에 LED를 활용할 수 없어서 실제로 제작한 모습은 보여드릴 수 없어서 아쉽네요. 이런건 실제로 만들어지는 걸 봐야 시각적 효과가 큰데 말이죠.

참고로 RGB LED로 구현도 가능합니다. 이경우는 총 4핀이여서 제작 과정이 좀 복잡하지만 원리는 동일합니다. 한번 연구해 보세요.


댓글()

[아두이노] 3x3x3 CUBE LED 제어 I

IOT/아두이노|2019. 3. 19. 09:00

[아두이노] 3x3x3 CUBE LED 제어 I



오늘의 주제는 흥미를 유발할 수 있는 3x3x3 CUBE LED 원리를 배우도록 하겠습니다. 지난 [아두이노] 스위치버턴을 이용한 Keypad 제어에서 스위치 버턴을 건물의 층과 호실로 나눠서 살펴봤는데 스위치 버턴은 아두이노로 입력을 받는 방식이라면 역으로 3x3x3 CUBE LED은 아두이노에서 LED로 출력하는 방식이라고 생각 하시면 됩니다. 스위치 버턴은 시각적으로 이해하기는 어렵지만 이번 포스팅 내용은 최대한 시각적인 표현을 통해 LED 제어하는 원리를 쉽게 이해할 수 있도록 표현해 보겠습니다.

1. 3x3x3 CUBE LED 원리


무료이미지 없고 실제로 제작한 것이 없어서 남의 이미지를 가져오는게 좀 문제가 있어서 가상시뮬레이터 최대한 표현은 해 봤네요. 검색어로 3x3x3 CUBE LED를 치시면 사진으로 화려한 이미지들을 볼 수 있을꺼에요. 이런걸 만드는 거구나 하고 우선 구경만 해보세요.


3x3x3 CUBE LED은 x,y 축으로 3x3 LED가 있고 z축으로 3x3 LED가 3개가 있는 것을 3x3x3 CUBE LED라고 부릅니다. 평면이 아닌 3차원 LED라고 생각하면 편하실꺼에요.

아래 그림은 x,y축으로 1개의 LED 단면을 나타낸 실험 그림입니다. 그림을 보면 5V 파워서플라이를 이용해서 전류를 보낼때 회로도 처럼 +, - 를 보내면 아래 한줄이 전부 불이 들어오는 결과를 보실 수 있을거에요. 왜 이렇게 불이 들어왔는지 그 원리에 대해서 공부해보도록 하겠습니다.

회로도결과

2. LED 원리



LED은 다리가 긴쪽이 '+' 이고 다리가 짧은쪽이 '-' 입니다. 가상시뮬레이터에서 사용하는 LED은 다리가 긴쪽이 꺽여있는 걸 보실꺼에요. 다리가 길다는 것을 표현한 이미지 입니다.


위 그림처럼 LED은 5V의 전류가 공급되면 바로 터져버립니다. 그래서 저항을 붙여줘서 전류의 양을 필요한 만큼만 공급되게 해야 아래와 같이 정상적으로 켜집니다. 220옴을 사용했네요. [아두이노] LED 제어 편에서 LED 색상별 필요한 전류가 표로 나와 있으니깐 참조하세요.



3. LED 동작



전류의 공급이 이루어진 LED만 불이 들어오는 것을 보실 수 있죠. 해당 LED에 Vcc(+) 전류가 공급되면 Gnd(-)핀과 쌍으로 연결된 LED에만 정상적으로 전류가 흐르니깐 LED에 불이 들어오게 됩니다. 이 동작 원리를 잘 이해해 주세요. 3x3x3 CUBE LED의 기본 동작 원리이기 때문입니다.


4. 3x3x3 CUBE LED 동작



위 LED 동작에서 보신 3개의 LED를 하나의 층으로 생각하고 총 3층으로 구성되었다고 생각하세요. 3개의 LED가 3개씩 총 9개가 배치된 그림입니다.

전선의 색을 잘 살펴봐주세요. 세로로 같은색을 이루고 있죠. 그 색은 서로 연결되어 있는거라고 생각해 주세요. 오렌지, 블루, 핑크색 선들이 세로로 연결되어 있으니깐 혼동하지 마세요

이제 3층 3호실로 이루어진 건물이 있다고 생각하세요. 각층의 1호실은 핑크선, 각층의 2호실은 블루선, 각층의 3호실은 오렌지선으로 연결되어 있다고 생각하세요.

1층 1호실방에 불이 들어올려면 LED 동작에서 이해 했다면 Vcc(+)와 Gnd(-)를 연결을 어떻게 해야 할까요. 핑크선에 Vcc(+)을 연결하고 1층의 녹색선에 Gnd(-)를 연결하면 됩니다.


여기서 핑크선에 Vcc(+)를 연결하면 1층1호실, 2층1호실, 3층1호실에 Vcc(+) 전류가 공급됩니다. 여기서 1층 녹색선만 Gnd(-)선이 연결되어 있다면 1층1호실만 불이 들어옵니다. 하지만 2,3층 녹색선도 Gnd(-)에 연결되어 있다면 어떻게 될까요.



위 그림처럼 각층에 1호실에 Vcc(+)가 공급되니깐 각층마다 Gnd(-)가 연결된다면 각층 1호실 전부다 불이 들어오게 되겠죠. 이 의미를 잘 이해해 주세요.

그러면 다시 2층 2호실에 불이 들어올려면 어떻게 해야 할까요. 블루선에 Vcc(+)가 연결되고 2층 녹색선만 Gnd(-)선이 연결된다면 아래와 같이 불이 들어 오겠죠.


어렵지 않죠. 여러분들도 원하는 위치에 불이 들어오게 한다면 Vcc(+)와 Gnd(-)를 어떻게 연결할지 그려보시거나 가상시뮬레이터에서 한번 실험 해보세요.

이게 3x3x3 CUBE LED의 원리입니다. 오늘 실험은 3x3x1 CUBE LED 라고 생각하시면 되겠죠. 3x3x3 CUBE LED라면 9 호실로 이루어진 3층 건물로 생각하시면 됩니다. 3호실이 아니라 9호실로 늘어났다고 생각하시면 됩니다. 단지 LED를 큐브 모양으로 배치했을뿐 위에서 다룬 LED 동작을 3층 9호실로 호실만 늘어난 구조라고 생각하시면 오늘 내용은 이게 전부입니다.

Vcc(+)와 Gnd(-)을 어떻게 연결하느냐에 따라 LED에 불이 들어오는지만 이해하시면 됩니다. 핑크, 블루, 오렌지 선에 공급되는 Vcc(+)와 각층에 연결된 Gnd(-)선이 서로 연결되었을때 원하는 위치에 LED에 불이 들오는 것만 이해하시면 됩니다.

설명보다는 실제로 가상시뮬레이터에서 선을 연결해보세요. 그래야 이해가 되실 듯 싶네요

마무리


위에서 Vcc(+), Gnd(-) 선을 연결하여 해당 위치의 LED에 불이 들어오게 하는 방법을 배웠습니다. 하지만 중요한것은 아두이노에서는 위에서 실험한것처럼 그때 그때마다 선을 바꿀 수 없잖아요. 그걸 아두이노가 디지털핀으로 Vcc(+0)와 Gnd(-)을 출력함으로써 그 역활을 대신합니다.


파워서플라이에서 선이 전부다 Vcc(+), Gnd(-)가 연결되어 있죠 1층 1,2,3호실에 불이 들어오고 나머지는 불이 안들어오게 하기 위해서 표현한 예인데 아두이노였다면 디지털핀 6개를 Vcc(+)와 Gnd(-)를 출력으로 보내면 보시는 것처럼 LED불이 들어오게 되겠죠.

그리고 선을 보시면 녹색선에 Gnd(-) 대신에 Vcc(+)를 연결하신 걸 보실꺼에요. 양극이 같은 극이 되면은 전류가 흐를 않는 원리를 이용한 것입니다. 왜 이렇게 구지 연결했냐면 다음편에 소개할 아두이노에서는 해당핀을 Vcc(+) or Gnd(-) 에서 핀은 둘중 하나의 상태를 유지하기 때문에 시각적으로 보여주기 위해서 파워서플라이 연결을 이런식으로 표현 한 것이죠.

이부분은 다음편에서 자세히 이야기 하도록 하겠습니다. 오늘은 LED 선을 어떻게 연결하면 어느 위치에 LED가 불이 들어오는지 전류의 흐름을 잘 관찰하시고 그 원리를 이해만 해주시면 어렵지 않을 것에요. 그리고 이 부분이 핵심이니깐 전류의 흐름의 느낌을 잘 이해해 주세요.

댓글()

[아두이노] 가변저항을 이용하여 3색 LED 제어

IOT/아두이노|2019. 2. 23. 10:17

[아두이노] 가변저항을 이용하여 3색 LED 제어



오늘은 가변저항 부품을 이용해서 3색 LED의 색상을 마음대로 조절하는 실험을 하겠습니다. 오늘의 주제는 가변저항입니다. 지금까지 실험에서는 고정저항으로 LED에 연결하여 불을 켜거나 다른 부품의 전류를 조절하기 위해서 써왔습니다. 이제는 고정저항 대신에 가변저항을 써서 유동적으로 제어할 수 있는 방법을 배워보도록 하겠습니다.

1. 가변저항


모양은 대충 이렇게 Termial1,2와 Wiper로 핀이 구성되어 있습니다. 실험에서는 Terminal2(5V), Terminal1(Gnd), Wiper(저항값추출)로 구성합니다. 가정에 보시면 형광등에 돌려서 밝기 조절하는 스위치를 보시거나 있으신분들 아마 있을거에요. 바로 그게 가변저항스위치 입니다. 가정 형관등 보시면 돌려서 저항이 작으면 전류가 통과하는 양이 많아 형광등이 밝아지고 저항이 크면 전류가 통과하는 양이 적어져서 형광등이 어두어집니다. 대충 가변저항 부품에 대해서 이해가 되시죠. 참고로 여기서 Terminal1과 Terminal2의 Vcc와 Gnd 연결 순서는 고정된 것이 아닙니다. 반대로 연결하더라도 작동은 됩니다. 자신이 연결했을때 Wiper의 값이 큰값이면은 반대로 연결하게 되면 Wiper의 값은 작은값으로 바뀌에 됩니다. Terminal1,2의 선을 어떻게 연결하느냐에 따라서 Wiper의 나오는 전류값은 서로 반대값이라고 생각하시면 됩니다. 쉽게 말해서 자신이 연결한 선에서 돌리면 0~1023으로 순차적으로 출력된다면 반대로 연결하여 돌리면 1023~0으로 순차적으로 출력됩니다. 참쉽죠.

1. 회로도 구성


  • 준비물 : 가변저항 3개, 3색 LED 1개, 저항 220옴 3개, 아두이노우노, 뻥판
  • 내용 : 가변저항스위치는 각각 RGB의 저항값을 추출하고 그 값을 토대로 RGB의 색상값으로 출력하도록 하는 실험


가변저항 부품을 보시면 Wiper에서 나오는 선이 아날로그 A0, A1, A2에 입력으로 들어옵니다. Wiper은 가변저항 부품에 흐를 전류의 값을 출력합니다. Terminal1은 Gnd, Terminal2은 5V에 연결합니다. 그러면 가변저항기에 전류가 공급되고 조절기로 돌리면 저항값이 변화되고 Wiper로 그 변화된 전류가 출력됩니다. 그 출력값이 A0, A1, A2에 아날로그 값으로 읽게 되고 그 값은 다시 3색 LED의 아날로그 신호값으로 출력되여 색이 결정됩니다. 하지만 아두이노는 아날로그 출력이 없으며 아날로그 출력과 비슷한 형태로 출력되는 PWM 출력으로 대신하는데 그냥 단순하게 PWM디지털핀으로 아날로그 신호를 내보낸다고 생각 하시면 돼요.

3. 코딩


  • 사용함수 : pinMode(), analogWrite(),analogRead()
  • 내용 : 간단히 가변저항 조절기로 조절하면 흐를 전류를 조절할 수 있고 그 조절된 전류 값을 3색 LED에 출력값으로 해서 색을 자유롭게 만들어 낸다.
  • 참고 : 3색 LED 제어(아두이노)

복습

  • pinMode(사용핀,모드) : 사용할 핀을 입력/출력모드(INPUT/OUTPUT) 인지 선언한다.
  • analogWrite(출력핀,상태) : 출력핀에 0~255 사이의 아날로그신호(PWM)를 내보낸다.(3색 LED에 출력용)
  • analogRead(입력핀) : 입력핀을 0~1023 사이의 아날로그 신호값을 읽어들인다.

설계

(1) 가변저항기 읽기

color_value[0] = analogRead(A0)/4; //(0~1023 => 1024/4=256)
color_value[1] = analogRead(A1)/4;
color_value[2] = analogRead(A2)/4;

analogRead()함수로 각 Wiper핀에서 발생된 가변저항값을 읽게 됩니다. 0~1023 사의 값을 가져오는데 3색 LED의 입력값은 0~255사이이기 때문에 나누기 4를 해줘야 되겠죠. 왜 나루기를 했는지는 아시겠죠.

color_value[0]=map(analogRead(A0),0,1023,0,255)

원래는 이렇게 코딩해야합니다. map()함수로 효율적으로 코딩해야 합니다. 하지만 대충 어떤 값이 들어가는지 직관적으로 이해 시키기 위해서 map()함수를 생략했네요.

  • map(입력값,입력최소,입력최대,출력최소,출력최대) : analogRead(A0)에서 읽은 값이 방금전 0~1023이라고 했죠. 그걸 입력최소, 입력최대로 범위를 지정해주고 이 값을 기준으로 출력값의 범위는 0~255니깐 출력최소 0과 출력최대 255로 지정하면 함수 하나로 쉽게 매핑 시킬 수 있습니다. 둘 중 아무거나 사용해서 이용하시면 돼요.

(2) 색 결정

우선 3색 LED에서 따로 Color()함수를 직접 만들었습니다. 이렇게 만들어 놓으면 3색 LED 부품을 사용하면 이 함수만 복사해오면 번거로운 코딩을 할 필요 없겠죠. 3색핀에 값이 결정되면 그 값을 color(Red, Green, Blue) 값을 넣으면 3색 LED에 색이 출력됩니다.

void Color(int red, int green, int blue)
{
  analogWrite(rpin, red);
  analogWrite(gpin, green);
  analogWrite(bpin, blue); 
}

코딩을 하면

  1. 3색 LED에 RGB 핀에 제어할려면 3핀이 필요하니깐 우선 3핀을 변수로 선언해야지(9,10,11)
  2. 가변저항1,2,3이 있는데 이걸 저장할 변수가 필요해! 3개가 필요하니 그냥 배열변수로 하지. (color_value[3])
  3. 3색 LED에 사용할 핀이 3개니깐 변수도 선언했고 그러면 이 핀이 출력으로 사용할려면 우선 선언해야지.
  4. 자! 가변저항을 읽어와서 배열변수에다 순서대로 저장하자.
  5. 이제 3색 LED에 저장된 값인 가변저항값으로 색을 만들자
int rpin = 11;
int bpin = 10;
int gpin = 9;
int color_value[3];
 
void setup()
{
  pinMode(rpin, OUTPUT);
  pinMode(gpin, OUTPUT);
  pinMode(bpin, OUTPUT);
}

void loop()
{
  color_value[0] = analogRead(A0)/4; //(0~1023 => 1024/4=256)
  color_value[1] = analogRead(A1)/4;
  color_value[2] = analogRead(A2)/4;
  Color(color_value[0],color_value[1],color_value[2]);
}

void Color(int red, int green, int blue)
{
  analogWrite(rpin, red);
  analogWrite(gpin, green);
  analogWrite(bpin, blue); 
}

5. 결과


그냥 가변저항 예제로 LED 1개, 가변저항 1개를 이용해서 밝기를 조절하는 실험을 해도 되는데 좀 더 복습하면서 3색 LED의 색을 만들어내는게 시각적으로 보기도 좋고 실험이 재밌을 것 같아서 선택했습니다.

마무리


가변저항을 이용해서 조절하니깐 시각적으로 3색 LED의 색의 변화를 체험하셨을 거라 생각합니다. 3색 LED의 색의 변화를 통해서 뭔가 재밌는 소재가 없나 한번 떠올려 보세요. 예전에 어떤분의 블로그 글을 본적 있는데 3색 LED를 이용한 실험이였습니다. 참 참신하더군요. 비닐인가 솜인가 구름의 형태로 만들어서 그 안에 3색 LED를 넣고 wifi쉴드를 이용해서 날씨 정보를 읽어오게 했는데 그 정보를 이용해서 현재의 날씨 상태를 3색 LED의 색으로 나타냈는데 참 재밌어 보이더군요.
한번 이런건 어떨까요. 토양습도센서를 아두이노에 연결해서 식물이 물이 부족할경우 그걸 시각적으로 3색 LED로 식물의 감정을 표현한다면 괜찮겠지요.

이야기가 삼천포로 빠졌지만 가변저항을 조절할 수 있다면 과연 일상에서 어떤것들이 있는지 찾아보고 또 어디에다 이 부품을 사용하면 재미 있을지 한번 상상의 나래를 펼쳐보세요.


댓글()

[아두이노] 74HC595+ 7 Segment LED 실제 실험

IOT/아두이노|2019. 2. 15. 09:26

[아두이노] 74HC595+ 7 Segment LED 실제 실험



가상시뮬레이터에서 아두이노우노를 다루면서 실제 현실에서도 동일하게 작동을 하는지 궁금하신 분들이 있을꺼에요. 그래서 귀찮니즘을 안고 실제로 실험을 하였습니다. 예전에 74HC595 칩을 뻥판에 꼽을 개고생 했는데 뻥판이 싼거라 핀이 잘 안들어가서 핀 부분이 약간 오그라졌는데 이번에도 역시나 꼽을 때 오그라지고 뽑을 때 오그라져서 나오더군요. 몇번 실험하고 나면 74HC595 칩이 망가질것 같네요.

1. 회로도 구성



[아두이노] 쉬프트레지스터(74HC595) 제어 편에서 실제로 7 Segment LED도 뻥판에 배치를 해야하기 때문에 약간 배치하는 위치를 수정했네요.


좀 더 깔끔해진 회로도 모습이지요.

지난시간에 가상시뮬레이터로 설계한 모습에서 다시 가상시뮬레이터의 뻥판에 재배치한 모습입니다. 그런데 실제로 같은 위치에 배치하면 이런 모습이 아니라 지져분한 모습입니다. 가상시뮬레이터에서 회로도를 배치한대로 실제로 동일하게 배치했습니다. 참고로 저항은 220옴뿐이 없어서 이걸로 했습니다. 저항은 LED에 필요한 허용치 저항을 연결하면 되니깐 제 경우는 저항 220옴으로 실험했네요.


위 사진은 뻥판 연결된 모습인데 실험 중의 한 컷인데요. 선만 봐도 엄청 지져분하죠. 74HC595 칩을 뻥판에 꼽을 때 개고생 했네요. 핀 위치는 대충 가상시뮬레이터에서 연습 몇번해서 그런지 선 연결은 지져분할 뿐 간단했네요. 가상시뮬레이터에 연습없이 바로 실제로 실험한다면 선 연결할 때 실수하는 경우가 많겠죠.

2. 코딩


코딩은 지난시간에 숫자를 순차적으로 출력하는 로직을 그대로 집어 넣었습니다.

byte data[]={
0B10000001,  
0B11110010,
0B01001001,
0B01100000,
0B00110011,
0B00100100,
0B00000101,
0B11110000,
0B00000001,
0B00100000
};
int latchPin = 11; //ST_CP Pin
int clockPin = 12; //SH_CP Pin
int dataPin = 9; //DS Pin

void setup(){
pinMode(latchPin, OUTPUT);
pinMode(clockPin, OUTPUT);
pinMode(dataPin, OUTPUT);
}
void loop(){
 for(int i=0;i<10;i++){
    digitalWrite(latchPin, LOW);
    shiftOut(dataPin, clockPin, MSBFIRST, data[i]);
    digitalWrite(latchPin, HIGH);       
    delay(1000);
  }  
}

3. RaspberryPi3 에서 아두이노우노로 프로그램 이식


PC에서 RaspberryPi3를 원격 제어를 통해 아두이노 IDE를 띄워고 위 코딩을 복사한 뒤에 실제 아두이노우노에 프로그램 이식을 수행했습니다.
PC에 아두이노 IDE를 설치했다면 바로 PC에서 아두이노우노에 프로그램을 이식하면 됩니다.


4. 결과


아래 13초짜리 동영상은 실제 프로그램이 이식된 아두이노우노가 74HC595 칩에 숫자 패턴 데이터값을 전송하여 7 Segment LED에 숫자가 가상시뮬레이터에서 나왔던 결과와 동일하게 출력되는지 테스트한 결과입니다.


마무리


실제 아두이노우노로 실험을 해 보았습니다. 가상시뮬레터의 결과와 동일하게 나오는 것을 보실 수 있었을 겁니다. 이처럼 실제로 키트를 사서 실험을 하실 수 있지만 실제로 실험하면 가상으로 한것보다 시간도 많이 걸리고 손으로 일일히 노가다 작업을 많이 해야 합니다. 편하게 마우스로 선을 연결할 수 있는 것을 실제로 뻥판에 억지로 꼽고 하는게 쉽지가 않죠. 그리고 부품이 작어서 아무 지식도 없는 상태에서 보고 따라서 꼽을려고 하면 그것도 만만치 않습니다.

특히, 74HC595 칩의 경우는 작고 핀들의 네임들을 외우지 않는 이상 햇갈릴 수 있습니다. 하지만 가상에서는 마우스을 대면 핀 네임들을 쉽게 확인이 가능합니다. 가상시뮬레이터로 여러번 반복 실험을 하다보면은 실제 실험에서도 어렵지 않게 핀 연결을 할 수 있게 됩니다.

제가 아두이노우노를 실험할 때 실제 아두이노우노 키트가 있지만 가상시뮬레이터에서 하는 이유는 실제 아두이노키트를 사용해서 실험하는 것보다 가상시뮬레이터에서 더 쉽게 실험할 수 있기 때문입니다.

막연히 아두이노우노 키트를 사서 하는것보다 가상시뮬레이터로 충분히 즐긴 후 좀 더 다양한 실험을 하고 싶을때
아두이노 시리즈 중 원하는 싱글보드를 구매하셔서 실험하시면 됩니다.


댓글()

[아두이노] 7 Segment LED 제어

IOT/아두이노|2019. 2. 12. 13:23

[아두이노] 7 Segment LED 제어



오늘은 LED 제어의 연장선상으로 7 Segment LED를 제어해 보도록 하겠습니다. 사용될 함수는 LED 제어때 사용한 pinMode( ), digitalWrite( )만 이해하시면 쉽게 제어를 할 수 있습니다.

1. 7 Segment LED


A,B,C,D,E,F,G,DP 핀으로 총 8개의 핀으로 구성되어 있습니다. 각 핀은 위 그림에서 보는것 같지 해당 위치의 LED에 불을 제어하는 핀으로 생각하시면 됩니다. 실제로 구매하시면 애노드형과 캐소드형이 있는데 위아래핀이 애노드 7 Segment LED이고 좌우고 캐소드였나 암튼 가상시뮬레이터에서 사용하는게 애노드형 초기 기본으로 되어 있는데 속성창에서 캐소드형으로 바꿔서 실험할 수 있습니다.

애소드와 캐소드 차이

7 Segment LED핀들이 가상시뮬레이터에서는 핀에서 COM1, COM2 핀이 있는데 아무핀이나 하나에 5V(+)핀이면 애노드형이고 Gnd(-)핀이면 캐소드형이 됩니다.

  1. 애노드 : A~G,DP핀이 0V이고 COM1 or COM2가 5V이면 해당 핀 LED에 불이 들어옵니다.
  2. 캐소드 : A~G,DP핀이 5V이고 COM1 or COM2가 Gnd이면 해당 핀 LED에 불이 들어옵니다.

쉽게 말해서 서로 반대라고 생각하시면 됩니다. 애노드형은 출력핀이 0일때 LED에 불이 들어오고 캐소드형은 출력핀이 1일때 LED에 불이 들어온다는 것만 이해하시면 됩니다. 참고로 7 Segment LED를 구매하실때 제품이 애노드형인지 캐소드형인지 확실히 확인하시고 구매하세요. 그래야 햇갈리는 핀연결과 코딩을 하지 않습니다.

2. 회로도 구성


  • 준비물 : 7 Segment LED 1개, 저항 330옴 1개, 아두이노우노
  • 내용 : 알파벳과 숫자를 출력해보자.

애노드형모델캐소드형모델



대충 붉은선이 5V에 연결되어 있고 검은선이 Gnd에 연결된 모습을 보면 대충 알겠죠.

##3. 코딩

  • 사용함수 : pinMode( ), digitalWrite( )
  • 내용 : 애노드형모델 기반으로 알파벳과 숫자를 순차적으로 출력하는 코딩을 해보자.

  • 7 Segment LED의 각 led 제어 :
    pinMode(핀, 모드)으로 전류를 출력할 핀들을 출력모드(OUTPUT) 사용하겠다고 선언
    digitalWrite(핀, 상태)은 HIGH(5V) or LOW(0V)로 전류를 출력할지 말지를 결정(여기서, 1은 5V, 0은 0V)

7 Segment LED도 내부에 LED들을 제어하기 때문에 LED 제어할때랑 동일합니다. 애노드형이기 때문에 0일때 불이 들어고 1일때 불이 꺼진다는 것만 생각하시고 코딩에 들어가세요. LED 제어의 복습차원이라고 생각하시면 됩니다. 참고로 알파벳과 숫자를 만드는 수작업인 노가다가 필요합니다.

설계

(1) 알파벳(A,B,C,D,E,F)과 숫자(0,1,2,3,4,5,6,7,8,9) 만들기



A글자 만들려면 LED D핀이 꺼지고 나머지 핀들이 켜져야 겠죠.(켜기=0, 꺼지기=1)

00010000

배열로 A[8]={0,0,0,1,0,0,0,0} 이 된다. 이건 하나의 A글자의 패턴입니다. 그러면 각 글자와 숫자의 패턴을 배열변수로 만들어야겠죠. 참고로, 앞에 7개는 글자나 숫자를 만드는 핀이고 마지막 8번째는 DP핀으로 점 LED 입니다. 0을 설정한 이유는 그냥 점 LED를 켜지는 것을 테스트 목적인것이지 의미는 없다는 점 참고하세요. 점 LED 빼고 싶다면 00010001 이러면 되겠죠.
아래 2차배열로 표현할때 점 LED은 글자가 출력할때 그냥 같이 켜지게 했고 숫자가 출력할때에 그냥 꺼지게 했어요. 원래 여러개 연결한다면 소숫점으로 사용하면 좋겠는데 한개 7 Segment LED라서 그냥 테스트로 켜지는지 보기위한 실험으로 의미는 없습니다.

int segValue[16][8] = {
   {0,0,0,1,0,0,0,0}, //A
   {1,1,0,0,0,0,0,0}, //B
   {1,1,1,0,0,1,0,0}, //C
   {1,0,0,0,0,1,0,0}, //D
   {0,1,1,0,0,0,0,0}, //E
   {0,1,1,1,0,0,0,0}, //F
   {0,0,0,0,0,0,1,1}, //0
   {1,0,0,1,1,1,1,1}, //1
   {0,0,1,0,0,1,0,1}, //2
   {0,0,0,0,1,1,0,1}, //3
   {1,0,0,1,1,0,0,1}, //4
   {0,1,0,0,1,0,0,1}, //5
   {0,1,0,0,0,0,0,1}, //6
   {0,0,0,1,1,1,1,1}, //7
   {0,0,0,0,0,0,0,1}, //8
   {0,0,0,0,1,0,0,1}  //9  
};

(2) 알파벳과 숫자를 만들었다면 실제 출력해야겠죠.

  • 핀배열 변수 선언 :
int segPin[8]={2,3,4,5,6,7,8,9}; 
  • 출력핀모드 선언 :
for(int i=0;i<9;i++){
    pinMode(segPin[i], OUTPUT);
  }
  • 출력 : 이부분이 실제 코딩 로직의 전부입니다. 각 패턴를 for문으로 루프 도는게 전부임
for(int i=0;i<16;i++){ //16패턴
  for(int j=0;j<8;j++){ //패턴값
   digitalWrite(segPin[j], segValue[i][j]);        
}
  delay(1000);
}  

애노드형모델 코딩을 실제 하면은

int segValue[16][8] = {
   {0,0,0,1,0,0,0,0}, //A
   {1,1,0,0,0,0,0,0}, //B
   {1,1,1,0,0,1,0,0}, //C
   {1,0,0,0,0,1,0,0}, //D
   {0,1,1,0,0,0,0,0}, //E
   {0,1,1,1,0,0,0,0}, //F
   {0,0,0,0,0,0,1,1}, //0
   {1,0,0,1,1,1,1,1}, //1
   {0,0,1,0,0,1,0,1}, //2
   {0,0,0,0,1,1,0,1}, //3
   {1,0,0,1,1,0,0,1}, //4
   {0,1,0,0,1,0,0,1}, //5
   {0,1,0,0,0,0,0,1}, //6
   {0,0,0,1,1,1,1,1}, //7
   {0,0,0,0,0,0,0,1}, //8
   {0,0,0,0,1,0,0,1}  //9  
};
int segPin[8]={2,3,4,5,6,7,8,9}; //사용핀{a,b,c,d,e,f,g,dp} 순서대로임

void setup() {

  for(int i=0;i<9;i++){
    pinMode(segPin[i], OUTPUT);
  }
}
void loop() {
  for(int i=0;i<16;i++){
    for(int j=0;j<8;j++){
     digitalWrite(segPin[j], segValue[i][j]);        
  }
    delay(1000);
  }  
}

for문에 대해서 저번에 설명을 했으며 for문을 이해해야만 왜 이렇게 코딩이 되었는지 이해가 되실꺼에요.
혹시 이해가 안되시는 분들은 LED 제어(아두이노)를 다시 한번 보시고 간단히 소개된 for문을 보시고 대충 어떤 느낌인지 살펴보세요.

여기서 캐소드형 경우는

패턴배열변수값들을 반대로 하시면 됩니다.

int segValue[16][8] = {
   {1,1,1,0,1,1,1,1}, //A
   {0,0,1,1,1,1,1,1}, //B
   {0,0,0,1,1,0,1,1}, //C
   {0,1,1,1,1,0,1,1}, //D
   {1,0,0,1,1,1,1,1}, //E
   {1,0,0,0,1,1,1,1}, //F
   {1,1,1,1,1,1,0,0}, //0
   {0,1,1,0,0,0,0,0}, //1
   {1,1,0,1,1,0,1,0}, //2
   {1,1,1,1,0,0,1,0}, //3
   {0,1,1,0,0,1,1,0}, //4
   {1,0,1,1,0,1,1,0}, //5
   {1,0,1,1,1,1,1,0}, //6
   {1,1,1,0,0,0,0,0}, //7
   {1,1,1,1,1,1,1,0}, //8
   {1,1,1,1,0,1,1,0}  //9  
};

코딩에서는 for문을 사용해서 led 출력핀들의 모드와 출력상태를 표현하는 것과 2차배열로 7 Segment LED패턴을 표현하는 것 두개뿐이 없습니다. 패턴을 2차배열로 표현하는 노가다 작업만 있을 뿐이죠.

4. 결과



애노드형 모델을 실험 동영상입니다.



마무리


LED 제어의 연장선상의 7 Segment LED제어를 해 보았습니다. 한개짜리지만 실제 4개짜리로 묶여진 부품도 있어서 좀 더 다양한 표현을 할 수 있습니다. 글자나 숫자를 출력할 수 있으면 이걸로 많은걸 표현이 가능합니다.

실생활에서 보면 시계를 떠오르시는분들이 많겠죠. 또는 거리에 나가면 가장 가깝게 신호등에 숫자가 출력되는걸 보실 수 있을꺼에요. 그러면 아두이노로 신호등을 만들고 7 Segment LED로 신호등의 타이머로 현실과 비슷하게 표현을 가능하겠죠. 이걸 대충요 종이로 신호등을 만들어서 제어하면 아이들에게 재밌는 교통 교육용이 될 수 있겠죠.

오늘 배운 패턴 배열변수는 무척 중요합니다. 꽤 많이 사용하는 2차배열변수인데요. 다음에 LED CUBE나 8x8 도트매트릭스를 소개할지 모르겠지만 계속해서 사용합니다. 배열변수를 혹시 모르시겠다면 관련글들을 검색해서 꼭 공부해주세요.


댓글()

[아두이노] 3색 LED 제어

IOT/아두이노|2019. 2. 2. 12:34

[아두이노] 3색 LED 제어




이번 시간에는 3색 Led를 제어하는 시간입니다. 한개의 3색 led를 어떻게 RGB 색으로 나타내는지 실험하도록 하죠.

1. 회로도 구성


  • 구성 : 3색LED 1개, 저항 220옴 3개, 뻥판, 아두이노우노

3색 LED의 각 꼭지점에 마우스를 대면 red, green, blue란 단어가 보여진다. 각각 보기 편하게 전선을 각 색상선으로 바꿔 준다. 저항은 클릭한 뒤에 Ctrl+C로 복사 한 뒤에 원하는 위치에 마우스 대고 Ctrl+V하면 복제가 된다.

그리고 두번째 검정색선(Gnd)로 연결하면 회로도 구성은 끝난다. 그런데 실제 부품은 제품 정보를 보시고 색선을 구별하셔야 합니다.

2. 코딩


  • 함수 : pinMode(핀번호,모드), analogWrite(핀번호, 색값), delay(시간값)
  • 코딩 내용 : R->G->B로 3색을 순차적으로 간단히 출력하는 로직을 짜본다.

int rpin=11;
int gpin=9;
int bpin=10;

void setup()
{
  pinMode(rpin, OUTPUT);
  pinMode(gpin, OUTPUT);
  pinMode(bpin, OUTPUT);
}

void loop()
{
  analogWrite(rpin, 255);
  analogWrite(gpin, 0);
  analogWrite(bpin, 0);
  delay(1000);
  
  analogWrite(rpin, 0);
  analogWrite(gpin, 255);
  analogWrite(bpin, 0);
  delay(1000);
  
  analogWrite(rpin, 0);
  analogWrite(gpin, 0);
  analogWrite(bpin, 255);
  delay(1000);  
}

첫번째 :
변수 선언에서 int은 자료형으로 변수의 그릇의 크기를 나타낸다. rpin, gpin, bpin으로 3개의 변수를 선언하고 그 변수 그릇에 각각 해당핀 값을 저장시켜 놓는다.

두번째 :
setup()함수는 초기화 작업 코딩하는 부분으로 11, 10, 9핀을 OUTPUT(출력모드)로 아두이노에서 외부로 전류를 보내는 출력모드로 사용하겠다는 것이다.
여기서, 주의할 것은 사용된 핀이 다른 디지털핀과 다르다는 것이다. 0~13번 디지털 핀 중에 6개의 핀은 앞에 (~)에 붙은 핀들이 있는데 아두이노우노 보드를 보면 표시 되어 있다. 이것은 아날로그값을 출력으로 내보낼 수 있는 핀이다. 즉 0~255의 전류로 신호를 출력할 수 있는 특수한 핀이다. 실제 아날로그 값이 나가는게 아니라 아날로그 값이 나가는 것처럼 느낌의 전류 신호를 제어해서 내보낼 수 있는 핀으로 생각하면 된다.
3개의 RGB 핀은 각각 0~255의 값의 범위로 해서 다양한 색상을 만들어 낼 수 있다.

3번째 :
loop()함수에서 기존의 digitalWirte(rpin,100)하면 안된다. 디지털출력은 0과 1의 값만 OV와 5V만 존재한다. 즉, 0이 아닌 값은 무조건 5V의 전류가 흐르게 된다. 그렇게 되면 다양한 색을 만들어 낼 수 없다. (~핀)을 사용하기 위해서는 analogWrite(rpin,100)을 해야 합니다. 0~255사이의 전기신호를 보내기 위한 함수 이고 이 함수를 통해서 아날로그 100의 값의 전기신호를 내보내게 된다. 그래서 RGB Led은 rpin을 통해서 100이란 전기신호값의 색밝기로 보여지게 된다.
여기서는 1초 단위로 max 값 255로 R->G->B로 색이 나오도록 출력한 코딩입니다.

rpin을 255의 전기신호를 보내고 gpin, bpin은 0의 전기신호를 보내면 자동으로 Red 색이 출력되겠죠. 함수의 의미만 잘 이해하시면 다양한 색으로 출력이 가능합니다.

3. 고민해야 할 것


여기서 고민을 해야 합니다. 색을 만들기 위해서 analogWrite()함수가 세번 loop()함수에 표현을 해야하는게 불편함을 느껴야 합니다.
여러가지 색을 표현하고자 한다면 매번 3개의 함수를 loop()함수 안에서 코딩으로 표현하는 것은 엄청 비효율적이지요. 그래서 C언어 문법 하나를 배우도록 하죠.

C언어 문법


  • 문법 : 외부사용자함수()

외부사용자함수란 프로그래머가 직접 함수를 만드는 것이죠. 특정 중복되는 동작을 함수로 묶어서 그 함수를 호출하여 해당 동작을 수행할 수 있게 만들 수 있습니다. 즉, pinMode(인자1, 인자2); 이렇게 함수에 2개의 인자값을 넣어서 pinMode()함수 동작을 수행되는데 이런식으로 직접 프로그래머가 함수를 만들어서 RGB 인자값을 넣어서 색을 출력하는 함수를 만들면 간단한 코딩이 되겠죠.
3개의 analogWrite()함수로 RGB led의 색이 결정되는 원리를 배웠습니다. 그러면 이 원리를 이용해서 하나의 새로운 함수를 만들어 보죠.

void Color(int red, int green, int blue)
{
  analogWrite(rpin, red);
  analogWrite(gpin, green);
  analogWrite(bpin, blue); 
}

이렇게 Color라는 함수를 만들면 이 함수를 표현하여 RGB 색을 출력할 수 있게 됩니다. loop()함수 안에다 아래와 같은 방식으로 코딩하면 좀 더 깔끔한 코딩이 되겠죠. 여기서 인자값 (255,0,0)은 (red,green,blue)라는 변수에 저장되게 됩니다. 그 값이 3개의 analogWrite()함수에 각각 인자값이 대입하여 전기 신호를 출력하게 되는 것이죠. 간단한 원리죠.

Color(255,0,0);
delay(1000);
Color(0,255,0);
delay(1000);
Color(0,0,255);
delay(1000);

또, 고민을 해보세요. 지난 시간의 for()문을 이해 했다면 for()문을을 이용해서 RGB의 각 값을 순차적으로 변화시켜서 여러가지의 색을 만들어 볼 수 도 있겠죠.

웹언어를 공부하신분이라면 색을 나타날때 아래 예처럼 16진수로 각각 두자리가 RGB로 3개의 색을 나타내는 것을 알고 있을 겁니다.

예) #ff0000(red), #00ff00(green), #0000ff(blue)

참고 자료를 보시면 색을 좀 더 자세히 나눌 수 있겠죠.
각각 00~ff의 16진수 범위를 10진수 0~255로 색의 범위를 잡아서 색을 정하면 되겠죠.

4. 결과






마무리


RGB led를 한개를 제어해 봤습니다. 이전 led 제어 코딩에서 analogWrite()라는 함수를 새롭게 한개 배웠습니다.
이 함수로 하나의 RGB Led에 원하는 색으로 표현할 수 있게 된 거죠.
그러면 이것을 현실에서 이용하는 것들이 무엇일까요?
가장 가까운것은 현재 당신이 보는 스마트폰이나 pc 모니터를 생각하시면 됩니다. 쉽게 bmp 이미지 파일을 생각하는게 더 이해가 쉬울지 모르겠군요. bmp 이미지 파일은 픽셀단위로 해서 각 픽셀당 RGB색값이 할당 되고 이게 합쳐져서 이미지로 보여줍니다. RGB led 한개를 하나의 픽셀로 생각해 보세요. 수많은 RGB led를 각각 픽셀로 색의 값으로 밝혀진다면 이걸 다 연결하면 하나의 이미지 처럼 픽셀 이미지가 완성 되겠죠.
대충 어떤 느낌이신지 아시겠지요. 실험에 사용한 RGB led은 집중된 밝기가 아니고 분산된 밝기여서 사실상 이미지화 한다는 것이 무척 어렵지만 특수 led의 경우는 가능하겠죠.
네오 픽셀이라는 RGB 색을 만들어 내는 부품이 있습니다. 이걸로 실제 모니터로 구현한 외국인분들도 있습니다.
유사 표현으로는 주변 전광판을 생각하시면 되겠죠.

그리고 상상을 해보세요. RGB led은 3색을 섞어서 다양한 색을 만들어 낼 수 있습니다. 그러면 이걸로 뭘 할 수 있을까 끊임없이 상상해보세요. 0~255까지의 값의 색을 지정할 수 있다. 그러면 센서를 이용해서 그 센서의 값에 따라 밝기를 나타낸다면 뭔가 시각적인 데이터 표현이 가능하겠죠. 그러면 이걸 어떤부품과 연결해서 표현하면 좋을지 상상을 더해 보세요.

원한 하나의 부품을 사용하고 그 원리를 이해하면 단순히 거기서 끝나면 안됩니다.
그 부품이 다른 부품만 결합하면 또 다른 시너지 효과를 발휘 할 수 있으며 또 그 부품의 원리를 잘 이해하면 그 자체의 기능만으로 재밌는 표현이 가능하게 됩니다.

단순히 한개의 RGB를 제어했지만 이걸 어떻게 제어 하느냐에 따라서 표현하고자 하는 목적에 따라 당신이 생각하는 그 이상의 표현을 할 수 있으며 그런 표현들을 현실에서 아두이노를 다루는 분들이 작품으로 만들고 있습니다.
현재의 여러분들이 이걸 통해서 어떤 상상을 하느냐에 따라서 그 가치가 달라지는 점을 유념하세요.

끊임없이 의문을 갖고 생각을 통해 많은것들을 표현하길 바래요.
다음은 이전 시간에 led와 이번시간의 설명한 내용을 기반으로 상상할 수 있는 것들을 이야기 해보도록 하죠.


'IOT > 아두이노' 카테고리의 다른 글

[아두이노] 피에조부저 제어  (0) 2019.02.04
[아두이노] 스위치 버턴 제어  (0) 2019.02.03
[아두이노] 코딩 구조  (0) 2019.01.31
[아두이노] LED 제어  (0) 2019.01.30
아두이노의 기초  (0) 2019.01.27

댓글()