esp32 OpenWeatherMap에서 실시간 날씨 정보 받기

OpenWeatherMap.org

OpenWeatherMap은 세계 각 지역의 현재 날씨, 예보, 과거 날씨 데이터를 제공하는 사이트입니다. (몇가지 제약이 있긴 하지만) 회원가입을 통해서 API Key를 생성하고 이를 통해 무료로 실시간 날씨 정보를 얻을 수 있습니다.


Free Current weather and forecasts collection


Current weather collections API Key 생성

  1. Sign Up을 클릭하여 회원가입을 하고,



  2. Sign In을 클릭하여 로그인한 뒤, API Keys를 누릅니다.



  3. Default로 지정되어 있는 Key를 복사하거나, 새로운 Key를 생성합니다.


  1. API Key를 이용하는 방법은 메인홈페이지 메뉴의 API 탭을 누른뒤, 얻고자 하는 날씨정보(예를 들어 Current weather data)의 API doc을 참고하세요.

  2. 먼저 인터넷 브라우저를 통해 날씨정보가 어떻게 출력되는지 알아보기위하여 아래 홈페이지 주소에 접속해봅니다. 이때 주소의 끝부분에 있는 YOUR_API_KEY 대신 본인의 API Key를 입력합니다.


1
https://api.openweathermap.org/data/2.5/weather?q=Seoul,KR&units=metric&APPID=1bf3d5e1bd2e5934aadd86..........

1
https://api.openweathermap.org/data/2.5/weather?q=London,GB&units=metric&APPID=1bf3d5e1bd2e5934aadd86..........

1
https://api.openweathermap.org/data/2.5/weather?q=Shanghai,CN&units=metric&APPID=1bf3d5e1bd2e5934aadd86..........

브라우저에 출력되는 내용

1
{"coord":{"lon":-0.13,"lat":51.51},"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04n"}],"base":"stations","main":{"temp":277.16,"pressure":1007,"humidity":86,"temp_min":274.82,"temp_max":279.26},"visibility":8000,"wind":{"speed":5.1,"deg":90},"clouds":{"all":90},"dt":1574300635,"sys":{"type":1,"id":1414,"country":"GB","sunrise":1574321288,"sunset":1574352288},"timezone":0,"id":2643743,"name":"London","cod":200}

그밖의 도시 및 국가명은 Maps> Weather maps를 클릭하여 표시되는 지도를 통해 확인할 수 있습니다.



ESP32에서 사용하는 경우

wifi 접속 설정

1
2
const char* ssid     = "your ssid";                  // 연결할 SSID
const char* password = "your password"; // 연결할 SSID의 비밀번호
  • wifi에 접속하기 위한 ssid와 password를 설정

OpenWeatherMap.org 접속 설정

1
2
3
4
5
const String endpoint = "https://api.openweathermap.org";
const String ver = "/data/2.5/weather?q=";
const String city = "Shanghai,CN"; // City,Country (띄어쓰기 금지)
const String appid = "&units=metric&APPID="; // Units: metric
const String key = "1bf3d5e1bd2e5934aadd86.........."; // API Key
  • 날씨 데이터를 받아올 도시, 국가를 지정 (Shanghai,CN) (※주의: Shanghai,CN 사이에 띄어쓰기를 하면 안됨)
  • key 변수에 본의 API Key를 입력

sketch

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#include <WiFi.h>
#include <HTTPClient.h>

const char* ssid = "your ssid"; // 연결할 SSID
const char* password = "your password"; // 연결할 SSID의 비밀번호

// OpenWeatherMap.org: Shanghai,CN
const String endpoint = "https://api.openweathermap.org";
const String ver = "/data/2.5/weather?q=";
const String city = "Shanghai,CN"; // City,Country (띄어쓰기 금지)
const String appid = "&units=metric&APPID="; // Units: metric
const String key = "1bf3d5e1bd2e5934aadd86.........."; // API Key

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

