#include <LiquidCrystal_I2C.h>
#include <ezButton.h>
#include <EEPROM.h>
#define COIN_PIN 2
#define BUTTON_1_PIN 4
#define BUTTON_2_PIN 5
#define MOTOR_1_PIN 6
#define MOTOR_2_PIN 7
#define MOTOR_1_PWM_PIN 10
#define MOTOR_2_PWM_PIN 11
// Initializations
LiquidCrystal_I2C lcd(0x27, 20, 4);
ezButton button1(BUTTON_1_PIN);
ezButton button2(BUTTON_2_PIN);
void (*resetFunc)(void) = 0;
// EEPROM address
const int eeprom_addr_first = 0;
const int eeprom_addr_credits = 1;
// Paper Size Prices
const int sizeShortPrice = 1;
const int sizeLongPrice = 2;
// Motor activation duration
const int size1Duration = 2000;
const int size2Duration = 3000;
unsigned long motorMillis = 0;
unsigned long motorElapsed = 0;
// Status line
unsigned long warnMillis = 0;
unsigned long warnElapsed = 0;
unsigned long animMillis = 0;
bool animFrame = 0;
bool noCredit = false;
// States
bool isFirstLaunch = true;
int credits = 0;
int dispensing = 0;
bool motor1State = false;
bool motor2State = false;
// Coin slot
unsigned long lastAction = 0; // When last action was made
unsigned long lastPulse = 0; // When last pulse was send
int pulseCount = 0; // How many pulses we got
const int pulseTimeout = 300;
const int actionTimeout = 5000;
unsigned long tempAction;
unsigned long tempPulse;
void coinInterrupt() {
lastAction = millis();
lastPulse = millis();
pulseCount++;
}
void setup() {
Serial.begin(115200);
Serial.println("[SYS] System started.");
// Pin modes
pinMode(MOTOR_1_PIN, OUTPUT);
pinMode(MOTOR_2_PIN, OUTPUT);
pinMode(8, OUTPUT);
pinMode(9, OUTPUT);
pinMode(COIN_PIN, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(COIN_PIN), coinInterrupt, RISING);
digitalWrite(8, LOW);
digitalWrite(9, LOW);
// EEPROM Initializations
Serial.print("[EEPROM] Length: ");
Serial.println(EEPROM.length());
Serial.print("[EEPROM] First launch: ");
Serial.println(EEPROM.read(eeprom_addr_first));
if (EEPROM.read(eeprom_addr_first) == 255) {
Serial.println(F("[EEPROM] System first launch. Setting inital values."));
EEPROM.put(eeprom_addr_first, isFirstLaunch);
EEPROM.put(eeprom_addr_credits, credits);
Serial.println(F("[EEPROM] Inital values set. Proceeding..."));
delay(500);
}
Serial.println(F("[EEPROM] Loading values..."));
EEPROM.get(eeprom_addr_credits, credits);
Serial.println("[EEPROM] credits: " + String(credits));
// LCD Initializations
lcd.init();
lcd.backlight();
lcd.setCursor(0, 0);
lcd.print("===== PAPERPAL =====");
lcd.setCursor(0, 1);
lcd. print("Long: P1.00");
lcd.setCursor(0, 2);
lcd. print("Short: P2.00");
lcd.setCursor(13, 1);
lcd.print("CREDITS");
}
void loop() {
// Button loop
button1.loop();
button2.loop();
// Credit Couter c. limmits to thousands
lcd.setCursor(13, 2);
if (credits < 10) {
lcd.print(" " + String(credits));
} else if (credits >= 9 && credits < 100) {
lcd.print(" " + String(credits));
} else {
lcd.print(" " + String(credits));
}
// Motor States
analogWrite(MOTOR_1_PWM_PIN, 180);
analogWrite(MOTOR_2_PWM_PIN, 180);
digitalWrite(MOTOR_1_PIN, motor1State);
digitalWrite(MOTOR_2_PIN, motor2State);
// Control
if (dispensing == 0) {
if (button1.isReleased() ) {
if (credits > 0) {
dispensing = 1;
noCredit = false;
warnMillis = 0;
warnElapsed = 0;
} else {
noCredit = true;
}
Serial.println("Button 1");
}
if (button2.isReleased()) {
if (credits > 1) {
dispensing = 2;
noCredit = false;;
warnMillis = 0;
warnElapsed = 0;
} else {
noCredit = true;
}
Serial.println("Button 2");
}
} else {
if (millis() - motorMillis > 1000) {
motorMillis = millis();
switch (dispensing) {
case 1:
motorElapsed += 1000;
motor1State = true;
Serial.println(F("Dispensing Short"));
if (motorElapsed >= size1Duration) {
reduceCredit(sizeShortPrice);
dispensing = 0;
motorElapsed = 0;
motor1State = false;
}
break;
case 2:
motorElapsed += 1000;
motor2State = true;
if (motorElapsed >= size2Duration) {
reduceCredit(sizeLongPrice);
dispensing = 0;
motorElapsed = 0;
motor2State = false;
}
break;
}
}
}
// Status line
if (dispensing == 0) {
lcd.setCursor(4, 3);
if (noCredit) {
if (millis() - warnMillis > 1000) {
warnMillis = millis();
warnElapsed += 1000;
lcd.setCursor(0, 3);
lcd.print("Insufficient credit");
if (warnElapsed >= 3000) {
noCredit = 0;
warnMillis = 0;
warnElapsed = 0;
}
}
} else {
lcd.print("Insert Coin ");
if (millis() - animMillis > 500) {
// save the last time you blinked the LED
animMillis = millis();
if (animFrame == 0) {
animFrame = 1;
lcd.setCursor(0, 3);
lcd.print(" ");
lcd.setCursor(0, 3);
lcd.print(" >>");
lcd.setCursor(16, 3);
lcd.print(" ");
lcd.setCursor(16, 3);
lcd.print(" <<");
} else {
animFrame = 0;
lcd.setCursor(0, 3);
lcd.print(" ");
lcd.setCursor(1, 3);
lcd.print(" >>");
lcd.setCursor(16, 3);
lcd.print(" ");
lcd.setCursor(16, 3);
lcd.print("<<");
}
}
}
} else if (dispensing == 1) {
lcd.setCursor(0, 3);
lcd.print("Dispensing short... ");
} else {
lcd.setCursor(0, 3);
lcd.print("Dispensing long... ");
}
// Serial Commands
if (Serial.available()) {
String command = Serial.readStringUntil('\n');
command.trim();
String commandName = command.substring(0, command.indexOf(' '));
String commandArg = command.substring(command.indexOf(' '), command.length());
commandArg.trim();
Serial.println("[CMD] " + commandName + " arg: " + commandArg);
if (commandName == "reset") {
Serial.println("[SYS] Resetting System...");
resetSystem();
}
else if (commandName == "addCredit") {
Serial.println(F("[SYS] Adding Credit..."));
int cmdCredit = commandArg.toInt();
if (cmdCredit > 0) {
addCredit(cmdCredit);
} else {
Serial.println(F("[SYS] ERROR: Invalid credit number."));
}
}
else if (commandName == "voidCredit") {
Serial.println("[SYS] Voiding Credit...");
voidCredit();
}
else {
Serial.println(F("[CMD] ERROR: Unrecognized command."));
}
}
// COIN ACCEPTOR
tempAction = lastAction;
tempPulse = lastPulse;
if (millis() - lastPulse >= pulseTimeout && pulseCount > 0) {
if (tempAction != lastAction || tempPulse != lastPulse) return;
Serial.print("Coin: ");
switch (pulseCount) {
case 20:
Serial.println(F("20 Pesos"));
addCredit(20);
break;
case 10:
Serial.println(F("10 Pesos"));
addCredit(10);
break;
case 5:
Serial.println(F("5 Pesos"));
addCredit(5);
break;
case 2:
Serial.println(F("1 Peso"));
addCredit(1);
break;
default:
Serial.println(F("Unrecognized Pulse"));
break;
}
pulseCount = 0;
}
delay(10);
}
void addCredit(int value) {
credits += value;
EEPROM.put(eeprom_addr_credits, credits);
Serial.println("[SYS] Credit now: " + String(credits));
}
void reduceCredit(int value) {
credits -= value;
EEPROM.put(eeprom_addr_credits, credits);
Serial.println("[SYS] Credit now: " + String(credits));
}
void voidCredit() {
Serial.println("[SYS] Voiding credit...");
credits = 0;
EEPROM.put(eeprom_addr_credits, credits);
Serial.println("[SYS] Credit now: " + String(credits));
}
void resetSystem() {
Serial.println(F("[EEPROM] Resetting memory..."));
for (int i = 0; i < EEPROM.length(); i++) {
EEPROM.write(i, 255);
}
Serial.println(F("[EEPROM] Memory Cleared."));
lcd.clear();
lcd.setCursor(4, 0);
lcd.print(F("SYSTEM RESET"));
lcd.setCursor(6, 1);
lcd.print(F("SUCCCESS"));
delay(2000);
resetFunc();
}