logo elektroda
logo elektroda
X
logo elektroda
Adblock/uBlockOrigin/AdGuard mogą powodować znikanie niektórych postów z powodu nowej reguły.

Jak pobrać pogodę z OpenWeatherMap API na samych socketach? Tutorial API, JSON, C

p.kaczmarek2 03 Gru 2024 15:41 2754 6

TL;DR

  • Pokazano pobieranie bieżącej pogody z OpenWeatherMap API w czystym C, bez bibliotek Arduino i bez zewnętrznego parsera JSON.
  • Zapytanie realizują gethostbyname, WSAStartup, socket TCP, connect i ręcznie złożony GET HTTP/1.1 wysyłany bezpośrednio po TCP.
  • W przykładowej odpowiedzi dla New York pojawiały się m.in. temperatura 274.3 K, wilgotność 71% i kod HTTP 200.
  • Nagłówek HTTP usuwano po dwóch pustych liniach, a pole temp wyciągano przez strstr i zamieniano na liczbę funkcją atof.
  • Rozwiązanie działało na Windowsie i BK7231 z LWIP, ale operacje send, recv i connect były blokujące i wymagały uruchomienia w wątku.
Wygenerowane przez model językowy.
📢 Słuchaj (AI):
  • Logo OpenWeather z pomarańczowym słońcem i napisami.
    OpenWeatherMap to usługa która oferuje m. in. dostęp do bieżącej informacji pogodowej dla danej pozycji na mapie. Dane te można łatwo pobrać za pomocą jednego zapytania GET, choć wcześniej potrzebny będzie nam klucz API, który na szczęście można otrzymać za darmo. Tu pokażę jak właśnie takie zapytanie GET wysłać, tym razem jednak bez zewnętrznych bibliotek.

    Ten temat opiera się zasadniczo na mojej wcześniejszej prezentacji OWM:
    ESP32 i wyświetlacz dotykowy - tutorial cz. 4 - pogoda z internetu, API, JSON
    Będzie tu jednak zasadnicza różnica, a mianowicie nie użyję tutaj bibliotek Arduino, całość spróbuję uruchomić w czystym C na najzwyklejszych socketach, które powinny być dostępne na większości platform - w tym też na Windowsie (Winsock). Również parsing JSON zrealizuję w najprostszy możliwy sposób, bez zewnętrznych bibliotek.

    Motywacja tematu jest prosta - chciałem uruchomić sobie pobieranie pogody na mikrokontrolerze w sposób wydajny i oszczędny, jak najbardziej zmniejszając użycie pamięci Flash. Docelowo chcę uruchomić kod na socketach z LWIP, czyli na rozwiązaniu dostępnym na wielu systemach wbudowanych.

    Uruchomienie na Windowsie
    A więc zacznijmy. Pierwszą, dość pozytywną dla nas obserwacją jest fakt, że sockety są też na Windowsie, więc prototyp zrobimy normalnie na komputerze...
    Przypomnijmy sobie poprzedni kod:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Zasadniczo musimy zaimplementować tylko ten fragment:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    I tu jest pierwsza niespodzianka - za http.GET kryje się nie jedno żądanie, lecz dwa:
    - najpierw musimy zamienić nazwę domenową api.openweathermap.org na adres IP (o ile nie mamy tego w cache)
    - potem dopiero na ten adres IP musimy wysłać zapytanie GET, czyli zasadniczo krótki pakiet HTTP poprzez protokół TCP
    Do pobrania IP dla nazwy domenowej służy funkcja gethostbyname, która dostępna jest zarówno na Windowsie/Linuxie, jak i np. w popularnej bibliotece sieciowej LWIP.
    Uruchomienie socketów na Windowsie wymaga jeszcze wywołania WSAStartup, co też uwzględniłem w kodzie:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Rezultat:
    Ekran konsoli pokazujący adres IP 141.95.47.140.
    Mamy już IP, teraz pora wysłać żądanie GET...
    Najpierw otwieramy socket TCP (SOCK_STREAM) i nawiązujemy połączenie:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Powyższy warunek będzie skutkować błędem jeśli na docelowym adresie IP nie ma nasłuchu TCP na określonym porcie. Po pomyślnym nawiązaniu połączenia możemy wysłać żądanie GET, czyli kolejno nagłówek GET zgodny z HTTP 1.1 wraz z adresem interesującego nas zasobu a potem pozwolenie na zamknięcie połączenia po dokonaniu transakcji:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Docelowo adres zasobu złożymy za pomocą sprintf (albo lepiej - snprintf, respektując rozmiar bufora), ale na razie na próbę dałem sztywno.

    Potem zostało tylko odebrać odpowiedź i wyświetlić ją na ekranie:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Oczywiście nie zapominajmy o zamknięciu socketa:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Rezultat:
    Konsola Windows z wyświetlonymi informacjami o pogodzie z OpenWeatherMap

    Teraz pora na przetwarzanie odpowiedzi. W przypadku metody opartej o gotową bibliotekę dostawaliśmy od razu sam plik JSON, ale tu odpowiedź zawiera też nagłówek HTTP. Musimy go pominąć. Sprawa jest prosta - dwa przejścia do następnej linii pod rząd oznaczają jego koniec:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Zrzut ekranu, tym razem z debuggera Visual Studio:
    Zrzut ekranu kodu C z analizatorem w Visual Studio

    Teraz pora wyciągnąć dane z tego ciągu znaków JSON...
    Rozważmy jego budowę (bez sztucznego formatowania):
    
    {"coord":{"lon":-74.0059,"lat":40.7127},"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04d"}],"base":"stations","main":{"temp":274.3,"feels_like":269.25,"temp_min":272.46,"temp_max":275.13,"pressure":1024,"humidity":71,"sea_level":1024,"grnd_level":1022},"visibility":10000,"wind":{"speed":5.81,"deg":311,"gust":5.81},"clouds":{"all":100},"dt":1733227560,"sys":{"type":1,"id":4610,"country":"US","sunrise":1733227402,"sunset":1733261345},"timezone":-18000,"id":5128581,"name":"New York","cod":200}
    

    Całość wygląda bardzo prosto, klucz od temperatury ("temp") jest wzięty w cudzysłów, a po nim jest dwukropek i jego wartość. Możemy go wyszukać w łańcuchu znaków za pomocą strstr, ale wcześniej dopiszemy do niego cudzysłów i ten dwukropek. Rezultat można zamienić z napisu na liczbę zmiennoprzecinkową za pomocą atof:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Przykład użycia funkcji:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Rezultat:
    Zrzut ekranu pokazuje dane z API pogodowego, w tym temperaturę i wilgotność dla Nowego Jorku w formacie JSON.
    Działa! Tylko czemu ta wartość temperatury jest taka dziwna?

    Poprawka jednostki temperatury
    OpenWeatherMap domyślnie wysyła temperaturę w Kelwinach. Zgodnie z dokumentacją:
    Cytat:

    Temperature is available in Fahrenheit, Celsius and Kelvin units. Kelvin is used by default, with no need to use the units parameter in API calls.

    For temperature in Fahrenheit, use "units=imperial".

    For temperature in Celsius, use "units=metric".

    You can find examples of API calls in the documentation for the service you are interested in.

    aby otrzymać wyniki w °C, należy do zapytania dopisać wybór jednostki:
    Zrzut ekranu z kodem JSON i wynikiem przetwarzania danych.
    Nieco lepiej, czy naprawdę w Nowym Jorku jest teraz jeden stopień?
    Aktualna prognoza pogody dla Nowego Jorku, pokazująca temperaturę, szansę opadów i prędkość wiatru.
    No, prawie. Powiedzmy, że się zgadza.


    Uruchomienie na mikrokontrolerze (tutaj dla przykładu BK7231)
    W przypadku BK7231 musiałem przede wszystkim załączyć inne nagłówki, tu sockety udostępnia wspomniany LWIP:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Dodatkowo, całość musiałem umieścić w wątku, bo operacje send, recv, a nawet i connect, są tutaj blokujące:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Rezultat (na mojej platformie OBK):
    Zrzut ekranu przedstawiający wyniki działania aplikacji pobierającej dane pogodowe z OpenWeatherMap przy użyciu zapytania HTTP.
    JSON jest poprawnie odbierany, teraz zostało go przetworzyć wedle uznania.

    Podsumowanie
    Jak widać wcale nie potrzeba zewnętrznych bibliotek by móc szybko i sprawnie uruchomić pobieranie bieżącej sytuacji pogodowej z API OpenWeatherMap. Co więcej, całość da się często uruchomić normalnie na komputerze z systemem Windows (czy tam Linux - tam też są sockety zgodne z moją prezentacją) a potem wygodnie przenieść na używaną przez nas platformę wbudowaną... w moim przypadku docelową platformą było BK7231 z LWIP, ale myślę, że nie tylko BK ten sposób może dotyczyć.
    Również samo wyłuskanie pomiarów z JSON okazało się być proste. Są do tego gotowe, zaawansowane biblioteki takie jak cJSON, ale jak widać, nie zawsze są koniecznie potrzebne...
    Czy widzicie jakieś zastosowanie dla tego API? Może jakiś wyświetlacz?
    PS: Oczywiście przedstawiony kod jest uproszczony, można by go ulepszyć, np. zabezpieczyć przed przekroczeniem rozmiaru buforu w funkcji szukającej w JSON, czy też można by go zoptymalizować, np. tworząc szukany łańcuch ręcznie, bo użyty w tym momencie tam sprintf jest dość ciężki...

    Fajne? Ranking DIY
    Pomogłem? Kup mi kawę.
    O autorze
    p.kaczmarek2
    Moderator Smart Home
    Offline 
    Inżynier programista z wieloletnim doświadczeniem embedded i full stack developer.
    Specjalizuje się w: embedded, Full-Stack Developer
    p.kaczmarek2 napisał 14466 postów o ocenie 12480, pomógł 650 razy. Jest z nami od 2014 roku.
  • #2 21331741
    metalMANiu
    Poziom 21  
    Posty: 619
    Pomógł: 11
    Ocena: 241
    p.kaczmarek2 napisał:
    Czy widzicie jakieś zastosowanie dla tego API? Może jakiś wyświetlacz?

    Ja mam pomysł.
    Mając informacje o bieżącym zachmurzeniu można porównać wydajność instalacji fotowoltaicznej posiadającej aktuatory podążające za słońcem do klasycznej nieruchomej instalacji. Ciekawe byłoby takie porównanie w pochmurne dni.
    Do tego potrzebne byłoby jeszcze jedno API dające informacje o aktualnym azymucie i elewacji słońca w konkretnym miejscu.
    Skąd takie dane wydobyć?
    Macie może jakiś zbiór innych przydatnych źródeł z przydatnymi API?
    W jaki sposób "zaimportować" dowolne API do influxDB?
  • #3 21331900
    krzbor
    Poziom 29  
    Posty: 1736
    Pomógł: 40
    Ocena: 1046
    Czy ktoś w dłuższym okresie testował prognozy pogody OpenWeather? Jak kiedyś to analizowałem, to niezbyt się sprawdzała. W każdym bądź razie pogoda Google była znacznie dokładniejsza.
  • #4 21332144
    pier
    Poziom 24  
    Posty: 2445
    Pomógł: 40
    Ocena: 1891
    Kilka lat temu zrobiłem pogodynkę z kolorowym wyświetlaczem i esp8266 która pobierała dane z OpenWeatherMap. Działało to poprawnie ale kilka miesięcy temu przestało, nie wiem dlaczego ale podejrzewam że coś zmienili. Brak mi czasu aby się tym zająć.
  • #5 21332157
    krzbor
    Poziom 29  
    Posty: 1736
    Pomógł: 40
    Ocena: 1046
    pier napisał:
    Kilka lat temu zrobiłem pogodynkę z kolorowym wyświetlaczem i esp8266 która pobierała dane z OpenWeatherMap

    Widzę, że testowałeś to przez dłuższy czas. Jaka była sprawdzalność prognozy pogody? Różniła się od innych prognoz?
  • #6 21332169
    pier
    Poziom 24  
    Posty: 2445
    Pomógł: 40
    Ocena: 1891
    krzbor napisał:
    pier napisał:
    Kilka lat temu zrobiłem pogodynkę z kolorowym wyświetlaczem i esp8266 która pobierała dane z OpenWeatherMap

    Widzę, że testowałeś to przez dłuższy czas. Jaka była sprawdzalność prognozy pogody? Różniła się od innych prognoz?


    Zgodność z rzeczywistością to jak wiadomo różnie ale w porównaniu do innych serwisów to raczej nie odbiegała.
  • #7 21339253
    Duch__
    Poziom 31  
    Posty: 2338
    Pomógł: 33
    Ocena: 1597
    Ja na podstawie prognozy pogody z openweathermap pobieram informacje tylko o poziomie zachmurzenia. Jeśli jest słonecznie(ID 800 i 801) to na termostacie obniżam temperaturę w domu o -1 st C. Powoduje to że piec gazowy mniej grzeje, a temperatura w domu rośnie od wpadającego słońca. Nie dochodzi tym samym do przegrzewów w pokojach, oraz zużycie gazu jest mniejsze.

    Co do rezygnacji z deszyfrowania odpowiedzi json za pomocą bibliotek to kiedyś walczyłem z AIRLY. Tam odpowiedź jest niemiłosiernie długa, danych od groma. Po odebraniu pewnego fragmentu danych (liczyłem cudzysłowia) przerywałem połączenie i dodawałem nawiasy klamrowe żeby cały json wyglądał prawidłowo i dopiero wtedy przystępowałem do ręcznej deszyfracji (liczenia cudzysłowia).
📢 Słuchaj (AI):

Podsumowanie tematu

✨ W dyskusji poruszono temat pobierania danych pogodowych z OpenWeatherMap API przy użyciu socketów w czystym C, bez zewnętrznych bibliotek. Użytkownik podkreśla, że do uzyskania danych potrzebny jest klucz API, który można otrzymać za darmo. Inni uczestnicy rozmowy dzielą się doświadczeniami z korzystania z OpenWeatherMap, wskazując na różnice w dokładności prognoz w porównaniu do innych serwisów, takich jak Google Weather. Wspomniano również o zastosowaniach danych pogodowych, takich jak optymalizacja pracy instalacji fotowoltaicznych oraz regulacja temperatury w domach na podstawie poziomu zachmurzenia. Pojawiły się pytania o inne API oraz o metody importowania danych do influxDB.
Wygenerowane przez model językowy.
REKLAMA