WiFi.begin(ssid, password);

// 와이파이 연결
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.println("Connecting to WiFi..");
}

Serial.println("Connected to the WiFi network");
}

// 연결 여부 로그 출력
void loop() {
if ((WiFi.status() == WL_CONNECTED)) { // Check the current connection status

HTTPClient http;

http.begin(endpoint + ver + city + appid + key); // Specify the URL
int httpCode = http.GET(); // Make the request

if (httpCode > 0) { // Check for the returning code

String json = http.getString(); // get JSON format data
Serial.println(httpCode);
Serial.println(json);
}
else {
Serial.println("Error on HTTP request");
}

http.end(); // Free the resources
}

delay(30000);
}

출력 DATA

출력 데이터는 JSON(JavaScript Object Notation)을 기반으로 합니다. JSON은 프로그래밍 언어에 관계없이 데이터를 생성하고 읽을 수 있도록 만들어졌으며, 이를 통해 상호간에 보다 쉽게 데이터를 교환이 가능한 텍스트 기반의 데이터 교환 방식입니다. OpenWeatherMap에서도 이 방식을 사용하여 날씨 데이터를 생성하여 출력하고 있으며, 출력 데이터의 형태는 아래와 같습니다.

스케치 맨 아래라인의 delay 명령으로 인해 10초마다 1번씩 data를 출력합니다. 단, OpenWeatherMap free service의 update time이 2시간 이하로 되어 있으므로, 자료 갱신이 매 출력시마다 진행되지는 않습니다.


Firefox 등의 몇몇 인터넷 브라우저에서는 JSON 데이터를 보다 직관적으로 이해할 수 있도록 계층별로 나누어 출력해주기도 합니다.



JSON Parsing

위와 같이 복잡한 형태로 출력되는 날씨 데이터 중에서 필요한 날씨 정보를 추출하기 위하여 JSON (JavaScript Object Notation) 파싱 과정을 거쳐야 하는데요. 이를 위해 아두이노 IDE에서 Json 파싱을 위한 라이브러리를 설치하여야 합니다.


라이브러리 설치

ArduinoJson (by Benoit Blanchon) 라이브러리 (version 6)를 설치합니다.

스케치> 라이브러리 포함하기> 라이브러리 관리



Memory pool 크기 계산

  1. JSON 파싱에 사용할 배열의 크기를 계산하여 메모리를 확보합니다. 이를 위해 샘플로 사용할 JSON데이터가 필요하므로, 먼저 웹브라우저에서 OpenWeatherMap에 접속한 뒤,
1
https://api.openweathermap.org/data/2.5/weather?q=Shanghai,CN&units=metric&APPID=1bf3d5e1bd2e5934aadd86..........

  1. 출력되는 JSON 데이터를 드래그하여 복사합니다.
1
{"coord":{"lon":-0.13,"lat":51.51},"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04n"}],"base":"stations","main":{"temp":277.16,"pressure":1007,"humidity":86,"temp_min":274.82,"temp_max":279.26},"visibility":8000,"wind":{"speed":5.1,"deg":90},"clouds":{"all":90},"dt":1574300635,"sys":{"type":1,"id":1414,"country":"GB","sunrise":1574321288,"sunset":1574352288},"timezone":0,"id":2643743,"name":"London","cod":200}

  1. https://arduinojson.org/assistant 에 접속한 뒤, Input란에 있는 내용을 모두 지우고 복사한 JSON데이터를 붙여넣기하면, Memory pool size가 자동으로 계산됩니다.


  1. Memory pool size의 Expression부분을 드래그하여 복사하고, Additional bytes for strings dubplucation 부분의 숫자를 이용하여 스케치에서 다음의 형식으로 변수를 생성합니다.
1
const size_t capacity = JSON_ARRAY_SIZE(1) + JSON_OBJECT_SIZE(1) + 2*JSON_OBJECT_SIZE(2) + JSON_OBJECT_SIZE(4) + 2*JSON_OBJECT_SIZE(5) + JSON_OBJECT_SIZE(13) + 240;

