13주차_공공 데이터 활용 - sookite22/SmartDivice_24 GitHub Wiki

목차

  • 공공 데이터
  • API
  • 실습
    • 공공 데이터 활용 신청하기
    • OLED 디스플레이에 아이콘 표시
    • OLED display에 미세먼지 상태 표시
  • 후기

1. 공공 데이터(Public Data)란?

정부공공기관 이 생성, 수집, 관리하는 데이터로, 일반 대중이 접근하고 사용할 수 있도록 공개된 정보를 말한다. 국가, 지방자치단체, 공공기관 등에서 다양한 목적으로 수집 및 관리되며, 이를 공개함으로써 투명성과 신뢰성을 높이고, 경제적 가치를 창출할 수 있는 중요한 자원으로 간주된다.

투명한 정부 운영을 지원하고, 시민들이 다양한 방식으로 데이터를 활용할 수 있게 하여 사회 전반에 걸쳐 긍정적 변화를 가져올 수 있는 중요한 자원이라고 할 수 있다. 이를 통해 새로운 아이디어와 솔루션이 창출되며, 더 나은 정책 결정사회적 참여 를 촉진할 수 있다.

접근성 이 좋고 다양한 형식 으로 제공되며, 다양한 목적으로 재사용 될 수 있고 범용성 이 좋다는 특징을 갖고 있다.

공공데이터 활용 예시

  • 앱 개발: 공공데이터를 활용한 모바일 앱이나 웹 애플리케이션을 개발하는데 활용할 수 있다.

  • 연구 및 분석: 학계 및 연구 기관에서 공공데이터를 분석하여 정책 결정에 필요한 인사이트를 도출하거나 사회 문제를 해결하는 데 활용할 수 있다.

  • 비즈니스 기회: 스타트업이나 기업에서 공공데이터를 활용하여 새로운 비즈니스 모델을 개발하거나 기존 서비스를 개선하는 데 활용할 수 있다.

  • 시민 참여: 시민들이 공공데이터를 활용하여 지역 사회 문제를 모니터링하고, 개선 방안을 제안하는 등 시민 참여를 촉진할 수 있다.

  • 대표적으로 우리나라에는 공공데이터포털 https://www.data.go.kr/ 이 있다. 스크린샷 2024-05-29 145448

  1. API(Application Programming Interface)란?

소프트웨어 애플리케이션 간의 상호작용을 정의 하는 규칙과 도구의 집합 이다. 서로 다른 소프트웨어 시스템과 애플리케이션이 데이터를 주고받거나 기능을 호출할 수 있도록 하는 표준화된 인터페이스를 제공한다. 이를 통해 개발자들은 복잡한 시스템의 내부 구현에 신경 쓰지 않고 표준화 된 방법으로 기능을 활용할 수 있으며, 애플리케이션의 확장성과 통합성을 높일 수 있다.

API는 소프트웨어 개발에서 필수적인 요소이다. 웹 서비스, 모바일 앱, IoT, 마이크로서비스 등 다양한 분야에서 중요한 역할을 한다.

API의 주요 구성 요소

  • 엔드포인트(Endpoints): API가 제공하는 특정 기능이나 데이터를 호출하기 위한 URL이다.

  • HTTP 메서드(HTTP Methods): API 호출에 사용되는 메서드이다. 일반적인 HTTP 메서드로는 GET(데이터 조회), POST(데이터 생성), PUT(데이터 갱신), DELETE(데이터 삭제)가 존재한다.

  • 요청(Request): 클라이언트가 API 엔드포인트에 데이터를 요청하는 것이다. 요청에는 URL, HTTP 메서드, 헤더, 바디 등이 포함된다.

  • 응답(Response): 서버가 클라이언트의 요청에 대해 반환하는 데이터이다. 응답에는 상태 코드, 헤더, 바디가 포함된다. 상태 코드는 요청의 성공 또는 실패 여부를 나타내며, 200(성공), 404(찾을 수 없음), 500(서버 오류) 등이 존재한다.

  • 헤더(Headers): 요청 및 응답의 메타데이터를 포함한다. 인증 정보, 콘텐츠 유형, 캐시 제어 등을 지정할 수 있다.

  • 바디(Body): 요청 또는 응답의 실제 데이터를 포함한다.

3. 실습

3.1. 공공 데이터 활용 신청하기

