week__13 (공공 데이터 활용) - JINEEH/SmartDevice_JinHee GitHub Wiki

공공 데이터를 활용한 OLED 실습

1. 학습 목표

1-1. 공공 데이터 포털에서 미세먼지 데이터를 받아와 OLED에 표시하기

2. OLED 실습 1

2-1. 아이콘 이미지를 OLED 디스플레이에 그리기
2-2. 실습 준비물: ESP32, ESP32 확장 실드, OLED 디스플레이, 점퍼 케이블
2-3. images.h 파일에 이미지 아이콘 저장하기
  • 10-1 코드와 동일한 파일에 '새 탭' 추가 -> 파일 이름 'images.h' 로 저장

13주차 10-1(4)

  • 실행 코드 1(10-1)
#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() {
}
  • 실행 코드 2(images.h 파일)
const unsigned char VeryGood [] PROGMEM = {
	0x00, 0x0f, 0xf0, 0x00, 0x00, 0x7f, 0xfe, 0x00, 0x00, 0xff, 0xff, 0x00, 0x03, 0xff, 0xff, 0xc0, 
	0x07, 0xff, 0xff, 0xe0, 0x0f, 0xff, 0xff, 0xf0, 0x1f, 0xff, 0xff, 0xf8, 0x1f, 0xff, 0xff, 0xf8, 
	0x38, 0xc7, 0xe3, 0x1c, 0x70, 0x03, 0xc0, 0x0e, 0x70, 0x03, 0xc0, 0x0e, 0x70, 0x03, 0xc0, 0x0e, 
	0xf0, 0x03, 0xc0, 0x0f, 0xf8, 0x07, 0xe0, 0x1f, 0xfc, 0x0f, 0xf0, 0x3f, 0xfe, 0x1f, 0xf8, 0x7f, 
	0xff, 0x3e, 0x7c, 0xff, 0xff, 0xfc, 0x3f, 0xff, 0xff, 0xfc, 0x3f, 0xff, 0xfd, 0xfe, 0x7f, 0xff, 
	0x79, 0xff, 0xff, 0x9e, 0x78, 0xff, 0xff, 0x9e, 0x7c, 0x7f, 0xff, 0x1e, 0x3e, 0x3f, 0xfe, 0x3c, 
	0x1f, 0x1f, 0xfc, 0x78, 0x1f, 0x87, 0xe0, 0xf8, 0x0f, 0xc0, 0x03, 0xf0, 0x07, 0xf8, 0x0f, 0xe0, 
	0x03, 0xff, 0xff, 0xc0, 0x00, 0xff, 0xff, 0x00, 0x00, 0x7f, 0xfe, 0x00, 0x00, 0x0f, 0xf0, 0x00
};

const unsigned char Good [] PROGMEM = {
	0x00, 0x0f, 0xf0, 0x00, 0x00, 0x7f, 0xfe, 0x00, 0x01, 0xff, 0xff, 0x80, 0x03, 0xff, 0xff, 0xc0, 
	0x07, 0xff, 0xff, 0xe0, 0x0f, 0xff, 0xff, 0xf0, 0x1e, 0x3f, 0xf8, 0xf8, 0x3e, 0x1f, 0xf8, 0x78, 
	0x3c, 0x0f, 0xf0, 0x3c, 0x78, 0x87, 0xe2, 0x1e, 0x71, 0xc3, 0xc7, 0x0e, 0x71, 0xe7, 0xc7, 0x9e, 
	0xfb, 0xff, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
	0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x7f, 0xff, 0xff, 0xfc, 0x3f, 0xff, 0xff, 0xfe, 0x7f, 0xff, 
	0x79, 0xff, 0xff, 0x9e, 0x78, 0xff, 0xff, 0x9e, 0x7c, 0xff, 0xff, 0x1e, 0x3e, 0x7f, 0xfe, 0x3c, 
	0x3f, 0x1f, 0xfc, 0x7c, 0x1f, 0x87, 0xf0, 0xf8, 0x0f, 0xc0, 0x01, 0xf0, 0x07, 0xf0, 0x0f, 0xe0, 
	0x03, 0xff, 0xff, 0xc0, 0x00, 0xff, 0xff, 0x00, 0x00, 0x7f, 0xfe, 0x00, 0x00, 0x0f, 0xf0, 0x00
};

