[아두이노] Neopixel을 이용한 7-Segment Display

IOT/아두이노|2019. 7. 18. 21:50


[아두이노] Neopixel을 이용한 7-Segment Display



아두이노 디지털 시계를 유연히 유튜브에서 NeoPixel을 이용한 디지털 시계가 만들어 놓은 영상을 보다가 재밌을 것 같아서 한번 Neopixel로 7-Segment Display를 만들어 보는 실험을 해보았습니다. 7-Segment Display의 원리가 8개의 LED를 구성된 부품입니다. 사실 LED로 수작업으로 7-Segment Display 모양을 직접 만들면 선 연결이 복잡해지고 시계같은 것을 LED로 만들게 되면 귀찮은 부분들이 많은데 Neopixel를 이용하면 의외로 선 연결이 간단하게 설계 할 수 있습니다. LED로만 7-Segment Display를 생각했는데 왜 NeoPixel로 7-Segment Display로 표현 할 생각을 왜! 못했을까 하는 생각에 부품의 원리를 알고 있어도 실제 상상할 때 이런 것을 가끔 놓치는 경우가 발생하네요.

오늘은 Neopixel를 이용한 7-Segment Display를 어떻게 표현한느지 간단히 실험을 통해 살펴보도록 하겠습니다.

1. 7-Segment Display



7-Segment Display 부품은 아래 그림 과 같습니다. led 제어 a,b,c,d,e,f,g,dp핀으로 해당 위치에 LED에 불이 on/off 상태로 숫자와 문자를 만들어 냅니다.


위 모습을 좀 더 자세하게 나타내면은 아래 그림과 같습니다.


위 그림 처럼 8개의 LED로 구성되고 LED가 7-Segment Display 모양 형태로 배치만 하면 아래와 같습니다.


위 그림을 보면 좀 지져분하게 되었지만 어떤 느낌인지 아시겠지요. 일렬로 된 그림은 보기 편하게 되어 있는 것이고 실제로 7-Segment Display를 표현하려면 위와 같이 배치를 하면 됩니다. 참고로 선 모양은 깔끔하게 표현을 못했고 그냥 대충 막 선을 연결했네요. 선 관리가 일반 발광다이오드(LED)에서는 좀 불편 합니다. 그래서 아두이노 디지털 시계를 만들때 이 표현을 사용하지 않았습니다. 이 표현대로 4-Digit 7-Segment Display 표현을 수작업으로 직접 디자인 한다면 공유선까지 하면 답이 안보이고 꽤 머리를 써서 디장인설계를 해야 합니다.

하지만 NeoPixel를 이용한 회로도 설계는 단순화 디자인을 할 수 있는데 어떻게 표현하는지 한번 살펴 볼까요.

2. Neopixel 7-Segment Display 만들기



7-Segment Display의 a,b,c,d,e,f,g 핀 위치를 그대로 동일하게 표현하면 위 그림처럼 되는데 약간 선 방향이 연결에서 좀 불편하게 연결하게 되더군요. 이 상태에서 한세트 더 7-Segment Display를 연결하면 선이 좀 지져분 해지고 선 연결이 답이 안보이겠더군요.

그래서, 아래와 같이 선연결을 S자 방향으로 자연스럽게 이여지도록 표현 했네요. 대부분 실험을 하시는 분들이 S자 방향으로 선을 많이 연결하시더군요. 그리고 현재 선 방향을 반대 S자 방향으로 연결해도 됩니다. 정해진 것은 없고 S자 방향선을 연결하면 꽤 깔끔하게 연결이 되니깐 참고하시고 선을 연결하시면 됩니다.


위 그림으로는 7-Segment Display라고 잘 연상이 안되죠. 아래 그림을 보고 이해하시면 됩니다.


그리고, 추가로 7-Segment Display를 만든다면 아래와 같이 늘릴 수 있습니다.


별로 복잡하지가 않죠.

3. Neopixel 7-Segment Display 실험


1) Neopixel 7-Segment Display 회로도


  • 준비물 : Neopixel 7개, 아두이노우노
  • 내용 : 3번핀을 Neopixel 제어핀으로 사용한다.


2) 코딩


  • 설계 : 0~9까지 카운터 테스트

[Neopixel 함수]

#include <Adafruit_NeoPixel.h>

const byte neopixelPin = 3; 
Adafruit_NeoPixel neopixel = Adafruit_NeoPixel(7, neopixelPin, NEO_GRB + NEO_KHZ800);
  • neopixel.begin() : 네오픽셀 시작
  • neopixel.setPixelColor(위치,R,G,B) : 네오픽셀 위치에 색상 세팅
  • opixel.show() : 세팅된 값을 출력
  • neopixel.clear() : 네오픽셀 초기화

0~9까지 카운터 세기 위해서는 0~9까지의 숫자 패턴을 만들어야 합니다.

 byte segValue[10][7] = {
   {1,1,1,0,1,1,1}, //0
   {1,0,0,0,1,0,0}, //1
   {1,1,0,1,0,1,1}, //2
   {1,1,0,1,1,1,0}, //3
   {1,0,1,1,1,0,0}, //4
   {0,1,1,1,1,1,1}, //5
   {0,0,1,1,1,1,1}, //6
   {1,1,0,0,1,0,0}, //7
   {1,1,1,1,1,1,1}, //8
   {1,1,1,1,1,1,0}  //9   
 };

아두이노 시계의 패턴을 그대로 적용하려고 했지만 배선 순서가 바뀌어서 어쩔 수 없이 segValue[10][7]값을 수정했네요.

0~9까지 순서대로 출력해야 코딩을 만들어 봅시다.

for(0~9까지 반복){
  for(해당숫자패턴 배열을 출력){
      네오픽셀 숫자패턴 세팅;
  }
  네오픽셀 출력;
  delay(1000);
}

대충 이렇게 틀을 잡으면 됩니다.

코딩으로 하면,

  for(int i=0;i<10;i++){  //0~9까지 10개의 숫자를 반복
    for(int j=0;j<7;j++){ //한개의 숫자 당 7개의 네오픽셀 상태임으로 7번 반복
      if(segValue[i][j]==1){ //해당 숫자의 패턴 상태 체크
        neopixel.setPixelColor(j, 255, 0, 0); // 1이면 해당 네오픽셀에 Red color로 표현           
      }else{
        neopixel.setPixelColor(j, 0, 0, 0);  // 0이면 무색으로 표현       
      }
    }
    neopixel.show();  //7개의 패턴 상태가 세팅되면 수자가 만들어지게 되고 그 숫자를 출력      
    delay(1000); //딜레이 시간 1초을 줌
  }  

1초 단위로 0~9까지 숫자가 출력됩니다. 카운터 효과가 되겠죠.

종합해 보면,

#include <Adafruit_NeoPixel.h>

const byte neopixelPin = 3; 
Adafruit_NeoPixel neopixel = Adafruit_NeoPixel(7, neopixelPin, NEO_GRB + NEO_KHZ800);


 byte segValue[10][7] = {
   {1,1,1,0,1,1,1}, //0
   {1,0,0,0,1,0,0}, //1
   {1,1,0,1,0,1,1}, //2
   {1,1,0,1,1,1,0}, //3
   {1,0,1,1,1,0,0}, //4
   {0,1,1,1,1,1,1}, //5
   {0,0,1,1,1,1,1}, //6
   {1,1,0,0,1,0,0}, //7
   {1,1,1,1,1,1,1}, //8
   {1,1,1,1,1,1,0}  //9   
 };

void setup() {
  neopixel.begin();  
}

void loop() {      
  for(int i=0;i<10;i++){  //0~9까지 10개의 숫자를 반복
    for(int j=0;j<7;j++){ //한개의 숫자 당 7개의 네오픽셀 상태임으로 7번 반복
      if(segValue[i][j]==1){ //해당 숫자의 패턴 상태 체크
        neopixel.setPixelColor(j, 255, 0, 0); // 1이면 해당 네오픽셀에 Red color로 표현           
      }else{
        neopixel.setPixelColor(j, 0, 0, 0);  // 0이면 무색으로 표현       
      }
    }
    neopixel.show();  //7개의 패턴 상태가 세팅되면 수자가 만들어지게 되고 그 숫자를 출력      
    delay(1000); //딜레이 시간 1초을 줌
  }  
    
  //초기화  
  neopixel.clear(); 
  neopixel.show();
  delay(100);     
}

3) 결과


숫자 모양 틀로 표현했다면 아래 움짤이 숫자로 보였을 텐데 이상한 모양처럼 보이네요. 마지막에 9 다음에 0으로 다시 넘어갈 때는 0~9까지 1초 단위로 숫자가 바뀌다가 9 이후 초기화로 전부 지우는 동작을 한번 수행하기 때문에 약간 보시기에 부자연스러울 수 있을 꺼에요. 초기화 명령라인들을 지우고 프로그램을 실행시켜면 자연스러워질 꺼에요. 초기화 명령라인을 넣은 이유는 나중에 추가로 수정할 때 초기화 작업이 필요 할 수 있으니깐 이렇게 Neopixel를 초기화 할 수 있다는 것을 보여 드리기 위해서 초기화 명령라인을 남겨 뒀네요. 이부분을 감안하시고 보시기 바랍니다.


4. Neopixel 디지털 시계 디자인




대충 디지털 시계 디자인은 위와 같이 표현하시면 "00:00"으로 "시:분"으로 표현 할 수 있습니다. 여기서 초까지 출력되게 하고 싶다면 Neopixel를 더 추가해야 겠죠.

