Cannot open a file from SD when using multiple loops [SOLVED]

[edit]
PROBLEM SOLVED.

Thanks for the help - with a lot of help from you and geeks at the lab I have found the solution!
The program works as intended - for now.

  1. char waitForKey() blocks everything. It unusable in this (and many other) scenarios. I've managed to apply char getKey() for the same purpose. It was kind of difficult since couldn't find any examples online.

  2. My power source was corrupted - extra ground was needed, all-ok now.

  3. Interrupt cable was connected to 10th pin - same as my SD card chipselect pin, now Interrupt is pin 19.

Program works as follows:

  1. Insert 1€ or more into the coin acceptor.
  2. Lcd shows you inserted amount, and informs you when 1€ is reached ("Skambink!")
  3. Dial a number (1441, 2552 or 3663).
  4. Hear a sound e.g. jonas.wav/petras.wav/antanas.wav

Code:

volatile float coinsValue = -0.10;
//Interrupt pin works somewhat difficult on DUE - sends a signal when DUE turns on, I offset value by one step

int coinsChange = 0;
//A Coin has been inserted flag

#include <Wire.h>
#include <LCD.h>
#include <LiquidCrystal_I2C.h>

#include <DAC.h>
#include <SD.h>
#include <SPI.h>
#include <Audio.h>
#include <Key.h>
#include <Keypad.h>

#define I2C_ADDR    0x3F // Add your address here.  Find it from I2C Scanner
#define BACKLIGHT_PIN     3
#define En_pin  2
#define Rw_pin  1
#define Rs_pin  0
#define D4_pin  4
#define D5_pin  5
#define D6_pin  6
#define D7_pin  7


LiquidCrystal_I2C  lcd(I2C_ADDR, En_pin, Rw_pin, Rs_pin, D4_pin, D5_pin, D6_pin, D7_pin);

const byte ROWS = 4;
const byte COLS = 4;

int code1 = 1441;  //The code I used, you can change it
int code2 = 2552;  //The code I used, you can change it
int code3 = 3663;  //The code I used, you can change it

int tot, i1, i2, i3, i4;
char c1, c2, c3, c4;

char hexaKeys[ROWS][COLS] = {
  {'1', '2', '3', 'A'},
  {'4', '5', '6', 'B'},
  {'7', '8', '9', 'C'},
  {'*', '0', '#', 'D'}
};

byte rowPins[ROWS] = {9, 8, 7, 6};
byte colPins[COLS] = {5, 4, 3, 2};

int keyCounter = 0;

Keypad myKeypad = Keypad(makeKeymap(hexaKeys), rowPins, colPins, ROWS, COLS);

void setup() {
  // debug output at 9600 baud
  Serial.begin(9600);

  //My LCD is 16x2
  lcd.begin (16, 2);
  SD.begin(10);

  //for DUE - any digital pin as Interrupt pin
  attachInterrupt(digitalPinToInterrupt(19), coinInserted, RISING);

  // Switch on the backlight
  lcd.setBacklightPin(BACKLIGHT_PIN, POSITIVE);
  lcd.setBacklight(HIGH);
  lcd.home (); // go home

  lcd.print("Suma:");
}

void coinInserted()
//The function that is called every time it recieves a pulse
{
  coinsValue = coinsValue + 0.10;
  //As we set the Pulse to represent 5p or 5c we add this to the coinsValue
  coinsChange = 1;
  //Flag that there has been a coin inserted
}
char keypresses[4];

void loop() {

  if (coinsChange == 1)
    //Check if a coin has been Inserted
  {
    coinsChange = 0;
    //unflag that a coin has been inserted
  }
  lcd.setCursor (0, 1);       // go to start of 2nd line
  //Print the Value of coins inserted
  lcd.print(coinsValue);
  Serial.println(coinsValue);

  if (coinsValue >= 1)
  {
    lcd.setCursor (7, 1);       // go to start of 2nd line
    lcd.print("Skambink!");
    Serial.println("Skambink!");

    keypadInput();
  }
}
void keypadInput() {

  //getting the key when button is pressed instead of waitForKey
  char customKey = myKeypad.getKey();
  if (NO_KEY != customKey) {
    //storing number into array
    keypresses[keyCounter] = customKey;
    //counting how many number we've got
    keyCounter++;
    Serial.print(customKey);
  }
  //if we have 4 numbers - proceed
  if (keyCounter > 3)
  {
    keyCounter = 0;
    //the keys pressed are stored into chars I convert them to int then i did some multiplication to get the code as an int of xxxx
    i1 = (keypresses[0] - 48) * 1000;
    i2 = (keypresses[1] - 48) * 100;
    i3 = (keypresses[2] - 48) * 10;
    i4 = keypresses[3] - 48;
    tot = i1 + i2 + i3 + i4;

    if (tot == code1) //if the code is correct you trigger to play .wav
    {
      playSound("jonas.wav");
    }
    else if (tot == code2)
    {
      playSound("petras.wav");
    }
    else if (tot == code3)
    {
      playSound("antanas.wav");
    }
    else if (tot == 0000)
    {
      playSound("test.wav");
    }
    else //if the code is wrong you get another thing
    {
      for (int j = 0; j < 256; j++) {
        for (int i = 0; i < 256; i++)
          analogWrite(DAC1, i);
      }
      playSound("blogas.wav");
    }
  }
}

