Failure to recognise an empty String within an if/else if statement

Hello,

I have two key functions: one to select and open a folder within an SDcard one to select play an audio file.

I have both of these within:

void loop() {

  buttonInit();
  updateIO();
  displayInit();

  

  if(directoryPath == "") // -> How can I get it say that this is false?
  {
    getFolder();
  }
  
  else if(filePath == "") // *= folderNames[4]* OR give it directoryPath == "Drums"? 
  {
    getFile();
  }

  else
  {
    homeScreen();
  } 
  
}

My aim is that once the directoryPath has been found, it should proceed to grab the filePath which can then be assigned to a pad. However, it always believes that no directory has been found and so never moves onto the else if statement. My specific question is, why does it always fail to see that a directoryPath has been found?

(Here is the whole of the code)

#include <SD.h>
#include <SPI.h>
#include <Bounce2.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Audio.h>
#include <string>

#include "displayFunctions.h"

Adafruit_SSD1306 display(128, 64, &Wire, -1);

#define MOSI_PIN 11
#define SCK_PIN 13
#define CS_PIN = BUILTIN_SDCARD


void homeScreen();
void printDirectory(File dir, int numTabs);
void buttonInit();
void updateIO();
bool checkWav(String wav);

#define maxNumCharDisplay 80

int led = 28;

const char *folderNames[] = { "Drums", "Vocals", "Pads", "Misc" };

Bounce selectButton = Bounce(36, 10);
Bounce returnButton = Bounce(37, 10);


const byte pins[] = {28, 29, 30, 31, 32, 33, 34, 35};
const byte numOfPads = sizeof(pins);
Bounce padPad[numOfPads];

File root;
File currentDirectory;
File entry;

String dirArray;
String directoryPath;// = "/Presets/";
String filePath;  //-> copy this into ch array ?

bool selectedFolder;

int folderChoice;
int fileChoice;
int numTabs;

char presetFolderName[] = "/Presets/";
String fileExtension = ".wav";


int assignedPad = -1;

AudioPlaySdWav playWav;

void setup() {
  
  buttonInit();
  updateIO();
  displayInit();

  Serial.begin(9600);


 
  SPI.setMOSI(MOSI_PIN);
  SPI.setSCK(SCK_PIN);

  if (!(SD.begin(BUILTIN_SDCARD))) {
    while (1) {
      Serial.println("unable to access SD card.");
    }
  }

  homeScreen(); //  Display list of preset folders

  AudioMemory(8); //  buffer for how much memory to provide
  playWav.stop();

  
  
}


void loop() {

  buttonInit();
  updateIO();
  displayInit();

  
  //char presetFolderName[0];
  //sprintf(presetFolderName, '\0');

  //if(strlen(presetFolderName) > 0) // -> How can I get it say that this is false? -  && presetFolderName == '\0'
  if(directoryPath == "")
  {
    getFolder();
  }
  //else if(strlen(presetFolderName) < 0) // *= folderNames[4]* OR give it directoryPath == "Drums"?
  else if(filePath == "") 
  {
    getFile();
  }

  else
  {
    homeScreen();
  }


  
  
}

void getFolder()  //  this should return/input a String (folder name) -> Although, the folderNames are const chars? if a const char
{

  dirArray = directoryPath;
  //strcat(dirArray, PresetFolderName);         //  concatenate /Presets/ with directory path -> should this happen later?
  for (uint8_t i = 0; i < directoryPath.length(); i++)  //  for the whole length of the String
  { 
    //Serial.println("line 142"); 
    //Serial.println(dirArray[i]);                //   
  }
  
 
  if (directoryPath == "") {  //  if no directory has been selected 

    while (!selectButton.update()) {}

    if (selectButton.fallingEdge()) {
      //int folderChoice = readInt(1, 4); // -> do the same as^ by changing int into a ch Array

      //int folderChoice; //  keeps track of which folder has been selected -> should this be -1? -> could just say -1 here instead of further down
      char folderChoiceChar[maxNumCharDisplay + sizeof(char)];
      sprintf(folderChoiceChar, "%d", folderChoice);  //  But how do I get it to read from 1-4? -> for loop
      for (int i = 0; i < 3; i++) {
        folderChoice = folderChoiceChar[i];  //should the folderChoiceChar be tracked with an index? -> yes
      }

   
      dirArray = presetFolderName;
      
     
 
      dirArray += folderNames[folderChoice];

      File folder;
      folder = SD.open(dirArray.c_str());
           
     
      //folder = SD.open(directoryPath.c_str()); //  Does this open straight to ...^ "directoryPath = "/Presets + folderName;"

      
      if(folder.isDirectory()){  //  
       
    
        while (true) //Does this loop & print twice?
        {
          File entry = folder.openNextFile(); //  I think, because it's just 'openNextFile' whilst Drums is a directory -> it will keep looping on the drums directory until all files are printed. 
      

          if (!entry) 
          {
            break;
          }
          display.clearDisplay();
          display.setTextColor(SSD1306_WHITE);  // Draw white text
          display.setCursor(0, 0);
          
          Serial.println("---LOOP START---");
          display.println(folder.name()); //  this prints: e.g. "Drums" - How to print multiple??
          Serial.println(folder.name());
          display.setCursor(12, 0);
          display.println(":");          
          

         
          for(uint8_t i = 0; i < numTabs; i++)
          {
            
            display.println('\t');  //  This line should print vertically
            
          }
          display.println(entry.name());  //  And this prints the audio - but only 1 file - how can we print multiple?
          Serial.println(entry.name());

          if(entry.isDirectory()) //  This is if it is a folder not a file -> not necessary?
          {
            display.println("/");

          }
          else
          {
            Serial.println("");
          }
           
          entry.close();
          
          Serial.println("---LOOP END^--");

          break; // -> this work in only printing 1 audio file - now how multiple?

        }
        
        

        Serial.println(sizeof(directoryPath), DEC); //  prints the no. of bytes directoryPath takes up
        Serial.println(" bytes");
        
      }
      folder.close();
      display.display();
    
      
    }
     //WRITE OUT OF BOUNDS IF PRESETFOLDERNAME IS LONGER THAN 0 CHARS. - HOW?




   
  
  } 

 


}