코딩은 위에서 숫자를 한개의 숫자를 출력하는데 0~6위치의 네오픽셀에 7개의 패턴을 만들어 출력했는데 이 원리를 기반으로 지난 시간에 아두이노 시계 코딩을 가지고 수정하시면 됩니다.

위 코딩에서는 일자의 자리 0~9까지를 카운터 했는데 십의 자리를 출력하고 싶다면

for문에서 0~6 Neopixel은 1의 자리이고 7~13 Neopixel은 10의 자리가 됩니다.

그러면 12라는 숫자를 출력하기 위해서는

7~13 Neopixel => 1
0~6 Neopixel => 2

이렇게 각 범위의 숫자를 만들어 내면 됩니다. 그러면 1의 자리 for문과 10의 자리 for문을 Neopixel 위치값을 지정해서 숫자를 만들어 내면 됩니다.

위의 사전학습에서 시계 코딩을 분석해 보고 오늘 post에서 숫자 카운터를 한 것을 기반으로 직접 상상 코딩을 통해 디티러 시계 코딩에 도전해 보세요.

이 코딩은 여러분들의 상상에 맡기고 소스는 공개하지 않겠습니다.

마무리


오늘은 Neopixel를 이용하여 7-Segment Display를 만드는 방법을 실험을 하였습니다. 직업 제작을 한다면 꽤 재밌는 것들을 만들 수 있습니다. 마지막에 디지털 시계 디자인 한 것처럼 시계를 만들 수 있습니다. 일반 발광다이오드(LED)로 디자인 하는 것보다 Neopixel 디자인이 좋은 점은 회로선을 연결하는데 편하고 아두이노에서 사용하는 핀은 단 한개로 제어가 가능합니다. 더 중요한 것은 바로 다양한 Color 표현이 가능하다는 점입니다. 3색 LED로 표현하면 Color를 만들 수 있지만 이경우는 회로도 선 배치가 단색 LED보다 더 복잡해지고 제어하는데 꽤 애먹습니다. 그러니 NeoPixel로 디자인하는게 훨 편하겠죠.

우연히 아두이노 디지털 시계 유튜드 영상을 보다가 "아! Neopixel이 있었지!"하고 Neopixel에 대해 잘 알고 있었는데 이걸 상상을 못했다는것이 참 아직도 아두이노의 실력이 부족하다는 것을 느낄 수 있었네요. 그래도 잠깐 몇초 본 영상으로 대충 느낌 아니깐 회로도 만드는 것은 어렵지 표현 했습니다.

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

댓글()

[아두이노] NEOPIXEL+피에조부조 이퀄라이저바 응용

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

[아두이노] NEOPIXEL+피에조부조 이퀄라이저바 응용



@codingart님의 led+스피커을 결합하여 사운드와 LED가 동시 출력하는 포스트를 보면서 가상시뮬레이터에서 구현해보고 싶어져서 이렇게 인용하여 실험을 한번 해 보았습니다. 그냥 그대로 표현하기가 좀 그래서 최근에 배웠던 NeoPixel로 응용해 보면 좋을 것 같아서 한번 적용해 보았습니다. 동작 구현은 멜로디 음계에 따라서 NeoPixel로 그래픽 이퀄아이저바 형태로 동시에 출력하는 로직으로 표현 했습니다. 즉, @codingart의 LED 부분을 NeoPixel로 대체하고 추가로 음계를 그래픽바로 응용했네요. 참고로 오늘 실험한 회로도와 코딩 부분은 위 참고자료 중 지금까지 연재한 내용에서 피에조부저 곰세마리 멜로디 코딩과 사운드센서 그래픽 이퀄아지어 표현을 합쳐서 만들 거라서 수정부분은 거의 없습니다. NeoPixel의 부분에서 한개 짜리를 바 형태로 출력 코딩을 약간 변경 했네요.

1. 회로도 구성


  • 준비물 : 피에조부조 1개, NeoPixel 8개, 아두이노우노
  • 내용 : 피에조부저은 출력핀을 제어 할 적당한 위치에 배치하고 NeoPixel은 바 형태로 만들어서 제어 할 위치에 배치하자.


위 그림은 12번 핀을 피에조부저 멜로디가 출력하는 핀으로 사용했으며 NeoPixel을 바 형태로 8개를 연결하고 3번핀을 통해서 제어하도록 선은 연결했네요. 참고로 NeoPixel의 자체의 input핀과 output핀을 정확히 확인하시고 선을 연결해야 합니다.

이제 기본 회로도 구성이 완료 되었으니깐 코딩을 해 볼까요.

2. 코딩 과정


  • 내용 : 피에조부저에 멜로디를 출력하면서 동시에 NeoPixel Bar에 멜로디 음계을 LED 길이로 출력해 보자.
    추가) NeoPixel의 Color는 랜덤함수를 사용하여 좀 더 화려하게 표현하자.

1) 피에조부조 멜로디



예전에 포스트 한 곰세마리 멜로디를 기억하실 지 모르겠네요. 기억이 안나거나 처음 접하시는 분들은 위 링크된 포스트를 참고해 주시고 따로 세부적으로 반복 설명은 하지 않겠습니다. 이미 다 설명한 내용이니깐요. 그러면 곰세마리 멜로디를 기본 베이스로 출발 하도록 하겠습니다.

[ 기본 소스 ]

#define NOTE_C5  523    //도
#define NOTE_D5  587    //레
#define NOTE_E5  659    //미
#define NOTE_F5  698    //파
#define NOTE_G5  784   //솔
#define NOTE_A5  880   //라
#define NOTE_B5  988   //시
#define NOTE_C6  1047 //도

int tonepin = 12;

int melody[] = {
NOTE_C5,NOTE_C5,NOTE_C5,NOTE_C5,NOTE_C5,
NOTE_E5,NOTE_G5,NOTE_G5,NOTE_E5,NOTE_C5,
NOTE_G5,NOTE_G5,NOTE_E5,NOTE_G5,NOTE_G5,NOTE_E5,
NOTE_C5,NOTE_C5,NOTE_C5,

NOTE_G5,NOTE_G5,NOTE_E5,NOTE_C5,
NOTE_G5,NOTE_G5,NOTE_G5,
NOTE_G5,NOTE_G5,NOTE_E5,NOTE_C5,
NOTE_G5,NOTE_G5,NOTE_G5,

NOTE_G5,NOTE_G5,NOTE_E5,NOTE_C5,
NOTE_G5,NOTE_G5,NOTE_G5,NOTE_A5,NOTE_G5,
NOTE_C6,NOTE_G5,NOTE_C6,NOTE_G5,
NOTE_E5,NOTE_D5,NOTE_C5
};

int noteDurations[]={
4,8,8,4,4,
4,8,8,4,4,
8,8,4,8,8,4,
4,4,2,
4,4,4,4,
4,4,2,
4,4,4,4,
4,4,2,
4,4,4,4,
8,8,8,8,2,
4,4,4,4,
4,4,2
};


void setup() { 
} 
void loop() {
  for (int i = 0; i < 49; i++) {
    
    int Durations = 1000/noteDurations[i];    // 음계의 음길이 계산
    tone(tonepin, melody[i], Durations);    
    int pauseBetweenNotes = Durations *1.3 ;
    delay(pauseBetweenNotes);
    noTone(tonepin);
  }

}

위 소스에서 NeoPixel의 코딩만 삽입하시면 됩니다.

2) NEOPIXEL 그래픽 이퀄라이저바에 사용 될 코드 추출



위 링크된 포스트를 참고하겠습니다. 링크 포스트에서는 "가변저항기와 NeoPixel"이라는 소제목의 가상시뮬레이터 실험이 있습니다. 가변저항기 값에 따라 한개의 NeoPixel의 Color를 만들어 냈던 실험이였는데 오늘은 이 원리를 이용하여 NeoPixel을 바 형태로 바꾸서 음계를 LED 길이 형태로 출력하기 위해서 우선 사용될 부분을 가져오도록 하겠습니다.

[ 기본 소스 ]

#include <Adafruit_NeoPixel.h>

const byte neopixelPin = 3; 
Adafruit_NeoPixel neopixel = Adafruit_NeoPixel(1, neopixelPin, NEO_GRB + NEO_KHZ800);
 
void setup()
{
  neopixel.begin();   
  randomSeed(analogRead(A1));
}

void loop()
{
  int SoundSenser=0; 
  int SoundColor=0; 
    
  SoundSenser = analogRead(A0);
  if(SoundSenser<26) SoundSenser=26;
  if(SoundSenser>300) SoundSenser=300;
  
  SoundColor= map(SoundSenser,26,300,0,255);
  neopixel.setPixelColor(0, random(SoundColor), random(SoundColor), random(SoundColor));    
  neopixel.show();
  delay(50);    
  neopixel.clear(); 
}

위 소스에서 필요한 부분만 빼내도록 하죠.

8개의 NeoPixel를 사용하고 3번핀이 제어핀이니깐 아래와 같이 기본 선언을 해야 겠죠.

#include <Adafruit_NeoPixel.h>

const byte neopixelPin = 3; 
Adafruit_NeoPixel neopixel = Adafruit_NeoPixel(8, neopixelPin, NEO_GRB + NEO_KHZ800);

Seteup() 세팅

void setup()
{
  neopixel.begin();   
  randomSeed(analogRead(A1));
}

