#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <WiFi.h>
#include <time.h>
#include "images.h"
-
Wire: I2C 통신을 지원.
-
Adafruit_GFX, Adafruit_SSD1306: OLED 디스플레이 제어 라이브러리.
-
WiFi: ESP32의 Wi-Fi 연결을 위해 포함.
-
time.h: 시간 관련 기능 제공.
-
images.h: 등급별 이미지 데이터를 포함한 헤더 파일.
const char* ssid = "Your_SSID";
const char* password = "Your_Password";
const int httpPort = 80;
const char* apiKey = "..."; // 공공데이터 API 인증키
const char* version = "&ver=1.3";
const char* server = "apis.data.go.kr";
const char* stationName = "과천동";
const char* returnType = "xml";
const char* numOfRows = "1";
const char* pageNo = "1";
const char* dataTerm = "DAILY";
WiFiClient client;
- Wi-Fi 연결 정보와 API 요청 파라미터를 정의.
-
WiFiClient client
: HTTP 요청 및 응답 처리를 위해 생성.
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 32
#define OLED_RESET -1
#define SCREEN_ADDRESS 0x3C
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
- OLED 디스플레이의 크기와 주소를 설정하고
display
객체 생성.
void setup() {
Serial.begin(115200);
if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
Serial.println(F("SSD1306 allocation failed"));
for (;;);
}
display.display();
delay(2000);
- 시리얼 모니터 시작, OLED 초기화 및 Adafruit 로고 화면 표시.
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");
- Wi-Fi 연결을 시도하고 연결될 때까지 대기.
display.clearDisplay();
display.setTextSize(1);
display.setCursor(0, 0);
display.setTextColor(WHITE);
display.drawBitmap(0, 0, VeryGood, 32, 32, 1);
display.display();
}
- OLED 화면 초기화 및
VeryGood
아이콘 출력.
void loop() {
String dateNtime, pm10Val, pm10Grade, tmp_str;
static int IntervalReq = 1800; // 30분 간격 요청
- 변수 초기화 및 30분마다 요청하도록 카운터 설정.
if (IntervalReq++ > 1800) {
IntervalReq = 0;
Requesthttp(); // 데이터 요청
}
delay(50);
- 50ms 지연 후, Wi-Fi 클라이언트 응답 대기.
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);
}
-
<dataTime>
태그에서 측정 일시 추출.
i = line.indexOf("</pm10Value>");
if (i > 0) {
tmp_str = "<pm10Value>";
pm10Val = line.substring(line.indexOf(tmp_str) + tmp_str.length(), i);
Serial.println(pm10Val);
}
-
<pm10Value>
태그에서 미세먼지 농도 추출.
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);
}
-
<pm10Grade>
태그에서 등급을 추출하여 OLED에 아이콘과 텍스트로 표시.
void Requesthttp() {
if (client.connect(server, httpPort)) {
Serial.println("\nSuccessed connection, and request http protocol");
client.print(String("Get /B552584/ArpltnInforInqireSvc"));
client.print(String("/getMsrstnAcctoRltmMesureDnsty?serviceKey=") + apiKey);
client.print(String("&returnType=") + returnType);
client.print(String("&numOfRows=") + numOfRows);
client.print(String("&pageNo=") + pageNo);
client.print(String("&stationName=") + urlencode(stationName));
client.print(String("&dataTerm=") + dataTerm);
client.print(version);
client.print(" HTTP/1.1\r\n");
client.print("Host: " + String(server) + "\r\n");
client.print("Connection: close\r\n\r\n");
} else {
Serial.println("\nfailed connection");
}
}
- Wi-Fi를 통해 공공데이터 API 서버에 연결하고 HTTP GET 요청을 전송.
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"); // 미세먼지 농도
}
- 날짜, 시간, 미세먼지 농도를 OLED에 출력.
void displayIcon(int grade) {
switch (grade) {
case 1: display.drawBitmap(0, 0, VeryGood, 32, 32, 1); break;
case 2: display.drawBitmap(0, 0, Good, 32, 32, 1); break;
case 3: display.drawBitmap(0, 0, Bad, 32, 32, 1); break;
case 4: display.drawBitmap(0, 0, VeryBad, 32, 32, 1); break;
}
}
- 미세먼지 등급(1~4)에 따라 대응하는 아이콘을 OLED에 출력.
String urlencode(String str) {
String encodedString = "";
for (int i = 0; i < str.length(); i++) {
char c = str.charAt(i);
if (c == ' ') encodedString += '+';
else if (isalnum(c)) encodedString += c;
else {
char code1 = (c & 0xf) + '0'; if ((c & 0xf) > 9) code1 = (c & 0xf) - 10 + 'A';
char code0 = (c >> 4) & 0xf; code0 = (code0 > 9) ? code0 - 10 + 'A' : code0 + '0';
encodedString += '%'; encodedString += code0; encodedString += code1;
}
}
return encodedString;
}
- URL에 한글 등 특수문자가 포함될 경우 % 인코딩을 적용하는 함수.
-
ESP32 + OLED + Wi-Fi + 공공데이터 API 연계를 통해 실시간으로 미세먼지 상태를 표시한다.
- 30분마다 데이터를 요청하고,
<dataTime>
, <pm10Value>
, <pm10Grade>
를 파싱한다.
- 등급별 아이콘과 수치를 OLED에 출력한다.