Coin acceptor+LCD1602+SD card module

Hi there everyone! I am building a piggy bank with a coin acceptor from sparkfun (6 coin types). Coin Acceptor - Programmable (6 coin types) - COM-11636 - SparkFun Electronics

I want to add a sd function so the piggy bank knows how much money there is at all time (in case of powering off). I am stuck where I plug everything back on and I cant continue the count from the sd file. Help my understanding the code. I also get two weird characters after the amount when I start up the system. Ex: 17XX USD

#include <LiquidCrystal.h>
#include <SD.h>

File myFile;
// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(8, 7, 6, 5, 4, 3);

const int coinInt = 0; 
//Attach coinInt to Interrupt Pin 0 (Digital Pin 2). Pin 3 = Interrpt Pin 1.

volatile float coinsValue = 0;
//Set the coinsValue to a Volatile float
//Volatile as this variable changes any time the Interrupt is triggered
int coinsChange = 0;                  
//A Coin has been inserted flag

long previousMillis = 0;        // will store last time LED was updated

// the follow variables is a long because the time, measured in miliseconds,
// will quickly become a bigger number than can be stored in an int.
long interval = 1000;    
void setup()
{
  if (!SD.begin(10)) {
    Serial.println("initialization failed!");
    return;
  }
  Serial.println("initialization done.");
  Serial.begin(9600);                 
  lcd.begin(16, 2);
  //Start Serial Communication
  lcd.setCursor(0, 0);
  lcd.print("Credit:");
  lcd.setCursor(0, 1);
  // re-open the file for reading:
  myFile = SD.open("coins.txt");
  if (myFile) {
    Serial.println("coins.txt:");

    // read from the file until there's nothing else in it:
    while (myFile.available()) {
      lcd.print(char(myFile.read()));
    }
    lcd.print(" USD");
    
    
    // close the file:
    myFile.close();
  } 
  else {
    // if the file didn't open, print an error:
    Serial.println("error opening test.txt");
  }
  attachInterrupt(coinInt, coinInserted, RISING);   
  //If coinInt goes HIGH (a Pulse), call the coinInserted function
  //An attachInterrupt will always trigger, even if your using delays
}

void coinInserted()    
//The function that is called every time it recieves a pulse
{
  coinsValue = coinsValue + 1;  
  //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.clear();
    Serial.print("Credit: ");
    Serial.print(coinsValue,0);
    Serial.println(" USD");
    lcd.setCursor(0, 0);
    lcd.print("Credit:");
    lcd.setCursor(0, 1);
    lcd.print(coinsValue,0);
    lcd.print(" USD");  
 

  }
  
    unsigned long currentMillis = millis(); 

    if(currentMillis - previousMillis > interval) {
      // save the last time you blinked the LED 
      previousMillis = currentMillis;        
      SD.remove("coins.txt");
      myFile = SD.open("coins.txt", FILE_WRITE);

      // if the file opened okay, write to it:
      if (myFile) {
        Serial.print("Writing to coins.txt...");
        myFile.println(coinsValue,0);

        // close the file:
        myFile.close();
        Serial.println("done.");
      } 
      else {
        // if the file didn't open, print an error:
        Serial.println("error opening coins.txt");
      } 

    }
}
  coinsValue = coinsValue + 1;

You know that the language we use is not called C=C+1. Don't you suppose that there is a reason for that?

Why is coinsValue a float? You are not using it as such.

int coinsChange = 0;

Used by loop() and coinInserted(), so this must be volatile, too.

I am stuck where I plug everything back on and I cant continue the count from the sd file.

The only thing you do with what you read from the file is print it to the LCD. Why is that?

I also get two weird characters after the amount when I start up the system. Ex: 17XX USD

Get them where? I'm guess that they are the carriage return and line feed you read from the file, since you print EVERYTHING to the LCD.

pjac82:
I want to add a sd function so the piggy bank knows how much money there is at all time (in case of powering off).

Sounds like a perfect task for the built-in EEPROM.

You know that the language we use is not called C=C+1. Don't you suppose that there is a reason for that?

Why is coinsValue a float? You are not using it as such.

Well the code is working for me that way..!

The thing with the LCD is a huge headache cuz I have searched the Internet for two days now and still no answer on how to read just one line from a file on the sd card!

