반응형

아두이노는 analogWrite() 함수를 통해 간단하게 RGB LED를 제어할 수 있다. 여기에 앞선 글에서 다루었던 버튼과 터치 센서를 달고 원하는 색상과 밝기를 설정하고, 또한 정해진 시간 간격으로 랜덤 한 색상이 변경되고 색상이 변경될 때 자연스럽게 변경되도록 코드를 짜 보자.

 

예제에 사용되는 부속들은 알리익스프레스에서 구매한 "최신 RFID 스타터 키트, Arduino UNO R3 업그레이드 버전 - improved version"을 구매하여 사용하였습니다. 사용된 구성품을 확인하실 수 있습니다. 

 

 

NEWEST RFID Starter Kit for Arduino UNO R3 Upgraded version Learning Suite With Retail Box - AliExpress 502

Smarter Shopping, Better Living! Aliexpress.com

www.aliexpress.com

 

일반 버튼 

1. RGB LED ON / 밝기 조절 모드 진입

2. 색상 설정 모드 진입 - 기본 LED ON

3. 멀티 색상 패턴 모드 - 기본 LED OFF 

4. RGB LED OFF

 

터치버튼

1. 밝기 조절 모드에서 RGB LED 밝기 제어

2. 색상 설정 모드에서 정해진 색상을 순서대로 변경

3. 멀티 색상 패턴 모드에서 아래 패턴 선택 

  - 정해진 시간 간격에 따라 정해진 색상 변경 

  - 정해진 시간 간격에 따라 무지개 색상 변경 

  - 정해진 시간 간격에 따라 부드러운 랜덤 색상 변경 

 

연결

1. 10K 옴 저항 한쪽 끝 - 아두이노 3.3V

2. 10K 옴 저항 한쪽 끝 - 아두이노 아날로그 입력 A0핀, 터치용 점퍼 선 

3. 택트 스위치 - 아두이노 7번 핀

4. 택트 스위치 - 아두이노 GND

5. RGB LED 모듈 R - 아두이노 9번 핀 (모듈에 따라 표기된 색상 마크 R, G, B와 실제 제어되는 색상이 다를 수 있음)

6. RGB LED 모듈 G - 아두이노 10번 핀

7. RGB LED 모듈 B - 아두이노 11번 핀

8. RGB LED 모듈 '-' - 아두이노 GND

* 모듈이 아닌 RGB LED를 사용할 경우에는 각 연결핀에 220옴 저항을 추가해야 한다.

필자가 사용하는 RGB LED 모듈은 모듈상의 색상 마크 R과 B가 실제 색상 핀에 매칭 되지 않고 서로 바뀌어있다. 즉 R로 표기된 핀이 빨간색을 제어하는 핀이 아니라 파란색을 제어하는 핀이며, B로 표기된 핀이 빨간색을 제어하는 핀이다.

 

이를 확인하기 위해서는 아래 기본 코드를 아두이노에 업로드하고 시리얼 모니터를 통해 9번 핀에 PWM 신호를 주어 빨간색이 켜지도록 테스트를 해보면 알 수 있다.

mood_rgbled.ino
0.00MB

#define ledPin 13 // 아두이노 기본 LED
#define Red 9     // 빨간색 제어 핀
#define Green 10  // 녹색 제어 핀
#define Blue 11   // 파란색 제어 핀

uint16_t pwm1 = 0; // RED - RGB LED 제어용 PWM 변수 
uint16_t pwm2 = 0; // GREEN
uint16_t pwm3 = 0; // BLUE

void setup() {
  Serial.begin(9600);       // 시리얼 모니터
  pinMode(ledPin, OUTPUT);  // 아두이노 기본 LED
  pinMode(Red, OUTPUT);
  pinMode(Green, OUTPUT);
  pinMode(Blue, OUTPUT);
}

void loop() {
  if (Serial.available()) {  
    String temp = Serial.readStringUntil('\n'); // 시리얼 버퍼에 데이타 있으면 '새줄'까지 읽기
    if (temp == "bb") {             // 읽은 값이 "bb" 이면
      if (pwm1 <= 205) pwm1 += 50;  // 빨간색 제어값 +50
      else pwm1 = 255;
    }
    else if (temp == "b") {         // 읽은 값이 "b" 이면
      if (pwm1 >= 50) pwm1 -= 50;   // 빨간색 제어값 -50
      else pwm1 = 0;
    }
  }
  analogWrite(Red, pwm1);     // RGB LED에 아날로그 값 쓰기
  analogWrite(Green, pwm2);
  analogWrite(Blue, pwm3);
}

 

 

시리얼 모니터의 전송 옵션을 '새 줄'로 한 상태에서 텍스트 "bb"를 입력하고 엔터를 쳐보자. RGB LED가 빨간색으로 켜진다면 모듈 색상 마크가 정상적으로 표기된 것이고, 다르다면 빨간색과 파란색 표시가 서로 바뀐 상태이다. 이럴 경우에는 모듈의 빨간색과 파란색 핀을 서로 바꿔서 연결해 주면 된다. 

 

 

RGB LED를 조명 용도로 사용하기 위해서는 약간의 가공이 필요하다. RGB LED 자체의 밝기가 너무 밝아 색상 구별이 잘 안되고 눈이 부시게 된다. 일반 무드등이 그렇듯 전등갓 같은 게 필요하다. 가장 쉽게 접할 수 있는 종이컵을 이용하여 전등갓의 대용으로 사용하면 나름 적당한 밝기로 조절되고 색상도 명확하게 표현된다.  

 

아래 영상은 사운드 모듈 DFPlayer와 RGB LED를 아두이노에 연결하고 DFcontroller 앱으로 제어한 영상이다.

4분 15초부터 종이컵을 이용한 RGB LED의 색상 표현을 볼 수 있다. 종이컵만 이용해서도 충분하지 않아 종이컵 안에 휴지를 넣어준 상태이다. 

 

https://youtu.be/ecD0GLU-uKw

 

RGB 값을 이용한 색상을 표현하기 위해 RGB 색상표를 찾아서 원하는 색상에 맞는 RGB 값을 얻어야 한다.

기본 색상에 chocolate 색상만 추가한 색상표이다. 유의할 점은 색상표의 색상이 실제 RGB LED에 그대로 표현되지 않는다는 것이다. 색상표는 물감이나 인쇄된 색상에서 반사된 빛을 눈이 인지하는 색상을 도식화해놓은 것이고 RGB LED에 의해 표현되는 색상은 빨강, 녹색, 파랑 빛의 조합에 의해 만들어지는 색상이라 정확하게 일치하지 않게 된다. 예를 들어 은색, 연회색, 짙은 회색 등은 빛의 조합으로는 명확하게 표현할 수 없는 색이다. 이러한 색들은 (특히, 어두운 계열) 밝기의 차이로만 느껴지게 된다. 

 

상기 표를 기준으로 색상표 보는 법을 살펴보면 아래와 같다. 

색상 || 색상 영문명 || 16진수 값 #RRGGBB(RR: 빨간색, GG: 녹색, BB: 파란색) || 10진수 값(빨간색, 녹색, 파란색)

 