NeoPixel를 시작하겠다고 선언해주고 NeoPixel의 칼러를 랜덤함수를 이용 할 예정이라서 랜덤함수 초기화 함수를 선언했네요. 이 두 줄은 그대로 사용합니다.

loop() 가져올 내용

neopixel.setPixelColor(네오픽셀위치, random(255), random(255), random(255));    
neopixel.show();
neopixel.clear(); 
  • setPixelColor() : NeoPixel Color 세팅
  • show() : NeoPixel로 출력
  • clear() : NeoPixel 세팅 된 값을 초기화

이 세가지 함수만 가져다 쓸 예정입니다. 이 세 함수로 그래픽바 출력이 이뤄지겠죠.

3) 피에조부저의 음계를 만들고 NeoPixel Bar로 출력

이부분이 핵심 코딩 부분입니다. 음계를 어떻게 NeoPixel Bar로 출력을 할까요. 오늘 실험에서는 음계의 값을 기준으로 제어를 하도록 하겠습니다.

음계를 배열변수로 선언 해볼까요.

const int scaleval[8] = {NOTE_C5,NOTE_D5,NOTE_E5,NOTE_F5,NOTE_G5,NOTE_A5,NOTE_B5,1047};

scalval[8] 배열변수에 5옥타브의 음계 "도레미파솔라시도"를 저장했습니다.

음계를 NeoPixel Bar로 출력합시다.

어떻게 NeoPixel의 길이로 출력을 할까요. 여기서, 상상력이 많이 막히실 꺼에요. 마이크사운드감지센서에서 기억하실 지 모르겠지만 입력소리를 영역으로 나눠서 구분 할 수 있다고 했습니다. 음을 영역으로 나누면 나눈 영역이 하나의 NeoPixel의 영역으로 간주 할 수 있게 됩니다. 해당 음계의 영역에 매칭되는 NeoPixel의 위치에 불이 들어오게 하면 쉽게 해결 되겠죠. 그래서, 음계를 scaleval[8] 배열변수로 만들었지요.

곰곰히 음계배열변수에 살펴보세요. '도'은 0번째 배열에 들어 있고, '레'는 1번째 배열에 들어 있습니다. 각 음계의 배열의 순차적으로 들어 있는데 8개NeoPixel를 이 배열에 매칭 시키면 어떻게 될까요.

여기서, '도'은 NeoPixel 0번째에만 불이 들어오고 '미'은 NeoPixel 2번째까지 불이 들어오게 만들어 코딩하면 음계가 NeoPixel의 그래픽 길이로 출력이 가능해 집니다.

출력 코딩을 어떻게 할까요. 기초 코딩에서 배열변수를 배웠습니다. 배열변수가 나오면 기본적으로 for문을 떠올리시라고 했죠. for문을 활용하면 쉽게 해결이 됩니다.

코딩은, 아래와 같습니다.

 for(int i=0;i<8;i++){
    if(scaleval[i]<=soundval){
      neopixel.setPixelColor(i, random(255), random(255), random(255));    
      neopixel.show();
    } 
    else break;
 }

이 코딩을 어떻게 나왔는지 순차적으로 코딩하는 과정을 볼까요.

for문으로 우선 8개의 NeoPixel에 불이 들어오게 만들어 봅시다.

 for(int i=0;i<8;i++){   
      neopixel.setPixelColor(i, random(255), random(255), random(255));    
      neopixel.show();
            delay(50);
  }
    neopixel.clear(); 

1초(1000)인 delay 시간값 50을 기준으로 setPixelColor()함수로 순차적으로 NeoPixel의 색이 세팅되고 show()함수로 NeoPixel로 출력이 됩니다.

한개의 음계의 값만큼 NeoPixel에 불이 들어오게 만들어 볼까요.

기초 코딩에서 배운 if문으로 조건문을 만들면 됩니다.

 const int scaleval[8] = {NOTE_C5,NOTE_D5,NOTE_E5,NOTE_F5,NOTE_G5,NOTE_A5,NOTE_B5,1047};
 for(int i=0;i<8;i++){   
    if(scaleval[i]<=입력음계){
      neopixel.setPixelColor(i, random(255), random(255), random(255));    
      neopixel.show();          
        } 
        else break;
  }
    neopixel.clear(); 

if문으로 8개의 음계를 for문으로 비교하게 됩니다. 입력한 음계랑 비교해서 "작거나 같다"면 해당 i번째의 NeoPixel의 Color가 만들어 지도록 조건문을 다시면 됩니다. 즉, 입력한 음계까지만 NeoPixel에 불이 들어오게 하는 것이죠.

여기서, else 문에서 break 명령을 붙였는데 이건 더이상 수행하지 말고 for문을 빠져 나오라는 문장입니다. Switch(선택제어문)에서 배우셨죠. 반복 수행을 더 할 필요가 없기 때문에 for문을 빠져나오는게 더 효율적인 코딩이겠죠. 참고로 생략은 가능합니다. 어짜피 if문에 입력 음계까지만 세팅이 이뤄지고 나머지 음계 i번째는 조건을 만족하지 않기 때문에 if문 안의 문장을 수행하지 않습니다. 8번 반복이니깐 else 이하 코딩은 생략하셔도 별로 지장은 없네요.

이제 입력음계까지 NeoPixel Bar 출력을 할 수 있겠죠.

4) 사용자 정의 함수로 만들기

loop 함수에다가 그대로 코딩하면 loop 함수의 가독성은 떨어집니다. 그렇기 때문에 NeoPixel Bar 출력을 사용자 정의 함수로 빼면 loop()함수 내부가 좀 깨끗해지겠죠.

void nepixelbar(int soundval){
  for(int i=0;i<8;i++){
    if(scaleval[i]<=soundval){
      neopixel.setPixelColor(i, random(255), random(255), random(255));    
      neopixel.show();
    }
    else break;
  }
}

이렇게 해서 soundval 값이 입력되면 그 입력된 값에 따라서 NeoPixel Bar가 출력이 됩니다.

여기서 표현은 두가지가 있습니다. neopixel.show() 명령문을 for문 안에다 표현해도 되고 for문 밖에다 표현해도 됩니다. 약간 차이가 있을 뿐 원하는 스타일로 코딩 하시면 됩니다.

5) 곰세마리멜로디 코딩에 NeoPixel 코딩을 합치기

void loop() {
  for (int i = 0; i < 49; i++) {
    
    int Durations = 1000/noteDurations[i];    // 음계의 음길이 계산
    tone(tonepin, melody[i], Durations);    
    int pauseBetweenNotes = Durations *1.3 ;
    delay(pauseBetweenNotes);
    noTone(tonepin);
  }
}

음계는 melody[i]가 됩니다. 그러면 for문에서 소리가 나기 직전 앞에다가 직접 만든 nepixelbar()함수를 삽입하면 NeoPixel에 불이 들어오는 동시에 음의 소리가 나겠죠.

    int Durations = 1000/noteDurations[i];    
    
    nepixelbar(melody[i]); //삽입 위치
    
    tone(tonepin, melody[i], Durations);  

여기서, 한 음계의 NeoPixel로 출력 했을때 출력 시간은 곰세마리 멜로디의 한음이 끝 날때까지 유지하고 초기화 해야 다음 음계에 NeoPixel로 출력이 가능해 집니다. 초기화 neopixel.clear() 함수를 소리가 끝나는 마지막에 삽입해야 겠지요.

  noTone(tonepin);
    
  neopixel.clear(); //삽입 위치

종합해보면.

#include <Adafruit_NeoPixel.h>

#define NOTE_C5  523    //도
#define NOTE_D5  587    //레
#define NOTE_E5  659    //미
#define NOTE_F5  698    //파
#define NOTE_G5  784   //솔
#define NOTE_A5  880   //라
#define NOTE_B5  988   //시
#define NOTE_C6  1047 //도

const byte neopixelPin = 3; 
Adafruit_NeoPixel neopixel = Adafruit_NeoPixel(8, neopixelPin, NEO_GRB + NEO_KHZ800);

const int tonepin = 12;

const int scaleval[8] = {NOTE_C5,NOTE_D5,NOTE_E5,NOTE_F5,NOTE_G5,NOTE_A5,NOTE_B5,1047};
//멜로디
const int melody[] = {
NOTE_C5,NOTE_C5,NOTE_C5,NOTE_C5,NOTE_C5,
NOTE_E5,NOTE_G5,NOTE_G5,NOTE_E5,NOTE_C5,
NOTE_G5,NOTE_G5,NOTE_E5,NOTE_G5,NOTE_G5,NOTE_E5,
NOTE_C5,NOTE_C5,NOTE_C5,

NOTE_G5,NOTE_G5,NOTE_E5,NOTE_C5,
NOTE_G5,NOTE_G5,NOTE_G5,
NOTE_G5,NOTE_G5,NOTE_E5,NOTE_C5,
NOTE_G5,NOTE_G5,NOTE_G5,

NOTE_G5,NOTE_G5,NOTE_E5,NOTE_C5,
NOTE_G5,NOTE_G5,NOTE_G5,NOTE_A5,NOTE_G5,
NOTE_C6,NOTE_G5,NOTE_C6,NOTE_G5,
NOTE_E5,NOTE_D5,NOTE_C5
};

//음길이
const int noteDurations[]={
4,8,8,4,4,
4,8,8,4,4,
8,8,4,8,8,4,
4,4,2,
4,4,4,4,
4,4,2,
4,4,4,4,
4,4,2,
4,4,4,4,
8,8,8,8,2,
4,4,4,4,
4,4,2
};

