/*
Slot Machine (Arduino Nano SLAVE)
Ver. 2023.07.11
Management of slot machine operations.
https://www.arduino.cc/reference/en/
Electrical Components Arduino Nano runs three stepper motors and a sound card.
Arduino NANO
https://store-usa.arduino.cc/products/arduino-nano?selectedStore=us
Three stepper motors, 4SHG-240A46S
Three stepper motor drivers, DRV8825
https://www.pololu.com/product/2133
DFPlayer Mini MP3 Player
https://wiki.dfrobot.com/DFPlayer_Mini_SKU_DFR0299
Three Optic Sensors
Switch
In PNP/NPN out NPN OUT Optical Isolation Board 4ch 5V to 12V Digital logic level conversion module
https://www.aliexpress.com/item/1005003772680859.html
Arduino Nano | DFPlayer
----------------------------
2 (RX) | 3 (TX)
3 (TX) | 2 (RX)
GND | 7 GND
Arduino Nano | 2560
----------------------------
(RX) | 14 (TX)
(TX) | 15 (RX)
GND | GND
# -------------------------- #
# Design by Ephraim Herman #
# -------------------------- #
*/
#define ADDRESS 0x27
#include <Wire.h>
#include <SoftwareSerial.h>
#include "DFRobotDFPlayerMini.h"
#include <LiquidCrystal_I2C.h>
SoftwareSerial DFPlayerSerial(2, 3); // RX pin 2 ==> DFPlayer TX pin 3, TX pin 3 ==> to DFPlayer RX pin 2
//SoftwareSerial Mega2560Serial(5, 6); // RX pin 4 ==> Mega2560 TX pin 14, TX pin 5 ==> to Mega2560 RX pin 15
DFRobotDFPlayerMini DF_Player;
LiquidCrystal_I2C lcd(ADDRESS, 16, 2); // set the LCD ADDRESS to 0x27 for a 16 chars and 2 line display
const byte customChar[5][8] = {
{0x00, 0x11, 0x09, 0x0D, 0x16, 0x12, 0x11, 0x00},
{0x00, 0x1F, 0x11, 0x11, 0x1D, 0x01, 0x1F, 0x00},
{0x00, 0x1F, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00},
{0x00, 0x0E, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00},
{0x00, 0x1F, 0x11, 0x11, 0x11, 0x11, 0x1F, 0x00}
};
const String MELODY_NAME[] = {
"Coin Fall melody",
"Wheel Run melody",
"Wheel Stop melod",
"No Win melody ",
"Any Win melody ",
"Big Win melody "
};
const int LIGT1_PIN = 9;
const int LIGT2_PIN = 10;
const int LIGT3_PIN = 11;
const int LIGT4_PIN = 12;
const int CoinFall_melody = 0;
const int WheelRun_melody = 1;
const int WheelStp_melody = 2;
const int BigWin_melody = 3;
const int AnyWin_melody = 4;
const int NoWin_melody = 5;
const int TOTAL_MELODES = 6;
const int BUSY_TIME[TOTAL_MELODES] = {1050, 13100, 350, 9850, 1700, 2350};
boolean DF_Player_OK;
boolean lcdOK = false;
boolean Silent_Mode_OFF = true;
boolean startNewAction = false;
int actionNumber = 9;
int currentLight = 0;
unsigned long LightControlTime = 0;
unsigned long DF_Player_Busy_Time = 0;
unsigned long resetActionTime = 0;
char* dynamicArray; // Dynamic character array
int bufferSize = 20; // Initial buffer size
int frontIndex = 0; // Front index of the FIFO queue
int rearIndex = 0; // Rear index of the FIFO queue
int inValue = 0;
int lastInValue = 1;
char result[80];
void(* resetFunc) (void) = 0;
void enqueue(char c) {
// Check if the array is full, if so, increase the buffer size
sprintf(result, "Stor melody: #%d", c);
LCDprint(0, 0, String(result));
if ((rearIndex + 1) % bufferSize == frontIndex) {
bufferSize *= 2; // Double the buffer size
dynamicArray = (char*)realloc(dynamicArray, bufferSize * sizeof(char));
}
// Add the character at the rear index
dynamicArray[rearIndex] = c;
rearIndex = (rearIndex + 1) % bufferSize;
}
char dequeue() {
// Check if the array is empty
if (frontIndex == rearIndex) {
frontIndex = 0;
rearIndex = 0;
return '\0'; // Return null character if the array is empty
}
// Retrieve the character at the front index
char frontChar = dynamicArray[frontIndex];
frontIndex = (frontIndex + 1) % bufferSize;
return frontChar;
}
void playMelody(int trackNum) {
LCDprint(0, 1, MELODY_NAME[trackNum + 1]);
if (DF_Player_OK && Silent_Mode_OFF) {
DF_Player.volume(20);
DF_Player.play(trackNum + 1);
}
}
void runAction() {
if (DF_Player_Busy_Time < millis()) {
if (frontIndex != rearIndex) {
actionNumber = int(dequeue()) - 48;
playMelody(actionNumber);
if (actionNumber < TOTAL_MELODES) DF_Player_Busy_Time = millis() + BUSY_TIME[actionNumber];
}
}
if (LightControlTime < millis()) {
switch (actionNumber) {
case CoinFall_melody :
case WheelRun_melody :
case WheelStp_melody :
digitalWrite(currentLight + LIGT1_PIN, LOW);
currentLight = (currentLight + 1) % 4;
digitalWrite(currentLight + LIGT1_PIN, HIGH);
LightControlTime = millis() + 100;
break;
case NoWin_melody :
digitalWrite(currentLight + LIGT1_PIN, LOW);
currentLight = (currentLight + 1) % 4;
LightControlTime = millis() + 250;
break;
case AnyWin_melody :
case BigWin_melody :
currentLight = random(LIGT1_PIN, LIGT4_PIN + 1);
digitalWrite(currentLight, !digitalRead(currentLight));
LightControlTime = millis() + 10;
break;
default:
digitalWrite(currentLight + LIGT1_PIN, LOW);
currentLight = (currentLight + 1) % 4;
digitalWrite(currentLight + LIGT1_PIN, HIGH);
LightControlTime = millis() + 1000;
break;
}
}
}
void LCDprint(int x, int y, String anyMessage) {
if (lcdOK) {
lcd.setCursor(x, y);
lcd.print(anyMessage);
}
}
void setLCD(uint8_t anyAddress) {
Wire.beginTransmission(ADDRESS);
if (Wire.endTransmission() == 0) {
lcdOK = true;
lcd.init();
lcd.backlight();
lcd.clear();
for (int i = 0; i < 5; i++) lcd.createChar(i, customChar[i]);
lcd.setCursor(11, 0);
for (int i = 4; i >= 0; i--) lcd.write((uint8_t)i);
LCDprint(0, 0, "LCD is OK");
} else {
lcdOK = false;
}
}
void setup() {
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, LOW);
Wire.begin();
setLCD(ADDRESS);
dynamicArray = (char*)malloc(bufferSize * sizeof(char));
Serial.begin(9600);
while (!Serial) delay(1);
DFPlayerSerial.begin(9600);
while (!DFPlayerSerial) delay(1);
DF_Player_OK = DF_Player.begin(DFPlayerSerial);
if (DF_Player_OK) {
LCDprint(0, 1, "DFplayer online ");
DF_Player.setTimeOut(500);
DF_Player.volume(20);
} else {
LCDprint(0, 1, "DFplayer offline");
digitalWrite(LED_BUILTIN, HIGH);
}
pinMode(LIGT1_PIN, OUTPUT);
digitalWrite(LIGT1_PIN, HIGH);
pinMode(LIGT2_PIN, OUTPUT);
digitalWrite(LIGT2_PIN, LOW);
pinMode(LIGT3_PIN, OUTPUT);
digitalWrite(LIGT3_PIN, LOW);
pinMode(LIGT4_PIN, OUTPUT);
digitalWrite(LIGT4_PIN, LOW);
LightControlTime = millis() + 1000;
}
void loop() {
if (Serial.available()) {
inValue = Serial.read(); // Read data from Device 1
if (inValue > 47 && inValue < 54) {
resetActionTime = millis() + 60000;
if (inValue != lastInValue) {
frontIndex = 0;
rearIndex = 0;
DF_Player_Busy_Time = millis() - 1;
}
enqueue(char(inValue));
} else if (inValue == 83) {
LCDprint(0, 1, "Silent mode ON ");
Silent_Mode_OFF = false;
} else if (inValue == 86) {
LCDprint(0, 1, "Silent mode OFF ");
Silent_Mode_OFF = true;
} else if (inValue == 90) {
LCDprint(0, 1, "Reset Arduino ");
delay(2000);
resetFunc();
} else {
if (inValue != 10) {
lcd.clear();
sprintf(result, "Income char #%d", inValue);
LCDprint(0, 0, String(result));
LCDprint(0, 1, "Invalid Command!");
}
}
}
if (resetActionTime < millis() && actionNumber != 0) actionNumber = 99;
runAction();
}