미세먼지 공공 데이터 활용 신청을 해보자.

  1. 공공데이터포털에서 공공데이터를 활용하기 위해서는 아이디가 있어야 하므로 가입 및 로그인 을 한다.

“한국환경공단_에어코리아_대기오염정보”라는 Open API 데이터를 활용해보자.

스크린샷 2024-05-29 145725

  1. 활용 신청 버튼을 클릭하여 활용 목적을 입력한 후, 라이센스 표기를 동의하고 활용신청 버튼을 클릭한다. 본 실습에서는 “측정소별 실시간 측정정보 조회”를 활용한다.

스크린샷 2024-05-30 092128 스크린샷 2024-05-30 092145

  1. 승인이 완료되면 '일반 인증키'가 발급된 것을 확인할 수 있다. 이 값은 ESP32에서 활용해야 하므로 복사해둔다.

스크린샷 2024-05-30 092355(1)

데이터 미리보기

본 실습에서는 , , 를 활용할 예정이다.

  1. “3. 측정소별 실시간 측정정보조회” 미리 보기를 클릭한다.

스크린샷 2024-05-30 093353

  1. 'serviceKey'에 복사해 둔 일반 인증키를 붙여 넣고, numOfRows는 100에서 1로 변경한 후, 미리보기를 클릭한다.

스크린샷 2024-05-30 093609 스크린샷 2024-05-30 093813(1)


3.2. OLED 디스플레이에 아이콘 표시

미세먼지 데이터의 Grade는 좋음, 보통, 나쁨, 매우 나쁨 4개의 등급이 존재한다. 이 등급을 이미지로 OLED에 나타내 보자.

이미지 준비

OLED LCD size가 128×32이므로 아이콘은 32×32로 준비한다. 각각의 파일로 저장해 둔다. 이는 https://javl.github.io/image2cpp 사이트에서 이미지를 변환할 수 있다.

스크린샷 2024-05-30 095230

  • 위와 같은 이미지들을 활용할 것이다.

다음과 같이 아두이노 IDE 스케치 editor 창에서 새 탭을 생성하여 파일이름을 “images.h”로 설정하고 저장한다.

스크린샷 2024-06-04 211921

회로 연결

스크린샷 2024-05-29 143143

코드 작성

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#include "images.h"  // 이미지 파일을 포함하는 헤더 파일

#define SCREEN_WIDTH 128  // OLED 디스플레이의 너비 (픽셀 단위)
#define SCREEN_HEIGHT 32  // OLED 디스플레이의 높이 (픽셀 단위)

#define OLED_RESET -1        // 리셋 핀 번호 # (아두이노 리셋 핀을 공유하는 경우 -1)
#define SCREEN_ADDRESS 0x3C  //128x64의 경우 0x3D, 128x32의 경우 0x3C
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

void setup() {
  Serial.begin(115200);

  // SSD1306_SWITCHCAPVCC = 내부적으로 3.3V에서 디스플레이 전압 생성
  if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
    Serial.println(F("SSD1306 allocation failed"));
    for (;;)
      ;  // OLED초기화 실패시, 더 이상 진행하지 않음
  }

  // 초기 디스플레이 버퍼 내용을 화면에 표시합니다.
  // 라이브러리는 이를 Adafruit 스플래시 화면으로 초기화합니다.
  display.display();
  delay(2000);  // 2초간 대기

  display.clearDisplay();       // 디스플레이 지우기

  display.drawBitmap(
    0,
    0,
    VeryGood, 32, 32, 1);  // VeryGood 이미지를 디스플레이에 그리기

  display.drawBitmap(
    32,
    0,
    Good, 32, 32, 1);  // Good 이미지를 디스플레이에 그리기

  display.drawBitmap(
    64,
    0,
    Bad, 32, 32, 1);  // Bad 이미지를 디스플레이에 그리기

  display.drawBitmap(
    96,
    0,
    VeryBad, 32, 32, 1);  // VeryBad 이미지를 디스플레이에 그리기

  display.display();
}

void loop() {
}

주의점!

  • images 파일을 저장할 때, 반드시 같은 폴더에 저장해야 한다.

  • "images.h.ino"처럼 잘못 저장되지 않게 확장명에 주의하자. "sketch13.ino" "images.h"

결과 확인

