[아두이노] 스위치버턴+Servo Motor 제어

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

[아두이노] 스위치버턴+Servo Motor 제어



어제 조이스틱으로 Servo Motor를 실험을 했는데 포스트를 본신 분들이 그냥 글을 보는거로만 끝내는 것이 좀 그런 것 같아서 조이스틱의 느낌을 스위치 버턴으로 최대한 살려서 비슷하게 가상시뮬레이터에서 테스트를 해 볼 수 있도록 변경을 해 봤습니다. 원리는 동일하고 스위치 버턴 2개로 조이스틱의 좌/우측의 회전 값으로 표현을 했고 스위치 버턴 1개로 조이스틱의 스위치 버턴으로 표현을 해서 총 3개의 스위치 버턴으로 조이스틱의 느낌을 담아 회로도와 코딩을 수정했네요. 위에 공개회로도를 클릭하시면 바로 가상시뮬레이터를 실행 할 수 있는 창이 나오니깐 가상시뮬레이터로 직접 스위치를 눌러 Servo Motor를 회전 시켜보세요. 대충 어떤 느낌으로 회전이 이루어지는지 체험을 하실 수 있습니다. 이제 본격으로 어떻게 표현 했는지 살펴보도록 하죠.

1. 회로도 구성


  • 준비물 : 스위치 버턴 3개, Red LED 1개, 저항 220옴 1개, Servo Motor 1개, 아두이노우노
  • 내용 : Servo Motor핀은 7번에 연결, 스위치 버턴은 3,4,5 번핀에 연결, Red LED은 12번 핀으로 연결


어제 포스팅한 조이스틱 + Servo Motor 회로에서 조이스틱 부분만 스위치 3개로 대신 표현한 회로도 입니다. 나머지는 동일하고 스위치 버턴 3개만 변경 했네요.

3. 코딩


  • 사용함수 : pinMode(), digitalRead(), delay(), servo.attach(), servo.write(), map()
  • 내용 : 스위치 버턴 2개로 Servo Motor를 회전 시키고 나머지 한개로 Red LED를 제어 깜박이게 해보자.

함수

  • pinMode(사용핀, 입/출력모드) : 해당 사용핀을 어떤 모드로 사용할지 결정한다. INPUT_PULLUP모드로 설정(내부저항을 사용할 예정)
  • digitalRead(디지털핀) : 디지털 신호를 읽음(0 or 1)
  • digitalWrite(출력핀, HIGH/LOW) : 출력핀에 HIGH(5V) or LOW(0)를 출력
  • delay(시간값) : 시간값만큼 대기한다.

Servo Motor 함수

1
#include <Servo.h>
  • Servo servo : 서보모터 객체 선언
  • servo.attach(서보핀) : 서보 모터에 출력을 담당할 핀 선언
  • servo.write(회전각) : 회전각 만큰 서보모터를 회전 시킴

[ 조이스틱 소스 ]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <Servo.h>
 
Servo servo;
 
const int AXIS_X = A0;
const int AXIS_Y = A1;
const int SW_P = 3;
 
const int servoPin = 7;
const int redLed = 12;
 
int m_Angle = 0;
  
void setup() {
  servo.attach(servoPin);
  pinMode(redLed, OUTPUT);
  pinMode(SW_P,INPUT_PULLUP);
}
  
void loop() {
  m_Angle = map(analogRead(AXIS_X),0,1023,0,180);
  digitalWrite(redLed,digitalRead(SW_P));
  servo.write(m_Angle);
  delay(100);
   
}

변경 후, 어떻게 코딩이 되었는지 볼까요.

[스위치 버턴 소스]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include <Servo.h>
 
Servo servo;
 
const int AXIS_X1 = 5;
const int AXIS_X2 = 4;
const int SW_P = 3;
 
const int servoPin = 7;
const int redLed = 12;
 
int m_Angle = 0;
 
void setup()
{
  servo.attach(servoPin);
  pinMode(redLed, OUTPUT);
  pinMode(AXIS_X1, INPUT_PULLUP);
  pinMode(AXIS_X2, INPUT_PULLUP);
  pinMode(SW_P, INPUT_PULLUP);
}
 
void loop()
{   
  if(digitalRead(AXIS_X1) == 0){ 
    if(m_Angle<180){
      m_Angle=m_Angle+10;
    }         
  }
  else if(digitalRead(AXIS_X2) == 0){
    if(m_Angle>0){
      m_Angle=m_Angle-10;
    }     
  }
  else{
    digitalWrite(redLed,digitalRead(SW_P));
  }
  servo.write(m_Angle);
  delay(100);
}

pinMode()함수로 두개의 스위치 버턴의 모드를 풀업저항모드로 설정을 했고 loop()함수 내부의 코드가 좀 바뀌었네요.

조이스틱보다 코딩이 좀 길어졌죠. if~else if 문을 사용했습니다. AXIS_X1과 AXIS_X2 로 두개의 변수를 설정해서 방향 스위치를 만들었습니다. AXIS_X1 방향이 정방향이면 AXIS_X2은 역방향이 됩니다.

이제 회전 시킬려면 스위치를 눌렀을 때 상황을 만들어 줘야 합니다. 정방향 스위치를 눌렀는지 역방향 스위치를 눌렀는지를 IF문으로 표현 하면 됩니다. 참고로 내부풀업저항을 이용 했기 때문에 초기 상태는 '1'의 상태입니다. 여기서 스위치를 누르면 '0'의 상태가 됨으로 스위치가 눌렀을 때 '0'인가 라고 IF문에서 체크하면 됩니다.

다음과 같은 코딩으로 표현이 되겠죠.

1
2
3
4
5
6
if(digitalRead(AXIS_X1) == 0) {
    정방향 10도 회전;
}
else if(digitalRead(AXIS_X2) == 0){ 
    역방향 10도 회전;
}

이제 두 개의 방향스위치로 해당 스위치를 누르면 10도씩 회전하게 만들려면 m_Angle(각) 변수를 하나 만들어 놓고 이 변수값을 10씩 변화를 주면 됩니다.

  • 정방향 => m_Angle=m_Angle+10;
  • 역방향 => m_Angle=m_Angle-10;

간단하게 이 표현으로 스위치를 눌렀을 때 정방향은 +10이 되고 역방향은 -10이 되도록 위 문장을 코딩을 하면 간단하게 회전 시킬 각을 만들 수 있습니다.

그런데 Servo Motor은 0~180도 회전만 할 수 있습니다. 아무리 스위치 버턴을 눌러도 180도 이상 증가하면 안되고 0도이하로 감소해도 안됩니다.

1
2
3
if(m_Angle<180)  m_Angle=m_Angle+10;
 
if(m_Angle>0)  m_Angle=m_Angle-10;

이렇게 if문으로 각에 대해 제한을 두면 아무리 스위치를 눌러도 180이상 0이하로 m_Angle 값은 더이상 변하지 않게 됩니다.

종합해보면,

1
2
3
4
5
6
7
8
9
if(digitalRead(AXIS_X1) == 0){ 
  if(m_Angle<180) m_Angle=m_Angle+10;
}
else if(digitalRead(AXIS_X2) == 0){
  if(m_Angle>0) m_Angle=m_Angle-10;    
}
  else{
  digitalWrite(redLed,digitalRead(SW_P));
}

이렇게 방향 스위치를 누르지 않았다면 else 문을 수행하는데 SW_P핀의 스위치가 상태값에 따른 redLed핀의 출력을 만들어 내면 마무리가 됩니다. 그냥 else에서 SW_P핀의 스위치가 눌러졌는지 체크하고 그냥 넘어가는 표현이라고 생각하시면 될 듯 싶네요.
이 부분을 else if()문으로 눌러졌는지 물어 보는 코딩을 해도 되지만 그냥 스위치 상태값으로 LED를 상태를 결정하기 때문에 묻는 조건식은 생략 했네요.

4. 결과


가상시뮬레이터 결과 영상입니다.


이것도 실제로 표현을 해서 어떻게 돌아가는지 돌려봤네요.


마무리


어제는 조이스틱으로 Servo Motor를 제어 했고 오늘은 스위치 버턴으로 같은 느낌으로 Servo Motor를 제어를 해 보았습니다. 뭔가를 표현학 그게 동작하는 모습을 봐야 어떤 느낌인지 이해가 빠를 꺼에요. 원래 Servo Motor 회전 제어를 예전 포스트에서 리모콘으로 조정했던 포스트였습니다. 아마도 Bluetooth 통신 포스트를 할 때도 LED나 Servo Motor를 가지고 제어를 하지 않을까 싶네요. 가장 시각적으로 표현하기 쉬운 부품이니깐요.

아무튼 어제 배운 조이스틱의 내용이 중요하니깐 꼭 조이스틱의 동작 원리를 이해하셨으면 합니다. 그리고 오늘 다룬 내용은 같은 의미로 스위치 버턴이 대신한 응용편으로 복습이라고 생각하시면 될 듯 싶네요.

여러분들도 다른 것을 이용하여 비슷한 동작을 수행하시고 싶으시다면 어떤 부품으로 비슷한 느낌을 표현할 수 있을지 상상의 나래를 펼쳐 보세요.


댓글()

[아두이노] 조이스틱 + Servo Motor 제어

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

[아두이노] 조이스틱 + Servo Motor 제어



조이스틱으로 뭔가를 조정하는 응용 예제로 소개할 만한게 없나 고민하다가 지금까지 포스트 한 것 중에서 찾다가 조정 느낌이 느낄 수 있는 LED로 실험 했다가 그냥 포스트에 올리는 걸 포기했네요. 다른 부품으로 찾다가 Servo Motor를 제어를 해보는게 재밌 소재가 될 것 같고 따로 코딩하는 부분도 별로 없을 것 같아 실험 주제로 결정 했네요. 코딩도 아주 초 간단하게 원리만 표현 했고 지난 시간에 조이스틱 조정기 값을 읽는 것을 배웠으니깐 이제는 그 값을 기준으로 Servo Motor의 Angle을 정하면 되니깐 별로 어렵지 않고 재밌는 포스팅이 될 것 같네요.

자! 그러면 Servo Motor를 조이스틱으로 제어를 해 봅시다.

1. 회로도 구성


  • 준비물 : 조이스틱 1개, Servo Motor 1개, Red LED 1개, 저항 220옴 1개, 아두이노우노
  • 내용 : A0, A1 핀은 x,y 방향 아날로그 신호를 받고 5번핀은 스위치 신호를 받도록 선은 연결하고 7번핀에 Servo Motor 출력핀으로 연결하고 12번핀은 Red LED의 출력으로 사용한다.

[ Fritzing 디자인 ]


회로도를 보시면 좀 복잡해 보일 수 있는데, 조이스틱에서 방향 신호(VRX, VRY)와 스위치 신호(SW) 선만 아날로그 핀과 디지털 핀에 연결 하고, Servo Motor핀과 LED 핀들은 사용하고자 하는 디지털 핀을 선택해서 연결하시면 됩니다. 나머지는 다 전원에 관련된 핀이라 회로도 그림만 좀 복잡해 보이고 선 연결은 어렵지 않습니다.

2. 코딩



함수

  • pinMode(사용핀, 입/출력모드) : 해당 사용핀을 어떤 모드로 사용할지 결정한다. INPUT_PULLUP모드로 설정(내부저항을 사용할 예정)
  • analogRead(아날로그핀) : 아날로그 신호를 읽음(0~1023)
  • digitalRead(디지털핀) : 디지털 신호를 읽음(0 or 1)
  • digitalWrite(출력핀, HIGH/LOW) : 출력핀에 HIGH(5V) or LOW(0)를 출력
  • delay(시간값) : 시간값만큼 대기한다.
  • map(입력값,입력최소,입력최대,출력최소,출력최대) : 입력값이 입력 최소~최대범위가 출력 최소~최대에 매칭되어 출력

Servo Motor 함수

1
#include <Servo.h>
  • Servo servo : 서보모터 객체 선언
  • servo.attach(서보핀) : 서보 모터에 출력을 담당할 핀 선언
  • servo.write(회전각) : 회전각 만큼 서보모터를 회전 시킴

설계

조이스틱의 어떤 값으로 Servo Motor를 제어 해야 할까요. X 방향, Y 방향, 스위치 버턴 이 세가지 신호 중에 어떤 것을 사용할까요. 여기서, X, Y 값 중에 하나를 선택하면 됩니다. Servo Motor 한개가 하나의 축 방향이라고 생각하면서 접근하시면 됩니다.

간단히, 저는 X 방향 아날로그 신호로 Servo Motor를 제어 해볼까 합니다.

그러면 조정값을 어떻게 Servo Motor랑 매칭해야 할까요. 한번 생각을 해보세요.

조이스틱이 처음 어떤 상태로 어떤 값을 초기값으로 되어 있나요. 조이스틱의 현재 상태와 현재 신호를 곰곰히 생각 해보시면 그 안에 답이 있습니다. 바로, 조이스틱은 방향 아날로그 신호값이 중앙값으로 처음 시작 합니다. 즉, 0~1023의 아날로그 신호에서 512이라는 중심값이 초기 상태로 아날로그 신호로 시작 합니다.

이때 왼쪽은 0~512 사이가 되고 오른쪽은 512~1023 사이의 값으로 좌우 방향을 나타낼 수 있습니다. 이 원리를 이용하시면 쉽게 해결 됩니다. 512를 기점으로 좌우 신호 값으로 Angle를 표현하면 되니깐요. 여기서 0~1023의 아날로그 신호값에서 0도가 0의 값이 되고 180도는 1023이 되게 하면 중앙값 512은 90도가 되고 이 개념을 가지고 코딩을 하면 됩니다.

그렇게 하면, 처음 시작은 중앙값 512로 Servo Motor은 90도에서 시작하게 되고 아날로그 신호가 0에 가까울 수록 0도에 가까워지고 아날로그 신호가 1023에 가까울 수록 180도에 가까워지겠죠.

여기서, 문제점은 0을 0도로 1023을 180도로 어떻게 표현 할까요. map()함수를 이용하면 됩니다. 은근히 자주 사용하는 함수인데 여기에서도 사용 하네요.

1
m_Angle = map(analogRead(AXIS_X),0,1023,0,180);

AXIS_X핀의 아날로그 신호값이 0~1023 범위에서 출력 0~180범위에 어느 정도의 위치가 되는지 알아서 해당 값을 찾아주는 함수입니다. 만약, 512값이 입력신호로 들어오면 출력 0~180 범위에서 90이라는 값의 위치하니깐 90이 반환되어 나옵니다. 즉, 입력신호값을 0~180사이의 값으로 자동으로 변환시켜주는 함수인 셈이죠.

이 한줄로 조이스틱의 X방향의 아날로그 신호를 각도로 만들어 낼 수 있겠죠.

1
2
servo.write(m_Angle);
delay(100);

아날로그 신호 512일 때, Servo.write(90)함수로 90도를 Servo Motor가 회전하게 됩니다. delay()함수는 Servo Motor가 충분이 회전할 시간값을 줘야 하기때문에 같이 코딩 해야 합니다. 100은 0.1초인데 더 짧게 하시면 좋겠죠. 그냥 이전 포스트 소스를 가져다가 표현한거라 딜레이 시간은 그냥 뒀네요. 원래 저 값은 특정한 각도로 회전 한뒤에 다시 다른 특정한 각도로 회전할 때까지의 충분한 시간 값인데 조이스틱 조정기에서는 이렇게 큰 시간값은 필요 없습니다. 각도 값 범위가 변화율은 크지 않기 때문에 짧게 시간을 주면 됩니다. 100은 아주 큰 값이지만 조정기로 해본 결과 그냥 사용해도 제 실험에서는 상관 없어서 그냥 뒀네요.

결론은 조이스틱의 X방향 신호값을 읽어와서 map()함수로 각도를 만들어 내고 servo.write()로 각도를 출력하는 코딩으로 딱 두줄이면 조이스틱으로 Servo Motor를 움직이게 할 수 있게 됩니다.

그러면, 전체 소스를 살펴봅시다.

[ 소스 ]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <Servo.h>
 
Servo servo;
 
const int AXIS_X = A0;
const int AXIS_Y = A1;
const int SW_P = 3;
 
const int servoPin = 7;
const int redLed = 12;
 
int m_Angle = 0;
  
void setup() {
  servo.attach(servoPin);
  pinMode(redLed, OUTPUT);
  pinMode(SW_P,INPUT_PULLUP);
}
  
void loop() {
  m_Angle = map(analogRead(AXIS_X),0,1023,0,180);
  digitalWrite(redLed,digitalRead(SW_P));
  servo.write(m_Angle);
  delay(100);  
}

변수가 코딩의 절반을 차지 하네요. 정작 로직 코딩은 몇 줄 안되는 데 말이죠.

1
digitalWrite(redLed,digitalRead(SW_P));

서비스 코딩으로 스위치 버턴을 누르면 12번에 연결된 Red LED가 꺼지도록 했네요. 조이스틱 스위치 버턴은 내부풀업저항을 이용하기 때문에 초기값은 '1'이 됩니다. 그래서 전원이 공급되면 Red LED에 불이 들어오게 됩니다. 그리고 스위치를 누르면 '0'의 상태가 되어서 Red LED는 불이 꺼지게 됩니다.

4. 결과




5. 추가 내용


위 실험 코딩은 그냥 조정기의 0~1023값을 단적으로 0~180도로 나눠서 조정한 거라서 실제로 뭔가 조정하거나 정밀 제어 할 때는 사용하지 않는 코딩입니다. 단순히 조정기를 움직일 때 그 움직임 값으로 Servo Motor를 움직이게 한 것 뿐이니깐요.

원래는 조정기에 대한 코딩을 제대로 하실려면은 조정기를 움직일 때에 채터링 문제 또는 조정값의 범위라던가 조정기의 수치에 따른 회전 속도라든가 고려해야할 것이 산더미로 많습니다. 거기에 조종기의 중심값의 보정 작업도 추가 해야 합니다.

이런것들을 다 코딩하면 회전 하나의 동작을 하기 위해서 수십 줄의 코딩으로 늘어나게 됩니다. 배보다 배꼽이 더 커지겠죠. 여기서 배워야할 것은 Servo Motor를 조이스틱으로 회전할 수 있는 기초 원리를 배우는게 목적이기 때문에 간단하게 원리만 전달하는 코딩만 한 것이니깐 여러분들이 실제로 뭔가를 제어할려면 많은 부분을 생각하고 코딩 하셔야 합니다.

그리고, 여러분들이 이 기초를 이해 하셨다면 조정기에 필요한 부분이 뭐가 있을지 생각하고 여러가지 상황들을 제시하고 그 문제를 해결하면서 코딩을 늘려가셨으면 합니다.

마무리


조이스틱으로 Servo Motor를 제어할 수 있게 되었네요. 한개의 Servo Motor를 제어 했지만 여기서 Y방향 값도 같이 적용을 하여 Motor 두개를 사용한다면 어떻게 될까요. x와 y방향으로 평면의 좌표로 이동할 수 있게 됩니다. 3D 프린터나 평면 그림을 그리는 프린트기 같은 걸 만들 수 있겠죠. 여기에 사용되는 Motor는 Stepper Motor로 360도를 회전하는 것을 사용합니다. 어찌 되었든 조정기로 뭔가를 움직이는 물체를 제작 가능해 지겠죠.

마지막으로, 뭔가 로봇 같은 관절 제어 쪽으로 상상력을 발휘해 보세요. 재밌는 상상력들을 많이 해 보셨으면 합니다.

예를들어, 로봇 손가락을 상상해 보세요. 자신의 손을 쫙 펴보세요. 손가락 하나에는 3마디로 구성 되어 있잖아요. 이 각 마디와 마디 사이에는 관절이 있습니다. 이 3개의 관절을 Servo Motor가 대신 한다고 상상을 해보세요. 손을 다시 주먹을 쥐어보세요. 한마디가 구부러질 때 몇도의 각도가 되나요. 각 관절이 90도에 가깝게 꺽기게 됩니다. 그러면 Servo모터로 3개의 관절을 동일하게 angle을 0도에서 서서히 90도에 가깝게 일정한 속도로 회전 시키면 어떻게 될까요. 주먹을 쥐는 형태로 자연스럽게 구브러 지겠죠. 다시 펼 때는 90도에서 서서히 0도로 3개의 Servo모터를 회전 시키면 어떻게 될까요 서서히 손가락을 펴는 모습이 되겠죠.
직접 자신의 손을 주먹을 쥐었다 폈다를 반복하면서 머리속에서 손가락 관절의 각도를 그려 보세요. 그 원리를 깨우치게 되면 로봇 손가락 관절 제어를 할 수 있게 됩니다.

일상의 모습을 보고 우리는 그걸 코딩화 할 수 있습니다. 일상의 모습을 코딩화 하기 위해서는 끊임없이 상상력을 끌어 올려야 하고요. 계속 자신에게 어떤 것을 배우고 다룰 수 있게 되면 다음에 뭘 배우지 보다는 이것을 이용하여 뭘 할 수 있지 하고 2개 이상의 응용할 수 있는 상상력 훈련을 하셨으면 합니다.


댓글()

[아두이노] 조이스틱 제어

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

[아두이노] 조이스틱 제어



최근 포스팅 내용이 너무 코딩에 치우친 것 같아서 오늘은 가벼운 마음으로 간단히 실험 할 수 있는 주제로 꾸몄습니다. 가상시뮬레이터에서 제공하지 않지만 설명만으로 대충 이해 할 수 있는 부품이여서 부담 없이 읽으시면 됩니다. 오늘 다룰 주제는 조이스틱으로 꽤 재밌는 모듈입니다. 방향을 조정 할 수 있는 모듈로서 RC카와 같은 뭔가 조정이 필요한 곳에 사용되는 모듈입니다. 아두이노마이크로나 레오라르도보드가 있다면 컴퓨터 마우스를 만들고 싶은 부품입니다. 아무튼 조이스틱의 구조는 복잡하지 않습니다. 조이스틱 모듈에서 만들어내는 신호는 방향을 표현하는 아날로그 신호와 스위치를 표현하는 디지털 신호가 있습니다. 이 둘 신호에 대해 읽기를 할 수 있다면 쉽게 제어가 가능한 부품입니다. 간단한 실험으로 어떻게 동작하는지 살펴보도록 하겠습니다.

1. 조이스틱의 구조

위 사진에서 자세히 보면 Gnd, +5V, VRX, VRY, SW 이렇게 5개의 핀으로 구성되 있습니다. Gnd, +5V은 전원부분에 해당되고 나머지 3개의 핀에서 VRX, VRY은 아날로그 출력핀으로 방향좌표(x,y) 아날로그 신호를 만들어 내고 SW은 디지털 출력핀으로 스위치 버턴 역할로 디지털 신호를 만들어 냅니다. 참고로 위 사진에서 스위치는 방향 조정기의 몸통을 누르거나 옆에 스위치 버턴이 있습니다.

방향(x,y)과 스위치 한개를 누를 수 있는 모듈이구나 정도로 이해하시면 됩니다.

그러면 조이스틱의 방향키를 움직였을 때 아날로그 신호는 어떻게 변하는지 살펴보도록 할까요.

X, Y 좌표를 나눠서 생각하시면 됩니다. 위 그림처럼 가운데 동그라미가 조이스틱 중심 지점이라고 생각 하십시오, 그리고 조이스틱이 그 중심에서 오른쪽, 왼쪽, 위, 아래 그리고 각 대각선으로 나눠서 구분하시면 됩니다.

그러면 간단히 살펴볼까요.