Sounds like a perfect task for the built-in EEPROM.

In my code it saves the coin value every 5 sec. I would like to power off if the signal input exceeds a certain amount of time when not active! Then EEPROM would be great!! Just don't know how to code that..

There's no need to save anything to flash or EEPROM unless the value has changed. You're only shortening the life of your storage device by writing to it too often.

There's a standard EEPROM library included in the Arduino package which has functions to write a byte to an address in EEPROM.
The reference is here http://arduino.cc/en/Reference/EEPROM and there are examples sketches included in the Arduino IDE.

You can also check this library which makes it easier to write more than a single byte
http://playground.arduino.cc/Code/EEPROMWriteAnything

There's no need to save anything to flash or EEPROM unless the value has changed. You're only shortening the life of your storage device by writing to it too often.

Thanks for the reply! Can you please give me a hint on how to write to EEPROM when the value has changed? It goes on loop?! Don't get that?

I am asking cuz when I put a coin that has 10 impulses then the EEPROM will be used ten times? I am maybe asking dumb questions here...but I really would like to understand!

Create a variable that holds the last value that you wrote to the eeprom, let's call it lastValue.
You already have a function that writes to eeprom every five seconds, expand that so that every time you are about to write to eeprom you also check if the value of coinsValue is different from lastValue.
If the values are different there has been a change so you should write coinsValue to eeprom and set lastValue to the same value as coinsValue.
If the values are equal there hasn't been a change (coin inserted) since you last wrote to eeprom and you don't have to write anything.

  1. I think you write on SD also carriage-return and/or new-line

myFile.println(coinsValue,0);

try myFile.print(coinsValue); removing "ln" and coinsValue must be unsigned int or unsigned long; you use float and after you don't use decimal !!!

  1. When you read from SD on setup() remember to load the value also on coinsValue, not only on LCD (as @PaulS tell you)
  2. eeprom is the best technique in this case I think, as @Perhof tell you.
    You can try with SD and after (when all is okay) use eemprom instead SD.
    On SD you can read the file from a PC.

Thank you so far! I will try out the advices! :sweat_smile:

But any advices on how to start the count from the sd (after powering off/on). I manage to show the last saved credit after powering on. I just need to start the count from that saved value... :fearful:

Create a variable that holds the last value that you wrote to the eeprom, let's call it lastValue.
You already have a function that writes to eeprom every five seconds, expand that so that every time you are about to write to eeprom you also check if the value of coinsValue is different from lastValue.
If the values are different there has been a change so you should write coinsValue to eeprom and set lastValue to the same value as coinsValue.
If the values are equal there hasn't been a change (coin inserted) since you last wrote to eeprom and you don't have to write anything.

For example lke this? Only modified for my code?

const int  buttonPin = 2;    // the pin that the pushbutton is attached to
const int ledPin = 13;       // the pin that the LED is attached to

// Variables will change:
int buttonPushCounter = 0;   // counter for the number of button presses
int buttonState = 0;         // current state of the button
int lastButtonState = 0;     // previous state of the button

void setup() {
  // initialize the button pin as a input:
  pinMode(buttonPin, INPUT);
  // initialize the LED as an output:
  pinMode(ledPin, OUTPUT);
  // initialize serial communication:
  Serial.begin(9600);
}


void loop() {
  // read the pushbutton input pin:
  buttonState = digitalRead(buttonPin);

  // compare the buttonState to its previous state
  if (buttonState != lastButtonState) {
    // if the state has changed, increment the counter
    if (buttonState == HIGH) {
      // if the current state is HIGH then the button
      // wend from off to on:
      buttonPushCounter++;
      Serial.println("on");
      Serial.print("number of button pushes:  ");
      Serial.println(buttonPushCounter);
    }
    else {
      // if the current state is LOW then the button
      // wend from on to off:
      Serial.println("off");
    }
  }
  // save the current state as the last state,
  //for next time through the loop
  lastButtonState = buttonState;

 
  // turns on the LED every four button pushes by
  // checking the modulo of the button push counter.
  // the modulo function gives you the remainder of
  // the division of two numbers:
  if (buttonPushCounter % 4 == 0) {
    digitalWrite(ledPin, HIGH);
  } else {
   digitalWrite(ledPin, LOW);
  }
 
}

