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 BLE, Blynk Gauge

ESP32에서 Blynk로 센서값 보내기

Blynk App의 GAUGE 위젯을 사용하여 ESP32에 연결된 Potentiometer의 저항값을 읽어 Blynk App에 전압(V), 저항(㏀) 값을 표시해본다. (ESP32 → Blynk App)

  • ESP32의 Potentiometer 값을 읽기 위해 BLYNK_READ(Vpin)을 사용한다.
  • 새로운 프로젝트를 만드는 경우, 새로운 auth token을 받아서 사용해야 한다.

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
#define BLYNK_PRINT Serial
#define BLYNK_USE_DIRECT_CONNECT

#include <BlynkSimpleEsp32_BLE.h>
#include <BLEDevice.h>
#include <BLEServer.h>

// You should get Auth Token in the Blynk App.
// Go to the Project Settings (nut icon).
char auth[] = "WUAeS6P57T3XNaIGyLK1-OUv........";

// Potentiometer Pin
#define PIN_POTENTIOMETER A0 // Potentiometer GPIO 36

// Resist, Voltage
int value_Potentiometer;
int value_R;
float value_V;

void setup()
{
// Debug console
Serial.begin(115200);
Serial.println("Waiting for connections...");

// for Blynk
Blynk.setDeviceName("Blynk-t");
Blynk.begin(auth);
}

void loop()
{
value_Potentiometer = analogRead(PIN_POTENTIOMETER); // 12bit ADC (0~4095)
value_V = map(value_Potentiometer, 0, 4095, 3300, 0); // 0~3300mV
value_R = map(value_V, 0, 3300, 10000, 0); // 0~10kΩ

Blynk.run();

Serial.print("Resist : "); Serial.print(value_R); Serial.println("Ω");
Serial.print("Voltage: "); Serial.print(value_V); Serial.println("mV");

delay(1000);
}

BLYNK_READ(V1) // ESP32 -> widget virtual pin1
{
Blynk.virtualWrite(V1, value_R);
}

BLYNK_READ(V2) // ESP32 -> widget virtual pin1
{
Blynk.virtualWrite(V2, value_V);
}
  • Blynk에서 2개의 Gauge를 사용하기 위해 BLYNK)READ(Vpin)을 2번 사용한다.
  • Blynk.virtualWrite(Vpin, value)를 통해 Blynk App의 Gauge 위젯에서 지정한 virtual pin에 value값을 표시한다.


Blynk 설정

  1. 새로운 프로젝트를 생성한다.
    • NAME : ESP32_Potentiometer
    • CHOOSE DEVICE : ESP32 Dev Board
    • CONNECTION TYPE : BLE

  1. 이메일로 발송된 토큰을 복사한다.

  1. +버튼을 눌러 위젯을 추가할 수 있는 화면으로 진입한다.

  1. 먼저 BLE 위젯을 선택하여 ESP32와 Blynk간의 통신 방법을 지정한다.

  1. 블루투스 모양의 위젯 버튼을 눌러 Blynk와의 통신을 준비한다.

  1. Connect BLE device

  1. 연결하고자 하는 BLE 장비를 선택하면,

  1. 연결 확인!

  1. +버튼을 눌러 출력 위젯 2개를 추가한다. (Gauge, Labeled value)

  1. 먼저 Gauge 위젯을 선택한다.

  1. Gauge 위젯이 추가되면 육각형 모양의 아이콘을 눌러 GAUGE Setting 화면으로 진입한다.


  1. 설정화면에서 다음 4가지를 설정한다.
    • NAME : 저항
    • INPUT : V1, 0~1000
    • LABEL : (단위를 표시하기 위해) x10 Ohm 을 적는다.
    • READING RATE : 1sec (PUSH 위젯은 실행되지 않음)

  1. Labeled Value 위젯을 추가하고, 다음과 같이 설정한다.
    • NAME : 저항
    • INPUT : V2, 0~3300
    • LABEL : (단위를 표시하기 위해) mV 를 적는다.
    • READING RATE : 1sec (PUSH 위젯은 실행되지 않음)

  1. 상단의 뒤로가기 화살표를 눌러 프로젝트 바탕화면으로 돌아간다.

  1. 여기서 플레이버튼을 누르면,

저항과 전압의 측정이 시작된다. (시리얼 모니터에서도 관찰 가능)



  1. 가변 저항을 돌릴 때마다 저항과 전압이 바뀐다.

