[아두이노] 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핀이여서 제작 과정이 좀 복잡하지만 원리는 동일합니다. 한번 연구해 보세요.


댓글()