Arduino :: Lecture & TIPs

[Arduino] 공공데이터포털 OPEN API 사용법 (2)

이전 게시글을 통해 OPEN API 로 공공데이터를 활용하기 위해 활용 신청하는 방법과 신청정보를 확인하는 방법에 대해 다루어보았습니다. 이번 게시물에서는 활용 신청한 OPEN API 를 호출하여 데이터를 처리하는 방법에 대해 다루어 볼 까 합니다.

OPEN API 를 호출하기 위한 URL 생성하기

OPEN API 호출 URL 은 네 가지 영역으로 구별할 수 있습니다. 가장 먼저 서비스 URL 은 OPEN API 서비스 명, 두 번째 인증키는 Open API 활용 신청자의 고유 ID, 세 번째 작업 이름은 Data ID (데이터 ID), 마지막으로 요청 변수는 OPEN API 활용 신청인의 고유 ID 입니다.

서비스 URL, 작업 이름, 요청 변수 정보는 활용신청하신 OPEN API 의 상세 정보 화면에 첨부된 참고 문서를 내려받아 URL 생성에 필요한 정보들을 확인 하실 수 있습니다.

미리보기 기능을 활용하여 OPEN API 호출 URL 쉽게 만들기

URL 을 직접 만들어보시기 어렵다면 공공데이터포털에서 제공하는 미리보기 기능을 활용하여 쉽게 생성해보실 수 있습니다.

개발 계정 상세보기 화면 하단에 위치한 상세기능정보 란에서 미리보기 헤더 하위의 [확인] 버튼을 클릭합니다.

요청 변수 입력란이 화면에 표시되면 ServiceKey 입력란에 인증키를 입력합니다. 이때 인증키 길이가 매우 길기 때문에 직접 입력하시는 편보다는 요청 변수 화면 위쪽에 위치한 인증키를 드래그하여 복사해두시면 좋습니다.

인증키를 붙여넣기 한 뒤 요청 변수 화면 하단의 미리 보기 아이콘을 선택합니다.

새 창이 열리면서 오픈API 호출 결과가 화면에 표시되는데요, 브라우저의 주소 입력 창에 오픈API 호출 URL이 입력되어 있습니다. 해당 URL 을 복사합니다.

OPEN API 호출 결과를 포함해 XML 형태의 데이터 구조 이해하기.

OPEN API 를 호출하기 위한 URL 을 생성하는 방법을 익히셨다면, OPEN API 호출 결과에 대해 알아봅시다. 공공데이터포털에서 제공하는 모든 OPEN API 는 공공데이터 사용자들이 동일한 방법으로 데이터를 처리할 수 있도록 표준화된 데이터 구조로 제공됩니다.

따라서, 데이터를 처리하고 가공하는 방법만 익히더라도 모든 OPEN API 에 대해 유사한 방법으로 활용해보실 수 있습니다.

OPEN API 를 요청하게 되면 데이터를 XML 또는 JSON 형식으로 받아올 수 있습니다. OPEN API 호출 URL 의 요청 변수에서 원하는 데이터 형태를 지정할 수 있는데요, 특별하게 지정해주지 않았다면 대부분이 XML 형식으로 데이터를 받아오게 됩니다. 여기서는 XML 형태의 데이터를 알아보도록 합시다.

OPEN API 를 XML 형식으로 요청하면 다음과 같은 구조로 이루어진 XML 데이터를 받아올 수 있습니다.

데이터 항목 : 실제 OPEN API 요청 시 받아올 수 있는 데이터에 대한 내용

OPEN API 호출 결과 데이터의 개수 : OPEN API 를 호출했을 때 얻게 되는 데이터의 개수
(한 번의 호출로 모든 데이터를 받아오면 좋겠지만, 방대한 양의 데이터를 받아오는 경우 제한된 컴퓨터 성능으로 인해 한 번에 전체 데이터를 받아 올 수 없으므로 여러 번으로 나누어서 데이터를 얻을 수 있도록 설계해둔 것 입니다. 호출 결과 데이터의 개수는 URL의 “요청 변수” 항목에서 변경이 가능하지만 숫자 제한이 있어 너무 큰 숫자를 지정하면 OPEN API 호출이 실패할 수 있습니다.)

