For this post, we will build a project that detects coins that we throw into a piggy bank. An LCD screen will inform us about the value of the detected coin. We will also store the amount of money along with the date on a MicroSD card.
The path of the coin leads into the box via a ramp. One idea could be to leave the front transparent to see the path of the coins and the circuit in the money box. Also, you could include a drawer to collect the coins.
Materials needed
1 |
|
1 |
|
6 |
|
1 |
|
1 |
|
1 |
|
|
|
|
|
|
Software needed
- Arduino IDE
- cash_box Sketch
- DS3231 library (Github, developed by Jarzebski)
- Wire library (integrated)
- LiquidCrystal_I2C library (via library manager)
- SD library (integrated)
- SPI library (integrated)
The basic idea of the piggy bank
The idea for this project is to build a ramp on which a coin rolls down. It is supported at the top and bottom by a rope so that it only rests on two small points. This minimizes friction and reduces the risk of it stopping. When the coin reaches its measuring point, it will fall through an opening that is the same size. The respective proximity sensor will detect the coin and we will thus get its value. The LCD screen will display it and we will store it on the MicroSD card.
You may wonder why we used wood and not a 3D printer for the assembly. The answer is that balsa wood is very light and handy. You can make possible changes during the development of the project. This saves time and money. Of course, when the project is finished, you can design the case with a nice design for a 3D printer.
The most important parts of the structure are the ramp and the openings for the coins. This gives the dimensions of the whole box. As you can see, the coin openings are not arranged in the order of lowest to highest value, but according to the size from the smallest to the largest: 10 cts (19 mm), 5 cts (21 mm), 20 cts (22 mm), 1 euro (23 mm), 50 cts (24 mm) and 2 euros (25 mm). The reason is obvious: if we were to arrange the openings according to their value, the first one would be the 5 cents opening, which would result in the 5 cents coin and the 10 cents coin falling through this first opening, since the latter is smaller. Just like the 50-cent coin and the 1-euro coin.
In order for each sensor to properly detect its coin, we need to adjust it. In my case, I had to make some changes to the design first because it was impossible to adjust the sensors. The emitting diode was not covered causing the signal to be emitted in all directions and some sensors detected the signal emitted by other sensors. The first solution I thought of and implemented (as you can see in the pictures) was to put some walls between each sensor. This improved the behavior, but it was still not possible to tune them all well. The signals in the box were bouncing off in all directions. So I implemented what the manual of the module says. I used cylinders of about 8 mm (the correct thickness of the BIC pins) of white cardboard.
I tried black, but it must be because this color absorbs light. This makes the light coming out of the tube very weak and so the receiver does not receive a usable signal. I then implemented it with the white color. The signal emitted by each sensor that leaves the tube bounces off the wall and returns only to the receiver of its module. That's the beauty of implementing such projects and looking for solutions to the problems. At first, you're a bit desperate, but when you find the solution, it's very reassuring. Now all the sensors can be set up properly on an individual basis. It is important to read the manuals of the different modules.
Circuit and description of the operation
As can be seen in the schematic, the circuit consists of 6 proximity sensors for coin detection, a MicroSD card reader module, a Real Time Clock (RTC) module, a Nano V3 CH340 microcontroller, and a 16x2 LCD display with an I2C communication module.
The entire circuit is powered by 5V. The first thing we should know about proximity detectors is that the output of this module has a HIGH level when it does not detect an object. Conversely, it has a LOW level when it detects an object. To make sure that the module has detected an object and it was not an error, we will program a double check in the sketch. There are explanations directly in the sketch. The outputs of the sensors are connected to digital pins of the microcontroller, which must be configured as inputs. When a signal arrives at the microcontroller, we perform the double check, and send the value of the coin to the LCD screen for output. Also, the value is sent to the SD module to be stored.
Now follows the description of the sketch. To begin, the first thing to do is to load all the libraries that we need for the correct operation of the components.
/* Necessary libraries */ #include <Wire.h> // Library for I2C Bus #include <LiquidCrystal_I2C.h> // Library for I2C LCD display #include <DS3231.h> // Library for Real Time Clock module #include <SD.h> // Library for SD module #include <SPI.h> // Library for SPI Communications
After loading the libraries, we have to configure the display. We specify the I2C address and the number of columns and rows.
/* LCD display implement with 20 column and 2 lines */ LiquidCrystal_I2C lcd = LiquidCrystal_I2C (0x27, 20, 2);
The next step is to create a clock-object for the RTC module to apply the required library methods and a variable to store the date of each currency reading.
/* Instance for the clock module object DS3231 */ DS3231 clock; // Implementation of the "clock" object. RTCDateTime dt; // Definition of the variable to store the date
As you can see in the code, we now need to define the numbers of the digital pins of the microcontroller to which we connect the proximity sensors. We also define the initial state of the variable for coin detection. At the beginning this should of course be false since no coin has been detected yet.
/* Define microcontroller pins to coin sensors */ #define sensor_10_cent 2 #define sensor_5_cent 3 #define sensor_20_cent 4 #define sensor_1_euro 5 #define sensor_50_cent 6 #define sensor_2_euro 7 /* Define the initial state value of the sensors variable (no coin detected) */. bool val_sensor_10_cent = false; bool val_sensor_5_cent = false; bool val_sensor_20_cent = false; bool val_sensor_1_euro = false; bool val_sensor_50_cent = false; bool val_sensor_2_euro = false;
Before we move on to the setup() method, we need to implement an object for our MicroSD card reader module and initialize a variable that will store the total value of the coins:
/* SD card module object instance */ File object_card_file; // Implementation of the "object_card_file" object. /* Variable to store the value of saved money */. float money_saved = 0;
After we have defined the required variables, objects and parameters, we need to configure the initialization of the circuit components in the setup() method. First we initialize the serial console so that we can see the messages sent by the microcontroller.
Serial.begin(9600); // Initializing the Serial Console
Now we have to initialize the pins of the sensors as inputs:
/* Define the microcontroller pins of the sensors as input */. pinMode(sensor_10_cent, INPUT); pinMode(sensor_5_cent, INPUT); pinMode(sensor_20_cent, INPUT); pinMode(sensor_1_euro, INPUT); pinMode(sensor_50_cent, INPUT); pinMode(sensor_2_euro, INPUT);
Next we initialize the LCD display. We activate the display illumination and display a message while all other modules of the circuit are initialized:
/* LCD display */ lcd.init(); // Initializing LCD display lcd.backlight(); // LCD display blacklight on lcd.print("Initializing ... "); // Message on the LCD display while the project is initializing
Now we have to initialize the RTC module, we do this with the following line:
/* Initializing DS3231 Module */ clock.begin();
The last module to be initialized is the MicroSD card reader. First we display the message on the serial console that the initialization of the module is starting. The condition checks if the CS pin (Chip Select) of the module is connected to pin 10 of the microcontroller. Returns the function false is returned, an error is displayed on the serial monitor and an error message is returned with while(1) an infinite loop is executed. Otherwise a successful initialization is displayed and the program continues:
/* Initialize card module */ Serial.print ("Initializing microSD module ..."); // Initialization message microSD module in Serial Monitor if (!SD.begin(10)) { // If there isn't microSD module Serial.println("Failed to initialize."); // Show "Initialization failure" message while(1); } Serial.println("Successful initialization); // Successful initialization
Then we execute the method read_data() to read the contents of the cardfile.txt file into the object object_card_file object. With the method SD.open we open the file cardfile.txt and display the contents of the file in the serial console. Then we close the contents of the object object_card_file. If the object is not available, the message that there is no information is displayed.
read_data(); // Go to read_data method
.
.
.
void read_data() { // read_data method object_card_file = SD.open("cardfile.txt"); // Open the file "cardfile.txt if (object_card_file) { // If the object file exists while (object_card_file.available()) { // Loop to display the file data on the serial console. Serial.write(object_card_file.read()); } object_card_file.close(); // Close the file. } else { Serial.println("No file to display"); // Display serial console message of file failure. } }
If the read_data()method is finished, the program returns to the starting point and positions the cursor in the display in column 0 and line 0, i.e. in the first position of the LCD screen with the instruction lcd.setCursor(0, 0). After that the current values are output:
void lcd_message() { // lcd_message method. lcd.clear(); // Clean LCD display. lcd.setCursor(0, 0); // Position the cursor in real column 1 and real row 1. lcd.print("Total money saved: "); // Display the message on screen lcd.setCursor(1, 1), // Position the cursor in real column 2 and real row 2. lcd.print(money_saved); // Display the message on screen. lcd.setCursor(10, 1); // Position the cursor in real column 10 and real row 2. lcd.print("Euros"); // Display the message on screen. save_data(); // Go to the save_data method. }
The comments of the preceding method sufficiently explain the actions of the individual lines. At the end of this method there is a call to the save_data(), also there the commands are commented in detail:
void save_data() { // save_data method. object_card_file = SD.open("cardfile.txt", FILE_WRITE); // If there is a card, open the file "cardfile.txt" or create it and open it if it does not exist. if(object_card_file) { // If the file exists, the following is written in a single line in the file. object_card_file.print("Total money saved: "); // Write the text between the quotation marks. object_card_file.print(money_saved); // Write the value of money_saved variable. object_card_file.print(" Euros ---> Date: "); // Write the text between the quotation marks. object_card_file.print(dt.year); // Write de year. object_card_file.print("-"); // Write the text between the quotation marks. object_card_file.print(dt.month); // Write de month. object_card_file.print("-"); // Write the text between the quotation marks. object_card_file.print(dt.day); // Write the day. object_card_file.println(" "); // Type a blank space and line break. object_card_file.close(); // Close the file. } else { Serial.println("Fail open file"); // Display Serial Console message of file open failure. } }
With the execution of the previous method the setup()block of our code ends and the loop()-method is executed. This method checks if a coin was really detected, stores the value in the variable money_saved, displays it on the LCD screen and stores the date and time in the corresponding variable. Let's see, each of the 6 sensors detects its coin. The code below applies to each sensor. Just change the sensor variable and remember that the sensor sends a HIGH level signal to the microcontroller when it does not detect a coin and a LOW level signal when it detects a coin.
For the detection we use two double conditions, the first one being if-statement checks whether HIGH level is still present at the corresponding pin. If this is the case, the value of the variable val_sensor_10_cent is not changed and the condition is left. But if the level changes to LOW, the else-part is executed. This checks whether the reading is incorrect. Then it waits 50 microseconds and checks the reading again, this time using the second if-statement. If the pin continues to have a HIGH level, it means that the previous state change was an erroneous reading. Then the program continues without changing the value of the variable val_sensor_10_cent and leaves the condition. If, on the other hand, a LOW level is present, it means that it continues to detect an object (in this case the coin) and changes the value of the variable val_sensor_10_cent to true changes.
/**** 10 cent coin detection process ****/ // When the sensor detects an object, it sends a LOW level to the Arduino. if(digitalRead(sensor_10_cent)) { // Check sensor: if sensor output is HIGH, val_sensor_10_cent = false; // no coin is detected and jumps to the next coin check. } else { // Then when currency is detected, the sensor reading is rechecked. delayMicroseconds(50); // Pause for 50 milliseconds to check again. if(digitalRead(sensor_10_cent)) { // if now the sensor output is HIGH, val_sensor_10_cent = false; // then the first reading was erroneous and } // it is out of the double conditional. else { // But if the reading is low again, val_sensor_10_cent = true; // then if currency was detected and the variable changes to TRUE. } }
If the value of the variable val_sensor_10_cent is set to true the following simple condition is executed and displays on the serial monitor the coin detection message of 10 cents. Then the value of the variable money_saved is loaded, to which the 10 cents are added. The new value is then stored in the variable. Finally, the method described above lcd_message method described above is executed.
if(val_sensor_10_cent == true) { // If the variable has the value TRUE, Serial.println("10 cent coin detected"); // Message on the serial console of the detected coin value. money_saved = (money_saved + 0.10); // Add the value of the detected currency to the variable and save it again. lcd_message(); // Go to the lcd_message method. }
These two conditions are implemented for each of the six sensors and the explanation is the same for all of them. At the end of the loop()-method the value of the current day, month, year and time is stored in the variable dt which we declare in the RTC module, which is row:
dt = clock.getDateTime(); // Save the data provided by the Real Time Clock module of the time and date in the variable dt.
I hope you found this project interesting and I hope you enjoy it.
2 Reacties
Andreas Wolter
@Günther Hinneburg: das sieht nicht nach Fehlermeldungen aus. Könnten Sie die genaue Meldung posten?
Haben Sie in den Voreinstellungen für “Ausführliche Ausgabe während” einen Haken gesetzt?
Da es scheinbar funktioniert, gehe ich davon aus, dass es sich nicht um schwerwiegende Probleme handelt.
Meistens sind es fehlerhafte Typkonvertierungen oder etwas ähnliches.
Grüße,
Andreas Wolter
AZ-Delivery Blog
Günther Hinneburg
Sehr geehrte Damen und Herren, ich habe den oben genannten Sketch kopiert. Funktionierte auch alles super. Jedoch habe ich ein Problem beim kompilieren erhalte ich zwei Meldungen:
18 | DS3231 clock; // Implementation of the “clock” object.
19 | RTCDateTime dt; // Definition of the variable to store the date
Was mache ich falsch?
Mit freundlichen Grüßen
Günther Hinneburg