Déserializer plusieurs fichier json

Salut la team, je souhaite savoir comment faire pour extraire 4 différents fichier Json de open weather map, l'api se présente comme ceci

{"coord":{"lon":-6.8934,"lat":30.9189},"weather":[{"id":800,"main":"Clair","description":"ciel dégagé"," icon":"01n"}],"base":"stations","main":{"temp":292.31,"feels_like":290.78,"temp_min":292.31,"temp_max":292.31,"pression": 1025,"humidity":19},"visibility":10000,"wind":{"speed":1.03,"deg":0},"clouds":{"all":0},"dt":1666650783 ,"sys":{"type":1,"id":2412,"country":"MA","sunrise":1666593375,"sunset":1666633622},"timezone":3600,"id":2540850 ","name":"Ouarzazate","cod":200}

Je souhaite extraire : dt, sunrise, sunset et description
Ils sont éparpillés un peu partout donc je suis obligé de tout prendre, mais je sais pas comment m'y prendre, voilà ou j'en suis

  String cmd ="GET /data/2.5/weather?q="+ville+"&appid=xxxxxxxxxxxxx3ad061374";
  envoieAuESP8266(cmd);
  if (! Serial1.find("{\"coord\":")) {} 
  while (i<60000) {
    if(Serial1.available()) {
      String json = "{\"coord\":";
      char c = Serial1.read();
      json=json+c;
      if(c=='}'){
        Serial.println ("fichier json trouvé");
        affiche_lcd ("fichier json","trouvé");
      break;}
     
     
      i=0;
     
    }
    i++;
  }
StaticJsonDocument<768> doc;

DeserializationError error = deserializeJson(doc, json);

if (error) {
  Serial.print(F("deserializeJson() failed: "));
  Serial.println(error.f_str());
  return;
}
  const char* description = doc["description"];
  long dt = doc["dt"];
  long sys_sunrise = doc["sunrise"];
  long sys_sunset = doc["sunset"];
  delay(3000);

Le problème c'est que évidemment il s'arrête à la première accolade qu'il rencontre, comment faire pour qu'il arrive jusqu'à la dernière ? Ou qlq'un a-t-il une autre méthode ?

Bonjour @othman2007

Je ne comprends pas pourquoi 4 différents fichiers json ?

Essayez ce petit programme avec un esp 32 :
L'heure est récupérée sur un serveur NTP et est mise à jour automatiquement aux passages à l'heure d'été et d'hiver.
Vous avez juste à vous inscrire sur openweathermap afin d'obtenir une appid gratuitement et la renseigner dans mon programme ainsi que vos paramètres wifi.
il vous faudra également mentionner votre latitude et longitude en degrés décimaux.

#include <WiFiClientSecure.h> //
#include <WiFi.h> // wifi
#include <HTTPClient.h>  // Navigateur web
#include <ArduinoJson.h>        // Installer la version 6 

byte decalage = 2; //decalage horaire été-hiver pour sunrise et sunset

const char* ssid       = "..........";
const char* password   = "............";

//heure
const int delaiDemande = 30; // nombre de secondes entre deux demandes consécutives au serveur NTP
unsigned long derniereDemande = millis(); // moment de la plus récente demande au serveur NTP
unsigned long derniereMaJ = millis(); // moment de la plus récente mise à jour de l'affichage de l'heure
#define MAX_SIZE 80
const char* ntpServer = "pool.ntp.org";
time_t timestamp;
struct tm *pTime;

//instanse json + requête
StaticJsonDocument<1024> doc;
String appid = "...................."; //id api openweathermap
String lon = "......................";
String lat = "......................";
const String request = "https://api.openweathermap.org/data/2.5/weather?lat=" + lat + "&lon=" + lon + "&units=metric&lang=fr&appid=" + appid ; // requête http

//requête HTTP
unsigned long derniereDemandeHttp = millis(); // moment de la plus récente requête HTTP
const int delaiDemandeHttp = 3600; // nombre de secondes entre deux requêtes HTTP consécutives

// déclaration fonctions et procédures
void openweathermap(void);
void afficheHeureDate(void);
void affichageJson (void);
void ephemeride (struct tm * leveCouche);
void setup_wifi(void);

void setup() {
  // Initialise serial + Wifi
  Serial.begin(115200);
  Serial.println();
  setup_wifi(); // Initialise Wifi

  openweathermap();

  // heure serveur NTP
  configTzTime("CET-1CEST-2,M3.5.0/02:00:00,M10.5.0/03:00:00", ntpServer);
  Serial.println("");
  Serial.print("Attente date et heure");
  while (time(nullptr) <= 100000) {
    Serial.print(".");
    delay(1000);
  }
  time(&timestamp);
  Serial.println("");

}

void loop() {
  // est-ce le moment d'effectuer la requête HTTP
  if ((millis() - derniereDemandeHttp) >=  delaiDemandeHttp * 1000 ) {
    openweathermap();
    derniereDemandeHttp = millis();
    Serial.println("requête HTTP");
  }

  // est-ce le moment de raffraichir l'heure NTP ?
  if ((millis() - derniereMaJ) >=   1000 ) {
    time(&timestamp);
    afficheHeureDate();
    derniereMaJ = millis();
    Serial.println("Affichage de l'heure");
  }

}

void setup_wifi(void) { // initialise wifi
  WiFi.begin(ssid, password);
  Serial.print("Connexion au reseau WiFi");
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print(".");
    delay(1000);
  }
  Serial.println();
}

