Следующая версия | Предыдущая версия |
products:laboratory_iot_c:exp70 [2024/12/17 18:06] – создано labuser30 | products:laboratory_iot_c:exp70 [2024/12/17 18:21] (текущий) – [Программный код эксперимента] labuser30 |
---|
Но эти данные приходят в виде удобном для программы, но не для человека. Человеку хотелось бы видеть красивую иконку погоды на дисплее, которая бы символизировала ситуацию на улице, например солнышко или облачко. Температуру нужно вывести крупным шрифтом, а влажность и скорость ветра — мелким. Многие другие данные вообще могут быть не интересны человеку. Иными словами нам нужно сделать графический пользовательский интерфейс (англ. graphical user interface, GUI) | Но эти данные приходят в виде удобном для программы, но не для человека. Человеку хотелось бы видеть красивую иконку погоды на дисплее, которая бы символизировала ситуацию на улице, например солнышко или облачко. Температуру нужно вывести крупным шрифтом, а влажность и скорость ветра — мелким. Многие другие данные вообще могут быть не интересны человеку. Иными словами нам нужно сделать графический пользовательский интерфейс (англ. graphical user interface, GUI) |
| |
Для отображения иконок погоды и пользовательского интерфейса их нужно загрузить во внутреннюю память микроконтроллера. Для этого нужно создать папку ''weather'' в корне файловой системы, в нее загрузим все файлы из архива. | Для отображения иконок погоды и пользовательского интерфейса их нужно загрузить во внутреннюю память микроконтроллера. Для этого нужно создать папку ''weather'' в папке ''data'' в папке со скетчем, в нее загрузим все файлы из архива. |
| |
<WRAP center round download 60%> | <WRAP center round download 60%> |
==== Программный код эксперимента ==== | ==== Программный код эксперимента ==== |
| |
<file python Exp70.py[enable_line_numbers="2", start_line_numbers_at="1"]> | <file arduino Exp70.ino[enable_line_numbers="2", start_line_numbers_at="1"]> |
from machine import Pin, SPI | #include <SPI.h> |
from tft import TFT_GREEN | #include <Adafruit_ST7735.h> |
import font | #include "LittleFS_ImageReader.h" |
import network | #include <ArduinoJson.h> |
import gc | |
import time | #define PIN_CS 2 |
import socket | #define PIN_DC 4 |
| #define PIN_RST 5 |
| |
| Adafruit_ST7735 tft = Adafruit_ST7735(PIN_CS, PIN_DC, PIN_RST); |
| LittleFS_ImageReader reader; |
| |
_init() | void setup() { |
gc.collect() | LittleFS.begin(); |
| tft.initR(INITR_BLACKTAB); |
| tft.setRotation(2); |
| |
dc = Pin(4, Pin.OUT) | 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}]})"; |
cs = Pin(2, Pin.OUT) | JsonDocument json_doc; |
rst = Pin(5, Pin.OUT) | 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"]); |
| |
spi = SPI(1, baudrate=40000000, polarity=0, phase=0) | String w_pic; |
tft = TFT_GREEN(128, 160, spi, dc, cs, rst, rotate=0) | if (w_code <= 233) w_pic = "11"; |
tft.initr(tft.BGR) # tft.initr(tft.RGB) #Если вместо синего цвета отображается красный, а вместо красного синий | 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); |
| |
data = {'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}]} | tft.fillScreen(ST77XX_WHITE); |
| |
wCity = data['data'][0]['city_name'] | tft.setTextColor(font_color); |
wTemp = round(data['data'][0]['temp']) | tft.setTextSize(2); |
wTempFeels = data['data'][0]['app_temp'] | tft.setCursor(5, 1); |
wHumidity = data['data'][0]['rh'] | tft.print(w_city); |
wWind = data['data'][0]['wind_spd'] | |
| |
pod = data['data'][0]['pod'] | reader.drawBMP("/weather/" + w_pic + w_pod + ".bmp", tft, 34, 16); |
| reader.drawBMP("/weather/" + w_pic_temp + ".bmp", tft, 5, 70); |
| |
wCode = int(data['data'][0]['weather']['code']) | tft.setTextSize(4); |
| tft.setCursor(30, 75); |
| tft.print(w_temp); |
| |
if wCode <= 233: | tft.setTextColor(font_color2); |
wPic = '11' | tft.setTextSize(1); |
elif (wCode >= 300 and wCode <= 520) or wCode == 522: | tft.setCursor(5, 120); |
wPic = '09' | tft.print("Feels: " + String(w_temp_feels)); |
elif wCode in (521, 600): | tft.setCursor(5, 130); |
wPic = '10' | tft.print("Humidity: " + String(w_humidity) + "%"); |
elif wCode >= 601 and wCode <= 622: | tft.setCursor(5, 140); |
wPic = '13' | tft.print("Wind: " + String(w_wind) + "m/s"); |
elif wCode >= 623 and wCode <= 751: | } |
wPic = '50' | |
elif wCode == 800: | |
wPic = '01' | |
elif wCode in (801, 802): | |
wPic = '02' | |
elif wCode == 803: | |
wPic = '03' | |
elif wCode == 804: | |
wPic = '04' | |
| |
if (wTemp > 0): | void loop() { |
wTempPic = 'tp' | } |
else: | |
wTempPic = 'tn' | |
| |
FontColor = tft.rgbcolor(33,149, 82) | |
FontColor2 = tft.rgbcolor(8,85, 41) | |
| |
| |
tft.clear(tft.rgbcolor(255,255, 255)) | |
| |
| |
tft.text(1,1,wCity, font.terminalfont, FontColor, 2) | |
| |
tft.draw_bmp(34,16,'/weather/' + wPic + pod + '.bmp') | |
tft.draw_bmp(5,70,'/weather/' + wTempPic + '.bmp') | |
| |
tft.text(30,75, str(wTemp), font.terminalfont, FontColor, 4) | |
| |
tft.text(5,120,"Feels: " + str(wTempFeels), font.terminalfont, FontColor2, 1) | |
tft.text(5,128,"Humidity: " + str(wHumidity) + "%", font.terminalfont, FontColor2, 1) | |
tft.text(5,136,"Wind: " + str(wWind) + "m/s", font.terminalfont, FontColor2, 1) | |
</file> | </file> |
| |
Первым делом извлекаем из структуры нужные нам данные. А именно: название города, текущую температуру, влажность, скорость ветра, время суток, код погоды. Разберем по порядку: | Первым делом извлекаем из структуры нужные нам данные. А именно: название города, текущую температуру, влажность, скорость ветра, время суток, код погоды. Разберем по порядку: |
| |
Название города записываем в переменную wCity | Название города записываем в переменную w_city |
<code python[enable_line_numbers="2", start_line_numbers_at="24"]> | <code arduino[enable_line_numbers="2", start_line_numbers_at="22"]> |
wCity = data['data'][0]['city_name'] | String w_city = json_doc["data"][0]["city_name"]; |
</code> | </code> |
| |
Текущую температуру записываем в переменную wTemp | Текущую температуру записываем в переменную w_temp |
<code python[enable_line_numbers="2", start_line_numbers_at="25"]> | <code arduino[enable_line_numbers="2", start_line_numbers_at="23"]> |
wTemp = round(data['data'][0]['temp']) | int w_temp = json_doc["data"][0]["temp"]; |
</code> | </code> |
| |
Ощущение температуры записываем в переменную wTempFeels. (например из-за ветра или влажности температура может ощущаться человеком как более низкая, чем есть на самом деле) | Ощущение температуры записываем в переменную w_temp_feels. (например из-за ветра или влажности температура может ощущаться человеком как более низкая, чем есть на самом деле) |
<code python[enable_line_numbers="2", start_line_numbers_at="26"]> | <code arduino[enable_line_numbers="2", start_line_numbers_at="24"]> |
wTempFeels = data['data'][0]['app_temp'] | int w_temp_feels = json_doc["data"][0]["app_temp"]; |
</code> | </code> |
| |
Влажность в % записываем в переменную wHumidity | Влажность в % записываем в переменную w_humidity |
<code python[enable_line_numbers="2", start_line_numbers_at="27"]> | <code arduino[enable_line_numbers="2", start_line_numbers_at="25"]> |
wHumidity = data['data'][0]['rh'] | int w_humidity = json_doc["data"][0]["rh"]; |
</code> | </code> |
| |
Скорость ветра в метрах в секунду записываем в переменную wWind | Скорость ветра в метрах в секунду записываем в переменную w_wind |
<code python[enable_line_numbers="2", start_line_numbers_at="28"]> | <code arduino[enable_line_numbers="2", start_line_numbers_at="26"]> |
wWind = data['data'][0]['wind_spd'] | float w_wind = json_doc["data"][0]["wind_spd"]; |
</code> | </code> |
| |
Текущее время суток записываем в переменную pod. Если сейчас день, то в ней будет значение d, а если ночь — n. Эти данные нам понадобятся для выбора правильной иконки погоды. Например, днем мы будем отображать солнышко, а ночью луну. | Текущее время суток записываем в переменную w_pod. Если сейчас день, то в ней будет значение d, а если ночь — n. Эти данные нам понадобятся для выбора правильной иконки погоды. Например, днем мы будем отображать солнышко, а ночью луну. |
<code python[enable_line_numbers="2", start_line_numbers_at="30"]> | <code arduino[enable_line_numbers="2", start_line_numbers_at="27"]> |
pod = data['data'][0]['pod'] | String w_pod = json_doc["data"][0]["pod"]; |
</code> | </code> |
| |
Сервис weatherbit.io среди прочих данных о погоде сообщает код текущей погоды. Это коды, которые используются именно этим сервисом. По ним можно понять какую иконку погоды нужно отобразить. [[https://www.weatherbit.io/api/codes|Подробнее о кодах и иконках.]] Код нам будет нужен в виде числа, поэтому сразу преобразуем его в целое число с помощью ''int()'' | Сервис weatherbit.io среди прочих данных о погоде сообщает код текущей погоды. Это коды, которые используются именно этим сервисом. По ним можно понять какую иконку погоды нужно отобразить. [[https://www.weatherbit.io/api/codes|Подробнее о кодах и иконках.]] |
<code python[enable_line_numbers="2", start_line_numbers_at="32"]> | <code arduino[enable_line_numbers="2", start_line_numbers_at="28"]> |
wCode = int(data['data'][0]['weather']['code']) | int w_code = int(json_doc["data"][0]["weather"]["code"]); |
</code> | </code> |
| |
Сервис поддерживает очень большое количество иконок у нас столько нет. У нас всего 18 погодных иконок— 9 дневных и 9 ночных. Поэтому нам нужно настроить соответствие кодов из сервиса нашим иконкам. Например несколько видов дождя, которые обозначаются разными кодами, мы будем отображать одной и той же иконкой. Этот код преобразует коды в 9 иконок: | Сервис поддерживает очень большое количество иконок у нас столько нет. У нас всего 18 погодных иконок — 9 дневных и 9 ночных. Поэтому нам нужно настроить соответствие кодов из сервиса нашим иконкам. Например несколько видов дождя, которые обозначаются разными кодами, мы будем отображать одной и той же иконкой. Этот код преобразует коды в 9 иконок: |
<code python[enable_line_numbers="2", start_line_numbers_at="34"]> | <code arduino[enable_line_numbers="2", start_line_numbers_at="30"]> |
if wCode <= 233: | String w_pic; |
wPic = '11' | if (w_code <= 233) w_pic = "11"; |
elif (wCode >= 300 and wCode <= 520) or wCode == 522: | else if ((w_code >= 300 && w_code <= 520) || w_code == 522) w_pic = "09"; |
wPic = '09' | else if (w_code >= 521 && w_code <= 600) w_pic = "10"; |
elif wCode in (521, 600): | else if (w_code >= 601 && w_code <= 622) w_pic = "13"; |
wPic = '10' | else if (w_code >= 623 && w_code <= 751) w_pic = "50"; |
elif wCode >= 601 and wCode <= 622: | else if (w_code == 800) w_pic = "01"; |
wPic = '13' | else if (w_code >= 801 && w_code <=802) w_pic = "02"; |
elif wCode >= 623 and wCode <= 751: | else if (w_code == 803) w_pic = "03"; |
wPic = '50' | else if (w_code == 804) w_pic = "04"; |
elif wCode == 800: | |
wPic = '01' | |
elif wCode in (801, 802): | |
wPic = '02' | |
elif wCode == 803: | |
wPic = '03' | |
elif wCode == 804: | |
wPic = '04' | |
</code> | </code> |
| |
Для отображения иконки термометра на дисплее нужно определить положительная сейчас температура или отрицательная. Если температура ниже нуля, то будем отображать "холодный" градусник с синим содержимым, иначе будем отображать "теплый" градусник. Этот код определяет какую иконку градусника нужно будет отобразить: | Для отображения иконки термометра на дисплее нужно определить положительная сейчас температура или отрицательная. Если температура ниже нуля, то будем отображать "холодный" градусник с синим содержимым, иначе будем отображать "теплый" градусник. Этот код определяет какую иконку градусника нужно будет отобразить: |
| |
<code python[enable_line_numbers="2", start_line_numbers_at="53"]> | <code arduino[enable_line_numbers="2", start_line_numbers_at="41"]> |
if (wTemp > 0): | String w_pic_temp; |
wTempPic = 'tp' | if (w_temp > 0) w_pic_temp = "tp"; |
else: | else w_pic_temp = "tn"; |
wTempPic = 'tn' | |
</code> | </code> |
| |
Определяем цвета для шрифтов. Один из них будет использоваться для отображения названия города и температуры, второй для остальной информации. | Определяем цвета для шрифтов. Один из них будет использоваться для отображения названия города и температуры, второй для остальной информации. |
<code python[enable_line_numbers="2", start_line_numbers_at="58"]> | <code arduino[enable_line_numbers="2", start_line_numbers_at="45"]> |
FontColor = tft.rgbcolor(33,149, 82) | unsigned int font_color = tft.color565(33, 149, 82); |
FontColor2 = tft.rgbcolor(8,85, 41) | unsigned int font_color2 = tft.color565(8, 85, 41); |
</code> | </code> |
| |
Закрашиваем весь дисплей белым | Закрашиваем весь дисплей белым |
<code python[enable_line_numbers="2", start_line_numbers_at="62"]> | <code arduino[enable_line_numbers="2", start_line_numbers_at="48"]> |
tft.clear(tft.rgbcolor(255,255, 255)) | tft.fillScreen(ST77XX_WHITE); |
</code> | </code> |
| |
| |
Выводим на дисплей название города | Выводим на дисплей название города |
<code python[enable_line_numbers="2", start_line_numbers_at="65"]> | <code arduino[enable_line_numbers="2", start_line_numbers_at="50"]> |
tft.text(1,1,wCity, font.terminalfont, FontColor, 2) | tft.setTextColor(font_color); |
| tft.setTextSize(2); |
| tft.setCursor(5, 1); |
| tft.print(w_city); |
</code> | </code> |
| |
Рисуем иконки: иконку погоды и градусник | Рисуем иконки: иконку погоды и градусник |
<code python[enable_line_numbers="2", start_line_numbers_at="67"]> | <code arduino[enable_line_numbers="2", start_line_numbers_at="55"]> |
tft.draw_bmp(34,16,'/w/' + wPic + pod + '.bmp') | reader.drawBMP("/weather/" + w_pic + w_pod + ".bmp", tft, 34, 16); |
tft.draw_bmp(5,70,'/w/' + wTempPic + '.bmp') | reader.drawBMP("/weather/" + w_pic_temp + ".bmp", tft, 5, 70); |
</code> | </code> |
| |
Выводим текущую температуру | Выводим текущую температуру |
<code python[enable_line_numbers="2", start_line_numbers_at="70"]> | <code arduino[enable_line_numbers="2", start_line_numbers_at="58"]> |
tft.text(30,75, str(wTemp), font.terminalfont, FontColor, 4) | tft.setTextSize(4); |
| tft.setCursor(30, 75); |
| tft.print(w_temp); |
</code> | </code> |
| |
Выводим дополнительные данные: температура по ощущениям, влажность и скорость ветра | Выводим дополнительные данные: температура по ощущениям, влажность и скорость ветра |
<code python[enable_line_numbers="2", start_line_numbers_at="72"]> | <code arduino[enable_line_numbers="2", start_line_numbers_at="62"]> |
tft.text(5,120,"Feels: " + str(wTempFeels), font.terminalfont, FontColor2, 1) | tft.setTextColor(font_color2); |
tft.text(5,128,"Humidity: " + str(wHumidity) + "%", font.terminalfont, FontColor2, 1) | tft.setTextSize(1); |
tft.text(5,136,"Wind: " + str(wWind) + "m/s", font.terminalfont, FontColor2, 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"); |
</code> | </code> |