#include <Wire.h>
#include <EEPROM.h>
#include <LiquidCrystal_I2C.h>
// Set the LCD address to 0x27 for a 16 chars and 2 line display
LiquidCrystal_I2C lcd(0x27, 16, 2);
const int
hopperRelay = 8,
coinPin = 2,
hopperPin = 3,
pinMinus = 4,
pinPlus = 5,
pinStart = 6,
debounceDelay = 200,
tMultiplier = 50;
int
modeState = EEPROM.read(0),
hopperValue,
delayTimer,
settingsMode,
state_btnMinus,
state_btnPlus,
state_btnStart,
lastState_btnMinus,
lastState_btnPlus,
lastState_btnStart,
hopperErrorTime = 10000,
btnHold;
volatile int
targetCount,
currentCount;
volatile unsigned long
lastInsert,
lastRelease,
previewsTime,
timeNow;
volatile bool
coinInserted,
coinReleased;
bool
hopperError,
doneMessage;
void coinInterrupt() {
timeNow=millis();
if(timeNow - lastInsert >= 20 && modeState == 0){
targetCount++;
lastInsert = timeNow;
coinInserted = true;
}
}
void hopperInterrupt() {
timeNow=millis();
if(timeNow - lastRelease >= 200){
currentCount+=hopperValue;
lastRelease = timeNow;
coinReleased = true;
}
}
void (*resetFunc)(void) = 0; //declare reset function @ address 0
void btnFunctions() {
state_btnMinus = digitalRead(pinMinus);
state_btnPlus = digitalRead(pinPlus);
state_btnStart = digitalRead(pinStart);
switch (modeState) {
case 0: // If Changer Mode
if (lastState_btnStart != state_btnStart && state_btnStart == LOW) {
if (settingsMode != 0) {
settingsMode = 0;
EEPROM.update(1, hopperValue);
EEPROM.update(2, delayTimer);
lcd.clear();
modeSwitch();
} else if (digitalRead(hopperRelay) == HIGH) {
startChanger();
}
delay(debounceDelay);
}
if (lastState_btnMinus != state_btnMinus && state_btnMinus == LOW) {
if (settingsMode == 1) {
switch (hopperValue) {
case 1:
hopperValue = 5;
break;
case 5:
hopperValue = 10;
break;
case 10:
hopperValue = 20;
break;
default:
hopperValue = 1;
break;
}
lcd.setCursor(0, 1);
lcd.print("Hopper Value: ");
lcd.setCursor(14, 1);
lcd.print(hopperValue);
} else {
lcd.setCursor(0, 1);
lcd.print("Hopper Value: ");
lcd.setCursor(14, 1);
lcd.print(hopperValue);
settingsMode = 1;
}
delay(debounceDelay);
}
if ((lastState_btnPlus != state_btnPlus && state_btnPlus == LOW) || (lastState_btnPlus == state_btnPlus && state_btnPlus == LOW)) {
if (settingsMode == 2) {
delayTimer++;
if (delayTimer > 60) {
delayTimer = 1;
}
lcd.setCursor(0, 1);
lcd.print("Start Delay: ");
lcd.setCursor(13, 1);
lcd.print(delayTimer);
lcd.print("s");
} else {
lcd.setCursor(0, 1);
lcd.print("Start Delay: ");
lcd.setCursor(13, 1);
lcd.print(delayTimer);
lcd.print("s");
settingsMode = 2;
}
delay(debounceDelay);
}
break;
case 1: // If Counter Mode
if (lastState_btnStart != state_btnStart && state_btnStart == LOW) {
if (currentCount >= targetCount) {
currentCount = 0;
}
switch (digitalRead(hopperRelay)) {
case 1:
digitalWrite(hopperRelay, LOW);
digitalWrite(LED_BUILTIN, LOW);
break;
case 0:
digitalWrite(hopperRelay, HIGH);
break;
}
lastRelease = timeNow;
hopperError = false;
EEPROM.update(3, targetCount / tMultiplier);
counterDisplay();
delay(debounceDelay);
}
if ((lastState_btnMinus != state_btnMinus && state_btnMinus == LOW) || (lastState_btnMinus == state_btnMinus && state_btnMinus == LOW)) {
if (digitalRead(hopperRelay) == LOW) {
digitalWrite(hopperRelay, HIGH);
} else{
switch (hopperValue) {
case 1:
hopperValue = 5;
break;
case 5:
hopperValue = 10;
break;
case 10:
hopperValue = 20;
if(targetCount % 20 != 0){
targetCount+=tMultiplier;
}
break;
default:
hopperValue = 1;
break;
}
}
currentCount = 0;
lcd.setCursor(0,0);
lcd.print("COIN VALUE : "+String(hopperValue)+" ");
lcd.setCursor(0, 1);
lcd.print(String(targetCount)+" ==> "+String(currentCount) + " ");
delay(debounceDelay);
}
if ((lastState_btnPlus != state_btnPlus && state_btnPlus == LOW) || (lastState_btnPlus == state_btnPlus && state_btnPlus == LOW)) {
if (currentCount != 0 && digitalRead(hopperRelay) == LOW) {
digitalWrite(hopperRelay, HIGH);
}else {
if (targetCount + tMultiplier <= 1000) {
if(hopperValue == 20){
targetCount=2*tMultiplier+targetCount;
}
else{
targetCount+=tMultiplier;
}
}
else{
if(hopperValue == 20){
targetCount = tMultiplier*2;
}
else{
targetCount = tMultiplier;
}
}
}
currentCount = 0;
lcd.setCursor(0,0);
lcd.print("COIN VALUE : "+String(hopperValue)+" ");
lcd.setCursor(0, 1);
lcd.print(String(targetCount)+" ==> "+String(currentCount) + " ");
delay(debounceDelay);
}
break;
}
lastState_btnMinus = state_btnMinus;
lastState_btnPlus = state_btnPlus;
lastState_btnStart = state_btnStart;
}
void startChanger() {
if (targetCount % hopperValue == 0 && targetCount > hopperValue && targetCount > currentCount && !coinInserted) {
digitalWrite(hopperRelay, LOW);
digitalWrite(LED_BUILTIN, LOW);
lastRelease = timeNow;
coinReleased = true;
hopperError = false;
}
}
void changerMode() {
if (coinInserted) {
digitalWrite(hopperRelay, HIGH);
digitalWrite(LED_BUILTIN, LOW);
doneMessage = false;
coinInserted = false;
lcd.home();
lcd.print("Inserting... ");
lcd.setCursor(0, 1);
lcd.print("Credits: " + String(targetCount - currentCount) + " ");
}
if(coinReleased){
coinReleased = false;
hopperError = false;
digitalWrite(LED_BUILTIN, LOW);
if (currentCount >= targetCount) {
digitalWrite(hopperRelay, HIGH);
currentCount = 0;
targetCount = 0;
doneMessage = true;
}
else {
lcd.home();
lcd.print("Releasing... ");
lcd.setCursor(0, 1);
lcd.print("Credits: " + String(targetCount - currentCount) + " ");
}
}
}
void counterDisplay(){
lcd.clear();
lcd.print("COIN VALUE : "+String(hopperValue));
lcd.setCursor(0, 1);
lcd.print(String(targetCount)+" ==> "+String(currentCount) + " ");
}
void counterMode() {
if(coinReleased){
if (currentCount >= targetCount) {
digitalWrite(hopperRelay, HIGH);
}
digitalWrite(LED_BUILTIN, LOW);
hopperError = false;
lcd.setCursor(0,1);
lcd.print(String(targetCount)+" ==> "+String(currentCount) + " ");
}
}
void modeSwitch() {
targetCount = 0;
currentCount = 0;
hopperError = false;
lastInsert = 0;
lastRelease = 0;
lcd.clear();
switch (modeState) {
case 0:
hopperValue = EEPROM.read(1);
delayTimer = EEPROM.read(2);
if (hopperValue < 1 || hopperValue > 20) {
hopperValue = 1;
EEPROM.update(1, hopperValue);
}
if (delayTimer < 1 || delayTimer > 60) {
delayTimer = 1;
EEPROM.update(2, delayTimer);
}
lcd.home();
lcd.print("--PISO CHANGER--");
lcd.setCursor(0, 1);
lcd.print(" Insert 5/10/20 ");
break;
case 1:
hopperValue = 1;
targetCount = EEPROM.read(3) * tMultiplier;
if (targetCount > 10000 || targetCount < 50) {
targetCount = 50;
}
counterDisplay();
break;
default:
EEPROM.update(0, 0);
resetFunc();
}
}
void setup() {
pinMode(hopperRelay, OUTPUT);
digitalWrite(hopperRelay, HIGH);
pinMode(pinMinus, INPUT_PULLUP);
pinMode(pinPlus, INPUT_PULLUP);
pinMode(pinStart, INPUT_PULLUP);
pinMode(LED_BUILTIN, OUTPUT);
pinMode(coinPin, INPUT);
pinMode(hopperPin, INPUT);
attachInterrupt(digitalPinToInterrupt(coinPin), coinInterrupt, FALLING);
attachInterrupt(digitalPinToInterrupt(hopperPin), hopperInterrupt, FALLING);
if (digitalRead(pinMinus) == LOW && digitalRead(pinPlus) == LOW) {
modeState = !modeState;
EEPROM.update(0, modeState);
}
// initialize the LCD
lcd.init();
lcd.backlight();
modeSwitch();
}
void loop() {
timeNow = millis();
switch (modeState) {
case 0:
changerMode();
if(timeNow - lastInsert >= 500 && timeNow - lastRelease >= 500){
if (timeNow - lastRelease > hopperErrorTime && digitalRead(hopperRelay) == LOW) {
digitalWrite(hopperRelay, HIGH);
hopperError = true;
}
if (hopperError) {
if (timeNow - previewsTime > 1000) {
previewsTime = timeNow;
if (digitalRead(LED_BUILTIN) == LOW) {
digitalWrite(LED_BUILTIN, HIGH);
lcd.home();
lcd.print(" -- ERROR -- ");
lcd.setCursor(0, 1);
lcd.print(" OUT OF COINS ");
} else {
digitalWrite(LED_BUILTIN, LOW);
lcd.setCursor(0, 1);
lcd.print("Balance: " + String(targetCount - currentCount) + " ");
}
}
}
if (timeNow - lastInsert > delayTimer * 1000 && digitalRead(hopperRelay) == HIGH && hopperError == false) {
startChanger();
}
if (doneMessage){
if(!digitalRead(LED_BUILTIN) && !currentCount && !targetCount){
lcd.home();
lcd.print(" DONE ");
lcd.setCursor(0, 1);
lcd.print(" Thank You! ");
digitalWrite(LED_BUILTIN, HIGH);
}
if(timeNow - lastRelease >= 2000 && digitalRead(LED_BUILTIN)){
digitalWrite(LED_BUILTIN, LOW);
}
if(timeNow - lastRelease >= 3000){
lcd.home();
lcd.print("--PISO CHANGER--");
lcd.setCursor(0, 1);
lcd.print(" Insert 5/10/20 ");
doneMessage = false;
}
}
}
break;
case 1:
counterMode();
break;
}
if (digitalRead(pinMinus) == LOW && digitalRead(pinPlus) == LOW ) {
btnHold++;
if (btnHold == 10) {
btnHold=0;
modeState = !modeState;
modeSwitch();
}
delay(debounceDelay);
} else {
btnHold = 0;
btnFunctions();
}
}