const unsigned char Bad [] PROGMEM = {
	0x00, 0x0f, 0xf0, 0x00, 0x00, 0x7f, 0xfe, 0x00, 0x01, 0xff, 0xff, 0x80, 0x03, 0xff, 0xff, 0xc0, 
	0x07, 0xff, 0xff, 0xe0, 0x0f, 0xff, 0xff, 0xf0, 0x1f, 0xff, 0xff, 0xf8, 0x3f, 0xff, 0xff, 0xf8, 
	0x3f, 0xff, 0xff, 0xfc, 0x70, 0x03, 0xc0, 0x0e, 0x70, 0x03, 0xc0, 0x0e, 0x70, 0x03, 0xc0, 0x0e, 
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x7f, 0xff, 0xff, 0xfc, 0x3f, 0xff, 
	0xff, 0xfc, 0x3f, 0xff, 0xff, 0xfe, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
	0x7f, 0xff, 0xff, 0xfe, 0x7f, 0xff, 0xff, 0xfe, 0x7f, 0xe0, 0x07, 0xfe, 0x3f, 0x80, 0x03, 0xfc, 
	0x3f, 0x0f, 0xf0, 0xfc, 0x1e, 0x3f, 0xfc, 0x78, 0x0f, 0xff, 0xfe, 0xf0, 0x07, 0xff, 0xff, 0xe0, 
	0x03, 0xff, 0xff, 0xc0, 0x00, 0xff, 0xff, 0x00, 0x00, 0x7f, 0xfe, 0x00, 0x00, 0x0f, 0xf0, 0x00
};

const unsigned char VeryBad [] PROGMEM = {
	0x00, 0x0f, 0xf0, 0x00, 0x00, 0x7f, 0xfe, 0x00, 0x01, 0xff, 0xff, 0x80, 0x03, 0xff, 0xff, 0xc0, 
	0x07, 0xff, 0xff, 0xe0, 0x0f, 0xff, 0xff, 0xf0, 0x1d, 0xef, 0xf7, 0xb8, 0x38, 0xc7, 0xe3, 0x1c, 
	0x3c, 0x0f, 0xf0, 0x3c, 0x7e, 0x1f, 0xf8, 0x7e, 0x7e, 0x1f, 0xf8, 0x7e, 0x7c, 0x0f, 0xf0, 0x3e, 
	0xf8, 0xc7, 0xe3, 0x1f, 0xfd, 0xef, 0xf7, 0xbf, 0xff, 0xfe, 0x7f, 0xff, 0xff, 0xfc, 0x3f, 0xff, 
	0xff, 0xfc, 0x3f, 0xff, 0xff, 0xfe, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
	0x7f, 0xff, 0xff, 0xfe, 0x7f, 0xff, 0xff, 0xfe, 0x7f, 0xe0, 0x07, 0xfe, 0x3f, 0x80, 0x03, 0xfc, 
	0x3f, 0x0f, 0xf0, 0xfc, 0x1e, 0x3f, 0xfc, 0x78, 0x0f, 0xff, 0xfe, 0xf0, 0x07, 0xff, 0xff, 0xe0, 
	0x03, 0xff, 0xff, 0xc0, 0x01, 0xff, 0xff, 0x80, 0x00, 0x7f, 0xfe, 0x00, 0x00, 0x0f, 0xf0, 0x00
};
  • 실습 결과

3. OLED 실습 2

3-1. 미세먼지 데이터와 이미지 아이콘을 OLED 디스플레이에 그리기
3-2. 실습 준비물: ESP32, ESP32 확장 실드, OLED 디스플레이, 점퍼 케이블
3-3. 공공 데이터 포털 가입 및 미세먼지 데이터 저장
3-4. images.h 파일에 이미지 아이콘 저장하기
  • 공공 데이터 포털 가입 및 Open API 검색
  • '한국환경공단_에어코리아_대기오염정보' 활용

13주차 10-1

  • 활용 목적 입력 후 활용 신청
  • 10-2 코드를 활용한 이번 실습에서는 '측정소별 실시간 측정정보 조회' 데이터 활용
  • 활용 신청 후 화면에 나오는 일반 인증키 복사하여 ESP32에 활용