// Playing assigned filename
void playSound(const char* cName) {
  File myFile = SD.open(cName);
  const int S = 1024; // Number of samples to read in block
  short buffer[S];
  // until the file is not finished
  Serial.println("playing ");
  Serial.println(cName);
  Audio.begin(44100, 100);
  while (myFile.available()) {
    myFile.read(buffer, sizeof(buffer));
    // Prepare samples
    int volume = 1024;
    Audio.prepare(buffer, S, volume);
    Audio.write(buffer, S);
  }
  Audio.end();
  myFile.close();
}

[end of edit]

Hi guys!

I am making this "phone", where users (children) use tokens/coins and keypad to make it speak (play .wav files) through the handset. For this project I am using i2c 16x2 LCD, 4x4 Keypad, SD shield + amplifier + speaker, Sparkfun 6 coin types acceptor, Arduino DUE.

The problem arise when I only allow to use keypad buttons when enough money is being put through the coin acceptor - code line 102.

When adding void keypadInput() in if or while statement I cannot send the names of .wav files to void playSound(const char* cName) . Everything else works as expected.

I know, I am a pretty lousy programmer. Here is my code, hopefully someone can spot my mistake(s):
Cheers! Have a nice Easter!

//code was removed, since it might misguide readers of the forum. For the right code look above or to the post #20.

Aren't keypad pins and lcd pins overlapping?

arduino_new:
Aren't keypad pins and lcd pins overlapping?

No, it seems. This part is for connecting LCD to Serial I2C LCD Display Adapter only:

#define I2C_ADDR    0x3F // Add your address here.  Find it from I2C Scanner
#define BACKLIGHT_PIN     3
#define En_pin  2
#define Rw_pin  1
#define Rs_pin  0
#define D4_pin  4
#define D5_pin  5
#define D6_pin  6
#define D7_pin  7

2 to 9 pins I use for a keypad.
10 pin is set as Interrupt for a coin acceptor.
11-13 - SPI for SD shield.
DAC0, DAC1 - sound.

And I communicate with I2C LCD adapter through 20 & 21 pins.

"The Arduino Due has two I2C / TWI interfaces SDA1 and SCL1 are near to the AREF pin and the additional one is on pins 20 and 21."

In this piece

 //if coins value is enough to make a call - dial
  while (coinsValue >= 1)

the comment says IF so why is the next line using WHILE? I suspect it would make more sense to use IF

And there seems to be no means to get out of the WHILE loop

Are the print statements in your keypadInput() function showing normally when line 102 is called?

...R

Robin2:
In this piece

 //if coins value is enough to make a call - dial

while (coinsValue >= 1)



the comment says IF so why is the next line using WHILE? I suspect it would make more sense to use IF

And there seems to be no means to get out of the WHILE loop

Are the print statements in your keypadInput() function showing normally when line 102 is called?

...R

Yes, I agree, IF seems more logical. I also tried it with IF. But as I saw in one example it might be a problem with IF to call different loop - keypadInput();.

Yes, print statements show input. Also, the DAC beeps, line 159-161.

Have you got your keypadInput() function to work and play .wav files in any other context? - if so post that program.

...R

Robin2:
Have you got your keypadInput() function to work and play .wav files in any other context? - if so post that program.

...R

Sure. It is a simpler program - without LED and Coin acceptor.
I could swear I had it working in the current program, outside IF statement, but now it doesn't seem possible.

#include <DAC.h>
#include <SD.h>
#include <SPI.h>
#include <Audio.h>
#include <Key.h>
#include <Keypad.h>

const byte ROWS = 4;
const byte COLS = 4;

int code1 = 1451;  //The code I used, you can change it
int code2 = 2562;  //The code I used, you can change it
int code3 = 3673;  //The code I used, you can change it

int tot, i1, i2, i3, i4;
char c1, c2, c3, c4;

