• Witaj na Forum Arduino Polska! Zapraszamy do rejestracji!
  • Znajdziesz tutaj wiele informacji na temat hardware / software.
Witaj! Logowanie Rejestracja


Ocena wątku:
  • 0 głosów - średnia: 0
  • 1
  • 2
  • 3
  • 4
  • 5
Czytanie poprawnie danych API
#1
Witam
Poszukuję rozwiązania problemu z poprawnym czytaniem API z danymi jakości powietrza.
Pierwsze pobranie danych wygląda tak:

Connected to WiFi network with IP Address: 192.168.0.179
Test PM10:0.00
Test SO2:4.35
Test No2:3.99
Test PM10:10.40
Test SO2:4.35
Test No2:3.99


a powinno wyglądać:

Connected to WiFi network with IP Address: 192.168.0.179
Test PM10:4.35
Test SO2:3.99
Test No2:10.40
Test PM10:4.35
Test SO2:3.99
Test No2:10.40


Jak to naprawić?

Kod:
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#include <WiFiClient.h>
#include <ArduinoJson.h>

const char *ssid = "SSID";
const char *password = "PASS";
// Serwer API
char serverPM[] = "http://api.gios.gov.pl/pjp-api/rest/data/getData/828"; // 828
char serverSO[] = "http://api.gios.gov.pl/pjp-api/rest/data/getData/831"; // 831
char serverNO[] = "http://api.gios.gov.pl/pjp-api/rest/data/getData/824"; // 824

float Pm10;
float So2;
float No2;