전체 데이터의 개수 : OPEN API 로 제공하는 데이터의 전체 개수

이러한 경우 모든 데이터를 얻으려면 OPEN API 를 총 3 번 호출하여야 합니다. 컴퓨터 프로그래밍 언어를 활용한 자동 처리 방법이 필요할 수 있는데요, OPEN API 호출 결과 데이터 개수를 늘리면 OPEN API 호출 횟수를 절감 할 수 있습니다. 예를 들어 호출 결과 데이터 개수를 1,000개로 지정하면 오픈API를 총 5 회 호출하면 됩니다.


최종적으로 XML 데이터를 처리하는 프로그램을 이용하여 필요한 데이터만 추출합니다.

아두이노 IoT (C++) 를 활용한 OPEN API 활용 방법

C++ 는 아두이노 개발에 누구나 사용할 수 있는 장점이 있으며 IoT 기반의 프로젝트에 많이 사용되고 있습니다. 아두이노 기반의 ESP8266 MCU 칩셋에는 인터넷에 접속하여 데이터를 주고 받을 수 있는 기능이 포함되어 있습니다. 이 기능들을 활용하여 OPEN API 를 호출하고 공공데이터를 받아와봅시다.

OPEN API 호출 URL 만들어보기

Service URL
http://apis.data.go.kr/6280000/

작업 이름
busArrivalService

요청 변수
OPEN API 호출 시 얻게되는 데이터 개수.
10

인증키
공공 데이터 포털에서 지급받은 서비스 키.
serviceKey (D1uoUKYssO%2Bkvh5I50q7lmMRL7J8SVVMEBTn4Qdl5%2FzVlpBmx0RZHgwY%2FKHZDywYiurN9NuJvVOJ00pWBGoF7g%3D%3D)

OPEN API 호출 URL 생성

serviceKey=" + iServiceKey + "&pageNo=1&numOfRows=10&routeId=";
  str.concat(routeId);
  str.concat("&bstopId=");
  str.concat(bstopId);
  str.concat(" HTTP/1.1\r\nHost:apis.data.go.kr\r\nConnection: close\r\n\r\n");

OPEN API 요청하기.
void requestArrivalTime(String routeId, String stationId) {
  String str = "GET /ws/rest/busarrivalservice?serviceKey=" + serviceKey + "&routeId=";
  str.concat(routeId);
  str.concat("&stationId=");
  str.concat(stationId);
  str.concat(" HTTP/1.1\r\nHost:openapi.gbis.go.kr\r\nConnection: close\r\n\r\n");

  if(client.connect(host, httpPort)){
  Serial.println("connected");
  Serial.print(str);
  client.print(str);
  client.println();

  cmdSize = str.length();

  client.println("AT+CIPSTART=\"TCP\",\"openapi.gbis.go.kr\",80");
  delay(500);
  client.print("AT+CIPSEND=");
  delay(500);
  client.println(cmdSize);
  delay(500);
  client.println(str);
  }
  else {
    Serial.println("Connection Failed: ");
    return;
  }
}
오픈API를 반복 호출하여 데이터 추출.
 void loop(){
  while(client.available())
  {
    char c = client.read();
    if(c != NULL){
      if(rcvbuf.length() > 1300)
        rcvbuf = "";
      rcvbuf += c;
      //Serial.write(c);
      Serial.print(c);
    }
  }

  if(millis() - previousMillis > GBISUPD_INTERVAL){
    result_26_1 = parseArrivalTime("26-1");
    do_oled(0, 11, result_26_1);
    do_oled(0, 22, result_63);
    do_oled(0, 33, result_11_3);
    requestLocker2 = true;
  }

  else if(millis() - previousMillis > GBISUPD_INTERVAL - 4000 && requestLocker)
  {
    result_63 = parseArrivalTime("63");
    
    // 26-1
    requestArrivalTime("224000027", "224000481");  
    requestLocker = false;
  }
  
  else if(millis() - previousMillis > GBISUPD_INTERVAL - 8000 && requestLocker1)
  {
    result_11_3 = parseArrivalTime("11-3");

    // 63
    requestArrivalTime("224000010", "224000481");  
    requestLocker1 = false;
    requestLocker = true;
  }

  else if(millis() - previousMillis > GBISUPD_INTERVAL - 12000 && requestLocker2)
  {

    // 11-3
    requestArrivalTime("213000014", "224000481");
    requestLocker2 = false;
    requestLocker1 = true;
  }
 }
