В предыдущем эксперименте мы зарегистрировались в интернет-сервисе weatherbit.io, подключились к нему и получили данные о текущей погоде в Москве. Если заменить название города, то можно получить данные о погоде в другом городе.
Но эти данные приходят в виде удобном для программы, но не для человека. Человеку хотелось бы видеть красивую иконку погоды на дисплее, которая бы символизировала ситуацию на улице, например солнышко или облачко. Температуру нужно вывести крупным шрифтом, а влажность и скорость ветра — мелким. Многие другие данные вообще могут быть не интересны человеку. Иными словами нам нужно сделать графический пользовательский интерфейс (англ. graphical user interface, GUI)
Для отображения иконок погоды и пользовательского интерфейса их нужно загрузить во внутреннюю память микроконтроллера. Для этого нужно создать папку weather
в папке data
в папке со скетчем, в нее загрузим все файлы из архива.
Отображать данные мы будем на TFT дисплее, поэтому подключим его согласно схеме.
#include <SPI.h> #include <Adafruit_ST7735.h> #include "LittleFS_ImageReader.h" #include <ArduinoJson.h> #define PIN_CS 2 #define PIN_DC 4 #define PIN_RST 5 Adafruit_ST7735 tft = Adafruit_ST7735(PIN_CS, PIN_DC, PIN_RST); LittleFS_ImageReader reader; void setup() { LittleFS.begin(); tft.initR(INITR_BLACKTAB); tft.setRotation(2); String json_string = R"({"count": 1, "data": [{"wind_cdir_full": "southeast", "weather": {"code": "804", "icon": "r01d", "description": "Light rain"}, "datetime": "2020-06-15:11", "temp": 24.4, "station": "E8051", "elev_angle": 53.53, "app_temp": 23.8, "state_code": "48", "wind_dir": 137, "last_ob_time": "2020-06-15T10:46:00", "solar_rad": 163.2, "sunrise": "00:43", "slp": 1007, "pod": "d", "vis": 1.5, "pres": 992.8, "wind_cdir": "SE", "wind_spd": 1.34, "ghi": 815.91, "ts": 1592217960, "snow": 0, "uv": 8.64467, "clouds": 100, "city_name": "Moscow", "precip": 2.84211, "timezone": "Europe/Moscow", "country_code": "RU", "dni": 882.12, "dhi": 113.84, "sunset": "18:15", "ob_time": "2020-06-15 10:46", "h_angle": 20, "dewpt": 6.2, "aqi": 48, "lat": 55.7522, "rh": 31, "lon": 37.6156}]})"; JsonDocument json_doc; deserializeJson(json_doc, json_string); String w_city = json_doc["data"][0]["city_name"]; int w_temp = json_doc["data"][0]["temp"]; int w_temp_feels = json_doc["data"][0]["app_temp"]; int w_humidity = json_doc["data"][0]["rh"]; float w_wind = json_doc["data"][0]["wind_spd"]; String w_pod = json_doc["data"][0]["pod"]; int w_code = int(json_doc["data"][0]["weather"]["code"]); String w_pic; if (w_code <= 233) w_pic = "11"; else if ((w_code >= 300 && w_code <= 520) || w_code == 522) w_pic = "09"; else if (w_code >= 521 && w_code <= 600) w_pic = "10"; else if (w_code >= 601 && w_code <= 622) w_pic = "13"; else if (w_code >= 623 && w_code <= 751) w_pic = "50"; else if (w_code == 800) w_pic = "01"; else if (w_code >= 801 && w_code <=802) w_pic = "02"; else if (w_code == 803) w_pic = "03"; else if (w_code == 804) w_pic = "04"; String w_pic_temp; if (w_temp > 0) w_pic_temp = "tp"; else w_pic_temp = "tn"; unsigned int font_color = tft.color565(33, 149, 82); unsigned int font_color2 = tft.color565(8, 85, 41); tft.fillScreen(ST77XX_WHITE); tft.setTextColor(font_color); tft.setTextSize(2); tft.setCursor(5, 1); tft.print(w_city); reader.drawBMP("/weather/" + w_pic + w_pod + ".bmp", tft, 34, 16); reader.drawBMP("/weather/" + w_pic_temp + ".bmp", tft, 5, 70); tft.setTextSize(4); tft.setCursor(30, 75); tft.print(w_temp); tft.setTextColor(font_color2); tft.setTextSize(1); tft.setCursor(5, 120); tft.print("Feels: " + String(w_temp_feels)); tft.setCursor(5, 130); tft.print("Humidity: " + String(w_humidity) + "%"); tft.setCursor(5, 140); tft.print("Wind: " + String(w_wind) + "m/s"); } void loop() { }
В этой программе мы сосредоточились на создании GUI — графического интерфейса пользователя, поэтому пока убрали из программы обращение к интернет-сервису. Вместо этого мы задали в коде типовой ответ от сервера. Эту «заглушку» и будем использовать как временный источник данных.
Первым делом извлекаем из структуры нужные нам данные. А именно: название города, текущую температуру, влажность, скорость ветра, время суток, код погоды. Разберем по порядку:
Название города записываем в переменную w_city
String w_city = json_doc["data"][0]["city_name"];
Текущую температуру записываем в переменную w_temp
int w_temp = json_doc["data"][0]["temp"];
Ощущение температуры записываем в переменную w_temp_feels. (например из-за ветра или влажности температура может ощущаться человеком как более низкая, чем есть на самом деле)
int w_temp_feels = json_doc["data"][0]["app_temp"];
Влажность в % записываем в переменную w_humidity
int w_humidity = json_doc["data"][0]["rh"];
Скорость ветра в метрах в секунду записываем в переменную w_wind
float w_wind = json_doc["data"][0]["wind_spd"];
Текущее время суток записываем в переменную w_pod. Если сейчас день, то в ней будет значение d, а если ночь — n. Эти данные нам понадобятся для выбора правильной иконки погоды. Например, днем мы будем отображать солнышко, а ночью луну.
String w_pod = json_doc["data"][0]["pod"];
Сервис weatherbit.io среди прочих данных о погоде сообщает код текущей погоды. Это коды, которые используются именно этим сервисом. По ним можно понять какую иконку погоды нужно отобразить. Подробнее о кодах и иконках.
int w_code = int(json_doc["data"][0]["weather"]["code"]);
Сервис поддерживает очень большое количество иконок у нас столько нет. У нас всего 18 погодных иконок — 9 дневных и 9 ночных. Поэтому нам нужно настроить соответствие кодов из сервиса нашим иконкам. Например несколько видов дождя, которые обозначаются разными кодами, мы будем отображать одной и той же иконкой. Этот код преобразует коды в 9 иконок:
String w_pic; if (w_code <= 233) w_pic = "11"; else if ((w_code >= 300 && w_code <= 520) || w_code == 522) w_pic = "09"; else if (w_code >= 521 && w_code <= 600) w_pic = "10"; else if (w_code >= 601 && w_code <= 622) w_pic = "13"; else if (w_code >= 623 && w_code <= 751) w_pic = "50"; else if (w_code == 800) w_pic = "01"; else if (w_code >= 801 && w_code <=802) w_pic = "02"; else if (w_code == 803) w_pic = "03"; else if (w_code == 804) w_pic = "04";
Для отображения иконки термометра на дисплее нужно определить положительная сейчас температура или отрицательная. Если температура ниже нуля, то будем отображать «холодный» градусник с синим содержимым, иначе будем отображать «теплый» градусник. Этот код определяет какую иконку градусника нужно будет отобразить:
String w_pic_temp; if (w_temp > 0) w_pic_temp = "tp"; else w_pic_temp = "tn";
Определяем цвета для шрифтов. Один из них будет использоваться для отображения названия города и температуры, второй для остальной информации.
unsigned int font_color = tft.color565(33, 149, 82); unsigned int font_color2 = tft.color565(8, 85, 41);
Закрашиваем весь дисплей белым
tft.fillScreen(ST77XX_WHITE);
Выводим на дисплей название города
tft.setTextColor(font_color); tft.setTextSize(2); tft.setCursor(5, 1); tft.print(w_city);
Рисуем иконки: иконку погоды и градусник
reader.drawBMP("/weather/" + w_pic + w_pod + ".bmp", tft, 34, 16); reader.drawBMP("/weather/" + w_pic_temp + ".bmp", tft, 5, 70);
Выводим текущую температуру
tft.setTextSize(4); tft.setCursor(30, 75); tft.print(w_temp);
Выводим дополнительные данные: температура по ощущениям, влажность и скорость ветра
tft.setTextColor(font_color2); tft.setTextSize(1); tft.setCursor(5, 120); tft.print("Feels: " + String(w_temp_feels)); tft.setCursor(5, 130); tft.print("Humidity: " + String(w_humidity) + "%"); tft.setCursor(5, 140); tft.print("Wind: " + String(w_wind) + "m/s");