String payload;
WiFiClient client;
HTTPClient http;
StaticJsonDocument<200> docPM;
StaticJsonDocument<200> docSO;
StaticJsonDocument<200> docNO;
void setup()
{
  Serial.begin(115200);

  WiFi.begin(ssid, password);
  Serial.println("Connecting");
  while (WiFi.status() != WL_CONNECTED)
  {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.print("Connected to WiFi network with IP Address: ");
  Serial.println(WiFi.localIP());
}
// Podprogramy

void apiPM10()
{ //  Zapytanie do API z PM10 Inowrocław
 
 
  http.begin(client, serverPM);
  payload = http.getString();
 
  DeserializationError error = deserializeJson(docPM, payload);
  int httpResponseCode = http.GET();
 
    Pm10 = docPM["values"][1]["value"];
    Serial.print("Test PM10:");
    Serial.println(Pm10);

}
void apiSO2()
{ // Zapytanie do API z SO2 Inowrocław
  http.begin(client, serverSO);
  payload = http.getString();
  DeserializationError error = deserializeJson(docSO, payload);
  int httpResponseCodeSO = http.GET();

    So2 = docSO["values"][1]["value"];
    Serial.print("Test SO2:");
    Serial.println(So2);
   
}
void apiNO2()
{ // Zapytanie do API z NO2 Inowrocław
  http.begin(client, serverNO);
  payload = http.getString();
  DeserializationError error = deserializeJson(docNO, payload);
  int httpResponseCodeNO = http.GET();
 
    No2 = docNO["values"][1]["value"];
    Serial.print("Test No2:");
    Serial.println(No2);
 
}

void loop()
{
  apiPM10();
  delay(1000);
  apiSO2();
  delay(1000);
  apiNO2();
  delay(5000);
}
 
Odpowiedź
#2
Problem może być taki, że nic w internecie nie dzieje się natychmiast, odpowiedź jest udzielana nie szybciej niż ping, kilkadziesiąt ms, a jeszcze maszyna musi się zorientować o co Ci chodzi. ESP w tym czasie potrafi skończyć i zapalić fajkę. Nieźle, że w ogóle działa, łapiesz się na odpowiedź z poprzedniego zapytania, wydaje mi się, że to może działać tylko czasami, przypadkowo. Wgrałem ten kod u mnie i nie działa wcale. W przykładach jest zapytanie i jest obudowane wieloma sprawdzeniami, ja bym zaczął od czy jest pora zapytać, czy jestem podłączony do WIFI, czy połączyłem się do serwera z poprawnym komunikatem, potem wg przykładów analogicznie, a potem dorobił do tego maszynę stanów by nie blokować LOOP.
Miło być decenianym https://buycoffee.to/kaczakat
 
Odpowiedź
#3
W sumie się nie znam, więc się wypowiem.
Moim zdaniem zła jest kolejność wykonywania zapytania - najpierw powinno być wysyłane GET, a później sprawdzany wynik, a nie odwrotnie.
Teraz kod wygląda następująco:
Kod:
  http.begin(client, serverPM);
  payload = http.getString();
 
  DeserializationError error = deserializeJson(docPM, payload);
  int httpResponseCode = http.GET();

Czyli najpierw odczytujesz za pomocą getString() odpowiedź, a później wysyłasz za pomocą GET() zapytanie. Dlatego przy kolejnym wywołaniu dostajesz poprzednie wyniki.
Kodu nie testowałem, ale wydaje mi się, że powinno to być coś bardziej jak:
Kod:
http.begin(client, serverPM);

int httpResponseCode = http.GET();
payload = http.getString();

DeserializationError error = deserializeJson(docPM, payload);
Oczywiście należałoby dodać jeszcze obsługę błędów. Natomiast opóźnieniami nie ma co się przejmować - wywołania i tak są blokujące.
 
Odpowiedź
#4
POLECAM Ci te API (darmowe) : api.weatherapi.com

u mnie działa bez zarzutu (na ESP-WROVER 8MB PS-RAM)
wycinek z programu - tylko że ja korzystam z modułu z pamięcią PS-RAM i tam alokuje dane)
ale może się przyda
Kod:
void AirUpdate()
{
    if (!F_ptrAir) ptrAirMalloc();    // jesli pamięć nie utworzona stwórz ją w PSRAM
   
    #if SERIAL
        #if ENGLISH
            Serial.printf("\n[Air Update] Try Downloading JSON File from https://api.weatherapi.com/");
        #endif
        #if POLISH
            Serial.printf("\n[Air Update] Próbuję pobrać plik JSON z https://api.weatherapi.com/");
        #endif
    #endif

    #if ENGLISH
        String path = "http://api.weatherapi.com/v1/current.json?key=" + PogodaToken + "&q=" + PogodaMiasto + "&aqi=yes";
    #endif
    #if POLISH
        String path = "http://api.weatherapi.com/v1/current.json?key=" + PogodaToken + "&q=" + PogodaMiasto + "&aqi=yes";
    #endif   
       
    HTTPClient http;
    http.useHTTP10(true);
    http.begin(client, path);
    http.GET();

    DynamicJsonDocument doc(2048);
    DeserializationError error = deserializeJson(doc, http.getStream());
       
    if(error)
    {
        F_AirUpdate = false;      // aktualizacja nie powiodła się
        #if SERIAL
            #if ENGLISH
                Serial.printf("\n[Air Update] DeserializeJson() failed: ");
                Serial.println(error.c_str());
            #endif
            #if POLISH
                Serial.printf("\n[Air Update] DeserializeJson() error: ");
                Serial.println(error.c_str());
            #endif        
        #endif 
    }
    else
    {
        F_AirUpdate = true;
    }

    http.end();
   
    if(F_AirUpdate)
    {
        #if SERIAL
            #if ENGLISH
                Serial.printf("\n[ESP] [Air Update] JsonDocument Usage Memory : %s bytes", String(doc.memoryUsage()));                 
                Serial.printf("\n[ESP] [Air Update] [FREE RAM]                : %d kB\n", ESP.getFreeHeap());      
            #endif
            #if POLISH
                Serial.printf("\n[ESP] [Air Update] JsonDocument zaalokował pamięć : %s bajtów", String(doc.memoryUsage())); 
                Serial.printf("\n[ESP] [Air Update] [FREE RAM]                     : %d kB\n", ESP.getFreeHeap());      
            #endif
        #endif

        // --- update struct Air in PSRAM !!! -------------------------------------------------- //

        ptrAir->co    = doc["current"]["air_quality"]["co"].as<double>();
        ptrAir->o3    = doc["current"]["air_quality"]["o3"].as<double>();
        ptrAir->no2   = doc["current"]["air_quality"]["no2"].as<double>();
        ptrAir->so2   = doc["current"]["air_quality"]["so2"].as<double>();
        ptrAir->pm25  = doc["current"]["air_quality"]["pm2_5"].as<double>();
        ptrAir->pm10  = doc["current"]["air_quality"]["pm10"].as<double>();
        ptrAir->defra = doc["current"]["air_quality"]["gb-defra-index"];
        ptrAir->epa   = doc["current"]["air_quality"]["us-epa-index"];

        switch (ptrAir->epa)
        {
            #if POLISH
                case    1:  strcpy(ptrAir->description,  "dobra"); break;
                case    2:  strcpy(ptrAir->description,  "umiarkowana"); break;
                case    3:  strcpy(ptrAir->description,  "niezdrowa dla alergików"); break;
                case    4:  strcpy(ptrAir->description,  "niezdrowa"); break;
                case    5:  strcpy(ptrAir->description,  "bardzo niezdrowa"); break;
                case    6:  strcpy(ptrAir->description,  "niebezpieczna"); break;
                default  :  strcpy(ptrAir->description,  "błąd parsowania"); break;
            #endif
            #if ENGLISH
                case    1:  strcpy(ptrAir->description,  "good"); break;
                case    2:  strcpy(ptrAir->description,  "moderate"); break;
                case    3:  strcpy(ptrAir->description,  "unhealthy for allergy sufferers"); break;
                case    4:  strcpy(ptrAir->description,  "unhealthy"); break;
                case    5:  strcpy(ptrAir->description,  "very unhealthy"); break;
                case    6:  strcpy(ptrAir->description,  "dangerous"); break;
                default  :  strcpy(ptrAir->description,  "parsing error"); break;
            #endif
        }

        // --- update struct Air in PSRAM !!! -------------------------------------------------- //

        #if SERIAL
            if (F_AirUpdate) AirShow();      // pokaż jeśli SERIAl jest TRUE   
        #endif
    }
}
 