void setup() { 
  neopixel.begin(); 
  randomSeed(analogRead(A1));
} 
void loop() {
  //연주
  for (int i = 0; i < 49; i++) {    
    int Durations = 1000/noteDurations[i];    // 음계의 음길이 계산
    
    nepixelbar(melody[i]);
    
    tone(tonepin, melody[i], Durations);    
    int pauseBetweenNotes = Durations *1.3 ;
    delay(pauseBetweenNotes);
    noTone(tonepin);
    
    neopixel.clear();     
  }

}

void nepixelbar(int soundval){
  for(int i=0;i<8;i++){
    if(scaleval[i]<=soundval){
        neopixel.setPixelColor(i, random(255), random(255), random(255));    
        eopixel.show();
    }
    else break;
  }
}

3. 결과


[출력 이미지 샷]


[구현 과정 동영상]


마무리


@codingart의 작품을 가상시뮬레이터로 응용해서 표현을 했네요. 기초 코딩 문법을 소개 한 뒤에 의욕이 앞선 나머지 오늘 포스팅 내용이 코딩에 너무 치우쳐서 전개한 듯 싶네요. 사용 된 기초 코딩 문법은 배열변수, IF문, FOR문 입니다. 중요한 것은NeoPixel에 불이 어떻게 들어오는지에 대한 원리를 이해하시면 상상력을 더하면 NeoPixel에 불이 들어오게 하는 방식을 다양하게 표현을 할 수 있게 됩니다.

그리고, 하나의 원리 배우면 표현을 "이걸로 뭘 하지! 뭘 표현할 수 있지! 이것을 써먹을 수 있는 곳은 어디 없을까!"라고 계속 자신에게 질문을 던져주세요. 이런 트래이딩 학습을 계속 하시게 되면 처음에는 좀 힘들지만 나중에 다양한 표현이 가능해집니다.

저도 처음에 C언어 기초 문법을 공부할 때 학습 방법입니다. 코딩은 같은 표현도 다양하게 표현이 가능이 가능합니다.

c=a+b;
printf("%d",c);

이런 표현이 있다면

printf("%d",a+b);

이렇게 직접적으로 표현 할 수 있고

c=a+b;
printf("%d + %d = %d",a,b,c);

이렇게 표현할 수 있습니다. 다 같은 덧셈의 원리를 이용하지만 표현하는 것은 코딩하시는 여러분의 상상력에 달려 있습니다.
비유가 적절했는지 모르겠지만 이렇게 정해진 코딩과 표현은 없습니다.

저는 C언어를 입문할 때 하나의 원리를 배우면 그 표현을 다양한 형태로 5가지 이상으로 표현하는 학습을 하였습니다. 처음에는 약간 힘들지만 이렇게 코딩하다 보면 어느순간에 새로운 코딩 원리를 발견하고 배우게 되면 이 원리에 대해서 다양하게 접근하고 표현하는 상상이 자연스럽게 되어 지더군요.

여러분들도 오늘 포스팅한 내용을 그대로 보시지 마시고 한번 여기서 더 추가하고 코딩을 변경해보고 새로운 표현이 없을지 상상력을 발휘 해보셨으면 합니다.


댓글()

[아두이노] NeoPixel 증흥적 패턴 놀이

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

[아두이노] NeoPixel 증흥적 패턴 놀이 



오늘은 NeoPixel를 가지고 for문만을 이용해서 즉흥적으로 깜박이는 실험을 해 보겠습니다. 지난 시간에는 패턴을 만들고 그 만들어진 패턴을 변수에 저장하고 그것을 다시 불러와서 출력하는 과정이 좀 복잡하고 어렵게 느껴졌을 거라 생각되네요. 재밌는 NeoPixel를 너무 어렵게 나간것 같아서 원래 예정에 없던 추가 포스트를 작성하게 되었네요. for문 하나를 가지고 여러가지 패턴으로 움직이게 하여 NeoPixel를 가지고 놀아 보도록 합시다.


오늘 제작 할 NeoPixel의 모습입니다.

1. 회로도 구성


  • 준비물 : NeoPixel Ring 24 1개 NeoPixel Ring 12 1개, NeoPixel Jewel 1개, 아두이노우노
  • 내용 : 사용 할 NeoPixel를 핀에 맞게 간단히 연결하시오

아래 그림에서는 NeoPixel Ring 24 짜리는 선을 구별할 수 있지만 NeoPixel Ring 12와 NeoPixel Jewel은 우리가 NeoPixel를 지난 시간분터 계속 연결해왔기 때문에 안보여도 대충 어떤식으로 연결되는지 아실거라고 생각되기에 그 부분은 설명에서 제외하겠습니다. 아래 결과물 동영상에서 제작과정이 잘 나와 있으니깐 참고해 주세요.


선연결은 그리 어렵지 않을꺼에요 Vcc은 Vcc로 Gnd은 Gnd로 각각 서로 연결하시면 됩니다. 그리고 아두이노에서 출력하는 값은 3번 핀으로 Orange 선이 Neopixel의 입력핀으로 연결되고 NeoPixel의 출력핀은 다른 NeoPixel의 입력핀과 연결된다는 점만 구별해 주세요.

2. 코딩


  • 사용함수 : Adafruit_NeoPixel(), neopixel.begin(), neopixel.setPixelColor(), neopixel.show(), neopixel.clear()
  • 내용 : 증흥적 다양한 릴레이 동작을 구현해 보자.(랜덤함수로 색을 다양하게 표현)

함수

  • Adafruit_NeoPixel(NeoPixel수, NeoPixelPin, NEO_GRB + NEO_KHZ800) : 생성자함수로 NeoPixel 클래스 객체를 만들때 사용합니다. 몇개의 NeoPixel를 사용하고 어느 핀을 사용할지와 Neopixel 타입을 설정을 하는 함수입니다. 우선 가상시뮬에이터는 이 값을 기본으로 사용하세요.
  • neopixel.begin() : 라이브러릴 사용하면 꼭 객체 시작을 알리는 함수가 있죠. 이것도 마찬가지입니다. 사용하겠다고 선언.
  • neopixel.setPixelColor(위치, R, G, B) : 색을 나타냅니다. 위와 같은 Neopixel 타입을 설정했을때 RGB의 위치는 이와 같이 결정됩니다. 참고로 타입이 바뀌면 색상 위치도 바뀌니깐 햇갈리지 마세요. 쉽게말해서, 색을 세팅하는 함수입니다.
  • neopixel.show() : 색이 세팅한 값을 실제로 보여주라는 함수입니다. Orange선을 통해서 해당 위치에 색이 켜지겠지요.
  • neopixel.clear() : 지금까지 켜진 NeoPixel를 초기화하는 함수입니다. 켜진 NeoPixel를 다 꺼지겠지요.

[기본 소스]

#include <Adafruit_NeoPixel.h>

const byte neopixelPin = 3; 
Adafruit_NeoPixel neopixel = Adafruit_NeoPixel(43, neopixelPin, NEO_GRB + NEO_KHZ800);

void setup() {
  neopixel.begin();  
}

void loop() {  
  for(int i=0;i<43;i++){
    neopixel.setPixelColor(i, 255, 0, 0);    
    neopixel.show();
    delay(100);    
  } 
  neopixel.clear(); 
}

순서대로 NeoPixel에 불이 들어오는지 첨에 이 소스로 돌려주세요.

랜덤 함수로 NeoPixel의 색을 결정하기 위해서 두개의 함수를 더 사용합니다.

randomSeed(analogRead(A0))
random(255)

setup()함수에다가 randomSeed()함수로 A0핀을 난수를 만들 핀으로 사용하겠다고 선언해주시고요.
실제로 RGB는 각각 0~255사이의 값으로 출력되기 때문에 random(255)로 0~255사이의 난수 값을 얻게 됩니다.

[전체 소스]

#include <Adafruit_NeoPixel.h>
const byte neopixelPin = 3; 
Adafruit_NeoPixel neopixel = Adafruit_NeoPixel(43, neopixelPin, NEO_GRB + NEO_KHZ800);

void setup() {
  neopixel.begin();  
  randomSeed(analogRead(A0));
}
void loop() { 
  
  //순차적으로 릴레이
  for(int i=0;i<43;i++){ //1씩 증가하니깐 0~42까지 해당 위치에 NeoPixel의 색을 결정
    neopixel.setPixelColor(i, random(255), random(255), random(255));    
    neopixel.show();
    delay(50);    
  } 
  neopixel.clear(); 
  
  //홀수층 릴레이
  for(int i=0;i<43;i+=2){  //0부터 2씩 증가하니깐 홀수 위치에 색을 설정
    neopixel.setPixelColor(i, 0,255,0);
    neopixel.show();
    delay(50);    
  }  
  //neopixel.clear(); 
  
  //짝수층 릴레이
  for(int i=1;i<43;i+=2){ //1부터 2씩 증가니깐 짝수 위치가 되겠죠.
    neopixel.setPixelColor(i, 0,0,255);
    neopixel.show();
    delay(50);    
  }  
  neopixel.clear(); 
 
  //홀수 위치는 밖에서부터 짝수 위치는 안쪽부터 두가지 패턴을 한번에 세팅
  for(int i=0;i<43;i+=2){
    neopixel.setPixelColor(i, 0,255,0);
    neopixel.setPixelColor(42-i-1, 0,0,255);
    neopixel.show();
    delay(50);    
  }  
  neopixel.clear(); 
  
  //짝수 홀수 교대로 깜박이기 (위의 홀수와 짝수를 릴레이 하는걸 홀수전체 세팅, 짝수 전체세팅으로 설정
  for(int k=0;k<5;k++){ //교대로 반복 5회
    for(int i=0;i<43;i+=2){
        neopixel.setPixelColor(i, random(255),random(255),random(255));  
    }
    neopixel.show();
    delay(50);    
    neopixel.clear(); 
  
    for(int i=1;i<43;i+=2){
        neopixel.setPixelColor(i, random(255),random(255),random(255));
    }
    neopixel.show();
    delay(50);    
    neopixel.clear(); 
  }
}