void getFile()
{

  //char samplesArr[filePath.length() + 1];  //  change path from string to Char arry -> think the +1 is becuase of the NULL character at the end
  //strcpy(samplesArr, filePath.c_str());    //  copy String directoryPath into destination dirArray
  
  String samplesArr = filePath;
  
  
  for (uint8_t i = 0; i < filePath.length(); i++)  //  
  {                                                // 
   
  }

  if (filePath == "") //  if directory has been selected but no audio file, proceed. I think filePath has to be more defined before^
                            //  Currently just saying, if String filePath is nothing -> must make it so that filePath = charArray 
  {  

    while (!selectButton.update()) {}

    if (selectButton.fallingEdge() && checkWav(filePath)) //  -> AND if .wav?
    {


   
      char fileChoiceChar[maxNumCharDisplay + sizeof(char)];
      sprintf(fileChoiceChar, "%d", fileChoice);  //  But how do I get it to read from 1-4? -> for loop
      for (int i = 0; i < 3; i++) {
        fileChoice = fileChoiceChar[i];  //should the folderChoiceChar be tracked with an index? -> yes
      }

      samplesArr = dirArray; // -> ???

      samplesArr += dirArray[fileChoice];  //  -> what should I change folderNames to?

   
      

      display.clearDisplay();
      display.setCursor(0, 0);
      display.println("Assign to pad:");

      for (int i = 0; i < numOfPads; i++) {
        display.print(i + 1);
        display.print(". Pad ");
        display.print(i);
      }
      display.display();
    }
  } else if (assignedPad == -1) {                         // Definitley have to do some joining of assignedPad = filePath?
    for (int i = 0; i < numOfPads; i++) {
      if (padPad[i].update() && padPad[i].fallingEdge()) {
        assignedPad = -1;  // should this be -1?
        display.clearDisplay();
        display.setCursor(0, 0);
        display.print("Assigned to pad: ");
        display.println(assignedPad + 1);
        display.display();
      }
    }
  }

  else {
    for (int i = 0; i < numOfPads; i++) {
      if (padPad[i].update() && padPad[i].fallingEdge() && i == assignedPad) {
        playWav.play(filePath.c_str());
        break;
      }
    }
  }
 
  
}

void homeScreen()
{

  while (!returnButton.update()){}
  if(returnButton.fallingEdge())
  {
    
  
    display.clearDisplay();
    display.setCursor(0, 0);
    display.println("Select a directory:");
    for (int i = 0; i < 4; i++) {
      display.print(i + 1);
      display.print(". ");
      display.println(folderNames[i]);
    }
    display.display();
  }
}

void printDirectory(File dir, int numTabs) {

  while (true) {

    File entry =  dir.openNextFile();

    if (! entry) {

      // no more files

      break;

    }

    for (uint8_t i = 0; i < numTabs; i++) 
    {

      display.print('\t');

    }

    display.print(entry.name());

    if (entry.isDirectory()) 
    {

      display.println("/");

      printDirectory(entry, numTabs + 1);

    } else 
    {

    }
    entry.close();
  }
}

bool checkWav(String wav)
{
  if (wav.endsWith(fileExtension))
  {
    return true; // Return true if file ends with wav
  }
  else
  {
    return false; // Return false if file does not
  }
}

void displayInit() {
  display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS);
  display.clearDisplay();
  display.setTextSize(1);               // Normal 1:1 pixel scale
  display.setTextColor(SSD1306_WHITE);  // Draw white text
  display.setCursor(0, 0);
}