char hexaKeys[ROWS][COLS] = {
  {'1', '2', '3', 'A'},
  {'4', '5', '6', 'B'},
  {'7', '8', '9', 'C'},
  {'*', '0', '#', 'D'}
};

byte rowPins[ROWS] = {9, 8, 7, 6};
byte colPins[COLS] = {5, 4, 3, 2};


Keypad myKeypad = Keypad(makeKeymap(hexaKeys), rowPins, colPins, ROWS, COLS);

void setup() {
  // debug output at 9600 baud
  Serial.begin(9600);
  // setup SD-card
  SD.begin(10);
}

void loop() {
    keypadInput();
}
void keypadInput(){

  //pressing the key -> assigning the filename

  char customKey = myKeypad.waitForKey();

  if (customKey != NO_KEY)
  {
    c1 = customKey;
    Serial.print(customKey);
  }
  customKey = myKeypad.waitForKey();
  if (customKey != NO_KEY)
  {
    c2 = customKey;
    Serial.print(customKey);
  }
  customKey = myKeypad.waitForKey();
  if (customKey != NO_KEY)
  {
    c3 = customKey;
    Serial.print(customKey);
  }
  customKey = myKeypad.waitForKey();
  if (customKey != NO_KEY)
  {
    c4 = customKey;
    Serial.println(customKey);
  }

  //the keys pressed are stored into chars I convert them to int then i did some multiplication to get the code as an int of xxxx
  i1 = (c1 - 48) * 1000;
  i2 = (c2 - 48) * 100;
  i3 = (c3 - 48) * 10;
  i4 = c4 - 48;
  tot = i1 + i2 + i3 + i4;

  if (tot == code1) //if the code is correct you trigger whatever you want here it just print a message on the screen
  {
    playSound("jonas.wav");
  }
  else if (tot == code2) //if the code is correct you trigger whatever you want here it just print a message on the screen
  {
    playSound("petras.wav");
  }
  else if (tot == code3) //if the code is correct you trigger whatever you want here it just print a message on the screen
  {
    playSound("antanas.wav");
  }
  else if (tot == 0000) //if the code is correct you trigger whatever you want here it just print a message on the screen
  {
    playSound("test.wav");
  }

  else //if the code is wrong you get another thing
  {
     for (int j = 0; j <256; j++){
     for (int i = 0; i <256; i++)
     analogWrite(DAC1,i);
      }
    playSound("blogas.wav");
    Serial.println("wrong code!");
  }
}

void playSound(const char* cName) {
  File myFile = SD.open(cName);
  const int S = 1024; // Number of samples to read in block
  short buffer[S];
  // until the file is not finished
  Serial.println("playing ");
  Serial.println(cName);
  Audio.begin(44100, 100);
  while (myFile.available()) {
    myFile.read(buffer, sizeof(buffer));
    // Prepare samples
    int volume = 1024;
    Audio.prepare(buffer, S, volume);
    Audio.write(buffer, S);
  }
  Audio.end();
  myFile.close();
}

I am not familiar with the Keypad library. What does the function waitForKey() do?

Also, maybe you can explain how the working program is used. I don't have a keypad so I can't try it out.

...R

char waitForKey()
This function will wait forever until someone presses a key. Warning: It blocks all other code until a key is pressed. That means no blinking LED's, no LCD screen updates, no nothing with the exception of interrupt routines.

Yeah, it also block all other code. Somehow my lcd works though. Now I'm not sure if I need to use waitForKey(). Most similar examples I've seen used that. Still, the problem seems somewhere in communication between the loops...I guess.

When merging code from void keypadInput() in void loop() program works, but now I'm unable to block number dial if coin amount is not reached.

My latest version of the program should work as follows:

  1. Insert 1€ or more into the coin acceptor.
  2. Lcd shows you inserted amount, and informs you when 1€ is reached ("Skambink!")
  3. Dial a number (1541, 2562 or 3673).
  4. Hear a sound e.g. jonas.wav

The simpler one works without LCD and coin acceptor (only points 3, 4).

atlaikesbriedi:
char waitForKey()
This function will wait forever until someone presses a key. Warning: It blocks all other code until a key is pressed. That means no blinking LED's, no LCD screen updates, no nothing with the exception of interrupt routines.

Yeah, it also block all other code. Somehow my lcd works though. Now I'm not sure if I need to use waitForKey(). Most similar examples I've seen used that. Still, the problem seems somewhere in communication between the loops...I guess.

Let's stick with this for a while.