void afficheHeureDate(void) {
  char buffer[MAX_SIZE];
  pTime  = localtime(&timestamp);
  strftime(buffer, MAX_SIZE, "heure : %H:%M:%S\ndate : %d/%m/%Y", pTime);
  Serial.println(buffer);
}

void openweathermap(void) {
  WiFiClientSecure client;
  client.setInsecure();
  HTTPClient http;
  http.useHTTP10(true);
  http.begin(client, request); // on prépare la requête
  int httpCode = http.GET(); // on lance la requête
  if (httpCode == 200 ) { // le serveur a répondu 200 (connexion réussie)
    String reponse = http.getString();
    Serial.println(reponse);
    deserializeJson(doc, reponse);
    affichageJson() ;
  } else {
    Serial.print("Error - HTTP code : ");
    Serial.println(httpCode);
  }
  http.end();
}

void affichageJson (void) {

  //sunrise
  time_t leveSoleil = doc["sys"]["sunrise"];
  struct tm *leveDuSoleil = localtime(&leveSoleil);
  Serial.print("Lever du soleil : ");
  ephemeride(leveDuSoleil);


  // sunset
  time_t coucheSoleil = doc["sys"]["sunset"];
  struct tm *coucheDuSoleil = localtime(&coucheSoleil);
  Serial.print("Coucher du soleil : ");
  ephemeride(coucheDuSoleil);

}

void ephemeride (struct tm * leveCouche) {
  if ((leveCouche->tm_hour ) < 10) {
    Serial.print("0");
  }
  Serial.print(leveCouche->tm_hour + decalage );  // heure entre 0 et 23
  Serial.print(":");
  if (leveCouche->tm_min < 10) {
    Serial.print("0");
  }
  Serial.print(leveCouche->tm_min);   // timeinfo->tm_min: minutes (0 - 59)
  Serial.print(":");

  if (leveCouche->tm_sec < 10) {
    Serial.print("0");
  }
  Serial.println(leveCouche->tm_sec);   // timeinfo->tm_sec: secondes (0 - 60)
}

Bonne journée.

C'est un esp 01 que j'utilise, je le commande via les commandes AT, je n'ai pas vraiment besoin de l'heure exacte mais juste de connaître si c'est la nuit ou le jour
Il faut donc voir si dt est compris entre sun_rise et sun_set,

@othman2007
Désolé, je suis débutant et j'ai tellement à apprendre que je ne connais pas encore les commandes AT.
Sinon ce programme fonctionnera sur esp32 et esp 8266 :

#include <WiFiClientSecure.h>  
#include <ArduinoJson.h>  
#ifdef ESP32
#include <WiFi.h>
#include <HTTPClient.h>
#else
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h> 
#endif

byte decalage = 2; //decalage horaire été-hiver pour sunrise et sunset

const char* ssid       = "..........";
const char* password   = "............";

//heure
const int delaiDemande = 30; // nombre de secondes entre deux demandes consécutives au serveur NTP
unsigned long derniereDemande = millis(); // moment de la plus récente demande au serveur NTP
unsigned long derniereMaJ = millis(); // moment de la plus récente mise à jour de l'affichage de l'heure
#define MAX_SIZE 80
const char* ntpServer = "pool.ntp.org";
time_t timestamp;
struct tm *pTime;