void buttonInit() {

  pinMode(36, INPUT_PULLUP);  // select button
  pinMode(37, INPUT_PULLUP);  // return button

  for(byte i = 0; i < numOfPads; i++)
  {
    padPad[i].attach(pins[i], INPUT_PULLUP);
    padPad[i].interval(5); //  debounce time    
  }
  //for (int i = 0; i < numOfPads; i++) {
   // int pin = pins[i];

   // pinMode(pin, INPUT_PULLUP);

   // Bounce *bb = new Bounce(pin, debounce);

   // padPtrs[i] = bb;
   //}
  //pinMode(35, INPUT_PULLUP);

  /*
  for(int i = 28; i <= 35; i++) // From pad[0-8] could I say from pins 28-35 
  {
    padButtons[i-28].attach(i, INPUT_PULLUP);
  }  
  */

  pinMode(led, OUTPUT);
}

void updateIO()
{
  selectButton.update();
  returnButton.update();

  for(byte i = 0; i < numOfPads; i++)
  padPad[i].update();
    //  for (int i = 0; i < numOfPads; i++) {
    //   Bounce padPad = *padPtrs[i];  //  Do I need to define Bounce padPad as an global array?

    //  padPad.update();

    //if(padPad.fallingEdge())
    //{
    //Serial.print("Pad: ");
    //Serial.print(i);
    //}
  //  }
  //    padButtons.update();

  //button2.update();
}

I have tried stating (!(directoryPath == "")) as well as if directoryPath = one of the folderNames. as well as providing the void directoryPath(with a const char) to represent one of the folder names as this is what will be returned. I have also tried stating within void loop that if(strlen(presetFolderNames) > 0) { getFolder()}.

Have you tried printing directoryPath after assigning it a value in the sketch ?

That, of course, raises the question as to where in the sketch it is given a value

Arduino Reference says: A String can only be compared to another String, so I assume not to a "" pointer.

1 Like

Yes, I have added within the void getFolder() function directoryPath = dirArray and Serial.println(directoryPath); The serial output is /Presets/Drums. Which is correct I believe?

Okay, I didn't realise "" was a pointer. Should I define a new String as StrEmpty = ""; For example?

Are you sure ?
From your sketch

    dirArray = directoryPath;

Find out yourself, I have no String expertise :frowning:

My intention is that dirArray is used to open the folder and directoryPath stores the /Presets/(e.g.Drums)/. So after the folder has been opened does it not make sense that directoryPath = dirArray? The output seems correct?

Okay, well thank you for informing me that "" is a pointer

A String can be compared to s string literal, even "", if you are determined to use Strings

String foo = "";

void setup()
{
    Serial.begin(115200);
    if (foo == "")
    {
        Serial.println("equals");
    }
}

void loop()
{
}

This prints

equals

As you would expect

Where in the sketch do you set directoryPath to anything ?

In the initial sketch, I think I perceived dirArray = directoryPath as setting it but I realise they are both empty strings at this point. I initially changed directoryPath into a char array so that I could use strcat(directoryPath, dirArray). However, I was advised against this. I'm not sure I understand why directoryPath = dirArray is not a good idea/ not working.

Once again

Where in the sketch do you set directoryPath to anything ?

 directoryPath = dirArray

is not in the code that you posted

My advice would be not to mix Strings and strings (zero terminated char arrays)

Thank you for your advice, although are you recommending that I change directoryPath back into a char array?

I can't easily see why these two calls to things named *Init() should be called in the loop() function.

a7

From the point of view of memory usage and long term stability it would be better to use C style strings (zero terminated arrays of chars)

Whatever you decide to use then if you never set directoryPath to a value then it won't miraculously be given one

You still have not explained where in the sketch directoryPath is given its value

okay, I understand that I have to give it a value but what I don't understand is if (as stated in my most recent comment) directoryPath = DirArray; Serial.println(directoryPath); the output is /Presets/Drums. Does this not mean that directory Path has the output value? and so if(directoryPath = "") would be then false and it should proceed to the else if? Or are you saying that I need to assign it a value earlier in the code?

String DirArray = "/Presets/Drums";
String directoryPath = DirArray;
void setup()
{
    Serial.begin(115200);
    if (directoryPath == "")
    {
        Serial.println("directoryPath is empty");
    }
    else
    {
        Serial.print("directoryPath is ");
        Serial.println(directoryPath);
    }
}

void loop()
{
}

This works as you expect so what is different in your sketch ?

But strlen("/Presets/") is 9. Why would you want it to say that 9 > 0 is false?

In this sketch you has defined String directoryPath = DirArray; as a global variable ? Whereas In my sketch 'directoryPath = dirArray;' is only declared at the end of the getFolder() function so therefore after "/Presets/Drums' has been assigned, when it loops back round wouldn't the if statement be false therefore move to the else? I hope I'm not missing something very obvious