RGB 색상값 참조 사이트

https://www.rapidtables.com/web/color/RGB_Color.html

 

상기 색상표의 색상을 모두 표현하기 위해 같은 계열(빨강/녹색/파랑) 색상값들을 모아 아래와 같이 배열로 만들어 배열의 인덱스를 통해 색상을 표시할 수 있도록 해 준다.

 

uint8_t color_index = 0; // 색상 제어용 인덱스

uint8_t red_t[8] = {255, 255, 0, 0, 255, 0, 255, 210};     // 빨간색 8개 값에 대한 배열
uint8_t green_t[8] = {255, 0, 255, 0, 255, 255, 0, 105};  // 녹색 8개 값에 대한 배열
uint8_t blue_t[8] = {255, 0, 0, 255, 0, 255, 255, 30};     // 파란색 8개 값에 대한 배열

 

아래 스케치를 아두이노에 업로드하면 1초마다 상기 테이블의 순서대로 RGB LED의 색상이 변하는 것을 볼 수 있다.

mood_rgbled_colorTable.ino
0.00MB

#define ledPin 13
#define Red 9
#define Green 10
#define Blue 11

uint8_t color_index = 0;
uint8_t red_t[8] = {255, 255, 0, 0, 255, 0, 255, 210};
uint8_t green_t[8] = {255, 0, 255, 0, 255, 255, 0, 105};
uint8_t blue_t[8] = {255, 0, 0, 255, 0, 255, 255, 30};

void setup() {
  pinMode(ledPin, OUTPUT);
  pinMode(Red, OUTPUT);
  pinMode(Green, OUTPUT);
  pinMode(Blue, OUTPUT);
}

void loop() {
  analogWrite(Red, red_t[color_index]);
  analogWrite(Green, green_t[color_index]);
  analogWrite(Blue, blue_t[color_index]);
  color_index++; // 색상 표시 후 색상제어용 인덱스 증가
  if (color_index == 8) color_index = 0; // 0 ~ 7 까지 8개 색상 표시되었으면 처음으로 초기화
  delay(1000); // 1초마다 색상 변경
}

 

터치 센서 구현 코드 테스트

이전 글 "아두이노 - ADC 핀을 이용한 터치 센서 구현"에서는 아두이노에 어떠한 장치나 모듈의 연결 없이 A0핀의 아날로그 값을 읽었었고 그 읽은 값의 범위가 675 ~ 677이었다. 이 값을 기준 범위 값으로 정하고 터치 센서를 구현했었지만 블루투스나 RGB LED 등 기타 장치가 부착되고 그 장치들이 작동될 때에는 아날로그 리드 값에 영향을 주게 되어 아날로그 리드 함수를 통해 읽은 값의 범위에 변동이 발생하게 된다. 

 

 

아래 코드는 상기 코드의 delay(1000)을 millis() 함수로 대체해주고 RGB LED 출력 코드를 rgb_display() 사용자 함수로 만들고 터치센서 코드를 합친 것이다.

mood_rgbled_colorTable_touchTest.ino
0.00MB

#define ledPin 13
#define Red 9
#define Green 10
#define Blue 11
#define capSensePin A0 // ADC pin (paperclip, conductive paint/fabric/thread, wire)

uint8_t color_index = 0;
uint8_t red_t[8] = {255, 255, 0, 0, 255, 0, 255, 210};
uint8_t green_t[8] = {255, 0, 255, 0, 255, 255, 0, 105};
uint8_t blue_t[8] = {255, 0, 0, 255, 0, 255, 255, 30};

// 터치버튼 변수
unsigned int long cap_button_delay = 0;
bool cap_button_check = false;
uint8_t delay_count = 0;
bool start_count = false;
int val;
bool Led_state = LOW;

void setup() {
  Serial.begin(9600);
  pinMode(ledPin, OUTPUT);
  pinMode(Red, OUTPUT);
  pinMode(Green, OUTPUT);
  pinMode(Blue, OUTPUT);
  pinMode(capSensePin, INPUT);
}

void loop() {
  cap_button();   
  if (cap_button_check == true) {
    if (start_count == false) {
      val = analogRead(capSensePin); // 기준값: 675 ~ 677(3.3V, 10K ohm)
      Serial.println(val);
    }
    else  delay_count++; 
    if (delay_count == 0) {
      if(val > 680 || val > 500 && val < 670 ){  // 기준값보다 떨어지는 경우: 500 ~ 670 
        Led_state = !Led_state;
        digitalWrite(ledPin, Led_state);
        start_count = true;
      }
    }
    else if (delay_count == 3) {
      start_count = false;
      delay_count = 0;
    }
    cap_button_check = false;
  }
  rgb_display_delay();
  rgb_display();
}

unsigned long int rgb_delay = 0; // delay() 함수 대체용 millis() 값 저장 변수
bool rgb_change = false;         // delay() 대체 함수에 대응하는 실행 함수 트리거 플래그

void rgb_display() {
  if (rgb_change == true) {      // 실행함수 트리거 플래그가 참이면
    analogWrite(Red, red_t[color_index]);
    analogWrite(Green, green_t[color_index]);
    analogWrite(Blue, blue_t[color_index]);
    color_index++;
    if (color_index == 8) color_index = 0;
    rgb_change = false;
  }
}

void rgb_display_delay() {            // delay(1000) 대체 사용자 함수
  if (millis() - rgb_delay >= 1000) { // 1000 밀리 초가 되면
    rgb_delay = millis();             // 밀리 초 변수 초기화
    rgb_change = true;                // 실행함수 트리거 플래그 변경
  }
}

void cap_button() {
  if (millis() - cap_button_delay >= 100) {
    cap_button_delay = millis();
    cap_button_check = true;
  }
}

작동 상태를 살펴보면 RGB LED는 1초마다 색상이 변경되어 정상 작동하지만 켜지지 말아야 할 아두이노 기본 LED가 불규칙하게 깜박이는 의도치 않는 현상을 확인할 수 있다. 

시리얼 모니터를 확인해보면 A0핀의 아날로그 읽은 값이 아래 그림처럼 692까지 표시된다. "if(val > 680 || val > 500 && val < 670 )" 코드에 의해 설정된 실행 조건값인 680을 넘게 될 때마다 실행코드가 실행되어 아두이노 기본 LED가 점멸하게 되는 것이다.

상기 테스트를 통해 터치 센서의 작동 조건값을 "if(val > 700 || val > 500 && val < 669 )"로 변경해 주었다. 

 

이제 터치 센서 테스트 이전 코드에 아두이노 버튼 코드와 터치 센서 코드를 합치고 "일반 버튼 - 1. RGB LED ON / 밝기 조절 모드 진입, 터치버튼 - 1. 밝기 조절 모드에서 RGB LED 밝기 제어"를 구현해 보자.

 

일반 버튼에는 4가지 버튼 모드가 있다. 따라서 버튼 모드용 변수 uint8_t button_mode = 0; 를 설정해주고 버튼이 눌려 실행 코드가 실행되는 부분에 아래 코드로 대체해 주었다. 

button_mode 초기값 0은 버튼 모드 4와 같고 RGB LED는 OFF 상태이다. 

