#include <LiquidCrystal_I2C.h>
#include "GenericQueue.h"
#include "ADebouncer.h"
LiquidCrystal_I2C myLCD(0x27, 20, 4);
#define cookingLEDPin 12
#define readyLEDPin 11
#define newLEDPin 10
#define debouncePeriod 10
#define blinkPeriod 500
#define numberOfInputs 4
const int inputPin[numberOfInputs] = {9, 8, 7, 6};
ADebouncer debouncer[numberOfInputs];
class Timer {
private:
uint32_t _duration;
uint32_t _startTime;
uint32_t _elapsed;
bool _state;
bool _enable;
public:
bool &state;
uint32_t &elapsed;
uint32_t &duration;
Timer(): state(_state), elapsed(_elapsed), duration(_duration) {}
void timeDelay(uint32_t duration) {
_duration = duration;
}
bool timerOn(bool enable) {
if (enable) {
uint32_t currTime = millis();
if (!_enable) {
_startTime = currTime;
}
if (!_state) {
_elapsed = currTime - _startTime;
if (_elapsed >= _duration) {
_elapsed = _duration;
state = true;
}
}
} else {
_state = false;
}
_enable = enable;
return state;
}
};
class Menu {
public:
String name;
uint32_t duration;
public:
Menu() {
name = "";
duration = 1000UL;
}
Menu(String const& name, uint32_t duration) {
this->name = name;
this->duration = duration;
}
};
Menu myMenu[] = { Menu("Potato", 5000UL)
, Menu("Sandwich", 4000UL)
, Menu("Burger", 3000UL)
, Menu("Chicken", 2000UL)
};
const int maxQueue = 36;
GenericQueue<Menu> order(maxQueue);
const int initialAddQ = 4;
const String menuList[4] = {"OInQ.: ", "NextQ: ", "CurrQ: ", "Cooki: "};
String lcdBuffer[4];
Timer cookingTimer;
Timer blinkTimer;
Timer intervalLCD;
float remainingTime;
bool blinkState;
void Cooking();
void RaiseOrder();
void QueueChanged(Menu m, QueueEventArgs e);
void setup() {
Serial.begin(115200);
myLCD.init();
myLCD.backlight();
for (int index = 0 ; index < sizeof(inputPin) / sizeof(int) ; index++) {
pinMode(inputPin[index], INPUT_PULLUP);
debouncer[index].mode(INSTANT, debouncePeriod, HIGH);
}
pinMode(cookingLEDPin, OUTPUT);
pinMode(LED_BUILTIN, OUTPUT);
order.onStateChanged(QueueChanged);
blinkTimer.timeDelay(blinkPeriod);
intervalLCD.timeDelay(100);
ReadQueue();
Cooking();
LCDDisplay();
delay(1000);
}
void loop() {
blinkTimer.timerOn(!blinkTimer.state);
if (blinkTimer.state) blinkState = !blinkState;
RaiseOrder(); // Raise an Order
ReadQueue(); // Update LCD Buffer
Cooking();
if (intervalLCD.timerOn(!intervalLCD.state)) {
LCDDisplay();
}
digitalWrite(LED_BUILTIN, blinkState);
}
void RaiseOrder() {
for (int index = 0 ; index < sizeof(inputPin) / sizeof(int) ; index++) {
debouncer[index].debounce(!digitalRead(inputPin[index]));
if (debouncer[index].rising()) {
order.enqueue(myMenu[index]);
}
}
}
void Cooking() {
int count = order.count;
if (!order.isEmpty() ) {
digitalWrite(cookingLEDPin, blinkState);
Menu currOrder = order.peek();
cookingTimer.timeDelay(currOrder.duration);
cookingTimer.timerOn(true);
if (cookingTimer.state) {
cookingTimer.timerOn(false);
order.dequeue();
}
remainingTime = (cookingTimer.duration - cookingTimer.elapsed) * 0.001;
}
else {
remainingTime = 0.0;
digitalWrite(cookingLEDPin, 0);
}
}
void ReadQueue() {
Menu currOrder;
Menu nexOrder;
int count = order.count;
// Get Current Queue
if (count > 0) {
currOrder = order.peek();
}
// Get Next Queue
if (count > 1) {
nexOrder = order[1];
}
// Set LCD Information to Buffers : 13 characters per Line (7 charactors Reserved for Title)
lcdBuffer[0] = PadMid(String(count), "(MQ" + String(order.count) + ")", 13, ' ' );
lcdBuffer[1] = PadMid(nexOrder.name, (order.count > 1 ? " " + String(nexOrder.duration * 0.001, 1) + "s" : "-"), 13, ' ');
lcdBuffer[2] = PadMid(currOrder.name, (order.count > 0 ? " " + String(currOrder.duration * 0.001, 1) + "s" : "-"), 13, ' ');
lcdBuffer[3] = PadMid(order.count > 0 ? (blinkState == 1 ? currOrder.name + " " : "") : " ", order.count > 0 ? String(remainingTime, 1) + "s" : "-", 13, ' ');
}
void QueueChanged(QueueEventArgs e, Menu m) {
switch (e.state) {
case ENQUEUE:
// newTimer.TimerOff(true);
Serial.println("enqueue: " + String(m.name) + " New Order!");
break;
case DEQUEUE:
// readyTimer.TimerOff(true);
Serial.println("dequeue: " + String(m.name) + " Ready to serve!");
break;
};
}
void LCDDisplay() {
String strLCD = "";
for (int index = 0 ; index < 4 ; index++) {
myLCD.setCursor(0, index);
strLCD = menuList[index];
strLCD = strLCD + lcdBuffer[index];
myLCD.print(strLCD);
}
}
// return string by padding charecter in the middle of 2 strings
String PadMid(String leftString, String rightString, int length, char paddingChar) {
int lenPad = length - leftString.length() - rightString.length();
for (int index = 0; index < lenPad; index++) {
leftString += paddingChar;
}
return (leftString + rightString);
}