Chemin d'accès fichier SD trop long (serveur FTP)

Bonjour, j'ai un "petit" soucis lors de la tentative d'ouverture de fichier (sur la SD d'un sheild W5500 sur Arduino Méga) afin d'envoyer le dis contenu du fichier à mon serveur FTP.

Si le nom du fichier sur la SD fait moins de 8 caractères utiles (ex: NOV2023.csv soit 7 utiles +le point +3 de type = 11 + encore le caractère de terminaison de String =12) c'est OK !
Mais si le nom du fichier sur la SD fait 13 caractères en tout du genre : NOVE2023.csv ce qui est le maximum autorisé par le système de l'Arduino là j'ai un échec d'ouverture du dit fichier!

Voici le code dont je me sers:

#include <SD.h>
#include <SPI.h>
#include <Ethernet.h>

byte mac[] = { 0x90, 0xA2, 0xDA, 0x0D, 0xFE, 0xBA };  
IPAddress ip( 192, 168, 0, 32 );    
IPAddress serverMoi(192, 168, 1, 78);
EthernetClient clientserveur;
EthernetClient dclient;

char outBuf[512];
char outCount;

#define pinCS_SD 4                             // on réserve le Pin 4 pour le lecteur de SD
#define pinCS_ETH  10                          // on affecte le pin 10
Sd2Card card;                                     // création de l'objet Sd nommé card
File file;  
String nomfichier= "Anemo1.txt";
String nompiecejointe = "DECE2023.CSV";
boolean piecejointe =1;
int nb =0;
String repFTPinput = "/"; // dossier homedir du serveur 

void setup()
{
 Serial.begin(9600);
 delay(1000);
 Serial.println(F("debut"));

 pinMode(53, OUTPUT);                              //carte Mēga pour pouvoir accéder en même temps à la SD et à Ethernet doit être activé
  pinMode(pinCS_SD, OUTPUT);
  
  /*Test pour voir si la carte SD fonctionne */
if (!card.init(SPI_HALF_SPEED, pinCS_SD)) {
                                            Serial.println(F("Pb carte "));   
 
                                          }     
  else {Serial.println(F("Montage correct et carte présente")); } 
  
Serial.println(F("on attaque ethernet"));

digitalWrite(pinCS_SD,HIGH);// pour activer ethernet sur un Uno et mega

  Ethernet.init(pinCS_ETH);     
  delay(2000);
  int erreur = 0;                                  // initialise la variable d'erreur de démarrage du Shield Ethernet

  erreur = Ethernet.begin(mac);                     // On tente de démarrer le Shield Ethernet SANS adresse IP (donc donnée via DHCP de la box)


  if (erreur == 0) {
                    Ethernet.begin(mac, ip);                   // forcage de l'adresse ip si pas acquise par box
                     Serial.println(F("on a mis une IP fixe"));;
                  }       
  if (SD.begin(pinCS_SD)) { 
                            file = SD.open(nomfichier,FILE_READ);
                          
                                if(file == 0)
                                {
                                  Serial.println(F("Echec ouverture SD pour lecture dans le setup"));
                                }
                                else{
                                  Serial.println(F("SD ouverte pour le téléchargement"));
                                }
                            file.close();  
                             Serial.println(F("SD fermée"));
  }
  else {  Serial.println(F("la Sd ne demarre pas"));}
  Serial.println(F("fin du setup"));

} // fin du setup


//fonctions


byte eRcv()
{
  byte respCode;
  byte thisByte;

  while(!clientserveur.available()) delay(1);

  respCode = clientserveur.peek();

  outCount = 0;

  while(clientserveur.available())
  {  
    thisByte = clientserveur.read();    
    Serial.write(thisByte);

    if(outCount < 511)
    {
      outBuf[outCount] = thisByte;
      outCount++;      
      outBuf[outCount] = 0;
    }
  }

  if(respCode >= '4')
  {
    efail();
    return 0;  
  }

  return 1;
}


void efail()
{
  byte thisByte = 0;

  clientserveur.println(F("QUIT"));

  while(!clientserveur.available()) delay(1);

  while(clientserveur.available())
  {  
    thisByte = clientserveur.read();    
    Serial.write(thisByte);
  }

  clientserveur.stop();
  Serial.println(F("Commande de déconnexion"));
  file.close();
  Serial.println(F("SD fermée"));
}

byte FTPopen()
{
  Serial.println(F(" "));
  Serial.println(F("Connexion FTP -----------------"));

  if (clientserveur.connect(serverMoi,21)) {
    Serial.println(F("connectée"));
  }else {
    file.close();
    Serial.println(F("Echec de la commande de connexion"));
    return 0;
  }

  if(!eRcv()) return 0;
  clientserveur.println(F("USER mails"));
  if(!eRcv()) return 0;
  clientserveur.println(F("PASS Gilles54"));
  if(!eRcv()) return 0;
  clientserveur.println(F("SYST"));
  if(!eRcv()) return 0;
  clientserveur.println(F("PASV"));
  if(!eRcv()) return 0;

  char *tStr = strtok(outBuf,"(,");
  int array_pasv[6];
  for ( int i = 0; i < 6; i++) {
    tStr = strtok(NULL,"(,");
    array_pasv[i] = atoi(tStr);
    
    if(tStr == NULL)
    {
      Serial.println(F("Mauvaise réponse PASV "));    
    }
  }

  unsigned int hiPort,loPort;

  hiPort = array_pasv[4] << 8;
  loPort = array_pasv[5] & 255;

  Serial.print(F("Port de donnée: "));
  hiPort = hiPort | loPort;
  Serial.println(hiPort);

  if (dclient.connect(serverMoi,hiPort)) {
    Serial.println(F("connecté"));

  }else {
    Serial.println(F("Echec de connexion"));
    clientserveur.stop();
   // file.close();
    return 0;
  }
  delay(200);
}




byte FTPupload(String fileName)
{
  Serial.println(F("Téléchargement -----------------------"));
  Serial.println(fileName);

  if (SD.begin(pinCS_SD)) {
                           file = SD.open(fileName,FILE_READ);
                        
                          if(file == 0)
                          {
                            Serial.println(F("Echec ouverture SD pour lecture"));
                          }
                         else{
                            Serial.println(F("SD ouverte pour le téléchargement"));
                            Serial.print(F("chemin sur serveur : "));
                            Serial.println(repFTPinput+fileName);
                          }
                          clientserveur.print(F("STOR "));
                          clientserveur.println(repFTPinput+fileName);
  }

  if(!eRcv())
  { Serial.print(F("stop dclient "));
    dclient.stop();
    return 0;
  }


 char clientBuf[BUFSIZ];
  int clientCount = 0;

  while(file.available())
  {
    clientBuf[clientCount] = file.read();
    clientCount++;

    if(clientCount > BUFSIZ-1)
    {
     dclient.write(clientBuf,BUFSIZ);
     clientCount = 0;
   }
  }

  if(clientCount > 0) dclient.write(clientBuf,clientCount);
  Serial.println(F("Fichier téléchargé"));

  delay(200);
  return 1;
}


byte FTPclose()
{
  Serial.println(F("Fermeture FTP -----------------------"));

  dclient.stop();
  Serial.println(F("Données déconnectées"));

  if(!eRcv()) return 0;

  clientserveur.println(F("QUIT"));

  if(!eRcv()) return 0;

  clientserveur.stop();
  Serial.println(F("commande de deconnexion"));
  file.close();
  Serial.println(F("SD fermée"));

  Serial.println(F("--------------------------------"));

}

// la boucle 
void loop()
{
nb=nb+1;

if (nb <=1 ){
            if(FTPopen() == 0) { // Ouverture du serveur FTP
                                            FTPopen();
                                           } 
            
            if (piecejointe == 1 ){Serial.print(F("envoie avec piece jointe : "));
                                  Serial.println(nompiecejointe);
                                  FTPupload (nompiecejointe); // copie de la piece jointe de la Sd sur le serveur
                                  FTPclose(); // fermeture du serveur ftp
                                  if(FTPopen() == 0) { // Ouverture du serveur FTP
                                                      FTPopen();
                                                      }
                                  FTPupload (nomfichier); // copie du fichier de la Sd sur le serveur
                                  }
            else {FTPupload (nomfichier); }// copie du fichier de la Sd sur le serveur
            }
  
  FTPclose(); // fermeture du serveur ftp

} // fin de la boucle

Donc dans cette exemple ci dessus j'obtiens lors de l'exécution dans le moniteur série échec ouverture SD pour le fichier DECE2023.csv si je met DEC2023.csv c'est tout bon (bien entendu sous réservé d'avoir un fichier de ce nom sur la SD!)

