[Résolu] ESP32 Wificlient getstream et téléchargements aux résultats aléatoires

Bonjour,

j'ai une fonction sur un ESP32-S3 qui télécharge un fichier a partir d'une Url et l'enregistre une carte SD (configurée en SPI)

le souci que j'ai, c'est que l'écriture du fichier sur la carte SD est "aléatoire". Parfois le fichier fait la bonne taille, parfois (plutot souvent ^^) le fichier est tronqué. Dans le cas d'un MP3, le fichier tronqué n'est pas corrompu, il est lisible mais coupe avant la fin prévue.

voici la fonction :

void download(String url, File file)
{
    HTTPClient http;
    int temp_ = millis();

    http.begin(url);
    http.setFollowRedirects(HTTPC_FORCE_FOLLOW_REDIRECTS);
    
    // Entêtes spécifiques
    http.addHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36");
    http.addHeader("Accept", "audio/*, */*;q=0.8");
    http.addHeader("Connection", "close");

    int httpcode = http.GET();

    if (httpcode <= 0) {
        Serial.println("[HTTP] GET failed");
        Serial.print("Error code: ");
        Serial.println(httpcode);
        http.end();
        return;
    }

    int file_size = http.getSize();
    Serial.print("Size : ");
    Serial.println(file_size);



    WiFiClient* stream = http.getStreamPtr();


    static uint8_t buff[128]; 
    int len = file_size;
    float full = 0;
    unsigned long lastProgressUpdate = millis();
    unsigned long startTime = millis();

    Serial.println("Starting download...");
    Serial.print("Expected size: ");
    Serial.print(file_size / 1000000.0, 2);
    Serial.println(" MB");

    while (http.connected() && (len > 0 || len == -1)) {
        size_t size = stream->available();
        
        if (size > 0) {
            //int toRead = min(size, sizeof(buff));
            int c = stream->readBytes((char*)buff, sizeof(buff));
            
            if (c > 0) {
                file.write(buff, c);
                full += c;
                if (millis() - lastProgressUpdate > 1000) {
                    Serial.print("Downloaded: ");
                    Serial.print(full / 1000000, 1);
                    Serial.println(" MB");
                    lastProgressUpdate = millis();
                }

                if (len > 0) len -= c;
              /*                  // Reset du watchdog toutes les 5 secondes
                if (millis() - startTime > 5000) {
                    yield(); // Permet au système de reset le watchdog
                }*/
            }
        } else {
            delay(2);
        }
    }

    file.flush();
    http.end();

    int temp2 = millis();
    int diff = (temp2 - temp_) / 1000;

    Serial.println("Download finished");
    Serial.print("Diff : ");
    Serial.println(diff);
    
   
    
    // Vérification finale
    if (file_size > 0 && file.size() != file_size) {
        Serial.println("WARNING: File size mismatch!");
        Serial.print("Expected: "); Serial.print(file_size); 
        Serial.print(", Got: "); Serial.println(file.size());
    } else if (file_size > 0) {
        Serial.println("Download completed successfully!");
    }
}

Il y a un coté aléatoire, voici par exemple le retour sur le Serial de 3 téléchargement du meme fichier à la suite :

