#include <WiFi.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include <Wire.h>
#include <U8g2lib.h>
#include <time.h>
const char* SERVICE_KEY = "16ef0f01ec010d556ea929c036bfad35d97a46b90617cac6c275f83bcf1ca892";
const char* API_URL = "http://apis.data.go.kr/B552584/ArpltnInforInqireSvc/getMinuDustFrcstDspth";
const char* REGION = "서울";
const int LED_RED = 25;
const int LED_YELLOW = 26;
const int LED_GREEN = 27;
const unsigned long UPDATE_MS = 10UL * 60UL * 1000UL;
unsigned long lastUpdate = 0;
U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE);
String todayKST() {
struct tm t;
if (getLocalTime(&t, 3000)) {
char buf[11];
strftime(buf, sizeof(buf), "%Y-%m-%d", &t);
return String(buf);
}
return "2026-04-15";
}
void connectWiFi() {
WiFi.mode(WIFI_STA);
while (WiFi.status() != WL_CONNECTED) {
WiFi.begin("Wokwi-GUEST", "", 6);
unsigned long st = millis();
while (WiFi.status() != WL_CONNECTED && millis() - st < 10000) {
delay(200);
}
}
Serial.print("WiFi OK, IP: ");
Serial.println(WiFi.localIP());
}
String normalizeGrade(String s) {
s.trim();
s.replace(" ", "");
if (s.indexOf("매우나쁨") >= 0) return "매우나쁨";
if (s.indexOf("나쁨") >= 0) return "나쁨";
if (s.indexOf("보통") >= 0) return "보통";
if (s.indexOf("좋음") >= 0) return "좋음";
return "알수없음";
}
String extractRegionGrade(const String& all) {
int start = 0;
while (start < all.length()) {
int comma = all.indexOf(',', start);
if (comma < 0) comma = all.length();
String token = all.substring(start, comma);
token.trim();
if (token.indexOf(REGION) >= 0) {
int colon = token.indexOf(':');
if (colon < 0) colon = token.indexOf(":");
if (colon >= 0) {
String g = token.substring(colon + 1);
g.trim();
return normalizeGrade(g);
}
}
start = comma + 1;
}
return "";
}
void setLed(const String& grade) {
digitalWrite(LED_RED, LOW);
digitalWrite(LED_YELLOW, LOW);
digitalWrite(LED_GREEN, LOW);
if (grade == "좋음") digitalWrite(LED_GREEN, HIGH);
else if (grade == "보통") digitalWrite(LED_YELLOW, HIGH);
else if (grade == "나쁨" || grade == "매우나쁨") digitalWrite(LED_RED, HIGH);
}
void showOLED(const String& grade, const String& dt) {
u8g2.clearBuffer();
u8g2.setFont(u8g2_font_unifont_t_korean2);
u8g2.drawUTF8(0, 20, "대기질 상태 :");
u8g2.drawUTF8(0, 44, grade.c_str());
u8g2.setFont(u8g2_font_6x10_tf);
String line = "Date: " + dt;
u8g2.drawStr(0, 62, line.c_str());
u8g2.sendBuffer();
}
bool fetchAir(String& outGrade, String& outDate) {
if (WiFi.status() != WL_CONNECTED) return false;
String d = todayKST();
String url = String(API_URL) +
"?serviceKey=" + SERVICE_KEY +
"&returnType=json" +
"&numOfRows=10" +
"&pageNo=1" +
"&searchDate=" + d +
"&InformCode=PM10";
HTTPClient http;
http.setTimeout(15000);
if (!http.begin(url)) return false;
int code = http.GET();
if (code != HTTP_CODE_OK) {
Serial.print("HTTP error: ");
Serial.println(code);
http.end();
return false;
}
String payload = http.getString();
http.end();
DynamicJsonDocument doc(45000);
DeserializationError err = deserializeJson(doc, payload);
if (err) {
Serial.print("JSON error: ");
Serial.println(err.c_str());
return false;
}
const char* rc = doc["response"]["header"]["resultCode"] | "";
if (String(rc) != "00") {
Serial.print("API resultCode: ");
Serial.println(rc);
return false;
}
JsonArray items = doc["response"]["body"]["items"].as<JsonArray>();
if (items.isNull() || items.size() == 0) return false;
JsonObject it = items[0];
String informGrade = it["informGrade"] | "";
String regionGrade = extractRegionGrade(informGrade);
if (regionGrade.length() == 0) {
String overall = it["informOverall"] | "";
regionGrade = normalizeGrade(overall);
}
if (regionGrade == "알수없음" || regionGrade.length() == 0) {
regionGrade = normalizeGrade(informGrade);
}
const char* d1 = it["informData"] | "";
const char* d2 = it["dataTime"] | "";
if (strlen(d1) > 0) outDate = String(d1);
else if (strlen(d2) > 0) outDate = String(d2);
else outDate = d;
outGrade = regionGrade;
return true;
}
void updateAll() {
String grade, dt;
if (fetchAir(grade, dt)) {
setLed(grade);
showOLED(grade, dt);
Serial.print("상태: ");
Serial.println(grade);
} else {
setLed("알수없음");
showOLED("통신오류", todayKST());
Serial.println("API 호출 실패");
}
}
void setup() {
Serial.begin(115200);
pinMode(LED_RED, OUTPUT);
pinMode(LED_YELLOW, OUTPUT);
pinMode(LED_GREEN, OUTPUT);
digitalWrite(LED_RED, LOW);
digitalWrite(LED_YELLOW, LOW);
digitalWrite(LED_GREEN, LOW);
u8g2.begin();
showOLED("초기화중", "-");
connectWiFi();
configTime(9 * 3600, 0, "pool.ntp.org", "time.nist.gov", "time.google.com");
updateAll();
lastUpdate = millis();
}
void loop() {
if (WiFi.status() != WL_CONNECTED) connectWiFi();
if (millis() - lastUpdate >= UPDATE_MS) {
lastUpdate = millis();
updateAll();
}
}
Loading
ssd1306
ssd1306