아날로그 신호는 X축으로 0~1023의 값을 읽을 수 있습니다. 그 중심값 512가 됩니다.
아날로그 신호는 Y축으로 0~1023의 값을 읽을 수 있습니다. 그 중심값 512가 됩니다.

그러면 방향조정기를 조절하고 그 신호값을 만들어 낼려할 때 여러분들이 조이스틱 설계자라면 아날로그 신호에서 어디를 기준으로 설계 할 까요. 바로 중심값을 기준으로 잡겠죠. 아날로그 신호가 0~1023값이 고정되어 있으니 X축을 기준으로 좌우의 방향을 나눌려면 어느 위치를 중심으로 해야 할까요. 바로 신호값 512가 되겠죠. 아날로그 신호 512 위치가 중심이 되면 좌측은 X신호값이 0~512사이의 값이 되고 우측은 512~1023사이의 값이 되겠죠. 이 값을 기준으로 좌측과 우측의 이동 신호값으로 표현하면 됩니다. Y축도 이와 같습니다.

처음에 핀을 아두이노에 연결하면 읽은 신호값이 초보분들은 정확히 뭘 의미하는지 이해를 잘 못하지만 위 그림처럼 그림을 그리고 나서 가운데 원이 방향조정기로 움직인다고 상상하면서 좌표를 이해하시면 됩니다.

방향 좌표 수치를 이해가 되셨다면, 중심(X,Y) = (512,512)을 기준으로 신호값이 실제 조정기를 해당 방향으로 움직이면 어떻게 변하는지 이해 하실 수 있을 거라 생각됩니다.

주의사항

현실에서는 중심점(512,512) 값을 가지지 못합니다. 그 이유는 조이스틱이 기계이고 조립과정 또는 환경 요인에 의해서 중심값은 (512,512)가 나오지 않습니다. 같은 모듈이여도 제각각으로 중심값 신호가 나옵니다. 실제 측정한 조이스틱 중심값을 다시 선을 뽑았다 다시 연결하니깐 약간의 수치가 살짜 1정도 다른 값이 나오기도 하더군요. 그래서 초기 기준값을 정하는 보정 작업이 필요 합니다. 오늘은 코딩적인 부분을 다루는 것이 목적이 아니고 간단히 조이스틱이 어떻게 생겼으며 어떻게 값을 읽을지를 알아보는 실험이니깐 코딩에 대해서 깊게 안들어가고 간단히 이해만 해주세요.

만약, 보정작업을 수행하고 싶으시다면 중심값 관련 보정 작업은 중심값의 환경적 요인에 의해 차이가 나니깐 일정 값의 범위를 지정해서 그 값 안에 있으면 중심으로 간주하는 방법으로 보정해 주는게 가장 간단한 보정 할 수 있는 방법이라고 생각 됩니다.

인터넷에서 조이스틱 실험 예제들을 살펴보시면 정확한 수치로 쪼개서 나누지 않고 방향의 범위값을 허용하는 일정 범위를 지정해 놓고 그 범위에 들어오면 방향을 움직이는 걸로 판정을 내리더군요. 그게 가장 간단하면서 문제를 쉽게 해결할 수 있는 방법이라고 저도 생각 되네요.

아무튼 실제로 테스트 한 결과값은 아래 실험에서 자세히 나와 있으니깐 제가 쓰는 조이스틱의 중심값이 어떻게 나왔는지 아래 포스팅 내용을 보시기 바랍니다.

2. 회로도


  • 준비물 : 조이스틱 1개, 아두이노우노
  • 내용 : A0, A1 핀은 x,y 방향 아날로그 신호를 받고 5번핀은 스위치 신호를 받도록 선은 연결 하시오.


[ Fritzing 디자인 ]

조이스틱은 아날로그 핀 2개랑 디지털 핀 1개를 아두이노에서 여러분들이 원하는 핀을 선택하여 연결하시면 됩니다. 따로 복잡한 부분은 없습니다.. 실제로 조이스틱을 구매 안하셔도 이런 부품이 있고 대충 연결은 어떤식으로 이루어지는지만 위 그림을 보시면 됩니다. 조이스틱 모듈에 핀 이름이 다 적혀 있으니깐 위 그림을 안보더라도 쉽게 선을 연결 할 수 있을 거라 생각됩니다.

3. 코딩


  • 사용함수 : pinMode(), analogRead(), digitalRead(), delay(), Serial.begin(), Serial.println()
  • 내용 : 시리얼모니터로 조이스틱 값을 출력하자.

함수

  • pinMode(사용핀, 입/출력모드) : 해당 사용핀을 어떤 모드로 사용할지 결정한다. INPUT_PULLUP모드로 설정(내부저항을 사용할 예정)
  • analogRead(아날로그핀) : 아날로그 신호를 읽음(0~1023)
  • digitalRead(디지털핀) : 디지털 신호를 읽음(0 or 1)
  • delay(시간값) : 시간값만큼 대기한다.

통신

  • Serial.begin(9600) : 시리얼통신 시작
  • Serial.println(값) : 시리얼모니터로 값을 출력함.

[ 소스 ] : 제 블로그에 있던 소스를 가져옴.(수정하기 귀찮아서요)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const int AXIS_X = A0;
const int AXIS_Y = A1;
const int SW_P = 2;
  
void setup() {
  Serial.begin(9600);
  pinMode(SW_P,INPUT_PULLUP);
}
  
void loop() {
 
  Serial.print("Axis_X: ");
  Serial.println(analogRead(AXIS_X));
 
  Serial.print("Axis_Y: ");
  Serial.println(analogRead(AXIS_Y));
 
  Serial.print("SW_P:  ");
  Serial.println(digitalRead(SW_P));
 
  delay(500);
}

방향 x, y 값은 아날로그 신호값 => analogRead(AXIS_X), analogRead(AXIS_Y)
스위치 값은 디지털 신호 값 =>digitalRead(SW_P)

참고로 내부풀업저항을 사용하기 때문에 스위치버턴의 초기 값은 1이 됩니다. 스위치를 누르면 디지털 신호는 0이 됩니다.

내부풀업저항이 기억이 안나면 다시 복습해야 겠죠. =>[아두이노] 스위치 버턴 제어

4. 결과


위 사진을 보면 조이스틱의 오차값이 발생합니다. 정확하게 중앙값이 생성되지 않습니다. 조이스틱을 만지지 않을 때 정중앙에 위치하는데 그 값이 X=499이고 Y=529가 표시 되네요. 중앙이니깐 X=512, Y=512 이라고 생각하시면 절대 안돼요. 아날로그 값을 읽을 때는 처음 읽을 때 자신이 생각하는 값이 나오지 않습니다. 그래서 초기 측정값을 기준으로 처음 보정 작업이 필요 합니다. 기준값을 우선 찾은 후 그 기준값으로 아두이노에서 어떻게 제어 할지를 정해야 합니다. 그리고 조이스틱은 같은 제품이여도 X, Y값은 약간씩 차이가 납니다. 그렇기 때문에 이 포스트 보고 포스트에 나온 값에 맞춰서 로직을 짜야지 하면 안되고 우선 자기가 사용하는 부품의 초기값이 몇인지를 체크하시고 나서 로직을 짜셔야 합니다.

마무리


조이스틱을 다루는데 어렵지 않죠. 해당 핀에 아날로그 신호랑 디지털 신호만 읽어오면 됩니다. 여기서, 조이스틱을 움직 일 때 그 움직임에 맞춰서 LED에 불이 들어오게 설계를 했는데 동일한 응용 예제를 누가 이미 다른 블로그에 시연을 해버려서 코드로직도 90% 이상 응용코드가 일치해서 실험은 했는데 포스트에 올리지를 못하게 되었네요. 기본 동작 소스는 90%이상 일치하더라도 이건 어쩔 수 없는 부분이지만 응용편이 90% 일치한 내용이 있다면 사실 올리기 좀 껄꺼롭기 때문에 실험은 했고 영상은 있는데 그냥 포기 했네요.

말로만 설명하자면 X축을 기준으로 if문으로 512을 중앙으로 가정하고 512이하면 0~512 사이면 왼쪽 LED에 불이 들어오고 아니면 512이상이면 512~1023 사이면 오른쪽 LED에 불이 들어오게 하고 Y축은 이와 동일하게 IF 조건문을 세우면 방향에 따른 LED에 불이 들어 오겠죠. 그리고 스위치 부분은 스위치가 눌러지면 LED 하나를 스위치 담당 LED로 해서 해당 LED에 불이 들어오게 표시하면 간단히 조이스틱을 조정하여 시각적으로 LED로 표현할 수 있었는데 너무 동일한 응용 예제라서 만들어 놓고 포스트에는 올릴 수 없었네요.

마지막으로, 가상시뮬레이터에서 제공되지 않는 부품 모듈이지만 한번 조이스틱으로 여러분들은 뭘 만들고 싶은지 상상만 해보셨으면 해요. 다른거로 표현 할 만한게 떠오르면 응용 예제로 올려 보도록 할께요. LED 응용 회로로 다 만들었는데 그냥 분해 했네요.


댓글()

[아두이노] Smoothing의 원리 공부

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

[아두이노] Smoothing의 원리 공부



오늘은 아두이노 공식 홈페이지에서 제공되는 튜토리얼 예제 중에서 Smoothing 예제가 있어서 알아 두면 좋은 예제라서 이렇게 포스팅을 하게 되었네요. Smoothing의 원리는 센서의 입력값을 보정해 주는 기능을 가지고 있어 제대로 이해하시면 활용 범위가 넓기 때문에 꼭 원리를 기억해 두셨다가 사용하시기 바랍니다. 이제 본격적으로 포스팅을 시작해 보도록 하겠습니다.

1. Smoothing의 원리


Smoothing은 말 그대로 부드럽게 하는 의미의 작업을 수행합니다 아두이노에서 Smoothing의 작업은 Sensor의 입력값을 부드럽게 받는 작업을 수행 합니다. 입력값을 부드럽게 받는다고 생각하면 의미 전달에 좀 그렇죠. 정확히 말하면 Sensor의 입력의 변화율을 줄인다고 생각하시면 됩니다. 오늘 사용하는 기법은 평균값을 이용했는데 이 평균값을 이용하여 부드럽게 Sensor의 값을 받게 됩니다. 하지만 이 평균값 구하는 로직이 딱 하나의 Smoothing 방법이라고 말 할 수 없으며 여러 방법중에 하나일 뿐입니다. 이거 딱 하나가 Smoothing이라고 생각하시면 안돼요.


평균은 대충 위 그림과 같은 형식으로 평균값을 구할 꺼에요.

1) Smoothing를 하는 이유

아두이노에서 Sensor를 통해 아날로그 신호로 읽을 때의 상황을 생각해 보세요. Sensor 주변 환경적 요인에 의해서 Sensor의 값은 달라 질 수 있습니다. Sensor 주변 환경에서 발생하는 노이즈 같은 이런 요인들이 Sensor 값에 영향을 주기 때문에 읽어들인 센서값의 변화가 일어나고 그로 인해 센서에 의한 동작 제어에도 영향을 미칠 수 밖에 없습니다.

예를 들어 Sensor에 9.9, 10.0, 10.1, 10.0, 10.2, 9.8 의 6개의 신호값이 들어온다고 칩시다. 여기서 우리가 Sensor의 값에 따라 동작을 제어를 할 때 10은 실제 Sensor의 값이고 나머지는 환경적 요인 노이즈에 의해 변형된 신호값이라면 그 변형된 값의한 동작을 아두이노가 하게 됩니다. 매 순간 측정 할 때 마다 정확하게 동일한 환경이 될 수 없습니다. 그래서 매 순간 Sensor의 환경요인에 의한 변화율이 발생합니다. 그부분을 줄이기 위해서 일정 측정한 수치의 평균값으로 아두이노에서 제어를 한다면 변화율이 최소화 될 수 있습니다. 그 변화의 폭을 부드러게 처리하기 위해 평균값이라는 원리를 이용하여 보정 작업을 수행을 합니다.

어떤 느낌이냐면 아래 그림이 좀 그런데 극단적으로 표현하자면 아래 그림과 같습니다.


검정선은 신호값들이 불규치적으로 매순간 들어온다고 생각하시고 빨간선은 그런 값들이 측정 신호를 묶음으로 해서 그 신호의 평균값으로 처리하면 부드럽게 곡선에 가까운 처리를 할 수 있게 되겠죠.

2) Smoothing의 평균값 구하기


1
2
3
4
5
6
7
8
9
float a[6] ={9.9, 10, 10.1, 10, 10.2, 9.8};
float total=0;
float avg=0;
for(int i=0;i<6;i++){
 
 total=tatal+a[i];
 
}
avg = toatal/6;

배열 변수에 저장된 6개의 값을 total 변수에 더한 값에서 6으로 나눈 값이 평균이 됩니다.

3) 센서로 Smoothing의 평균값 구하기

Sensor로 아날로그 신호 값은 0~1023 사이의 값이니깐 int형 변수로 선언하면 되고 아날로그 값을 읽는 analogRead()함수를 사용하면 됩니다. A0핀을 Sensor핀으로 사용한다고 하면 다음과 같이 코딩을 할 수 있겠죠.

1
2
3
4
5
6
7
int total = 0;
int avg = 0;
 
for(int i=0;i<6;i++){
 total=tatal+analogRead(A0);
}
avg = toatal/6;

for문으로 6번 analogRead(A0)의 아날로그 신호를 읽고 total에 저장합니다. 그리고 6번 반복이 끝나면 for문을 빠져 나와서 최종적으로 avg변수에 total값을 6으로 나눠서 평균값을 저장하게 됩니다.

평균값 구하는 방식을 알았으니깐 이제 회로도를 만들어서 실험을 해 봐야 겠지요.

2. 회로도 구성


  • 준비물 : 가변저항 1개, 아두이노우노
  • 내용 : 가변저항기로 Sensor 읽기를 대신 한다.


가변저항의 선 연결은 어려운 부분은 없네요.

2. 코딩



함수

  • analogRead(아날로그핀) : 아날로그 핀 값을 읽는다.
  • Serial.begin(9600) : 시리얼통신 시작
  • Serial.println(출력값) : 시리얼모니터로 출력

코딩은 공식 아두이노 홈페이지의 튜토리얼 소스를 기반으로 약간만 수정한 소스입니다. 가독성과 이해를 돕기 위해 수정이였을 참고하시고 보세요. 제가 만들어 낸 것이 아니라 기본 로직은 위에 링크된 참고소스의 출처에서 인용된 코딩입니다.

1
2
3
4
for(int i=0;i<6;i++){
 total=tatal+analogRead(A0);
}
avg = toatal/6;

이 수식을 이용할꺼에요. 표현은 좀 달라 보일 수 있겠네요.

아날로그 신호값을 읽기 전 처음 세팅을 먼저 해야겠지요.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const int sensorPin = A0; //측정 센서핀
const int readMax = 6//평균값을 구하기 위한 측정 횟수
 
int readings[readMax]; //측정값 저장 배열변수
int readIndex = 0; //reading[readIndex]로 측정된 값이 저장될 위치
int total = 0//측정한 값들의 전체합
int avg = 0; //측정한 값들의 평균값
 
void setup() {
   
  Serial.begin(9600);
   
  for (int i = 0; i < readMax; i++) {
    readings[i] = analogRead(sensorPin); 
        total = total + readings[i];
  }
}

출처 소스는 Sensor에서 측정된 값을 setup()함수에서 0으로 모두 초기화 작업을 수했습니다. 물론 그렇게 코딩하셔도 상관없습니다. 여기서 곰곰히 생각을 해보시면 이 상태로 loop()함수에 진입하게 되면 어떤 현상이 발생 할까요. total이 0인 상태에서 출발합니다. loop()함수가 6번 반복될 동안은 평균값을 만들어 낼 수 없습니다. 6번 반복이 된 후에야 평균값이 만들어지는 것이죠. 그 부분을 미리 setup()함수에서 total값을 만들어 낸다면 loop()함수가 시작되자마자 바로 Sensor의 측정된 평균값을 구할 수 있겠죠. 그렇게 되면 바로 아두이노는 그 평균값으로 동작 제어를 수행할 수 있겠죠.

그래서, 저는 수정하여 setup()함수에서 미리 loop()함수로 들어가기 전에 6번 Sensor의 값을 측정을 하여 total에 합산 한뒤에 평균값을 구할 수 있게 세팅 코딩을 하였습니다.

준비 세팅은 끝났으니 loop()함수로 진입해 볼까요.

측정 Sensor의 값은 readings[readMax]배열 변수에 저장되어 있습니다. 새로운 Sensor의 값을 읽기 위해서는 가장 오래된 Sensor의 값을 버려야 합니다. 가장 오래된 센서의 값은 readings[0]에 저장된 값이겠죠. 그래서 변수 선언할 때 readIndex값을 0으로 초기화 한 것이지요.


위 그림에서 보는 것처럼 첫번째 위치의 값을 버려야 합니다. 그러면 total에는 이전 6개의 측정 Sensor값이 합산되어 있으니간 뺄셈연산으로 reading[0]의 값을 빼주면 첫번째 위치의 값을 버린 효과가 되겠죠.

1
total = total - readings[readIndex]; 

이제 첫 번째 readings[readIndex]의 값을 버렸으니간 다시 이 곳에 새로운 Sensor값을 읽어와서 저장해야 겠죠.

1
readings[readIndex] = analogRead(sensorPin);   

그리고 total은 다시 새로운 측정값을 더해야 평균값을 계산 할 수 있겠죠.

1
total = total + readings[readIndex];

그러면 평균값을 구해볼까요.

1
avg = total / readMax;

이렇게 해서 loop()함수는 'readIndex=0'인 위치에 새로 측정한 값을 배열공간의 저장하고 새로측정된 값과 이전 측정된 값을 합산한 값에 평균을 구할 수 있게 되었습니다.


대충 위 그림처럼 동작 했습니다. 처음 setup()함수로 6개의 측정 데이터를 읽고 각 측정 데이터를 읽은 순서대로 배열변수에 저장했습니다. 그리고 total 변수에 6개의 값이 합산되어 있습니다. 첫번째 값을 total 값에서 빼주면 나머지 5개의 데이터만 9, 11, 12, 10, 8 의 값만 합산되어 있게 됩니다. 첫번째 위치의 값을 새로운 11값이 채워질 경우 total은 새로 측정된 값을 더해주면 11, 9, 11, 12, 10, 8 의 값이 합산된 값이 되겟죠. 여기서 이 전체의 값을 6으로 나눠주면 현재 Sensor의 평균값이 나오게 됩니다.

이제 다음의 측정 할 Sensor 값은 어디에 저장되고 total값을 어느 값을 버리고 새로운 값을 더 할까요.

바로, 두번째인 'readIndex=1'인 위치에 reading[1]의 값을 빼주고 새로운 센서값을 저장해야 합니다. 가장 오래전에 측정한 값은 이제 reading[1]의 위치값이니깐요.

위치를 이동하기 위해서는 loop()함수에서 readIndex의 위치값에 대한 제어문이 필요 합니다. 다음 loop()문에서는 1씩 증가시켜서 reading[]배열의 위치를 오른쪽으로 시켜야겠죠.

1
readIndex++;

이렇게만 표현하면 문제점이 발생합니다. readIndex++로 무한 1씩 증가해버리게 됩니다. 배열변수 reading[6]은 총 6칸으로 이뤄졌습니다. 'readIndex=5'를 초과하면 안됩니다. 0~5까지만 증가해야 합니다. 그리고 마지막 5의 위치가 되면 다시 0으로 초기화 해서 0번째 위치를 가리키게 해야 합니다.

이 제어문을 만들려면 IF문을 활용하면 됩니다.

1
2
3
if (readIndex >= readMax) { 
   readIndex = 0;
}

위에 설명보다는 코딩이 아주 간단하죠. 읽을 최대 갯수 readMax(6)보다 '크거나 같을때' 0으로 초기화 하면 됩니다. 0~5까지 배열변수의 위치니깐 6이 되면 0으로 다시 초기화 하라는 의미인 것이죠.

코드를 종합해 보면,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
const int sensorPin = A0; //측정 센서핀
const int readMax = 6//평균값을 구하기 위한 측정 횟수
 
int readings[readMax]; //측정값 저장 배열변수
int readIndex = 0; //reading[readIndex]로 측정된 값이 저장될 위치
int total = 0//측정한 값들의 전체합
int avg = 0; //측정한 값들의 평균값
 
void setup() {
   
  Serial.begin(9600);
   
  for (int i = 0; i < readMax; i++) {
    readings[i] = analogRead(sensorPin); 
        total = total + readings[i];
  }
}
 
void loop() {
   
  total = total - readings[readIndex]; 
  readings[readIndex] = analogRead(inputPin); 
  total = total + readings[readIndex]; 
  readIndex = readIndex + 1;
   
  if (readIndex >= readMax) { 
    readIndex = 0;
  }
   
  avg = total / readMax;
   
  Serial.println(avg);
  delay(1);      
}

3. 결과


가변저항기로 Sensor의 역활을 대신했습니다. 실제로 구현한다면 가변저항 부분에 Sensor를 부착하면 되겠죠.




4. 추가 내용


오늘 배운 Smoothing의 원리는 아래의 그림과 같은 방식이였습니다.

하지만 이런 방식만 있는게 아닙니다. 평균값을 어떤 기준으로 잡느냐에 따라 이렇게도 바뀔 수 있습니다.

아예 빠르게 6개의 측정을 수행하고 그 값의 평균값을 기준으로 아두이노에서 동작제어를 할 수도 있겠죠. 그런데, 이 경우는 문제점이 있습니다. 바로 평균값을 만들 측정 갯수가 늘어날 경우 측정 수 만큼 지연이 발생합니다. 짧은 측정은 상관없지만 많은 측정을 할 경우는 아두이노가 동작 제어를 할 때 느리게 반응을 하겠죠.

그래도 코딩은 어떻게 할까요. 아주 간단합니다 Setup()함수에서 했던 초기화 for문을 그대로 loop()로 옮겨오면 됩니다

1
2
3
4
5
6
7
8
9
10
11
12
void loop() {
  total=0;
     
  for (int i = 0; i < readMax; i++) {   
        total = total + analogRead(sensorPin); 
  }
   
  avg = total / readMax;
   
  Serial.println(avg);
  delay(1);      
}

코딩은 위와 같이 엄청 단순화 되었지만 별로 추천하는 코딩은 아닙니다. readMax 값이 커질 수록 측정하는 횟수가 늘어나면 그만큼 아두이노가 다른 동작제어를 할 때 대기시간이 늘어나기 때문에 추천되지 않는 코딩입니다. 그냥 이런식으로도 평균값을 구 할 수 있구나 정도로 생각하시면 되겠습니다. 이외에도 한번 어떤식으로 측정해볼까 하고 상상력을 발휘해 보세요. 제가 계속 열거하면 상상 표현이 줄어드니깐 여러분들이 직접 상상력을 발휘해 보세요.

마무리


오늘은 코딩은 별로 어렵지 않는데 설명이 좀 어렵게 되었네요. 괜히 혼동스럽게 되지 않았나 싶네요. 코딩한 로직만 보고 한번 한줄씩 어떤 동작을 하지니 해석을 하셨으면 합니다. 그러면 이해가 빠르게 되지 않을까 싶네요.

Sensor값을 단순히 이제까지 읽어 왔지만 Sensor를 어떻게 읽을 것인지에 대한 부분도 이제 배워야 합니다. 어떻게 읽느냐에 따라서 아두이노의 동작제어를 제대로 할 수 있겠죠.

마지막으로, Sensor의 값을 어떻게 읽어 들일지 상상의 나래를 펼쳐보세요.


댓글()