esp32 BLE, Blynk Slider (PWM)

ESP32와 Blynk에서의 PWM 사용

Blynk app에서 Slider 위젯을 사용하여 App → ESP32로 PWM 신호를 보내 LED의 밝기를 조절해본다.

  • Blynk에서 Slider는 Virtual pin을 사용해야한다. (Digital핀을 선택하는 것이 불가능)
  • Virtual pin을 사용하기 위해 BLYNK_WRITE(Vpin)을 사용한다.
  • ESP32에서 PWM을 사용하기 위한 절차에 따라 코딩을 진행한다.

schematic

※ LED모듈을 사용하는 경우 저항이 필요없다.


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
#define BLYNK_PRINT Serial

#define BLYNK_USE_DIRECT_CONNECT

#include <BlynkSimpleEsp32_BLE.h>
#include <BLEDevice.h>
#include <BLEServer.h>

// You should get Auth Token in the Blynk App.
// Go to the Project Settings (nut icon).
char auth[] = "rc77E7UrbQHhoLE-LV1Ajxz20k8eLBrk";

// setting PWM properties
#define LEDC_FREQ 5000 // Hz
#define LEDC_CHANNEL_0 0
#define LEDC_RESOLUTION 12 // 10bit (0~1023) 12bit(0~4095) 16bit(0~65535)

// led Pin
const int pin_ledR = 19;

BLYNK_WRITE(V5) // widget virtual pin5 -> ESP32
{
int value_V5 = param.asInt();
ledcWrite(LEDC_CHANNEL_0, value_V5);
Serial.print("V5 value: "); Serial.println(value_V5);
}

void setup()
{
// Debug console
Serial.begin(115200);
Serial.println("Waiting for connections...");

// configure LED PWM functionalitites
ledcSetup(LEDC_CHANNEL_0, LEDC_FREQ, LEDC_RESOLUTION);

// attach the same channel to the GPIO to be controlled
ledcAttachPin(pin_ledR, LEDC_CHANNEL_0);

// for Blynk
Blynk.setDeviceName("Blynk-t");
Blynk.begin(auth);
}

void loop()
{
Blynk.run();
}

sketch 분석

Button 위젯 사용을 위해 사용하였던 예제 sketch에 다음의 라인을 추가하여야 한다.


  1. LED가 연결되어 있는 ESP32 핀
1
const int pin_ledR = 19;

  1. Blynk App에서 ESP32로 신호를 보내는 Virtual 핀
1
2
3
4
5
BLYNK_WRITE(V5) {                          // widget virtual pin(V5) -> ESP32
int value_V5 = param.asInt(); // V5로 지정된 위젯의 값을 읽고
ledcWrite(LEDC_CHANNEL_0, value_V5); // PWM 채널0으로 보냄
Serial.print("V5 value: "); Serial.println(value_V5); // 시리얼모니터에 출력
}

  1. LED에 PWM 신호 출력 (밝기 조절)
1
2
3
4
5
// configure LED PWM functionalitites
ledcSetup(LEDC_CHANNEL_0, LEDC_FREQ, LEDC_RESOLUTION);

// attach the same channel to the GPIO to be controlled
ledcAttachPin(pin_ledR, LEDC_CHANNEL_0);

Blynk App 설정

  1. Button 위젯을 삭제한다. (그냥 두어도 관계없음)



  1. 프로젝트 바탕화면에서 ⊕버튼을 누른 후, Slider 위젯을 추가한다.

  1. 프로젝트 바탕화면의 Slider 위젯을 누른다.

  1. Slider 설정화면이 나오면,

PIN을 누르고 사용할 Virual 핀을 선택한 뒤 OK버튼을 누른다. (여기서는 V5선택)



  1. 다음 3가지를 추가로 설정한다.
    • OUTPUT 범위 : 0 ~ 4095 (12bit 사용시)
    • SEND ON RELEASE : OFF (OFF 선택시 Slide를 조절하는 즉시 실시간으로 출력값 반영되며, ON 선택시 Slide에서 손가락을 떼면 출력값이 반영된다.)
    • WRITE INTERVAL : SEND ON RELEASE를 OFF로 선택시 나타나며, 100ms가 가장 작은 값이다.