/Quand l'impossible devient possible, à quand le déclic - .mp3Size : 4836347
Starting download...
Expected size: 4.84 MB
Downloaded: 0.6 MB
Downloaded: 1.3 MB
Downloaded: 2.0 MB
Downloaded: 2.7 MB
Downloaded: 3.4 MB
Downloaded: 4.1 MB
Download finished
Diff : 8
WARNING: File size mismatch!
Expected: 4836347, Got: 4583424
/Quand l'impossible devient possible, à quand le déclic - .mp3Size : 4836347
Starting download...
Expected size: 4.84 MB
Downloaded: 0.6 MB
Downloaded: 1.5 MB
Downloaded: 2.4 MB
Downloaded: 3.2 MB
Downloaded: 4.0 MB
Download finished
Diff : 6
WARNING: File size mismatch!
Expected: 4836347, Got: 335872
/Quand l'impossible devient possible, à quand le déclic - .mp3Size : 4836347
Starting download...
Expected size: 4.84 MB
Downloaded: 0.6 MB
Downloaded: 1.2 MB
Downloaded: 2.0 MB
Downloaded: 2.9 MB
Downloaded: 3.7 MB
Downloaded: 4.6 MB
Download finished
Diff : 7
WARNING: File size mismatch!
Expected: 4836347, Got: 1097728
/Quand l'impossible devient possible, à quand le déclic - .mp3Size : 4836347
Starting download...
Expected size: 4.84 MB
Downloaded: 0.6 MB
Downloaded: 1.1 MB
Downloaded: 1.7 MB
Downloaded: 2.3 MB
Downloaded: 2.8 MB
Downloaded: 3.4 MB
Downloaded: 3.8 MB
Downloaded: 4.3 MB
Download finished
Diff : 9
Download completed successfully!

Le comportement typique d'un téléchargement qui n'abouti pas c'est une vitesse constante, et on observe soudainement une brusque augmentation de la vitesse de téléchargement. La taille finale du fichier correspond au moment du basculement entre les 2 vitesses.

Ce que j'ai testé :

  • Jouer sur la taille du buffer : Je suis allé du très petite (64) au très gros (16384). Pour le moment la taille qui a eu le plus téléchargement correcte semble etre 128
  • jouer sur la vitesse du port SPI (de 4 a 80 Mhz) mis a part jouer sur la vitesse de téléchargement, je n'ai pas remarqué d'effet sur le succès ou non du téléchargement
  • appeler régulièrement un .flush() dans la boucle pour forcer l'écriture
  • j'ai rapidement testé la fonction : http.writeToStream(&file); qui doit gérer la taille du buffer de façon automatique et éviter les erreur. Résutlat : Même sur les petits fichier, ça n'abouti par, la taille finale du fichier varie de façon aléatoire.

La seule tendance que j'observe que quand une combinaison de tout ça semble fonctionner, les fichiers de qlq Mo

ce qui ne n'ai pas testé :

  • des requettes en httpS

Si quelqu'un a une idée de l'origine du problème, je suis preneur. Merci !

Pouvez-vous ajouter ces deux lignes juste après la fin de la boucle while, avant le file.flush() ?

Serial.print("Loop exit - len remaining: "); Serial.println(len);
Serial.print("Loop exit - connected: "); Serial.println(http.connected());

Cela permettra de savoir si la boucle s'est arrêtée dans les cas d'échec : si len est supérieur à 0 et connected vaut 0, c'est confirmé que la connexion se ferme avant la fin du transfert.

Bonjour,

voici le retour après avoir rajouté ces deux lignes :

/Quand l'impossible devient possible, à quand le déclic - .mp3Size : 4115799
Starting download...
Expected size: 4.12 MB
Downloaded: 0.7 MB
Downloaded: 1.6 MB
Downloaded: 2.9 MB
Downloaded: 4.1 MB
Loop exit - len remaining: 0
Loop exit - connected: 1
Download finished
Diff : 5
WARNING: File size mismatch!
Expected: 4115799, Got: 880640

donc si je comprends bien, la connexion ne s'est pas fermée prématurément

un autre log qui montre la taille de données reçues VS la taille du fichier écrit. Pour ça j'ai du changer un peu le code d'origine et rajouter un flush() dans la boucle de progression .