실제로는 아래 Parsing Program에 이미 변수가 설정되어 있으므로, 이 과정은 수행하지 않아도 됩니다.


Parsing program

  1. Memory pool size가 출력된 화면의 아래쪽에 Parsing program도 함께 출력됩니다.


  1. 우선 Parsing Program 전체를 복사한 뒤, 스케치의 void loop() 부분에 붙여넣기 합니다.

  1. 밑줄친 부분의 json 변수가 나타나 있는 라인을 모두 지우고
1
const char* json = "{\"coord\":{\"lon\":121.49,\"lat\":31.23},\"weather\":[{\"id\":804,\"main\":\"Clouds\",\"description\":\"overcast clouds\",\"icon\":\"04d\"}],\"base\":\"stations\",\"main\":{\"temp\":293.02,\"pressure\":1027,\"humidity\":55,\"temp_min\":289.82,\"temp_max\":298.71},\"visibility\":10000,\"wind\":{\"speed\":3,\"deg\":60},\"clouds\":{\"all\":94},\"dt\":1574303919,\"sys\":{\"type\":1,\"id\":9659,\"country\":\"CN\",\"sunrise\":1574288762,\"sunset\":1574326426},\"timezone\":28800,\"id\":1796236,\"name\":\"Shanghai\",\"cod\":200}";

  1. OpenWeatherMap에서 실시간으로 받는 String 형식의 데이터 변수로 바꿔줍니다.
1
String json = http.getString();

  1. Timezone 반영을 위하여, 붙여넣기 한 Parsing program의 아래 부분에 아래 내용을 추가합니다.
1
2
3
dt = dt + timezone;                     // for GMT+8 -> +28800, for GMT+9 -> +32400
sys_sunrise = sys_sunrise + timezone;
sys_sunset = sys_sunset + timezone;

  1. UNIX Timestamp로 표기되는 시간을 일반적인 날짜, 시간으로 표기되도록 변환해줍니다. 이를 위해서 timelib.h 파일을 이용해야하는데, 이 파일은 Time 라이브러리(by Michael Margolis)에 포함되어 있지요. 그러므로 라이브러리 매니저를 통해 Time라이브러리를 찾아 설치해줍니다.

1
year(dt), month(dt), day(dt), hour(dt), minute(dt), second(dt), weekday(dt));

​ 위의 스케치를 통해서 UTC를 YYYY.MM.DD HH:MM:SS(요일) 형태의 날짜를 변환하며, printf문을 통해 출력할 수 있습니다.


Data 출력에 사용한 variables

JSON 형식의 데이터를 파싱할때 사용한 변수들입니다.

내용 형식 변수 unit
UNIX Time(UTC) long dt
Weather ID int weather_0_id
Weather Main const char* weather_0_main
Weather Description const char* weather_0_description
Weather Icon ID const char* weather_0_icon
Temperature float main_temp
Temperature Max. float main_temp_max
Temperature Min. float main_temp_min
Pressure int main_pressure hPa
Humidity int main_humidity %
Visibility int visibility m
Wind Speed int wind_speed m/s
Wind Degree int wind_deg º
Clouds int clouds_all
Sunrise long sys_sunrise
Sunset long sys_sunset
City ID long id
City name const char* name
Country const char* sys_country
Longitude float coord_lon º
Latitude float coord_lat º
GMT int timezone (※timezone/3600) hour
int sys_type
int sys_id
Base const char* base
COD int cod


sketch: 전체

위의 과정을 통해 만들진 부분 스케치를 병합하여 JSON 파싱을 위한 전체 스케치를 구성해봅니다.

