WiFi 를 통한 아두이노 활용(5) : 날씨 정보 가져오기 #2

2017. 7. 18. 11:50

Arduino/Wireless

아두이노와 무선인터넷을 통해 날씨 정보 가져오기 두번째

이번 글에선, 이전 글에 이어서, 날씨 정보 사이트인 OpenWeatherMap에서 제공되는 API를 통해 날씨 정보를 받아오는 프로그램을 완성하겠습니다. 이미 접속하여 응답받는 기본 프로그램은 완성했기 때문에, 이번에는 API 에 대해서만 알아보고 적용해도 원하는 정보를 쉽게 받아 올 수 있습니다.

OpenWeatherMap 의 API 알아보기

API는 애플리케이션(아두이노 같은...)에서 프로그래밍으로 접근할 수 있도록 만든 인터페이스입니다. 웹 방식의 API는 주로, 웹브라우저에서 웹페이지를 호출하는 웹주소 구문에 주어진 명세대로 API를 담아서 보내는 형식입니다.

api.openweathermap.org/data/2.5/forecast?id=524901&APPID=1111111111

이전 글에서 봤던, API 명세 샘플입니다. 해당사이트의 Weather APIs - How to start에서 볼 수 있습니다. 이 API 요청문도 결국 한줄의 웹주소입니다. 웹브라우저에서 특정 홈페이지로 이동하기 위해 입력하는 주소와 동일합니다. 약간 길거나 매우 길거나 차이입니다. 

이전 글에서 Server로 향하는 Client의 Request문에 대해서 알아봤습니다. GET, POST 메소드라고 언급했었는데, GET 메소드는 Client Request 문(웹주소)에 데이터를 담아서 전송하는 방식이고, OpenWeatherMap은 GET 방식을 사용합니다. 따라서, API 규칙 대로 Request문을 완성하면 결국 웹주소 한줄을 완성하는 것을 의미합니다. 앞으로 연습하는 요청문마다 웹브라우저 주소표시줄에 입력하면 동일한 결과값을 웹브라우저 화면에서 볼 수 있습니다.

위 API 샘플 구문을 자세히 살펴 보겠습니다.

위 그림은 지난 번에 봤던 부분입니다. 1번이 호스트명이고, 2번이 요청 데이터 즉 API 구문입니다. 아두이노(Arduino) 소스에서는 아래와 같이 Request 문을 작성하게 됩니다.

API 구문만 자세히 보겠습니다.

data/2.5/

이 부분은 고정값입니다. 그냥 삽입하시면 됩니다.

forecast?

요청하는 메소드 또는 오퍼레이션입니다. forecast는 지난 5일 동안의 날씨를 3시간 단위로 보여줍니다. 여기에 weather 라고 쓰면 현재의 날씨만 보여주며 데이터 길이가 짧아서 주로 이 메소드만 사용하겠습니다. 오퍼레이션은 항상 물음표(?)로 끝납니다.

id=524901

메소드 뒤엔 파라미터(parameter, 인자)들이 나열됩니다. 여러개의 파라미터가 올 수 있고, 각 파라미터는 &(ampersand)로 구분합니다. 위 샘플 보시면 앰퍼샌드가 하나 있으며 양쪽의 파라미터를 구분하는 역할을 합니다. 이 파라미터는 날씨 정보를 알고 싶은 해당 도시의 ID값입니다. 524901은 찾아보니 모스코바네요. 원하는 도시를 찾는 지정하는 방법은 이외에 도시명을 직접 입력하거나, 위도 경도값, ZIP Code로 검색할 수 있습니다. 정확한 검색 결과를 위해서 도시명보다는 도시 아이디값을 사용하길 권장하고 있습니다. City's IDs List를 다운받아 전체 도시 ID 및 정보를 검색하실 수 있습니다.

파라미터에 값을 지정할 때는 equal(=) 기호를 사용합니다.

APPID=111111111

이 부분이 API Key를 입력하는 곳입니다. 트래픽 등을 관리하기 위해 회원 가입 후 API Key 를 받아야 하고, 데이터 요청시에 이를 입력해줘야 합니다.

다른 파라미터가 더 필요하다면 앰퍼샌드(&)로 이어서 파라미터를 적어주고, 이퀄기호(=)로 해당 값을 넣어서 사용하면 됩니다.

예를 들어, 응답 결과를 JSON 형식과 XML 형식 등으로 골라서 받을 수 있는데, 아무런 언급이 없을 시엔 JSON Format이 기본값이고, &mode=xml 을 붙이면 XML format으로 받게 됩니다.

이 외의 파라미터 등 API 사용법은 아래 링크를 따라 가서 확인 가능 합니다.

OpenWeatehrMap Weather API

OpenWeatherMap API Key 발급하기

http://www.openweathermap.org/에 가서 홈페이지 제일 상단 가운데 쯤에 Sign Up 메뉴가 있습니다. 클릭!

Sign Up 을 클릭하면 위와 같이 간단한 회원 가입 입력 화면이 나타납니다. 알맞게 입력하시고 계정 생성 하시기 바랍니다.

계정이 생성되면서 위 화면이 팝업됩니다. 적당히 넣어 주시고 Save 하시면 됩니다.

손쉽게 바로 가입이 완료되었고, 입력한 이메일을 통해 가입 확인 메일이 도착합니다. 개인 키(API Key)와 간단한 사용법이 들어 있습니다. 위 화면은 평상시에 로그인 후에 보여지는 My Home 화면이며 하위 메뉴중 API keys 를 클릭하시면 개인키를 확인하실 수 있습니다.