KakaoTalk_20240604_215844994(1)

  • 지정한 이미지들이 출력되는 것을 확인할 수 있다.

3.3. OLED display에 미세먼지 상태 표시

ESP32는 네트워크 사용이 가능하므로 인터넷의 데이터를 가져와 프로젝트에 활용할 수 있다. 공공데이터 포털에서 미세먼지 데이터를 받아 와서 등급을 아이콘으로 그려 주고 미세먼지 값을 표시해 보자.

코드 작성

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#include <WiFi.h>
#include <time.h>

#include "images.h"  // 이미지 파일을 포함하는 헤더 파일

const char* ssid = "Your_SSID";          // 사용하는 WiFi 네트워크 이름 (SSID)
const char* password = "Your_Password";  // 사용하는 WiFi 네트워크 비밀번호
const int httpPort = 80;

const char* apiKey = "KxUwYnsTwupDGZaymV6gEPF1LS%2BhmuALUZMguQbFDa6ZvuG6O5dy%2BWPgc6%2FJk4deTOnPOosebmo7BxdLthM7pw%3D%3D";
const char* version = "&ver=1.3";
const char* server = "apis.data.go.kr";
const char* stationName = "과천동";
const char* returnType = "xml";  //or json
const char* numOfRows = "1";
const char* pageNo = "1";
const char* dataTerm = "DAILY";

WiFiClient client;

#define SCREEN_WIDTH 128  // OLED 디스플레이의 너비 (픽셀 단위)
#define SCREEN_HEIGHT 32  // OLED 디스플레이의 높이 (픽셀 단위)

#define OLED_RESET -1        // 리셋 핀 번호 # (아두이노 리셋 핀을 공유하는 경우 -1)
#define SCREEN_ADDRESS 0x3C  //128x64의 경우 0x3D, 128x32의 경우 0x3C
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

void setup() {
  Serial.begin(115200);

  // SSD1306_SWITCHCAPVCC = 내부적으로 3.3V에서 디스플레이 전압 생성
  if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
    Serial.println(F("SSD1306 allocation failed"));
    for (;;)
      ;  // OLED초기화 실패시, 더 이상 진행하지 않음
  }

  // 초기 디스플레이 버퍼 내용을 화면에 표시합니다.
  // 라이브러리는 이를 Adafruit 스플래시 화면으로 초기화합니다.
  display.display();
  delay(2000);  // 2초간 대기

  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  Serial.println("\nConnecting to WiFi");
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print(".");
    delay(1000);
  }
  Serial.println("\nWiFi is connected");

  display.clearDisplay();       // 디스플레이 지우기
  display.setTextSize(1);       // 텍스트 크기를 1로 설정
  display.setCursor(0, 0);      // 커서 위치를 디스플레이 왼쪽 위 모서리로 설정
  display.setTextColor(WHITE);  // 텍스트 색상을 흰색으로 설정

  display.drawBitmap(
    0,
    0,
    VeryGood, 32, 32, 1);  // VeryGood 이미지를 디스플레이에 그리기

  display.display();
}

void loop() {
  String a[3];
  int i = 0;
  String dateNtime;
  String pm10Val;
  String pm10Grade;
  String tmp_str;
  static int IntervalReq = 1800;  // 30분을 초단위로 계산

  if (IntervalReq++ > 1800) {  //30분 간격으로 data요청
    IntervalReq = 0;
    Requesthttp();
  };

  delay(50);
  while (client.available()) {

    String line = client.readStringUntil('\n');
    Serial.println(line);

    i = line.indexOf("</dataTime>");
    if (i > 0) {
      tmp_str = "<dataTime>";
      dateNtime = line.substring(line.indexOf(tmp_str) + tmp_str.length(), i);
      Serial.println(dateNtime);
    }

    i = line.indexOf("</pm10Value>");

    if (i > 0) {
      tmp_str = "<pm10Value>";
      pm10Val = line.substring(line.indexOf(tmp_str) + tmp_str.length(), i);
      Serial.println(pm10Val);
    }

    i = line.indexOf("</pm10Grade>");

    if (i > 0) {
      tmp_str = "<pm10Grade>";
      pm10Grade = line.substring(line.indexOf(tmp_str) + tmp_str.length(), i);
      Serial.println(pm10Grade);
      client.stop();
      display.clearDisplay();
      displayIcon(atoi(pm10Grade.c_str()));  // 등급에 따라 해당 아이콘 그리기
      displayString(dateNtime, pm10Val);     // 날짜 및 시간과 미세먼지 농도 표시
      display.display();
      break;
    }
  }

  delay(1000);
}