설정을 모두 마치면 상단의 뒤로가기 화살표를 눌러 프로젝트 바탕화면으로 돌아간다.


  1. Slider 위젯을 1초이상 누르고 있으면 위젯의 위치와 크기를 조절할 수 있다. 위젯의 위치를 아래로 내리고 가로폭의 크기를 늘려본다.


  1. 이제 플레이(▷) 버튼을 누르고 Slide를 조절해본다.

Slide값에 따라 LED의 밝기가 조절된다.



  1. Slider를 조절하는 동안 Arduino IDE의 시리얼 모니터값을 확인해볼 수 있다.

esp32 BLE, Blynk Button

Blynk를 처음 사용하는 경우

Blynk 라이브러리 설치하기

  1. docs.blynk.cc 접속 – Downloads – Blynk Library
  2. https://github.com/blynkkk/blynk-library/releases/tag/v0.6.1
    • Blynk_Release_v0.6.1.zip 다운로드
  3. 압축을 풀면 libraries, tools 폴더가 보임
  4. 이 두 폴더를 아두이노IDE의 기본 스케치북 위치로 복사하여 붙여넣기 함
    • 기존의 libraries 폴더와 합쳐짐
    • 기본 스케치북 위치 확인 방법
      • 아두이노IDE – 파일 – 환경설정 – 설정탭 – 스케치북 위치

스마트폰에 Blynk App 설치하기

  • 아이폰 : 애플 앱스토어
  • 안드로이드 : 구글 플레이스토어


버튼 위젯을 이용한 LED On/Off 하기

schematic

※ RGB LED 모듈을 사용하는 경우에는 저항을 연결하지 않는다.


Blynk 설정

  1. Blynk App 실행

  1. Create New Account : 주로 사용하는 e-mail을 사용하여 계정 생성 (계정이 이미 생성되어 있는 경우에는 Log In)


  1. New Project 생성 : ESP32_LED

  • 프로젝트명 : ESP32_LED
  • CHOOSE DEVICE : ESP32 Dev Board
  • CONNECTION TYPE : BLE


  1. Create를 누르면 Blynk 가입시 계정으로 사용한 이메일로 Auth Token 발송된다.

  1. OK버튼을 누르면 프로젝트가 생성된다.

  1. 컴퓨터를 사용하여 이메일 계정에 로그인 한 후, Auth Token을 복사한다. (복잡한 형태의 토큰을 코딩시 사용할 것이므로, 스마트폰인 아닌 컴퓨터에서 확인하여 복사하는 것이 좋다.)


  1. Blynk 위젯 설정 : 생성된 프로젝트(ESP32_LED) 바탕화면으로 진입하면 Blynk App 상단의 초록색 메뉴바의 이름이 “ESP32_LED”로 바뀐다.

만약, 아래와 같이 초록색 메뉴바의 이름이 “Blynk”로 되어있다면, 프로젝트 바탕화면으로 진입하지 못한 것이다. 이때에는 검은색 바탕화면의 “ESP32_LED”프로젝트명을 확인한 후 터치하면 해당프로젝트로 진입할 수 있다. 기존에 다른 프로젝트를 만들어 둔 적이 있다면 검은색 바탕화면의 프로젝트명이 “ESP32_LED”가 아닐 수도 있다. 이런 경우에는 스마트폰 화면을 좌우로 스크롤하여 프로젝트명을 변경한 후 진입한다.


  1. 프로젝트 바탕화면에서 ⊕버튼을 누른 후, Button 위젯과 BLE 위젯을 눌러 추가한다.



  1. 프로젝트 바탕화면에 두개의 위젯이 표시된다.

  1. BUTTON 위젯을 누르면 Buttom Settings 화면이 나타난다. 여기서 PIN부분을 누른다.

  1. LED를 연결한 PIN을 선택한다. 여기서는 Digital GP19를 선택하였다.

  1. OK버튼을 누르면 OUTPUT 버튼이 GP19로 설정된다. Mode는 SWITCH로 설정한다.

  1. Blynk App 상단의 뒤로가기 버튼을 누른뒤 프로젝트 바탕화면으로 간다.

Blynk 연결하기

  1. Arduino IDE에서 예제를 로드한다. 파일> 예제> Blynk> Boards_Bluetooth> ESP32_BLE


  1. 예제의 char auth[] = “YourAuthToken”; 부분을 찾아서 쌍따옴표 사이의 YourAuthToken을 지운 뒤, 이메일에서 복사한 AuthToken 붙여넣은 뒤, 다른 이름으로 저장한다.



  1. 예제의 Blynk.setDeviceName(“Blynk”); 부분을 찾아 따옴표 안의 BLE Device 이름을 적당한 이름으로 바꿔준다. (※이름을 바꿔주지 않으면 주변의 다른 기기와 중복되어 오작동할 소지가 있으므로 반드시 다른 이름으로 바꿔주어야 한다.)