Odpowiedź
#5
jeszcze wersja na PYTHON'a. to możesz uruchomić sobie nawet na PECECIE, z "prawie" wszystkimi danymi jakie można pobrać z API.WEATHER.COM bo jest tam tego dużo więcej (jak prognoza pogody na tydzień, dane archiwalne, itp)

[Obrazek: all.png]

Kod:
import json
import requests

token = "XXXX_TWOJ_TOKEN"   # token serwera API api.weatherapi.com

airquality = {
    'city':                 'null',     # miasto
    'country':              'null',     # kraj
    'tz_id':                'null',     # region (kontynent) / stolica
    'latitude':             'null',     # długość geograficzna
    'longitude':            'null',     # szerokość geograficzna
    'localtime_epoch':      'null',     # data i czas -> format : epoch
    'localtime':            'null',     # data i czas -> format : local
    'last_update_epoch':    'null',     # data i czas ostatniej aktualizacji -> format : epoch
    'last_update':          'null',     # data i czas ostatniej aktualizacji -> format : local
    'co':                   'null',     # tlenek węgla
    'o3':                   'null',     # ozon
    'no2':                  'null',     # dwutlenek azotu
    'so2':                  'null',     # dwutlenek siarki
    'pm25':                 'null',     # pyły i aerozole pm2.5
    'pm10':                 'null',     # pyły i aerozole pm10
    'defra':                'null',     # jakość powietrza kody DEFRA
    'epa':                  'null',     # jakość powietrza kody EPA
}

def AirUpdate(city = "Lubin"):
   
    url = "http://api.weatherapi.com/v1/current.json?key=" + token + "&q=" + city + "&lang=pl&aqi=yes"
 
    data = requests.get(url).text
    data = json.loads(data)

    airquality['city']                  = data['location']['name']                  # nazwa miasta
    airquality['country']               = data['location']['country']               # nazwa kraju
    airquality['tz_id']                 = data['location']['tz_id']                 # kontynent/stolica
    airquality['latitude']              = data['location']['lat']                   # szerokość geograficzna
    airquality['longitude']             = data['location']['lon']                   # długość geograficzna
    airquality['localtime_epoch']       = data['location']['localtime_epoch']       # czas lokalny format: epoch
    airquality['localtime']             = data['location']['localtime']             # data i czas lokalny
    airquality['last_update_epoch']     = data['current']['last_updated_epoch']     # czas ostatniej aktualizacji epoch
    airquality['last_update']           = data['current']['last_updated']           # czas ostatniej aktualizajci locak
    airquality['co']                    = data['current']['air_quality']['co']      #
    airquality['o3']                    = data['current']['air_quality']['o3']      #
    airquality['no2']                   = data['current']['air_quality']['no2']     #
    airquality['so2']                   = data['current']['air_quality']['so2']     #
    airquality['pm25']                  = data['current']['air_quality']['pm2_5']   #
    airquality['pm10']                  = data['current']['air_quality']['pm10']    #
    airquality['defra']                 = data['current']['air_quality']['gb-defra-index'] 
    airquality['epa']                   = data['current']['air_quality']['us-epa-index']   