button_mode ++; // 초기값 0 - OFF 상태에서 버튼 모드 증가
if (button_mode == 1) rgb_display(); // 버튼 모드 1: RGB LED ON
else if (button_mode == 2) Serial.println("button_mode 2"); // 버튼 모드 2
else if (button_mode == 3) Serial.println("button_mode 3"); // 버튼 모드 3 
else if (button_mode == 4) { // 버튼 모드 4
  button_mode = 0;           // 버튼 모드 초기값: RGB LED OFF
  analogWrite(Red, 0); 
  analogWrite(Green, 0); 
  analogWrite(Blue, 0); 
}

RGB LED의 밝기 제어를 위해 밝기 제어용 변수들을 선언해주고 기존 analogWrite() 함수들을 사용자 함수 rgb_display()로 아래와 같이 만들어 주었다. 

// brightness 설정 변수
uint8_t brightness = 0; // 밝기 제어 변수
uint8_t red_tb = 0;     // 변경된 빨간색 밝기 값 저장용 변수
uint8_t green_tb = 0;   // 변경된 녹색 밝기 값 저장용 변수
uint8_t blue_tb = 0;    // 변경된 파란색 밝기 값 저장용 변수

void rgb_display() {
  red_tb = red_t[color_index] >> brightness; // 시프트 연산자 이용 밝기 제어 변수 값만큼 이동
  green_tb = green_t[color_index] >> brightness;
  blue_tb = blue_t[color_index] >> brightness;
  analogWrite(Red, red_tb);                  // 변경된 RGB 값 이용 색상 표시
  analogWrite(Green, green_tb);
  analogWrite(Blue, blue_tb);
}

RGB 각각의 색상 값으로 0 ~ 255의 값이 사용되는데 조합된 색상의 관점에서 보면 이는 255를 최댓값으로 하는 비율 값이다. 만약 R의 값이 변경되었을 때 다른 G나 B값이 R값의 변경된 비율만큼 변경된다면(RGB 각각의 색상 비율이 같다면) 색상은 그대로인 상태가 된다. 예를 들어 "R:255, G:255 B:255"로 조합된 색상은 흰색이다. "R:100, G:100 B:100"로 조합된 색상 또한 흰색이다. 단지 밝기가 변경되는 것이다. 이렇게 원하는 색상의 밝기만 조절하고자 한다면 RGB 각각의 색상 값을 같은 값으로 나누어 주게 되면 되는데, 나누는 방법으로 시프트 연산자를 사용하겠다. 한 칸씩 우측으로 이동할 때마다 2, 4, 6, 8로 나누는 것과 같다. 자세한 사항은 "시프트 연산자"를 검색하면 살펴볼 수 있다.    

 

 

아래 시프트 연산자 테스트 코드를 업로드하고 시리얼 모니터에서 결괏값을 확인해 볼 수 있다.

shift_cal.ino
0.00MB

void setup() {
  Serial.begin(9600);
  uint8_t temp = 255;
  temp = temp >> 1;
  Serial.println(temp);
}

temp = temp >> 1; // 결괏값: 127

temp = temp >> 2; // 결괏값: 63

temp = temp >> 3; // 결괏값: 31

temp = temp >> 4; // 결괏값: 15

 

터치 버튼에는 버튼 모드에 따라 3가지 기능이 있다. 따라서 터치가 인식되어 실행 코드가 실행되는 부분에 아래 코드로 대체해 주었다. 

if (button_mode == 1) {  // 버튼 모드가 1일 경우 밝기 조절
  brightness++;          // 밝기 조절용 변수 증가
  if (brightness == 5) brightness = 0; // 밝기 조절은 4단계
  rgb_display();         // 변경된 밝기 반영
}
else if (button_mode == 2) Serial.println("button_mode2 - touch"); // 버튼 모드가 2일 경우
else if (button_mode == 3) Serial.println("button_mode3 - touch"); // 버튼 모드가 3일 경우

아래 스케치를 업로드하고 일반 버튼의 4가지 모드가 설정대로 잘 작동하는지 확인해보고 또한 버튼 모드 1에서 터치 버튼을 터치하여 밝기 변경이 잘되는지 확인해 보자.

mood_rgbled_colorTable_brightAdjust.ino
0.00MB

더보기
더보기
#define ledPin 13
#define Red 9
#define Green 10
#define Blue 11
#define capSensePin A0 // ADC pin (paperclip, conductive paint/fabric/thread, wire)
#define buttonPin 7

uint8_t color_index = 0;
uint8_t red_t[8] = {255, 255, 0, 0, 255, 0, 255, 210};
uint8_t green_t[8] = {255, 0, 255, 0, 255, 255, 0, 105};
uint8_t blue_t[8] = {255, 0, 0, 255, 0, 255, 255, 30};

// touch button 변수
unsigned int long cap_button_delay = 0;
bool cap_button_check = false;
uint8_t delay_count = 0;
bool start_count = false;
int val;

// tact button 변수
unsigned int long button_delay = 0;
bool button_hold = false;
bool pre_button_state = 1; // PULL_UP button 접점 붙은 상태: 0, 떨어진 상태: 1 -> 초기값 1
bool button_state = 1;  
uint8_t button_mode = 0;   // 버튼 모드용 변수 

// brightness 설정 변수
uint8_t brightness = 0;
uint8_t red_tb = 0;
uint8_t green_tb = 0;
uint8_t blue_tb = 0;

void setup() {
  Serial.begin(9600);
  pinMode(ledPin, OUTPUT);
  pinMode(Red, OUTPUT);
  pinMode(Green, OUTPUT);
  pinMode(Blue, OUTPUT);
  pinMode(capSensePin, INPUT);
  pinMode(buttonPin, INPUT_PULLUP);
}

void loop() {
  cap_button();
  cap_button_act();
  button();
  button_act();
}

void rgb_display() {
  red_tb = red_t[color_index] >> brightness;
  green_tb = green_t[color_index] >> brightness;
  blue_tb = blue_t[color_index] >> brightness;
  analogWrite(Red, red_tb);
  analogWrite(Green, green_tb);
  analogWrite(Blue, blue_tb);
}

void cap_button_act() {
  if (cap_button_check == true) {
    if (start_count == false) val = analogRead(capSensePin); // 기준값: 673 ~ 692(3.3V, 10K ohm)
    else  delay_count++; 
    if (delay_count == 0) {
      if(val > 700 || val > 500 && val < 669 ){  // 기준값보다 떨어지는 경우: 500 ~ 670 
        if (button_mode == 1) {
          brightness++;
          if (brightness == 5) brightness = 0;
          rgb_display();
        }
        else if (button_mode == 2) Serial.println("button_mode2 - touch");
        else if (button_mode == 3) Serial.println("button_mode3 - touch");
        start_count = true;
      }
    }
    else if (delay_count == 3) {
      start_count = false;
      delay_count = 0;
    }
    cap_button_check = false;
  }
}

void cap_button() {
  if (millis() - cap_button_delay >= 100) {
    cap_button_delay = millis();
    cap_button_check = true;
  }
}