Device Name을 “Blynk-t”로 수정하였다.


  1. ESP32에 스케치를 업로드한다. (업로드 시간이 Arduino에 비해 오래 걸림)
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
#define BLYNK_PRINT Serial
#define BLYNK_USE_DIRECT_CONNECT

#include <BlynkSimpleEsp32_BLE.h>
#include <BLEDevice.h>
#include <BLEServer.h>

// You should get Auth Token in the Blynk App.
// Go to the Project Settings (nut icon).
char auth[] = "rc77E7UrbQHhoLE-LV1Ajxz........."; // Edit Your Auth Token!!

void setup()
{
// Debug console
Serial.begin(115200);
Serial.println("Waiting for connections...");

Blynk.setDeviceName("Blynk-t"); // Edit Your ESP32 Name!!!
Blynk.begin(auth);
}

void loop()
{
Blynk.run();
}


  1. 이제 스마트폰의 Bluetooth기능을 키고, 위의 코드에서 수정한 Device Name을 찾아 연결한다.


  1. Blynk App에서 BLE버튼(블루투스 아이콘 모양)을 누른다.

  1. Connect BLE device 버튼을 누른다.

  1. 연결할 Device(여기서는 Blynk-t)를 선택하고 OK를 누른다.

  1. Connected 메세지가 나오면 연결 설정이 완료된 것이다.

  1. Blynk App 상단의 뒤로 돌아가기 버튼을 눌러 프로젝트 바탕화면으로 돌아간다.

  1. App 오른쪽 상단의 ▷(플레이버튼)버튼을 누른다.

플레이버튼을 눌렀을 때, App상단에 빨간색 숫자1이 보이면 아직 연결이 되지 않았다는 것을 의미한다. 잠시 기다리면 빨간색 숫자1이 사라지고, 이 상태가 작동 준비가 완료된다.


  1. 현재 Button은 OFF 되어 있으며, 이에 따라 GPIO 19에 연결되어 있는 LED도 OFF되어 있다.

  1. Button을 누르면 ON 상태로 바뀌면서, LED가 ON 상태로 점등된다.

  1. 다시 Button을 누르면 OFF 상태로 바뀌면서, LED가 OFF 상태로 소등된다.

  1. 정지버튼을 누르면 플레이상태가 정지상태로 바뀌면서 프로젝트 바탕화면으로 돌아간다.

Arduino, Bluetooth, Blynk

Blynk 설정

Blynk 라이브러리 설치

  1. docs.blynk.cc 접속 – Downloads – Blynk Library
  2. https://github.com/blynkkk/blynk-library/releases/tag/v0.6.1
    • Blynk_Release_v0.6.1.zip 다운로드
  3. 압축을 풀면 libraries, tools 폴더가 보임
  4. 이 두 폴더를 아두이노IDE의 기본 스케치북 위치로 복사하여 붙여넣기 함
    • 기존의 libraries 폴더와 합쳐짐
    • 기본 스케치북 위치 확인 방법
      • 아두이노IDE – 파일 – 환경설정 – 설정탭 – 스케치북 위치

스마트폰에 Blynk App 다운로드 하여 설치하기

  • 아이폰 : 애플 앱스토어
  • 안드로이드 : 구글 플레이스토어


버튼 위젯을 이용한 LED On/Off 하기

Blynk 계정생성 및 Auth Token 받기

  1. Blynk App 실행
  2. Create New Account : 주로 사용하는 e-mail을 사용하여 계정 생성
  3. Blynk에서 New Project 생성
    • 아이폰용 MLT-BT05의 경우
      • Project Name 설정: BLE Test
      • CHOOSE DEVICE: Arduino UNO
      • CONNECTION TYPE: BLE
      • 설정 후 Create 버튼
    • 안드로이드용 HC-06의 경우
      • Project Name 설정: HC-06 Test
      • CHOOSE DEVICE: Arduino UNO
      • CONNECTION TYPE: Bluetooth
      • 설정 후 Create 버튼
  4. Auth Token 확인
    • 가입시 지정한 이메일로 로그인하여 토큰 확인
    • (매우 복잡한 형태의) 토큰을 복사하여 코딩시 사용할 것이므로, 컴퓨터로 확인!
    • 스마트폰으로 확인하는 것은 무의미함

