', { cookie_domain: 'auto', cookie_flags: 'max-age=0;domain=.tistory.com', cookie_expires: 7 * 24 * 60 * 60 // 7 days, in seconds }); ESP32 날씨 표시기 #3 - openweathermap 데이터 분류와 정리. :: MakerLee's Workspace
728x90

Openweathermap에서 날씨를 받아와 사용하려 했는데 데이터 변환 과정에서 골치아픈 부분이 있었습니다. 

차후 4.2" 전자잉크 등 보다 화면이 넓은 기기를 사용하기 위해서 당일의 온도변화, 7일치의 날씨변화 등도 있었으면 했고요.

무엇보다 현재 날씨는 알 수 있는데 당일 날씨나 최저, 최고 온도 등 우리가 흔히 쓰는 기준으로 데이터를 얻기가 힘들더군요.

 

 

그리고 이 모든 것을 ChatGPT4.0 에게 맡겼습니다. 

 

 

 

 

 

 

 

 

저는 그냥 이렇게 해줘, 저렇게 해줘, 여기 틀렸잖아

를 반복하다 보니 코드는 손도 안대고 좋은 결과가 나오더군요

 

 

금일 온도 / 최저 / 최고온도 / 체감온도/ 구름 / 풍속 / 강수량 / 강설량 등이 잘 정리되어 출력된 것을 볼 수 있습니다. 

5일간 날씨 데이터도 마찬가지로 정리되어 나오고 있고요

 

 

 

아래는 이 코드입니다.

#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <HTTPClient.h>
#include <Arduino_JSON.h>

// WiFi 설정
const char* ssid = "Yourssid";
const char* password = "yourPW";

// OpenWeatherMap API 키
String openWeatherMapApiKey = "YourAPI";

// 서울의 도시명과 국가 코드
String city = "Seoul";
String countryCode = "KR";

// 업데이트 주기 설정 (15초)
unsigned long lastTime = 0;
unsigned long timerDelay = 15000; 

void setup() {
  Serial.begin(115200); // 시리얼 모니터 시작
  Serial.println("serial start"); // 디버깅 메시지 추가
  WiFi.begin(ssid, password); // WiFi 연결 시작
  Serial.println("Connecting to WiFi");
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("\nConnected to WiFi");
  Serial.print("IP Address: ");
  Serial.println(WiFi.localIP());

  // 즉시 첫 번째 API 호출을 위해 lastTime 초기화
  lastTime = millis() - timerDelay;
}

void loop() {
  // 타이머가 만료되었을 때 데이터 요청
  if ((millis() - lastTime) > timerDelay) {
    if (WiFi.status() == WL_CONNECTED) {
      // API 요청 URL 구성 - 3시간 간격의 5일 예보 데이터
      String serverPath = "https://api.openweathermap.org/data/2.5/forecast?q=" + city + "," + countryCode + "&appid=" + openWeatherMapApiKey + "&units=metric";
      
      // 데이터 요청 및 수신
      String weatherData = httpGETRequest(serverPath.c_str());
      if (weatherData != "{}") { 
        parseWeatherData(weatherData); // 수신된 데이터 파싱
      }
      lastTime = millis();
    } else {
      Serial.println("WiFi Disconnected");
    }
  }
}

// HTTP GET 요청 함수
String httpGETRequest(const char* serverName) {
  WiFiClientSecure client; 
  client.setInsecure();    // SSL 인증서 검증 무시
  HTTPClient http;
  http.begin(client, serverName); // HTTPS 연결 시작
  Serial.println("Requesting URL: " + String(serverName));

  int httpResponseCode = http.GET();
  
  String payload = "{}";
  if (httpResponseCode == 200) {
    Serial.print("HTTP Response code: ");
    Serial.println(httpResponseCode);
    payload = http.getString();
  } else {
    Serial.print("Error code: ");
    Serial.println(httpResponseCode);
    payload = http.getString(); 
    Serial.println("Error response: " + payload);
  }
  http.end();
  return payload;
}

// 타임스탬프를 한국 표준시(KST)로 변환하는 함수
String convertUnixTimeToKST(long unixTime) {
  unixTime += 9 * 3600; // UTC+9 시간대 보정
  time_t t = unixTime;
  struct tm *tmStruct = localtime(&t);
  char buffer[30];
  strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", tmStruct);
  return String(buffer);
}