I am working after examples.. :blush:

It's hard to point you in the right direction since you're only copying and pasting between examples instead of actually trying to write any code.

I read the code in your first post again and realize you already have a flag indicating that money has been insterted. You seem to use it only for displaying text on your LCD but you can also use it to decide if you should update SD/EEPROM or not.

If you want to update your SD card (or the EEPROM) "ONLY when a coin has been inserted AND at most one time every five seconds" you can add the (coinsChange == 1) critera to the interval criteria you already have in your code:
if(currentMillis - previousMillis > interval)

Change it to something like this

if((currentMillis - previousMillis > interval) && (coinsChange == 1))

Of course you will have to change the contents of the interval variable to 5000 instead of 1000 if you want the minimum interval to be five seconds.

okay.. the last tip didnt work for me... or I guess I am such a newbie that I pretty much dont know what I am doing... anyways this code below is my working code but I miss one useful function. I need to start the count from the sd card. I did it very easy for myself because the file updates itself and contrains only one line of number. I am reading the number on my lcd when I power the arduino on.

I am trying to write something like this but with no luck.. For example I start out with Credit 75. I put in a coin that has 10 as the value (pulses) then it goes down to 62. The second coin I insert is correct..

#include <LiquidCrystal.h>
#include <SD.h>

File myFile;
LiquidCrystal lcd(8, 7, 6, 5, 4, 3);

const int coinInt = 0; 
//Attach coinInt to Interrupt Pin 0 (Digital Pin 2). Pin 3 = Interrpt Pin 1.

volatile float coinsValue= 0;
volatile float coinsChange = 0; 

byte runXTimes = 1; 

void setup()
{
  if (!SD.begin(10)) {
    Serial.println("initialization failed!");
    return;
  }
  Serial.println("initialization done.");
  Serial.begin(9600);                 
  lcd.begin(16, 2);
  lcd.setCursor(0, 0);
  lcd.print("Credit:");
  lcd.setCursor(0, 1);
  myFile = SD.open("coins.txt");
  if (myFile) {
    Serial.println("coins.txt:");

    while (myFile.available()) {
      lcd.print(char(myFile.read()));
    }
    lcd.print(" USD");

    myFile.close();
  } 
  else {
    Serial.println("error opening test.txt");
  }
  attachInterrupt(coinInt, coinInserted, RISING);   
}

void coinInserted()    
{
  myFile = SD.open("coins.txt");
  if (myFile) {
    Serial.println("coins.txt:");

    while (myFile.available()) {
      lcd.print(char(myFile.read()));
      coinsChange = 1;     
      if (runXTimes)
      {
        runXTimes--;
        coinsValue = char(myFile.read());
      } 
      else {
        coinsValue = coinsValue + 1;

      }
      lcd.print(" USD");

      myFile.close();
    } 

  }
  attachInterrupt(coinInt, coinInserted, RISING);   
}

void loop()
{

  if(coinsChange == 1)          

  {
    coinsChange = 0;              
    lcd.clear();
    Serial.print("Credit: ");
    Serial.print(coinsValue,0);
    Serial.println(" USD");
    lcd.setCursor(0, 0);
    lcd.print("Credit:");
    lcd.setCursor(0, 1);
    lcd.print(coinsValue,0);
    lcd.print(" USD");  

    SD.remove("coins.txt");
    myFile = SD.open("coins.txt", FILE_WRITE);

    if (myFile) {
      Serial.print("Writing to coins.txt...");
      myFile.print(coinsValue,0);

      myFile.close();
      Serial.println("done.");
    } 
    else {
      Serial.println("error opening coins.txt");
    }    
  } 
}

but I miss one useful function. I need to start the count from the sd card.

Then you need to do more than just print the data to the LCD. You need to save the data in an array, and then convert that to a value that you store in a variable, using atoi() or atof(), depending on how you write the data to the file:

  myFile = SD.open("coins.txt");
  if (myFile) {
    Serial.println("coins.txt:");

    while (myFile.available()) {
      lcd.print(char(myFile.read()));
    }
    lcd.print(" USD");

    myFile.close();
  }

Hello PaulS! I thought I had it here...?