위 소스만 보면 코딩이 엄청 길지만 기본소스에 원리를 그대로 적용해서 어떻게 출력할지만 표현 했을뿐 자세히 보시면 코딩이 반복되는 코딩으로 for문의 규칙을 잘 살펴보시면 별차이가 없다는 것을 느끼 실 꺼에요.

여기서, 코딩을 이해하실때 neopixel.show()함수와 neopixel.clear()함수의 의미를 의해 잘 생각 하시고 어느 위치에 있느냐에 따라서 결과가 어떻게 변하는지 구별 하실 수 있게 되면은 자기가 원하는 형태로 다양한 패턴을 만들어 낼 수 있을 거라 생각됩니다.

3. 결과


결과만 보여드릴까도 했는데 제작과정을 다 담았네요. 참고로 짝수 릴레이, 홀수 릴레이에서 동영상에서 실수로 오타가 났네요. '0,2,4,,6,8,10...' 순으로 가니깐 짝수인것처럼 한글로 짝수 릴레이라고 했는데요. 실제로는요 0이 첫번째 NeoPixel이니깐 홀수층 릴레이가 됩니다. 그리고 '1,3,5,7,9...'순으로 가니깐 한글로 홀수 릴레이라고 했는데요 이건 반대로 배열 1번은 2번째 위치입니다. 그러니 정확한 표현은 짝수층이 되겠죠. 아무튼, 동영상에서는 오타가 난거고 숫자가 짝수니깐 짝수층이고 홀수니깐 홀수층이라고 생각하시면 안됩니다. 정확히 짚고 넘어갑니다.

프로그램에서는 배열에서 a[0]은 1번째 위치고 a[1]은 2번째 위치입니다. 순서로 a[0]은 홀수 위치가 되고 a[1]은 짝수 위치가 됩니다. 이부분을 한글 오타를 정정합니다.

동영상을 급하게 대충 표현한거라 여러분들은 더 멋진 패턴으로 깜박이게 한번 해보세요.


마무리


NeoPixel은 다양한 형태로 출력할 수 있기 때문에 재밌는 부품입니다. RGB LED를 쉽게 여러개을 한번에 연결하여 제어할 수 있는 부품이라고 생각하시면 아마 될 듯 싶네요.
실제로 구매하셔서 만들어 보시는 것도 재미있을 듯 싶네요.


댓글()

[아두이노] NeoPixel로 글자패턴 만들기

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

[아두이노] NeoPixel로 글자패턴 만들기 


오늘의 주제는 NeoPixel를 이용해서 지난 시간에 배운 3x3x3 CUBE LE와 8x8 Dot Matrix에서 사용한 코딩기법을 복습하는 시간을 가져보도록 하겠습니다. 기본 출력 로직을 회로도에 맞춰서 만들어 놓으면 나머지 패턴 글자만 만드는 작업만 좀 고생 되겠지만 이 후 코딩은 글자 패턴를 만드는 작업뿐이 없으니깐 아마 편하게 코딩 할 수 있을꺼에요. 암튼 좀 어려울 수 있는 내용이지만 코딩을 처음 어떻게 시작해서 어떻게 진화한 코딩으로 바뀌는지 느끼는 시간이 되었으면 합니다. 코딩은 처음부터 정해진 코딩으로 뚝닥 완성되는게 아니라 출발은 아래 글처럼 하나의 글자를 증흥적으로 코딩해보고 다른 방식들을 찾아보면서 최종적으로 자신이 원하는 형태의 로직 형태를 찾아서 그 결과 로직을 토대로 다양한 글자들을 만들어 아래와 같은 결과물을 얻게되는데 이 과정을 잘 살펴 보셨으면 합니다. 완성된 코딩은 그렇게 까지 의미는 없습니다. 자신이 출발은 어디서 부터이고 그 출발에서 어떻게 변화 해가는지가 중요합니다. 아래의 코딩과정을 길게 나열한 것은 그 의미를 전달하기 위해서 입니다. 이 글을 쓰는 저자가 완성된 로직을 만들기 위해서 처음에 여기서부터 출발했고 어떤 과정을 거쳐서 여기까지 왔구나 정도만 이해하시면 됩니다.

코딩한 결과가 중요한 것이 아니라 코딩하는 과정이 무척 중요합니다.

1. NeoPixel 회로도 배치 고민


처음에는 위와 같은 순서대로 회로도를 연결했습니다. 문제는 위치에 대한 공간인지가 좀 불편하실 수 있으며 나중에 2차배열로 위치값을 정해야 하는데 순번이 햇갈릴 수 있기 때문에 이경우는 초보분들에게 혼동을 야기 할 것 같아서 포기했습니다.


그래서 옆으로 눞여 봤는데 홀수 행은 배열 형태를 잘 나타나는데 짝수행은 배열의 역순 위치 값으로 표기 되기 때문에 이 또한 혼동을 야기 할 것 같아서 포기했습니다.

최종적으로 선 연결을 복잡하게 한 대신에 배열 형태를 맞춰서 디자인 했습니다. 그러면 어떻게 했는지 회로도 구성에서 살펴보도록 하겠습니다.

2. 회로도 구성


  • 준비물 : NeoPixel 35개, 아두이노우노
  • 내용 : 사용 할 NeoPixel를 간단히 연결해 보자.


회로도의 경우는 자신의 원하는 형태로 연결하시면 됩니다. 단, 연결하실 때는 어떤식으로 코딩할지에 대해서 고려하시고 연결하셔야 해요. 연결하고 나서 그 연결된 형태를 고려해서 코딩을 해도 되지만 반대로 코딩 할 로직을 어느정도 틀이 잡혀 있으면 그 틀에 맞게 회로도를 배치하는 것도 좋습니다. 제가 코딩을 소개하는 방향에서는 2차배열을 사용하기 때문에 최대한 위치에 대한 순서를 맞춰서 회로도의 선을 연결했네요.

3. 코딩 과정


  • 사용함수 : Adafruit_NeoPixel(), neopixel.begin(), neopixel.setPixelColor(), neopixel.show(), neopixel.clear()
  • 내용 : "STEEM CODINGMAN" 글자를 만들어 출력하자.
  • 참고소스 :

함수

  • Adafruit_NeoPixel(NeoPixel수, NeoPixelPin, NEO_GRB + NEO_KHZ800) : 생성자함수로 NeoPixel 클래스 객체를 만들때 사용합니다. 몇개의 NeoPixel를 사용하고 어느 핀을 사용할지와 Neopixel 타입을 설정을 하는 함수입니다. 우선 가상시뮬에이터는 이 값을 기본으로 사용하세요.
  • neopixel.begin() : 라이브러릴 사용하면 꼭 객체 시작을 알리는 함수가 있죠. 이것도 마찬가지입니다. 사용하겠다고 선언.
  • neopixel.setPixelColor(위치, R, G, B) : 색을 나타냅니다. 위와 같은 Neopixel 타입을 설정했을때 RGB의 위치는 이와 같이 결정됩니다. 참고로 타입이 바뀌면 색상 위치도 바뀌니깐 햇갈리지 마세요. 쉽게말해서, 색을 세팅하는 함수입니다.
  • neopixel.show() : 색이 세팅한 값을 실제로 보여주라는 함수입니다. Orange선을 통해서 해당 위치에 색이 켜지겠지요.
  • neopixel.clear() : 지금까지 켜진 NeoPixel를 초기화하는 함수입니다. 켜진 NeoPixel를 다 꺼지겠지요.

[기본 소스]

#include <Adafruit_NeoPixel.h>

const byte neopixelPin = 3; 
Adafruit_NeoPixel neopixel = Adafruit_NeoPixel(35, neopixelPin, NEO_GRB + NEO_KHZ800);

void setup() {
  neopixel.begin();  
}

void loop() {  
  for(int i=0;i<35;i++){
    neopixel.setPixelColor(i, 255, 0, 0);    
    neopixel.show();
    delay(100);    
  } 
  neopixel.clear(); 
  neopixel.show();
  delay(100);   
}

순서대로 NeoPixel에 불이 들어오는지 테스트를 하였습니다.

2) 하나의 'S' 글자패턴 만들기


위 표에서 보는 것 처럼 2차 배열이 있으면 'S'라는 글자를 만들면 해당 위치에 가운데 표에서 보는 것처럼 색이 칠해지겠죠. 그 위치값을 가지고 글자패턴을 만든다면 아래와 같이 표현이 됩니다.

const byte p[7][5]= {
        {0,1,2,3,0},
        {5,0,0,0,9},
        {10,0,0,0,0},
        {0,16,17,18,0},
        {0,0,0,0,24},
        {25,0,0,0,29},
        {0,31,32,33,0}};

즉, 배열에다가 위치값을 아예 저장하는 방식입니다. 출력을 해볼까요.

   for(int i=0;i<7;i++){
      for(int j=0;j<5;j++){
          if(p[i][j]!=0)neopixel.setPixelColor(p[i][j], 255, 0, 0);    
      }     
    }
    neopixel.show();
    delay(1000);