void button_act() {
  button_state = digitalRead(buttonPin);
  if (button_hold == false) {
    if (button_state != pre_button_state) {
      button_hold = true;
      button_delay = millis();
      if (button_state == 0) {
        button_mode ++;
        if (button_mode == 1) rgb_display();
        else if (button_mode == 2) Serial.println("button_mode 2");
        else if (button_mode == 3) Serial.println("button_mode 3");
        else if (button_mode == 4) {
          button_mode = 0;
          analogWrite(Red, 0);
          analogWrite(Green, 0);
          analogWrite(Blue, 0);
        }
      }
      pre_button_state = button_state;
    }
  }
}

void button() {
  if (button_hold == true) {
    if (millis() - button_delay >= 100) {
      button_hold = false;
    }
  }
}

 

"일반 버튼 - 2. 색상 설정 모드 진입 - 기본 LED ON"과 "터치 버튼 - 2. 색상 설정 모드에서 정해진 색상을 순서대로 변경"을 위해 아래와 같이 수정해 주었다. 

// button_act() 함수 수정 전
if (button_mode == 2) Serial.println("button_mode 2");

// button_act() 함수 수정 후
if (button_mode == 2) digitalWrite(ledPin, HIGH); // 기본 LED ON, 버튼 모드 2 표시

// cap_button_act() 함수 수정 전
if (button_mode == 2) Serial.println("button_mode2 - touch");

// cap_button_act() 함수 수정 후
if (button_mode == 2) {  // 버튼 모드 2일 때
  color_index++;         // 색상 선택 인덱스 증가
  if (color_index == 8) color_index = 0; // 색상 인덱스 8이면 0으로 초기화
  rgb_display();         // 변경된 색상 표시
}

"일반 버튼 - 3. 멀티 색상 패턴 모드 - 기본 LED OFF"와 "터치 버튼 - 3. 색상 패턴 모드에서 아래 패턴 선택"의 패턴에는 3가지가 있다. 3가지 패턴에 대해 코드를 추가하고 첫 번째 "정해진 시간 간격에 따라 정해진 색상 변경"을 위해 아래와 같이 수정하였다. 

// "정해시 시간 간격에 따라 정해진 색상 변경"위해 추가
unsigned long int rgb_delay = 0; // 색상 변경 딜레이 함수 밀리 초 저장 변수  
bool rgb_change = false;         // 색상 변경 함수 트리거 플래그
bool rgb_change_start = false;   // 색상 변경용 딜레이 함수 시작
uint16_t auto_duration = 1000;   // 색상 변경 시간

void rgb_display_auto() {        // 색상 자동 변경 사용자 함수
  if (rgb_change == true) {      // 색상 변경 함수 트리거 플래그 참이면
    rgb_display();               // 색상 표시
    color_index++;               // 색상 변경 인덱스 증가
    if (color_index == 8) color_index = 0; // 색상 인덱스가 8이면 0으로 초기화
    rgb_change = false;          // 색상 변경 딜레이함수에 의해 트리거 될때까지 현재 색상 유지
  }
}

void rgb_display_delay() {        // 색상 변경용 사용자 딜레이 함수
  if (rgb_change_start == true) { // 색상 변경용 딜레이 함수 시작이면
    if (millis() - rgb_delay >= auto_duration) { // 색상 변경 시간이 되면
      rgb_delay = millis();       // 밀리초 초기화
      rgb_change = true;          // 색상 변경 함수 트리거 플래그 참
    }
  }
}

// button_act() 함수 수정 전
if (button_mode == 3) Serial.println("button_mode 3");
else if (button_mode == 4) {
  button_mode = 0;
  analogWrite(Red, 0);
  analogWrite(Green, 0);
  analogWrite(Blue, 0);
}
        
// button_act() 함수 수정 후
else if (button_mode == 3) {
  digitalWrite(ledPin, LOW);
  rgb_change_start = true;
}
else if (button_mode == 4) {
  button_mode = 0;
  analogWrite(Red, 0);
  analogWrite(Green, 0);
  analogWrite(Blue, 0);
  rgb_change_start = false; // LED OFF시 자동 패턴도 정지 
  color_index = 0; // 색상 인덱스 초기화
}

// button_act() 함수 수정 전
if (button_mode == 3) Serial.println("button_mode 3");

// button_act() 함수 수정 후
else if (button_mode == 3) {
  digitalWrite(ledPin, LOW);  // 패턴 선택 모드 표시 - 기본 LED OFF
  rgb_change_start = true;    // 패턴 선택 진입시 "정해시 시간 간격에 따라 정해진 색상 변경" 실행
}

// 패턴 선택용 변수 추가
uint8_t pattern_mode = 0;

// cap_button_act() 함수 수정 전
else if (button_mode == 3) Serial.println("button_mode3 - touch");


// cap_button_act() 함수 수정 후
else if (button_mode == 3) { 
  pattern_mode++;
  if (pattern_mode == 1) rgb_change_start = false; // 패턴 변경시 앞선 패턴 정지
  else if (pattern_mode == 2) Serial.println("pattern mode 3");
  else if (pattern_mode == 3) {
    pattern_mode = 0;
    rgb_change_start = true;
  }
}

아래 스케치를 업로드하고 버튼 모드 2에서 색상변경이 잘되는지 확인해보고 버튼 모드 3에 진입하면 바로 실행되는 "정해진 시간 간격에 따라 정해진 색상 변경"이 잘되는지도 확인해 보자.

mood_rgbled_colorTable_brightAdjust_colorSelect.ino
0.00MB

더보기
더보기
#define ledPin 13
#define Red 9
#define Green 10
#define Blue 11
#define capSensePin A0 // ADC pin (paperclip, conductive paint/fabric/thread, wire)
#define buttonPin 7

uint8_t color_index = 0;
uint8_t red_t[8] = {255, 255, 0, 0, 255, 0, 255, 210};
uint8_t green_t[8] = {255, 0, 255, 0, 255, 255, 0, 105};
uint8_t blue_t[8] = {255, 0, 0, 255, 0, 255, 255, 30};

// touch button 변수
unsigned int long cap_button_delay = 0;
bool cap_button_check = false;
uint8_t delay_count = 0;
bool start_count = false;
int val;

// tact button 변수
unsigned int long button_delay = 0;
bool button_hold = false;
bool pre_button_state = 1; // PULL_UP button 접점 붙은 상태: 0, 떨어진 상태: 1 -> 초기값 1
bool button_state = 1;  
uint8_t button_mode = 0;   // 버튼 모드용 변수 

// brightness 설정 변수
uint8_t brightness = 0;
uint8_t red_tb = 0;
uint8_t green_tb = 0;
uint8_t blue_tb = 0;

void setup() {
  Serial.begin(9600);
  pinMode(ledPin, OUTPUT);
  pinMode(Red, OUTPUT);
  pinMode(Green, OUTPUT);
  pinMode(Blue, OUTPUT);
  pinMode(capSensePin, INPUT);
  pinMode(buttonPin, INPUT_PULLUP);
}

void loop() {
  cap_button();
  cap_button_act();
  button();
  button_act();
  rgb_display_auto();
  rgb_display_delay();
}