//instanse json + requête
StaticJsonDocument<1024> doc;
String appid = "...................."; //id api openweathermap
String lon = "......................";
String lat = "......................";
const String request = "https://api.openweathermap.org/data/2.5/weather?lat=" + lat + "&lon=" + lon + "&units=metric&lang=fr&appid=" + appid ; // requête http

//requête HTTP
unsigned long derniereDemandeHttp = millis(); // moment de la plus récente requête HTTP
const int delaiDemandeHttp = 3600; // nombre de secondes entre deux requêtes HTTP consécutives

// déclaration fonctions et procédures
void openweathermap(void);
void afficheHeureDate(void);
void affichageJson (void);
void ephemeride (struct tm * leveCouche);
void setup_wifi(void);

void setup() {
  // Initialise serial + Wifi
  Serial.begin(115200);
  Serial.println();
  setup_wifi(); // Initialise Wifi

  openweathermap();

  // heure serveur NTP
  configTzTime("CET-1CEST-2,M3.5.0/02:00:00,M10.5.0/03:00:00", ntpServer);
  Serial.println("");
  Serial.print("Attente date et heure");
  while (time(nullptr) <= 100000) {
    Serial.print(".");
    delay(1000);
  }
  time(&timestamp);
  Serial.println("");

}

void loop() {
  // est-ce le moment d'effectuer la requête HTTP
  if ((millis() - derniereDemandeHttp) >=  delaiDemandeHttp * 1000 ) {
    openweathermap();
    derniereDemandeHttp = millis();
    Serial.println("requête HTTP");
  }

  // est-ce le moment de raffraichir l'heure NTP ?
  if ((millis() - derniereMaJ) >=   1000 ) {
    time(&timestamp);
    afficheHeureDate();
    derniereMaJ = millis();
    Serial.println("Affichage de l'heure");
  }

}

void setup_wifi(void) { // initialise wifi
  WiFi.begin(ssid, password);
  Serial.print("Connexion au reseau WiFi");
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print(".");
    delay(1000);
  }
  Serial.println();
}

void afficheHeureDate(void) {
  char buffer[MAX_SIZE];
  pTime  = localtime(&timestamp);
  strftime(buffer, MAX_SIZE, "heure : %H:%M:%S\ndate : %d/%m/%Y", pTime);
  Serial.println(buffer);
}

void openweathermap(void) {
  WiFiClientSecure client;
  client.setInsecure();
  HTTPClient http;
  http.useHTTP10(true);
  http.begin(client, request); // on prépare la requête
  int httpCode = http.GET(); // on lance la requête
  if (httpCode == 200 ) { // le serveur a répondu 200 (connexion réussie)
    String reponse = http.getString();
    Serial.println(reponse);
    deserializeJson(doc, reponse);
    affichageJson() ;
  } else {
    Serial.print("Error - HTTP code : ");
    Serial.println(httpCode);
  }
  http.end();
}

void affichageJson (void) {

  //sunrise
  time_t leveSoleil = doc["sys"]["sunrise"];
  struct tm *leveDuSoleil = localtime(&leveSoleil);
  Serial.print("Lever du soleil : ");
  ephemeride(leveDuSoleil);


  // sunset
  time_t coucheSoleil = doc["sys"]["sunset"];
  struct tm *coucheDuSoleil = localtime(&coucheSoleil);
  Serial.print("Coucher du soleil : ");
  ephemeride(coucheDuSoleil);

}

void ephemeride (struct tm * leveCouche) {
  if ((leveCouche->tm_hour ) < 10) {
    Serial.print("0");
  }
  Serial.print(leveCouche->tm_hour + decalage );  // heure entre 0 et 23
  Serial.print(":");
  if (leveCouche->tm_min < 10) {
    Serial.print("0");
  }
  Serial.print(leveCouche->tm_min);   // timeinfo->tm_min: minutes (0 - 59)
  Serial.print(":");

  if (leveCouche->tm_sec < 10) {
    Serial.print("0");
  }
  Serial.println(leveCouche->tm_sec);   // timeinfo->tm_sec: secondes (0 - 60)
}

Mais j'ai bien peur qu'il ne vous soit pas utile.

A partir du moment où vous avez :

  • l'heure et la date ;
  • sun_rise ;
  • sun_set.