위 그림에서 Key 항목이 바로 API Key입니다. 긁어서 복사하세요! 이 키는 디폴트로 생성된 것이고, 그림에서 잘린 오른쪽 화면에서 새로운 키를 생성할 수 있습니다. 물론 트래픽은 합산됩니다.

API Request Call 작성하기

개인 키가 생겼으므로 실제 API 요청 콜을 작성하겠습니다. 우선, 여러 차례 봤던 것처럼 API 요청의 End Point 인 api.openweathermap.org 부터 시작합니다.

api.openweathermap.org

그 다음 디폴트로 들어가는 data/2.5

api.openweathermap.org/data/2.5

그 다음은, 메소드 즉 오퍼레이션 코드가 나와야 합니다. 위에서 본 샘플은 forecast 였는데, 날씨 정보를 3시간 간격으로 제공하는 데 지난 5일간의 정보입니다. 제가 사용할 오퍼레이션은 weather 입니다. 이건 그냥 현재 날씨 정보만을 제공합니다. 테스트를 위한 프로그램이므로 되도록 간단한 API를 이용하려 합니다. 그리고 오퍼레이션은 물음표(?)로 끝나야 합니다.

api.openweathermap.org/data/2.5/weather?

이제 원하는 정보를 얻기 위해 파라미터 값을 붙여 줘야 합니다. 첫번째 파라미터를 앰퍼샌드(&) 없이 붙여 주고, 두번째 파라미터 부터 & 로 연결합니다. 우선 알고자 하는 도시를 입력합니다. 정확한 결과를 위해, 권장하는대로 CITY ID 값으로 요청하겠습니다.

{
    "id": 1835847,
    "name": "Seoul-teukbyeolsi",
    "country": "KR",
    "coord": {
      "lon": 127,
      "lat": 37.583328
    }
  },
  {
    "id": 1838524,
    "name": "Busan",
    "country": "KR",
    "coord": {
      "lon": 129.040283,
      "lat": 35.102779
    }
  },
  {
    "id": 1843564,
    "name": "Incheon",
    "country": "KR",
    "coord": {
      "lon": 126.731667,
      "lat": 37.453609
    }
  },

테스트를 위해서 서울, 부산, 인천 세 곳을 위 쪽 제가 드린 링크에서 받은 파일 안에서 찾았습니다. 각 도시별로 먼저 숫자로된 ID값이 나오고, 그 다음 이름, 도시, 좌표값 순으로 나옵니다. 좌표는 위도(lat, latitude) 경도(lon, longitude) 값입니다. 서울을 선택하겠습니다.

api.openweathermap.org/data/2.5/weather?id=1835847

그 다음 파라미터로 API Key 를 APPID=값으로 &를 붙여서 넣어줍니다. (주의 : 아래 키는 가짜임)

api.openweathermap.org/data/2.5/weather?id=1835847&APPID=7b18dfe35b4273bbe63654d75573cacf

완성된 API Request Call 입니다. 이 코드를 복사해서 웹브라우저의 주소표시줄에 넣고 이동을 하시면 결과값을 확인하실 수 있고, 제대로된 값이 리턴되었다면 아두이노에서도 동일하게 작동합니다.

위와 같이 비슷하게 나오면 됩니다. 이제 프로그램에 적용해 보겠습니다.

 if (client.connect(server, 80)) {
    Serial.println("connected to server"); 
    client.println("GET /data/2.5/weather?id=1835847&APPID=7b18dfe35b4273bbe63654d75573cacf HTTP/1.1");
    client.println("Host: api.openweathermap.org");
    client.println("Connection: close");
    client.println();
  }

이전 글에서 완성한 소스의 윗부분만 보기와 같이 수정하는데 결국 GET 다음 구문만 변경하시면 됩니다. 역시 전체 소스는 아래쪽에 게시하겠습니다.

위와 같이 결과값을 확인했습니다. 화면 밑에서 두번째 줄이 날씨 정보입니다. 포맷을 지정하지 않았기 때문에 디폴트로 JSON Format을 이용한 결과입니다.

이상으로 날씨 정보까지 받아 보는데 성공했습니다. 이제 다음 글에서는 응답 받은 데이터에서 좀더 알아보고 원하는 부분만 추출해서 출력해 보겠습니다. 아래쪽에 전체 소스를 추가하였습니다.

#include <SPI.h>
#include <WiFi101.h>
char ssid[] = "TURTLE";
char pass[] = "yesican1";
int status = WL_IDLE_STATUS;
char server[] = "api.openweathermap.org";
WiFiClient client;
void setup() {
  //Configure pins for Adafruit ATWINC1500 Feather
  WiFi.setPins(8,7,4,2); 
  Serial.begin(9600);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
  }
  // check for the presence of the shield:
  if (WiFi.status() == WL_NO_SHIELD) {
    Serial.println("WiFi shield not present");
    // don't continue:
    while (true);
  }
  while (status != WL_CONNECTED) {
    Serial.print("Attempting to connect to SSID: ");
    Serial.println(ssid);
    status = WiFi.begin(ssid, pass);
    // wait 2 seconds for connection:
    delay(2000);
  }
  Serial.println("Connected to wifi");
  Serial.println("\nStarting connection to server..."); 
  if (client.connect(server, 80)) {
    Serial.println("connected to server"); 
    client.println("GET /data/2.5/weather?id=1835847&APPID=7b18dfe35b4273bbe63654d75573cacf HTTP/1.1");
    client.println("Host: api.openweathermap.org");
    client.println("Connection: close");
    client.println();
  }
}
void loop() { 
  while (client.available()) {
    char c = client.read();
    Serial.write(c);
  }
  if (!client.connected()) {
    Serial.println();
    Serial.println("disconnecting from server.");
    client.stop();
    // do nothing forevermore:
    while (true);
  }
}

Comments