Эксперимент 70. Графический интерфейс метеостанции

В предыдущем эксперименте мы зарегистрировались в интернет-сервисе weatherbit.io, подключились к нему и получили данные о текущей погоде в Москве. Если заменить название города, то можно получить данные о погоде в другом городе.

Но эти данные приходят в виде удобном для программы, но не для человека. Человеку хотелось бы видеть красивую иконку погоды на дисплее, которая бы символизировала ситуацию на улице, например солнышко или облачко. Температуру нужно вывести крупным шрифтом, а влажность и скорость ветра — мелким. Многие другие данные вообще могут быть не интересны человеку. Иными словами нам нужно сделать графический пользовательский интерфейс (англ. graphical user interface, GUI)

Для отображения иконок погоды и пользовательского интерфейса их нужно загрузить во внутреннюю память микроконтроллера. Для этого нужно создать папку weather в корне файловой системы, в нее загрузим все файлы из архива.

Отображать данные мы будем на TFT дисплее, поэтому подключим его согласно схеме.

Схема эксперимента

Рисунок 1. Монтажная схема эксперимента с 8 выводами

Рисунок 2. Монтажная схема эксперимента с 11 выводами

Программный код эксперимента

Exp70.py
  1. from machine import Pin, SPI
  2. from tft import TFT_GREEN
  3. import font
  4. import network
  5. import gc
  6. import time
  7. import socket
  8.  
  9. _init()
  10. gc.collect()
  11.  
  12. dc = Pin(4, Pin.OUT)
  13. cs = Pin(2, Pin.OUT)
  14. rst = Pin(5, Pin.OUT)
  15.  
  16.  
  17. spi = SPI(1, baudrate=40000000, polarity=0, phase=0)
  18. tft = TFT_GREEN(128, 160, spi, dc, cs, rst, rotate=0)
  19. tft.initr(tft.BGR) # tft.initr(tft.RGB) #Если вместо синего цвета отображается красный, а вместо красного синий
  20.  
  21.  
  22. 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}]}
  23.  
  24. wCity = data['data'][0]['city_name']
  25. wTemp = round(data['data'][0]['temp'])
  26. wTempFeels = data['data'][0]['app_temp']
  27. wHumidity = data['data'][0]['rh']
  28. wWind = data['data'][0]['wind_spd']
  29.  
  30. pod = data['data'][0]['pod']
  31.  
  32. wCode = int(data['data'][0]['weather']['code'])
  33.  
  34. if wCode <= 233:
  35. wPic = '11'
  36. elif (wCode >= 300 and wCode <= 520) or wCode == 522:
  37. wPic = '09'
  38. elif wCode in (521, 600):
  39. wPic = '10'
  40. elif wCode >= 601 and wCode <= 622:
  41. wPic = '13'
  42. elif wCode >= 623 and wCode <= 751:
  43. wPic = '50'
  44. elif wCode == 800:
  45. wPic = '01'
  46. elif wCode in (801, 802):
  47. wPic = '02'
  48. elif wCode == 803:
  49. wPic = '03'
  50. elif wCode == 804:
  51. wPic = '04'
  52.  
  53. if (wTemp > 0):
  54. wTempPic = 'tp'
  55. else:
  56. wTempPic = 'tn'
  57.  
  58. FontColor = tft.rgbcolor(33,149, 82)
  59. FontColor2 = tft.rgbcolor(8,85, 41)
  60.  
  61.  
  62. tft.clear(tft.rgbcolor(255,255, 255))
  63.  
  64.  
  65. tft.text(1,1,wCity, font.terminalfont, FontColor, 2)
  66.  
  67. tft.draw_bmp(34,16,'/weather/' + wPic + pod + '.bmp')
  68. tft.draw_bmp(5,70,'/weather/' + wTempPic + '.bmp')
  69.  
  70. tft.text(30,75, str(wTemp), font.terminalfont, FontColor, 4)
  71.  
  72. tft.text(5,120,"Feels: " + str(wTempFeels), font.terminalfont, FontColor2, 1)
  73. tft.text(5,128,"Humidity: " + str(wHumidity) + "%", font.terminalfont, FontColor2, 1)
  74. tft.text(5,136,"Wind: " + str(wWind) + "m/s", font.terminalfont, FontColor2, 1)

