프로젝트

나도 메이커! 메이커스 여러분들의 작품/프로젝트를 공유하는 공간입니다.

날씨 정보를 알려주는 무드 램프 만들기[part 1]

2015-10-13 16:05:26

안녕하세요!

한글 시계 프로젝트 이후에 정말 오랜만에 진행하는 프로젝트인 것 같네요!

 

이전에 날씨 정보를 알려주는 구름 조명 프로젝트를 진행했었는데요!

많은 분들이 관심을 주셨고, 피드백도 해주셔서 피드백 내용을 바탕으로 부족한 점을 보완해 업그레이드 버젼의 구름 조명을 만들어보기로 결심했습니다.

자 그럼 시작해볼까요?

 

 

 

개요

 

날씨 정보를 알려주는 구름 조명에 대한 피드백 중 제가 주목한 내용은 다음과 같습니다. 

1. 조명인데 계속해서 날씨를 표현한다면, 신경이 쓰일 것 같다.

- 날씨 정보를 알려주는 기능에 너무 치중했던 것 같습니다. 기본적으로 '조명'인데 말이죠. 피드백이 아니였다면 생각해보지 못했을 부분이였습니다.

2. 날씨 정보를 표현하는 것 이외의 조명의 기능이 있었으면 좋겠다.

- 이번 프로젝트에서는 조명의 기능에 조금 더 비중을 둬야겠다는 생각이 들었습니다. 

 

이번 프로젝트에서 만들 것은 날씨를 알려주는 무드램프입니다.

가변 저항 3개와 버튼 2개가 부착되어 있는데요.

 

각각의 가변 저항을 통해 램프의 RGB 색상을 조절할 수 있습니다.

 

버튼의 각 기능은 다음과 같습니다.

- on, off 버튼 : 무드 램프를 키고 끄는 기능을 합니다.

- 날씨 버튼 : 날씨 정보를 표현하는 기능을 합니다.

 

정리해보면 on,off 버튼을 통해 무드 램프를 키고 끌 수 있으며, 기본 모드는 무드램프입니다. 기본 모드에서는 가변 저항을 이용해 사용자가 원하는 색상으로 조절이 가능합니다.

사용자가 날씨를 알고 싶을 때, 날씨 버튼을 누르면 날씨 정보를 표현한 후 다시 기본 모드로 돌아오게 됩니다.

즉 사용자 입장에서는 평소에는 조명으로 사용하고, 날씨 정보가 궁금할 때만 날씨 버튼을 눌러주면 되겠죠?

 

 

 

관련 튜토리얼

 

이 프로젝트에서는 Wi-Fi 쉴드, 버튼, 가변 저항을 사용합니다. 

코코아팹 튜토리얼을 참고해 미리 사용법을 익혀두세요!

Wi-Fi 쉴드를 이용하여 API를 통해 인터넷에서 날씨 정보 읽어오기

push 버튼으로 LED 제어하기

가변 저항으로 LED 밝기 조절하기

 

 

 

부품 목록

 

NO 부품명 수량 상세 설명
1 오렌지보드 1 아두이노 UNO
2 Wi-Fi 쉴드  
3 가변 저항 3  
4 버튼 2 PUSH 버튼
5 저항 330Ω
6 LED  1 RGB LED
점퍼케이블  15개 이상  
8 브레드 보드 1  

 

부품명 오렌지보드 Wi-Fi 쉴드 가변 저항 PUSH 버튼 RGB LED
파트

 

 

 

 

하드웨어 메이킹

 

<참고사항> 

이번 프로젝트에서는 와이파이 쉴드를 사용합니다.

아래의 회로에서도 와이파이 쉴드가 결합된 상태로 이해해주시면 됩니다.

즉 와이파이 쉴드를 오렌지보드와 결합 후 와이어링 해주세요 ;)

 

브레드보드 레이아웃

 

 

 

회로도(스케메틱)

 

 

 

 

 

 

소프트웨어 코딩

 

기본 코드 사용 방법은 Wi-Fi 쉴드를 이용하여 API를 통해 인터넷에서 날씨 정보 읽어오기와 동일합니다. 

변경 사항은 openweathermap API의 사용법입니다.

기존의 openweathermap API 사용시 API key를 별도로 요구하지 않았지만, 현재는 openweathermap에 가입 후 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

날씨 정보를 알려주는 무드 램프, 오렌지보드, 코코아팹, 지니어스키트, 오픈소스하드웨어,openweathermapAPI