[아두이노] 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) 피에조부조 멜로디



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

[ 기본 소스 ]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#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 길이 형태로 출력하기 위해서 우선 사용될 부분을 가져오도록 하겠습니다.

[ 기본 소스 ]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#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번핀이 제어핀이니깐 아래와 같이 기본 선언을 해야 겠죠.

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

Seteup() 세팅

1
2
3
4
5
void setup()
{
  neopixel.begin();  
  randomSeed(analogRead(A1));
}

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

loop() 가져올 내용

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

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

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

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

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

1
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문을 활용하면 쉽게 해결이 됩니다.

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

1
2
3
4
5
6
7
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에 불이 들어오게 만들어 봅시다.

1
2
3
4
5
6
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문으로 조건문을 만들면 됩니다.

1
2
3
4
5
6
7
8
9
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()함수 내부가 좀 깨끗해지겠죠.

1
2
3
4
5
6
7
8
9
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 코딩을 합치기

1
2
3
4
5
6
7
8
9
10
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에 불이 들어오는 동시에 음의 소리가 나겠죠.

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

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

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

종합해보면.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
#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언어 기초 문법을 공부할 때 학습 방법입니다. 코딩은 같은 표현도 다양하게 표현이 가능이 가능합니다.

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

이런 표현이 있다면

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

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

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

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

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

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


댓글()

[번외] C 기초 코딩 문법으로 다른 언어 배우기

IOT/아두이노기초문법|2019. 4. 16. 09:00

[번외] C 기초 코딩 문법으로 다른 언어 배우기



오늘은 아두이노를 포스팅 하려다가 주말이고 해서 이번주에 배운 기초 코딩 문법으로 다른 언어를 습득에 대해서 이야기를 하려고 합니다. C언어의 기초 문법만으로도 다른 언어를 어느정도 기초 독학이 가능합니다. 그 예로 자바계열인 자바스크립트를 소개하도록 하겠습니다. 소개한 목적은 나중에 Steem API를 연동해서 Steem 블록체인 정보를 개인적으로 활용해 볼 수 있기 때문에 간단히 기초만 소개 합니다.

1. 자바스크립트 세팅


[기본 HTML]

1
2
3
4
5
6
7
8
9
10
11
<!DOCTYPE html>
<html>
<body>
 
<p id="demo"></p>
<script>
    document.getElementById("demo").innerHTML = "Hello World!";
</script>
 
</body>
</html>

위의 코딩은 html 기본 구조입니다. 여기서 코딩하는 위치는 stript 태그 사이에 자바스크립트 언어로 코딩할 예정입니다.

[결과]


여기에서 자바스크립트 문법은

1
document.getElementById("demo").innerHTML = "Hello World!"

HTML 문서에 id "demo"의 위치를 가리키게 되는데 nnerHTML는 값을 가져오거나 설정할 수 있다. 해당 id를 가진 태그를 가리키고 거기에다 "Hello World!"를 저장한다고 생각하시면 됩니다.

1
<p id="demo> 저장위치 </p>

이런식으로 그냥 이해해 버리세요. 원래는 p태그 사이에 "Hello World"로 설정하는건데 쉽게 의미적으로 저 문장으로 자바스크립트에서 문자열을 html로 출력시킬 수 있구나 정도로 이해하시면 될 듯 싶네요.

1
document.getElementById("demo").innerHTML

이부분은 통째로 암기해놓고 계속 여기에다 출력할꺼에요. 자중에 관심있으신 분들은 정독으로 태그 관련 접근과 id 접근, 클래스 접근하는 방법을 따로 공부하셔야 합니다.

1
<p id="demo"></p>

아무튼, id가 "demo"인 p태그 사이에 "Hellow World"가 출력시키는 문장을 이해하셨으면 넘어 가겠습니다.

2. 자바스크립트 변수


참고 : [아두이노] 아두이노 코딩 변수


[ C언어 ]

1
2
3
4
int a = 10;
char b = 'a';
int c[6] ={1,2,3,4,5,6};
char d[6] = "hello";

[자바스크립트]

1
2
3
4
var a = 10;
var b = 'a';
var c = {1,2,3,4,5,6};
var d = "hello";

C언어에서는 자료형을 선언해 줘야 했지만. 자바스크립트는 var로 통일 됩니다. 알아서 값에 대해 잡아주니깐 더 쉽죠. var의 단점은 var로 선언한 변수가 재선언 되어도 에러가 발생하지 않습니다. 그래서 지역범위 변수인 let 형을 사용하기는 하지만 처음에는 간단한 표현 var로 편하게 사용하세요.

1
2
let a =10;
const b = 10;

let 의 범위 안에서 a변수는 재선언 불가능하기 때문에 변수 선언에 대한 에러를 줄일 수 있습니다. const 형은 상수형의 의미러 상수변수라고 생각하시면 됩니다.

예제)

1
2
3
4
5
6
<p id="demo"></p>
 
<script>
    var a ="hello World!";   
    document.getElementById("demo").innerHTML =a;
</script>

[결과]


추가 내용 Objects 변수

1
var  obj = {Name:"codingman", Age:"10", Address:"한국"};

변수를 위와 같은 객체 형식으로 저장할 수 있습니다. 이걸 배열로 저장하면 배열객체가 되겠죠. 제각각인 개별 요소들을 하나의 하나의 큰 묶음으로 묶어서 변수화 할 수 있는데 이걸 객체변수라고 합니다. 객체명으로 각 요소들을 쉽게 삽입, 삭제, 갱신을 할 수 있기 때문에 효율적인 코딩을 할 수 있습니다.

접근 obj.Name => "codingman"

접근자 콤마(.)로 각 요소를 쉽게 접근 할 수 있죠. 나중에 Steem API를 통해 Steem 블록체인 정보를 가져올 때 Json 형태로 만들어 놓고 접근자를 통해 쉽게 특정 데이터를 접근 할 수 있습니다. 그러니 표현하는 것을 잘 기억해 주세요.

예제)

1
2
3
4
5
6
<p id="demo"></p>
 
<script>
  var obj = {Name:"codingman", Age:"10", Address:"한국"};
  document.getElementById("demo").innerHTML = obj.Name;
</script>

[결과]


3. 자바스크립트 사칙연산(덧셈)


[C언어]

1
2
3
4
int A = 10;
int B = 2;
int addAB = 0;
addAB = A + B;

[자바스크립트]

1
2
3
var a =10;   
var b = 2;
var addAB = a+b;   

기본 차이가 없죠. 확실히 자바스크립트가 변수 선언이 편하죠.

예제)

1
2
3
4
5
6
7
<p id="demo"></p>
<script>
    var a =10;   
    var b = 20;
    var addAB = a+b;   
    document.getElementById("demo").innerHTML =a+'+'+b+'='+addAB;
</script>

여기서, 문자열을 합치는 기호는 '+' 입니다.

예) "hello" + "World"

1
2
3
4
5
6
7
var a= "hello";
var b = World";
var c = a+b;
결과 c => "helloWorld" 됩니다.
 
var c = a+' '+b;
결과 c =>"hello World" 됩니다.

문자열변수들 사이에 따로 문자나 공백을 넣을 수 있습니다. a,b사이에 '+'문자를 넣고 결과 '='문자를 넣어서 출력을
"10+20=30"의 값이 출력되는데 아래 결과 보시면 되겠습니다.

[결과]


4. 자바스크립트 IF문


참고 : [아두이노] 아두이노 코딩 IF문


[C언어]

1
2
3
4
5
6
7
8
9
  int a = 10;
  int b = 5;
  if(a>b) {
    참문장;
  }
  else {
      거짓문장;
  }
<

[자바스크립트]

1
2
3
4
5
6
7
8
var a = 10;
var b = 5;
if(a>b) {
  참문장;
}
else {
    거짓문장;
}

if문은 동일합니다. 조건식이 참이면 참문장이 수행되고 거짓이면 거짓문장이 수행됩니다. 따로 공부할 필요가 없겠죠.

예제)

1
2
3
4
5
6
7
8
9
var a = 10;
var b = 5;
  
if(a>b) {
   document.getElementById("demo").innerHTML = "a가 보다 크다";
}
else {
   document.getElementById("demo").innerHTML = "a가 b보다 크지 않다.";
}  

[결과]


5. 자바스크립트 For 문



[C언어]

1
2
3
4
int sum=0;
 for(int i=0;i<=10;i++){
    sum=sum+i;
}

[자바스크립트]

1
2
3
4
var sum=0;
for(var i=0;i<=10;i++){
    sum = sum + i;
}

변수 선언 부분만 var로 변경하고 나머지 동일 합니다. for문의 여러가지 표현이 있는데 초보분들은 이거 하나만 아셔도 됩니다.
예제) 0부터 10까지의 합을 구하시오.

1
2
3
4
5
6
7
8
9
<p id="demo"></p>
 
<script> 
 var sum=0;
 for(var i=0;i<=10;i++){
     sum = sum + i;
 }
 document.getElementById("demo").innerHTML = sum;
</script>

[결과]


6. 자바스크립트 While 문



[C언어]

1
2
3
while(조건식){
    실행문;   
}

[자바스크립트]

1
2
3
while(조건식){
    실행문;   
}

동일합니다. 조건식이 거짓이 될때까지 계속 실행문이 반복 수행합니다. 따로 공부할 것이 없겠죠.

예제) 1부터 10까지의 합을 구하시오.(불가피하게 For문 예제 활용)

1
2
3
4
5
6
7
8
9
10
11
12
<p id="demo"></p>
 
<script> 
var input_val = 10;
var sum=0;
 
while(input_val>0){
    sum=sum+input_val;
    input_val--;
}
document.getElementById("demo").innerHTML = sum;
</script>

while문의 조건식 0보다 큰지를 비교하면서 반복하다가 0이 되면 빠져 나옵니다.
[결과]


7. 자바스크립트 Switch 문



[C언어]

1
2
3
4
5
6
7
8
9
10
switch(ch) {
    case 1:
        수행문1 ;
        break;
    case 2:
        명령문 2;
        break;                 
    default:
         불일치 명령문;
}

[자바스크립트]

1
2
3
4
5
6
7
8
9
10
switch(ch) {
    case 1:
        수행문1 ;
        break;
    case 2:
        명령문 2;
        break;                 
    default:
        불일치 명령문;
}

switch 문의 경우도 동일하게 사용합니다. 사실 모든 C언어에서 사용된 문법이 동일하게 적용됩니다. 변수 선언만 차이가 있을 뿐이죠. 그래서 C언어를 공부하시면 다른 언어를 어느정도 독학이 가능합니다. 단지 해당 언어에서 사용하는 일부 함수들을 찾아내는게 혼자서 독할 하실 때 어려운 점이고요. 기본 베이스는 어떤 언어라도 다 동일합니다.

예제)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<p id="demo"></p>
 
<script> 
 
  var ch = 'y'
  switch(ch){
    case 'y':
         document.getElementById("demo").innerHTML = "Input : Y";
         break
    case 'n'
         document.getElementById("demo").innerHTML = "Input : N";      
         break
    default
         document.getElementById("demo").innerHTML = "잘못입력함";
  
</script>
간단히 id가 "demo"인 위치에 해당 문자열을 출력하는 예제입니다.

[결과]



8. 자바스크립트 사용자 정의 함수



[C언어]

1
2
3
4
5
6
7
8
9
10
11
void 함수명(인자){
      명령문;
}
 
자료형 함수명(인자, 인자){
      명령문;
}
 
int add(int a, int b){
     return a+b;
}

자료형 char, int, float 등등을 말함. 반환값의 자료형인거 안 까먹으셨죠. void 반환되는 자료형이 없을 때 붙여주는 것이고요.

[자바스크립트]

1
2
3
function addAB(a, b) {
    return a + b;
}

자료형은 안붙지만 앞에 function이라는 함수명을 써주고 인자의 경우는 따로 자료형을 지정할 필요가 없습니다. 그렇게 까지 차이점은 없네요.

예제)

1
2
3
4
5
6
7
8
9
<p id="demo"></p>
 
<script>
  function addAB(a, b) {
    return a + b;
  }  
 
  document.getElementById("demo").innerHTML = addAB(10,2);
</script>

덧셈 함수를 사용자 정의 함수로 만들어 넣고 두 수를 더한 값 반환해주는 함수입니다. 그결과는 id가 "demo"인 p 태그로 출력됩니다.

[결과]


마무리


오늘은 C언어를 공부하면 다른 언어를 얼마나 쉽게 독학이 되는지를 자바스크립트의 기초 문법을 통해 살펴보았습니다. 이렇게 C언어를 공부하시고 나면 자바스크립트의 변수 선언부분과 사용자 정의 함수 표현이 바뀐 부분만 체크해주시기만 하면 나머지는 모두 동일하니깐 어렵지 않게 배울 수 있게 됩니다. for문에서 재밌는 for문 표현들이 있는데 그 부분은 거론 하지 않았지만 꼭 찾아보세요.

그리고, 자바스크립트에 사용되는 일부 함수들을 알아야 합니다. 위 소개한 것은 기초 문법이고 실제 사용하기 위해서는 사용되는 함수들을 알아야 제대로 활용 할 수 있습니다.

W3Schools.com => https://www.w3schools.com/js/default.asp

이곳에 가셔서 자바스크립트 튜토리얼을 필독을 해주세요. 충분히 하실 수 있을 꺼에요.

아두이노를 주말에 진행하려다가 간단히 어제 마무리로 쓴 글이 생각나서 자바스크립트로 포스트하게 되었네요.

이상 오늘 포스팅을 마무리 합니다.

댓글()

[아두이노] 아두이노 코딩 사용자 정의 함수

IOT/아두이노기초문법|2019. 4. 15. 09:00

[아두이노] 아두이노 코딩 사용자 정의 함수



오늘은 제어문이 아닌 사용자 정의 함수에 대해서 살펴보도록 하겠습니다. 사용자 정의 함수라는 것은 프로그래머가 직접 만든 함수를 지칭합니다. Serial 라이브러리에서 제공되는 println()함수을 이용하여 우리는 쉽게 시리얼모니터로 직접 메세지를 출력했습니다. 시리얼통신을 위한 프로그램 코딩을 직접 만들 필요 없이 이런 함수들을 호출로 쉽게 코딩했습니다. 만약 프로그래머가 어떤 명령 로직을 짜놓고 그것을 함수로 만들어 놓으면 어떻게 될까요. 만든 함수를 원하는 위치에 호출만으로 해당 명령 로직을 수행 할 수 있게 됩니다. 이 사용자 정의 함수는 아두이노 코딩에서 꼭 필요한 개념이니깐 꼭 알아두세요. 본격적으로 사용자 정의 함수에 대해서 살펴보도록 하겠습니다.

1. 사용자 정의 함수



위 그림은 간단히 사칙연산에서 덧셈을 사용자 정의 함수로 표현한 예제입니다. 글보다는 실제 모습을 보여주고 설명하는게 빠를 것 같아서 간단히 덧셈함수를 만들어 보았네요.

우선, 사용자 정의 함수는 앞에 자료형이 붙습니다. 함수에 자료형이 붙는다는게 약간 이해가 안될 수 있는데 이 자료형은 사용자 정의 함수에서 반환되는 값의 자료형입니다. 함수에서 어떤 계산을 하고 특정한 값을 반환 시킬때 그때 그 값의 자료형으로 생각하시면 됩니다.

다시 설명을 하자면.


위 그림에서 add(10,20)의 사용자 정의 함수가 a, b에 10과 20의 인자가 되고 사용자 정의 함수에 던져주고 사용자정의 함수에서는 이 인자값을 기준으로 10+20의 계산이 이루어지겠죠. 그러면 덧셈함수이니 30이란 값이 계산되어 나옵니다. 이때 30이란 값이 반환 시킬 때 30의 자료형이라고 생각하시면 됩니다. 30이니깐 사용자 정의 함수의 자료형은 int add() 함수가 되겠죠. 어떤 자료형인지 아시겠지요. 그리고 반환시키는 명령어가 return 이라는 명령어가 수행합니다. "return a+b;" 하면 a+b의 값을 반환하시오라고 말로 이해하시면 됩니다.

결론, 위 그림처럼 a+b의 값을 return 반환 명령어로 반환되는 값이 정수형이니깐 함수 앞에다 int형을 붙였이게 된다는 것을 기억해 주세요.



그리고, void라는 표현이 있는데 이건 의미적으로 '빈'으로 비어있는 반환값이 없는 함수라는 생각하시면 될 듯 싶네요. 반환시킬 값이 있으면 반환되는 값의 자료형으로 함수 앞에 붙이시고요. 반환값이 없으면 void란 단어를 붙여서 표현하시면 됩니다.

[ 코딩 ]


[ 결과 ]


프리젠테이션에서 그림 그리기에 재미 붙여서 그림으로 만들었네요. 위 그림에서 보는 것 같이 대충 이런식으로 표현하시면 됩니다. 참고로 함수에 인자가 있을 때는 사용자 정의 함수에서는 인자 값에 맞게 자료형을 각각 선언해야하는 것은 꼭 기억해 주세요.

특이한 점은 아두이노에서는 사용자 정의 함수를 하단에 표현해도 됩니다. C 컴파일러에서는 하단에 표현할 경우 상단에 함수명을 선언해 줘야 합니다. 이게 좀 특이하더군요.

C언어의 경우,

1
2
3
4
5
6
7
8
9
10
int add(int a, int b){
  return a+b;
}
int main(void)
{
    int c=0;
    c= add(10,20);
    printf("%d\n",c);
    return 0;
}

이렇게 상단에 표현 해줘야 합니다. 아니면 하단에 표현할경우 상단에 해당 사용자 함수명을 선언을 꼭 해줘야 합니다.

1
2
3
4
5
6
7
8
9
10
11
int add(int a, int b);
int main(void)
{
    int c=0;
    c= add(10,20);
    printf("%d\n",c);
    return 0;
}
int add(int a, int b){
  return a+b;
}

나중에 코딩한 뒤에 컴파일 때 문제가 생깁니다. main()함수가 먼저 코드에 대해 처리하면 add()라는 함수가 정의되어 있지 않기 때문에 에러가 발생합니다. 그래서 main()앞에 표현을 하던 main()함수 앞에 함수 선언을 해 줘야 합니다. 혹시 C언어 컴파일로 사용자 정의 함수를 사용했는데 에러가 발생하는 이유를 찾지 못하고 해맬 수 있으니깐 이 부분을 주의해 주세요.

아두이노에서는 그냥 상관없이 돌아가더군요. 아두이노 내부에서는 setup(), loop()함수를 처리하는 main() 함수가 따로 하단에 있어서 loop() 아래 표현을 해도 main() 함수 상단에 표현된 것인지 모르겠군요.

3. 아두이노에서 사용자 정의 함수


아두이노에서는 언제 사용하면 좋을까요. 아마 초음파 센서 제어편과 3색 LED 제어편에서 보셨을 꺼에요. 초음파 센서를 이용하여 거리를 측정하는 식을 사용자 정의 함수로 만들어 놓으면 편하게 호출만으로 해당 명령을 수행할 수 있게 됩니다.