В этой программе мы сосредоточились на создании GUI — графического интерфейса пользователя, поэтому пока убрали из программы обращение к интернет- сервису. Вместо этого мы задали в коде типовой ответ от сервера. Эту «заглушку» и будем использовать как временный источник данных.

Первым делом извлекаем из структуры нужные нам данные. А именно: название города, текущую температуру, влажность, скорость ветра, время суток, код погоды. Разберем по порядку:

Название города записываем в переменную wCity

  1. wCity = data['data'][0]['city_name']

Текущую температуру записываем в переменную wTemp

  1. wTemp = round(data['data'][0]['temp'])

Ощущение температуры записываем в переменную wTempFeels. (например из-за ветра или влажности температура может ощущаться человеком как более низкая, чем есть на самом деле)

  1. wTempFeels = data['data'][0]['app_temp']

Влажность в % записываем в переменную wHumidity

  1. wHumidity = data['data'][0]['rh']

Скорость ветра в метрах в секунду записываем в переменную wWind

  1. wWind = data['data'][0]['wind_spd']

Текущее время суток записываем в переменную pod. Если сейчас день, то в ней будет значение d, а если ночь — n. Эти данные нам понадобятся для выбора правильной иконки погоды. Например, днем мы будем отображать солнышко, а ночью луну.

  1. pod = data['data'][0]['pod']

Сервис weatherbit.io среди прочих данных о погоде сообщает код текущей погоды. Это коды, которые используются именно этим сервисом. По ним можно понять какую иконку погоды нужно отобразить. Подробнее о кодах и иконках. Код нам будет нужен в виде числа, поэтому сразу преобразуем его в целое число с помощью int()

  1. wCode = int(data['data'][0]['weather']['code'])

Сервис поддерживает очень большое количество иконок у нас столько нет. У нас всего 18 погодных иконок— 9 дневных и 9 ночных. Поэтому нам нужно настроить соответствие кодов из сервиса нашим иконкам. Например несколько видов дождя, которые обозначаются разными кодами, мы будем отображать одной и той же иконкой. Этот код преобразует коды в 9 иконок:

  1. if wCode <= 233:
  2. wPic = '11'
  3. elif (wCode >= 300 and wCode <= 520) or wCode == 522:
  4. wPic = '09'
  5. elif wCode in (521, 600):
  6. wPic = '10'
  7. elif wCode >= 601 and wCode <= 622:
  8. wPic = '13'
  9. elif wCode >= 623 and wCode <= 751:
  10. wPic = '50'
  11. elif wCode == 800:
  12. wPic = '01'
  13. elif wCode in (801, 802):
  14. wPic = '02'
  15. elif wCode == 803:
  16. wPic = '03'
  17. elif wCode == 804:
  18. wPic = '04'

Для отображения иконки термометра на дисплее нужно определить положительная сейчас температура или отрицательная. Если температура ниже нуля, то будем отображать «холодный» градусник с синим содержимым, иначе будем отображать «теплый» градусник. Этот код определяет какую иконку градусника нужно будет отобразить:

  1. if (wTemp > 0):
  2. wTempPic = 'tp'
  3. else:
  4. wTempPic = 'tn'

Определяем цвета для шрифтов. Один из них будет использоваться для отображения названия города и температуры, второй для остальной информации.

  1. FontColor = tft.rgbcolor(33,149, 82)
  2. FontColor2 = tft.rgbcolor(8,85, 41)

Закрашиваем весь дисплей белым

  1. tft.clear(tft.rgbcolor(255,255, 255))

Выводим на дисплей название города

  1. tft.text(1,1,wCity, font.terminalfont, FontColor, 2)

Рисуем иконки: иконку погоды и градусник

  1. tft.draw_bmp(34,16,'/w/' + wPic + pod + '.bmp')
  2. tft.draw_bmp(5,70,'/w/' + wTempPic + '.bmp')

Выводим текущую температуру

  1. tft.text(30,75, str(wTemp), font.terminalfont, FontColor, 4)

Выводим дополнительные данные: температура по ощущениям, влажность и скорость ветра

  1. tft.text(5,120,"Feels: " + str(wTempFeels), font.terminalfont, FontColor2, 1)
  2. tft.text(5,128,"Humidity: " + str(wHumidity) + "%", font.terminalfont, FontColor2, 1)
  3. tft.text(5,136,"Wind: " + str(wWind) + "m/s", font.terminalfont, FontColor2, 1)