unsigned long int rgb_delay = 0;
bool rgb_change = false;
bool rgb_change_start = false;
uint16_t auto_duration = 1000;

void rgb_display_auto() {
  if (rgb_change == true) {
    rgb_display();
    color_index++;
    if (color_index == 8) color_index = 0;
    rgb_change = false;
  }
}

void rgb_display_delay() {
  if (rgb_change_start == true) {
    if (millis() - rgb_delay >= auto_duration) {
      rgb_delay = millis();
      rgb_change = true;
    }
  }
}

void rgb_display() {
  red_tb = red_t[color_index] >> brightness;
  green_tb = green_t[color_index] >> brightness;
  blue_tb = blue_t[color_index] >> brightness;
  analogWrite(Red, red_tb);
  analogWrite(Green, green_tb);
  analogWrite(Blue, blue_tb);
}

uint8_t pattern_mode = 0;

void cap_button_act() {
  if (cap_button_check == true) {
    if (start_count == false) val = analogRead(capSensePin); // 기준값: 673 ~ 692(3.3V, 10K ohm)
    else  delay_count++; 
    if (delay_count == 0) {
      if(val > 700 || val > 500 && val < 669 ){  // 기준값보다 떨어지는 경우: 500 ~ 670 
        if (button_mode == 1) {
          brightness++;
          if (brightness == 5) brightness = 0;
          rgb_display();
        }
        else if (button_mode == 2) {
          color_index++;
          if (color_index == 8) color_index = 0;
          rgb_display();
        }
        else if (button_mode == 3) {
          pattern_mode++;
          if (pattern_mode == 1) rgb_change_start = false;
          else if (pattern_mode == 2) Serial.println("pattern mode 3");
          else if (pattern_mode == 3) {
            pattern_mode = 0;
            rgb_change_start = true;
          }
        }
        start_count = true;
      }
    }
    else if (delay_count == 3) {
      start_count = false;
      delay_count = 0;
    }
    cap_button_check = false;
  }
}

void cap_button() {
  if (millis() - cap_button_delay >= 100) {
    cap_button_delay = millis();
    cap_button_check = true;
  }
}

void button_act() {
  button_state = digitalRead(buttonPin);
  if (button_hold == false) {
    if (button_state != pre_button_state) {
      button_hold = true;
      button_delay = millis();
      if (button_state == 0) {
        button_mode ++;
        if (button_mode == 1) rgb_display();
        else if (button_mode == 2) digitalWrite(ledPin, HIGH);
        else if (button_mode == 3) {
          digitalWrite(ledPin, LOW);
          rgb_change_start = true;
        }
        else if (button_mode == 4) {
          button_mode = 0;
          analogWrite(Red, 0);
          analogWrite(Green, 0);
          analogWrite(Blue, 0);
          rgb_change_start = false;
          color_index = 0;
        }
      }
      pre_button_state = button_state;
    }
  }
}

void button() {
  if (button_hold == true) {
    if (millis() - button_delay >= 100) {
      button_hold = false;
    }
  }
}

이제 "정해진 시간 간격에 따라 무지개 색상 변경"과 "정해진 시간 간격에 따라 부드러운 랜덤 색상 변경" 두 가지만 구현하면 되는데, 무지개 색상 변경과 랜덤 색상 변경 시 상기의 "정해진 시간 간격에 따라 정해진 색상 변경" 색상이 갑자기 바뀌는 게 아니라 자연스럽게 변하도록 하는 코드를 짜기 위해서는 빛의 조합에 따른 색상 표현 방법에 대한 지식이 필요하게 되는데, 필자는 그러한 지식이 없다. 인터넷을 통해 관련 설명과 예제 코드가 있는 사이트를 찾았고 그 코드를 이용하기로 한다. 

 

참조 사이트 

https://www.instructables.com/id/How-to-Make-Proper-Rainbow-and-Random-Colors-With-/

 

상기 사이트에는 HSV에 대해 설명하고 있으며, 색상(Hue) 값은 0 ~ 360도 원형 값이고, 명도(Value)는 0~100인 퍼센트 값으로 표시되며, 채도(Saturation)를 조절하여 파스텔 톤의 색상을 만들 수는 있으나 빛의 조합으로 만들어지는 RGB LED의 색상에서는 흰색의 밝기 변화와 비슷하게 느껴질 거라고 언급하고 있다. 더불어 아래 그림처럼 무지개 색 변화를 표현하기 위한 RGB Sine wave 변화와 HSV 파의 변화를 비교하여 설명하고 있다. 또한, 이 표에 표시된 3가지 색상 표현방식 "Regular HSV, Power-conscious HSV, Sine wave"들의 RGB 변동 값을 배열로 만드는 코드를 제시하고 있으며(RGB 각 항목의 변동 패턴은 동일하다, 다만 위상차가 있다. - 각 색상 표현 방식은 한 개의 변동 값 배열만 필요함), 이 변동 값이 포함된 배열을 이용하여 무지개 색 변화를 표현하는 코드도 제시하고 있다.

* HSV: hue, saturation, value의 약어. 색상(Hue), 채도(Saturation), 명도(Value (or Brightness))로 색을 지정하는 방법

 

 

필자는 상기 사이트에서 제시하는 방식 중 Sine wave파의 변동 값에 근접한 배열 값을 이용하여 무지개 색변화를 하겠다. 

Sine wave 변동 값 배열을 만드는 코드는 언급하지 않겠다. 상기 사이트의 글을 참조하기 바란다.

무지개 색상 변화의 핵심 코드는 시간에 따라 변하는 색상(Hue) 각도 값에 맞게 각각의 RGB의 변동 값을 Sine wave 변동 값 배열과 연동시켜주는 아래 코드라 할 수 있다. 이 코드에서 알 수 있는 것은 녹색을 기준으로 색상 앵글 값이 0일 때 빨간색의 색상 각도 값은 120이고 파란색의 각도 값은 240이 되어 상기 그림과 일치하도록 짜여 있음을 알 수 있다. 

녹색 색상 각도 값 0 : 녹색 색상 값 = 0, 빨간색 = 255, 파란색 = 0

녹색 색상 각도 값 120 : 녹색 색상값 = 255, 빨간색 = 0, 파란색 = 0

녹색 색상 각도 값 240 : 녹색 색상값 = 0, 빨간색 = 0, 파란색 =255

녹색 색상 각도 값 360 : 녹색 색상값 = 0, 빨간색 = 255, 파란색 = 0

void sineLED(int angle)  {  // sine wave rainbow
  setRGBpoint(lights[(angle+120)%360], lights[angle],  lights[(angle+240)%360]);
}

아래 스케치는 상기의 제시 코드를 단순화시킨 것이다. 아두이노에 업로드해보면 RGB LED가 무지개 색으로 바뀌는 것을 볼 수 있다.

rainbow.ino
0.00MB