1
2
3
4
5
6
7
8
9
void loop() { 
  if(digitalRead(5)==0){
    float v1=UltrasonicDistance(6);
    float v2=UltrasonicDistance(7);
    float total=v1+v2+10;
    Serial.println(total);
    delay(500);
  
}

위 로직에서 UltrasonicDistance()함수는 사용자 정의 함수로 거리를 측정하는 함수입니다. 참고예제에 가셔서 보시면 잘 나와 있습니다. loop()함수에서 두번 호출되었는데 만약 그냥 코딩을 했다면 UltrasonicDistance()안에 로직이 좀 길고 두번 중복 코딩을 했겠죠. 단 두줄로 코딩할 것을 꽤 길게 코딩을 하게 된다면 비효율적인 코딩으로 가독성도 떨어지겠죠.

위 코딩에서는 뭔지는 모른데 사용자정의함수에서 계산된 값이 v1과 v2에 저장되고 이 값들고 10을 더한 값을 시리얼모니터로 출력했네하고 쉽게 코드를 해석할 수 있습니다. 뭔가 복잡한 로직을 사용자 정의 함수로 만들어 외부로 빼놓으면 위에서 표현한 것처럼 쉽게 의미 전달 형태의 코딩으로 표현 할 수 있게 됩니다. 이게 바로 사용자 정의 함수의 장점입니다.



1
2
3
4
5
6
7
8
void loop(){
    Color(255,0,0);
    delay(1000);
    Color(0,255,0);
    delay(1000);
    Color(0,0,255);
    delay(1000);
}

위 로직에서 Color()함수는 사용자 정의 함수입니다.

1
2
3
4
5
6
void Color(int red, int green, int blue)
{
  analogWrite(rpin, red);
  analogWrite(gpin, green);
  analogWrite(bpin, blue);
}

코딩은 간단합니다. 세개의 출력핀에 아날로그 값을 출력하는 명령어 입니다. 그런데 외부로 빼서 사용자 정의 함수를 만든 이유가 뭘까요. 바로 중복 코딩을 제거하는 방식입니다. loop()함수는 R->G->B로 순서대로 색이 1초단위로 출력됩니다. 문제는 하나의 색을 만들려면 RGB핀 3개에 아날로그 신호를 보내는 세줄의 명령어를 코딩해야 합니다. 그러면 코딩을 analogWrite()함수를 9번 코딩해야 합니다. 이걸 길게 코딩해 놓으면 시각적으로 보면 가독성이 떨어지고 무슨색이 출력되는지도 쉽게 알 수 없습니다.
하지만 사용자 정의 함수로 표현을 하면,

1
Color(255,0,0);
1
Color(R, G, B);

시각적으로 R의 인자만 255니깐 빨간색이구나하고 쉽게 해석할 수 있습니다. 이렇게 중복되는 코딩을 줄일 수 있고 가독성을 높이는 효과를 지닌게 바로 사용자 정의 함수입니다.

사용자 정의 함수를 쓰면 얼마나 좋은지 아시겠지요.

마무리


원래는 C언어 하면 포인트 변수를 배워야 하는데 그건 생략하겠습니다. 지금까지 배운 변수, 배열변수, IF, For, While, Switch, 사용자 정의 함수였네요. 이정도만 이해하시면 아두이노를 사용하는데 아무런 문제가 없습니다.
C언어는 이외에도 더 많은 문법이 있지만 사실 그렇게 까지는 아두이노에 사용되지는 않습니다. 알고 있으면 좋기는 하지만 위 기초정도만 이해하시면 왠만한 표현은 다 하실 수 있습니다.

이정도만 이해하실 수 있으면 다른 프로그램 언어도 충분히 독학으로 배우 실 수 있습니다. 모든 프로그램 언어의 문법은 거의 비슷하거든요. C언어만 잘하면 다른 언어를 배우는데 그렇게 어렵지 않습니다. 단지 처음에 표현하는 부분만 익숙치 않을 뿐이죠.

암튼 기본적으로 사용하는 기초만 이해하시면 무난히 아두이노를 제어 하실 수 있습니다.
포인트랑 클래스까지 더 나가고 싶지만 이부분은 프로그램 언어 영역이라 아두이노에 대한 흥미를 읽을 수 있기 때문에 너무 깊게 들어가는 것은 좀 문제가 있겠죠.

사설이지만 이번에 설명한 개념을 가지고 자바스크립트언어를 살작 독학으로 공부하셔서 Steem API를 사용해 보세요. 그리 어렵지 않을꺼에요. 스티미언 분들이라면 Steem API를 다루면 효율적인 스티미언 활동을 할 수 있습니다. 여기서 제공되는 함수들을 불러다 쓰기만 하면 되니깐요. Steem 블록체인을 읽고 쓸 수 있으면 많은 것들을 할 수 있게 됩니다. 그러니 한번 여기까지 따라오셧으면 w3school 사이트에 가셔서 자바스크립트 문법을 독학하시고 Steem API를 접근해 보세요.

아무튼 기초설명은 이정도로 해서 마무리 합니다.

댓글()

[아두이노] 아두이노 코딩 Switch-case 문

IOT/아두이노기초문법|2019. 4. 14. 09:00

[아두이노] 아두이노 코딩 Switch-case 문



오늘은 제어문 중에 Switch-case 문에 대해서 살펴보도록 하겠습니다. 이전 시간에 IF문은 조건식에 의해서 참 or 거짓으로 둘 중 하나를 선택하는 제어문이라면 이번시간에 배우는 Switch-case문은 둘 이상의 case 중에 하나를 선택하는 제어문입니다. 쉽게 말해서, 여러개 중에 하나를 선택하는 제어문이라고 생각하시면 됩니다. IF문은 두개로 고정되어 있다면 Switch-case은 여러개의 선택문으로 개념을 잡아 주세요. 이제 Switch-case 문을 어떻게 다루는지 자세히 알아보도록 합시다.

1. Switch-case 문의 순서도



Switch문은 어떤 값이 입력이 되면 각 case 값들 중 일치하는 case의 명령문을 수행하는 제어문입니다. 입력값이 case 1과 일치하면 True로 다음 명령문 1을 수행하지만 일치하지 않으면 다음 case 2와 비교하고 일치하면 명령문 2를 수행하고 일치하지 않으면 다음 case문을 비교하게 됩니다. 여기서, 모든 case 값들이 일치하지 않으면 default가 수행 됩니다. 한마디로 여러개의 case 중에서 일치하면 해당 case 라인의 명령을 수행하고 일치하는 case가 없다면 default의 명령문을 수행한다고 생각하시면 되겠습니다.

그리고, 입력값이 case 2랑 일치하면 명령문 2를 수행하고 뒤에 break 명령으로 Switch문을 빠져 나오는데 이 break 단어를 만나면 더이상 진행하지 말고 해당 Swtich문을 빠져나오라는 명령이니깐 꼭 기억해 주세요. 만약에 break라는 명령문이 없다면 어떤 현상이 발생할까요.

1
2
3
4
case 1 : 명령문 1;
case 2 : 명령문 2;
case 3 : 명령문 3;
default : 불만족명령문;

이렇게 코딩되어 있으면 break가 생략할 경우 입력값이 2로 case 2랑 일치하여 명령문 2을 수행하고 다음 라인인 명령문 3과 불만족명령문까지 전부 수행하게 됩니다. 이 말은 처음 case 진입은 일치한 위치에서 진입이 이루어지고 수행명령은 일치한 라인을 기준으로 순차적으로 다음 라인의 명령문을 수행한다고 생각하시면 됩니다. case 2의 명령문만 수행할려면 해당 명령문 2를 수행한 뒤에 break 명령으로 더이상 진행하지 말고 해당 Switch문에서 빠져나오라는 강제 명령으로 이해해 주세요.

왜! break 명령을 쓰는지 이해 하셨죠.

2. Switch-case 문의 코드


위 그림과 같은 구조로 구성되었습니다. 입력값 ch변수이고 ch의 저장된 값이 case랑 일치하는 라인의 명령문을 수행합니다.

[ 동작 ]

  • ch=='a' 이면 명령문 1 수행하고 break 명령으로 탈출
  • ch=='b' 이면 명령문 2 수행하고 break 명령으로 탈출
  • ch가 일치하는게 없다면 default의 불일치명령문이 수행되고 끝납니다.

대충 어떻게 동작하시는지 아시겠지요. 실제 코딩을 해볼까요.

[ 소스 ]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void setup()
{
  Serial.begin(9600); 
 
  char ch = 'y';
   
  switch(ch){
    case 'y':
                Serial.println("Input : Y");             
                break
    case 'n'
                Serial.println("Input : N");             
                break
    default
                Serial.println("error");                       
  
}

시리얼통신으로 입력 받으면 좋은데 코딩이 길어질 것 같아서 그냥 ch = 'y'가 입력 했다고 치고 진행해보도록 하겠습니다. switch(ch)함수로 입력을 하면 case가 'y'와 'n'이 있습니다. 그래서 'y'가 입력되었기 때문에 첫번째 case문이 수행되고 해당 라인의 "input : Y"라는 문자열이 시리얼모니터로 출력됩니다. 그리고 break명령문으로 switch문을 빠져 나옵니다.

여기서, 'y' or 'n' 이 아닌 다른 문자나 숫자를 입력하면 어떻게 될까요. 바로 default라인이 수행되고 시리얼모니터에는 "error"라는 문자열이 출력되고 끝나게 됩니다. 대충 switch 문을 이해하셨나요.

[ 결과 ]


위에 빨간 사각형이 입력 'y'과 일치하는 첫번째 "case 'y':"라인이 명령문을 수행하고 시리얼모니터로 결과가 출력됩니다. 그리고 break명령으로 switch문을 빠져 나오게 됩니다.

3. Switch-case 문 범위


원래 이경우는 잘 사용하지 않고 여러개의 범위를 나누어서 선택할 때는 IF~else if문을 사용합니다. 비주얼베이직 같은 경우 같은 표현이지만 Select 문이 Switch문인데 범위 지정하는게 의미적으로 편한데 C에서는 좀 표현이 제 경우 부자연스럽게 보입니다. 그래서 그냥 IF~else if문을 사용하고 있네요.

[ 범위 ]

1
2
3
4
5
6
swtich(ch){
    case 1 ... 10:
    case 11 ... 20:
    case 21 ... 30:
    default:
}
1
case 'a' ... 'z':

이렇게 점 세개로 표시 합니다. 비주얼베이직의 경우는 To라는 단어로 범위를 표시를 합니다.

1
2
3
4
5
Select Case ch
    case 1 To 10 :
    case 11,12,13 :
    case else:
End Select

이런식이면 좀 좋은데 점 3개라 그리고 붙어서 표현하면 안되고 암튼 좀 부자연스러운 범위 지정이네요.

이제 지난시간에 IF문에서 성적 학점을 출력하는 예제를 switch문으로 변경해 보겠습니다.

[ 원소스 ]

1
2
3
4
5
6
7
8
9
10
11
void setup()
{
  Serial.begin(9600);
   
  int Eng = 75;
  if (Eng >= 90) Serial.println("A");
  else if (Eng >= 80) Serial.println("B");
  else if (Eng >= 70) Serial.println("C");
  else if (Eng >= 60) Serial.println("D");
  else Serial.println("F");
}

[ Switch-case 소스 ]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
void setup()
{
  Serial.begin(9600);
   
  int Eng = 75
  switch(Eng){
    case 90 ... 100:
                Serial.println("A");             
                break
    case 80 ... 89
                Serial.println("B");             
                break
    case 70 ... 79
                Serial.println("C");             
                break
    case 60 ... 69
                Serial.println("D");             
                break
    default
                Serial.println("F");                       
  }   
}

[ 결과 ]


대충 소스들을 보시고 코딩한 뒤에 입력값들을 다른 값으로 변경하고 실제 돌려보시면 선택 제어문의 동작을 어느정도 이해하실 수 있을거에요.

마무리


Switch문은 IF문가 다르게 여러개의 case 중에 일치하는 하나의 case의 명령을 수행하는 선택 제어문이라고 머리속에 넣어 두세요. 범위의 경우는IF~else if문을 그냥 사용하는게 보기 더 좋겠죠. 오늘 배운 범위 switch 문에서는 이런 표현이 있다는 정도만 알아 두시고요. 개별적인 경우에만 여러개 중에 하나를 선택할 때는 if문보다 Switch문이 가독성이나 표현에서 더 나을 것 같다는 생각이 듭니다.

코딩이라는 것은 우선 자신이 코딩하고 그 코딩한 내용을 읽기 편한 형태로 코딩하는게 무척 중요합니다. 그래서 프로그램 언어 교재들을 보면 스타일들이 약간씩 다릅니다. 분명 동일한 코딩인데 느낌 상 자신과 잘 맞지 않는 코딩들이 있어서 읽어도 무슨 표현인지 잘 이해가 안되는 경우가 많을꺼에요. 제가 쓴 글을 이해 못한신다고 해서 초보분들은 자신이 부족한게 아니라 제 코딩 스타일이 잘 안맞아서 그런거니깐요 여러 코딩들을 살펴보고 자신에 맞는 스타일을 찾아서 공부하시는게 진짜 중요합니다.

저도 C언어 책이 5권이 넘습니다. 필요에 따라서 여러 종류를 구매를 했는데 어떤 책은 보기가 쉽고 어떤 책은 나와 안맞아서 읽기가 힘든 책들도 있었습니다. 이게 저자의 코딩과 나의 코딩 스타일이 다를 때 발생하는 문제인데요 혹시 프로그램언어에 관심있으시면 서점에서 같은 종류의 서로 다른 저자들의 책들을 한번 읽어보세요. 어떤책은 느낌이 오고 어떤책은 봐도 모르겠다고 생각이 드는 느낌이 전혀 없는 책들도 있습니다. 그 책이 문제가 있는게 아니라 자신이 공부한 수준과 또는 지금까지 공부해왔던 코딩 스타일이 책을 쓴 저자와 나의 코딩이 다르기 때문입니다.

혹시 이글을 읽고 이해가 안되면 다른 루트로 검색을 통해서 해당 제어문을 찾아서 읽어 주시기 바랍니다. 아두이노 입문자에게 코딩을 할 때 필요한 최소 문법만 소개하는 포스트라서 생략된 부분이 많습니다. 이해가 안되면 타이틀 제목을 키워드로 해서 구글 검색을 통해 해당 문법을 따로 공부해주세요.

이상으로 마무리 하도록 하겠습니다.

댓글()

[아두이노] 아두이노 코딩 WHILE 문

IOT/아두이노기초문법|2019. 4. 13. 09:00

[아두이노] 아두이노 코딩 WHILE 문



오늘은 제어문 중에 While문에 대해 설명하고자 합니다. While문은 For문과 유사한 반복문을 수행하는 제어문입니다. 대개 For문에서 배운 코딩을 While문으로 표현을 억지로 시키는 경우가 있고 그 반대로 While문을 배운 표현을 억지로 For문으로 코딩해보라고 시키는 경우가 있습니다. 사실 동작원리가 유사하기 때문에 표현은 가능하지만 이렇게 처음 배운분들은 대개 어떤 경우에 For문을 쓰고 While문을 쓰는지 선택하기 어렵습니다. 오히려 두 제어문을 배우면서 초보분들의 머리속에서는 "나보고 어떤 것으로 코딩하라는 거야!"라고 생각이 들게 합니다. 진짜 안좋은 학습인데 둘은 구분지어 따로 사용하는 방법으로 배우셔야 합니다. 두 제어문은 동작원리는 동일합니다. 하지만 사용하는 방향은 차이가 있습니다. 그런데 두 제어문을 배우면서 똑같은 코딩을 하라고 강요를 하게 되면 사실 두 제어문을 어떻게 써야할지 역효과가 발생하기 때문에 억지로 For문의 코딩을 While문으로 표현하는 방식을 강요하지 않겠습니다. 사설은 이만하고 While문에 대해서 알아보도록 하겠습니다.

1. WHILE 문을 FOR 문과 왜 구별해야 하는가?



지난 시간의 For문의 순서도를 왜 다시 While문을 설명하는데 올려놨냐고 의아해 하시는 분들이 많을꺼에요. 이게 While문 순서도 입니다.

[ For 소스 ]

1
2
3
4
5
6
7
8
9
10
void setup()
{
  Serial.begin(9600);
   
  int A[6] = {10,20,30,40,50,60};
   
  for(int i=0;i<6;i++){
    Serial.println(A[i]);   
  }
}

[While 소스 ]

1
2
3
4
5
6
7
8
9
10
11
12
void setup()
{
  Serial.begin(9600);
   
   
  int A[6] ={10,20,30,40,50,60};
  int i=0;
  while(i<6){ 
    Serial.println(A[i]);
    i++;
  }
}

[ 결과 ]


결과는 For문과 동일하게 나왔네요. 소스를 보니깐 "For문과 다르네!"라고 하시겠죠. 초기값, 조건식, 증감연산, 실행문의 명령부분들이 For문에 있던 것들이 그대로 표현되었습니다. 해당 명령들의 코딩 위치만 좀 바뀌었을 뿐이죠.

1
2
3
for(초기값; 조건식; 증감연산){
    실행문;
}
1
2
3
4
5
초기값;
while(조건식){
    실행문;
    증감연산;
}


위 그림처럼 구조가 나뉘고 순서도에서 본것처럼 For문이나 While문 유사하게 동작합니다. 그래서 처음 접하시는 분들은 For문과 While문에서 어떤 제어문을 써야할지 잘 구별을 못합니다. 처음 배울때 While문 배울때 억지로 While문으로 표현했던 내용을 For문으로 만들어 오라고 레포트를 내줬던 기억이 나네요. 의도는 For문의 사용능력도 키우기 위한 목적이였지만 처음 배우는 사람은 서로 다른 제어문이 똑같은 동작 코딩을 하라고 요구하면 나중에 뭔가를 표현하려고 할 때 두 제어문에서 선택에 대한 혼동을 하게 됩니다.

초보분들은 완전 분리된 개념으로 차이나게 학습 해야 합니다. 그래야 이럴 때 이 제어문을 사용하고 저럴 때 저 제어문을 사용하게 됩니다. 초보분들에게는 For문이나 While문이나 그게 그거라고 생각되기 때문에 개념을 잡는게 무엇보다 중요합니다.

2. WHILE 문과 For 문의 차이점


For문은 일정한 규칙의 반복문에서 사용하기 좋고 While문은 불규칙적인 반복문에서 사용하기에 좋습니다. 처음에는 구분하여 생각해 주세요. 즉, For문은 시작과 끝이 일정한 간격으로 규칙적으로 접근하거나 반복하거나 할때 For문을 사용하고 While문은 시작은 주어졌지만 끝이 날때까지는 명령을 가변적 반복할 때 사용한다고 생각하시면 됩니다.


위 그림 표현에서 For문은 반복하기전에 시작때 반복횟수가 정해져 있고 일정한 간격으로 해서 순차적으로 반복을 하게 됩니다. 이 느낌을 잘 기억해 주세요.

그리고 While문은 뭐랄까 반복 횟수가 정해져 있지 않고 While문이 실행되면은 그안에서 반복을 하다가 조건이 끝나면 빠져나오는 느낌이라고 생각하시면 됩니다.

예를들면, 번호 자물쇠가 있는데 자물쇠 번호를 모를때 자물쇠를 열려고 계속 시도하는 과정을 While문이라고 생각하시면 됩니다. 번호 자물쇠가 운좋게 한번에 눌러서 열릴 수도 있고 100번을 눌러서 열릴 수도 있잖아요. 이렇게 계속 반복적으로 번호를 누르는 동작을 수행하면서 어떤 조건에 만족하면 빠져나오는 원리라고 생각하면 좋을 듯 싶네요.

뭔가 For문은 순차적으로 정해진 횟수만큼 반복하는 느낌이고 While문은 진입하게 되면 계속 그 안에서 탈출하려고 반복적으로 시도하는 느낌이라고 생각하면 좋을 것 같네요.

다른 예로는 회원 주소록을 만든다고 해봅시다. 주소록을 입력하는 창을 코딩했을 때 사용되는게 While문이고 주소록을 조회할 때 사용되는게 For문의 느낌입니다.

1
2
3
4
5
6
while(상태=="Y"){
 이름 입력;
 나이 입력;
 주소 입력;
 계속 입력하시면 "Y" 종료하실려면 "N";
}

이렇게 회원 등록을 입력하고 나서 또 등록이 필요하면 "Y"을 입력해서 다시 While문을 반복합니다. 하지만 이게 시작값에서 회원 주소록이 꼭 입력횟수가 정해져 있는게 아니라 주소록 입력을 한명하고 나서 바로 나올 수 있고 2명이상이면 그 이상 입력하고 빠져 나오기 때문에 반복횟수가 정해져 있지 않는 상황에서는 For문 보다는 While문이겠지요.

주소록 조회 같은 경우는

1
2
3
for(int i=0;i<회원수;i++){
    회원[i]정보;
}

회원 주소록 조회할때는 이미 입력이 끝났고 고정된 회원수에서 회원정보를 조회하게 됩니다. 입력된 회원수만큼 검색을 해야 하잖아요 고정된 회원수 만큼 반복명령을 수행하게 됩니다. 그럴때 For문을 사용하게 되는 것이죠.

For문은 반복의 횟수가 정해져 있고 While문은 반복의 횟수가 정해져 있지 않는 제어문에 사용하기 좋다고 생각하시면 됩니다. 둘 다 반복문이지만 약간 다른 느낌으로 개념을 처음에 잡아 주세요.

그냥 이렇게 생각하세요. For문은 일정한 반복문을 수행하는데 고정된 정해진 횟수만 만큼 반복되는 제어문에 어울리고, While문은 고정되어 있지 않은 끝이 만족할 때까지 반복 횟수는 달라지는 제어문에 어울린다라고 생각하시면 되겠습니다.

엄밀이 따지면 For문도 반복 횟수를 고정 시킬 수 있고 반복 횟수를 가변적으로 코딩 할 수 있습니다. 그리고 While()문도 반복 횟수를 고정 시킬 수 있고 반복 횟수를 가변적으로 코딩 할 수 있습니다. 처음 공부하시는 분들은 둘을 동일하게 생각하고 배우게 되면은 코딩을 할 때 어떤것을 써야할 지 어려워 합니다.

처음은 이정도로 해서 차이나게 구분 지어 주세요. 참고로 For문을 이용해서 While문처럼 가변적인 횟수로 반복할 수 도 있고 While문에서 For문처럼 고정적인 횟수로 반복할 수 있는데 그렇게 코딩하면 코딩의 가독성이 떨어집니다.

그 상황은.

1
for(int i=0;i<10;i++)

이 문장 한줄 For문 안에 반복명령이 0~9까지 10번 반복수행한다는 것을 쉽게 확인 할 수 있습니다. 한줄로 for문의 반복횟수를 쉽게 이해할 수 있죠. 하지만 아래의 같은 경우를 살펴봅시다.

1
2
3
4
5
int i=0
while(i<10){
반복명령문;
i++;
}

이 경우는 i++ 증감연산이 while문 안에 있기 때문에 코딩이 짧으면 쉽게 찾을 수 있지만 코딩이 복잡하고 길면 사실 while()문이 10번 반복되는지 확인이 쉽지 않습니다. 코딩 안에서 i=i+2로 2씩 증가한다면 반복 횟수는 절반으로 줄어 들겠죠. 코딩의 가독성이 현저하게 떨어집니다. 그래서 For문으로 표현하고 While문으로 표현하지 않습니다.

반대의 경우은,

1
2
3
4
while(입력값>0){
    반복명령문;
    입력값변화;
}

이렇게 입력값변화에 따라서 while문은 반복합니다. 이 한줄로 "아! 입력값이 도달할때까지 계속 반복수행하구나!"로 가독성이 높습니다. 하지만 아래와 같은 경우를 살펴봅시다.

1
2
3
4
for(입력값;입력값>0; ){
 반복명령문;
 입력값변화;
 }

이렇게 표현을 할 수 있지만 실제 코딩을 하고나면 부자연스러워 집니다. 증감연산부분을 생략해서 빠진 느낌과 For문이 가지고 있는 고정적이고 순차적인 이미지에서 벗어나게 되고 억지로 실을 꿰맨 느낌을 갖는 코딩이 됩니다. 이 느낌은 아직 For문과 While문의 개념이 서지 않는 상태에서는 잘 모를 수 있습니다. 이부분은 나중에 코딩을 많이 연습해 보시면 그 의미를 이해가 됩니다.

3. WHILE 문



While문은 두가지 표현 형식이 있습니다.

1
2
3
4
while(입력값>0){
  명령문;
    입력값의 변화;
}

위 식은 표에서 설명 했듯이 while문의 명령문을 수행하기 전에 조건식에 만족하는지 먼저 체크합니다. 입력값이 '0'보다 큰가를 체크하고 '0'보다 크면 조건식이 참이 되고 명령문을 수행합니다. 이때 수행된 명령문에 의해 입력값은 변하게 되고 그 변화된 입력값이 다시 '0'보다 크면 방금 수행된 명령문을 다시 반복 수행하게 됩니다. 이렇게 입력값이 '0'보다 클 때 까지 계속 반복하는 제어문이라고 생각하시면 됩니다.

1
2
3
4
do{
  명령문;
    입력값의 변화;
}while(입력값>0);

위 식은 표에서 설명 했듯이 while문의 명령문을 먼저 한번은 꼭 수행한 뒤에 수행 후의 결과값을 기준으로 while()문을 계속 반복할지를 결정하는 방식입니다.

두개의 차이점을 예를 들면은,

1
2
3
while("배고파"){
    밥먹어;   
}

이렇게 처음에 "배고픈지"체크 한뒤에 배고프면은 밥을 계속 먹게하다가 어느정도 포만도에 도달하면 배가 안고파지면 While문을 빠져 나온다고 생각하시면 됩니다. 먼저 배고픈지 체크를 한 뒤에 밥을 먹는 방식이고요.

1
2
3
4
do{
 센서값 측정해;
 계산해;
}while("원하는 값 아냐!")

이렇게 표현하면은 아두이노가 센서의 값을 측정하고 나온 값이 원하는 값인지를 계산한 뒤에 원하는 값이 아니라면 다시 측정을 하고 원하는 값이 나올때까지 계속 반복하는 방식이라고 생각하시면 됩니다. 이렇게 먼저 명령을 수행한 뒤에 그결과를 토대로 다시 명령을 수행할지를 결정하는 방식입니다.

두가지 비유가 좀 이상했지만 순간 떠오른 것을 글로 쓰다 보니깐 적절한 비유가 생각이 안나네요.

혹시 이해가 안되면 다시 순서도로 보시기 바랍니다.


위 순서도에서 While문에서 조건식이 참일때 명령문을 수행하고 다시 조건식으로 돌아와서 체크합니다. 그리고 계속 True일때는 계속 반복하다가 조건식이 거짓(False)가 되면 빠져 나옵니다.


위 순서도에서 먼저 do의 중괄호 안에 명령문을 먼저 수행한 뒤에서 조건식을 체크합니다. 그리고 참이면 다시 do안에 문장을 반복 수행합니다. 조건식이 거짓(False)가 될때 비로소 빠져 나오게 됩니다.

위에서 말로 만든것처럼 이 순서도에 일상의 말로 대입해서 한번 표현해 보세요.

While 예제

이게 실험 할 예제를 마땅치 않아서 시리얼통신 예제로 loop()함수를 대신 while()함수로 표현 해보았습니다.



[ 원소스 ]

1
2
3
4
5
6
7
8
9
10
void setup() {
  Serial.begin(9600);    //시리얼 통신 9600 통신속도로 시작
}
void loop() {
   
  if (Serial.available() > 0) { //데이터가 수신되었는지 확인
    char ch = Serial.read(); //1byte 읽음
    Serial.println(ch); //1byte 읽은거 출력
  }
}

ch변수에 시리얼 통신을 통해서 들어온 1byte 데이터를 저장합니다. 그리고 저장된 값은 시리얼모니터로 다시 출력하는 프로그램입니다. 주석을 달았지만 이해가 안가셔도 괜찮습니다. 여기서 외부로 부터 시리얼통신으로 데이터를 읽고 그 데이터를 시리얼모니터로 출력하는 로직이라고만 알고 계시면 됩니다. loop()함수 안에서 이 동작을 계속 반복하잖아요. 그것을 While문으로 표현을 하면 아래와 같습니다.

[ While 소스 ]

1
2
3
4
5
6
7
8
9
10
11
void setup()
{
  Serial.begin(9600); 
   
  while(1){
    if(Serial.available() > 0) { //데이터가 수신되는지 확인
      char ch = Serial.read(); //1byte 읽음
      Serial.println(ch); //1byte 읽은거 출력
    }
  
}

원소스에서 loop()함수는 전류가 공급되면 계속 무한 반복하는 함수라고 했죠. 그 역할을 while(1)으로 표현을 해서 setup()함수에 넣어 While문을 loop()함수와 동일하게 무한 반복을 시켜버려린 코딩입니다. 원소스나 While문 소스는 동일한 동작을 수행하는 명령입니다. while(1)은 조건식이 0이 아니면 참이라고 했죠. 1이니깐 무조건 While 문은 참이되고 영원히 참이되는 문한 방복함수가 됩니다. 그래서 시리얼통신으로 계속 데이터를 읽고 그 값을 시리얼모니터로 출력이 이루어 집니다. 원소스 loop()함수와 같은 함수가 되어버린 것이죠.

[ 결과 ]


결과는 위 그림처럼 나오게 됩니다. 시리얼 통신으로 'a'를 입력 하면 그 값이 시리얼모니터로 'a'가 출력됩니다. 아두이노 예제를 들어 표현을 하다 보니깐 시리얼통신 예제를 사용했네요. 시리얼통신 명령문을 설명하자면 또, 길어지니깐 참고 자료에 링크가 걸린 시리얼 통신 포스트를 보시기 바랍니다.

다른 예제로는,

1
while (!Serial.available()); 

이런 문장이 있으면 어떤 의미일까요. 데이터 수신되었는지 확인하는데 문장입니다. Serial.available()함수는 시리얼통신으로 데이터가 수신되지 않으면 반환값은 '0'이 됩니다. 수신되면 '0'이 아닌 값이 되겠죠. 그러면 저렇게 문장을 표현하면 데이터가 입력이 될 때 까지 무한 대기하게 됩니다. 한마디로, "난! 데이터가 입력될 때 까지 기다리고 있을꺼야!"라고 생각하시면 됩니다. 데이터가 입력이 되면 While()문을 빠져나와서 다음 명령문을 수행 하겠죠.

1
2
3
4
5
6
7
8
9
10
11
12
void setup()
{
  Serial.begin(9600);  
  
void loop()
{
   while (!Serial.available()); 
   char ch = Serial.read(); //1byte 읽음
   Serial.println(ch); //1byte 읽은거 출력
  
}

표현은 이렇게 됩니다.

시리얼통신 명령문으로 예제를 들었는데 좀 어렵게 되어 버렸네요.

loop()함수를 대신 while(1)로 표현을 했고 그다음 어떤 상태가 만족할 때까지 무조건 대기하는 "while(조건식);"을 다뤄 보았습니다. 아두이노 상에서 While문을 설명하려고 하니 적절한 예제가 시리얼통신 뿐이 생각 안나네요. 시리얼통신을 이해해야 하는데 오히려 더 복잡하게 되었네요.

그냥, 1부터 입력된 수 까지의 합을 구하시오. 예제로 하겠습니다. For문의 느낌이 강해서 이런 예제를 안들려고 했는데 While문의 동작을 이해하는 차원으로 소개 합니다.

1
2
3
4
5
6
7
int input_val = 10;
int total=0;
 
while(input_val>0){
    total=total+input_val;
    input_val--;
}

코딩에서는 input_val로 변수를 선언하고 임이의 숫자 '10'을 저장 시켰습니다. 이것을 입력값이라고 정하고 나서 조건식이 '0'보다 클때 참이니깐 입력값이 '0'이될때 까지 while문은 반복하게 됩니다.
그리고 'input_val--'로 한번씩 반복명령문을 수행할 때마다 input_val값은 1씩 감소하게 됩니다. 이 의미는 10이 입력되면 10에서 부터 1씩 감소해서 0이 될때 While문을 빠져나오게 된다는 의미가 됩니다.

입력된 수 까지의 합은,

1
total = total+input_val
1
2
3
4
5
input_val이 10일때  => total = 0 + 10;
input_val이 9일때  => total = 10 + 9;
input_val이 8일때  => total = 19 + 8;
...
input_val이 1일때 => total = 54 + 1;

이렇게 입력된 수까지의 합이 구해지게 됩니다. 마지막 "total = 55"가 되겠죠. While문이 어떤 느낌으로 돌아가는지 이해가 되셨는지 모르겠네요.

위 시리얼통신 예제는 이해가 안되면 이 예제로 이해해 주세요. for문 성향이 유사해서 예제 코딩을 좀 고민 했네요.

마무리


While문의 의미를 보다 쉽게 전달해야 했는데 For문과 차별화를 너무 고집한 나머지 예제를 어려운 걸 보여 드린 것 같고 좀 어렵게 설명이 되었네요.

For문과 While문은 반복 제어문으로 유사하지만 For문은 회수가 정해진 고정된 일련의 규칙적인 반복 제어문에 어울리고 While문은 고정되지 않는 반복 제어문의 표현에 어울리는 제어문이라고 생각해 주세요.

While문과 For문의 소스들을 많이 접해 보고 따라 해보면 그 차이를 구분하여 사용하실 수 있을꺼에요.

댓글()

[아두이노] 아두이노 코딩 배열과 FOR문

IOT/아두이노기초문법|2019. 4. 12. 09:00

[아두이노] 아두이노 코딩 배열과 FOR문



오늘은 제어문 중에 FOR문에 대해 설명하고자 합니다. FOR문을 들어가기 전에 배열에 대해서 살펴보고 FOR문을 다룰 예정입니다. 배열를 먼저 다루는 이유는 FOR문 한쌍을 이루고 있으면 같이 배우면 이해하기 쉽기 때문입니다. 배열와 FOR문은 한쌍을 이룬다고 생각하시면 됩니다. 1차배열은 FOR문 한개, 2차배열은 FOR문 2개, 3차배열은 FOR문 3개로 표현이 되는데 이게 무슨 소리인지 아직 모를 수 있으나 대충 배열과 FOR문의 갯수는 같구나 정도만 생각하시고 이제 본격적으로 포스팅 하겠습니다.

1. 배열변수


지난 시간에 변수 선언은 어떻게 했나요.

int a = 10;

a라는 변수를 int형으로 선언했고 a변수에 10을 저장했습니다. 대충 이렇게 변수를 선언 했습니다. 이것을 단일변수라고 부릅니다.

그러면, 배열변수는 a라는 변수명을 갖는 것 까지는 단일변수와 같은데 차이점은 여러개의 저장공간을 하나의 변수명으로 갖을때 이 변수를 배열변수라고 부릅니다. 즉, a변수에 여러개의 값을 저장할 수 있다는 것이죠.

[ 표현식 ]


A[6]는 A라는 변수명을 가지고 6개의 저장공간을 가지고 있다는 의미가 됩니다. A[6] 배열변수에는 순서대로 "10, 20, 30, 40, 50, 60"이라는 값들이 저장하게 됩니다. 선언하면서 값이 저장될 경우 숫자인 경우는 중괄호({ })를 사용하여 위와 같이 표현 합니다. 참고로 C언에서는 첫번째 위치가 0부터 시작합니다. 그리고 A[6]으로 표현했다고 해서 0부터 시작하니깐 7개의 저장공간이 있겠구나 생각하실 수 있는데 공간은 있는게 맞지만 마지막 7번째 저장소는 사용되지 않습니다. 배열을 선언하시면 A[7]하면 7개의 저장소를 갖는데 "0~6"의 위치(index)에 값을 저장한다고만 머리속에 담아 두시면 됩니다.

여기서, 배열변수의 각 저장소 접근은 다음과 같은 방법으로 접근하게 됩니다. 배열변수에 위치(index) 값으로 배열의 위치를 지정하고 그 위치에 저장된 값을 가리키게 됩니다.

** A[index] => ?**

실제로 접근해보면,

A[0] => 10
A[1] => 20
A[2] => 30
A[3] => 40
A[4] => 50
A[5] => 60

어떻게 배열변수가 선언되고 접근되는지 아시겠지요. 시리얼모니터로 출력을 한번 해볼까요.

[ 소스 ]

1
2
3
4
5
6
7
8
9
10
11
12
void setup()
{
  Serial.begin(9600);
   
  int A[6] = {10,20,30,40,50,60};
  Serial.println(A[0]);
  Serial.println(A[1]);
  Serial.println(A[2]);
  Serial.println(A[3]);
  Serial.println(A[4]);
  Serial.println(A[5]);
}

[ 결과 ]


배열변수를 선언하고 그안에 값을 넣고 다시 그 배열변수에 저장된 값을 출력을 하였습니다. 여기까지 배열변수의 선언과 출력을 어떻게 하는지 이해 하셨죠.

그런데 자세히 살펴보세요. Serial.println()함수가 6번 반복됩니다. 만약에 배열변수에 100개 아니 1000개의 데이터가 있다면 1000개의 println()문을 코딩해야 겠죠. 정말 비효율적인 코딩이겠죠. 이 문제를 해결하기 위한 제어문이 있습니다. 배열변수가 표현되면 한쌍을 이룬다는 제어문이 있는데 그게 바로 FOR문입니다.

이제 FOR문에 대해서 본격적으로 다뤄보겠습니다.

2. FOR 문



위와 같은 형식으로 표현이 되는데 시작값은 i=0부터 조건식(i<6)이 참이 될때까지 반복 수행 한다고 생각하시면 됩니다. 좀 햇갈릴 수 있는데 다음 표현으로 이해해 주세요.


번호 순서대로 진행되는데 1번 i=0에서 시작해서 2번 조건식(i<6)이 참이면 아래 수행문 3번을 수행합니다. 그리고 나서 4번 i++ 증감연산으로 i=i+1의 표현인데 1씩 증가한다고 생각하시면 됩니다. i가 1이 증가된 값에서 다시 2번 조건식으로 참인지 거짓인지 비교하게 됩니다. 이렇게 "i<6"이 거짓이 될때까지 수행문은 반복한다고 생각하시면 됩니다.

잘 이해가 안 갈 수 있으니 예제로 들어 설명할께요. 위 소스에서 Serial.println()문이 6번 반복된다고 했잖아요. for문으로 표현을 하면 다음과 같습니다.

[ 소스 ]

1
2
3
4
5
6
7
8
9
10
void setup()
{
  Serial.begin(9600);
   
  int A[6] = {10,20,30,40,50,60};
   
  for(int i=0;i<6;i++){
    Serial.println(A[i]);   
  }
}

i는 0부터 조건식(i<6)이 참이 될 때 까지 Serial.println()함수를 반복 수행하는 코딩입니다. 여기서 A[i]라는 표현을 잘 기억해 주세요. for문은 i가 0부터 1,2,3,4,5까지 반복 수행 합니다. 그럴 때 A[i]로 표현하면은 i의 값은 0부터 1,2,3,4,5가 되겠죠.

A[i] => A[0], A[1], A[2], A[3], A[4], A[5]

A[i]은 위에서 표현한 것처럼 순서대로 가리키게 됩니다. 이 표현이 가장 중요합니다. for문을 돌리는 핵심이고요.

배열 변수 A[10]이면,

1
2
3
for(int i=0;i<10;i++){
    A[i]
}

이렇게 기계적으로 나와야 합니다.

2차배열변수 A[5][5]이면

1
2
3
4
5
for(int j=0;j<5;j++){
    for(int i=0;i<5;i++){
        A[j][i]
    }
}

이렇게 기계적으로 나와야 합니다. 배열변수이면 for문은 저렇게 기본적으로 표현한다고 생각하시면 됩니다.

위 소스에서

1
2
3
for(int i=0;i<6;i++){
  Serial.println(A[i]);   
}

이 표현을 아시겠지요. 혹시 모르시겠다면 다음 순서도를 보시기 바랍니다.


시작 하면 i=0으로 초기값이 정해지고 조건식 i<6보다 작으면 참(True)일때 아래 Serial.println()문이 수행되고 i++로 i가 1이 증가합니다. 파란색 화살표 방향으로 참일때 계속 반복 수행 됩니다. 그러다가 i가 6일 때 거짓(False)이 되면 for문을 빠져나와 종료가 됩니다.

순서도의 진행 과정을 머리속에서 그려 보세요. For문의 진행과정이니깐 꼭 숙지해 주세요.

[ 결과 ]


이제 for문이 어떻게 돌아가는지 이해가 되셨겠죠.

3. FOR문을 언제 사용할까요.


기본적으로 배열변수가 있을 때 배열변수의 위치(index)를 순서대로 접근을 할 때 사용합니다. 그리고 순서가 있는 중복되는 코딩이 있을때 for문을 사용하여 중복된 코드 부분을 줄이고자 사용합니다.

예를 들어 아두이노에서 단일변수에 저장된 값을 bit단위로 추출하고자 할때를 생각해 봅시다. p라는 단일 변수에 저장된 8bit로 구성된 1byte 값을 bitRead(입력값, 추출위치)로 1bit씩 추출이 가능한 함수입니다. 이 함수를 이용해서 총 8bit로 이루어진 1byte 데이터를 1bit씩 추출하기 위해서는 bitRead()함수가 8번 중복이 되겠죠. 이 중복된 명령을 for문으로 표현하면 다음과 같이 간단히 표현 됩니다.

1
2
3
4
const byte p=0B01110000;
for(int i=0;i<8;i++){
     bitRead(p, i);
}

[ 결과 ]


추출한 결과를 println()함수로 순서대로 출력되었네요.

간단히 정리하자면 for문은 일정한 순서가 있고 반복되는 코딩이 있을 때 사용하는 제어문이라고 생각하시면 됩니다. 명령이 일정한 순서가 있다면 "아! for문으로 표현해야지!"라고 생각을 하시면 됩니다. "배열변수가 있네! for문을 써야지!" 이렇게 바로 생각을 떠올리셔야 합니다.

4. 추가 공부


1) 문자열변수


문자배열변수는 char형으로 선언되고 "abcde"라고 단어를 그냥 표현하시면 됩니다. 참고로 5글자라고 해서 A[5]로 하시면 안됩니다. 문자의 경우는 마지막 끝나는 기호 '\0'이 저장되어야 합니다.

문자열 => 단어+'\0'

이 표현을 주의하셔야 합니다. 문장의 끝을 나타내는 기호를 저장하는 공간이 필요하니깐 문자배열변수를 선언할때 문자의 갯수와 마지막 문자의 끝 '\0' 기호를 포함된다는 것을 기억 해주세요. 그리고 각 알파벳을 접근하는 방식은 배열의 위치(index)로 접근하시면 해당 알파벳을 접근 할 수 있습니다.



위 그림에서 처럼 A[i]의 위치의 알파벳을 순서대로 출력했네요. 저장하는 것과 접근하는 것과 출력하는 것의 느낌을 잘 기억해 주세요.

2) 2차원배열


위 그림은 2차원 배열입니다. 표에 있는 것은 위치(index)라고 생각하시면 됩니다. 3차배열은 3x3x3인데 위 그림의 2차배열을 x,y 평면으로 생각한다면 3차는 여기서 z축으로 깊이 만큼 x,y 평면인 2차 배열이 3개 있는 거라고 생각하시면 됩니다. 2차 배열은 예를 들면은 이미지같은 평면적 데이터를 제어할 때 사용하고요. 3차 배열은 3차원 형태의 3D 데이터를 제어할 때 사용한다고 생각하시면 됩니다.

3차배열은 이런 느낌입니다.


2차 배열을 For 문으로 접근한다면

1
2
3
4
5
for(int j=0;j<3;j++){
    for(int i=0;i<3;i++){
        A[j][i]
    }
}

이렇게 접근을 하겠죠.

For문을 편하게 사용하실려면 다양한 예제들을 구글 검색을 통해 찾으시고 직접 코딩을 해보셔야 합니다. 왜냐면 문법을 알아도 정확히 어떤 느낌의 제어문인지 그 의미를 잘 모르시는 분들이 많아요. 그러다보니 For를 활용을 잘 못하더군요. For문은 계속 다양한 예제를 검색하셔서 직접 코딩하고 그 코딩의 의미 해석을 여러번 하셔야 For문을 자유자재로 쓰실 수 있을거에요.

3) 증감연산자

[ 증감연산자 ]
i++ => i=i+1 => i가 1씩 증가
i-- => i=i-1 => i가 1씩 감소

대충 '++'과 '--'의 의미를 아시겠지요. 그런데 이게 좀 햇갈려 하시는 분들이 많아요. 혼동하는데 과거 전산직 공무원 시험문제로 자주 출제되는 문제인데 지금은 바뀌어서 나온지는 모르겠네요. 아무튼 혼동하시는 분들이 은근 많습니다.

증감연산자는 원래는 다음과 같은 4가지로 표현됩니다.
++i => i를 먼저 1증가 시키고 나서 대입
i++ => i를 먼저 대입하고 나서 1증가
--i => i를 먼저 1감소 시키고 나서 대입
i-- => i를 먼저 대입하고 나서 1감소

1
2
3
int a =10;
int i = 2;
s1 = a + (++i);
1
2
3
int a =10;
int i = 2;
s2= a + (i++);

이 둘의 s1과 s2 값은 다릅니다.

그리고,

1
2
3
4
int a =10;
int i = 2;
s1 = a + (++i);
s2 = a + (i++);

이렇게 연달아 있으면 s1과 s2에 저장된 값은 같습니다. 그 차이점을 아시면 증감연산자를 혼동하지 않습니다. 위의 증감연산자 4가지 표현 부분의 해답이 있습니다.

1
s1 = a + (++i);

a+i가 i=2일때 i를 먼저 1을 증가시킨후 a에 더해집니다. 그래서 i은 3이 되고 s1은 13이 됩니다.
그다음 문장에서는

1
s2 = a + (i++);

a+i가 i=3이 된 상태에서 먼저 a에 더한 후에 i가 1이 증가되기 때문에 a+i은 s2에 13이 저장됩니다.

혹시 햇갈릴 수 있으니간 위 코딩을 setup()함수 안에다가 넣고 println()문으로 그 값을 출력를 해보세요.

마무리


For문에 대해서 살펴보았습니다. For문은 일정한 규칙으로 반복수행 할 때 배열변수가 있을 때 중복되는 코드들이 있을 때 그 부분을 간소화 된 코딩으로 표현을 할 때 사용합니다. 꼭 숙지해 주세요.

For문이 처음에는 잘 이해가 안될 수 있는데 계속 For문 예제를 찾아서 몇번 코딩하게 되면 이해가 되실 거라 생각합니다. 대개 C언어 For문에 대해 배울때 숙제로 많이 내주는게 구구단 프로그램 입니다. 한번 C언어로 된 구구단 프로그램을 찾아보시거나 상상력을 동원해서 표현을 직접 코딩을 해보세요. 2차 배열의 대표적인 예제이니깐요.

제 포스트 중에 종종 For문을 써왔는데 한번 잘 살펴보세요. 여러게 아두이노 핀을 제어할 때 주로 For문을 사용했고요. 패턴을 만들때 배열변수에 저장하고 그것을 출력 할 때 For문을 사용하였습니다. for문을 얼마나 자유자재로 잘 사용하느냐에 따라서 아두이노의 코딩량을 대폭 줄일 수 있으니깐 꼭 For의 의미를 이해해 주세요.

계속 머리속에서 "중복되는데 순서를 가지고 있다 For문을 써야지", "배열변수네 For문 써야지" 생각하시고 어떻게 접근하는지만 숙지하시면 For문을 사용하는데 어려움이 없을꺼에요.

마지막으로, For문으로 1부터 100까지 더한 합을 구해 보세요. '*'기호로 삼각형, 마름모 등 다양한 도형으로 출력시켜 보세요.

댓글()

[아두이노] 아두이노 코딩 IF문

IOT/아두이노기초문법|2019. 4. 11. 09:00

[아두이노] 아두이노 코딩 IF문



오늘은 제어문 중에 IF문에 대해 설명하고자 합니다. IF문은 어떻 구조이고 어떤 느낌인지 그리고 코딩할때는 어떤 느낌일 때 IF문을 사용하는지에 대해서 알아봅시다.

1. 비교연산자



위 표에 나와 있는 비교연산자의 의미를 잘 기억해 두세요. 다음에 다룰 IF문에서 조건문으로 사용하는 표현입니다.

2. IF문



IF문은 기본적으로 3개의 형식으로 구분하여 살펴 볼 수 있습니다. 위 표가 약간 가독성이 떨어지게 표현 되었는데 자세히 한번 읽어보세요. IF문은 조건식이 참이냐 거짓이냐 두가지 상태로 나눌 때 사용합니다.

예)