받아온 데이터 필터링 후 OLED 에 표시.
String parseArrivalTime(String busNum)
{
  previousMillis = millis();
  int startIndex = rcvbuf.indexOf("<predictTime1>");
  if(startIndex == -1){ 
    rcvbuf = "";
    return busNum + " :     no bus";
  }

  int strLength = strlen("<predictTime1>");
  int endIndex = rcvbuf.indexOf("<", startIndex + strLength);
  String predictTime1 = rcvbuf.substring(startIndex+strLength,endIndex);
  startIndex = rcvbuf.indexOf("<predictTime2>");
  strLength = strlen("<predictTime2>");
  endIndex = rcvbuf.indexOf("<", startIndex + strLength);
  String predictTime2 = rcvbuf.substring(startIndex+strLength,endIndex);

  if(predictTime2.equals("") && predictTime1 == "1"){
    return busNum + " : " + predictTime1 + "min";
  }
  else if(predictTime2.equals("") && predictTime1 != "1"){
  return busNum + " : " + predictTime1 + "mins";
  }
 
  Serial.println("===========");
  Serial.println(predictTime1);
  Serial.println(predictTime2);
  Serial.println("===========");
  rcvbuf = "";
  
  if (predictTime1 != "1" && predictTime2 != "1") { 
  return busNum + " :     " + predictTime1 + "mins, " + predictTime2 + "mins";
  }
  else if (predictTime1 == "1" && predictTime2 != "1"){
  return busNum + " :     " + predictTime1 + "min, " + predictTime2 + "mins";
  }
  else if (predictTime1 != "1" && predictTime2 == "1"){
  return busNum + " :     " + predictTime1 + "mins, " + predictTime2 + "min";
  }  
}
#include "SSD1306.h"
SSD1306 display(0x3c, D3, D4);

void setup_oled(){
  display.init();
  display.clear();  
  display.flipScreenVertically();
  display.setFont(ArialMT_Plain_10);
  display.drawString(0,13, "initialize OLED...");  
  display.display();
}

void wifi_oled(int cnt) {
  display.clear();  
  display.setFont(ArialMT_Plain_10);
  display.drawString(0,13, "waiting wifi..."); 
  display.drawString(0,24, String("ssid : ")+String(ssid));
  display.drawString(0,35, String("password : ")+ String(password)); 
  display.drawString(0,53, String(cnt));
  display.display();  
}

void nowifi_oled() {
  display.clear();  
  display.setFont(ArialMT_Plain_10);
  display.drawString(0,0, "NO WIFI: skip wifi..."); 
  display.display();  
}

void do_oled(int16_t x, int16_t y, String result) {
  display.clear();
  display.setFont(ArialMT_Plain_10);
  display.drawString(0,0, "Bus No.");
  display.drawString(64,0, "Time Left");
  display.setFont(ArialMT_Plain_10);
  display.drawString(0,13, result_26_1);
  display.drawString(0,29, result_63);
  display.drawString(0,45, result_11_3);
  display.display();
  Serial.println(result);
}

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: