[58호]컬러테라피에 기반한 이모션 램프
2019 ICT 융합 프로젝트 공모전 참가상
컬러테라피에 기반한 이모션 램프
글 | 건양대학교 장건호
1. 심사평
칩센 컬러테라피를 이용하여 사용자의 감정 기복을 완화하려는 목표는 충분히 이해하나, 작품의 구성이 아쉽습니다. 우선 개발된 작품은 불쾌지수의 indicator로서의 역할이 더 주요해 보여, 이를 궁극적으로 해결할수 있는 에어컨이나 선풍기등과 같은 장치도 함께 동시에 컨트롤 할수 있는 형태로 구성하여 불쾌지수 자체를 낮출수 있는 방안도 함께 제안되었으면 좋았겠습니다. 또한 제작된 제품의 경우 램프 조명을 컨트롤 하는 형태이므로 매뉴얼 동작을 위한 빛과 파장에 민감한 IR 적외선 리모콘 보다는 간단한 무선 기술을 이용한 리모콘으로 제작하면 좋았겠다는 생각도 듭니다. 최근 IoT 조명들에 대한 관심이 높아지고 있고, 조명을 이용한 컬러테라피 등에 대한 아이디어도 많아 지고 있습니다. 금번에 진행한 작품을 기반으로 IoT/ICT 등으로 확장하면 더 흥미롭고 좋은 작품을 볼수 있을듯 합니다.
뉴티씨 생활에서 얻은 아이디어로 센서를 사용하여 기분을 전환할 수 있는 장치를 만든 점에서 관찰력에 많은 점수를 주고 싶습니다. 첫 작품으로 이렇게 기획하고, 실제로 만들어가면서 고민한 흔적이 많이 보입니다. 계속 노력한다면, 다음에는 더 좋은 작품을 만들수 있을 것으로 생각합니다. 작품의 난이도는 높지 않지만, 실제 생활에 도움이 될 수 있는 장치를 만들고자 노력한 점이 참 돋보이는 작품입니다.
위드로봇 최근 유행하는 IoT와 연동할 수 있으면 더 재미있는 작품이 될 것 같습니다.
펌테크 아이디어와 실용성을 겸비한 우수한 작품이라고 생각합니다. 추후 안마기, 러닝머신 등의 건강에 관련된 헬스기구 또는 흥미를 유발할 수 있는 실내외 인테리어 등과 연동해서 실생활에서 다양한 활용이 가능할 것으로 판단됩니다. 작품 자체의 시스템 구성과 기술적 난이도는 평범한 수준으로 생각이 되며 작품의 완성도를 높일 경우 힐링과 재미를 느낄 수 있는 상업용 성격을 지닌 제품으로도 충분히 활용이 가능할 것으로 생각됩니다.
2. 작품 개요
1. 개발 목표 : 날씨 변화에 따라 감정기복을 완화 시킬 요법으로 컬러 테라피를 이용하고자 한다. 이에 따라 자동으로 색이 바뀌는 램프를 개발하는 것
2. 세부 목표 : 날씨에 따라 조명의 색을 바꾸어 방안의 분위기를 바꾼다. 날씨의 객관적인 지표로 불쾌지수를 사용한다. 불쾌지수 4단계에 따라서 점진적으로 파란빛으로 방안을 채운다. 더운 여름날 높은 온도와 습도 때문에 불쾌함을 느끼는데 파란 빛을 통해 시원한 느낌을 불러 일으켜 불쾌함을 상쇄시키고자 한다. 실제 실내 등을 기획한다는 가정 하에 수동모드의 필요성을 느꼈다. 적외선 리모컨을 사용하여 보다 다양한 색을 설정할 수 있는 기능을 첨가 하였다.
3. 초기 램프 디자인 :
3. 작품 설명
3.1. 주요 동작 및 특징
· 온습도 센서를 사용하여 불쾌지수를 자동으로 계산한다.
· 불쾌지수 4단계에 따라서 백색 등에 파란빛을 첨가한다.
· 버튼을 통해 자동 모드에서 수동모드로 전환, 반대로도 가능하다,
· 적외선 리모컨을 사용하여 다양한 색으로 설정 가능하다.
3.2. 핵심 강조 사항
가. LCD 한글 및 이모티콘 출력
아두이노 LCD는 기본적으로 한글이 지원이 되지 않는다. ‘불쾌지수’ 글자를 표현하기 위해 커스텀 문자를 디자인 해야만 했다. ‘Design a Custom Character for an LCD’ 사이트를 이용하면 내가 원하는 문자를 디자인 할 수 있고 그에 해당되는 픽셀 좌표를 2진수로 바꿔준다. 글자 하나하나를 직접 만들어서 적재적소에 사용했다. 일단 기본적으로 LCD에 한글로 ‘불쾌지수’를 프린트 했고 얼굴 이모티콘을 4가지를 만들어서 불쾌지수 4단계 마다 긍정에서 부정으로 바뀌도록 프로그래밍 하였다.
나. 불쾌지수 계산
온습도 센서로 읽은 값은 건구 온도이다. 하지만 실제 불쾌지수 계산에는 습구온도를 사용하고 건구온도를 습도온도로 변환하는 식은 또한 습도에 따라 바뀌기 때문에 상당히 복잡하다. 그래서 오차를 인정하고 대략적인 값으로 계산해주는 식을 찾았다. 온도와 습도 값을 각각 변수에 저장하고 수식을 계산하여 60~90 사이의 값을 도출해내었다. 1.8*온도-0.55*(1-습도/100)*(1.8*온도-26)+32;
다. 적외선 리모컨 수동모드
실용적인 램프를 설계한다는 가정 하에 커스텀 모드를 만들 필요성이 있었고 적외선 리모컨을 사용하기로 하였다. 가장 먼저 간단한 적외선 수신회로를 만들어서 각 리모컨 버튼에 해당된 16진수 번호를 알아냈다. 그다음 원하는 색을 뽑아내기 위해서 RGB led를 3개의 가변 저항으로 제어하는 회로를 만들어서 수동으로 색을 조절하고 그때에 해당하는 가변저항 값을 0~255 범위로 변환하여 설정 값을 알아냈다. 최종적으로 리모컨을 사용하여 램프 색을 바꿀 수 있게 되었다. < on, off, 빨, 주, 노, 초, 파, 하늘, 보 > 온오프와 7가지 색 설정
라. 자동 수동 모드 전환
불쾌지수에 따른 자동모드와 적외선 리모컨 수신모드가 서로 따로 loop를 돌아야 했었다. 일반적인 구조로는 실행할 수 없었고 방법을 찾던 중 인터럽트 함수를 이용하기로 했다. 버튼의 전압이 바뀔 때마다 count 변수 값을 0 혹을 1로 바뀌도록 하였고, 0일 땐 자동 1일 땐 수동. 이런 식으로 count값이 바뀌지 않는 이상 해당 명령어는 계속 루프로 수행되었다. 이렇게 인터럽트를 이용하여 모드 스위칭 문제를 해결하였다.
3.3. 전체 시스템 구성
3.4. 개발 환경 및 사용 부품
4. 단계별 제작 과정
4.1. 적외선 리모컨 작동원리 습득, 킷값 조사
적외선 통신은 특정 리모컨 키를 눌렀을 때마다 각기 다른 신호를 쏜다. 갖고 있는 리모컨의 모든 버튼의 시리얼 값을 알아내어 차 후 원하는 동작을 실행시킬 때 이용하고자 한다.
간단하게 IR 수신기 테스트 회로를 만들었다. 센서의 작동 유무 확인과 리모컨 버튼별 값을 추출하기 위함이다. 적외선 신호를 받을 때마다 LED가 켜지게 하여 시각적으로 제대로 작동하고 있음을 확인했다. 시리얼통신을 통해서 각 버튼별 HEX값을 확인한다.
int RECV_PIN = 13;
IRrecv irrecv(RECV_PIN);
decode_results results;
void setup()
{
Serial.begin(9600);
irrecv.enableIRIn(); // Start the receiver
}
void loop() {
if (irrecv.decode(&results)) {
Serial.println(results.value, HEX);
irrecv.resume(); // Receive the next value
}
}
HEX 값은 다음과 같다.
4.2. 온습도 센서 작동원리 습득, 불쾌지수 계산식 조사
온습도 센서는 DHT 11 모듈을 사용하였다. 온도와 습도를 아날로그 신호로 출력해주며 이 외부 정보를 내부에서 다시 계산식을 통해 불쾌지수를 계산하였다.
불쾌지수는 Thom이 제창한 것으로서 기온과 습도의 조합으로 구성되어 있으며 일반적으로 온습도지수라고도 한다. 불쾌지수에 따른 신체증상을 보면 DI 68 이하(20℃ 이하)는 전원 쾌적, DI 70(21℃)은 불쾌를 나타냄, DI 75(24℃)은 10% 정도 불쾌, DI 80(26.5℃)은 50% 정도 불쾌, DI 83(28.5℃)은 전원 불쾌, DI 86(30.0℃)은 매우 불쾌한 것으로 보고 있다. 원래 불쾌지수 DI=0.4(건구온도+습구온도)+15로 화씨온도를 사용하도록 고안되었으나, DI=0.72(건구온도+습구온도)+40.6으로 수정하여 섭씨온도로도 사용할 수 있다. [출처 : 네이버 지식백과 불쾌지수 [不快指數, discomfort index] (지구과학사전, 2009. 8. 30. 북스힐]
따라서 다음과 같은 수식을 세울 수 있다.
불쾌지수 = 1.8 * 건구 온도 -0.55*(1-습도/100)*(1.8*건구 온도-26)+32
제품의 전면부에 표시할 목적으로 각 불쾌지수의 4단계에 따라 표시등이 켜지도록 하였다. (전원쾌적 : Blue , 불쾌 : Green, 10%불쾌: Yellow , 50% 불쾌 : Red)
#include<DHT.h> //DHT.h 라이브러리 추가
DHT dht(2, DHT11); //DHT 설정 dht(핀, DHT종류)
void setup() {
pinMode(13, OUTPUT);
pinMode(12, OUTPUT);
pinMode(11, OUTPUT);
pinMode(10, OUTPUT);
Serial.begin(9600); //시리얼모니터 시작
}
void loop() {
delay(1000);
int tem = dht.readTemperature(); //온도 값 정수형 변수 tem에 저장
int hum = dht.readHumidity(); //습도 값 정수형 변수 hum에 저장
int rate = 1.8*tem-0.55*(1-hum/100)*(1.8*tem-26)+32;
Serial.print(“Temperature :”); //Temperature : 글자 출력
Serial.print(tem); //측정된 온도 값 출력
Serial.print(“C “); //온도C로 표현
Serial.print(“Humidity : “);
Serial.print(hum);
Serial.print(“% “); //온도와 동일
Serial.println(rate);
if( rate < 68 ){
digitalWrite(10, HIGH);
digitalWrite(11,LOW);
digitalWrite(12,LOW);
digitalWrite(13,LOW);
}else if(rate >=68 && rate <75){
digitalWrite(10,LOW);
digitalWrite(11,HIGH);
digitalWrite(12,LOW);
digitalWrite(13,LOW);
}else if (rate >=75 && rate <80){
digitalWrite(10,LOW);
digitalWrite(11,LOW);
digitalWrite(12,HIGH);
digitalWrite(13,LOW);
}else{
digitalWrite(10,LOW);
digitalWrite(11,LOW);
digitalWrite(12,LOW);
digitalWrite(13,HIGH);
}
}
4.3. 가변저항 RGB 회로로 원하는 색상 값 조사
방안의 온습도를 측정하여 불쾌지수를 계산하고 또한 그 값에 따라 4단계로 나누어 표시등 제어 까지 완료 하였다. 표시등 제어 뿐만 아니라 RGB LED를 제어하여 점진적으로 파란빛을 첨가하는 건 일도 아닐 것이다. 하지만 실제로 실내 전등을 제작한다는 목표아래 수동으로 커스터 마이징 할 수 있는 전등의 필요성이 느껴졌다. 앞서 적외선 리모컨을 사용할 준비는 완료 되었다. RBG led의 각 색상 값을 기존의 색 배합 값으로 적용하였을 때 원하는 색과 분위기를 연출할 수 없었다. 그래서 각 색상 값을 가변저항으로 컨트롤 하는 회로를 만들어서 수동으로 색 배합을 하였고 원하는 색이 나올 때마다 시리얼 통신을 통해 수치를 확인하였다.
원하는 색상 값을 알아낸 후 RGB led를 적외선 리모컨을 통해 제어하는 시스템을 만들었다. 전체 전원 trun, off와 red, orenge, yellow, green, blue, navy, purple 등 6개의 색을 설정했다.
int red =6;
int green=5;
int blue=3;
void setup (){
pinMode(red, OUTPUT);
pinMode(green, OUTPUT);
pinMode(blue, OUTPUT);
Serial.begin(9600);
}
void loop(){
int redco = analogRead(A0);
int greenco = analogRead(A1);
int blueco = analogRead(A2);
int redbri = map(redco, 0, 1023, 0, 255);
int greenbri = map(greenco, 0, 1023, 0, 255);
int bluebri = map(blueco, 0, 1023, 0, 255);
analogWrite(red, redbri);
analogWrite(green, greenbri);
analogWrite(blue, bluebri);
Serial.println(String(redbri)+String(“..”)+String(greenbri)+String(“..”)+String(bluebri));
}
온습도 센서로 불쾌지수를 계산하게 되었고, 적외선 리모컨을 통해 led를 제어할 수 있게 되었다. 물론 불쾌지수의 단계별로 led 제어도 가능하다.
4.4. LCD 내용 출력하기
온도와 습도를 첫째 줄에 프린트 하였고 둘째 줄에 ‘불쾌지수’와 함께 불쾌지수 4단계별로 변화하는 이모티콘을 삽입하였다.
1. : ) 2. :| 3. : / 4. : (
이모티콘과 불쾌지수 한글 단어를 프린트하기 위해 세부 픽셀 하나하나를 컨트롤 할 필요가 있었고 ‘Design a Custom Character for an LCD’ 웹사이트를 이용했다.
왼쪽 사각형 배열을 클릭하여 원하는 모양으로 만들면 자동으로 각 픽셀별 2진수 코드를 계산해준다. 좀 더 직관적인 정보 습득을 위해 온도와 습도를 제공하고 한글과 이모티콘을 활용했다.
4.5. 인터럽트 함수 습득. 버튼으로 모드전환 기능 수행
불쾌지수에 따른 자동모드와 적외선 리모컨 수신모드가 서로 따로 loop를 돌아야 했었다. 일반적인 구조로는 실행할 수 없었고 방법을 찾던 중 인터럽트 함수를 이용하기로 했다. 버튼의 전압이 바뀔 때마다 count 변수 값을 0 혹은 1로 바뀌도록 하였고 0일 땐 자동 1일 땐 수동. 이런 식으로 count값이 바뀌지 않는 이상 해당 명령어는 계속 루프로 수행되었다. 이렇게 인터럽트를 이용하여 모드 스위칭문제를 해결하였다.
4.6. 회로·작품 제작
5. 결과물
6. 회로도 및 소스코드와 참고문헌
6.1. 회로도
6.2. 소스코드
#include <IRremote.h> #include<DHT.h>
#include <Wire.h> #include <LiquidCrystal_I2C.h> // 라이브러리 불러옴
DHT dht(9, DHT11); // 온습도 센서 핀 지정
int RECV_PIN = 8; // 적외선 수신부 핀 지정
int L_r = 6; int L_g = 5; int L_b = 3; // 이모션 램프 변수 설정
IRrecv irrecv(RECV_PIN); // IR리모콘 사용을 위한 클래스 생성
decode_results decodedSignal; // IR리모콘으로부터 받은 데이터
LiquidCrystal_I2C lcd(0x3f, 16, 2); // LCD 준비
int button = 2; volatile int flag = 0; volatile int count = 1; // 인터럽트 변수 설정
byte smile1[8] = {B00000, B01010, B01010, B00000, B10001, B10001, B01110, B00000};
byte smile2[8] = {B00000, B01010, B01010, B00000, B00000, B11111, B00000, B00000};
byte smile3[8] = {B00000, B01010, B01010, B00000, B00000, B11110, B00001, B00000};
byte smile4[8] = {B00000, B01010, B01010, B00000, B00000, B01110, B10001, B10001};
byte cel[8] = {B01000, B10100, B01000, B00110, B01001, B01000, B01001, B00110};
byte muc[8] = {B00000, B01111, B01111, B01001, B01001, B11001, B11011, B00011};
byte bul[8] = {B01010, B01110, B01010, B11111, B00100, B01110, B01110, B01110};
byte que[8] = {B11101, B00101, B11101, B00101, B10111, B11101, B00101, B00101};
byte ji[8] = {B00001, B11101, B01001, B01001, B10101, B10101, B00001, B00001};
byte soo[8] = {B00100, B01010, B10001, B00000, B11111, B00100, B00100, B00100};
// lcd 문자 커스텀 디자인
void setup() {
digitalWrite(button, INPUT_PULLUP);
attachInterrupt( digitalPinToInterrupt(button), BUTTON, CHANGE); // 인터럽트 함수 셋팅
lcd.init(); // lcd 초기화
lcd.backlight(); // lcd 라이트 켜기
lcd.createChar(1, smile1); lcd.createChar(2, smile2);
lcd.createChar(3, smile3); lcd.createChar(4, smile4);
lcd.createChar(5, bul); lcd.createChar(6, que);
lcd.createChar(7, ji); lcd.createChar(8, soo); // lcd 커스텀 문자
pinMode(L_r, OUTPUT); pinMode(L_g, OUTPUT); pinMode(L_b, OUTPUT); // 이모션 램프 핀 설정
pinMode(13, OUTPUT); pinMode(12, OUTPUT);
pinMode(11, OUTPUT); pinMode(10, OUTPUT); // 불쾌지수 표시등 핀 설정
irrecv.enableIRIn(); // 리모콘 수신 시작
Serial.begin(9600); //시리얼모니터 시작
}
void loop() {
if (count == 0) { // 인터럽트 입력 후 카운트가 0일때 수동모드 전환
lcd.setCursor(0, 0); lcd.print(“Customizing Mode”);
lcd.setCursor(0, 1); lcd.print(“ By. KONO “);
digitalWrite(10, LOW); digitalWrite(11, LOW);
digitalWrite(12, LOW); digitalWrite(13, LOW); // 수동 모드 초기설정
if (irrecv.decode(&decodedSignal) == true) { // IR 수신값이 있는지 판단.
Serial.println(decodedSignal.value);
switch (decodedSignal.value) {
case 0xFFA25D: analogWrite(L_r, 255); analogWrite(L_g, 255); analogWrite(L_b, 255);
break; // CH- -> LED on
case 0xFF629D: analogWrite(L_r, 0); analogWrite(L_g, 0); analogWrite(L_b, 0);
break; // CH -> LED OFF
case 0xFF30CF: analogWrite(L_r, 255); analogWrite(L_g, 0); analogWrite(L_b, 0);
break; // 1 -> red on
case 0xFF18E7: analogWrite(L_r, 255); analogWrite(L_g, 37); analogWrite(L_b, 0);
break; // 2 -> orange on
case 0xFF7A85: analogWrite(L_r, 255); analogWrite(L_g, 88); analogWrite(L_b, 0);
break; // 3 -> yellow on
case 0xFF10EF: analogWrite(L_r, 0); analogWrite(L_g, 255); analogWrite(L_b, 0);
break; // 4 -> green on
case 0xFF38C7: analogWrite(L_r, 0); analogWrite(L_g, 0); analogWrite(L_b, 255);
break; // 5 -> blue on
case 0xFF5AA5: analogWrite(L_r, 0); analogWrite(L_g, 255); analogWrite(L_b, 255);
break; // 6 -> sky on
case 0xFF42BD: analogWrite(L_r, 255); analogWrite(L_g, 0); analogWrite(L_b, 255);
break; // 7 -> purple on
} delay(100); irrecv.resume(); // IR 다음 데이터 수신
}
} else if (count == 1) { // 인터럽트 입력 후 카운트가 0일때 자동모드 전환
delay(1000);
int tem = dht.readTemperature(); //온도 값 정수형 변수 tem에 저장
int hum = dht.readHumidity(); //습도 값 정수형 변수 hum에 저장
int rate = 1.8 * tem – 0.55 * (1 – hum / 100) * (1.8 * tem – 26) + 32;
// 불쾌지수 계산 변수 rate에 저장
Serial.print(“Temperature :”); Serial.print(tem); Serial.print(“C “);
Serial.print(“Humidity : “); Serial.print(hum); Serial.print(“% “);
Serial.println(rate); Serial.print(“count”); Serial.println(count); // 센싱값 시리얼 통신
lcd.clear();
lcd.setCursor(2, 0); lcd.print(“T:”); lcd.print(tem); lcd.print(“c’”);
lcd.setCursor(9, 0); lcd.print(“H:”); lcd.print(hum); lcd.print(“%”); // lcd 온습도 프린트
lcd.setCursor(3, 1); for (int i = 5; i < 9; i++) { lcd.write(byte(i));}//’불쾌지수’ 프린트
if ( rate < 68 ) {
digitalWrite(10, HIGH); digitalWrite(11, LOW);
digitalWrite(12, LOW); digitalWrite(13, LOW); // 불쾌지수 표시등
lcd.setCursor(7, 1); lcd.print(“: “); lcd.print(rate);
lcd.print(“ “); lcd.write(byte(1)); // 이모티콘 설정
analogWrite(L_r, 255); analogWrite(L_g, 255); analogWrite(L_b, 255); // 이모션 램프 제어
} else if (rate >= 68 && rate < 75) {
digitalWrite(10, LOW); digitalWrite(11, HIGH);
digitalWrite(12, LOW); digitalWrite(13, LOW); // 불쾌지수 표시등
lcd.setCursor(7, 1); lcd.print(“: “); lcd.print(rate);
lcd.print(“ “); lcd.write(byte(2)); // 이모티콘 설정
analogWrite(L_r, 127); analogWrite(L_g, 127); analogWrite(L_b, 255);// 이모션 램프 제어
} else if (rate >= 75 && rate < 80) {
digitalWrite(10, LOW); digitalWrite(11, LOW);
digitalWrite(12, HIGH); digitalWrite(13, LOW); //불쾌지수 표시등
lcd.setCursor(7, 1); lcd.print(“: “); lcd.print(rate);
lcd.print(“ “); lcd.write(byte(3)); //이모티콘 설정
analogWrite(L_r, 63); analogWrite(L_g, 63); analogWrite(L_b, 255);// 이모션 램프 제어
} else {
digitalWrite(10, LOW); digitalWrite(11, LOW);
digitalWrite(12, LOW); digitalWrite(13, HIGH); //불쾌지수 표시등
lcd.setCursor(7, 1); lcd.print(“: “); lcd.print(rate);
lcd.print(“ “); lcd.write(byte(4)); // 이모티콘 설정
analogWrite(L_r, 0); analogWrite(L_g, 0); analogWrite(L_b, 255);// 이모션 램프 제어
}
}
}
void BUTTON () { // 인터럽트 함수 버튼을 누르는 동작시
if (digitalRead(button) == LOW) {
if (count == 1) {
if (flag == 0) { flag = 1; count = count – 1;
Serial.print(“count”); Serial.println(count); } else {}
} else if (count == 0) {
if (flag == 0) { flag = 1; count = count + 1;
Serial.print(“count”); Serial.println(count); } else {}
}
} else {
flag = 0;
}
}
LCD 2진수 변환 사이트 : https://mikeyancey.com/hamcalc/lcd_characters.php