13주차 10-1(2)

  • 이전 실습과 동일하게 images.h 파일 생성

스크린샷 2024-06-04 112242

  • 실행 코드(10-2), images.h 파일의 코드는 10-1 실습과 동일함
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

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

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

const char* ssid = "zineeh";          // 사용하는 WiFi 네트워크 이름 (SSID)
const char* password = "SMARTDEVICE0527";  // 사용하는 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;
}
  • 실습 결과

4. 오픈 API(Open Application Programming Interface)

4-1. 의미: 플랫폼 기능 또는 콘텐츠를 외부에서 웹 프로토콜(HTTP)로 호출하여 사용할 수 있게 개방한 API
     - 누구나 사용할 수 있도록 공개된  API
     - 이미 갖춰진 정보와 인터페이스를 활용하여 다양한 방법으로 개발 가능
     - HTTP를 통해 요청받고 XML이나 JSON 형식으로 데이터를 변환 -> 기존 애플리케이션에 새로운 기능 추가 가능
4-2. API 의미: 응용 프로그램 인터페이스로 소프트웨어 또는 서비스가 제공하는 기능을 다른 프로그램이 사용할 수 있게 연결할 수 있게 전달
4-3. 오픈 소스와 차이점 및 공통점
     - 차이점: 오픈 API는 응용 프로그램 인터페이스 역할을 하지만 오픈 소스는 공개된 소스 코드 자체를 뜻함
           - 오픈 API는 서비스 제공자가 설정한 약관에 따라 규제되지만 오픈 소스는 라이선스에 따라 규제됨
     - 공통점: 공개된 인터페이스와 소스코드를 활용하여 혁신적인 프로그램 개발을 촉진시킴
           - 공개된 하나의 자원을 통해 여러 결과값을 도출하여 전체적인 기술 발전을 도모함
  • 오픈 API 관련 기사 자료

배달 플랫폼 표준 API 연동

당근마켓 API 관련 기사

  • 관련 부분만 발췌

13주차 open api 기사 발췌

5. 부가 설명

5-1. XML(eXtensible Markup Language): 인터넷 웹페이지를 만드는 html을 개선하여 만든 언어
                              - XML 문서들은 SGML 문서 형식을 따름
                              - 다목적 마크업 언어
                              - 주로 다른 종류의 시스템끼리 데이터를 쉽게 주고 받을 수 있으며 html의 한계를 극복함
                              - 단순성, 일반성, 인터넷을 통한 사용 가능성 강조
                              - 유니코드를 사용하여 전세계 언어를 지원함
5-2. SGML(Standard Generalized Markup Language): 다양한 형태의 전자문서가 시스템 환경에서 정보의 손실없이 전송/저장/자동처리가 가능하도록 국제표준화기구(ISO)에서 정한 문서처리 표준

5-3. 파싱(Parsing): 컴퓨터에서 컴파일러 또는 번역기가 원시 부호를 기계어로 번역하는 과정
            - 문장의 문법적인 구성 또는 구문을 해석(번역)
            - 하향식 파싱: 문법의 시작부터 끝으로 가면서 분석하며 시작 심벌에서부터 재귀적으로 하위 식별자를 찾아가는 파싱 방식
                         - 입력 문자열에 대한 좌측 유도 과정
                         - 상향식 파싱보다는 일반적이지 않음
                         - 종류: Recursive Descent Parser, Predictive Parser, LL Parser
            - 상향식 파싱: 터미널 노드로부터 루트쪽으로 파싱 트리를 구성(단말 노드 -> 로트 노드로 파싱 트리 구성)
                         - 주어진 문자열이 시작 심벌(루트 노드)로 축약될 수 있으면 올바른 문장으로 판단함
                         - 종류: Shift Reduce Parser, LR Parser

5-4. REST API(REpresentational State Transfer API): 두 컴퓨터 시스템이 인터넷을 통해 정보를 안전하게 교환할 때 사용하는 인터페이스
                                                   - 안전하고 신뢰성높은 정보 교환을 지원함
                                                   - REST 아키텍처 스타일의 원칙: 균일한 인터페이스, 무상태, 계층화 시스템, 캐시 가능성, 온디맨드 코드
                                                   - 장점: 확장성, 유연성, 독립성 보장 가능
⚠️ **GitHub.com Fallback** ⚠️