조건식이 밥을 먹었는지 체크했는데 참이면 "응! 먹었어"라고 답변할 것이고 거짓이면 "아니! 안먹었어!"라고 답변하게 됩니다. 이렇게 IF문은 조건식으로 참 or 거짓으로 나눌때 사용합니다. 대충 어떤 느낌인지 아시겠지요.

이제 if문 표현들을 살펴보도록 하겠습니다.

1) IF문

1
if(조건식) { 참 명령; }

if(조건식)은 조건식이 참이냐 거짓이냐 구분 할 때 사용합니다.

1
2
3
if(a>b) {
  Serial.println("a>b");
}

이 문장을 잘 살펴보세요. if문의 "a>b"의 조건식이 참이되면 if문의 영역인 중괄호({...}) 안의 문장을 수행합니다. if문만 딸랑 있고 중괄호로 되어 있으면 거짓이면 if문 안에 문장을 수행하지 않고 지나가고 참이면 if문 안의 있는 문장을 수행한다고 생각하시면 됩니다.

즉, a>b가 참이면 시리얼모니터에 "a>b"라는 문장이 출력되고 만약 a>b가 거짓이라면 a가 b보다 크지 않다는 의미로 시리얼모니터로 아무런 문장도 출력하지 않습니다. 오로지 참일때만 시리얼모니터로 "a>b"가 출력됩니다.
[ 블록 ]


블록 코드를 보시면 IF문이 "a>b"가 참일때 안에 파란블록 println()문이 수행되고 거짓일때는 그냥 지나 갑니다. 블록으로 코딩하면 이 블록은 loop()함수으로 코딩되어 무한반복하니깐 의미만 이해하시고 text창에서 아래와 같이 코딩하세요.

[ 소스 ]

1
2
3
4
5
6
7
8
9
10
void setup()
{
  Serial.begin(9600);
 
  int a = 10;
  int b = 5;
  if(a>b) {
    Serial.println("a>b");
  }
}

[ 결과 ]


여기서, 거짓일 때 b가 더 크다는 표현을 출력하고 싶지 않으세요. 그런 표현이 다음 두번째 형식입니다.

2) IF~else 문

1
2
3
4
5
6
if(a>b) {
      Serial.println("a>b");
}
else {
      Serial.println("b>a");
}

위 식에서는 else문이 추가 됩니다. if문이 조건식이 참이면 "a>b"문장이 출력되지만 조건식이 거짓이면 else이하 문장을 수행합니다. 이말은 if문이 참일때 if문을 수행하고 거짓일때는 else 문을 수행한다고 구분 지으면 됩니다.

1
2
if(조건식) { 참 }
else { 거짓 }