void Requesthttp() {
  if (client.connect(server, httpPort)) {
    Serial.println("\nSuccessed connection, and request http protocol");

    // HTTP 요청을 보냄
    client.print(String("Get /B552584/ArpltnInforInqireSvc"));
    client.print(String("/getMsrstnAcctoRltmMesureDnsty?serviceKey="));
    client.print(String(apiKey));
    client.print(String("&returnType=") + String(returnType));
    client.print(String("&numOfRows=") + String(numOfRows));
    client.print(String("&pageNo=") + String(pageNo));
    // URL 인코딩하여 문자열 전송
    client.print(String("&stationName=") + urlencode(String(stationName)));
    client.print(String("&dataTerm=") + String(dataTerm));
    client.print(String(version));
    client.print(String(" HTTP/1.1\r\n"));
    client.print(String("Host: ") + String(server) + String("\r\n"));
    client.print(String("Connection: close\r\n"));
    client.print(String("\r\n\r\n"));
  } else {
    Serial.println("\nfailed connection");
  }
}

void displayString(String dnt, String pmval) {
  display.setCursor(50, 3);
  display.println(dnt.substring(0, 10));  // 날짜를 디스플레이에 출력
  display.setCursor(50, 13);
  display.println(dnt.substring(11));  // 시간을 디스플레이에 출력
  display.setCursor(50, 23);
  display.print(pmval);
  display.println(" ug/m^3");  // 미세먼지 농도를 디스플레이에 출력
}

void displayIcon(int grade) {
  switch (grade) {
    case 1:
      display.drawBitmap(
        0,
        0,
        VeryGood, 32, 32, 1);  // VeryGood 아이콘을 디스플레이에 그리기
      break;
    case 2:
      display.drawBitmap(
        0,
        0,
        Good, 32, 32, 1);  // Good 아이콘을 디스플레이에 그리기
      break;
    case 3:
      display.drawBitmap(
        0,
        0,
        Bad, 32, 32, 1);  // Bad 아이콘을 디스플레이에 그리기
      break;
    case 4:
      display.drawBitmap(
        0,
        0,
        VeryBad, 32, 32, 1);  // VeryBad 아이콘을 디스플레이에 그리기
      break;
  }
}

String urlencode(String str) {
  String encodedString = "";
  char c;
  char code0;
  char code1;
  for (int i = 0; i < str.length(); i++) {
    c = str.charAt(i);
    if (c == ' ') {
      encodedString += '+';
    } else if (isalnum(c)) {
      encodedString += c;
    } else {
      code1 = (c & 0xf) + '0';
      if ((c & 0xf) > 9) {
        code1 = (c & 0xf) - 10 + 'A';
      }
      c = (c >> 4) & 0xf;
      code0 = c + '0';
      if (c > 9) {
        code0 = c - 10 + 'A';
      }
      encodedString += '%';
      encodedString += code0;
      encodedString += code1;
    }
  }
  return encodedString;
}

결과 확인

KakaoTalk_20240605_114853181

  • 미세먼지 정보와 그에 따른 이미지가 출력되는 것을 확인할 수 있다.

4. 후기

웹 사이트를 개발할 때 API를 활용해 본 적은 있지만, 공공 데이터를 이용하여 OLED 디스플레이 모듈에 표시한 것은 처음이었다. 웹 상에서의 시각화도 있지만 직접 OLED 디스플레이 모듈을 통해 시각화한 것에 새로운 흥미를 느낄 수 있었다. 미세먼지 표시 시스템은 일상에서 어렵지 않게 찾아볼 수 있는데, 이러한 시스템들도 비슷한 구조를 갖고 있다는 것을 알게 되었다. 본 실습을 통해 유용하고 신뢰성 있는 공공 데이터를 더 활용해보고 싶다는 생각이 들었다. API만 잘 다룰 줄 알아도 개발에 많은 도움이 된다는 것을 체감할 수 있었고, 더 다양한 종류의 API를 다뤄보면서 더 공부해 볼 예정이다.

⚠️ **GitHub.com Fallback** ⚠️