위 코딩으로 2차원 배열로 행7과 열 5로 순차적으로 0이 아닌 위치에 neopixel.setPixelColor()함수로 색을 세팅합니다. 2차 for문이 다 돌고나면 'S'라는 글자의 위치에는 색이 다 세팅되어 있겠죠. 그리고 나서 neopixel.show()함수로 실제 NeoPixel로 출력하게 됩니다. 그러면 'S'라는 글자를 볼 수 있습니다.

#include <Adafruit_NeoPixel.h>

const byte neopixelPin = 3; 
Adafruit_NeoPixel neopixel = Adafruit_NeoPixel(35, neopixelPin, NEO_GRB + NEO_KHZ800);
const byte p[7][5]= {
        {0,1,2,3,0},
        {5,0,0,0,9},
        {10,0,0,0,0},
        {0,16,17,18,0},
        {0,0,0,0,24},
        {25,0,0,0,29},
        {0,31,32,33,0}};
void setup() {  
   neopixel.begin();   
}       
void loop(){        
    for(int i=0;i<7;i++){
      for(int j=0;j<5;j++){
          if(p[i][j]!=0)neopixel.setPixelColor(p[i][j], 255, 0, 0);    
      }     
    }
    neopixel.show();
    delay(1000);
    neopixel.clear();
    neopixel.show();
    delay(100);  
}       

2) 글자패턴 다른식으로 접근

사실 위 코딩으로 's' 글자를 만들때 위치값을 해도 되지만 '0'과 '1'로 표현을 해보도록 하겠습니다. 글자가 들어가는 위치를 '1'로 그렇지 않는 곳은 '0'으로 표현 하겠습니다.

const byte p[7][5]= 
        {{0,1,1,1,0},
        {1,0,0,0,1},
        {1,0,0,0,0},
        {0,1,1,1,0},
        {0,0,0,0,1},
        {1,0,0,0,1},
        {0,1,1,1,0}};

그러면 처음 코딩한 곳에서 어디를 수정해야 할까요. 바로 for문안에 문장이겠죠.

   byte y=0;
   for(int i=0;i<7;i++){
      for(int j=0;j<5;j++){
          if(p[i][j]!=0)neopixel.setPixelColor(y+j, 255, 0, 0);   
      }
            y+=5; // 행위치값을 5씩 증가,
    }

그냥 i+y하면 안됩니다. 그 이유는 i가 0일때는 j에 더하면 '0,1,2,3,4'가 나오지만 i가 1일때는 '1,2,3,4,5'가 나오게 됩니다. 원래는 '5,6,7,8,9'이렇게 나와야 하는데 말이죠. 이렇게 나오게 하기 위해서 y라는 변수를 행 위치변수로 하나 만들어서 행의 위치를 나타내는 변수가 필요합니다. 행인 i가의 for문이 증가할때마다 행위치변수인 y은 5씩 증가하게 표현함으로 2차 배열의 위치를 정확히 지정할 수 있게 됩니다.

첫번째 코딩한 것에서 위 표현을 변경할 부분에 삽입하셔서 바꾸시면 됩니다. 변수선언과 for문안에 로직과 y변수 선언하시면 됩니다.

3) '0'과 '1'의 표현이면 '0B00000000'로 표현

'0'과 '1'이 나왔으면 어떤 표현이 떠오르지 않나요. 바로 byte형으로 하나의 숫자 값으로 '0B00000000'로 표현이 떠오르셨다면 지난 내용을 제대로 공부하셨던 분일꺼에요, 이 표현을 통해 열을 표현한 5개의 배열공간을 하나의 숫자로 표현이 가능해 집니다.

01110 + 000 => 0B01110000

뒤에다 '000' 붙였는지만 앞에다 붙이셔도 됩니다. 실험은 뒤에다 붙여서 실험했는데 이 표현은 제 마음이니깐요 이게 꼭 해야하는 건 아니니깐 앞이든 뒤든 상관이 없고 나중에 for문을 표현할 때 앞이냐 뒤냐에 따라서 표현이 좀 달라지기는 하지만 그렇게 큰 의미는 없는 표현입니다.

const byte p[7]= {
0B01110000,
0B10001000,
0B10000000,
0B01110000,
0B00001000,
0B10001000,
0B01110000 }

이렇게 표현되는데 아두이노라이브러리에 bit를 읽는 함수를 기억하실지 모르겠네요. bitRead(x,n)함수가 있습니다. X라는 숫자에서 n번째 위치의 bit값을 가져오는 함수입니다.

위의 패턴배열변수에서

0B01110000

이값을 가져올려면

const byte p=0B01110000;
for(int j=7;j>2;j--){
     bitRead(p, j);
}

이렇게 표현해야합니다. 읽는 위치는 역순이니깐요. 오른쪽끝이 0번째가 됩니다. 그런데 왼쪽부터 순서대로 하나식 가져올려면 7번 위치의 자리에 값부터 순서대로 읽어와야겠죠. 총 5개니깐 j는 2보다 커야하고요. 순서대로 가져오니깐 'j--'로 감소연산자로 1씩 감소시켜야 겠죠. 이렇게 표현해야 왼쪽부터 순서대로 1bit씩 가져오게 됩니다.

로직은 이러헥 되겠죠.

    byte x=0; //열위치
   byte y=0;//행위치
   for(int i=0;i<7;i++){
      for(int j=7;j>2;j--){
          if(bitRead(p[i,j)]!=0)neopixel.setPixelColor(y+j, 255, 0, 0);   
                x+=1;
      }
            x=0;
            y+=5; // 행위치값을 5씩 증가,
    }

여기서 열의 위치값을 j로 할 수 없습니다. j가 7부터 시작하기 때문이지요. 그렇기 때문에 따로 x변수로 열도 위치변수를 만들어 줘야 합니다. j가 반복수행 할 때 x를 1씩 증가하게 하고 j가 5번 반복이 끝나면 x를 초기화 해야합니다. 두번째 행의 위치를 처음부터 다시 찾아야 하니깐요.

변수 선언과 이 로직을 처음 코딩한 소스에서 삽입하고 변경하시면 됩니다. 따로 전체소스는 안올리겠습니다.

4) 'S' 글자 16진수화

0B01110000 = 0x70

const byte p[7] ={0x70,0x88,0x80,0x70,0x08,0x88,0x70};

이렇게 한줄로 대폭 줄일 수 있습니다. 단, 힘든 16진수 계산이 필요하지만요. 따로 16진수로 출력되는 프로그램을 만드셔도 됩니다. 해당 2진수 값이 저장된 배열을 for문을 이용해서 출력을 16진수로 바꾸시면 됩니다. 참고로 10진수로 변경하셔도 됩니다.

5) 'STEEM CODING MAN' 글자패턴을 만들기


글자 한개를 만드는 것도 힘들데 총 14자의 글자를 만든다고 생각하니깐 앞이 깜깜하시죠. 이제 글자를 쉽게 만드는 꼼수를 찾아냈는데 알려드리도록 하겠습니다.



위 블로그에 올리신 분께서 젤 하단에 이런 프로그램을 링크 걸어 놓았네요. 글자 패턴을 만들기 힘드신분들을 위한 배려인 듯 합니다. 저도 이런거 프로그램을 만든분 없는지 구글 검색을 통해서 찾아보다가 우연히 발견했네요. 그리고, 쉽게 글자를 만들었네요.


7행의 패턴 글자이기 때문에 마지막 8행의 16진수 값은 필요 없으니깐 지우시면 됩니다. 이렇게 글자를 만들면 다음과 같은 패턴 배열변수가 완성되었네요.

이런식으로 글자들을 14자를 완성하면 이렇게 간단히 표현됩니다.

const byte p[14][7]={ //글자 패턴이 14개임
  {0x70,0x88,0x80,0x70,0x08,0x88,0x70}, //S
    {0xF8,0x20,0x20,0x20,0x20,0x20,0x20}, //T
    {0xF8,0x80,0x80,0xF0,0x80,0x80,0xF8}, //E
    {0xF8,0x80,0x80,0xF0,0x80,0x80,0xF8}, //E
    {0x88,0xD8,0xA8,0x88,0x88,0x88,0x88}, //M
    {0x70,0x88,0x80,0x80,0x80,0x88,0x70}, //C
    {0x70,0x88,0x88,0x88,0x88,0x88,0x70}, //0
    {0xF0,0x88,0x88,0x88,0x88,0x88,0xF0}, //D
    {0x70,0x20,0x20,0x20,0x20,0x20,0x70}, //I
    {0x88,0x88,0xC8,0xA8,0x98,0x88,0x88}, //N
    {0x70,0x88,0x80,0xB8,0x88,0x88,0x70}, //G
    {0x88,0xD8,0xA8,0x88,0x88,0x88,0x88}, //M
    {0x70,0x88,0x88,0xF8,0x88,0x88,0x88}, //A
    {0x88,0xD8,0xA8,0x88,0x88,0x88,0x88} //M
    };

이 글자가 14개의 패턴 글자가 나오니깐 기존 소스에서 14번 for문을 한번 더 돌려야 겠죠.

for(int k=0;k<14;k++){

        기존 2차for문 로직;
        y=0; // 하나의 패턴 글자가 끝나니깐 y행위치 변수값을 초기화 해야겠죠.
}

최종적으로 코딩을 하면

