#include <SPI.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 16, 2); // I2C address 0x27, 16 column and 2 rows
// Product data
struct productDataType {
String name;
String uidString;
int price; // in RM * 100
int wantedNo;
};
constexpr int noOfProducts = 5;
productDataType product[noOfProducts] = {
{"Maggie", "53 FD 6A AB 71 00 01", 350, 0},
{"Bekas", "53 F4 6E AB 71 00 01", 500, 0},
{"Lifebuoy ", "53 FC 72 AB 71 00 01", 700, 0},
{"Mushroom Carbonara", "53 2A 64 AB 71 00 01", 750, 0},
{"Soap", "53 0D 67 AB 71 00 01", 250, 0}
};
// End of product data
// Wokwi has a passive buzzer that requires tone() for sound
constexpr boolean isWokwi = true;
constexpr byte wokwiBuzzerPin = 9;
constexpr int wokwiBuzzerFreq = 700;
constexpr int noOfOutputDevices = 4;
constexpr byte redLedIndex = 0;
constexpr byte yellowLedIndex = 1;
constexpr byte greenLedIndex = 2;
constexpr byte buzzerIndex = 3;
byte outputPins[noOfOutputDevices] = {22, 24, 26, 28};
constexpr int noOfButtons = 6;
enum buttonModes {WANT,DIDNOTWANT,PLUS,MINUS, DONE, REMOVE};
byte buttonPins[noOfButtons] = {8, 7, 6, 5, 4, 3};
byte buttonState[noOfButtons];
byte lastButtonState[noOfButtons];
unsigned long lastChange[noOfButtons];
String scannedUID = "";
void setup()
{
// Hardware and Software Setup
//Serial
Serial.begin(115200); // Initiate a serial communication
// Input pins and variables
for (int i = 0; i < noOfButtons; i++) {
pinMode(buttonPins[i], INPUT_PULLUP);
buttonState[i] = HIGH;
lastButtonState[i] = HIGH;
lastChange[i] = 0;
}
// Output pins
for (int i = 0; i < noOfOutputDevices; i++) {
pinMode(outputPins[i], OUTPUT);
}
// LCD
lcd.init(); // initialize the lcd
lcd.backlight();
lcd.begin(16, 2);
// Test buzzer
setOn(buzzerIndex);
delay(500);
setOff(buzzerIndex);
// Show Startscreen
showStartScreen(true);
}
void loop() {
checkAllButtons();
reactToButtons();
if (successFullScanned()) {
checkUid();
}
}
void showStartScreen(boolean showAll) {
if (showAll) {
lcd.setCursor(0, 0);
setOn(redLedIndex); //RED
setOn(yellowLedIndex); // YELLOW
setOn(greenLedIndex);
delay(1000);
setOff(redLedIndex); delay(500);
setOff(yellowLedIndex); delay(500);
setOff(greenLedIndex); delay(500);
scrollText(" Welcome to Ceria Market... ", 25, 300);
}
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Please Add Item");
}
void setOn(byte Index) {
if (Index == buzzerIndex && isWokwi) {
tone(wokwiBuzzerPin, wokwiBuzzerFreq);
return;
}
digitalWrite(outputPins[Index], HIGH);
}
void setOff(byte Index) {
if (Index == buzzerIndex && isWokwi) {
noTone(wokwiBuzzerPin);
return;
}
digitalWrite(outputPins[Index], LOW);
}
void checkButton(int no) {
byte actState = digitalRead(buttonPins[no]);
if (actState != lastButtonState[no]) {
lastChange[no] = millis();
lastButtonState[no] = actState;
}
if (buttonState[no] != lastButtonState[no] && millis() - lastChange[no] > 30) {
buttonState[no] = lastButtonState[no];
}
}
void checkAllButtons() {
for (int i = 0; i < noOfButtons; i++) {
checkButton(i);
}
}
void scrollText(String out, int steps, int delayTime) {
lcd.clear();
setOn(yellowLedIndex);
for (int i = 0; i < steps; i++) {
lcd.setCursor(0, 0);
lcd.scrollDisplayLeft();
lcd.print(out);
delay(delayTime);
}
setOff(yellowLedIndex);
}
boolean successFullScanned() {
scannedUID = readProductIndexFromSerial();
return (scannedUID != "");
}
// This function replaces the missing RFID scan in Wokwi
String readProductIndexFromSerial() {
if (Serial.available()) {
char c = Serial.read();
c = c - '0';
if (c >= 1 && c <= noOfProducts) {
return product[c - 1].uidString;
}
if (c > noOfProducts) {
return "FF FF FF FF FF FF FF"; // Test of wrong UID
}
}
return "";
}
void checkUid() {
int index = -1;
for (int i = 0; i < noOfProducts; i++) {
if (product[i].uidString == scannedUID) {
index = i;
}
}
if (index == -1) {
Serial.println("Item " + scannedUID + " not found!");
Serial.println("Rescan item!");
showError();
} else {
Serial.println("Found " + product[index].name);
setOn(yellowLedIndex);
showProduct(index);
setOff(yellowLedIndex);
}
}
void showProduct(int no) {
String name = " " + product[no].name;
scrollText(name, name.length(), 100);
showProductData(no);
}
void showProductData(int no) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(product[no].name);
lcd.setCursor(0, 1);
float price = product[no].price / 100;
lcd.print("Price: RM ");
lcd.print(price);
}
void showError() {
setOn(yellowLedIndex); // Yellow
setOn(buzzerIndex); // Buzzer
delay(50);
setOff(buzzerIndex); // Buzzer
delay(100);
setOn(buzzerIndex); // Buzzer
delay(200);
setOff(buzzerIndex); // Buzzer
delay(100);
setOn(redLedIndex); // Red
delay(1000);
setOff(redLedIndex); // Red
delay(50);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(" Error!");
lcd.setCursor(0, 1);
lcd.print(" Rescan Item ");
setOff(yellowLedIndex); // Yellow
}
void reactToButtons(){
static byte lastMode = 255;
byte mode = 255;
for (int i=0;i<noOfButtons;i++){
if (buttonState[i] == LOW) {
mode = i;
exit;
}
}
if (mode != lastMode) {
lastMode = mode;
if (mode != 255) {
callButton(mode);
}
}
}
// buttonModes {WANT,DIDNOTWANT,PLUS,MINUS, DONE, REMOVE};
void callButton(byte mode){
switch(mode){
case WANT :
Serial.println("Item wanted");
break;
case DIDNOTWANT :
Serial.println("Item not wanted");
break;
case PLUS :
Serial.println("Add item");
break;
case MINUS :
Serial.println("Subtract item");
break;
case DONE :
Serial.println("Purchase done");
break;
case REMOVE :
Serial.println("Remove item");
break;
}
}