Vous avez tous les éléments pour savoir si il fait jour ou nuit.
Mais désolé pour les commandes AT, je ne connais pas encore.
Sinon vous avez aussi la bibliothèque de @bricoleau
https://forum.arduino.cc/t/calcul-ephemeride-precis-lever-et-coucher-de-soleil/266532

Je viens de regarder à quoi correspond un esp 01 et les commandes AT.
C'est pas mon truc. Je préfère les esp32 qui ne sont pas chers du tout sur aliexpress (voir les esp8266 encore moins chers) !
Après, il y aura peut-être quelqu'un qui connait bien votre configuration et qui passera par là.

Bonne journée.

Ce ne sont pas des fichiers que tu veux extraire mais des champs.
Il me semble que l'on a déjà eu cette discussion au paravent. Pour manipuler un fichier json, le plus simple c'est d'utiliser la librairie ArduinoJson

Tu rentres l'URL de ta requête dans le simulateur sur le site, tu réponds à quelques questions sur ta configuration matérielle et il te sort un code type que tu n'as plus qu'à recopier et adapter à ton besoin.

Rien à voir avec les commandes AT mais je ne connaissais pas vraiment le champ dt, il s'agit donc de l'heure de calcul des données en UTC. Du coup j'ai rajouté une procédure void jourNuit () à mon programme pour esp 8266 et esp32. Je pense qu'elle renvoie correctement si c'est le jour ou la nuit :

#include <WiFiClientSecure.h>
#include <ArduinoJson.h>
#ifdef ESP32
#include <WiFi.h>
#include <HTTPClient.h>
#else
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#endif

byte decalage = 2; //decalage horaire été-hiver pour sunrise et sunset


const char* ssid       = ".....................";
const char* password   = ".................";

//heure
const int delaiDemande = 30; // nombre de secondes entre deux demandes consécutives au serveur NTP
unsigned long derniereDemande = millis(); // moment de la plus récente demande au serveur NTP
unsigned long derniereMaJ = millis(); // moment de la plus récente mise à jour de l'affichage de l'heure
#define MAX_SIZE 80
const char* ntpServer = "pool.ntp.org";
time_t timestamp;
struct tm *pTime;

//instanse json + requête
StaticJsonDocument<1024> doc;
String appid = "................................"; //id api openweathermap
String lon = "..........................";
String lat = "............................";
const String request = "https://api.openweathermap.org/data/2.5/weather?lat=" + lat + "&lon=" + lon + "&units=metric&lang=fr&appid=" + appid ; // requête http

//requête HTTP
unsigned long derniereDemandeHttp = millis(); // moment de la plus récente requête HTTP
const int delaiDemandeHttp = 3600; // nombre de secondes entre deux requêtes HTTP consécutives

// déclaration fonctions et procédures
void openweathermap(void);
void afficheHeureDate(void);
void affichageJson (void);
void ephemeride (struct tm * leveCouche);
void setup_wifi(void);


time_t dt;
time_t leveSoleil;
time_t coucheSoleil;

void setup() {
  // Initialise serial + Wifi
  Serial.begin(115200);
  Serial.println();
  setup_wifi(); // Initialise Wifi

  openweathermap();

  // heure serveur NTP
  configTzTime("CET-1CEST-2,M3.5.0/02:00:00,M10.5.0/03:00:00", ntpServer);
  Serial.println("");
  Serial.print("Attente date et heure");
  while (time(nullptr) <= 100000) {
    Serial.print(".");
    delay(1000);
  }
  time(&timestamp);
  Serial.println("");

}

void loop() {
  // est-ce le moment d'effectuer la requête HTTP
  if ((millis() - derniereDemandeHttp) >=  delaiDemandeHttp * 1000 ) {
    openweathermap();
    derniereDemandeHttp = millis();
    Serial.println("requête HTTP");
  }

  // est-ce le moment de raffraichir l'heure NTP ?
  if ((millis() - derniereMaJ) >=   1000 ) {
    time(&timestamp);
    afficheHeureDate();
    derniereMaJ = millis();
    Serial.println("Affichage de l'heure");
  }

}

void setup_wifi(void) { // initialise wifi
  WiFi.begin(ssid, password);
  Serial.print("Connexion au reseau WiFi");
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print(".");
    delay(1000);
  }
  Serial.println();
}