#include <Adafruit_NeoPixel.h>

const byte neopixelPin = 3; 
Adafruit_NeoPixel neopixel = Adafruit_NeoPixel(35, neopixelPin, NEO_GRB + NEO_KHZ800);


const byte p[14][7]={ //글자 패턴이 14개임
    {0x70,0x88,0x80,0x70,0x08,0x88,0x70}, //S
    {0xF8,0x20,0x20,0x20,0x20,0x20,0x20}, //T
    {0xF8,0x80,0x80,0xF0,0x80,0x80,0xF8}, //E
    {0xF8,0x80,0x80,0xF0,0x80,0x80,0xF8}, //E
    {0x88,0xD8,0xA8,0x88,0x88,0x88,0x88}, //M
    {0x70,0x88,0x80,0x80,0x80,0x88,0x70}, //C
    {0x70,0x88,0x88,0x88,0x88,0x88,0x70}, //0
    {0xF0,0x88,0x88,0x88,0x88,0x88,0xF0}, //D
    {0x70,0x20,0x20,0x20,0x20,0x20,0x70}, //I
    {0x88,0x88,0xC8,0xA8,0x98,0x88,0x88}, //N
    {0x70,0x88,0x80,0xB8,0x88,0x88,0x70}, //G
    {0x88,0xD8,0xA8,0x88,0x88,0x88,0x88}, //M
    {0x70,0x88,0x88,0xF8,0x88,0x88,0x88}, //A
    {0x88,0xD8,0xA8,0x88,0x88,0x88,0x88} //M
    };

void setup() {
  neopixel.begin();  
}

void loop() {
  byte x=0;
  byte y=0;
  for(int k=0;k<14;k++){ //글자 패턴의 숫자만큼 반복
    for(int i=0;i<7;i++){
        for(int j=7;j>2;j--){
            if(bitRead(p[k][i],j)!=0) neopixel.setPixelColor(y+x, 0, 255, 0);    
            x+=1;
        }
        x=0;//열 초기화
        y+=5;//열이 5칸이니 행이 증가할때마다 5가 더해져야함
    }
    y=0; //행 초기화
    neopixel.show();
    delay(100);   
    neopixel.clear(); 
    neopixel.show();
    delay(100);
  }
}

이렇게 완성이 되었습니다. 코드를 이해 못하셔도 좋습니다. 제가 처음에 어떻게 시작을 했고 어떤 과정을 통해서 여기까지 왔는지의 과정만 이해하시면 됩니다. 어떤 코딩을 할 때 처음부터 완벽한 코딩은 없습니다. 하나씩 차근 차근 원리를 코딩하고 그것을 끊임없이 다른 방법은 없는지 찾으면서 코딩을 하는게 중요합니다.

그 의미를 잘 이해해 주셨으면 합니다.

4. 결과


원래는 'STEEM CODINGMAN'이라는 14자를 출력해야 하는데 동영상 녹화를 논스톱으로 하다보니깐 'O'자를 실수로 빼먹었네요. 13자만 출력되었네요. 어쩐지 글자가 다 출력되고 다시 loop()함수가가 시작하기 전에 딜레이 시간이 생겨서 가상시뮬레이터 상에서 발생하는 렉인 줄 알았는데 13자 출력하고 반복 패턴글자 1글자 분량의 동작을 더 수행하다 보니 결과는 없고 1글자 분량의 동작은 수행만큼의 딜레이 현상처럼 보였네요. 수정할려다가 그러면 영상을 편집해야 하기 때문에 그냥 패턴글자를 'STEEM CDINGMAN'로 출력하는걸로 끝내겠습니다.

참고로, 공개된 회로도 링크 주소로 가셔서 결과를 실제 돌려보시면 정상적으로 'STEEM CODINGMAN'이란 글자가 출력되도록 수정했으니깐 참고하세요.


만드는 과정이 좀 길었네요. 다 자르고 뒤에 결과만 편집할려다가 그냥 올렸습니다. 젤 마지막의 글자 패턴 결과만 보시거나 아니면 실제로 공개된 회로도 링크 주소로 가셔서 직접 돌려보셔도 됩니다.

마무리


NeoPixel이 재밌다고 했는데 갑자기 코딩수업이 되어서 어렵게 느껴졌을 듯 싶네요. 간단한 단순 패턴을 만들어서 화려하게 꾸미고 싶었는데 글자가 너무 땡겨서 글자 출력으로 하고 말았네요. 사실 특정한 문자로 출력하는 것은 좀 코딩이 복잡하고 불편합니다. 단순하게 홀짝 출력이나 릴레이 색상출력이나 큰틀에서의 한두가지 패턴을 색상과 덧붙여서 출력하는게 더 재미 있고 화려한데 말이죠.
이부분은 이 포스팅을 읽으시는 분들의 몫으로 남겨 두겠습니다. 혹시 땡겨서 밤에 만들어 내일 올릴 수 도 있겠지만 오늘밤 땡기냐 안땡기냐로 기분에 맡겨야 겠습니다.
갑자기 코딩이 좀 어렵게 됐는데 이것 말고 간단히 색상 별 한두가지 패턴으로 지그재그로 꾸며 보세요. 그리고 위치를 달리해서 어떤 독특한 모양을 만들어 보세요.
아니면 이 출력하는 원리를 잘 생각해보셔서 어느 부품하고 연결하면 좋을지 상상의 나래를 한번 펼쳐보세요.


댓글()

[아두이노] NeoPixel 제어

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

[아두이노] NeoPixel 제어 


오늘의 주제는 NeoPixel로 재밌는 LED 제어에 대해서 살펴보도록 하겠습니다. NeoPoxel를 제어하기 위해서 Adafruit_NeoPixel 라이브러리를 이용하는데 별로 어렵지 않게 LED 색을 만들어 내기 때문에 재미 있는 시간이 될거라 생각됩니다. 사용되는 함수는 몇개 안되지만 그래도 상상력을 얼마나 발휘하느냐에 따라서 멋진 작품들을 만들 수 있는 부품임으로 관심을 많이 가져주세요.

1. NeoPixel


NeoPixel은 여러 종류가 있습니다. 긴 띠, 한개짜리, 링형 막대형 등 다양한 부품들이 있습니다. 가상시뮬레이터에서는 아래 그림처럼 한개짜리와 원형과 여러종류의 링을 제공하고 있네요. 다루는 것은 다 동일하니깐 간단히 다뤄보도록 하겠습니다.


NeoPixel의 핀 구조는 Vcc, Gnd 핀이 각각 두개씩 있으며 Vin과 Vout 핀으로 나눠져 있습니다. Vcc, Gnd은 NeoPixel의 전원을 담당하겠죠. Vin은 해당 NeoPixel의 들어오는 입력값이고 Vout은 해당 NeoPixel에 들어왔던 입력값 중 일부를 다음 NeoPixel로 보내는 출력핀이 됩니다. 아래 그림의 Orange 선을 보시면 됩니다. Orange 선이 신호값이 들어오고 다음 NeoPixel로 보내는 흐름선으로 생각하시면 됩니다.


예를들어, 순차적으로 1초 단위로 1,2,3 NeoPixel이 켜진다고 가정해 봅시다. 신호 값은 1,2,3번에 불이 켜지라고 명령을 내리게 됩니다. 이때 데이터값이 Orange선으로 따라서 해당 위치에 불이 들어오게 합니다. 고로, Orange선은 명령값을 전달하는 통로로 생각하시면 됩니다.


다시 종합하면, 3번까지 NeoPixe에 불이 들어오라고 명령을 내리면 그 신호 값이 1번에 불이 들어오고 2번의 Neopixel에 신호를 보내고 2번에 불이들어오고 다시 Orange선을 통해서 3번 NeoPixel에 신호값이 전달되어 3번에 불이 들어오게 됩니다. NeoPixel간의 연결은 Vout과 Vin을 연결한 통로로 신호가 전달되니깐 이 점만 잘 기억해 주세요.

2. 회로도 구성


  • 준비물 : NeoPixel Ring 12 1개, NeoPixel 6개, 아두이노우노
  • 내용 : 사용 할 NeoPixel를 간단히 연결해 보자.

대충 두종류의 NeoPixel를 Vcc, Gnd 명칭을 잘 보고 연결하시면 됩니다. 그리고 Vin, Vout은 3번핀에 연결했네요. 제 블로그에 올렸던 회로도에서 약간만 수정했네요.


주의할것은 1개짜리 NeoPixel을 연결할때 두번째 라인은 회전시켜서 배치한 거라 Vout, Vin을 잘 체크하시고 연결하셔야 합니다. 그냥 드래그해서 배치한 형태에서 두번째 라인을 Vout에서 Vout로 연결하는 실수를 하시면 안됩니다.

3. 라이브러리 추가


참조 : [아두이노] LCD16x2 I2C(LiquidCrystal_I2C) 제어
라이브러리 출처 : https://github.com/adafruit/Adafruit_NeoPixel


NeoPixel 라이브러리는 여러 종류가 있습니다. 실제로 실험하실 분은 위 링크된 참조 LCD16x2 I2C 포스트 내용중에 라이브러리 추가하는 방법이 잘 나와 있으니깐 보시고 라이브러리를 추가하시면 됩니다.

그런데 가상시뮬레이터로 하시는 분들은

#include <Adafruit_NeoPixel.h>

이 문구만 있으면 됩니다.

라이브러리 올려주신 분의 github 주소입니다. 거기 가셔서 Adafruit_NeoPixel.h, Adafruit_NeoPixel.h.cpp 파일을 꼭 보시기 바랍니다.