// 날씨 데이터 파싱 함수
void parseWeatherData(String json) {
  JSONVar myObject = JSON.parse(json);
  if (JSON.typeof(myObject) == "undefined") {
    Serial.println("Parsing failed!");
    return;
  }

  // 오늘 날씨 데이터 파싱 및 출력
  Serial.println("Today's Weather Data:");
  JSONVar list = myObject["list"];

  // 하루 동안의 데이터에서 최저/최고 온도 계산
  float dayMinTemp = 1000;
  float dayMaxTemp = -1000;
  for (int i = 0; i < 8; i++) { // 24시간 내의 3시간 간격 데이터
    JSONVar data = list[i];
    float temp_min = (float)(double)data["main"]["temp_min"];
    float temp_max = (float)(double)data["main"]["temp_max"];
    if (temp_min < dayMinTemp) dayMinTemp = temp_min;
    if (temp_max > dayMaxTemp) dayMaxTemp = temp_max;
  }
  JSONVar todayData = list[0];
  float temp = (float)(double)todayData["main"]["temp"];
  float feels_like = (float)(double)todayData["main"]["feels_like"];
  const char* weather_description = (const char*)(todayData["weather"][0]["description"]);
  float humidity = (float)(double)todayData["main"]["humidity"];
  float clouds = (float)(double)todayData["clouds"]["all"];
  float wind_speed = (float)(double)todayData["wind"]["speed"];
  float rain = todayData.hasOwnProperty("rain") ? (float)(double)todayData["rain"]["3h"] : 0;
  float snow = todayData.hasOwnProperty("snow") ? (float)(double)todayData["snow"]["3h"] : 0;

  Serial.print("Temp: ");
  Serial.println(temp);
  Serial.print("Min Temp: ");
  Serial.println(dayMinTemp);
  Serial.print("Max Temp: ");
  Serial.println(dayMaxTemp);
  Serial.print("Feels Like: ");
  Serial.println(feels_like);
  Serial.print("Humidity: ");
  Serial.println(humidity);
  Serial.print("Clouds: ");
  Serial.println(clouds);
  Serial.print("Weather: ");
  Serial.println(weather_description);
  Serial.print("Wind Speed: ");
  Serial.println(wind_speed);
  Serial.print("Rain Volume (3h): ");
  Serial.println(rain);
  Serial.print("Snow Volume (3h): ");
  Serial.println(snow);
  Serial.println("-------");

  // 5일간 날씨 데이터 파싱 및 출력
  Serial.println("5 Days Weather Data:");
  for (int day = 0; day < 5; day++) {
    float minTemp = 1000;
    float maxTemp = -1000;
    float avgHumidity = 0;
    float avgClouds = 0;
    float avgWindSpeed = 0;
    float totalRain = 0;
    float totalSnow = 0;
    int count = 0;
    String weatherDescriptions = "";

    for (int i = day * 8; i < (day + 1) * 8 && i < list.length(); i++) {
      JSONVar dayData = list[i];
      float temp_min = (float)(double)dayData["main"]["temp_min"];
      float temp_max = (float)(double)dayData["main"]["temp_max"];
      float humidity = (float)(double)dayData["main"]["humidity"];
      float clouds = (float)(double)dayData["clouds"]["all"];
      float wind_speed = (float)(double)dayData["wind"]["speed"];
      float rain = dayData.hasOwnProperty("rain") ? (float)(double)dayData["rain"]["3h"] : 0;
      float snow = dayData.hasOwnProperty("snow") ? (float)(double)dayData["snow"]["3h"] : 0;
      const char* weather_description = (const char*)(dayData["weather"][0]["description"]);

      if (temp_min < minTemp) minTemp = temp_min;
      if (temp_max > maxTemp) maxTemp = temp_max;
      avgHumidity += humidity;
      avgClouds += clouds;
      avgWindSpeed += wind_speed;
      totalRain += rain;
      totalSnow += snow;
      weatherDescriptions = String(weather_description);
      count++;
    }
    avgHumidity /= count;
    avgClouds /= count;
    avgWindSpeed /= count;

    long date = (long)list[day * 8]["dt"];
    Serial.print("Date: ");
    Serial.print(convertUnixTimeToKST(date));
    Serial.print(", Min Temp: ");
    Serial.print(minTemp);
    Serial.print(", Max Temp: ");
    Serial.print(maxTemp);
    Serial.print(", Humidity: ");
    Serial.print(avgHumidity);
    Serial.print(", Clouds: ");
    Serial.print(avgClouds);
    Serial.print(", Wind Speed: ");
    Serial.print(avgWindSpeed);
    Serial.print(", Rain Volume (3h): ");
    Serial.print(totalRain);
    Serial.print(", Snow Volume (3h): ");
    Serial.print(totalSnow);
    Serial.print(", Weather: ");
    Serial.println(weatherDescriptions);
    Serial.println("-------");
  }
}

 

 

그렇지만 이 모든 걸 갈아엎게 되는데.. 다음 포스팅에 이어서 쓰겠습니다.

728x90

+ Recent posts