이 부분을 머리속에 넣어주세요.

[ 블록 ]


파란블록을 잘 보세요. "a>b"참일때 "a>b"가 출력되고 거짓일때 "b>a"가 출력됩니다. 블록으로 대충 어떤 느낌인지만 잘 구분해 주세요.

[ 소스 ]

1
2
3
4
5
6
7
8
9
10
11
12
13
void setup()
{
  Serial.begin(9600);
 
  int a = 5;
  int b = 10;
  if(a>b){
        Serial.println("a>b");
  }
  else {
        Serial.println("b>a");  
  }
}

[ 결과 ]


결과창에 소스를 보시면 약간 차이가 있는 것을 보실꺼에요 중괄호"{"와 "}"가 사라졌습니다. if문에서는 만약 수행할 명령문이 한줄일 경우는 중괄호를 생략 가능합니다. 위 소스에서는 중괄호를 넣고 결과에서는 중괄호를 생략한 이유는 우선 중괄호를 넣고 if문의 영역을 구분하고 그 영역을 참일때와 거짓일때 수행된다는 개념을 잡기 위해서 중괄호를 넣었어요. 처음 배우시는 분들은 한줄이라도 중괄호를 넣어주세요. 익숙해지면 한줄짜리를 생략하시고요. if문에 대한 의미를 이해하시는게 중요하니간 중괄호를 넣는 습관을 가져주세요.

[ 참고 사항 ]
위 코딩에는 약간 문장 상 문제가 있습니다. 뭘까요. "a>b" 크지 않다고 해서 b가 크다고 말할 수 없습니다. 그것은 "a==b"가 같을 수 있는 조건이 나오기 때문입니다.

즉,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void setup()
{
  Serial.begin(9600);
 
  int a = 5;
  int b = 10;
  if(a>b){
        Serial.println("a>b");
  }
  else if(a==b){
        Serial.println("b==a");  
  }
  else {
        Serial.println("b>a");  
  }
}

위와 같이 코딩을 해야 합니다. 한번 위와같이 코딩하시고 a, b 변수의 값을 동일한 값으로 넣어보세요. 그리고 시뮬레이터에서 실행시켜서 시리얼모니터에 어떤 문장이 수행되는지 살펴보세요.

비교 대상이 여러개 일때는 IF ~ else IF 문을 통해서 비교가 이루어집니다. 첫번째 조건식이 거짓이면 두번째 조건식을 비교해서 참 or 거짓을 결정하는데 만약에 두번째 조건식에도 거짓이면 else 문이 수행되는 표현입니다.

다음에 이 제어문에 대해서 살벼도록 하겠습니다.

3) IF ~ else IF 문

마지막 세번째 형식은 IF문이 하나 더 추가하는 방식입니다. IF문이 참과 거짓 두가지 상태뿐이 없습니다. 그런데 비교 대상이 둘 이상일때가 생기게 됩니다. 예를들어 시험을 보고 영어 점수의 학점을 주려고 할때 "A,B,C,D,E,F" 학점을 줄때 IF문으로 하면 어떻게 될까요.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
영어  = 75점;
if(영어>=90){ A학점}
else{
  if(영어>=80){ B학점}
  else {
      if(영어>=70){C학점}
        else{
          if(영어>=60){D학점}
            else {
              F학점
            }
        }
    }
}

이렇게 표현해야 됩니다. else 이하 문으로 계속 if문이 추가되지요 이렇게 복잡하게 표현하지 않고 다음과 같이 간단히 표현됩니다. 비슷한 의미인데 가독성으로 이렇게 표현 하지요.

1
2
3
4
5
6
영어  = 75점;
if(영어>=90){ A학점}
else if(영어>=80){ B학점}
else if(영어>=70){C학점}
else if(영어>=60){D학점}
else {F학점}         

만약 영어가 90이상이면 A학점
거짓이면 영어가 80점이상이면 B학점
거짓이면 영어가 70이상이면 C학점
거짓이면 영어가 60이상이면 D학점
거짓이면 F학점

어떤 입력값에 대한 여러개의 비교가 필요할 때 이렇게 IF문을 활용하게 됩니다. 이런 표현을 잘 숙지해 주세요. 여기서 다중 IF문의 의미를 잘 이해하시면 다양한 곳에서 활용이 가능합니다. 다중 IF문에서 조건식이 참과 거짓의 의미를 약간 혼동하시는 분들이 많습니다.

위 표현은
90이상
89~80
79~70
69~60
60미만

이걸 의미하는데 잘 매칭이 안되는 분들이 많은 것 같아요. 실제 영어 점수를 다르게 입력해서 어디에서 멈춰 결과가 출력되는지 체크를 여러번 하셔야 표현의 의미를 알 수 있습니다.

[ 블록 ]


[ 소스 ]

1
2
3
4
5
6
7
8
9
10
11
void setup()
{
  Serial.begin(9600);
   
  int Eng = 75;
  if (Eng >= 90) Serial.println("A");
  else if (Eng >= 80) Serial.println("B");
  else if (Eng >= 70) Serial.println("C");
  else if (Eng >= 60) Serial.println("D");
  else Serial.println("F");
}

[ 결과 ]


75니깐 C학점으로 출력되었네요. 75점이니 C+이 되어야 겠지만 암튼 대충 다중 IF문까지 코딩 해 봤습니다. 이 세가지 표현 방식을 가상시뮬레이터에서 그대로 코딩해보세요. 위 예제대로 하셔도 되고 다른 것을 생각하셔서 IF문을 적용하셔도 됩니다.

IF문 어디서 사용할까요?


코딩을 하면 IF문을 사용안하는 곳이 없습니다. 대부분 IF문을 사용합니다. 아두이노에서는 체크문으로 많이 사용합니다. 센서에서 입력된 값이 정상적으로 들어왔는지 확인할때 사용합니다. 또는 센서의 값을 읽어왔는데 그 값에 따라서 특정한 동작을 수행할 때도 사용합니다.

예를들면,

1
int val = digitalRead(센서디지털핀);

이와 같은 표현으로 센서값을 읽어올 경우에 val에는 디지털 신호 1과 0의 값을 갖게 됩니다. 그러면 IF문으로 사용하게 된다면 이렇게 표현을 할 수 있습니다.

1
2
3
4
5
6
if(val!=0){
  val은 1입니다.
}
else {
 val은 0입니다.
}

두가지 상태로 나눌 수 있게 됩니다. val 1일때 1의 문장을 수행하고 val 0일때는 0의 문장이 수행 될 수 있습니다. 만약, LED를 제어 한다면은 어떻게 될까요. LED가 13번 핀에 연결되었다고 가정를 해봅시다.

1
2
3
4
5
6
if(val!=0){
 digitalWrite(13,HIGH);
}
else {
 digitalWrite(13,LOW);
}

val이 1이면 13번 디지털핀에 HIGH(5V)상태로 5V의 전류가 공급됩니다. val이 0이면 13번 디지털핀에 LOW(0V)의 전류가 공급됩니다. 즉, 13번 핀은 센서의 값이 1일때 불이 들어오고 센서의 값이 0일때 불이 꺼지게 됩니다.

이렇게 IF문을 통해 두가지 상태를 제어할 수 있게 됩니다.

마무리


오늘은 IF문을 배웠습니다. IF문의 의미와 사용법을 잘 이해해 주세요. 아두이노에서 대개 제어를 할때 IF문을 활용하여 특정한 명령을 내리게 됩니다. 주의할 것은 IF문은 참과 거짓으로 이 두가지 상태만 있지만 그렇다고 해서 1과 0의 두가지 상태만 있는게 아닙니다. IF문은 0이 아닌 모든 것이 참이 됩니다. 햇갈리 수 있는데요. 조건식이 0이 아니면 IF문은 참이 된다는 소리가 됩니다. IF(조건식)에서 조건식의 결과가 0이 아니면 무조건 참이라는 의미가 됩니다. 1도 참이고 1.2도 참이고 2도 참이라는 소리입니다. 좀 표현이 그렇지만요 0과 아니면 조건식은 무조건 참인데 우선은 IF문은 참과 거짓으로 두가지 상태로 나뉜다고만 구별해서 사용하시고 너무 복잡한 생각은 잠시 미뤄주세요.

오늘 배운 IF문의 의미만 잘 이해하시면 아두이노를 재미있게 즐길 수 있습니다. 그냥 편하게 일상의 대화처럼 위에서 표현대로 IF ("너 밥먹었니") 참이면 "응! 먹었어!" 거짓이면 "아니! 안 먹었어!" 라고 일상의 표현들로 IF문을 사용해 보세요. 계속 그렇게 문장을 만들고 상상을 하시면 IF문은 어떤 느낌인지 느끼실 꺼에요.

댓글()

[아두이노] 아두이노 코딩 변수

IOT/아두이노기초문법|2019. 4. 10. 09:00

[아두이노] 아두이노 코딩 변수



오늘은 예전에 아두이노 변수란 포스팅을 한 적이 있습니다. 불가피하게 이번 연재한 포스트의 흐름을 이여가기 위해서 약간은 중복된 내용이 포함되게 되었네요. 참고로 오늘 내용은 변수에 대해서 간단히 살펴보고 변수를 사용하여 코딩하는 것 까지 다루도록 하겠습니다.

1. 아두이노의 변수


이 부분은 자세히 참고 포스트에서 설명했지만 처음부터 다시 시작한다는 기분으로 간단히 복습 차원으로 짧게 기초만 알아둘 것만 정리하겠습니다.


자료형은 담을 수 있는 그릇이고 변수는 그 그릇의 이름입니다. C에서는 변수를 선언할때 변수의 크기인 자료형을 선언해줘야 합니다. 사용할 변수에는 자료형이 무조건 선언된다고 암기해 주세요.

1
char A = 'a';

char은 문자형 그릇이고 A라는 이름을 가집니다. A라는 그릇에 'a'라는 문자를 저장합니다.

1
int B = 123;

int은 정수형 그릇이고 B라는 이름을 가집니다. B라는 그릇에 '123' 이라는 숫자를 저장합니다.

1
float C = 123,12;

float은 실수형 그릇이고 C라는 이름을 가집니다. C라는 그릇에 '123.12'이라는 실수를 저장합니다.


위의 그림처럼 그릇의 크기가 대충 이런 느낌이라고 생각하시면 됩니다.


자료형 범위

  • 1 Bytes => -128~127
  • 2 Bytes => -32768~32767
  • 4 Bytes => -2147483648 ~2.147.483.647

그냥 작은수, 큰수, 실수 이정도로만 생각하시고 넘어가세요. 나중에 코딩하다보면 자연스럽게 자료형을 뭘 써야지 알게 됩니다. 아두이노에서 핀번호를 지정한다면 byte로 하면 가장 알맞겠죠. 그리고 아날로그 출력은 0~255 사이의 값이니깐 대충 int 변수겠죠. 아날로그입력도 0~1024니깐 int 정도면 되겠죠. 뭔가 복잡한 실수 계산이 들어가면 float 실수형으로 선언하면 되겠죠.

간단히, 작은 정수는 byte, 큰 정수면 int or long , 실수면 float or double 형으로 선택하셔서 자료형을 선언한다고 머리속에 넣어두세요.

그외,

1
2
3
4
boolean D = true; //참거짓에 사용되는 자료형
byte E =1//1byte 크기의 자료형
long F = 123; //int자료형과 같은데 그릇의 크기가 4byte으로 int형보다 더 크다.
double G =123,23; //float형보다 더 큰 그릇인데 아두이노에서 4byte 크기로 동일하게 나오네요.

결론, 변수는 값을 담는 그릇이라고 생각하시면 됩니다.

[ 코딩 ]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void setup()
{
  Serial.begin(9600);
 
  char A = 'a';
  int B =123;
  float C =123.23;
 
  Serial.println(A);
  Serial.println(B);
  Serial.println(C); 
 
}
void loop()
}

결과만 보기 위해서 처음 한번만 수행하는 setup()함수에 코딩했습니다. 각 자료형 별 변수들에 저장된 값들을 시리얼모니터로 출력을 하면 아래와 같습니다.


변수에 값을 저장할 수 있고 그 값을 이제 시리얼모니터에 출력할 수 있게 되었습니다. 여기까지 따라오셨나요.

2. 사칙연산 코딩


프로그램 코딩은 수학과 아주 밀접한 관계가 있습니다. 덧셈을 하나 생각해 볼까요.

예) 임의의 두 수 a, b를 더하는 식을 만드세요.

1
S = a + b

위 식은 초등학교 기초 수학시간에 배웠을 꺼에요. 이 표현은 코딩에서는 어떻게 표현되냐면 변경없이 그대로 적용됩니다.

1
S = a + b;

이렇게 세미콜론(;)만 뒤에 붙이고 그대로 코딩하시면 됩니다. 바뀐건 세미콜론(;) 하나 추가된 것 밖에 없죠. 어떻게 보면 코딩은 우리가 처리하고자 하는 명령들을 수학공식과 같은 형식으로 식을 만들고 그 식을 그대로 코딩하는 거라고 생각하시면 쉬울꺼에요.

그러면, 수학 사칙연산 프로그램을 만들어 볼까요.


사칙연산 표현을 그대로 그냥 동일하게 표현 하시면 됩니다. 덧셈, 뺄셈, 곱셈, 나눗셈을 하고 그 값을 저장하는 변수만 선언해주면 됩니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
void setup()
{
  Serial.begin(9600);
 
  int A = 10;
  int B = 2;
  int addAB = 0;
  int subAB = 0;
  int mulAB = 0;
  int divAB = 0;
   
  addAB = A + B;
  subAB = A - B;
  mulAB = A * B;
  divAB = A / B;
   
  Serial.println(addAB);
  Serial.println(subAB);
  Serial.println(mulAB); 
  Serial.println(divAB); 
 
}

addAB, subAB, mulAB, divAB라는 4개의 저장변수를 선언했고 해당 식을 수행하면 아래과 같은 결과가 시리얼 모니터로 출력됩니다.


1
2
3
4
10+2 = 12
10-2 = 8
10*2 = 20
10/2 = 5

여기까지 따라 오셨나요.

3. 삼각형의 넓이 구하는 프로그램을 만들자



  • 공식 : S= 1/2ah

수학시간에 배우셨죠. 삼각형의 넓이 공식을 떠올려 보세요. 위 공식이 프로그램 전부입니다. 이걸 코딩을 하면

1
S= (float)1/2*a*h;

세미콜론(;)만 붙이고 곱셈부분은 곱셈기호(*)를 붙여 줍니다. 그리고 계산된 값이 실수 형태로 저장될려면 (float)을 붙여줘야 합니다. 그 이유는 정수/정수는 결과가 정수가 되기 때문에 실수형으로 저장하기 위해서는 이 문장을 표현해야 합니다. 그냥 단순한 수학 공식인데 세미콜론(;)을 붙이니깐 프로그램 로직 명령어가 되었네요. 이렇게 수학적인 표현을 하는게 코딩입니다. 이건 순수 수학의 표현이고요. 이런 의미로 일상의 표현 특정한 동작의 표현을 공식으로 만들고 그걸 그대로 코딩하면 프로그램 코딩이 됩니다. 어렵게 생각하실 필요가 없어요.

실제 돌아가는걸 확인해 볼까요. 공식을 보면 필요한 변수가 3개 입니다. 밑변, 높이, 삼각형넓이 변수가 필요합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void setup()
{
  Serial.begin(9600);
 
  int a = 10;
  int h = 5;
  float S =0;
     
    //삼각형 넓이 공식
    S = (float) 1/2*a*h;
     
  Serial.print("base : ");
  Serial.println(a);
  Serial.print("height  : ");
  Serial.println(h);
  Serial.println("Area of triangles : "); 
  Serial.println(S); 
 
}


[ 결과 ]


밑변(a)와 높이(h)의 값만 바꾸면 모든 삼각형의 넓이를 구할 수 있는 프로그램을 완성했습니다. 그렇게 어렵지 않죠. 이렇게 식을 만들고 그 식을 그대로 코딩하면 그게 프로그램이 됩니다. 이 의미만 오늘 기억하시면 됩니다.

1
(float) 1/2 * a * b

이부분이 약간 햇갈릴 수 있기 때문에 다시 설명합니다. C언어에서는 정수/정수는 정수가 출력됩니다. 즉, 실수값이 나와도 그 값은 정수형으로 나와버린다는 것이죠. 실수값을 얻기 위해서는 다음과 같은 표현으로 식을 만들어야 합니다.

정수/실수 or 실수/정수 or (flaot) 정수/정수

이렇게 세가지 표현 중에서 하나를 선택해서 표현 하시면 됩니다. 입력값이 a, h가 정수임으로 위 나눗셈이 들어간 식에서는 저장변수가 실수변수여서 식 앞에 (float)형으로 선언하게 된 것이죠. 이 표현은 해당 자료형으로 저장하겠되는 의미입니다.

여러분들도 한번 생각 나는 수학공식을 제가 코딩했던 스타일로 표현을 해 보세요. 위에서 사칙연산을 수행한 계산 값을 출력했는데 뭘 계산한지 모르잖아요 보기 좋게 하기 위해서 이번에는 print(), println()함수로 어떤 값들을 계산했고 결과가 뭔지 출력문에 담았습니다. 이런 표현들을 꼭 숙지하시고 이해해 주세요.

이렇게 해서 대충 변수를 사용하는 방식을 알아 봤습니다.

4. 코딩의 과정



  • 입력단계 : 변수를 선언해서 그 값을 직접 입력하거나 아니면 외부로 부터 값을 입력을 받는다.
  • 처리단계 : 입력된 값을 위에서 수행한 사칙연산과 같은 원하는 동작의 계산식을 만든다.
  • 출력단계 : 결과를 외부로 보내거나 특정한 위치에 출력한다.

무조건 이 과정으로 코딩을 한다는 점 꼭 기억해 주세요. 그러면 아두이노를 생각해 볼까요. 예를 든다면 아두이노에서 센서의 값을 읽으면 입력부분이 되고 그 입력된 센서의 값을 특정한 동작을 수행하도록 하는 표현식(계산식)이 처리단계가 됩니다. 그 결과를 실질적으로 보여지는게 출력 단계입니다.

여러분들이 이제 코딩을 하게 되면 "입력 ->처리->출력" 이걸 머리속에 꼭 담고 코딩를 해주세요.

어떤 값을 받을 것인지!
그 값을 받았으면 어떻게 처리할 것인지!
최종적으로 어떻게 보여질 것인지!

이 개념을 머리속에 담고 코딩을 전개해 나가시면 됩니다.

마무리


오늘은 변수의 자료형인 문자형, 정수형, 실수형 표현을 배웠으며 그 변수를 이용하여 수학공식을 프로그램 코딩을 해 보았습니다. 그리고 마지막으로 코딩은 어떤식으로 이루어지는지 그 과정을 간단히 살펴보았습니다.

프로그램 언어를 배우면 젤 먼저 사칙연산을 코딩하게 됩니다. 그리고 더 관심이 생기면 이 원리를 이용해서 공업용 계산기를 만들기도 합니다. 저도 처음에 사칙연산을 배우고 나서 젤 먼저 공업용 계산기를 코딩을 해 봤고 더 나아가 전자 회로로 계산기도 응용해서 표현을 해 봤네요. 뭔가 원리를 배우면 이걸 어디에다 써먹을까 상상의 나래를 잘 펼치거든요. 여러분들도 오늘 사칙연산과 삼각형의 넓이 구하는 식을 코딩으로 배웠습니다.
여러분들은 이걸 토대로 상상의 나래를 펼쳐보세요. 난 이 원리로 뭘 만들어 볼까하고 계속 상상을 하셔야 응용력이 생겨 새로운것을 만들 수 있게 됩니다.

더 내용을 추가 할 부분이 있었지만 너무 많이 설명하고 깊게 들어가면 오히려 역효과가 날 것 같아서 이부분은 이정도로 마무리 하겠습니다.

프로그램 코딩을 너무 어렵게 생각하지 마시고 그냥 수학식을 하나 만든다고 생각하시고 그 식을 그대로 코딩하면 그게 코딩이 되니깐 어렵게 생각 안하셔도 돼요.

댓글()

[아두이노] 아두이노 코딩 입문

IOT/아두이노기초문법|2019. 4. 9. 09:00

[아두이노] 아두이노 코딩 입문



오늘은 간단히 코딩에 대해 포스팅을 하겠습니다. 사실 비전공자분들은 코딩이 약간 외계어처럼 느껴지시는 분들이 있는 것 같아서 원래는 이런 내용을 초창기 포스트에서 소개 했었야 했는데 늦게 포스팅을 하게 되었네요. 아두이노 코딩을 할때 알아야 기초적인 문법만 포스팅을 하도록 하겠습니다. 이전에 포스팅한 내용들이 일부 중복되지만 그래요 한번에 기초적인 문법을 묶어서 설명하는게 나을 것 같아서 몇일간 코딩에 관련해서 포스팅을 하도록 하겠습니다.

1. 코딩의 준비


[ 아두이노 ]

[ 동작 ]


아두이노에 전원이 공급되면 젤 먼저 setup()함수가 한번 수행되고 그다음 loop()함수가 반복 수행됩니다. 간단히 아두이노에 전류가 공급되면 setup()함수를 수행후 loop()함수가 반복 호출된다는 정도만 머리속에 넣어 두세요.

2. "Hello World" 출력하기


[ 메인창 ]



아두이노를 편집창으로 드래그 하고 나면 오른쪽 항목에 Code 클릭을 해주세요.



기본 베이스 블록을 위 그림처럼 휴지통이나 블록이 있는 쪽으로 드래그 하면 블록은 삭제됩니다.

[ 블록 ]



시리얼통신 블록 :



오른쪽 항목에 Code가 있는데 그걸 선택하면 위 그림처럼 세가지 방식을 선택 할 수 있습니다. 여기서 두번째 줄에 있는 Bloocks+Text를 선택하십시오. 그리고 Output 항목에서 아래 print 블록을 오른쪽으로 드래그 해주세요. Output는 파란색 블록에 있습니다. 그러면 아래와 같은 코딩이 같이 오른쪽에 text창에 나타납니다. print 블록은 Serial 블록이여서 Serial 관련 기본 세팅이 코딩으로 보여집니다. 사실 블록으로 간단히 표현하면 코딩을 구지 할 필요 없습니다. 하지만 블록을 사용하시면 표현하실 때 약간 제약을 받기 때문에 제 경우는 블록보다 Text 코딩을 선호 합니다.

블록 하나를 배치했을때 아래 코딩이 자동으로 생성됩니다.

[ 코딩 ]

1
2
3
4
5
6
7
8
9
10
11
void setup()
{
  Serial.begin(9600); //시리얼통신 시작
 
}
 
void loop()
{
  Serial.println("hello world"); //시리얼모니터로 출력
  delay(10);
}

실행을 하기 위해서 아두이노 IDE 시뮬레이터 창을 띄워야겠죠.


위 그림을 보시면 Serial Monitor를 누르면 모니터창이 나타납니다.

시뮬레이터 실행을 누르시면


"hello world"가 계속 출력됩니다.

1
println("내용");

위 명령문으로 내용을 시리얼모니터로 출력하라는 명령입니다. 이제 C문법을 배울때 그 결과를 이 함수를 사용하여 출력할 예정입니다.

3. Text 모드로 변경


현재상태에서 Text모드로 바꾸시면 아래와 같이 블록만 없어지고 Serial 출력코딩은 그대로 남습니다. 그러면 loo()함수안에 있는 명령들을 아래 그림처럼 setup()함수로 옮겨주세요.


그리고 실행을 누르면 다음과 같이 딱 한번만 수행합니다.