const uint8_t lights[360]={  // sine wave 방식 색상 각도 변화에 따른 변동값 배열
  0,   0,   0,   0,   0,   1,   1,   2,   2,   3,   4,   5,   6,   7,   8,   9, 
 11,  12,  13,  15,  17,  18,  20,  22,  24,  26,  28,  30,  32,  35,  37,  39, 
 42,  44,  47,  49,  52,  55,  58,  60,  63,  66,  69,  72,  75,  78,  81,  85, 
 88,  91,  94,  97, 101, 104, 107, 111, 114, 117, 121, 124, 127, 131, 134, 137, 
141, 144, 147, 150, 154, 157, 160, 163, 167, 170, 173, 176, 179, 182, 185, 188, 
191, 194, 197, 200, 202, 205, 208, 210, 213, 215, 217, 220, 222, 224, 226, 229, 
231, 232, 234, 236, 238, 239, 241, 242, 244, 245, 246, 248, 249, 250, 251, 251, 
252, 253, 253, 254, 254, 255, 255, 255, 255, 255, 255, 255, 254, 254, 253, 253, 
252, 251, 251, 250, 249, 248, 246, 245, 244, 242, 241, 239, 238, 236, 234, 232, 
231, 229, 226, 224, 222, 220, 217, 215, 213, 210, 208, 205, 202, 200, 197, 194, 
191, 188, 185, 182, 179, 176, 173, 170, 167, 163, 160, 157, 154, 150, 147, 144, 
141, 137, 134, 131, 127, 124, 121, 117, 114, 111, 107, 104, 101,  97,  94,  91, 
 88,  85,  81,  78,  75,  72,  69,  66,  63,  60,  58,  55,  52,  49,  47,  44, 
 42,  39,  37,  35,  32,  30,  28,  26,  24,  22,  20,  18,  17,  15,  13,  12, 
 11,   9,   8,   7,   6,   5,   4,   3,   2,   2,   1,   1,   0,   0,   0,   0, 
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
  0,   0,   0,   0,   0,   0,   0,   0};

#define Red 9
#define Green 10
#define Blue 11

void setup() {
  pinMode(Red, OUTPUT);
  pinMode(Green, OUTPUT);
  pinMode(Blue, OUTPUT);
}

void loop() {
  for (int k=0; k<360; k++) { // 30 밀리 초마다 각도 변화 증가 
    sineLED(k);
    delay(30);
  }
}

void sineLED(int angle)  {  // sine wave rainbow
  setRGBpoint(lights[(angle+120)%360], lights[angle],  lights[(angle+240)%360]);
}

void setRGBpoint(uint8_t red, uint8_t green, uint8_t blue) {  
  analogWrite(Red, red);   
  analogWrite(Green, green);
  analogWrite(Blue, blue);
}

상기 코드에 사용된 delay(30)을 대체해주기 위해 loop() 함수 내의 코드를 사용자 함수 rainbow()로 빼주고 아래와 같이 변경해 주었다.

rainbow_delay.ino
0.00MB

unsigned long int rainbow_delay = 0;
uint16_t rainbow_duration = 50;      // delay(30) -> 50 밀리 초로 변경 
bool rainbow_start = false;
bool rainbow_delay_start = false;
int rainbow_k = 0;

void rainbow() {
  if (rainbow_start == true) {
    sineLED(rainbow_k);
    rainbow_k++;
    if (rainbow_k == 360) rainbow_k = 0;
    rainbow_start = false;
  }
}

void rainbow_display_delay() {
  if (rainbow_delay_start == true) {
    if (millis() - rainbow_delay >= rainbow_duration) {
      rainbow_delay = millis();
      rainbow_start = true;
    }
  }
}

아래 스케치는 자연스럽게 변하는 랜덤 칼라 코드를 단순화시킨 것이다.

random_color.ino
0.00MB

const uint8_t lights[360]={  // uint8_t is the same as byte, uint16_t is unsigned int
  0,   0,   0,   0,   0,   1,   1,   2,   2,   3,   4,   5,   6,   7,   8,   9, 
 11,  12,  13,  15,  17,  18,  20,  22,  24,  26,  28,  30,  32,  35,  37,  39, 
 42,  44,  47,  49,  52,  55,  58,  60,  63,  66,  69,  72,  75,  78,  81,  85, 
 88,  91,  94,  97, 101, 104, 107, 111, 114, 117, 121, 124, 127, 131, 134, 137, 
141, 144, 147, 150, 154, 157, 160, 163, 167, 170, 173, 176, 179, 182, 185, 188, 
191, 194, 197, 200, 202, 205, 208, 210, 213, 215, 217, 220, 222, 224, 226, 229, 
231, 232, 234, 236, 238, 239, 241, 242, 244, 245, 246, 248, 249, 250, 251, 251, 
252, 253, 253, 254, 254, 255, 255, 255, 255, 255, 255, 255, 254, 254, 253, 253, 
252, 251, 251, 250, 249, 248, 246, 245, 244, 242, 241, 239, 238, 236, 234, 232, 
231, 229, 226, 224, 222, 220, 217, 215, 213, 210, 208, 205, 202, 200, 197, 194, 
191, 188, 185, 182, 179, 176, 173, 170, 167, 163, 160, 157, 154, 150, 147, 144, 
141, 137, 134, 131, 127, 124, 121, 117, 114, 111, 107, 104, 101,  97,  94,  91, 
 88,  85,  81,  78,  75,  72,  69,  66,  63,  60,  58,  55,  52,  49,  47,  44, 
 42,  39,  37,  35,  32,  30,  28,  26,  24,  22,  20,  18,  17,  15,  13,  12, 
 11,   9,   8,   7,   6,   5,   4,   3,   2,   2,   1,   1,   0,   0,   0,   0, 
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
  0,   0,   0,   0,   0,   0,   0,   0};

#define Red 9
#define Green 10
#define Blue 11

uint16_t color[3], nextColor[3];
long colorStep[3];
byte count, a0, a1, a2;

void setup() {
  pinMode(Red, OUTPUT);
  pinMode(Green, OUTPUT);
  pinMode(Blue, OUTPUT);
}

void setRGBpoint(uint8_t red, uint8_t green, uint8_t blue) {  
  analogWrite(Red, red);   
  analogWrite(Green, green);
  analogWrite(Blue, blue);
}

void setNextColorSine() {
  a0 = random(240);
  nextColor[count] = lights[a0] << 8;
  a1 = random(1);
  a2 = ((!a1)+count+1) % 3;
  a1 = (count+a1+1) % 3;
  nextColor[a1] = lights[(a0+100)%240] << 8;
  nextColor[a2] = 0;
}

void loop() {
  setNextColorSine();
  for (byte k=0; k<3; k++) colorStep[k] = ((long)nextColor[k] - color[k])/255;
  for (byte k=0; k<120; k++) {
    setRGBpoint((color[0] + colorStep[0] * lights[k]) >> 8,
                (color[1] + colorStep[1] * lights[k]) >> 8,
                (color[2] + colorStep[2] * lights[k]) >> 8);
    delay(30);
  }
  for (byte k=0; k<3; k++) color[k] = nextColor[k];
  setRGBpoint(color[0] >> 8, color[1] >> 8, color[2] >> 8);
  delay(100);
  count++;
  count %= 3;
}

상기 코드에 사용된 delay(30), delay(100)을 대체해주기 위해 loop() 함수 내의 코드를 사용자 함수 random_color()로 빼주고 아래와 같이 변경해 주었다.