if __name__ == "__main__":
    AirUpdate('Warszawa')
    print('carbon monoxide           :', airquality['co'], '[μg / m3]')      # tlenek węgla
    print('nitrogen dioxide          :', airquality['no2'], '[μg / m3]')     # dwutlenek azotu
    print('ozone                     :', airquality['o3'], '[μg / m3]')      # ozon
    print('sulphur dioxide           :', airquality['so2'], '[μg / m3]')     # dwutlenek siarki
    print('PM2.5 dusts and aerosols  :', airquality['pm25'], '[μm]')         # pyły PM2.5
    print('PM10 dusts and aerosols   :', airquality['pm10'], '[μm]')         # pyły PM10
    print('DEFRA quality index       :', airquality['defra'])                # defra GB index
 
Odpowiedź
#6
wersja pod ESP32, ESP8266 (kiedyś robiłem)
dla serwera API : api.weatherapi.com

Kod:
#include <Arduino_JSON.h>
JSONVar JSON_Air;                                                  // obiekt Powietrze

struct Air
{
    double  co    = 0;                     // Carbon Monoxide (μg/m3)    -   Tlenek węgla (μg / m3)
    double  o3    = 0;                     // Ozone (μg/m3)              -   Ozon (μg / m3)
    double  no2   = 0;                     // Nitrogen dioxide (μg/m3)   -   Dwutlenek azotu (μg / m3)
    double  so2   = 0;                     // Sulphur dioxide (μg/m3)    -   Dwutlenek siarki (μg / m3)
    double  pm25  = 0;                     // PM2.5 (μg/m3)              -   aerozole atmosferyczne o średnicy nie większej niż 2,5 μm
    double  pm10  = 0;                     // PM10 (μg/m3)               -   mieszanina zawieszonych w powietrzu cząsteczek o średnicy nie większej niż 10 μm
    int     Defra = 0;                     // Air Quality Index          -   index jakości powietrza   
    int     EPA   = 0;                     // Air Qaality Index          -   index jakosci powietrza (standard EPA)
    String  opis = "NULL";                 // Air Quality opis
}
powietrze;

String httpGETRequest(const char* serverName)                                                        
{                                                                                                    
    HTTPClient http;                                                                                 
    http.begin(serverName);                                                                          
    int httpResponseCode = http.GET();                                                               
    String bufor = "{}";                                                                             
                                                                                                     
    if ( httpResponseCode > 0 )                                                                      
    {                                                                                                
       bufor = http.getString();                                                                    
    }                                                                                                
    else                                                                                             
    {                                                                                                
        // error                                                                                     
    }                                                                                               
    http.end();                                                                           
    return bufor;                                                                                    
}

void AirQualityUpdate()
{
    String PogodaToken  = "twoj_token";                    // token serwera pogodowego http://api.weatherapi.com
    String PogodaMiasto = "twoje_miasto";                                              // miasto dla ścieżki HTTP  http://api.weatherapi.com    
    String path = "http://api.weatherapi.com/v1/current.json?key=" + PogodaToken + "&q=" + PogodaMiasto + "&aqi=yes";
    String jsonBuffer = httpGETRequest(path.c_str());     
       
    JSON_Air = JSON.parse(jsonBuffer);                

    if (JSON.typeof(JSON_Air) == "undefined")                                                
    {                                                                                        
        // błąd parsowania
    }       

    powietrze.co     = JSON_Air["current"]["air_quality"]["co"];
    powietrze.o3     = JSON_Air["current"]["air_quality"]["o3"];
    powietrze.no2    = JSON_Air["current"]["air_quality"]["no2"];
    powietrze.so2    = JSON_Air["current"]["air_quality"]["so2"];
    powietrze.pm25   = JSON_Air["current"]["air_quality"]["pm2_5"];
    powietrze.pm10   = JSON_Air["current"]["air_quality"]["pm10"];
    powietrze.Defra  = JSON_Air["current"]["air_quality"]["gb-defra-index"];
    powietrze.EPA    = JSON_Air["current"]["air_quality"]["us-epa-index"];
}
 
Odpowiedź
  


Skocz do:


Przeglądający: 1 gości