setup()함수와 loop()함수의 차이점을 구별하셨나요. loop() "hello World"문장을 계속 출력하지만 setup()함수는 딱한번 출력하는 것을 확인 하실 수 있을거에요.

여기까지해서 기본 세팅은 끝났네요. 아두이노를 이용해서 "hello World"란 문장으로 시리얼모니터에 출력할 수 있게 되었습니다. 이제 코딩을 하면 그결과를 시리얼모니터로 출력할 수 있겠죠. 여기까지 따라 오셨으면 코딩의 절반을 배운 거나 다름 없습니다.

이제는 코딩 문법을 하나씩 기초 위주로 배워보도록 하겠습니다. 사실 C언어를 C컴파일를 사용해서 C공부를 하면 좋지만 아두이노 환경에서 아두이노와 친근감을 가지기 위해서 아두이노 상에서 프로그램 언어 공부를 배워 보도록 하겠습니다.

4. 시리얼모니터 출력 함수


시리얼모니터에 출력하는 함수가 print(), println(), write()가 있습니다. write()함수를 제외한 print(), println()함수만 주로 사용할 예정입니다. 그래서 이 둘의 차이점만 구별해 주세요.

  • print("내용") : 해당 내용을 출력하고 커서의 위치는 출력된 해당 라인에 계속 머물어 있게 됩니다.
  • println("내용") : 해당 내용을 출력하고 커서의 위치는 새로운 라인으로 이동하게 됩니다.

print()함수의 예제



위 그림을 보시면 두개의 print()함수를 사용했습니다. 첫번째 "hello "문장을 출력하고 커서는 현재라인에 머물어 있기 때문에 다음 "world"라는 문장이 이여서 출력됩니다. 그런데 또 print()함수를 사용한다면 계속 현재라인에서 이여서 출력되겠죠. 그래서 마지막 출력문에는 println()함수를 사용해서 출력라인의 끝을 표현해야 합니다.

println()함수의 예제



위 그림을 보시면 두개의 println()함수를 사용했습니다. 첫번째 "hello "문장을 출력하고 커서는 새로운 라인으로 이동한다고 했죠. 그래서 "world"문장이 새로운 라인에 출력됩니다. 여기서 두번째 함수도 println()함수임으로 3번째 새로운 라인으로 커서가 옮겨져 있겠죠.

출력함수 올바름 사용



위 그림처럼 두이상의 여러개 내용을 한 라인에 모두 출력을 해야 할경우는 print()문으로 표현하다가 마지막 내용에서는 println()으로 표현해서 커서를 옮겨주는 코딩을 하셔야 합니다.

참고로, "내용"에서 안에 공백문자도 인식을 하니깐 공백(스페이스바) 효과도 넣어 띄워쓰기를 할 수 있습니다.

이 차이점만 이해하시고 출발하시면 되겠습니다.

추가 주의사항

세미클론(;) 이라는 기호가 있습니다. 명령문의 끝을 나타내는 기호로 편하게 명령어 수행하라 정도로만 이해해 두세요. 명령문에는 무조건 세미클론이 붙습니다. 이게 생략되면 에러가 발생하니깐 꼭 기억해 주세요.

'{' 와 '}' 괄호는 함수에는 함수의 영역을 표시하는 기호입니다. setup()함수가 수행하면 이 괄호가 시작하는데 부터 괄호가 끝나는 데까지의 내용을 기계가 읽고 수행합니다. 간단히 "괄호는 영역이다!" 정도로 이해하시면 되겠습니다.

마무리


오늘은 코딩의 첫 관문인 시리얼모니터에 명령한 결과를 출력해보는 시간으로 채웠습니다. 이정도만 이해하시면 프로그램언어를 절반정도 이해하셨다고 보면 될꺼에요.
처음하시는분들은 어색할 수 있지만 setup(), loop()함수가 있는데 전원이 공급되면 setup()이 젤 먼저 호출되어 수행한 뒤에 loop()함수가 그 다음에 반복해서 호출 수행한다 정도정도만 이해하세요.
그리고, 오늘 소개한 Serial.print(), Serial.println()함수만 이해하시면 프로그램언어 공부 절반을 배운것이니깐 꼭 원리를 기억해 주세요. 출력함수 하나만 이해해도 프로그램언어를 절반 배운거에요.

원래는 내용이 더 있는데 너무 길게 빼면 그렇 것 같아서 여기서 마무리 합니다.



댓글()

[아두이노] 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로 정확히 출력됩니다. 감안하시고 보세요.


마무리


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

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


댓글()

[아두이노] 사운드센서로 그래픽 이퀄라이저 표현

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

[아두이노] 사운드센서로 그래픽 이퀄라이저 표현



어제 마이크사운드 감지센서를 통해서 센서의 값의 초기값 조정하는 것과 읽는 법을 배웠습니다. 오늘은 마이크사운드 감시센서을 이용하여 소리를 NeoPixel로 출력하는 실험을 해보도록 하겠습니다. "아두이노 이퀄라이저" 키워드로 찾으시면 화려한 표현들을 많이 보실 수 있을거에요. 오늘 포스팅은 그런 화려한 표현이 아닌 간단히 어떻게 소리를 NeoPixel로 표현을 하는지에 대한 원리를 이해하는 시간으로 채우겠습니다. 화려한 그래픽 이퀄라이저를 상상했다면 아마 실망하실지 모르겠군요.

1. 마이크사운드 감지센서와 3색LED




3색 LED에 대한 설명은 아래 링크된 포스트로 가셔서 잠깐 살펴보세요.
[아두이노] 3색 LED 제어



마이크사운드 감지센서는 어제 포스트인 아래 링크로 다시 복습를 해주세요.
[아두이노] 마이크사운드 감지센서 제어(아두이노)

입력은 마이크르사운드 감지센서를 사용하고 출력은 NeoPixel이 없는 관계로 비슷한 3색 LED를 이용하여 실험이 이루어집니다.

2. 회로도 구성


  • 준비물 : 마이크사운드 감지센서 1개, 3색 LED 1개, 저항 220옴 3개, 아두이노우노
  • 내용 : 사운센서랑 3색 LED를 사용할 핀을 원하는 위치에 배치해 보자.

가상회로도 이미지에서 저항은 실수 1k옴으로 그려졌네요. 혼동하시면 안되고 정상적으로 220옴을 실제로 사용하셔야 합니다. 이미지의 저항색깔을 보시고 혼동하시면 안됩니다.


RGB 색상핀은 아날로그 출력을 해야하기 때문에 PWM핀들 중에 9,10,11번 핀을 선택했습니다. 그리고 마이크사운드 감지센서의 값을 읽는 아날로그 핀은 이전시간과 동일한 A0핀을 선택했습니다.

3. 코딩


  • 사용함수 : analogRead(), PinMode(), analogWrite(), map(), randomSeed(), random()
  • 내용 : 마이크사운드 감지센서의 소리의 값을 기준으로 RGB 색상을 만들어 그래픽 이퀄라이저 효과를 표현하자.

[ 기본 소스 ] : 마이크사운드 감지센서를 이용한 소리의 최소값과 최대값 출력

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
int m_min = 26;
int m_max = 0;
 
void setup()
{
  Serial.begin(9600);
}
void loop()
{
  int SoundSensor =analogRead(A0);
    if(SoundSensor<m_min) m_min=SoundSensor;
    if(SoundSensor>m_max) m_max=SoundSensor;
     
    Serial.print(m_min);   
    Serial.print("  :  ");
  Serial.println(m_max);   
  delay(50);
}

이전 시간에 실험한 소스입니다. 이 소스를 기반으로 접근해 보겠습니다.

우선 RGB핀을 사용하기 때문에 RGB핀 변수를 만들어야 겠죠.

1
2
3
const int RedPin = 9;
const int GreenPin = 10;
const int BluePin = 11;

그 다음에는 RGB핀들은 출력모드입니다. 그러면 setup()함수에서 "출력모드로 사용할꺼야!" 하고 선언해야겠죠.

1
2
3
4
5
6
7
8
void setup()
{
  Serial.begin(9600);
  
  pinMode(RedPin, OUTPUT);
  pinMode(GreenPin, OUTPUT);
  pinMode(BluePin, OUTPUT);
}

이제 소리를 어떤 기준으로 해서 색을 만들면 좋을지 생각해보세요. 마이크사운드 감지센서의 아날로그 신호 0~1023 값을 아두이노가 읽는다고 했죠. 그러면 0~1023값을 쪼개서 색으로 나타내면 되겠다고 처음에 다들 여기서 부터 출발합니다. 여기서, RGB핀 출력은 PWM핀으로 아날로그 0~255 출력을 낼 수 있습니다. 0(0V)이고 255(5V)입니다. 그러면 마이크사운드 감지센서에서 읽은 0~1023 값을 아날로그 출력 0~255값에 매핑 시켜서 출력시키면 되겠다고 생각 하실꺼에요.

1
2
int SoundSenser =analogRead(A0);
int SoundColor = map(SoundSenser,0,1023,0,255);

map()함수는 다시 복습을 하면

  • map(입력값,입력최소값,입력최대값,출력최소값,출력최대값) : 입력최소~최대범위에서 입력값이 출력 최소~최대범위에 어느정도 위치인지 그 출력위치값을 반환합니다.

예) map(입력값,0,100,0,10) 이면, 입력값이 10일때 출력값는 1이 됩니다. 입력값이 20일때 출력값은 2가 출력됩니다. 즉, 이말은 입력범위를 출력범위에 맞춰서 출력된다고 생각하시면 됩니다. 일정한 간격 비율에 맞춰서 출력값에 매칭 된다고 생각하면 될듯요. 0~10사이의 값은 1로 매칭되고 11~20사이의 값은 2로 매칭이 되고 이렇게 생각하시면 될 듯 하네요. 입력범위에 대한 입력값의 위치가 출력범위에서 어느정도의 위치가 출력위치인지 그 출력위치의 값을 반환해주는 함수라고 생각하시면 됩니다.

1
2
3
4
Serial.println(SoundSenser);
analogWrite(RedPin,random(SoundColor));
analogWrite(GreenPin,random(SoundColor));
analogWrite(BluePin,random(SoundColor));

우선 소리값을 그냥 color 값으로 출력한다면 소리값에 0~255사의 값이 RGB 색의 출력된 값이 되고 출력명령은 analogWrite()함수로 color를 출력하게 됩니다. 이렇게 하면 코딩이 끝났다고 생각하실꺼에요. 한번 돌려보세요 어떤 현상이 발생하는지요. 마이크사운드센서에 소리를 입력하기도 전에 3색 LED를 막 깜박일꺼에요.

그 이유가 뭘까요. 지난시간에 공부하셨으면 쉽게 찾을 수 있을꺼에요. 지난시간에 마이크사운드 감지센서는 초기값으로 일정한 전류가 발생한다고 했죠. 같은 종류라도 센서라도 초기의 센서에 흐르는 전류의 초기값은 제각각 입니다. 제가 사용하는 Sensor는 처음 46이 초기값이였고, 가변저항기를 돌려서 26정도로 맞췄습니다. 여기서 아무런 소리를 입력하기 전 초기 상태에서 이미 소리값이 일정 수치가 읽어지니깐 자연히 그 초기값의 색이 RGB LED에 출력이 되는 것이죠 제 경우 초기값이 26이라면 26에 대한 색이 출력되더군요.

처음 마이크 상태는 0에서 출발해야 하는데 그렇게 안되겠죠. 그럼 그렇게 0에서 출발하도록 만들어줘야 합니다. 바로 map()함수를 수정하시면 됩니다.

1
int SoundColor = map(SoundSenser,26,1023,0,255);

입력 최소값을 26으로 잡아주면 간단히 해결됩니다. 그런데 두번째 문제가 발생합니다. 과연 뭘까요. 어제 포스트를 읽어봤다면 찾을 수 있을꺼에요. 소리가 입력되고 다시 입력이 안되면 전기신호를 감소합니다. 중요한 것은 소리가 입력될때는 전기신호가 커졌다가 입력이 안될때는 전기신호가 작아지는데 초기값보다 마이너스(-)로 빠져버리는 현상이 발생한다고 했죠. 그러면 어떻게 될까요. 그 이상한 값에 의해서 색이 출력되어버립니다. 최소값 문제가 발생합니다.

그걸 해결하기 위해서 어제 최소값 구하는 로직을 이용할꺼에요.

1
if(SoundSensor<26) SoundSensor=26;

만약에 26보다 작은 전기신호가 온다면 26에 수렴되게 만들면 됩니다. 그리고 또 어떤 문제가 발생할까요. 아두이노에서 사용되는 마이크사운드 감지센서를 아주 싸고 질이 떨어집니다. 결론은 소리감지에 좀 문제가 있습니다. 어느정도 큰소리라고 생각했지만 측정되는 수치는 그렇게 높지 않습니다. 거의 마이크를 두둘겨야 비로소 일정 수치까지 올라갑니다. 말하자면 대부분의 소리가 낮은 전기신호값을 가지고 있으면 일부 소리만 큰전기신호값으로 입력으로 들어옵니다. 결론은 거의 동작을 안하는 것처럼 보이고 색상도 아주 낮게 보입니다. 그래픽 이퀄라이저 효과를 뚜렷하게 표현하기 어렵다는 것이죠.

해결책으로는 낮은 값을 기준으로 색의 값을 재배열 하면 됩니다.

1
if(SoundSensor>300) SoundSensor=300;

이렇게 300이상의 소리는 그냥 300에 수렴하게 만들면 됩니다. 대충 아래 그래프처럼 입력값의 범위를 강제적으로 정하면 됩니다. 가로축는 마이크사운드 감지센서의 측정값이고 세로축은 소리입력허용 수치입니다. 26~300사이의 값으로 소리입력범위가 잡으시면 됩니다.


그러면, map()함수는 어떻게 될까요.

1
int SoundColor = map(SoundSenser,26,300,0,255);

이렇게 해서 26~300사이의 값을 입력범위로 해서 0~255의 색상값으로 출력하겠다고 표현하시면 됩니다.
여기서, 어제 배웠던 최소값과 최대값 구한 로직을 기본소스로 우선 보여준 이유가 이 로직으로 이런 2가지의 문제를 해결하기 위해서 입니다.

이렇게하면 소리에 대한 밝기만 표현되기 때문에 그래픽 이퀄라이저라고 보기 어렵습니다. 그래서 밝기와 더불어 다양한 색을 만들어보는 시도를 해보겠습니다. 그 방법으로 random()함수를 사용하겠습니다.

1
2
3
4
5
6
7
void setup()
{
  randomSeed(analogRead(A1));
}
void loop(){
    random(SoundColor);
}

A0은 마이크사운드 감지센서로 사용되니깐 A1은 랜덤함수 초기값으로 세팅하겠습니다. 여기서 아날로그핀값을 읽어오기만 하는게 난수초기세팅인 이유는 뭘까요. 그 이유는 아두이노는 핀마다 미세한 전류가 자체적으로 흐르고 있습니다. 사용을 안하더라고 핀마다 작게 전류가 흘러서 그 전류는 미세하지만 랜덤한 전류의 양을 가지고 있어서 그 값을 기준으로 난수를 세팅하면 랜덤값을 추출할 수 있습니다.
그리고 random(255)함수면 255이 max값으로 임의의 난수를 만들어 냅니다. 이 값을 RGB 마다 random()함수로 색의 난수값을 만들어내면 소리에 대한 다양한 색을 만들 수 있겠죠.

종합해보면.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
const int RedPin = 9;
const int GreenPin = 10;
const int BluePin = 11;
 
void setup()
{
  Serial.begin(9600);
  randomSeed(analogRead(A1));
  pinMode(RedPin, OUTPUT);
  pinMode(GreenPin, OUTPUT);
  pinMode(BluePin, OUTPUT);
}
 
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);
   
  Serial.println(SoundColor);
  analogWrite(RedPin,random(SoundColor));
  analogWrite(GreenPin,random(SoundColor));
  analogWrite(BluePin,random(SoundColor));
     
  delay(50);
}

4. 결과



음악으로 실험해야하는데 음악은 저작권 때문에 단지 손으로 마이크사운드 감지센서에 자극을 줘서 출력실험을 했네요. 보면은 터치센서인줄 착각할 수 있는 장면이네요.

참고로 위 소스대로 하시면 그렇게까지 그래픽 이퀄라이저 효과를 얻을 수 없습니다. 결함이 있는 소스입니다. 그 이유가 뭘까요. 바로 random()함수를 단순히 측정한 소리값을 max값으로 넣었기 때문입니다. 이렇게 표현하면은 소리에 따라 다양한 색을 만들어 내기는 합니다. 하지만 300의 소리가 입력되었어도 100이 나올수 있고 150의 소리가 입력되어도 100이 나올 수 있다는 것이죠. 이말은 색상을 일정한 패턴 범위에 맞추지 못했다는 결함적 소스입니다.

그런데 왜 수정을 안하고 여기서 멈추었냐면 이제부터서 여러분들이 수정해 나갈 차례입니다. 기본 베이스는 다 주어졌습니다. 소리의 초기상태를 읽을 수 있고 최소와 최대값을 정할 수 있게 되었고 map()으로 색의 값의 범위를 정할 수 있게 되었습니다. random()함수로 색을 랜덤하게 만들어 낼 수 있게 되었습니다. 여러분들은 이제 소리를 좀 더 디테일적으로 나누는 작업과 그 나누었을 때 색을 랜덤함수로 범위를 정할지 아니면 고정된 색값으로 하고 밝기만 변화를 줄지의 선택은 여러분의 몫입니다.

참고로, 이렇게 코딩을 해도 실제 돌려보면 색이 화려하지 않고 낮은 밝기와 색값으로 출력됩니다. 그것은 바로 마이크사운드 감지센서의 문제입니다. 좀 큰소리를 말해도 실질적으로 측정되는 수치는 그렇게 높지 않습니다. 그렇기 때문에 미세한 1의 변화에도 색의 밝기값을 좀 더 큰값 범위를 기준으로 해서 정해보시는 것을 추천드려요.

5. 가상시뮬레이터에서 느낌만 실험


그냥 실제로만 하면 뭔가 아쉬울 것 같아서 느낌만 살펴서 가상시뮬레이터로 실험해 해보세요. 위에 공개회로도에 링크 걸렸으니깐 돌려만 보세요.

1) 가변저항기와 3색 LED


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
const int RedPin = 9;
const int GreenPin = 10;
const int BluePin = 11;
 
void setup()
{
  Serial.begin(9600);
  randomSeed(analogRead(A1));
  pinMode(RedPin, OUTPUT);
  pinMode(GreenPin, OUTPUT);
  pinMode(BluePin, OUTPUT);
}
 
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);
   
  Serial.println(SoundColor);
  analogWrite(RedPin,random(SoundColor));
  analogWrite(GreenPin,random(SoundColor));
  analogWrite(BluePin,random(SoundColor));
     
  delay(50);
}

2) 가변저항기와 NeoPixel


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#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();
}

3) 두가지 실험 결과

위 3색 LED와 NeoPixel을 가변저항기가 마이크사운드 감지센서라고 상상하고 실험한 영상입니다.


마무리


하다보니깐 길어졌네요. 원래는 추가적으로 NeoPixl 바 모양 향태로 음악을 들을때 사운드 그래픽 바 모양을 아실꺼에요. 그것도 추가 설명할려고 했는데 그건 못하게 되었네요. 내일 또 이여서 하면 너무 길게 연장 될 것 같아서 여러분들의 상상력으로 표현을 해보세요.

혹시, 사운드 그래픽 바를로 출력하고자 하고자 한다면 참고해주세요. NeoPixel을 예전 포스팅에 어떻게 표현을 했었죠. 배열 행태로 표현했었습니다. 그 배열을 사운드 그래픽 바의 위치라고 상상하고 소리의 크기에 따라서 배열로 몇번째 배열까지 불이 들어오게 할건지 그 간격만 정하시면 됩니다. 그리고 해당 NeoPixel의 색 값은 random()함수로 표현하시거나 고정값을 하셔도 됩니다. 쉽게 말해서, 소리가 1~10까지 있으면 3이라는 신호값이 발생하면은 NeoPixel를 3번째까지 불이 들어오게 하면 됩니다. 이렇게 소리의 크기 값에 neoPixel의 위치를 정하면 됩니다. 한번 연구를 해보세요.

오늘도 어떻게 표현하면 더 자연스럽고 화려할지를 상상의 나래를 펼쳐 보세요.


댓글()

[아두이노] 마이크사운드 감지센서 제어

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

[아두이노] 마이크사운드 감지센서 제어



오늘은 어제 설명한 Sensor 읽기에서 가상시뮬에이터에서 제공되지 않는 실제 Sensor를 다뤄보도록 하겠습니다. 실험에 사용되는 마이크사운드 감지센서는 D0, A0로 디지털신호와 아날로그신호를 출력하기 때문에 해당 핀에 맞는 함수를 사용하여 읽으시면 됩니다. 실험에서는 아날로그신호를 읽는 방식으로 진행되니깐 잘 참고를 해주세요.

1. 마이크사운드 감지센서



사진이 깔끔하게 나오지 않고 약간 좀 떨려서 찍혔네요. 좀 흐려도 자세히 보시면 대충 구분이 되실거라 믿고 진행하겠습니다. 보시면 파란색 나사이 있을꺼에요. 가변저항기로 전류의 출력을 조절하는 장치입니다. 어느쪽 방향인지 햇갈리는데 여기서 오른쪽으로 올리면 마이크센서의 초기 값이 작아지던가 아무튼 아두이노에 연결해서 Serial 통신으로 그 값을 찍어보시면 돌리는 방향에 따라 값이 커졌다 작아졌다 하니깐 초기값을 정하시면 됩니다.

그리고 오른쪽 핀 연결부분을 보시면 A0, G, +, D0으로 표시되어 있는데 A0은 아날로그 신호로 출력하는 거고, D0은 디지털 신호로 출력하는 핀입니다. +, Gnd은 전원부분으로 아두이노우노에 Vcc, Gnd로 연결하시면 되겠죠. 제가 사용하는 모델은 5V에 연결했네요 부품마도 허용치 Vcc가 다르니깐 구매 부품의 사용설명서를 참조하세요.

2. 마이크사운드 감지센서값 읽기


1
2
3
4
5
6
7
8
9
10
11
void setup()
{
  Serial.begin(9600);
}
 
void loop()
{
  int SoundSensor =analogRead(A0);
  Serial.println(SoundSensor);   
  delay(50);
}

대충 이 코딩으로 현재 사용하시는 마이크사운드 감지센서의 출력값을 아두이노 IDE 시리얼모니터에 출력시켜 초기값을 확인하시면 됩니다.
제 마이크사운드 감지센서의 값은 23~26사이의 아날로그 출력값을 초기값으로 출력하는데 평균 26을 주기적으로 출력하더군요.
아무것도 소리를 입력하지 않는 현재 공간의 소음상태의 신호값입니다. 위에서 설명한 가변저항기의 일자나사모양을 돌리시면 돌리는 방향에 따라서 이 수치가 커지거나 작어지거나 할꺼에요. 자신이 정한 기준에 맞춰서 세팅하시면 됩니다.

3. 회로도 구성


  • 준비물 : 마이크사운드 감지센서 1개, 아두이노우노
  • 내용 : 마이크사운드 감지센서를 아두이노에 연결하자.