PS: la partie serveur FTP n'est pas de moi elle vient de ce site!

Merci

votre carte SD est-elle en FAT16 ou FAT32 ?

avez vous essayé avec SdFat plutôt que SD? (vous aurez le support de exFAT si nécessaire et le support des noms de fichier longs (plus que le 8.3 de FAT))

Bonjour ma carte SD est formatée en FAT32, le PC ne me propose que NTFS, FAT32 ou ExFat.
Et non je n'ai jamais même installé la librairie SDFat (je ne connaissais même pas avant que tu en parles).
J'ai sur ces mêmes Arduino un serveur web qui liste le contenu de la SD en permettant soit d'effacer le fichier soit de le télécharger et la cela fonctionne bien mais il est vrai que fileName est décrit comme un type char *filemeName.
Mais je n'ai pas réussi (en tout cas sans avertissement du compilateur ) à transformer une String en char *pour tester...
Merci

Il faut appeler c_str() sur votre String pour avoir sa représentation

si c'est ce que vous avez fait, le compilateur n'est peut être pas content car c_str() retourne un const char * et que la fonction attend un char * ?

on en saurait plus si vous postiez le code

FAT32 supporte les noms longs mais pas la bibliothèque SD. faut passer sur sdFat.

Bonjour, j'ai testé en exfat... ben du coup impossible de lire sur la SD!
Elle ne démarre pas lors du test:
if (SD.begin(pinCS_SD)) {
du setup je viens sur le :
else : la Sd ne demarre pas testé fait en en exfat 32 k ou 16k (pour la table d'allocation)
Dés que je remet en fat 32 cela fonctionne aussi bien (ou mal qu'avant..)

Merci

Oui voici le code, mais là il s'agit d'un code de test avec pleins d'affichages qui sont là pour suivre un peu le truc..
Avant d'envoyer à la fonction FTPupload() je passe par la fonction caste(nompiecejointe)

L'objectif était d'essayer d'envoyer à la fonction SD.open un char* (string sans le caractère de terminaison...)

void caste(String nomtemp){
  Serial.print(nomtemp); 
  int taille1 = nomtemp.length();
  Serial.println(taille1);   
  Serial.println(sizeof(nomtemp));                                                      
  char *nomF= nomtemp.c_str();
  Serial.print(nomF);                                                               
  int taille = strlen(nomF);
  Serial.print("taille nomF : ");
  Serial.println(taille);
  
  for(int i = 0; i < taille; i++){
    Serial.println(nomF[i]);
  }
                                                                  
  if(nomF[strlen(nomF)-1] == '/') {     // Découper le nom de fichier de répertoire
    nomF[strlen(nomF)-1] = 0;           // car l'ouverture génère une erreur si on laisse à la fin le caractère /
    Serial.println("on a modifié la chaine");
  }     // fin du if filename
  
  Serial.print(F("Requête Web pour : ")); 
  Serial.println(nomF);         // Affichage du nom du fichier demandé  
  
  nompiecejointeF=nomF;
  }

merci

désolé c'est illisible sur mon iPhone. indentez le code

SD ne sait pas lire du exfat

C'est fait.
Merci

Je ne comprends pas. Il faut absolument le nul final pour une cString

Si vous utilisez une String faites les manipulations avec les fonctions de la classe String, sinon utilisez directement une cString.

J'ai sur une autre sketch une fonction qui liste les fichiers présents sur la SD et me permet de les télécharger depuis un PC:
elle utilise un char * fileName pour ouvrir les dis fichiers et cela fonctionne avec 8 caractères utiles, j'essayais de m'en inspirer voici la fonction en question:


void GestionFichier(int Choix1) {               // affichage page web selection fichiers


  
char clientline[BUFSIZ];
char name[17];
int index = 0;

  
EthernetClient client = server.available();
  

  if (client) {
	Serial.println(F("il y a un client"));
                               
	root = SD.open("/");
	printDirectory(root, 0);              // va à la fonction de création du listing des fichiers présent sur la SD
              
	// initialisation avec une extrémité de la requête HTTP avec une ligne vide
	boolean current_line_is_blank = true;
              
	// réinitialisation du tampon d'entrée
	index = 0;
    
	while (client.connected()) {
		
		if (client.available()) {
                                                                                
  			char c = client.read();
                                                                  
			// S'il ne s'agit pas d'une nouvelle ligne, ajoutez le caractère au tampon
			if (c != '\n' && c != '\r') {                       // obtenir un \ n ou \ r nouvelle ligne, signifie que la chaîne est terminée
                        	  clientline[index] = c;
                                  index++;
                                  // Sommes-nous trop gros pour le tampon? Si c'est la cas on commence à jeter des données
                                  if (index >= BUFSIZ) 
                                  	index = BUFSIZ -1;
                                                                                              
                                         // on continue à lire plus de données!
                                         continue;
                                  }
        
                                  // obtenir un \ n ou \ r nouvelle ligne, signifie que la chaîne est terminée
                                  clientline[index] = 0;

                                                                
                                  // Recherchez une sous-chaîne telle qu'une demande pour obtenir le fichier
                                  if (strstr(clientline, "GET /") != 0) {
                                  	// cette fois pas d'espace après le /, donc un sous-fichier!
                                       	char *filename;
                                                                  
                                         filename = clientline + 5;        // un petit truc,regardez après le "GET /" (5 caractères) ******* 
                                         // recherchez la chaîne "HTTP / 1.1" et transformez le premier caractère de la sous-chaîne en un 0 pour l'effacer.
                                          (strstr(clientline, " HTTP"))[0] = 0;
                                                         
                                         if(filename[strlen(filename)-1] == '/') {     // Découper le nom de fichier de répertoire
                                         	filename[strlen(filename)-1] = 0;           // car l'ouverture génère une erreur si on laisse à la fin le caractère /
                                          }     // fin du if filename
                                                                  
                                         Serial.print(F("Requête Web pour : ")); 
                                         Serial.println(filename);         // Affichage du nom du fichier demandé
                                                                                                                                                                                                                                             
                                         File file = SD.open(filename, O_READ);
                                                                 
                                         if ( file == 0 ) {            // L'ouverture du fichier avec le code retour de 0 est une erreur dans SDFile.open
                                         	client.println(F("HTTP/1.1 404 Not Found"));
                                                client.println(F("Content-Type: text/html"));
                                                client.println();
                                                client.println(F("<h2>Fichier non trouv&#233;!</h2>"));
                                                client.println(F("<br><h3>Impossible d'ouvrir le fichier!</h3>"));
                                                drapeau = 0;              // réinitialise la variable pour la prochaine fois
                                                compteursauvegarde = 0;       // réinitialise la variable pour la prochaine fois
						break; 
                                          }            // fin de l'erreur d'ouverture fichier
                                                                  
                                         Serial.println(F("Fichier ouvert!"));
                                                                            
                                         client.println(F("HTTP/1.1 200 OK"));
                                         if (file.isDirectory()) {
                                          	 Serial.println(F("C'est un répertoire"));   // peut-être masqué
                                                                                      
                                                  client.println(F("Content-Type: text/html"));
                                                  client.println();
                                                  client.print(F("<h2>Fichiers contenu dans la SD: /"));
                                                  client.print(filename); 
                                                  client.println(F(":</h2>"));
                                                  ListFiles(client,LS_SIZE,file);  
                                                  file.close();                   
                                          }        // fin du if repertoire
                                 
                                          else {                           // Tout non-répertoire cliqué, le serveur enverra le fichier au client pour téléchargement

                                          	/*fonction d'effacement de fichier*/                                                          
                                                if (Choix1 == 1){  SD.remove(filename);  
                                                	Serial.print(F("on va effacer "));  // peut-être masqué
                                                        Serial.println(filename);     // peut-être masqué
                                                        drapeau = 0;        // réinitialise la variable pour la prochaine fois
                                                        compteursauvegarde = 0;   // réinitialise la variable pour la prochaine fois
                                                                                            
                                                        client.println(F("HTTP/1.1 200 OK"));        //envoie une nouvelle page
                                                        client.println(F("Content-Type: text/html"));
                                                        client.println();     
                                                        client.println(F("<HTML>"));
                                                        client.println(F("<BODY>"));
                                                        client.println(F("<h2>On a effac&#233; le fichier :"));
                                                        client.println(filename);
                                                        client.println(F("<br />"));            // saut de ligne
                                                        client.println(F("Vous pouvez quitter cette page!"));
                                                        client.println(F("</h2>"));
                                                        client.println(F("</BODY>"));
                                                        client.println(F("</HTML>"));
                                                        break;
                                                   }         // fin de la fonction d'effacement de fichier
                            
              
                                                   /*fonction de téléchargement du fichier : */
                                                                            
                                                  if (Choix1 == 2) {
                                                  	client.println(F("Content-Type: application/octet-stream"));       //Si changement texte entre guillemets on affiche dans le navigateur les données sans les sauvegarder dans un fichier...
                                                        client.println();
                                                        char file_buffer[16];
                                                        int avail;
                                                        while (avail = file.available()) {
                                                         	int to_read = min(avail, 16);
                                                                if (to_read != file.read(file_buffer, to_read)) {  
                                                                	break; 
                                                                }
                                                                                                                                                                                                                                                                                                                                                                                         
                                                                client.write(file_buffer, to_read);
                                                       }      // fin du while
                                                                                                                           
                                                    file.close();                         // ferme le fichier
                                                    drapeau = 0;                        // réinitialise la variable pour la prochaine fois
                                                    compteursauvegarde = 0;             // réinitialise la variable pour la prochaine fois
                                                    }    // fin du téléchargement de fichier
                                                                       
                                                                             
                                            }                                  //fin du else envoie fichier au client pour téléchargement
                                                              
                                      }                                         // fin du if fin de chaine strstr

                                      else {
                                     	// N'importe quelle erreur est une erreur 404
                                        client.println(F("HTTP/1.1 404 Not Found"));
                                        client.println(F("Content-Type: text/html"));
                                        client.println();
                                        client.println(F("<h2>Fichier non trouv&#233;!</h2>"));          
                                                         
                                      }   // fin du else = erreur
                                break;
                               }           // fin du if client disponible
                       }          // fin du if client connecté
    
         delay(800);               // donne au navigateur Web le temps de recevoir les données 
         client.stop();              // arrête le client
	}                                   // fin du if client


}                                         // fin de la fonction du serveur web Arduino


Merci

Mais concrètement que cherchez vous à faire avec votre String?

Je cherchais à faire en sorte que comme dans le dernier code les noms de fichier issu de la SD fonctionnent même avec même avec 8 caractères utiles.

Pas compris…

Bonjour je cherche à ouvrir (pour envoyer au serveur FTP) un fichier nommé NOVE2023.csv tous les fichiers qui comprennent 12345678.123 impossible de les ouvrir ceux qui vont moins de 8 caractères pas de soucis.
Merci

Vous êtes sûr qu’il n’y a pas un espace caché dans le nom?

Un fichier en 8.3 sur une carte formatée en FAT16 devrait pouvoir être lu

oui sur pas d'espace caché d'ailleurs c'est pour cela que j'ai mis l'exemple de la lecture de mon dir (en vue de téléchargement des fichiers) qui lui fonctionne avec les mêmes fichiers (post de 14h)
Merci

le N° du post serait plus simple - je ne vois pas de 14h

post # 11 je n'avais pas vu le numéro désolé
merci

le code que vous avez traite le cas où vous recevez dans une requête GET un truc du genre

"GET /nom_de_fichier HTTP/1.1"
ou
"GET /nom_de_fichier/ HTTP/1.1"

et ça extrait la partie nom_de_fichier. Pour cela vous placez le début de la chaine juste après le GET / en sautant 5 caractères et vous recherchez HTTP et remplacez l'espace par un caractère nul. ça donnerait nom_de_fichier ou nom_de_fichier/. dans ce dernier cas comme vous ne voulez pas le / à la fin, vous le remplacez par un caractère nul ce qui termine à la chaîne à cet endroit et vous obtenez nom_de_fichier

Dans votre fonction

void caste(String nomtemp){
  ..

que passez vous en paramètre ?