ArduinoJson 라이브러리의 경우 version 5와 6의 사용 문법이 조금 다릅니다. 현재 Arduino IDE에서 라이브러리 추가과정을 통해 다운로드 받을 수 있는 최신 라이브러리 버전이 ArduinoJson 6이므로, 이에 맞게 작성하여야합니다. (기존의 ArduinoJson 5에 맞게 작성된 스케치는 컴파일이 되지 않으므로, Migrating from version 5 to 6 문서를 참고하여 스케치를 변경해야 합니다.)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
#include <WiFi.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include <TimeLib.h>

const char* ssid = "your ssid"; // 연결할 SSID
const char* password = "your password"; // 연결할 SSID의 비밀번호

const String endpoint = "https://api.openweathermap.org";
const String ver = "/data/2.5/weather?q=";
const String city = "Shanghai,CN"; // City,Country (띄어쓰기 금지)
const String appid = "&units=metric&APPID="; // Units: metric
const String key = "1bf3d5e1bd2e5934aadd86.........."; // API Key

unsigned long offset_days = 3; // 3 days for convert unix timestamp to datetime

void setup() {

Serial.begin(115200);

WiFi.begin(ssid, password);

while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.println("Connecting to WiFi..");
}

Serial.println("Connected to the WiFi network");
}