마이크사운드 감지센서는 어떤 종류라도 상관 없습니다. 사용되는 핀은 아날로그 신호를 입력받기 때문에 A0핀과 전원부분인 +(Vcc), Gnd 핀을 사용하기 때문에 아날로그 출력핀은 아두이노의 원하는 아날로그핀(A0~A5)을 선택하셔서 연결하시면 되고 전원부분은 맞춰서 연결하시면 됩니다. 마이크사운드 감지센서는 종류에 따라 전원공급 허용치에 맞게 연결하시면 되니깐 그건 부품의 사용설명서를 참고해주세요.

4. 코딩


  • 사용함수 : analogRead()
  • 내용 : 간단히 마이크사운드 감지센서를 통해 소리의 최소와 최대값을 출력해 보자.

마이크사운드 감지센서값 읽기에서 최소값, 최대값 변수를 만들어서 if문 조건문을 통해서 최소값과 최대값을 저장하여 어느정도의 범위의 소리 신호를 센서가 측정할 수 있는지 살펴보도록 하겠습니다.

1
2
3
4
5
int m_min = 26;
int m_max = 0;
int SoundSensor =analogRead(A0);
if(SoundSensor<m_min) m_min=SoundSensor;
if(SoundSensor>m_max) m_max=SoundSensor;

첫번째 if문은 SoundSensor값이 초기 26보다 작다면 그 값을 최소값으로 한다.
두번째 if문은 SoundSensor값이 초기 0보다 크다면 그 값을 최대값으로 한다.

간단하게 최소값하고 최대값을 구하는 로직입니다. 왜 초기 최소값을 26으로 했을까요. 그냥 마이크사운드 감지센서로 읽은 값을 Serial 모니터로 출력하면 초기평균값이 나오잖아요 .그 값을 최소로 잡은 것이죠. 그냥 26을 최소값으로 하고 구지 if문으로 최소값을 찾을 이유가 없지 않느냐라고 반문할 수 있지만 실제로 아래 돌려보시면 생각했던 값이 나오지 않고 더 작은 값이 최소값으로 찍히게 됩니다. 그 이유는 결과부분에서 자세히 설명드리겠습니다.

우선은 이런식으로 최소값과 최대값을 구하는 로직을 짰는 걸로 정리해주시기 바랍니다. 그럼 완성된 코딩은 다음과 같겠죠.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
int m_min = 26;
int m_max = 0;
 
void setup()
{
  Serial.begin(9600);
}
void loop()
{
  int SoundSensor =analogRead(A0);
    if(SoundSensor<m_min) m_min=SoundSensor;
    if(SoundSensor>m_max) m_max=SoundSensor;
     
    Serial.print(m_min);   
    Serial.print("  :  ");
  Serial.println(m_max);   
  delay(50);
}

5. 결과


[ 초기상태 ]




[ 소리입력 후 결과 ]



두장의 사진을 보면 어떤 차이가 있나요. 첫번째 사진은 최소값이 23이고 최대값은 27로 나옵니다. 이건 처음에 마이크사운드 감지센서에 전원이 공급될때 발생하는 전류의 순간 초기 변화의 값이고 평균적으로 26의 값을 초기값으로 찍어 냅니다.

하지만 두번째 사진을 보시면 최소값이 17이고 최대값은 1005로 나옵니다. 마이크사운드 감지센서의 소리가 1005까지 증가한 신호가 측정되었네요. 하지만 이상한점은 최소값이 17로 감소한 지점이 마이크사운드 감지센서에서 발생했습니다.

그 이유가 뭘까요. 바로 소리를 측정하고 전기 신호를 발생하는 순간 측정된 만큼의 전류가 늘어나겠죠. 그리고 소리가 끊어지면 전류의 소비된 만큼이 순간 떨어지게 되고 초기 전류상태에서 아래로 전류값이 떨어지게 되는 것 같습니다. 소리의 측정된 전류가 늘었다 줄어드는 순간 초기 전류상태에서 그 아래로 떨어졌다가 다시 초기상태로 되돌아간다고 생각하면 될 듯 싶네요.

소리가 발생할때마다 전류의 신호의 파형이 증가했다가 떨어질때는 초기값 아래로 일시적으로 전류가 떨어졌다가 다시 올라고 반복된 현상을 보입니다.

이 현상을 잘 기억하셔서 다른 부품을 제어할 때 주의해서 코딩해야 합니다. 초기값이 26이니깐 그 값을 기준으로 해야지 했는데 기준값 아래로 떨어질때는 원하지 않는 값으로 다른 부품을 제어하게 됩니다. 위에서 최소값을 26으로 잡고 다시 if문을 써서 최소값을 구하는 로직을 표현했는지 그 이유를 아시겠지요. 정확히 26이란 값이 최소값이 되지 않기 때문에 그 아래 신호값이 발생할 수 있기 때문에 자신이 사용하는 마이크사운드 감지센서에 대한 정확한 측정 수치를 테스트를 해보셔야 합니다.

마무리


원래는 이 포스팅이 아니였는데 쓰다보니깐 길어져 버렸네요. 이 부품을 가지고 다른 원리를 설명하고자 했는데 의도치 않게 마이크사운드 감지센서 포스트가 되고 말았네요.
그리고 디지털 읽기로 한번 해보세요. 어떤 값이 찍히는지요. 그 값을 Serial 모니터로 출력해보세요. 그리고 이런류의 부품을 사용할때는 보정작업이 필요합니다. 초기값을 어느값으로 할지와 그 값의 범위를 정해서 그 밤위의 값으로 나누는 방법도 생각하시면 다양한 표현을 할 수 있습니다.
초기값을 정하면 그 값을 기준으로 다른 부품의 동작을 제어할 수 있습니다. 그리고 범위값을 나누면 소리의 범위별로 서로 다른 동작을 시킬 수 있습니다.

참고로 가상시뮬에이터에서는 실험 할 수 없습니다. 마이크사운드 감지센서가 존재하지 않으니깐요. 그 대안으로 가변저항기를 이용해주세요. 가변저항기를 돌리면 0~1023의 값을 입력받을 수 있습니다.

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

이 포스트가 예제로 적당할 듯 하네요. 다음 포스팅 내용도 이 예제를 기반으로 설명을 할 예정입니다. 한번 가변저항기로 마이크사운드 감지센서가 이 값은 음역대 신호를 발생한다고 가정하고 읽는 것을 연습해보세요. 그리고 이 아날로그 신호값이 발생하면 3색 LED 제어한 것처럼 난 무엇을 해볼까 상상력을 발휘를 해보세요.

마지막으로 마이크사운드 감지센서를 가지고 여러분들은 뭘 만들고 싶을지 상상의 나래를 펼쳐보세요.

댓글()

[아두이노] Sensor 읽는 법

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

[아두이노] Sensor 읽는 법



오늘은 지금까지 다뤘던 Sensor 들에 대해서 정리하는 시간을 갖도록 하겠습니다. 가상시뮬레이터에서 제공되는 Sensor의 종류들은 극히 일부에 지나지 않습니다. 실제는 더 많은 수십 수백가지이상의 센서들이 존재합니다. 여기서 중요한 것은 종류가 많다고 해서 개별적으로 다르게 접근하는게 아니라 이제 설명하는 기본 세가지 방법으로 Sensor의 값을 읽어 올 수 있습니다. 이 세가지 방법만 알고 있으면 센서 종류만 많지 다루는데 그렇게까지 어려움이 없을 거라 생각합니다.

1. Sensor의 종류



가상시뮬레이터에서는 값을 읽는데 사용하는 부품들입니다. 몇 종류가 되지 않지만 그래도 세가지 방법을 다 실험할 수 있는 부품들로 구성되어 있습니다. 실제로 아두이노키트를 구매하시면 몇십가지의 부품들로 구성되어 판매하지만 사실 종류별로 구분하여 분류하면 종류는 그렇게 많지 않습니다. 개수만 많을뿐이죠. 실제 부품들 사진을 올려야 하는데 제 키트 사진을 찍어서 올리기 힘들고 인터넷 키트 사진을 올리는 것도 문제가 될 것 같아서 그부분은 생략하도록 하겠습니다. 관심있으신 분들은 구글 검색에서 "아두이노센서 종류"라는 키워드로 검색하시면 목록 사진들이 나열된 게시물들을 쉽게 찾을 수 있으니깐 어떤 종류의 센서들이 있는지 개별적으로 살펴보시면 되겠습니다.

2. Sensor의 읽기 구분



Sensor를 읽이 위해서는 크게 세가지 방법으로 구분한다고 했죠. 바로 디지털 읽기, 아날로그 읽기, 시간값 읽기로 나누어 살펴볼 수 있습니다.

1) 디지털 읽기 :

디지털핀에서 센서의 값을 읽는 방법입니다. 기본적으로 디지털핀은 HIGH or LOW의 상태값을 읽을 수 있고 쓸 수 있습니다. 쉽게 말해서, 디지털핀으로 읽을때 HIGH or LOW의 상태값을 읽게 됩니다. Sensor에서 디지털 상태값을 출력하는 종류가 뭐가 있을까요. PIR Sensor(인체감지센서), Tilt Sensor(기울기센서), 스위치버턴 등이 대표적으로 들 수 있겠죠. 이런 종류의 Sensor는 두가지 HIGH or LOW 상태값만 갖게 됩니다.

int val = digitalRead(디지털핀);

val이란 변수에 디지털핀에 연결된 센서의 값을 저장하게 됩니다. 우리는 두가지 상태값 HIGH or LOW의 값을 통해서 아두이노에서 특정 동작을 제어하게 됩니다.

2) 아날로그 읽기 :

아날로그핀에서 센서의 값을 읽는 방법입니다. 기본적으로 아날로그핀은 0~1023사이의 값을 읽게 됩니다. 아날로그 값을 출력하는 종류가 뭐가 있을까요. 위 사진을 보면 대충, Gas Sensor(가스센서), Photoresistor(LDR)(조도센서) 등이 있네요. 이런 종류의 센서는 전류의 값을 출력되는데 아두이노는 0~1023사이의 전류의 세기 값을 읽게 됩니다.

int val = analogRead(아날로그핀) ;

val이란 변수에 아날로그핀에 연결된 센서의 값을 저장하게 됩니다. 우리는 전류의 세기 0~1023의 값을 통해서 특정 동작을 제어하게 됩니다. 참고로 부품에 따라 아날로그 신호와 디지털 신호를 두개 다 내보내는 부품이 있는데 사용목적에 따라서 디지털 신호는 디지털 읽기로 아날로그 신호는 아날로그 읽기 함수를 사용해서 읽으시면 됩니다.

3) 시간값 읽기 :

디지털핀에서 센서의 값을 읽는데 어떤 특정한 상태를 기준으로 해서 그 상태가 유지하는 시간을 추출할 수 있습니다. 대표적으로 초음파 센서를 들 수 있습니다.

int val =pulseIn(디지털핀, HIGH/LOW);

val이란 변수에 디지털 핀에 연결된 센서의 값을 읽어오는데 상태의 값이 HIGH or LOW 중 어떤 상태값이 얼마정도 유지 되었는지 그 시간을 측정하는데 사용됩니다. 가령 pulseIn(디지털핀, HIGH) 라고 하면 디지털핀에 들어오는 HIGH값이 LOW가 될때까지의 시간값을 반환하게 됩니다. 초음파센서에서 초음파를 쏘고 되돌아올때 이때 초음파센서를 통해 HIGH에서 LOW가 될때까지의 시간값을 가지고 거리를 계산 했었죠.

원래는 디지털 읽기와 아날로그 읽기로 두가지로 구분하시면 됩니다. 시간값 읽기는 특수한 경우이고요. 다양한 부품들이 있는데 대부분 디지털 값을 출력하거나 아날로그 값을 출력하는 부품들이고 우리는 digitalRead()함수와 analogRead()함수로 센서의 값을 읽게 됩니다.

추가적으로 통신에서 '1'과 '0'의 신호값으로 읽기도 합니다. 가령 1byte의 값을 전송한다면 '00000000'의 범위의 전기신호를 보내는데 '1'이라는 값을 보낸다고 치면 '00000001'이렇게 2진수로 보낸다면 전기 신호가 'LOW' 7번, 'HIGH' 1번으로 짧게 끈어서 8bit의 신호를 보내겠죠. 통신에서는 읽을때 이 전기신호를 읽어서 다시 복호화하여 1이란 숫자로 저장하게 됩니다. 통신 부분이기 때문에 이 부분은 간단히 전기신호로 읽는 방법정도만 이해하시면 되겠습니다.

SoftwareSerial mySerial(RX,TX);
byte val = mySerial.read();

Serial 통신핀을 통해서 1byte의 신호값을 val에 저장합니다. 센서값을 읽은 기기에서 통신을 통해서 그 값을 아두이노우노에서 읽을때 사용 됩니다. 이부분은 통신 부분이니깐 별도로 알아두시기 바랍니다.

마무리


오늘은 간단히 센서의 값을 읽는법을 정리하는 시간으로 채웠습니다. 어떤 Sensor를 사용하시더라고 기본 골격은 디지털 읽기와 아날로그 읽기 뿐이 없습니다. 별도로 라이브러리리 제공 될 경우 해당 라이브러리 객체에 사용핀을 던져주기만 하면 별도의 코딩은 필요 없겠지만 실제 여러분들이 코딩을 하실 경우는 대표적으로 이 두가지 방법으로 접근이 이루어지기 때문에 두 함수만 이해하시면 사용해보지 않는 센서들일지라도 대부분 사용하실 수 있을 꺼에요.

오늘은 정리하는 시간으로 간단히 포스팅을 했네요.

댓글()

[아두이노] ATtiny 제어

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

[아두이노] ATtiny 제어



오늘은 ATtiny에 대해서 살펴보는록 하겠습니다. 아주 작은 프로그램을 이식할 수 있고 다른 부품을 제어할 수 있어서 소형 아두이노로 생각하시면 아마 편하실꺼에요. 그런데 사용하기 위해서는 프로그램을 이식해야하는데 좀 복잡한 과정을 거쳐야 하기 때문에 사용하시기 번거로울 꺼에요. 프로그램 이식과정을 무시하고 바로 가상시뮬레이터에서 동작을 시킬 수 있기 때문에 한번 사용해 보겠습니다. 사용을 해봐야 어디다 사용할지 상상을 할 수 있기 때문에 상상하기 전에 간단히 제어를 통해 ATtiny를 배워도록 하겠습니다.

1. ATtiny 구조



파워포인트로 대충 그려봤는데 잘 그려졌는지 모르겠네요. 우선 핀을 보시면 총 8개의 핀으로 구성되어 있습니다. 전원 공급핀과 리셋핀을 제외하면 대충 Pin 0,1은 PWM으로 사용되고 Pin 2,3,4은 Analog Input으로 사용되네요.

따로 설명할 필요가 없을 것 같아 이 핀들을 가지고 간단히 LED를 제어하는 실험을 해보도록 하겠습니다.

2. 회로도 구성


  • 준비물 : ATtiny 1개, coin Cell 3V Battery 1개, Red Led 1개, 저항 220옴 1개
  • 내용 : 간단히 Red Led를 깜박이기게 하자.


위 그림에서 ATtiny 칩이 동작할려면 전원을 공급해야겠죠. 작은 건전지를 Vcc, Gnd로 연결해서 전원을 공급하게 됩니다. 그리고 첫번째 Pin 0에 출력핀으로 해서 Red Led를 전류를 공급하도록 배치해 놨습니다. 제일 처음 LED 제어편에서 아두이노를 13번 핀에 연결한 Red Led를 깜박이는 예제가 생각나실 꺼에요.

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

3. 코딩


  • 사용함수 : pinMode(), digitalWrite(), delay()
  • 내용 : "STEEM CODINGMAN" 글자를 만들어 출력하자.
  • 참고소스 :

함수

  • pinMode(사용핀, 입/출력모드) : 해당 사용핀을 어떤 모드로 사용할지 결정.
  • digitalWrite(사용핀, HIGH/LOW) : 해당 사용핀을 디지털 출력으로 HIGH or LOW 인지 결정.
  • delay(시간값) : 시간값만큼 대기한다.

[기본 소스]

1
2
3
4
5
6
7
8
9
10
11
12
void setup()
{
  pinMode(0, OUTPUT);
}
 
void loop()
{
  digitalWrite(0, HIGH);
  delay(1000); // 1초(1000) 대기
  digitalWrite(0, LOW);
  delay(1000); // 1초(1000) 대기
}

Pin 0을 출력모드로 설정 합니다. 1초 동안 HIGH(5V)를 공급 한 뒤에 1초동안 LOW(0V)상태가 됩니다. 이렇게 loop()함수는 계속 반복하게 됩니다. 그럼 1초 단위로 깜박이는 로직이 됩니다. 이미 LED 제어편에서 설명한 내용이니깐 간단히 로직에 대해서만 이야기 했네요.

4. 결과



위 그림은 간단히 Red Led가 깜박이는 장면입니다. 코딩하는 것도 아두이노랑 차이가 없고 핀번호에 대해서 구분하여 사용하시면 사용하는데 어려움이 없겠죠. 하지만 문제는 프로그램을 ATtiny 칩에 이식하는 과정입니다. 사용은 쉽고 코딩도 쉬운데 프로그램을 이식하는게 어렵습니다.

5. 아두이노에서 ATtiny로 연결



위 출처로 가시면 자세히 설명 나와 있습니다. 저도 이글을 통해서 ATtiny에 프로그램을 이식시키구나 하고 대충 의미를 이해했네요. 구글검색하시면 국내 게시물도 다양하게 공개되어 있으니깐 어느걸 보셔도 상관없습니다. 따로 제가 아는척 설명드리기는 뭐해서 링크만 걸어 놓겠습니다.


대충 가상시뮬레이터에서 그림만 그린다면 이렇게 되네요. 그리고 나서 위 참고자료에 따라 순서대로 아두이노 IDE에서 설정만 해주시면 된다고 하네요. 실제 ATtiny 칩이 없어서 실험은 해보지 못했네요. 그냥 여러분들도 가상시뮬레이터에서 실험하기 대문에 동작 제어만 한번 코딩해보고 ATtiny 칩에 응용 방법만 머리속에서 설계하시면 됩니다.

6. 추가로 찾아보셔야 할 내용



필독 내용도 꼭 읽어주시기 바랍니다. 어떻게 아두이노에서 다른 칩에 프로그램을 이식시킬 수 있는지 아두이노공식페이지에 잘 나와 있습니다.

그리고 데이터시트 경우는 구글검색 키워드 "attiny85 datasheet"로 하시면 저 게시물이 나옵니다. 해당 데이터시트를 링크를 안걸어 놓은 이유는 다운로드 PDF 파일이기 때문입니다. 오해를 살 수 있는 부분이라서 링크를 걸어놓지 않겠습니다. 다른 데이터시트를 보셔도 됩니다. 내용은 비전공자분들은 어려울 수 있습니다. 제가 소개한 데이터시트의 p59 페이지의 표만 참고하시면 됩니다. 각 핀에 대한 설명이 자세히 나와 있습니다. 그걸 참고하시고 위에서 간단히 핀 설명을 설명을 PWM, Analog Input이라고만 했는데 우선은 이정도만 이해하셔도 충분합니다. 보다 자세히 알고 싶으면 데이터시트를 꼭 참고해 주세요.

마무리


간단히 ATtiny 칩을 제어해 보았습니다. 코딩은 따로 할 것은 없고 아두이노 상에서 코딩한 것 처럼 그대로 코딩하시면 됩니다. 어떤 핀들이 있는지만 잘 이해하시면 됩니다. 명령을 내릴 수 있는 핀은 어떤 핀인지만 구별하시면 어려움은 없을 듯 하네요. 좀 어려운 부분은ATtiny에 프로그램을 이식시키는 과정이 좀 복잡하다는 점이죠.
저는 코딩을 좋아하고 코딩을 위주로 하기 때문에 전자회로쪽은 좀 약한 편입니다. 얇은 지식으로 2편을 나눠서 제가 설명하는 것보다 위에 나온 링크한 자료들을 토대로 공부하시는게 더 좋을 것 같아 포스팅을 늘리지 않겠습니다.

실제로는 다른칩에 프로그램을 이식하기 위해서는 ArduinoISP 튜토리얼을 참고하셔서 그 과정대로 세팅하고 실제 코딩해놓은 프로그램을 이식시키면 됩니다. 가상시뮬레이터에서는 프로그램을 이식시키는 과정이 필요 없기 때문에 뭘 만들지만 여러분들이 상상력을 발휘하시면 됩니다. 지금까지 배워온 여러가지 부품들을 ATtiny와 연결해서 회로도를 설계를 해보시고 동작시켜서 ATtiny의 활용도를 상상해 보세요.

마지막으로 작은 ATtiny 칩을 어디에다 쓸지 상상의 나래를 펼쳐보세요. 아마 소형으로 구현할 수 있는 기기들을 상상하시겠지요.

댓글()

[아두이노] 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를 다 꺼지겠지요.

[기본 소스]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#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의 색을 결정하기 위해서 두개의 함수를 더 사용합니다.

1
2
randomSeed(analogRead(A0))
random(255)

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

[전체 소스]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#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를 다 꺼지겠지요.

[기본 소스]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#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'라는 글자를 만들면 해당 위치에 가운데 표에서 보는 것처럼 색이 칠해지겠죠. 그 위치값을 가지고 글자패턴을 만든다면 아래와 같이 표현이 됩니다.

1
2
3
4
5
6
7
8
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}};

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

1
2
3
4
5
6
7
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'라는 글자를 볼 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#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'으로 표현 하겠습니다.

1
2
3
4
5
6
7
8
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문안에 문장이겠죠.

1
2
3
4
5
6
7
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문을 표현할 때 앞이냐 뒤냐에 따라서 표현이 좀 달라지기는 하지만 그렇게 큰 의미는 없는 표현입니다.

1
2
3
4
5
6
7
8
const byte p[7]= {
0B01110000,
0B10001000,
0B10000000,
0B01110000,
0B00001000,
0B10001000,
0B01110000 }

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

위의 패턴배열변수에서

1
0B01110000

이값을 가져올려면

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

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

로직은 이러헥 되겠죠.

1
2
3
4
5
6
7
8
9
10
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자를 완성하면 이렇게 간단히 표현됩니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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문을 한번 더 돌려야 겠죠.

1
2
3
4
5
for(int k=0;k<14;k++){
 
        기존 2for문 로직;
        y=0; // 하나의 패턴 글자가 끝나니깐 y행위치 변수값을 초기화 해야겠죠.
}

최종적으로 코딩을 하면

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#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이 재밌다고 했는데 갑자기 코딩수업이 되어서 어렵게 느껴졌을 듯 싶네요. 간단한 단순 패턴을 만들어서 화려하게 꾸미고 싶었는데 글자가 너무 땡겨서 글자 출력으로 하고 말았네요. 사실 특정한 문자로 출력하는 것은 좀 코딩이 복잡하고 불편합니다. 단순하게 홀짝 출력이나 릴레이 색상출력이나 큰틀에서의 한두가지 패턴을 색상과 덧붙여서 출력하는게 더 재미 있고 화려한데 말이죠.
이부분은 이 포스팅을 읽으시는 분들의 몫으로 남겨 두겠습니다. 혹시 땡겨서 밤에 만들어 내일 올릴 수 도 있겠지만 오늘밤 땡기냐 안땡기냐로 기분에 맡겨야 겠습니다.
갑자기 코딩이 좀 어렵게 됐는데 이것 말고 간단히 색상 별 한두가지 패턴으로 지그재그로 꾸며 보세요. 그리고 위치를 달리해서 어떤 독특한 모양을 만들어 보세요.
아니면 이 출력하는 원리를 잘 생각해보셔서 어느 부품하고 연결하면 좋을지 상상의 나래를 한번 펼쳐보세요.


댓글()