/Dix idées pour résister - Un conseil des générations futures (10-10) .mp3Size : 16293856 Starting download... Expected size: 16.29 MB
Data received : 462848.00 / Data writed : 462848
Data received : 950295.00 / Data writed : 950295
Data received : 1376279.00 / Data writed : 1376279
Data received : 1999895.00 / Data writed : 1421824
Data received : 2671639.00 / Data writed : 1421824
Data received : 3326999.00 / Data writed : 1421824
Data received : 3970199.00 / Data writed : 1421824
Data received : 4637719.00 / Data writed : 1421824
Data received : 5309463.00 / Data writed : 1421824
Data received : 5806743.00 / Data writed : 1421824
Data received : 6243351.00 / Data writed : 1421824
Data received : 6865943.00 / Data writed : 1421824
Data received : 7455767.00 / Data writed : 1421824
Data received : 8143895.00 / Data writed : 1421824
Data received : 8815639.00 / Data writed : 1421824
Data received : 9520151.00 / Data writed : 1421824
Data received : 10208279.00 / Data writed : 1421824
Data received : 10896407.00 / Data writed : 1421824
Data received : 11584535.00 / Data writed : 1421824
Data received : 12289047.00 / Data writed : 1421824
Data received : 13042711.00 / Data writed : 1421824
Data received : 14238743.00 / Data writed : 1421824
Data received : 15057943.00 / Data writed : 1421824
Data received : 15959063.00 / Data writed : 1421824
Diff : 26
WARNING: File size mismatch!
Expected: 16293856, Got: 1421824

on voit qu'a partir du moment où il y a un loupé dans l'écriture, plus rien n'est écrit dans le fichier jusqu'a la fin de la transmission de données.

j'ai posté en parallèle sur stackexchange, et une solution a été trouvée, je mets le lien ici pour avoir les détails. Et voici le code :

   int saveData(WiFiClient* source, File* dest, int bytes) {
        size_t size = source->available();
        if (size <= bytes) {                //if data stream size smaller than buffer size
            source->readBytes(buff, size);  //feed buffer whith the right size
            dest->write(buff, size);        // save them all to file
            dest->flush();                  // blocking to ensure save is completed
            return size;                    // return the number of bytes saved
        }
        source->readBytes(buff, bytes); // read them all into buffer
        dest->write(buff, bytes);       // save them all to file
        dest->flush();                  // blocking to ensure save is completed
        return bytes;                   // return the number of bytes saved
    }
    
    void download(String url, File file) {
        HTTPClient http;
        int        temp = millis();
        http.begin(url);
        http.setFollowRedirects(HTTPC_FORCE_FOLLOW_REDIRECTS);
    
        // Entêtes spécifiques
        http.addHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36");
        http.addHeader("Accept", "audio/*, */*;q=0.8");
        http.addHeader("Connection", "close");
    
        int httpcode = http.GET();
    
        if (httpcode <= 0) {
            Serial.printf("[HTTP] GET failed, error=%d\n", httpcode);
            http.end();
            return;
        }
    
        int file_size = http.getSize();
        Serial.printf("Size : %d bytes\n", file_size);
    
        WiFiClient* stream = http.getStreamPtr();
    
        bool downloadCompleted = false;
        int  full = 0;
    
        while (file_size - full > 0) {
            full += saveData(stream, &file, ONE_CHUNK);
        }
    
        downloadCompleted = true;
        int temp2 = millis();
        Serial.println("Download finished");
        Serial.printf("Expected: %d, Got: %d\n", file_size, full);
        if (file.size() != file_size) {
            Serial.println("WARNING: File size mismatch!");
        } else {
            Serial.println("Download completed successfully!");
        }
        file.flush();
        file.close();
        http.end();
        Serial.print("diff : ");
        Serial.println((temp2 - temp) / 1000);
    }

Merci pour le retour

le code initiale est fait avec une taille de buffer de 512. Ca fonctionne a coup sur, mais c'est terriblement lent (une 100aine de Ko/s). Il faut augmenter progressivement la taille du buffer, et jouer sur la vitesse du port SPI qui semble aussi avoir influence sur la vitesse et le succes ou non des téléchargements. Là avec un buffer de 1024, et un SPI a 20 Mhz, j'ai une vitesse de 184Ko/s et ça passe avec des fichiers de 75Mo

j'ai plus facilement de succès a plus haute vitesse sur des podcast hébergés sur acast que sur ceux de radiofrance ... mais là je crois que ce n'est pas forcément fondé ^