거기 보시면 아래와 같은 코딩들이 있습니다. 타입 설정하는데 어느정도 보셔야 합니다. 실제로 구현 하실때 왜 불이 안들어오지 의도치 않게 결과가 나오는 이유가 타입 설정 때문에 그렇습니다. 하나로 고정되어 있는게 아니라 부품에 따라서 그 타입에 맞게 설정해야지 정상적으로 동작합니다.

 Constructor: number of LEDs, pin number, LED type
 
 LED type :
 NEO_KHZ800  800 KHz datastream
 EO_KHZ400  400 KHz datastream
 
 NEO_RGB  ((0 << 6) | (0 << 4) | (1 << 2) | (2))
 NEO_RBG  ((0 << 6) | (0 << 4) | (2 << 2) | (1))
 NEO_GRB  ((1 << 6) | (1 << 4) | (0 << 2) | (2))
 NEO_GBR  ((2 << 6) | (2 << 4) | (0 << 2) | (1))
 NEO_BRG  ((1 << 6) | (1 << 4) | (2 << 2) | (0))
 NEO_BGR  ((2 << 6) | (2 << 4) | (1 << 2) | (0))
 NEO_RGBW
 ...
 NEO_WRGB 
 ...
 

다른 함수들도 꼭 보시고 대충 어떤 느낌으로 흘러가는지만 살펴보세요. 나중에 색을 세팅하는 함수에서 위에 나와있는 RGB라는 글자들이 보이시죠. 위치를 나타냅니다. 색을 세팅하는 함수는 같지만 해당 위치의 인자값은 달라지게 됩니다. RGB 인자를 순서대로 넣었는데 처음 세팅을 BGR로 해버렸다면 코딩하는 사람은 RGB라고 생각하고 색 값을 넣었지만 색 세팅함수에서는 BGR로 인식해버릴 수 있으니깐 잘 살펴보셔야 해요. 그리고 함수명들만 헤더 파일에 어떤게 있는지만 한번 살펴봐 주세요.

4. 코딩


  • 사용함수 : Adafruit_NeoPixel(), neopixel.begin(), neopixel.setPixelColor(), neopixel.show(), neopixel.clear()
  • 내용 : 간단히 순차적으로 NeoPixel에 불이 들어오게 하자.
  • 참고소스 : [아두이노] 3x3x3 CUBE LED 제어 III

함수

  • Adafruit_NeoPixel(NeoPixel수, NeoPixelPin, NEO_GRB + NEO_KHZ800) : 생성자함수로 NeoPixel 클래스 객체를 만들때 사용합니다. 몇개의 NeoPixel를 사용하고 어느 핀을 사용할지와 Neopixel 타입을 설정을 하는 함수입니다. 우선 가상시뮬에이터는 이 값을 기본으로 사용하세요.
  • neopixel.begin() : 라이브러릴 사용하면 꼭 객체 시작을 알리는 함수가 있죠. 이것도 마찬가지입니다. 사용하겠다고 선언.
  • neopixel.setPixelColor(위치, R, G, B) : 색을 나타냅니다. 위와 같은 Neopixel 타입을 설정했을때 RGB의 위치는 이와 같이 결정됩니다. 참고로 타입이 바뀌면 색상 위치도 바뀌니깐 햇갈리지 마세요. 쉽게말해서, 색을 세팅하는 함수입니다.
  • neopixel.show() : 색이 세팅한 값을 실제로 보여주라는 함수입니다. Orange선을 통해서 해당 위치에 색이 켜지겠지요.
  • neopixel.clear() : 지금까지 켜진 NeoPixel를 초기화하는 함수입니다. 켜진 NeoPixel를 다 꺼지겠지요.
    참고로 clear()함수로 명령을 내렷다고 해서 NeoPixel에 들어온 불이 꺼지지는 않습니다. 내부적으로 세팅한 setPixelColor()함수의 값만 초기화 될 뿐이죠. 내부적으로 초기화 된 값에서 show()함수를 실행 시켜야 초기화된 상태로 NeoPixel이 꺼지게 됩니다. ( "왜! 안꺼져! 이런 실수를 안하셨으면 해요!")

설계

NeoPixel 라이브러리를 사용하니깐 사용할 객체를 선언해야 합니다. 한개짜리 6개와 12개짜리 링 한개로 총 18개의 NeoPixel를 제어하니깐 아래와 같이 선언하시면 되겠습니다.

const byte neopixelPin = 3;
Adafruit_NeoPixel neopixel = Adafruit_NeoPixel(18, neopixelPin, NEO_GRB + NEO_KHZ800);

순차적으로 NeoPixel로 불이 들어와야 하니깐 18개NeoPixel 기준으로 for문 한개를 사용하여 18번 반복하면 되겠죠.

for(int i=0;i<18;i++){
   NeoPixel i에 불이 들어오게하라;
}

이 로직으로 대충 돌아가야 겠죠. NeoPixel 함수는 불이 들어올려면 색을 세팅하고 그 세팅값을 NeoPixel로 보내야 합니다.

setPixelColor()함수로 색을 정하고, show()함수로 출력하면 됩니다.

  for(int i=0;i<24;i++){
    neopixel.setPixelColor(i, 255, 0, 0);    
    neopixel.show();
    delay(100);    
  } 

대충 이렇게 코딩 됩니다. i번째 위치 NeoPixel에 RGB에서 R(255)로 Red색으로 세팅하고 show()함수로 해당 위치에 NeoPixel에 불이 들어오게 됩니다. 그렇게 어렵지 않죠. NeoPixel 라이브러리로 간단히 해당 위치에 원하는 색으로 불이 들어오게 하는 함수가 이 두 함수입니다. 어떻게 로직을 짜냐에 따라서 재밌는 패턴이 만들어 지고 화려하게 불이 들어오게 할 수 도도 있게 됩니다.

딜레이는 빠르게 보기 위해서 0.1초로 했네요. 가상시뮬레이터에는 지연렉이 좀 있기 때문에 좀 빠르게 보기 위한 값이니깐 실험할 때는 원하는 시간 간격으로 돌려보세요.

그리고, 시작은 begin()함수를 사용하고 초기화는 clear()함수를 사용합니다. 꺼먹지 마세요. 그리고 clear()함수 명령을 내렸는데 왜 NeoPixel이 안꺼지지 하고 혼동하시는 분이 있는데요. 무조건 Neopixel에 명령을 보내는 것은 show()함수 입니다. 내부적으로 clear()함수로 초기화 되었어도 출력된 NeoPixel은 초기화 되지 않습니다. show()함수로 같이 쓰시면 외부의 NeoPixel까지 초기화 된다는 점을 꼭 기억해주세요.

전체적으로 코딩을 하면,

#include <Adafruit_NeoPixel.h>

const byte neopixelPin = 3; 
Adafruit_NeoPixel neopixel = Adafruit_NeoPixel(18, neopixelPin, NEO_GRB + NEO_KHZ800);

void setup() {
  neopixel.begin();  
}

void loop() {  
  for(int i=0;i<18;i++){
    neopixel.setPixelColor(i, 255, 0, 0);    
    neopixel.show();
    delay(100);    
  } 
  neopixel.clear(); 
  neopixel.show();
  delay(100);    
}

5. 결과




오늘은 회로도 만드는 것이 간단하니깐 전과정을 기록해 봤네요. 그리고 결과는 Red 색 하나만 순차적으로 불이 들어오게 하면 좀 그러니깐 R->G->B순으로 불이 들어오게 가상시뮬레이터에서 돌려봤네요. 어떤 결과가 나오는지 동영상을 꼭 확인 해보세요. 아니면 공개 회로도에 링크된 곳에 가셔서 가상시뮬레이터를 직접 돌려보세요.


마무리


진짜 오늘 배운 부품 소재는 재맸는 소재입니다. 아마도 한번은 보셨을지 모르겠네요. 클럽에 네온싸인 같은 것이나 할로윈 축제때 어떤 사람을 보면 몸에서 야광 LED가 깜박이는 것들을 보셨을꺼에요. 유튜브를 검색을 하시면 NeoPixel로 모니터를 만들어서 동영상을 감상하시는 분도 있고 연주가들은 악기에 붙여서 휘황찬란하게 표현하는 분도 있고 찾아보시면 스케일들이 남다른 분들의 영상물들을 많이 보실 수 있을 꺼에요. 아마도 실제로 구매해서 만들고 싶은 충동이 느껴지실 거라고 생각됩니다. 그런데 개당 가격이 좀 비싼편이라서 약간 구매해서 실험하기는 애매한 점도 있습니다. 우리에게는 가싱시뮬레이터가 있고 그걸 통해서 대리 만족을 느끼면 됩니다.

오늘은 간단한 동작으로만 채웠습니다. 그 이유는 여러분들의 상상력을 발휘할 시간을 주기 위해서 입니다. 다양한 표현을 시도해 보세요. 여러분들은 NeoPixel로 난 무엇을 만들어 볼까? 무엇을 만들 수 있을까? 계속 자신에게 질문을 던져주세요. 그리고 순차적으로 출력되는 저 for문을 가지고도 다양한 표현을 할 수 있습니다. setPixelColor()함수와 show()함수의 의미와 동작원리를 곰곰히 잘 생각해보시면 for문 한개로도 꽤 다양한 패턴을 만드실 수 있으니깐 꼭 동작 원리를 이해해 주세요.


댓글()