void afficheHeureDate(void) {
  char buffer[MAX_SIZE];
  pTime  = localtime(&timestamp);
  strftime(buffer, MAX_SIZE, "heure : %H:%M:%S\ndate : %d/%m/%Y", pTime);
  Serial.println(buffer);
}

void openweathermap(void) {
  WiFiClientSecure client;
  client.setInsecure();
  HTTPClient http;
  http.useHTTP10(true);
  http.begin(client, request); // on prépare la requête
  int httpCode = http.GET(); // on lance la requête
  if (httpCode == 200 ) { // le serveur a répondu 200 (connexion réussie)
    String reponse = http.getString();
    Serial.println(reponse);
    deserializeJson(doc, reponse);
    affichageJson() ;
  } else {
    Serial.print("Error - HTTP code : ");
    Serial.println(httpCode);
  }
  http.end();
}

void affichageJson (void) {


  //dt Heure de calcul des données, unix, UTC
  dt = doc["dt"];
  struct tm *dateHeure = localtime(&dt);
  Serial.print("heure date : ");
  ephemeride(dateHeure);

  //sunrise
  leveSoleil = doc["sys"]["sunrise"];
  struct tm *leveDuSoleil = localtime(&leveSoleil);
  Serial.print("Lever du soleil : ");
  ephemeride(leveDuSoleil);


  // sunset
  coucheSoleil = doc["sys"]["sunset"];
  struct tm *coucheDuSoleil = localtime(&coucheSoleil);
  Serial.print("Coucher du soleil : ");
  ephemeride(coucheDuSoleil);

  jourNuit();
}

void ephemeride (struct tm * dtLeveCouche) {
  if ((dtLeveCouche->tm_hour ) < 10) {
    Serial.print("0");
  }
  Serial.print(dtLeveCouche->tm_hour + decalage );  // heure entre 0 et 23
  Serial.print(":");
  if (dtLeveCouche->tm_min < 10) {
    Serial.print("0");
  }
  Serial.print(dtLeveCouche->tm_min);   // timeinfo->tm_min: minutes (0 - 59)
  Serial.print(":");

  if (dtLeveCouche->tm_sec < 10) {
    Serial.print("0");
  }
  Serial.println(dtLeveCouche->tm_sec);   // timeinfo->tm_sec: secondes (0 - 60)
}

void jourNuit () {
  ((dt >= leveSoleil) && (dt <= coucheSoleil) ) ? Serial.println("jour") : Serial.println("nuit");
}

Voilà j'espère ne pas polluer ce fil de discussion.
Bonne journée.

