Кроссплатформенная разработка погодной станции для Raspberry Pi

    Как известно, что ни делай под Raspberry Pi, получится либо медиаплеер, либо метеостанция. Постигла эта участь и меня — когда после очередного ливня датчик метеостанции залило, настала пора или купить новую, или сделать самому.

    От метеостанции нужны были следующие функции:

    • отображение температуры
    • отображение графика атмосферного давления
    • прогноз дождя
    • синхронизация времени (в случае обычной метеостанции, по DCF77, если уж на устройстве есть часы, они должны показывать точное время)

    Из покупных, по сочетанию «дизайн-цена-функции» не понравилась ни одна — либо нет одного, либо другого, либо слишком громоздко и дорого. В итоге решено было задействовать Raspberry Pi с TFT-экраном, и сделать те функции, которые нужны.

    Получилось примерно так:



    Подробности реализации и готовый проект под катом.

    Получение данных погоды


    Первое, с чем нужно было определиться — это получение данных погоды. Здесь есть 2 варианта, либо использовать свои датчики, либо брать погоду из интернета. Первое интереснее, но есть несколько «но»:

    • Сделать датчик «абы как» несложно, но сделать датчик хорошо, чтобы он например год работал от одного комплекта батареек, задача уже не столь тривиальная. Есть конечно сейчас и малопотребляющие процы, и радиомодули, но потратить на это месяц было бы лень.
    • Нужно заморачиваться с корпусом, влагозащитой и прочими мелочами (3д-принтера у меня нет).
    • Балкон выходит на солнечную сторону, так что погрешность измерения температуры в первую половину дня была бы слишком большой.

    Альтернативным вариантом была покупка готового метеомодуля с датчиками для Raspberry Pi.
    Увы, поиск показал что в продаже есть всего 2 варианта:

    Raspberry Pi sense hat

    Плата имеет «на борту» термометр, барометр, датчик влажности, гироскоп и акселерометр — но чем думали разработчики, ставя такой «экран» 8х8 светодиодов, непонятно — ничего внятного на нем вывести нельзя. Желаю разработчикам этого модуля всю жизнь UI под матрицу 8х8 писать :)

    Raspberry Pi weather hat

    Ничего кроме светодиодов здесь нет вообще.

    В общем, как ни странно, но нормального готового шилда для метеостанции с хорошим экраном и хорошим набором датчиков так никто и не сделал. Краудсорсеры, ау — рыночная ниша пропадает :)

    В итоге, не паримся и делаем по-простому — берем погоду из Интернета и выводим на обычный TFT. Как подсказал гугл, самое развитое API сейчас у https://openweathermap.org/api, его и будем использовать.

    В комментариях был вопрос про точность интернет-данных. Как можно видеть в коде ниже, для вывода температуры и атмосферного давления используется запрос на получение текущих (current weather data), а не прогнозируемых данных. Их точность можно считать вполне достаточной для бытовых целей, более того, скорее всего она даже выше, чем точность уличного термометра/датчика, размещенного за окном или на балконе.

    Регистрация


    Для получения данных погоды с openweathermap нужен ключ, его можно получить бесплатно, зарегистрировавшись на вышеупомянутом сайте. Ключ выглядит примерно так «dadef5765xxxxxxxxxxxxxx6dc8». Большинство функций доступны бесплатно, платные API нам не понадобятся. Для бесплатных функций есть ограничение на 60 запросов в минуту, нам этого достаточно.

    Чтение данных


    Чтение данных весьма просто благодаря библиотеке pyowm.

    Получение погоды на данный момент (Python):

    import pyowm
    
    owm = pyowm.OWM(apiKey)
    observation = owm.weather_at_coords(lat, lon)
    w = observation.get_weather()
    dtRef = w.get_reference_time(timeformat='date')
    t = w.get_temperature('celsius')
    temperatureVal = int(t['temp'])
    
    p = w.get_pressure()
    pVal = int(p['press'])
    

    Получение прогноза погоды для отображения осадков:

    fc = owm.three_hours_forecast_at_coords(lat, lon)
    rain = fc.will_have_rain()
    snow = fc.will_have_snow()
    rains = fc.when_rain()

    На выходе получаем массив данных со списком дождей и их интенсивностью. Несмотря на название функции three_hours_forecast_at_coords, дожди прописаны на 2-3 дня вперед.

    Можно использовать GET-запросы напрямую, например так. Это может пригодиться, например при портировании кода на MicroPython под ESP.

    Получение координат пользователя


    Как можно видеть выше, для получения данных нужны широта и долгота. Получение координат также весьма просто, и делается в 3 строчки кода:

    import geocoder
    
    g = geocoder.ip('me')
    lat = g.latlng[0]
    lon = g.latlng[1]

    UI


    Собственно, самая сложная часть. На Raspberry Pi используется TFT-дисплей от Adafruit, поддерживающий систему команд ILI9340. Библиотеки под него найти несложно, однако отлаживать код на Raspberry Pi не очень удобно. В итоге было принято решение написать высокоуровневый набор контролов, которых нужно было всего 3 — изображения, текст и линии. При запуске на Raspberry Pi контрол будет рисовать себя на TFT, при запуске на десктопе будет использоваться встроенная в Python библиотека tkinter. В итоге, код будет работать везде — и на Raspberry Pi, и на Windows, и на OSX.

    Код одного контрола выглядит примерно так:

    class UIImage:
      def __init__(self, image = None, x = 0, y = 0, cId = 0):
        self.x = x
        self.y = y
        self.width = 0
        self.height = 0
        self.cId = cId
        self.tkID = None
        self.tftImage = None
        self.tkImage = None
        self.useTk = utils.isRaspberryPi() is False
        if image is not None:
            self.setImage(image)
    
      def setImage(self, image):
        width, height = image.size
        if self.useTk:
            self.tkImage = ImageTk.PhotoImage(image)
        self.tftImage = image
        self.width  = width
        self.height = height
    
      def draw(self, canvas = None, tft = None):
        if tft != None:
            tft.draw_img(self.tftImage, self.x, self.y, self.width, self.height)
        elif canvas != None and self.tkImage != None:
            if self.tkID == None or len(canvas.find_withtag(self.tkID)) == 0:
                self.tkID = canvas.create_image(self.x, self.y, image=self.tkImage , anchor=tkinter.NW)
            else:
                canvas.itemconfigure(self.tkID, image=self.tkImage)
    

    Класс «FakeTFT» создает обычное окно программы:

    class FakeTFT:
        def __init__(self):
            self.tkRoot = tkinter.Tk()
            self.tkRoot.geometry("500x300")
    
            self.screenFrame = tkinter.Frame(self.tkRoot, width=330, height=250, bg="lightgray")
            self.screenFrame.place(x=250 - 330 / 2, y=5)
    
            self.tkScreenCanvas = tkinter.Canvas(self.tkRoot, bg = 'white', width = 320, height = 240, highlightthickness=0)
            self.tkScreenCanvas.focus_set()
            self.tkScreenCanvas.place(x=250 - 320 / 2, y=10)
    
            self.controls = []
    
        def draw(self):
              for c in self.controls:
                  c.draw(self.tkScreenCanvas)
    

    Класс «LCDTFT» использует «настоящий» дисплей (фрагмент кода):

    class LCDTFT:
        def __init__(self, spidev, dc_pin, rst_pin=0, led_pin=0, spi_speed=16000000):
            # CE is 0 or 1 for RPI, but is actual CE pin for virtGPIO
            # RST pin.  0  means soft reset (but reset pin still needs holding high (3V)
            # LED pin, may be tied to 3V (abt 14mA) or used on a 3V logic pin (abt 7mA)
            # and this object needs to be told the GPIO and SPIDEV objects to talk to
            global GPIO
            self.SPI = spidev
            self.SPI.open(0, 0)
            self.SPI.max_speed_hz = spi_speed
    
            self.RST = rst_pin
            self.DC = dc_pin
            self.LED = led_pin
    
            self.controls = []
    
        def draw(self):
              for c in self.controls:
                  c.draw(tft = self)
    

    При инициализации автоматически выбирается нужный дисплей, в зависимости от того, где запускается программа:

    def lcdInit():
      if utils.isRaspberryPi():
          GPIO.setwarnings(False)
          GPIO.setmode(GPIO.BCM)
          
          DC =  25
          LED = 18
          RST = 0
          return LCDTFT(spidev.SpiDev(), DC, RST, LED)
      else:
          return FakeTFT()

    Все это позволяет полностью абстрагироваться от «железа», и писать код типа такого:

    self.labelPressure = libTFT.UILabel("Pressure", 18,126, textColor=self.tft.BLACK, backColor=self.tft.WHITE, fontS = 7)
    self.tft.controls.append(self.labelPressure)
    self.labelRain = libTFT.UILabel("Rain", 270,126, textColor=self.tft.BLUE, backColor=self.tft.WHITE, fontS = 7)
    self.tft.controls.append(self.labelRain)

    Собственно UI выглядит так:



    На экране отображаются текущая температура, график атмосферного давления за сегодняшний день (в следующей версии будет добавлен график температуры), также в случае прогноза дождя, его время отмечается на графике вертикальной синей чертой (на данной картинке дождей нет). Также выводятся время последнего обновления данных и IP-адрес, если понадобится подключиться к устройству.

    Желающие ознакомиться с исходником подробнее, могут посмотреть его на guthub.

    Установка на Raspberry Pi


    Для тех, кто не хочет заморачиваться описанным выше, короткая инструкция по установке под спойлером.

    Инструкция
    — Скачиваем исходники с github:
    git clone github.com/dmitryelj/RPi-Weather-Station.git

    — Если не установлен Python3, ставим:
    sudo apt-get install python3

    — Ставим дополнительные библиотеки (они нужны для работы с дисплеем):
    sudo pip3 install numpy pillow spidev

    — Добавляем в автозапуск (sudo nano /etc/rc.local)

    python3 /home/pi/Documents/RPi-Weather-Station/weather.py &

    — Пробуем запустить

    python3 weather.py

    Если все работает, то перезагружаемся (sudo reboot) и пользуемся.

    В плане добавить еще что-нибудь полезное, например отображение карты облачности, API на openweathermap для этого есть.

    Продолжение следует.
    Поделиться публикацией
    Реклама помогает поддерживать и развивать наши сервисы

    Подробнее
    Реклама
    Комментарии 58
    • +5
      Не совсем уверен в целесообразности такой станции. Можно уж тогда виджет на рабочий стол или systray вывести
      • 0
        Погодная станция нужна как раз для того, чтобы видеть погоду не подходя к компу :) Одеваюсь утром на работу например, удобно знать температуру.
        • +3
          Одеваясь утром хотелось бы знать температуру на улице, а не в интернете.
          • 0
            Вопрос на самом деле интересный :) Прогнозы погоды бывает и врут, а текущая погода должна быть вполне точная, если конечно не жить в такой глухомани где до ближайшей метеостанции 100км. Я не думаю, что для городов погрешность будет большой.
            • +1
              Ну… У меня прогноз на телефоне и на компе. Прогнозы берутся с разных сайтов. Та же температура часто отличается на 3-5 градусов — вот вам и погрешность
            • +1
              Вот в моей Wi-Fi метеостанции c питанием от солнечной панели и ионисторов (посмотреть), не считая реальных данных по температуре, давлении, влажности за окном, все прогнозы (осадков, облачности, вероятности заморозков, ВНГО) осуществляются на базе данных собственных датчиков, используются методики из книги Савичева «Синоптические методы прогноза погоды», Броунова и Лебедевой.

              image
              image

            • 0
              в телефон вы утром смотрите? виджет на главный экран с часами.
              в варианте raspberry интересно железячное решение с точки зрения конструкции выходного дня, как вариант. датчики влажности, освещенности, температуры доступны на любой вкус, подключение по шинам i2c, spi — что вообще не требует шилда и позволяет вынести датчики на расстояние от коробки.
              при более глубоком проникновении а тему можно внешний блок сделать беспроводным и оборудовать анемометром, датчиком количества осадков и тд. дисплеи с тачскрином есть 5-7-8" подключаемые в режиме параллельного интерфейса или spi. что может доставить удовольствие в плане программирования этой связки
              • 0
                и опять же, если вы хотите шилд с датчиками относящимися к категории метео — вам подойдёт плата расширения для прототипирования — с разведённым разъемом и кучей монтажный отверстий. на ней вы можете собрать связку из барометра, гигрометра, датчика температуры и тд за пару часов
                • 0
                  Да, код написан так что добавить новый источник данных можно, не затрагивая UI и остальные классы. Но пока большого смысла не вижу, точности онлайн-прогноза по ощущениям, вполне хватает, ну а интернет дома всегда есть.
                  • 0
                    Насчет платы расширения понятно, вопрос был о покупке готового шилда, с уже установленным экраном, датчиками и разъемами.
                  • 0
                    Кстати про i2c, spi, опять же повторюсь, хороших шилдов готовых в продаже нет. Например тот же шилд с TFT от adafruit — ни один разъем не выведен, ставишь шилд, вся плата закрывается наглухо. Обратная сторона ведь пустая, что мешало хотя бы i2c вывести. Странные люди их проектировали, или же цель была сэкономить каждый цент.
                  • +2
                    Покупаем смартфон\планшет (стоимость 2-10 т.р. в зависимости от желаемого качества экрана), вешаем на стену, подключаем к заряднику. На экран выводим желаемое готовым или самописным приложением. Профит! При желании планшет\смартфон можно вклеить в настольную или настенную рамку. Бонусы — встроенный корпус, встроенный аккумулятор, встроенный GSM-канал,…

                    Если хочется поработать с электроникой, делаем датчик местной погоды с BT-каналом.
                    • 0
                      Я как-то думал о таком варианте, остановила мысль о надежности аккума и зарядки в режиме постоянного включения — все-таки это не штатный режим для телефона.
                      • +1

                        У меня уже 1,5 года висят три планшета за 60 баксов в режиме постоянной зарядки и постоянно включенного дисплея. Пока полет нормальный.

                        • +1
                          Можно использовать суточный таймер для подачи напряжения для зарядки на несколько часов в сутки.
                          • +1
                            Проще вообще батарею вытащить, если она конечно съемная.
                  • +2
                    А при чем тут DCF77?
                    • 0
                      При том, что если рассматривать не wifi-решение, то метеостанция должна иметь возможность приема DCF (впрочем у многих это есть, Oregon например). При наличии Wifi это разумеется, неактуально.
                      • +1
                        У вас же тут это не реализовано? Да и смысл в DCF77, когда устройство и так сетевое, проще в таком случае синхронизировать время по NTP.
                        • 0
                          Имелось в виду, что когда я искал обычные метеостанции, то выбирал только среди моделей с DCF, а это несколько сужало выбор. По WiFi это некритично, разумеется.
                          • 0
                            Понятно. Просто так написано, что я подумал, что от самодельной метеостанции требуется DCF77, поэтому и не понял, зачем. Так то DCF77 модули тоже есть на ebay, для всяких оффлайн самоделок.
                    • +2
                      Моя реализация домашней метеостанции:
                      • ESP8266 с датчиками влажности и температуры стоят по комнатам
                      • на домашнем сервере крутиться mosquitto (MQTT) и парочка скриптов на Python
                      • старый планшет (убитая батарейка), на экран которого с помощью приложения выводятся показания датчиков

                      Фотографии





                      • 0
                        Спасибо, интересно. Кстати, сколько ESP от батарейки живет?

                        PS: Порадовал значок «в туалете нет осадков» :)
                        • +1

                          Это значит в туалете нет протечек;)
                          ESP отправляет и принимает показания раз в 5 минут, потом уходит в глубокий сон, но при этом дисплей продолжает отображать информацию. При такой работе на старых аккумуляторах живёт неделю.

                          • 0
                            Я бы в таком случае отказался от дисплея, все равно он мелкий и не особо читабелен имхо.

                            А в целом, потому и не хочу возиться с самодельными датчиками — каждую неделю менять/заряжать батарейки лениво, а делать специально на малопотребляющем железе, это сильно заморачиваться надо. Хотя валяются дома 2 модема от Lora, надо бы как-нибудь задействовать.
                            • 0

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

                            • 0
                              Неделя это очень мало для датчика. Может имеет смысл убрать дисплей? Тем более, если подсветка включена постоянно.
                            • +1
                              Кстати, сколько ESP от батарейки живет?

                              Я насчитал 8 месяцев, но вольтметром это такое а повесить ADC на акб я не догадался при сборке. Пока 25-37 дней работают. ESP тратит 255 мс на включение, ~130 на подключение к wifi и ~40 на отправку данных. Потребление с полного акб через DD0503MA было 65 mA в работе и 0.05 mA в deepsleep.

                              • +1
                                Какая у вас прошивка? У меня NodeMCU. Подключение занимает до 5 секунд, еще 1-2 — передача данных. В таком режиме повербанк на 1 шт 18650 заканчивается за 2 недели…
                                • 0

                                  Прошивка — скетч для arduino ide. Скорее всего у вас включен DHCP-клиент, с ним было 0.8-2.5с подключение в зависимости от роутера, в ардуино это решается через WiFi.config(ip, gw, sn) сразу после старта. Еще esp запоминает настройки вайфая и сам подключается, инициализировать подключение каждый раз не нужно.
                                  Передача очень долгая если сервер в локалке, мб сервер такой, у меня датчики сразу кидают данные через tcp и если нет команд в очереди сервер шлет '\n' и разрывает соединение.

                            • 0
                              А что за приложение? И что прошито в esp модули?
                              Espшки прожорливые до батарейки, сколько они живут от одной зарядки?
                              • +2

                                По батарейки написал ниже.
                                Приложение MQQT Dash. В esp прошит espeasy с небольшими добавлениями.

                            • +2
                              Думал будет интересно… С интернета брать прогноз — это не метод.
                              • 0
                                Вполне себе метод, особенно если все окна выходят на юг и на солнце весь день градусник показывает +35… +45. Куда датчик не вешай, один фиг он будет нагреваться и врать до 25 градусов.
                                И берётся не прогноз, а текущая температура, погрешность, думаю, градусов 5 максимум будет.
                                Это если систему управления климатом дома делать, тут конечно по несколько датчиков в каждую комнату надо и одной ESP8266 на комнату будет в самый раз.
                                • 0

                                  В данном случае датчик выносится на ближайшее дерево/столб...

                              • 0
                                А в чем заключается кроссплатформенность из заголовка?
                                • 0
                                  Посмотрите на самый первый скриншот в статье.
                                • 0

                                  Интересная идея.
                                  А GRIB в качестве источника метеоданных не пробовали? Статистическая точность отображения у них повыше будет — таки не совсем для граждан данные.
                                  А RPI для такого слишком избыточно, только если некоторой дополнительной нагрузкой на уже достаточно задействованное устройство.
                                  Это все спокойно можно поднять и на ESP, а с появлением семейства камней ESP32 все стало ещё проще. Да и цена сильно разнится. В 32 не только WiFi c BT на борту, но и даже обычный LAN присутствует.
                                  А с собственными датчиками даже на солнечной стороне можно разобраться. Минимум парой в двух трубах с разного цвета поверхностю и/или материалом и поправочной таблицей.

                                  • 0
                                    GRIB не пробовал, если это аналог raw-данных, то самому делать расчет — уже пожалуй черезчур получается.

                                    На ESP32 пойдет разумеется, даже на обычной ESP наверно тоже, только web-запросы придется портировать на Micropython. Ну и у меня уже валяются и старая «малина» и дисплей от нее, так что кроме трудозатрат, все бесплатно.
                                    • 0

                                      Для GRIB есть описание запросов и данных, есть API, есть программы с открытым кодом ( например zgrib). В программах почти весь функционал — красивое отображение на экране ;).
                                      На 8266 будет сильно геморройнее реализовать, разве что на lua… :(
                                      Приставка micro не делает продукт отличимым от родителя ;). И под оригинальным (и любом другом) Python будет работать.
                                      Валяется — это не совсем бесплатно. Тут просто опыт реализации, как компенсация затрат в прошлом.

                                  • 0

                                    Сам поклонник малины, но приберёг ее для более серьёзных целей Я тоже решил для себя, что актуальней всего прогноз погоды за моим окном. И сделал девайсу на Atmega168.

                                    • 0
                                      Да, сейчас ESP32 за 7$ — и дешево и сердито.

                                      Но старая «малина» наверное у каждого есть, а для вывода погоды и RPi1 хватит.
                                    • 0
                                      А может кто поможет ссылкой на примеры драйверов для расбери?
                                      Хочу драйвер из GPIO в SPI сделать, а знаний пока не хватает.
                                    • 0

                                      У меня в качестве платформы стоит OpenHAB. Температуру беру из интернета, используя Weather Underground. Достаточно точно. Все данные отображаются на настенных планшетах.


                                      По поводу локальной погоды — для управления автоматическим закрытием маркизы и мансардного окна мне нужны были датчики ветра и дождя. Они бывают готовые, их только надо подключить к соответствующим входным модулям. У меня сеть построена на Z-wave, поэтому использовал Fibaro Binary Sensor.

                                      • +1
                                        Использовать цветной TFT дисплей, для того что бы эмулировать монохромный символьный :)
                                        И все это на малинке.
                                        • 0
                                          У меня есть подобный дисплей. Выполнение пары скриптов позволяет использовать его не напрямую по spi, а как обычный дисплей, запускать иксы и всякие DE.
                                          Вам не кажется, что написать полноэкранное Qt приложение проще и даже «более кроссплатформенно»?
                                          • 0
                                            Да, я экран в таком виде тоже использовал. Но для таких несложных задач консоль мне больше нравится.
                                            • 0

                                              А полегче вариант реализации выбрать? xfce и на GTK. Собственно и без них вполне X достаточно. Это не система со сложными окнами. Вполне можно обойтись даже библиотеками Python для окон.

                                              • 0
                                                На самом деле для таких случаев можно использовать хитрый режим Qt, когда приложение вообще без Иксов запускается
                                                • 0
                                                  Кстати интересно, можно ли как-то активировать на Raspberry Pi графический режим без иксов, примерно как раньше во времена MSDOS было.
                                                  • 0
                                                    Если работает консоль, то не вижу причин, почему что-то другое не должно работать.
                                                    Хотя не уверен, что драйвер для этого дисплея поддерживает ускорение.
                                                    Пример
                                                    image
                                                    • 0

                                                      Так там FB таки запускается, а дальше уже на нем повторит путь X по выводу графической информации в необходимом объёме и функционале.
                                                      Собственно по ключу FB поиск ведёт к готовым вариантам использования.

                                                    • 0

                                                      Весь проект открыт — пример реализации в студию.

                                                • +2
                                                  Буквально вчера сделал похожую штуку на трехцветном e-ink дисплее WaveShare:
                                                  image
                                                  По-моему, e-ink для таких применений лучше приспособлен, погода каждую секунду не меняется, а смотреть приятнее. Правда, у трехцветного дисплея обнаружилась неприятная особенность: прорисовка занимает 15 секонд, в течение которых он моргает всеми своими тремя цветами, а частичной прорисовки нет (по крайней мере, на уровне библиотеки).

                                                  Прогноз и текущяя погода берется с Weather Underground API, он умеет выдавать почасовой детальный прогноз на 10 дней в json. Очень рекомендую.
                                                  • 0

                                                    На Waveshare для всех трехцветных в колонке partial update указано No. Возможно это уже аппаратная проблема контроллера.
                                                    Для исключения моргания там вроде как на время обновления можно подавить вывод на экран. Давно последний раз их пользовал, но эта проблема была как-то решена.

                                                    • +1
                                                      Что-то не уверен я, что на e-ink можно подавить вывод во время обновления, просто исходя из самого принципа его работы.
                                                      • 0

                                                        Надо поднимать DS и смотреть. Сейчас их нет под рукой для натурного эксперимента, а в коде для прошивки на ассемблере из-за цейтнота практически полностью отсутствуют комментарии. Ждёт нужного сочетания планет для причесывания кода перед помещением в архив.

                                                  Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.