Your working program in Reply #6 has 4 successive calls to waitForKey() so I assume that it would wait forever at any one of those and, when you say your program works that is because you do actually press the keypad 4 times. Is that correct?

...R

Robin2:
Let's stick with this for a while.

Your working program in Reply #6 has 4 successive calls to waitForKey() so I assume that it would wait forever at any one of those and, when you say your program works that is because you do actually press the keypad 4 times. Is that correct?

...R

Yes, the number I dial contains 4 digits. So yes, keypad is being pressed 4 times.

Tomorrow me and my prototype are meeting a group of pretty experienced geeks, hopefully I'll get some clarity why it doesn't work fully.

atlaikesbriedi:
Yes, the number I dial contains 4 digits. So yes, keypad is being pressed 4 times.

When you have the keypadInput() function in your main program does it receive the keypad entries properly?

...R

Robin2:
When you have the keypadInput() function in your main program does it receive the keypad entries properly?

...R

Yes, keypad itself works perfectly in any these programs (serial monitor shows every char pressed, every .wav I am trying to access). Seems like playSound() cannot be accessed from keypadInput() = no music from the speaker.

I will produce more working examples today.

What happens if you remove (comment out) all these lines from the code in your Original Post

 while (coinsValue >= 1)
  {
    lcd.setCursor (7, 0);       // go to start of 2nd line
    lcd.print("Skambink!");

...R

Robin2:
What happens if you remove (comment out) all these lines from the code in your Original Post

while (coinsValue >= 1)

{
    lcd.setCursor (7, 0);      // go to start of 2nd line
    lcd.print("Skambink!");




...R

Thanks, I think I've already tried this. It works like before, just without the need to put reach a coinsValue >= 1.

Now this is something I haven't noticed before:
From the example from serial monitor - input works, filenames works, it just File myFile = SD.open(cName) doesn't. Seems that void playSound works, since it shows these two lines through serial monitor like intended:

Serial.println("playing ");
Serial.println(cName);

[edit]
Serial monitor is printing:

1451 << Keypad input is all-ok
playing
jonas.wav << filename from playSound() works

Please post the latest version of your program in your next Reply. Also, please don't post photos of text, just copy and paste the text.

...R

Robin2:
Please post the latest version of your program in your next Reply. Also, please don't post photos of text, just copy and paste the text.

...R

Sorry, I always forget updating what I am trying now.

Version where if (coinsValue >= 1) is commented out:

const int coinInt = 0;
//Attach coinInt to Interrupt Pin (Any Digital Pin for DUE).

double coinsValue = 0.00;
//Set the coinsValue to a double

int coinsChange = 0;
//A Coin has been inserted flag

#include <Wire.h>
#include <LCD.h>
#include <LiquidCrystal_I2C.h>

#include <DAC.h>
#include <SD.h>
#include <SPI.h>
#include <Audio.h>
#include <Key.h>
#include <Keypad.h>

#define I2C_ADDR    0x3F // Add your address here.  Find it from I2C Scanner
#define BACKLIGHT_PIN     3
#define En_pin  2
#define Rw_pin  1
#define Rs_pin  0
#define D4_pin  4
#define D5_pin  5
#define D6_pin  6
#define D7_pin  7


LiquidCrystal_I2C  lcd(I2C_ADDR, En_pin, Rw_pin, Rs_pin, D4_pin, D5_pin, D6_pin, D7_pin);

const byte ROWS = 4;
const byte COLS = 4;

int code1 = 1451;  //The code I used, you can change it
int code2 = 2562;  //The code I used, you can change it
int code3 = 3673;  //The code I used, you can change it

int tot, i1, i2, i3, i4;
char c1, c2, c3, c4;

char hexaKeys[ROWS][COLS] = {
  {'1', '2', '3', 'A'},
  {'4', '5', '6', 'B'},
  {'7', '8', '9', 'C'},
  {'*', '0', '#', 'D'}
};

byte rowPins[ROWS] = {9, 8, 7, 6};
byte colPins[COLS] = {5, 4, 3, 2};


Keypad myKeypad = Keypad(makeKeymap(hexaKeys), rowPins, colPins, ROWS, COLS);

void setup() {
  // debug output at 9600 baud
  Serial.begin(9600);

  //My LCD is 16x2
  lcd.begin (16, 2);

  //for DUE - any digital pin as Interrupt pin
  attachInterrupt(digitalPinToInterrupt(10), coinInserted, RISING);

  // Switch on the backlight
  lcd.setBacklightPin(BACKLIGHT_PIN, POSITIVE);
  lcd.setBacklight(HIGH);
  lcd.home (); // go home

  lcd.print("Suma:");
}

void coinInserted()
//The function that is called every time it recieves a pulse
{
  coinsValue = coinsValue + 0.05;
  //As we set the Pulse to represent 5p or 5c we add this to the coinsValue
  coinsChange = 1;
  //Flag that there has been a coin inserted
}

void loop() {    
  if (coinsChange == 1)
    //Check if a coin has been Inserted
  {
    coinsChange = 0;
    //unflag that a coin has been inserted
  }
  lcd.setCursor (0, 1);       // go to start of 2nd line
  //Print the Value of coins inserted
  lcd.print(coinsValue);
  
    keypadInput();   // <<<<------ When adding this here, system cannot assign file names through playSound("***.wav");!!!
    
  //if coins value is enough to make a call - dial
//  if (coinsValue >= 1)
//  {
//    lcd.setCursor (7, 0);       // go to start of 2nd line
//    lcd.print("Skambink!");
//  }
}
void keypadInput() {

  //pressing the key -> assigning the filename
  char customKey = myKeypad.waitForKey();

  if (customKey != NO_KEY)
  {
    c1 = customKey;
    Serial.print(customKey);
  }
  customKey = myKeypad.waitForKey();
  if (customKey != NO_KEY)
  {
    c2 = customKey;
    Serial.print(customKey);
  }
  customKey = myKeypad.waitForKey();
  if (customKey != NO_KEY)
  {
    c3 = customKey;
    Serial.print(customKey);
  }
  customKey = myKeypad.waitForKey();
  if (customKey != NO_KEY)
  {
    c4 = customKey;
    Serial.println(customKey);
  }

  //the keys pressed are stored into chars I convert them to int then i did some multiplication to get the code as an int of xxxx
  i1 = (c1 - 48) * 1000;
  i2 = (c2 - 48) * 100;
  i3 = (c3 - 48) * 10;
  i4 = c4 - 48;
  tot = i1 + i2 + i3 + i4;

  if (tot == code1) //if the code is correct you trigger to play .wav
  {
    playSound("jonas.wav");
  }
  else if (tot == code2)
  {
    playSound("petras.wav");
  }
  else if (tot == code3)
  {
    playSound("antanas.wav");
  }
  else if (tot == 0000)
  {
    playSound("test.wav");
  }
  else //if the code is wrong you get another thing
  {
    for (int j = 0; j < 256; j++) {
      for (int i = 0; i < 256; i++)
        analogWrite(DAC1, i);
    }
    playSound("blogas.wav");
  }
}

// Playing assigned filename
void playSound(const char* cName) {
  File myFile = SD.open(cName);
  const int S = 1024; // Number of samples to read in block
  short buffer[S];
  // until the file is not finished
  Serial.println("playing ");
  Serial.println(cName);
  Audio.begin(44100, 100);
  while (myFile.available()) {
    myFile.read(buffer, sizeof(buffer));
    // Prepare samples
    int volume = 1024;
    Audio.prepare(buffer, S, volume);
    Audio.write(buffer, S);
  }
  Audio.end();
  myFile.close();
}

atlaikesbriedi:
Version where if (coinsValue >= 1) is commented out:

What happens when you run that version?

What happens when bring the un-commented lines back into use?

...R

Robin2:
What happens when you run that version?

What happens when bring the un-commented lines back into use?

...R

I wrote in post #14 - It works like before, just without the need to reach a coinsValue >= 1 (keypad works even if no coins were put through the acceptor).

When I bring the lines back to use - it also doesn't need coins - keypadInput(); is outside IF loop. But now LCD says "Skambink!" when 1€ is reached.

Now I cannot rut the coin acceptor anymore since my 12V 2A transformer seem to be blown up. Will have universal power source in the lab tomorrow.

Sorry, but I can't see anything that might be causing the problem, but I don't have your hardware so I can't do any tests.

In the program that is in Reply #16 try adding these few extra lines into the keypadInput() function

void keypadInput() {
   
   if (coinsValue < 1) {                                 // new piece
       Serial.println("not enough money");
       return;
   }

  //pressing the key -> assigning the filename
  char customKey = myKeypad.waitForKey();

  if (customKey != NO_KEY)
  {

Another diagnostic approach is to try this, without any keypadInput()

      // if coins value is enough to make a call - dial
  if (coinsValue >= 1)
  {
    lcd.setCursor (7, 0);       // go to start of 2nd line
    lcd.print("Skambink!");
    playSound("test.wav");
  }

It would be much better if you separated the keypadInput() code into two functions. One that reads the keypad and saves the value and another that checks that saved value and plays the sound. That way you can test the two parts separately.

...R