void loop() {
if ((WiFi.status() == WL_CONNECTED)) { // Check the current connection status

HTTPClient http;

http.begin(endpoint + ver + city + appid + key); // Specify the URL
int httpCode = http.GET(); // Make the request

if (httpCode > 0) { // Check for the returning code
// 데이터 파싱
const size_t capacity = JSON_ARRAY_SIZE(1) + JSON_OBJECT_SIZE(1) + 2*JSON_OBJECT_SIZE(2) + JSON_OBJECT_SIZE(4) + 2*JSON_OBJECT_SIZE(5) + JSON_OBJECT_SIZE(13) + 270;
DynamicJsonDocument doc(capacity);

String json = http.getString(); // get JSON format data

deserializeJson(doc, json);

float coord_lon = doc["coord"]["lon"]; // 121.49
float coord_lat = doc["coord"]["lat"]; // 31.23

JsonObject weather_0 = doc["weather"][0];
int weather_0_id = weather_0["id"]; // 804
const char* weather_0_main = weather_0["main"]; // "Clouds"
const char* weather_0_description = weather_0["description"]; // "overcast clouds"
const char* weather_0_icon = weather_0["icon"]; // "04d"

/*
JsonObject weather_1 = weather[1];
int weather_1_id = weather_1["id"]; // 701
const char* weather_1_main = weather_1["main"]; // "Mist"
const char* weather_1_description = weather_1["description"]; // "mist"
const char* weather_1_icon = weather_1["icon"]; // "50n"

JsonObject weather_2 = weather[2];
int weather_2_id = weather_2["id"]; // 741
const char* weather_2_main = weather_2["main"]; // "Fog"
const char* weather_2_description = weather_2["description"]; // "fog"
const char* weather_2_icon = weather_2["icon"]; // "50n"
*/

const char* base = doc["base"]; // "stations"

JsonObject main = doc["main"];
float main_temp = main["temp"]; // 293.02
int main_pressure = main["pressure"]; // 1027
int main_humidity = main["humidity"]; // 55
float main_temp_min = main["temp_min"]; // 289.82
float main_temp_max = main["temp_max"]; // 298.71

int visibility = doc["visibility"]; // 10000

int wind_speed = doc["wind"]["speed"]; // 3
int wind_deg = doc["wind"]["deg"]; // 60

int clouds_all = doc["clouds"]["all"]; // 94

long dt = doc["dt"]; // 1574303919

JsonObject sys = doc["sys"];
int sys_type = sys["type"]; // 1
int sys_id = sys["id"]; // 9659
//float sys_message = sys["message"]; // Internal parameter (0.0226)
const char* sys_country = sys["country"]; // "CN"
long sys_sunrise = sys["sunrise"]; // 1574288762
long sys_sunset = sys["sunset"]; // 1574326426

int timezone = doc["timezone"]; // 28800
long id = doc["id"]; // 1796236
const char* name = doc["name"]; // "Shanghai"
int cod = doc["cod"]; // 200

dt = dt + timezone; // for GMT+8 -> +28800, for GMT+9 -> +32400
sys_sunrise = sys_sunrise + timezone;
sys_sunset = sys_sunset + timezone;

//int main_sea_level = main["sea_level"]; // Atmospheric pressure hPa on the sea level
//int main_grnd_level = main["grnd_level"]; // Atmospheric pressure hPa on the ground level
//int rain_1h = doc["rain"]["1h"]; // Rain volume for the last 1 hour, mm
//int rain_3h = doc["rain"]["3h"]; // Rain volume for the last 3 hour, mm
//int snow_1h = doc["snow"]["1h"]; // Snow volume for the last 1 hour, mm
//int snow_3h = doc["snow"]["3h"]; // Snow volume for the last 3 hour, mm

//데이터 출력
Serial.print("UNIX Time of Weather: "); Serial.println(dt);
Serial.printf("Time of Weather: %4d-%02d-%02d %02d:%02d:%02d(%1d)\n", year(dt), month(dt), day(dt), hour(dt), minute(dt), second(dt), weekday(dt));
Serial.print("Weather ID: "); Serial.println(weather_0_id);
Serial.print("Weather Main: "); Serial.println(weather_0_main);
Serial.print("Weather Description: "); Serial.println(weather_0_description);
Serial.print("Weather Icon: "); Serial.println(weather_0_icon);
Serial.print("Temperature(℃): "); Serial.println(main_temp);
Serial.print("Temperature Max(℃): "); Serial.println(main_temp_max);
Serial.print("Temperature Min(℃): "); Serial.println(main_temp_min);
Serial.print("Pressure(hPa): "); Serial.println(main_pressure);
Serial.print("Humidity(%): "); Serial.println(main_humidity);
Serial.print("Visibility(m): "); Serial.println(visibility);
Serial.print("Wind Speed(m/s): "); Serial.println(wind_speed);
Serial.print("Wind Degree(º): "); Serial.println(wind_deg);
Serial.print("Clouds: "); Serial.println(clouds_all);
Serial.printf("Sunrise: %02d:%02d:%02d\n", hour(sys_sunrise), minute(sys_sunrise), second(sys_sunrise));
Serial.printf("Sunset: %02d:%02d:%02d\n", hour(sys_sunset), minute(sys_sunset), second(sys_sunset));
Serial.print("City/Country: "); Serial.print(name); Serial.print("/"); Serial.println(sys_country);
Serial.print("longitude/latidude: "); Serial.print(coord_lon); Serial.print("/"); Serial.println(coord_lat);
Serial.print("Timezone: GMT+"); Serial.println(timezone/3600);

/*
Serial.print("Sea Level(hPa): "); Serial.println(main_sea_level);
Serial.print("Ground Level(hPa): "); Serial.println(main_grnd_level);
Serial.print("Rain 1h(mm): "); Serial.println(rain_1h);
Serial.print("Rain 3h(mm): "); Serial.println(rain_3h);
Serial.print("Snow 1h(mm): "); Serial.println(snow_1h);
Serial.print("Snow 3h(mm): "); Serial.println(snow_3h);
*/
}

else {
Serial.println("Error on HTTP request");
}

http.end();
}

delay(30000);
}

출력 Data



Weather Condition Code

Group 2xx: Thunderstorm

ID Main Description Icon
200 Thunderstorm thunderstorm with light rain img 11d
201 Thunderstorm thunderstorm with rain img 11d
202 Thunderstorm thunderstorm with heavy rain img 11d
210 Thunderstorm light thunderstorm img 11d
211 Thunderstorm thunderstorm img 11d
212 Thunderstorm heavy thunderstorm img 11d
221 Thunderstorm ragged thunderstorm img 11d
230 Thunderstorm thunderstorm with light drizzle img 11d
231 Thunderstorm thunderstorm with drizzle img 11d
232 Thunderstorm thunderstorm with heavy drizzle img 11d