random_color_delay_pause.ino
0.00MB

unsigned long int random_delay = 0;
uint16_t random_duration = 20;      // delay(30) -> 20 밀리 초로 변경
bool random_color_start = false;
bool random_delay_start = false;
int random_k = 0;

unsigned long int pause_delay = 0;
uint16_t pause_duration = 2000;    // delay(100) -> 2000 밀리 초로 변경(2초 동안 변경된 색상 유지)
bool random_pause = false;
bool random_resume = false;

void random_color() {
  if (random_color_start == true) {
    if (random_pause == false) {
      if (random_k == 0) {
        setNextColorSine();
        for (byte k=0; k<3; k++) colorStep[k] = ((long)nextColor[k] - color[k])/255;
      }
      setRGBpoint((color[0] + colorStep[0] * lights[random_k]) >> 8,
                    (color[1] + colorStep[1] * lights[random_k]) >> 8,
                    (color[2] + colorStep[2] * lights[random_k]) >> 8);
      random_k++;
      random_color_start = false;
      if (random_k == 120) {
        random_k = 0;
        for (byte k=0; k<3; k++) color[k] = nextColor[k];
        setRGBpoint(color[0] >> 8, color[1] >> 8, color[2] >> 8);
        pause_delay = millis();
        random_pause = true;
      }
    }
    if (random_resume == true) {
      count++;
      count %= 3;
      random_resume = false;
    }
  }
}

void random_pause_delay() {
  if (random_pause == true) {
    if (millis() - pause_delay >= pause_duration) {
      random_resume = true;
      random_pause = false;
    }
  }
}

void random_display_delay() {
  if (random_delay_start == true) {
    if (millis() - random_delay >= random_duration) {
      random_delay = millis();
      random_color_start = true;
    }
  }
}

"정해진 시간 간격에 따라 무지개 색상 변경"과 "정해진 시간 간격에 따라 부드러운 랜덤 색상 변경" 적용하기 위해 아래와 같이 수정해 주었다. 

// button_act() 함수 수정 전
else if (button_mode == 4) {
  button_mode = 0;
  analogWrite(Red, 0);
  analogWrite(Green, 0);
  analogWrite(Blue, 0);
  rgb_change_start = false;
  color_index = 0;
}

// button_act() 함수 수정 후
else if (button_mode == 4) {
  button_mode = 0;
  analogWrite(Red, 0);
  analogWrite(Green, 0);
  analogWrite(Blue, 0);
  rgb_change_start = false;
  color_index = 0;
  rainbow_delay_start = false;
  random_delay_start = false;
}

// cap_button_act() 함수 수정 전
else if (button_mode == 3) {
  pattern_mode++;
  if (pattern_mode == 1) rgb_change_start = false;
  else if (pattern_mode == 2) Serial.println("pattern mode 3");
  else if (pattern_mode == 3) {
    pattern_mode = 0;
    rgb_change_start = true;
  }
}


// cap_button_act() 함수 수정 후
else if (button_mode == 3) {
  pattern_mode++;
  if (pattern_mode == 1) {
    rgb_change_start = false;
    rainbow_delay_start = true;
  }
  else if (pattern_mode == 2) {
    rainbow_delay_start = false;
    random_delay_start = true;
  }
  else if (pattern_mode == 3) {
    pattern_mode = 0;
    rgb_change_start = true;
    random_delay_start = false;
  }
}

아래는 모든 사항이 적용된 스케치이다. 앞에서 서술한 기능들이 잘 실행되는지 확인해 보자.

mood_rgbled_colorTable_brightAdjust_colorSelect_rainbow.ino
0.01MB

더보기
더보기
const uint8_t lights[360]={  // sine wave 방식
  0,   0,   0,   0,   0,   1,   1,   2,   2,   3,   4,   5,   6,   7,   8,   9, 
 11,  12,  13,  15,  17,  18,  20,  22,  24,  26,  28,  30,  32,  35,  37,  39, 
 42,  44,  47,  49,  52,  55,  58,  60,  63,  66,  69,  72,  75,  78,  81,  85, 
 88,  91,  94,  97, 101, 104, 107, 111, 114, 117, 121, 124, 127, 131, 134, 137, 
141, 144, 147, 150, 154, 157, 160, 163, 167, 170, 173, 176, 179, 182, 185, 188, 
191, 194, 197, 200, 202, 205, 208, 210, 213, 215, 217, 220, 222, 224, 226, 229, 
231, 232, 234, 236, 238, 239, 241, 242, 244, 245, 246, 248, 249, 250, 251, 251, 
252, 253, 253, 254, 254, 255, 255, 255, 255, 255, 255, 255, 254, 254, 253, 253, 
252, 251, 251, 250, 249, 248, 246, 245, 244, 242, 241, 239, 238, 236, 234, 232, 
231, 229, 226, 224, 222, 220, 217, 215, 213, 210, 208, 205, 202, 200, 197, 194, 
191, 188, 185, 182, 179, 176, 173, 170, 167, 163, 160, 157, 154, 150, 147, 144, 
141, 137, 134, 131, 127, 124, 121, 117, 114, 111, 107, 104, 101,  97,  94,  91, 
 88,  85,  81,  78,  75,  72,  69,  66,  63,  60,  58,  55,  52,  49,  47,  44, 
 42,  39,  37,  35,  32,  30,  28,  26,  24,  22,  20,  18,  17,  15,  13,  12, 
 11,   9,   8,   7,   6,   5,   4,   3,   2,   2,   1,   1,   0,   0,   0,   0, 
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
  0,   0,   0,   0,   0,   0,   0,   0};

#define ledPin 13
#define Red 9
#define Green 10
#define Blue 11
#define capSensePin A0 // ADC pin (paperclip, conductive paint/fabric/thread, wire)
#define buttonPin 7

uint8_t color_index = 0;
uint8_t red_t[8] = {255, 255, 0, 0, 255, 0, 255, 210};
uint8_t green_t[8] = {255, 0, 255, 0, 255, 255, 0, 105};
uint8_t blue_t[8] = {255, 0, 0, 255, 0, 255, 255, 30};

// touch button 변수
unsigned int long cap_button_delay = 0;
bool cap_button_check = false;
uint8_t delay_count = 0;
bool start_count = false;
int val;

// tact button 변수
unsigned int long button_delay = 0;
bool button_hold = false;
bool pre_button_state = 1; // PULL_UP button 접점 붙은 상태: 0, 떨어진 상태: 1 -> 초기값 1
bool button_state = 1;  
uint8_t button_mode = 0;   // 버튼 모드용 변수 

// brightness 설정 변수
uint8_t brightness = 0;
uint8_t red_tb = 0;
uint8_t green_tb = 0;
uint8_t blue_tb = 0;

void setup() {
  Serial.begin(9600);
  pinMode(ledPin, OUTPUT);
  pinMode(Red, OUTPUT);
  pinMode(Green, OUTPUT);
  pinMode(Blue, OUTPUT);
  pinMode(capSensePin, INPUT);
  pinMode(buttonPin, INPUT_PULLUP);
  randomSeed(analogRead(5) + random(50));
}