Oui on a effectivement déjà eu 2 autres discussions, je n'ai pas actuellement un problème avec la déserialization parce que pour la vitesse du vent je mettais un Serial. find qui cherchait "wind" :, puis je précisais que le Json commençais ici et doit s'arrêter à la prochaine accolade, mtn y'a aucun problème sur ça,
Ce pendant je ne peux pas reproduire la même procédure si je veux prendre l'intégralité des données de l'api car si je prends le Serial1.read en entier, il le prends de cette manière +IPD,469:{"coord":{"lon":-6.8934,"lat":30.9189},"weather":[{"id":800,"main":"Clair","description":"ciel dégagé"," icon":"01n"}],"base":"stations","main":{"temp":292.31,"feels_like":290.78,"temp_min":292.31,"temp_max":292.31,"pression": 1025,"humidity":19},"visibility":10000,"wind":{"speed":1.03,"deg":0},"clouds":{"all":0},"dt":1666650783 ,"sys":{"type":1,"id":2412,"country":"MA","sunrise":1666593375,"sunset":1666633622},"timezone":3600,"id":2540850 ","name":"Ouarzazate","cod":200}0,CLOSED
Ce que je veux c'est retirer le +IPD, 469: ainsi que le 0,CLOSED et ne prendre que ce qui se trouve entre la première et dernière accolade

À présent je travaille avec un esp 01, un programme avec des commandes AT me semble beaucoup plus simple, mais de toute façon lorsque je finaliserai mon projet de A à Z avec mon Esp 01 je basculerai vers l'esp 32 pour de meilleurs performances et à ce moment, une grande convertion au niveau du code programme serait inévitable :wink:

OK,
Après la procédure peut très bien devenir une fonction boolean
Tout est possible :wink:
Bon après midi

Salut

Je vois une virgule de trop après 2540850, mais en supprimant cette virgule dans l'assistant ArduinoJSON, la taille requise pour le buffer est de 768 octets, comme dans ton code. Je suppose donc que c'est une erreur de copier/coller.
Une remarque quand même : 768 octets représentent quand même une bonne partie de la RAM d'un ARDUINO. Le projet sera limité en taille.

Idem . Même avec un petit ESP8266 le confort sera incomparable, même si le buffer à allouer doit avoir une taille supérieure (1024 octets).

Pas vraiment, puisque sur un esp8266, 01 ou pas, peux exécuter directement le code que tu exécute sur ton Arduino.
Là tu es obligé de passer par un tas de commande AT, avec peu de retour.

Si en plus a la fin tu veux passer sur un esp32, pourquoi s'embêter à passer par d'austére commandes?

Pourquoi ne passe tu pas par la librairie que t'a proposer @fdufnews
Sinon je n'ai pas compris pourquoi tu ne peux passer par un find, pour rechercher tes autres valeurs?

Je passe effectivement par la librairie arduino Json, mais l'input doit toujours être conforme à la structure Json, je ne peux pas passer par le find dans ce cas car la valeur qui est après +IPD change constamment et bien d'autres soucis que j'ai mentionné en haut
À présent j'ai utiliser un substring () et ça a marché, mais il n'arrive pas à déserializer la description du weather, il me retourne un "null"

Si tu as enlève le début et la fin, c'est à dire que ton json commence et fini par une accolade ?

Tu peux redonner le json que tu essaye de passer?
Tu as corrigé l'erreur remonté par @hbachetti , car dans un parser online, tout est ok

Sur un ARDUINO, plus tu utilises de String et subString, plus les risques de fragmentation mémoire augmentent. Avec un ESP8266 ou ESP32, ce risque est sérieusement limité.

Oui ça commence avec une accolade et se termine par une autre, l'erreur mentionné par @hbachetti n'était qu'une erreur de copié collé, c bon ça à marché fallait sortir le Json objet root d'abord puis le réparser

Honnêtement j'ai pas bien compris, si cela peut causer un problème durant l'exécution de mon programme est je un moyen pour y remédier ?

@hbachetti a entièrement raison.

Oui soit en travaillant avec des tableaux de char au lieu de la classe String soit en achetant un ESP32 qui ne coûte pas très cher ou Le mieux serait le choix des tableaux de char et de l'ESP32...

J'en profite pour vous remettre mon code corrigé de quelques incohérences :

#include <WiFiClientSecure.h>
#include <ArduinoJson.h>
#ifdef ESP32
#include <WiFi.h>
#include <HTTPClient.h>
#else
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#endif

byte decalage = 2; //decalage horaire été-hiver pour sunrise et sunset

const char* ssid       = ".........";
const char* password   = ".................";

//heure
unsigned long derniereDemande = millis(); // moment de la plus récente demande au serveur NTP
unsigned long derniereMaJ = millis(); // moment de la plus récente mise à jour de l'affichage de l'heure
#define MAX_SIZE 80
const char* ntpServer = "pool.ntp.org";
time_t timestamp;
struct tm *pTime;

//instanse json + requête
StaticJsonDocument<1024> doc;
String appid = "................................."; //id api openweathermap
String lon = "..............................";
String lat = "..................................";
const String request = "https://api.openweathermap.org/data/2.5/weather?lat=" + lat + "&lon=" + lon + "&units=metric&lang=fr&appid=" + appid ; // requête http

//requête HTTP
unsigned long derniereDemandeHttp = millis(); // moment de la plus récente requête HTTP
const int delaiDemandeHttp = 660; // nombre de secondes entre deux requêtes HTTP consécutives

// déclaration fonctions et procédures
void openweathermap(void);
void afficheHeureDate(void);
void affichageJson (void);
void ephemeride (struct tm * dtLeveCouche);
void setup_wifi(void);
void jourNuit (time_t d, time_t leve, time_t couche);

void setup() {
  // Initialise serial + Wifi
  Serial.begin(115200);
  Serial.println();
  setup_wifi(); // Initialise Wifi

  openweathermap();

  // heure serveur NTP
  configTzTime("CET-1CEST-2,M3.5.0/02:00:00,M10.5.0/03:00:00", ntpServer);
  Serial.println("");
  Serial.print("Attente date et heure");
  while (time(nullptr) <= 100000) {
    Serial.print(".");
    delay(1000);
  }
  time(&timestamp);
  Serial.println("");

}

void loop() {
  // est-ce le moment d'effectuer la requête HTTP
  if ((millis() - derniereDemandeHttp) >=  delaiDemandeHttp * 1000 ) {
    openweathermap();
    derniereDemandeHttp = millis();
    Serial.println("requête HTTP");
  }

  // est-ce le moment de raffraichir l'heure NTP ?
  if ((millis() - derniereMaJ) >=   1000 ) {
    time(&timestamp);
    afficheHeureDate();
    derniereMaJ = millis();
    Serial.println("Affichage de l'heure");
  }

}

void setup_wifi(void) { // initialise wifi
  WiFi.begin(ssid, password);
  Serial.print("Connexion au reseau WiFi");
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print(".");
    delay(1000);
  }
  Serial.println();
}