void coinInserted()    
{
  myFile = SD.open("coins.txt");
  if (myFile) {
    Serial.println("coins.txt:");

    while (myFile.available()) {
      lcd.print(char(myFile.read()));
      coinsChange = 1;     
      if (runXTimes)
      {
        runXTimes--;
        coinsValue = char(myFile.read());
      } 
      else {
        coinsValue = coinsValue + 1;

      }
      lcd.print(" USD");

      myFile.close();
    } 

  }

You need to store EVERY character read from the file in a variable. You are reading and printing one character, then reading, using, and not printing one character. Neither is correct. Print EVERY character. Use EVERY character.

I have searched the Internet on how to handle the sd card with arduino! I can't find any examples for my case! So reading every character does not help me because I thought I read all the characters...? Let say; if the file contains number 24! I manage with my embarrassing code to read 24! Not 2 and 4! So I don't understand your answer.. Sorry!!!! ;(

So reading every character does not help me because I thought I read all the characters...?

You are. But, the way you are doing it is like taking cookies out of a bag. One for me. One for you. One for me. One for you...

If the cookies had letters on them, the letters on your cookies wouldn't tell you anything. The letters on my cookies wouldn't tell me anything.

If, on the other hand, we laid out all the cookies in a row, read the message, and then ate them, we'd get the message that the cookies had on them and we'd get a snack, too.

You being the part of the code that tries to interpret the data in the file, and me being the part of the code that displays the message on the LCD.

You want something more like this:

    char contents[20];
    byte index = 0;

    while (myFile.available()) 
    {
       contents[index] = myFile.read();
       contents[index++] = '\0'; // NULL terminate the array
    }

    int val = atoi(contents); // Convert the contents to an int

    lcd.print("Value saved: ");
    lcd.print(val);

    lcd.print("Proof: ");
    lcd.print(contents);

I have this code so far... the problem is that the credit doesnt go further than 20 and then the counter resets itself and starts counting from 0 again... What am I doing wrong??

#include <LiquidCrystal.h>
#include <SD.h>
char contents[20];
byte index = 0;


File myFile;
LiquidCrystal lcd(8, 7, 6, 5, 4, 3);

const int coinInt = 0; 
//Attach coinInt to Interrupt Pin 0 (Digital Pin 2). Pin 3 = Interrpt Pin 1.

volatile float coinsValue;
volatile float coinsChange = 0; 

byte runXTimes = 1; 

void setup()
{
  if (!SD.begin(10)) {
    Serial.println("initialization failed!");
    return;
  }
  Serial.println("initialization done.");
  Serial.begin(9600);                 
  lcd.begin(16, 2);
  lcd.setCursor(0, 0);
  lcd.print("Credit:");
  lcd.setCursor(0, 1);
  myFile = SD.open("coins.txt");
  if (myFile) {
    Serial.println("coins.txt:");

    while (myFile.available()) {
      lcd.print(char(myFile.read()));
    }
    lcd.print(" USD");

    myFile.close();
  } 
  else {
    Serial.println("error opening test.txt");
  }
  attachInterrupt(coinInt, coinInserted, RISING);   
}

void coinInserted()    
{
  myFile = SD.open("coins.txt");
  if (myFile) {
    Serial.println("coins.txt:");

    while (myFile.available()) 
    {
      contents[index++] = myFile.read();
      //contents[index++] = '\0'; // NULL terminate the array
    }

    int val = atoi(contents); // Convert the contents to an int


    if (runXTimes)
    {
      runXTimes--;
      coinsValue = val+1;

    } 
    else 
    {
      coinsValue = coinsValue + 1;
      coinsChange = 1;
    }


  } 
  myFile.close();
}



void loop()
{

  if(coinsChange == 1)          

  {
    coinsChange = 0;              
    lcd.clear();
    Serial.print("Credit: ");
    Serial.print(coinsValue,0);
    Serial.println(" USD");
    lcd.setCursor(0, 0);
    lcd.print("Credit:");
    lcd.setCursor(0, 1);
    lcd.print(coinsValue,0);
    lcd.print(" USD");  

    SD.remove("coins.txt");
    myFile = SD.open("coins.txt", FILE_WRITE);

    if (myFile) {
      Serial.print("Writing to coins.txt...");
      myFile.print(coinsValue,0);

      myFile.close();
      Serial.println("done.");
    } 
    else {
      Serial.println("error opening coins.txt");
    }    
  } 
}