코코아팹은 누구나 창의적 아이디어를 현실로 만들어 낼 수 있도록
만들고, 공유하고, 배울 수 있는 터전이
되고자 합니다.
아이디와 비밀번호를 잊으셨나요?아이디 / 비밀번호 찾기
코코아팹 회원이 아니신가요? 회원가입
2015-10-13 16:05:26
안녕하세요!
한글 시계 프로젝트 이후에 정말 오랜만에 진행하는 프로젝트인 것 같네요!
이전에 날씨 정보를 알려주는 구름 조명 프로젝트를 진행했었는데요!
많은 분들이 관심을 주셨고, 피드백도 해주셔서 피드백 내용을 바탕으로 부족한 점을 보완해 업그레이드 버젼의 구름 조명을 만들어보기로 결심했습니다.
자 그럼 시작해볼까요?
날씨 정보를 알려주는 구름 조명에 대한 피드백 중 제가 주목한 내용은 다음과 같습니다.
1. 조명인데 계속해서 날씨를 표현한다면, 신경이 쓰일 것 같다.
- 날씨 정보를 알려주는 기능에 너무 치중했던 것 같습니다. 기본적으로 '조명'인데 말이죠. 피드백이 아니였다면 생각해보지 못했을 부분이였습니다.
2. 날씨 정보를 표현하는 것 이외의 조명의 기능이 있었으면 좋겠다.
- 이번 프로젝트에서는 조명의 기능에 조금 더 비중을 둬야겠다는 생각이 들었습니다.
이번 프로젝트에서 만들 것은 날씨를 알려주는 무드램프입니다.
가변 저항 3개와 버튼 2개가 부착되어 있는데요.
각각의 가변 저항을 통해 램프의 RGB 색상을 조절할 수 있습니다.
버튼의 각 기능은 다음과 같습니다.
- on, off 버튼 : 무드 램프를 키고 끄는 기능을 합니다.
- 날씨 버튼 : 날씨 정보를 표현하는 기능을 합니다.
정리해보면 on,off 버튼을 통해 무드 램프를 키고 끌 수 있으며, 기본 모드는 무드램프입니다. 기본 모드에서는 가변 저항을 이용해 사용자가 원하는 색상으로 조절이 가능합니다.
사용자가 날씨를 알고 싶을 때, 날씨 버튼을 누르면 날씨 정보를 표현한 후 다시 기본 모드로 돌아오게 됩니다.
즉 사용자 입장에서는 평소에는 조명으로 사용하고, 날씨 정보가 궁금할 때만 날씨 버튼을 눌러주면 되겠죠?
이 프로젝트에서는 Wi-Fi 쉴드, 버튼, 가변 저항을 사용합니다.
코코아팹 튜토리얼을 참고해 미리 사용법을 익혀두세요!
- Wi-Fi 쉴드를 이용하여 API를 통해 인터넷에서 날씨 정보 읽어오기
NO | 부품명 | 수량 | 상세 설명 |
1 | 오렌지보드 | 1 | 아두이노 UNO |
2 | Wi-Fi 쉴드 | 1 | |
3 | 가변 저항 | 3 | |
4 | 버튼 | 2 | PUSH 버튼 |
5 | 저항 | 3 | 330Ω |
6 | LED | 1 | RGB LED |
7 | 점퍼케이블 | 15개 이상 | |
8 | 브레드 보드 | 1 |
부품명 | 오렌지보드 | Wi-Fi 쉴드 | 가변 저항 | PUSH 버튼 | RGB LED |
파트 | ![]() |
![]() |
![]() |
![]() |
![]() |
<참고사항>
이번 프로젝트에서는 와이파이 쉴드를 사용합니다.
아래의 회로에서도 와이파이 쉴드가 결합된 상태로 이해해주시면 됩니다.
즉 와이파이 쉴드를 오렌지보드와 결합 후 와이어링 해주세요 ;)
기본 코드 사용 방법은 Wi-Fi 쉴드를 이용하여 API를 통해 인터넷에서 날씨 정보 읽어오기와 동일합니다.
변경 사항은 openweathermap API의 사용법입니다.
기존의 openweathermap API 사용시 API key를 별도로 요구하지 않았지만, 현재는 openweathermap에 가입 후 API key를 할당받아야합니다.
자세한 내용은 아래 링크를 참고하세요!
#include "SPI.h"
#include "WiFi.h"
char ssid[] = "WiFi SSID"; //와이파이 ID
char pass[] = "WiFi password"; //와이파이 비밀번호
String location = "Busan";
WiFiServer server(80);
WiFiClient client;
IPAddress hostIp;
uint8_t ret;
String currentLine = ""; //서버에서 전송된 데이터 String 저장
String codeString = ""; //날씨 코드 저장 변수
boolean readingCode = false; //코드 데이터가 있는지 여부 판단
int redVal; //RGB LED red 값 변수
int greenVal; //RGB LED green 값 변수
int blueVal; //RGB LED blue 값 변수
int redPin= 3; //RGB LED red 핀을 3번에 연결
int greenPin =5; //RGB LED green 핀을 5번에 연결
int bluePin =6; //RGB LED blue 핀을 6번에 연결
int weatherButton = 9; //날씨 데이터 파싱 동작을 위한 버튼을 9번에 연결
int powerButton = 8; //조명 on, off 동작을 위한 버튼을 8번에 연결
//on, off 버튼 토글 동작을 위한 변수
int oneTimeFlag;
boolean onOffStatus;
//날씨 파싱 동작을 위한 변수
boolean parsingStart = false;
int weatherCode;
int temp = 0;
void setup() {
//버튼 셋팅(INPUT 설정)f
pinMode(powerButton,INPUT_PULLUP);
pinMode(weatherButton,INPUT_PULLUP);
//LED 셋팅(OUTPUT 설정)
pinMode(redPin,OUTPUT);
pinMode(greenPin,OUTPUT);
pinMode(bluePin,OUTPUT);
//각 변수에 정해진 공간 할당
currentLine.reserve(100);
codeString.reserve(10);
Serial.begin(115200);
//Wi-Fi 연결 시도
delay(10);
Serial.println("Connecting to WiFi....");
WiFi.begin(ssid,pass);
server.begin();
Serial.println("Connect success!");
Serial.println("Waiting for DHCP address");
while(WiFi.localIP() == INADDR_NONE) {
Serial.print(".");
delay(300);
}
printWifiData(); //Wi-Fi 정보 출력
}
void loop() {
/*on, off 버튼 토글 설정
off : onOffStatus = 0
on : onOffStatus = 1*/
int i = digitalRead(weatherButton); //날씨 버튼 상태를 변수 i에 저장
if(digitalRead(powerButton)==LOW){ //power 버튼이 눌러지면 onOffstatus의 상태값 토글
if(oneTimeFlag==0){
oneTimeFlag = 1;
onOffStatus = !onOffStatus;
}
}
else{
oneTimeFlag = 0;
}
if(onOffStatus==0){ //onOffStatus의 상태가 0이면
LEDset(0,0,0); //led 소등
}
//날씨 데이터 파싱 및 RGB led 색상 조절 모드
else{
if(i == 0){ //weatherButton의 상태가 0이라면
parsingStart = true; //parsingStart의 상태값 변경
}
while(parsingStart){ //parsingStart의 상태값이 참일동안
connectToServer(); //서버 연결(openweathermap API에 날씨데이터 호출)
if(client.connected()){ //클라이언트에 연결되어 있다면
delay(3000); //**
while(client.available()){ //클라이언트에 들어온 데이터가 있다면
char inChar = client.read(); //들어온 데이터를 inChar에 저장
currentLine += inChar; //inChar에 저장된 char 변수는 currenLine에 저장됨
Serial.print(inChar);
if(inChar == '\n'){ //줄바꿈(\n) 문자열이 전송되면 데이터를 저장하지 않음
currentLine = "";
}
//날씨 코드 데이터가 전송되었는지 확인
if(currentLine.endsWith("<weather number=")){ //currentLine의 string이 <weather number=로 끝났다면
readingCode = true; //날씨 코드 데이터를 받을 준비를 한다
codeString = "";
}
if(readingCode){
if(inChar != 'v'){ //inChar string에 v가 전송될 때까지
codeString += inChar; //codeString에 저장
}
else{ //전송된 문자가 v라면
readingCode = false; //저장 중지
weatherCode = getInt(codeString); //저장한 날씨 코드 string을 integer로 변환
//Serial.print("cccccccccccc");
//Serial.print("weather : "); //시리얼 모니터로 날씨 코드 출력
//Serial.println(codeString);
}
}
}
}
else{
Serial.println("connect fail"); //클라이언트에 연결되지 않았다면 시리얼 모니터창에 connect fail 출력
errorLED();
//weatherCode=0;
}
//날씨 코드 분류
if(weatherCode>299 && weatherCode<532){ //rainny
rainnyweatherLED();
}
else if(weatherCode>599 && weatherCode<623){ //snow
snowweatherLED();
}
else if(weatherCode>700 && weatherCode<782){ //mist
cloudyweatherLED();
}
else if(weatherCode >799 && weatherCode<802){ //clear and sunny
clearweatherLED();
}
else if(weatherCode>801 && weatherCode<805){ //cloudy
cloudyweatherLED();
}
// client.stop();
parsingStart = false; //날씨 데이터를 파싱하고 LED로 날씨를 표현한 후 상태값 변경
weatherCode=0;
}
//조명모드
redVal = map(analogRead(A0),0,1023,0,255);
greenVal = map(analogRead(A1),0,1023,0,255);
blueVal = map(analogRead(A2),0,1023,0,255);
LEDset(redVal,greenVal,blueVal);
}
}
//Wi-Fi 서버 연결 함수
void connectToServer() {
client.stop();
Serial.println("connecting to server...");
//String content = "";
if (client.connect(hostIp, 80)) {
//Serial.println("Connected! Making HTTP request to api.openweathermap.org for "+location+"...");
//Serial.println("GET /data/2.5/weather?q="+location+"&mode=xml");
client.println("GET /data/2.5/weather?q="+location+"&mode=xml&APPID={APPID}");
//위에 지정된 주소와 연결한다.
client.print("HOST: api.openweathermap.org\n");
client.println("User-Agent: launchpad-wifi");
client.println("Connection: close");
Serial.println("Weather information for "+location);
}
}
void printHex(int num, int precision) {
char tmp[16];
char format[128];
sprintf(format, "%%.%dX", precision);
sprintf(tmp, format, num);
Serial.print(tmp);
}
void printWifiData() {
// Wifi쉴드의 IP주소를 출력
Serial.println();
Serial.println("IP Address Information:");
IPAddress ip = WiFi.localIP();
Serial.print("IP Address: ");
Serial.println(ip);
//MAC address출력
byte mac[6];
WiFi.macAddress(mac);
Serial.print("MAC address: ");
printHex(mac[5], 2);
Serial.print(":");
printHex(mac[4], 2);
Serial.print(":");
printHex(mac[3], 2);
Serial.print(":");
printHex(mac[2], 2);
Serial.print(":");
printHex(mac[1], 2);
Serial.print(":");
printHex(mac[0], 2);
Serial.println();
//서브넷 마스크 출력
IPAddress subnet = WiFi.subnetMask();
Serial.print("NetMask: ");
Serial.println(subnet);
//게이트웨이 주소 출력
IPAddress gateway = WiFi.gatewayIP();
Serial.print("Gateway: ");
Serial.println(gateway);
Serial.print("SSID: ");
Serial.println(WiFi.SSID());
ret = WiFi.hostByName("api.openweathermap.org", hostIp);
Serial.print("ret: ");
Serial.println(ret);
Serial.print("Host IP: ");
Serial.println(hostIp);
Serial.println("");
}
//정수 변환을 위한 함수
int getInt(String input){
int i = 2;
while(input[i] != '"'){
i++;
}
input = input.substring(2,i);
char carray[20];
input.toCharArray(carray, sizeof(carray));
temp = atoi(carray);
return temp;
}
//RGB 색 제어를 위한 함수
void LEDset(int red,int green, int blue){
analogWrite(redPin,red);
analogWrite(greenPin,green);
analogWrite(bluePin,blue);
}
//LED 패턴 함수
//clear and sunny, 무지개빛 출력
void clearweatherLED(){
for(int i=0; i<2;i++){
for(int j =0; j<=255;j++) {
analogWrite(redPin, 255-j);
analogWrite(greenPin, j);
delay(5);
}
for(int j =0; j<255;j++) {
analogWrite(greenPin, 255-j);
analogWrite(bluePin, j);
delay(5);
}
for(int j =0; j<255;j++) {
analogWrite(bluePin, 255-j);
analogWrite(redPin, j);
delay(5);
}
}
}
//cloudy and mist, 흰색빛으로 dimming
void cloudyweatherLED(){
for(int i=0;i<5;i++){
for(int j=0; j<120; j++){
LEDset(j,j,j);
delay(20);
}
}
}
//rainny and thunder, 노란색 빛으로 dimming후 blink
void rainnyweatherLED(){
for(int i=0;i<3;i++){
for(int j=0; j<256; j++){
LEDset(j,j,0);
delay(7);
}
}
for(int i=0; i<10;i++){
LEDset(255,255,0);
delay(50);
LEDset(0,0,0);
delay(50);
}
}
//snow, 파란색 빛으로 blink
void snowweatherLED(){
for(int i=0; i<6;i++){
LEDset(0,255,255);
delay(400);
LEDset(0,0,0);
delay(400);
}
}
//connect error, 빨간색 빛으로 blink
void errorLED(){
for(int i=0; i<256; i++){
LEDset(i,0,0);
delay(20);
LEDset(0,0,0);
}
}
현재 기능 구현을 위한 기계부만 완성되어 있는 상태입니다.(RGB LED는 5개를 사용 할 예정입니다)
하지만 이대로라면 전혀 무드램프라는 생각이 들지 않겠죠?
조명스러운 외관이 필요할 것 같습니다.
스케치업을 사용해 외관 설계(디자인)을 진행하였습니다.
귀여운 구름 모양입니다.
구름의 입은 버튼으로 날씨 버튼입니다. 날씨가 궁금할 때 앞쪽의 버튼을 누르면 날씨를 표현해준 후 다시 조명 모드로 전환됩니다.
무드 램프의 뒷면에는 버튼과 가변 저항을 배치하였습니다.
버튼은 ON, OFF 버튼으로 조명을 키고, 끄는데 사용되며, 가변 저항은 조명의 색상을 조절하는데 사용됩니다. 각 각 LED의 R, G, B를 조절할 수 있습니다.
다음 프로젝트에서는 3D 프린터를 이용해 부품을 출력한 후 배치하는 과정과 완성품에 대해 소개하도록 하겠습니다. 기대해주세요!
Klant