void afficheHeureDate(void) {
  char buffer[MAX_SIZE];
  pTime  = localtime(&timestamp);
  strftime(buffer, MAX_SIZE, "heure : %H:%M:%S\ndate : %d/%m/%Y", pTime);
  Serial.println(buffer);
}

void openweathermap(void) {
  WiFiClientSecure client;
  client.setInsecure();
  HTTPClient http;
  http.useHTTP10(true);
  http.begin(client, request); // on prépare la requête
  int httpCode = http.GET(); // on lance la requête
  if (httpCode == 200 ) { // le serveur a répondu 200 (connexion réussie)
    String reponse = http.getString();
    Serial.println(reponse);
    deserializeJson(doc, reponse);
    affichageJson() ;
  } else {
    Serial.print("Error - HTTP code : ");
    Serial.println(httpCode);
  }
  client.stop();
  http.end();
  
}

void affichageJson (void) {


  //dt Heure de calcul des données, unix, UTC
  time_t dt = doc["dt"];
  struct tm *dateHeure = gmtime(&dt);
  Serial.print("heure date : ");
  ephemeride(dateHeure);

  //sunrise
  time_t leveSoleil = doc["sys"]["sunrise"];
  struct tm *leveDuSoleil = gmtime(&leveSoleil);
  Serial.print("Lever du soleil : ");
  ephemeride(leveDuSoleil);

  // sunset
  time_t coucheSoleil = doc["sys"]["sunset"];
  struct tm *coucheDuSoleil = gmtime(&coucheSoleil);
  Serial.print("Coucher du soleil : ");
  ephemeride(coucheDuSoleil);

  jourNuit(dt, leveSoleil, coucheSoleil);
}

void ephemeride (struct tm * dtLeveCouche) {
  if ((dtLeveCouche->tm_hour + decalage) < 10) {
    Serial.print("0");
  }
  Serial.print(dtLeveCouche->tm_hour + decalage );  // heure entre 0 et 23
  Serial.print(":");
  if (dtLeveCouche->tm_min < 10) {
    Serial.print("0");
  }
  Serial.print(dtLeveCouche->tm_min);   // timeinfo->tm_min: minutes (0 - 59)
  Serial.print(":");

  if (dtLeveCouche->tm_sec < 10) {
    Serial.print("0");
  }
  Serial.println(dtLeveCouche->tm_sec);   // timeinfo->tm_sec: secondes (0 - 60)
}

void jourNuit (time_t d, time_t leve, time_t couche) {
  Serial.println(d);
  Serial.println(leve);
  Serial.println(couche);
  ((d >= leve) && (d <= couche) ) ? Serial.println("Il fait jour") : Serial.println("il fait nuit");
}

Toutes les 11 minutes, une nouvelle requête HTTP fournit un nouveau fichier json à jour (j'ai remarqué que les requêtes ne renvoyaient des données à jour que toutes les 10 minutes environ). La procédure void jourNuit (time_t d, time_t leve, time_t couche) permet de déterminer si il fait jour ou nuit mais elle le fait à partir du fichier json mis à jour toutes les 11 minutes. Pour plus de précision, il vous faudra travailler à partir de l'heure locale !

Bonne jounée.

EDIT deux fois.

Avec un ARDUINO, non. L'allocation d'une String peut échouer quand la mémoire RAM est fragmentée, à n'importe quel moment, y compris au bout de plusieurs minutes, heures, ou jours de fonctionnement. L'allocation peut même être partielle, et renvoyer une chaîne de caractère plus courte que prévu.