void loop() {
  cap_button();
  cap_button_act();
  button();
  button_act();
  rgb_display_auto();
  rgb_display_delay();
  rainbow_display_delay();
  rainbow();
  random_display_delay();
  random_color();
  random_pause_delay();
}

// 랜덤 색상 변경
uint16_t color[3], nextColor[3];
long colorStep[3];
byte count, a0, a1, a2;

unsigned long int random_delay = 0;
uint16_t random_duration = 20;
bool random_color_start = false;
bool random_delay_start = false;
int random_k = 0;

unsigned long int pause_delay = 0;
uint16_t pause_duration = 2000;
bool random_pause = false;
bool random_resume = false;

void random_color() {
  if (random_color_start == true) {
    if (random_pause == false) {
      if (random_k == 0) {
        setNextColorSine();
        for (byte k=0; k<3; k++) colorStep[k] = ((long)nextColor[k] - color[k])/255;
      }
      setRGBpoint((color[0] + colorStep[0] * lights[random_k]) >> 8,
                    (color[1] + colorStep[1] * lights[random_k]) >> 8,
                    (color[2] + colorStep[2] * lights[random_k]) >> 8);
      random_k++;
      random_color_start = false;
      if (random_k == 120) {
        random_k = 0;
        for (byte k=0; k<3; k++) color[k] = nextColor[k];
        setRGBpoint(color[0] >> 8, color[1] >> 8, color[2] >> 8);
        pause_delay = millis();
        random_pause = true;
      }
    }
    if (random_resume == true) {
      count++;
      count %= 3;
      random_resume = false;
    }
  }
}

void random_pause_delay() {
  if (random_pause == true) {
    if (millis() - pause_delay >= pause_duration) {
      random_resume = true;
      random_pause = false;
    }
  }
}

void random_display_delay() {
  if (random_delay_start == true) {
    if (millis() - random_delay >= random_duration) {
      random_delay = millis();
      random_color_start = true;
    }
  }
}

void setNextColorSine() {
  a0 = random(240);
  nextColor[count] = lights[a0] << 8;
  a1 = random(1);
  a2 = ((!a1)+count+1) % 3;
  a1 = (count+a1+1) % 3;
  nextColor[a1] = lights[(a0+100)%240] << 8;
  nextColor[a2] = 0;
}

// 무지개 색상 변경
unsigned long int rainbow_delay = 0;
uint16_t rainbow_duration = 40;
bool rainbow_start = false;
bool rainbow_delay_start = false;
int rainbow_k = 0;

void rainbow() {
  if (rainbow_start == true) {
    sineLED(rainbow_k);
    rainbow_k++;
    if (rainbow_k == 360) rainbow_k = 0;
    rainbow_start = false;
  }
}

void rainbow_display_delay() {
  if (rainbow_delay_start == true) {
    if (millis() - rainbow_delay >= rainbow_duration) {
      rainbow_delay = millis();
      rainbow_start = true;
    }
  }
}

void sineLED(int angle)  {  // sine wave rainbow
  setRGBpoint(lights[(angle+120)%360], lights[angle],  lights[(angle+240)%360]);
}

void setRGBpoint(uint8_t red, uint8_t green, uint8_t blue) {  
  red_tb = red >> brightness;
  green_tb = green >> brightness;
  blue_tb = blue >> brightness;
  analogWrite(Red, red_tb);
  analogWrite(Green, green_tb);
  analogWrite(Blue, blue_tb);
}

unsigned long int rgb_delay = 0;
bool rgb_change = false;
bool rgb_change_start = false;
uint16_t auto_duration = 1000;

void rgb_display_auto() {
  if (rgb_change == true) {
    rgb_display();
    color_index++;
    if (color_index == 8) color_index = 0;
    rgb_change = false;
  }
}

void rgb_display_delay() {
  if (rgb_change_start == true) {
    if (millis() - rgb_delay >= auto_duration) {
      rgb_delay = millis();
      rgb_change = true;
    }
  }
}

void rgb_display() {
  red_tb = red_t[color_index] >> brightness;
  green_tb = green_t[color_index] >> brightness;
  blue_tb = blue_t[color_index] >> brightness;
  analogWrite(Red, red_tb);
  analogWrite(Green, green_tb);
  analogWrite(Blue, blue_tb);
}

uint8_t pattern_mode = 0;

void cap_button_act() {
  if (cap_button_check == true) {
    if (start_count == false) val = analogRead(capSensePin); // 기준값: 673 ~ 692(3.3V, 10K ohm)
    else  delay_count++; 
    if (delay_count == 0) {
      if(val > 700 || val > 500 && val < 669 ){  // 기준값보다 떨어지는 경우: 500 ~ 670 
        if (button_mode == 1) {
          brightness++;
          if (brightness == 5) brightness = 0;
          rgb_display();
        }
        else if (button_mode == 2) {
          color_index++;
          if (color_index == 8) color_index = 0;
          rgb_display();
        }
        else if (button_mode == 3) {
          pattern_mode++;
          if (pattern_mode == 1) {
            rgb_change_start = false;
            rainbow_delay_start = true;
          }
          else if (pattern_mode == 2) {
            rainbow_delay_start = false;
            random_delay_start = true;
          }
          else if (pattern_mode == 3) {
            pattern_mode = 0;
            rgb_change_start = true;
            random_delay_start = false;
          }
        }
        start_count = true;
      }
    }
    else if (delay_count == 3) {
      start_count = false;
      delay_count = 0;
    }
    cap_button_check = false;
  }
}

void cap_button() {
  if (millis() - cap_button_delay >= 100) {
    cap_button_delay = millis();
    cap_button_check = true;
  }
}

void button_act() {
  button_state = digitalRead(buttonPin);
  if (button_hold == false) {
    if (button_state != pre_button_state) {
      button_hold = true;
      button_delay = millis();
      if (button_state == 0) {
        button_mode ++;
        if (button_mode == 1) rgb_display();
        else if (button_mode == 2) digitalWrite(ledPin, HIGH);
        else if (button_mode == 3) {
          digitalWrite(ledPin, LOW);
          rgb_change_start = true;
        }
        else if (button_mode == 4) {
          button_mode = 0;
          analogWrite(Red, 0);
          analogWrite(Green, 0);
          analogWrite(Blue, 0);
          rgb_change_start = false;
          color_index = 0;
          rainbow_delay_start = false;
          random_delay_start = false;
        }
      }
      pre_button_state = button_state;
    }
  }
}

void button() {
  if (button_hold == true) {
    if (millis() - button_delay >= 100) {
      button_hold = false;
    }
  }
}

 

다음에는 상기 무드등에 블루투스를 연결하고 스마트폰 앱으로 무드등을 원격제어해 보겠다.

 

관련 글

[arduino] - 아두이노 - 입력 버튼 설정 방법, debouncing

[arduino] - 아두이노 - ADC 핀을 이용한 터치 센서의 구현

[arduino] - 아두이노 - 무드등 예제, RGB LED 제어

[arduino] - 아두이노 - 무드등, 블루투스 연결 스마트폰 원격제어

 

 

 

+ Recent posts