Group 3xx: Drizzle

ID Main Description Icon
300 Drizzle light intensity drizzle img 09d
301 Drizzle drizzle img 09d
302 Drizzle heavy intensity drizzle img 09d
310 Drizzle light intensity drizzle rain img 09d
311 Drizzle drizzle rain img 09d
312 Drizzle heavy intensity drizzle rain img 09d
313 Drizzle shower rain and drizzle img 09d
314 Drizzle heavy shower rain and drizzle img 09d
321 Drizzle shower drizzle img 09d

Group 5xx: Rain

ID Main Description Icon
500 Rain light rain img 10d
501 Rain moderate rain img 10d
502 Rain heavy intensity rain img 10d
503 Rain very heavy rain img 10d
504 Rain extreme rain img 10d
511 Rain freezing rain img 13d
520 Rain light intensity shower rain img 09d
521 Rain shower rain img 09d
522 Rain heavy intensity shower rain img 09d
531 Rain ragged shower rain img 09d

Group 6xx: Snow

ID Main Description Icon
600 Snow light snow img 13d
601 Snow Snow img 13d
602 Snow Heavy snow img 13d
611 Snow Sleet img 13d
612 Snow Light shower sleet img 13d
613 Snow Shower sleet img 13d
615 Snow Light rain and snow img 13d
616 Snow Rain and snow img 13d
620 Snow Light shower snow img 13d
621 Snow Shower snow img 13d
622 Snow Heavy shower snow img 13d

Group 7xx: Atmosphere

ID Main Description Icon
701 Mist mist img 50d
711 Smoke Smoke img 50d
721 Haze Haze img 50d
731 Dust sand/ dust whirls img 50d
741 Fog fog img 50d
751 Sand sand img 50d
761 Dust dust img 50d
762 Ash volcanic ash img 50d
771 Squall squalls img 50d
781 Tornado tornado img 50d

Group 800: Clear

ID Main Description Icon
800 Clear clear sky img 01d img 01n

Group 80x: Clouds

ID Main Description Icon
801 Clouds few clouds: 11-25% img 02d img 02n
802 Clouds scattered clouds: 25-50% img 03d img 03n
803 Clouds broken clouds: 51-84% img 04d img 04n
804 Clouds overcast clouds: 85-100% img 04d img 04n

ICON 출력방법

Weather ID 801에 해당하는 아이콘 code가 02d이므로 URL을 다음과 같이 지정하면

1
http://openweathermap.org/img/wn/10d.png

ICON크기를 크게 출력하고 싶은 경우에는 아이콘 code에 @2x를 덧붙입니다.

1
http://openweathermap.org/img/wn/10d@2x.png



1602 LCD에 날씨 정보 출력하기

여러가지 날씨 정보 중에서 현재 온도를 1602 LCD(I2C)에 출력해보겠습니다.


라이브러리 설치하기

스케치> 라이브러리 포함하기> 라이브러리 관리

  • LiquidCrystal I2C (by Frank de Brabander) 검색하여 설치

schematic


sketch

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
#include <WiFi.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include <TimeLib.h>

const char* ssid = "your ssid"; // 연결할 SSID
const char* password = "your password"; // 연결할 SSID의 비밀번호

const String endpoint = "https://api.openweathermap.org";
const String ver = "/data/2.5/weather?q=";
const String city = "Shanghai,CN"; // City,Country (띄어쓰기 금지)
const String appid = "&units=metric&APPID="; // Units: metric
const String key = "1bf3d5e1bd2e5934aadd86.........."; // API Key

unsigned long offset_days = 3; // 3 days for convert unix timestamp to datetime
float main_temp;

// set the LCD
#include <LiquidCrystal_I2C.h>