Blynk 위젯 설정

  1. 아이폰용 MLT-BT05를 사용할 경우
    • 생성된 프로젝트(BLE Test) 바탕화면으로 진입하면 Blynk App 상단의 초록색 메뉴바의 이름이 BLE Test로 바뀜
    • 여기서 ⊕버튼 터치한 후 Button위젯과 BLE위젯 추가
    • 추가된 BUTTON 위젯 선택 후 설정
      • OUTPUT: Select pin: Digital D13 / MODE: SWITCH
  2. 안드로이드용 HC-06를 사용할 경우
    • 생성된 프로젝트(HC-06 Test) 바탕화면으로 진입하면 Blynk App 상단의 초록색 메뉴바의 이름이 HC-06 Test로 바뀜
    • 여기서 ⊕버튼 터치한 후 Button위젯과 Bluetooth위젯 추가
    • 추가된 BUTTON 위젯 선택 후 설정
      • OUTPUT: Select pin: Digital D13 / MODE: SWITCH

Blynk 연결하기

  1. 아두이노 스케치 예제 불러오기
    • 파일 – 예제 – Blynk – Boards_USB_Serial – Arduino_SoftwareSerial 선택
    • 예제의 char auth[] = “YourAuthToken”; 부분을 찾아서 쌍따옴표 사이의 YourAuthToken을 지운 뒤, 이메일에서 복사한 AuthToken 붙여넣기
  2. 아두이노 UNO에 스케치 업로드
  3. 스마트폰의 블루투스 기능을 키고,
    • 아이폰용 MLT-BT05의 경우
      • Bluetooth 모듈 설정에서 설정한 모듈ID 디바이스를 연결(예를 들어 MLT-BT05-T) (디폴트 연결PIN 123456)
      • Blynk App에서 BLE버튼(블루투스 아이콘 모양) 터치 후 Connect BLE device 버튼을 누른 뒤,
      • 연결할 디바이스 MLT-BT05-T를 찾은 후 OK버튼 누름
      • MLT-BT05-T connected 메시지가 나오면 연결 완료된 것임
    • 안드로이드용 HC-06의 경우
      • Bluetooth 모듈 설정에서 설정한 모듈ID 디바이스를 연결(예를 들어 HC-06-T 디바이스를 연결 (디폴트 연결PIN 1234)
      • Blynk App에서 Bluetooth버튼(블루투스 아이콘 모양) 터치 후 Connect Bluetooth device 버튼을 누른 뒤,
      • 연결할 디바이스 HC-06-T를 찾은 후 OK버튼 누름
      • HC-06-T connected 메시지가 나오면 연결 완료된 것임
  4. Blynk App 상단의 뒤로 돌아가기 버튼을 누른 뒤, App 오른쪽 상단의 ▷(플레이버튼)버튼을 누름
    • App상단에 빨간색 숫자1이 보이면 아직 연결이 되지 않았다는 것을 의미함
    • 잠시 기다리면 빨간색 숫자1이 사라지고, 이 상태가 작동 준비가 완료된 것을 의미함
  5. BUTTON 아이콘을 눌러 ON/OFF → 아두이노 Built-in LED ON/OFF
  6. Blynk App 오른쪽 상단의 □(정지버튼)을 누른 뒤, ⊕버튼 터치한 후 Text Input 위젯 추가


슬라이더 위젯을 이용한 LED 밝기 제어

Built-in LED가 사용하는 13번핀은 PWM이 지원되지 않으므로, 11번핀을 사용한다.


sketch

아두이노 스케치는 그대로 사용 (위의 “Blynk 연결하기” 부분 스케치 참고)


Blynk 앱 설정

  1. 버튼 위젯을 삭제: 버튼 위젯을 누르고 맨아래쪽으로 내려가서 Delete
  2. Slider 위젯 추가: OUTPUT D11
  3. Slider 위젯의 위치/크기 조절: Slider 위젯 아이콘을 1초이상 누르면 위치/크기를 조절할 수 있음 (가로 크기를 최대로 늘림)
  4. App 오른쪽 상단의 ▷(플레이버튼)버튼을 누름
  5. 슬라이드를 조절하여 LED의 밝기 조절 확인


과제

3색 RGB LED를 zeRGBa 위젯으로 조정하여 여러 가지 색깔 만들어보기 (스케치는 위의 스케치를 그대로 사용)

Blynk에 센서값 출력하기

원문: http://help.blynk.cc/en/articles/512056-how-to-display-any-sensor-data-in-blynk-app


센서값을 Blynk로 출력하기 위해서는 Blynk가 연결되지 않은 상태에서 시리얼모니터에 센서값들을 출력할 수 있어야 한다. ​센서값을 출력하는 코드가 완성되면, 그 다음으로 Blynk에 센서값을 보내는 작업을 시작하는 것이 좋다.

Blynk앱에 센서값을 표시하는 방법에는 크게 두가지 방법이 있으며, 진행중인 프로젝트에 따라 적절한 방법을 선택하면 된다.


PULL

  • 이 경우에 블링크는 앱이 열려있는 동안에만 Blynk에서 값을 받는다.
  • 앱에서 센서값을 표시하는 가장 간단한 방법이며, Blynk 앱의 Virtual Pins이 구동중일때 센서값을 가져온다. (Blynk를 닫거나 백그라운드에서 실행될때 데이타는 요청되지 않는다.) 타이머를 사용할 필요가 없으므로 코딩이 단순한 편이지만, 데이터가 서버에 저장되지 않으므로 히스토리 그래프를 볼 수 없다는 단점이 있다.

Blynk 앱 설정

  1. 새 프로젝트를 만든다. (새로운 Auth Token값이 당신의 이메일에 전송된다)
  2. Value Display 위젯을 추가한다.
  3. 위젯 세팅으로 가서 PIN 항목을 Virtual Pin V5로 설정한다.
  4. Reading Rate를 3초로 설정한다.

sketch

  1. Analog Pin에서 값을 (간단하게) 읽어들이는 경우에는 코드를 쓸 필요가 없다. 단지 예제파일을 오픈하여 하드웨어와 커넥션만 변경한다.
  2. e-mail을 체크하여 Auth Token을 확인하고, 스케치에 Auth Token을 수정입력한다.
  3. 하드웨어에 코드를 업로드한다.
  4. Blynk에서 플레이버튼을 누른다.
  5. 값이 표시되는 것을 볼 수 있다.
1
2
3
4
BLYNK_READ(V5) { //Blynk app has something on V5
sensorData = analogRead(A0); //reading the sensor on A0
Blynk.virtualWrite(V5, sensorData); //sending to Blynk
}

이 코드는 매시간마다 Blynk가 _READ 요청을 Virtual Pin V5에 보내도록 한다. A0핀에서 센서값을 읽은 후, Blynk에서 Virtual Pin V5에 되돌려 준다.



PUSH

  • 이 경우 하드웨어(아두이노 or ESP32 등)는 Blynk가 오프라인 상태일때도 센서값을 Blynk 서버에 저장하고, 앱을 열어 온라인 상태가 되면 앱으로 전송하여 센서값을 보여준다.

  • Virtual Pin을 통하여 일정 주기로 하드웨어로부터 센서값을 전송하고, 이때마다 Blynk가 값을 받아들인다.


주의할 점

void loop() 에서는 센서 값을 보낼 수 없다!!!

만약 void loop()문 내에 어떤 값을 넣게 되면 MCU가 아주 짧은 시간 간격으로 무한 반복하여 실행하게 되는데, Blynk는 인터넷을 통하여 값을 전송할 것이므로 Blynk 클라우드가 하드웨어로부터 오는 계속적인 데이터를 받게 될 것이다. Blynk 클라우드가 이러한 상황을 감지하면 자동적으로 접속을 끊게 된다.

그러므로 데이타를 일정 간격으로 보내야하며, 이를 위해 타이머를 사용하는 방법을 취한다. 일정간격으로 데이타를 보내는 방법에는 여러가지가 있지만, 가장 간단한 방법으로 BlynkTimer의 사용을 권장한다. BlynkTimer는 Blynk 라이브러리 패키지안에 포함되어 있으므로, Blynk 라이브러리만 제대로 설치되었다면 별도의 라이브러리를 설치하지 않아도 된다.


Blynk 앱 설정

  1. 새 프로젝트를 만든다. (새로운 Auth Token값이 당신의 이메일에 전송된다)
  2. Value Display 위젯을 추가한다.
  3. 위젯 세팅으로 가서 PIN 항목을 Virtual Pin V5로 설정한다.
  4. Frequency를 PUSH로 설정한다.

Example

  1. timer라고 하는 BlynkTimer 오브젝트를 만든다.
    1
    BlynkTimer timer; // Announcing the timer

  1. 그 다음으로 일정 간격으로 실행하게 될 함수를 만든다. 이 함수는 A0에 연결된 센서로부터 값을 Virtual Pin V5를 통하여 전송한다.
    1
    2
    3
    4
    void myTimerEvent() {
    sensorData = analogRead(A0);
    Blynk.virtualWrite(V5, sensorData);
    }

  1. setup()문에서, 서브함수 myTimerEvent()가 매 1000ms, 즉 1초 간격으로 실행되도록 선언할 것이다.
    1
    2
    3
    4
    5
    6
    void setup() {
    ...
    ...
    ...
    timer.setInterval(1000L, myTimerEvent);
    }

  1. loop문은 다음과 같이 설정한다.
    1
    2
    3
    4
    void loop() {
    Blynk.run();
    timer.run(); // running timer every second
    }

Bluetooth Serial Controller 앱

Bluetooth 시리얼 통신

Bluetooth Serial Controller 앱을 통하여 아두이노와 스마트폰 간의 간단한 시리얼 통신을 해보겠습니다. 이를 위해서는 먼저 Bluetooth 페어링 문서를 참고하여 블루투스 모듈과 스마트폰을 페어링 해두어야 합니다. 페어링이 완료된 후 시리얼 통신을 진행합니다.


schematic


sketch

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <SoftwareSerial.h>
SoftwareSerial BTSerial(2, 3); //Connect HC-06 TX,RX

void setup()
{
Serial.begin(9600);
Serial.println("Hello!");

// set the data rate for the BT port
BTSerial.begin(9600);
}

void loop()
{
if (BTSerial.available())
Serial.write(BTSerial.read());
if (Serial.available())
BTSerial.write(Serial.read());
}


스마트폰에 Bluetooth Serial Controller 앱 설치하기

  1. 이제 Google Playstore를 통해서 스마트폰에 Bluetooth Serial Controller 앱을 설치합니다. (iOS는 유사한 프로그램으로 설치)

​ 비슷한 역할을 하는 수많은 앱들이 있는데, 여러 앱을 설치하고 사용해본 결과, 이 앱이 사용하기에 유연함을 갖고 있는 것 같더군요.


​ 설치되었습니다.


  1. 설치 후 처음 실행을 하면, LANDSCAPE 모드(가로모드)로 실행이 되는데요. 그대로 사용해도 무방하지만 PORTRAIT 모드(세로 모드)로 바꿔보겠습니다. 먼저 PREFERENCE를 누르세요.

  1. 그러면 아래 화면이 뜹니다. 아무곳이나 누르고 위로 스크롤을 하면 아래에 숨겨져 있는 메뉴가 나타나게 되는데요.

  1. OPTION - Orientation을 선택한 뒤,

  1. PORTRAIT를 누릅니다.

  1. 이제 세로모드로 나타나요. 이제 HC-06모듈과 스마트폰과의 Serial 통신을 해보도록 하겠습니다. 다시 PREFERENCE를 누르고, TERMINAL MODE를 활성화 시키세요.

  1. 아래 화면이 TERMINAL Mode 화면입니다. 여기서 돋보기 모양을 누르고,

  1. Paired Devices 목록에서 SKS100을 선택하세요. 그러면 TERMINAL이 HC-06에 접속하여, 서로간 통신할 수 있는 상태가 됩니다.

​ 만일, 아래와 같이 Paired Devices 목록이 없으면, Scan for devices를 눌러 HC-06을 찾아보세요.


  1. TERMINAL이 SKS100에 접속을 시도합니다.

  1. 접속 완료!


터미널과 Serial 통신하기

  1. 이제 터미널 창에 Test를 입력하고 SEND를 눌러보세요.

  1. 그러면 화면에 Test가 출력됨과 동시에,

​ 시리얼 모니터에 Test가 출력됩니다. 즉, TERMINAL에서 입력한 Test라는 글자가 HC-06을 통해, 아두이노와 연결되어 있는 컴퓨터의 시리얼 모니터에 출력된 것이에요.


  1. 이번에는 반대로 시리얼 모니터에 Good morning! 을 입력하고 전송을 누르면

​ 스마트폰의 TERMINAL에 Good morning! 이 출력됩니다.