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);
}
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.
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.
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
}
}
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)
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
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"];
}