int lcdColumns = 16;
int lcdRows = 2;

// set LCD address, number of columns and rows
// if you don't know your display address, run an I2C scanner sketch
LiquidCrystal_I2C lcd(0x27, lcdColumns, lcdRows);

void setup() {

Serial.begin(115200);

WiFi.begin(ssid, password);

while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.println("Connecting to WiFi..");
}

Serial.println("Connected to the WiFi network");

// initialize LCD
lcd.init();
// turn on LCD backlight
lcd.backlight();
}

void loop() {
if ((WiFi.status() == WL_CONNECTED)) { // Check the current connection status

HTTPClient http;

http.begin(endpoint + ver + city + appid + key); // Specify the URL
int httpCode = http.GET(); // Make the request

if (httpCode > 0) { // Check for the returning code
// 데이터 파싱
const size_t capacity = JSON_ARRAY_SIZE(1) + JSON_OBJECT_SIZE(1) + 2*JSON_OBJECT_SIZE(2) + JSON_OBJECT_SIZE(4) + 2*JSON_OBJECT_SIZE(5) + JSON_OBJECT_SIZE(13) + 270;
DynamicJsonDocument doc(capacity);

String json = http.getString(); // get JSON format data

deserializeJson(doc, json);

float coord_lon = doc["coord"]["lon"]; // 121.49
float coord_lat = doc["coord"]["lat"]; // 31.23

JsonObject weather_0 = doc["weather"][0];
int weather_0_id = weather_0["id"]; // 804
const char* weather_0_main = weather_0["main"]; // "Clouds"
const char* weather_0_description = weather_0["description"]; // "overcast clouds"
const char* weather_0_icon = weather_0["icon"]; // "04d"

/*
JsonObject weather_1 = weather[1];
int weather_1_id = weather_1["id"]; // 701
const char* weather_1_main = weather_1["main"]; // "Mist"
const char* weather_1_description = weather_1["description"]; // "mist"
const char* weather_1_icon = weather_1["icon"]; // "50n"

JsonObject weather_2 = weather[2];
int weather_2_id = weather_2["id"]; // 741
const char* weather_2_main = weather_2["main"]; // "Fog"
const char* weather_2_description = weather_2["description"]; // "fog"
const char* weather_2_icon = weather_2["icon"]; // "50n"
*/

const char* base = doc["base"]; // "stations"

JsonObject main = doc["main"];
float main_temp = main["temp"]; // 293.02
int main_pressure = main["pressure"]; // 1027
int main_humidity = main["humidity"]; // 55
float main_temp_min = main["temp_min"]; // 289.82
float main_temp_max = main["temp_max"]; // 298.71

int visibility = doc["visibility"]; // 10000

int wind_speed = doc["wind"]["speed"]; // 3
int wind_deg = doc["wind"]["deg"]; // 60

int clouds_all = doc["clouds"]["all"]; // 94

long dt = doc["dt"]; // 1574303919

JsonObject sys = doc["sys"];
int sys_type = sys["type"]; // 1
int sys_id = sys["id"]; // 9659
//float sys_message = sys["message"]; // Internal parameter (0.0226)
const char* sys_country = sys["country"]; // "CN"
long sys_sunrise = sys["sunrise"]; // 1574288762
long sys_sunset = sys["sunset"]; // 1574326426

int timezone = doc["timezone"]; // 28800
long id = doc["id"]; // 1796236
const char* name = doc["name"]; // "Shanghai"
int cod = doc["cod"]; // 200

dt = dt + timezone; // for GMT+8 -> +28800, for GMT+9 -> +32400
sys_sunrise = sys_sunrise + timezone;
sys_sunset = sys_sunset + timezone;

//int main_sea_level = main["sea_level"]; // Atmospheric pressure hPa on the sea level
//int main_grnd_level = main["grnd_level"]; // Atmospheric pressure hPa on the ground level
//int rain_1h = doc["rain"]["1h"]; // Rain volume for the last 1 hour, mm
//int rain_3h = doc["rain"]["3h"]; // Rain volume for the last 3 hour, mm
//int snow_1h = doc["snow"]["1h"]; // Snow volume for the last 1 hour, mm
//int snow_3h = doc["snow"]["3h"]; // Snow volume for the last 3 hour, mm

//데이터 출력
Serial.print("UNIX Time of Weather: "); Serial.println(dt);
Serial.printf("Time of Weather: %4d-%02d-%02d %02d:%02d:%02d(%1d)\n", year(dt), month(dt), day(dt), hour(dt), minute(dt), second(dt), weekday(dt));
Serial.print("Weather ID: "); Serial.println(weather_0_id);
Serial.print("Weather Main: "); Serial.println(weather_0_main);
Serial.print("Weather Description: "); Serial.println(weather_0_description);
Serial.print("Weather Icon: "); Serial.println(weather_0_icon);
Serial.print("Temperature(℃): "); Serial.println(main_temp);
Serial.print("Temperature Max(℃): "); Serial.println(main_temp_max);
Serial.print("Temperature Min(℃): "); Serial.println(main_temp_min);
Serial.print("Pressure(hPa): "); Serial.println(main_pressure);
Serial.print("Humidity(%): "); Serial.println(main_humidity);
Serial.print("Visibility(m): "); Serial.println(visibility);
Serial.print("Wind Speed(m/s): "); Serial.println(wind_speed);
Serial.print("Wind Degree(º): "); Serial.println(wind_deg);
Serial.print("Clouds: "); Serial.println(clouds_all);
Serial.printf("Sunrise: %02d:%02d:%02d\n", hour(sys_sunrise), minute(sys_sunrise), second(sys_sunrise));
Serial.printf("Sunset: %02d:%02d:%02d\n", hour(sys_sunset), minute(sys_sunset), second(sys_sunset));
Serial.print("City/Country: "); Serial.print(name); Serial.print("/"); Serial.println(sys_country);
Serial.print("longitude/latidude: "); Serial.print(coord_lon); Serial.print("/"); Serial.println(coord_lat);
Serial.print("Timezone: GMT+"); Serial.println(timezone/3600);

/*
Serial.print("Sea Level(hPa): "); Serial.println(main_sea_level);
Serial.print("Ground Level(hPa): "); Serial.println(main_grnd_level);
Serial.print("Rain 1h(mm): "); Serial.println(rain_1h);
Serial.print("Rain 3h(mm): "); Serial.println(rain_3h);
Serial.print("Snow 1h(mm): "); Serial.println(snow_1h);
Serial.print("Snow 3h(mm): "); Serial.println(snow_3h);
*/
lcd.setCursor(0, 0);
lcd.print("Temperature : ");
lcd.setCursor(0, 1);
lcd.print(main_temp);
}

else {
Serial.println("Error on HTTP request");
}

http.end();
}

delay(30000);

lcd.setCursor(0, 1);
lcd.print(" ");
}

Arduino IDE에서 컴파일시 *””경고: 라이브러리 LiquidCrystal_I2C가 avr 아키텍처에서 실행되며 esp32아키텍처에서 실행되는 현재보드에서는 호환되지 않을 수 있습니다.””*라는 메세지가 나타나지만, LCD 출력에는 문제가 없습니다.



공공데이터 활용하기

위의 방법을 응용하면 국가별, 관련 업체별로 제공되는 여러가지 공공데이터를 활용할 수 있습니다.

그밖에 활용가능한 공공데이터가 어떤 것이 있는지, 살펴보세요~

esp32 OpenWeatherMap에서 실시간 날씨 정보 받기

http://stemwith.github.io/2019/11/19/ESP32-OpenWeatherMap/

Author

chemidot

Posted on

2019-11-19

Updated on

2021-11-17

Licensed under

댓글