#include <Adafruit_ILI9341.h>
#include <Keypad.h>
#include <ESP32Firebase.h>
#include <WiFi.h>
#include <Arduino.h>
#include <map>
#include <string>
#include <ArduinoJson.h>
#include <RTClib.h>
using namespace std;
//Sample ID
//123456789,
//987654321
//112233445
//111122223
//112233445
/*-------KeyPad Init------*/
const byte ROWS = 4;
const byte COLS = 4;
char hexaKeys[ROWS][COLS] =
{
{'1', '2', '3', 'A'},
{'4', '5', '6', 'B'},
{'7', '8', '9', 'C'},
{'*', '0', '#', 'D'}
};
byte rowPins[ROWS] = {12, 14, 27, 26};
byte colPins[COLS] = {25, 33, 32, 35};
Keypad customKeypad = Keypad(makeKeymap(hexaKeys), rowPins, colPins, ROWS, COLS);
/*-------Dispay Init------*/
#define CS 5
#define RST 15
#define DC 4
#define MOSI 23 // DIN
#define SCLK 18 // CLK
#define MISO 19 // BL
Adafruit_ILI9341 LCD = Adafruit_ILI9341(CS, DC);
/*-------Firebase Init------*/
#define WIFI_SSID "Wokwi-GUEST"
#define WIFI_PASSWORD ""
#define API_KEY "AIzaSyAnIyBy4OkkpnzfaqwVfdFFsntPQZNqgoc"
#define DATABASE_URL "https://votersdatabase-default-rtdb.firebaseio.com/"
string defaultPath = "VotingMachine";
Firebase firebase(DATABASE_URL);
/*-----Init Push BUttons-----*/
const int buttonPins[] = {22, 21, 17, 16, 0, 2};
const int numButtons = sizeof(buttonPins) / sizeof(buttonPins[0]);
/*----- RTC ----- */
RTC_DS3231 rtc;
char daysOfWeek[7][12] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
/* Other veriable Initialization */
uint8_t state; // 0 - Start //1 - BioID //2 - PostCode //3 - voting
const char* ElectID;
unsigned long sendDataPrevMillis = 0;
bool signupOK = false;
DynamicJsonDocument FingerDatas(2048);
DynamicJsonDocument Candidates(2048);
DynamicJsonDocument VoteHistory(2048);
const char* SelectedVoterName;
const char* SelectedVoterID;
char ID_Value[10];
uint8_t b_len = 9;
uint8_t p_len = 5;
uint8_t ValueIndex;
int candidatecount = 5;
string CandidateID[50];
bool isPrint = true;
std::map<string, int> CandidateButtonMap;
bool isfull = false;
int lastpressed = -99;
bool isvoted = false;
//current time
string getTime()
{
DateTime now = rtc.now();
std::string Day(daysOfWeek[now.dayOfTheWeek()]);
string formattedTime = Day + "| " +
std::to_string(now.day()) + "-" +
std::to_string(now.month()) + "-" +
std::to_string(now.year()) + " " +
std::to_string(now.hour()) + ":" +
std::to_string(now.minute()) + ":" +
std::to_string(now.second()) ;
Serial.print("The local date and time is: ");
Serial.println(formattedTime.c_str());
return formattedTime;
}
//Init Display message
void InitDisplay()
{
state = 0;
LCD.begin();
LCD.setRotation(1);
LCD.fillScreen(ILI9341_BLACK);
LCD.setTextColor(ILI9341_GREEN);
LCD.setTextSize(2);
LCD.setCursor(0, 50);
LCD.println("Display Initialization...");
//delay(5000);
}
void InitRTC()
{
if (!rtc.begin())
{
Serial.println("Couldn't find RTC. Dead now.");
while(1);
}
}
void StartMessage()
{
state = 0;
LCD.setCursor(0, 10);
LCD.fillScreen(ILI9341_BLACK);
LCD.setTextColor(ILI9341_WHITE);
LCD.println("Welcome to General Election!!\n");
LCD.println("Please enter '#' to start..");
}
//Init Push Button pins and mode
void InitPushBtn()
{
LCD.setCursor(0, 50);
LCD.fillScreen(ILI9341_BLACK);
LCD.println("Push button Initialization...");
for (int i = 0; i < numButtons; i++)
{
pinMode(buttonPins[i], INPUT_PULLUP);
}
//delay(5000);
}
void InitFirebase()
{
LCD.setCursor(0, 50);
LCD.fillScreen(ILI9341_BLACK);
LCD.println("DataBase Initialization...");
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
Serial.print("Connecting to Wi-Fi");
while (WiFi.status() != WL_CONNECTED)
{
Serial.print(".");
delay(300);
}
Serial.println("Connected with IP: ");
Serial.print(WiFi.localIP());
Serial.println();
firebase.json(true);
String data = firebase.getString("Connection");
const size_t capacity = JSON_OBJECT_SIZE(3) + 50;
DynamicJsonDocument doc(capacity);
deserializeJson(doc, data);
int currentSize = doc.size() + 1;
string vtCount = std::to_string(currentSize);
string newpath = "Connection/LastConnected_" + vtCount;
string timeString = getTime();
firebase.setString(newpath.c_str(), timeString.c_str() );
}
void candidatemapping()
{
CandidateButtonMap.clear();
Serial.println("set mapping");
for (int i = 0; i < numButtons; ++i)
{
string cid = CandidateID[i];
Serial.print(cid.c_str());
Serial.print(" : ");
Serial.print(buttonPins[i]);
CandidateButtonMap[cid] = buttonPins[i];
}
Serial.println("\nCandidate Mapping....");
for (const auto& pair : CandidateButtonMap)
{
Serial.print(pair.first.c_str());
Serial.print(" : ");
Serial.print(pair.second);
}
}
void GetCandidateData()
{
string path = defaultPath + "/Candidates";
String data = firebase.getString(path.c_str());
deserializeJson(Candidates, data);
candidatecount = Candidates.size();
Serial.print("Received Candidate :\t");
int index = 0;
for (JsonPair detail : Candidates.as<JsonObject>())
{
const char* CandicateID = detail.value()["CandicateID"];
const char* CandidateName = detail.value()["CandidateName"];
const char* Election = detail.value()["Election"];
Serial.print("CandicateID: ");
Serial.println(CandicateID);
Serial.print("CandidateName: ");
Serial.println(CandidateName);
Serial.print("Election: ");
Serial.println(Election);
Serial.println();
//std::string electids(Election);
//if(ElectID == Election)
{
std::string myString(CandicateID);
CandidateID[index] = myString;
index++;
}
}
candidatemapping();
}
void GetVotersData()
{
string path = defaultPath + "/Fingers";
String data = firebase.getString(path.c_str());
deserializeJson(FingerDatas, data);
Serial.print("Received Candidate :\t");
for (JsonPair detail : FingerDatas.as<JsonObject>())
{
const int BioID = detail.value()["BioID"];
const char* FingerID = detail.value()["FingerID"];
const char* Name = detail.value()["Name"];
const int PostID = detail.value()["PostID"];
Serial.print("BioID: ");
Serial.println(BioID);
Serial.print("FingerID: ");
Serial.println(FingerID);
Serial.print("Name: ");
Serial.println(Name);
Serial.print("PostID: ");
Serial.println(PostID);
Serial.println();
}
}
void GetVoteHistory()
{
string Path = defaultPath + "/Voting";
String data = firebase.getString(Path.c_str());
deserializeJson(VoteHistory, data);
Serial.print(data);
Serial.println("Vote history :\t");
for (JsonPair detail : VoteHistory.as<JsonObject>())
{
const char* FingID = detail.value()["FingID"];
const char* CandiID = detail.value()["CandiID"];
const char* ElectID = detail.value()["ElectID"];
Serial.print("FingID: ");
Serial.println(FingID);
Serial.print("CandiID: ");
Serial.println(CandiID);
Serial.print("ElectID: ");
Serial.println(ElectID);
Serial.println();
}
}
void fnCollectData()
{
LCD.println("Data collecting...");
GetCandidateData();
GetVotersData();
GetVoteHistory();
}
int getVotersCount()
{
int count = 0;
for (JsonPair detail : VoteHistory.as<JsonObject>())
{
count++;
}
return count;
}
bool isAlredyVoted()
{
for (JsonPair detail : VoteHistory.as<JsonObject>())
{
if( /*detail.value()["ElectID"] == ElectID && */
detail.value()["FingID"] == SelectedVoterID)
{
return true;
}
}
return false;
}
bool StoreVote(string Candicate)
{
std::map<string, string> VotesMap;
std::string SelectedVoter(SelectedVoterID);
std::string election(ElectID);
int voteCount = getVotersCount() + 1;
string count = "V" + std::to_string(voteCount);
VotesMap["VoteID"] = count;
VotesMap["CandiID"] = Candicate;
VotesMap["ElectID"] = election;
VotesMap["FingID"] = SelectedVoter;
VotesMap["VoteTime"] = getTime();
string path = defaultPath + "/Voting/" + count;
int isdone = 0;
for (const auto& pair : VotesMap)
{
string newpath = path + "/" + pair.first;
Serial.println("Data Sending to server..");
Serial.print(pair.first.c_str());
Serial.print(" : ");
Serial.print(pair.second.c_str());
isdone = firebase.setString(newpath.c_str(), pair.second.c_str());
Serial.print(" / ");
Serial.println(isdone);
}
return isdone == 200;
}
bool isCorrectBIO()
{
bool isCorrect = false;
for (JsonPair detail : FingerDatas.as<JsonObject>())
{
int Orgval = detail.value()["BioID"];
int intValue = std::stoi(ID_Value);
if (Orgval == intValue)
/*detail.value()["BioID"] != nullptr && */
{
isCorrect = true;
SelectedVoterName = detail.value()["Name"];
SelectedVoterID = detail.value()["FingerID"];
}
}
return isCorrect;
}
void CheckBioID()
{
if (FingerDatas.size() <= 0)
{
GetVotersData();
}
bool iscrt = isCorrectBIO();
//LCD.print("entered id is \t");
//LCD.println(iscrt);
if (!iscrt)
{
LCD.fillScreen(ILI9341_BLACK);
LCD.setCursor(0, 20);
LCD.setTextColor(ILI9341_RED);
LCD.println("Invalid BIO ID");
LCD.println("Please wait");
int val = 0;
while(val < 5)
{
LCD.print(".");
delay(1000);
val++;
}
//delay(5000);
state = 1;
}
else
{
LCD.fillScreen(ILI9341_BLACK);
LCD.setCursor(0, 20);
LCD.println("Checking please wait...\n");
bool isVoted = isAlredyVoted();
if (isVoted)
{
LCD.fillScreen(ILI9341_BLACK);
LCD.setCursor(0, 30);
LCD.setTextColor(ILI9341_RED);
LCD.println("You are alredy voted!!!!\n");
LCD.println("Please reach the assistant...");
delay(30000);
state = 3;
}
else
{
LCD.println("Correct User...");
state = 5;
isPrint = true;
}
}
}
void CorrectID()
{
if (state == 4 && ValueIndex == b_len)
{
CheckBioID();
}
}
void nextChar(char key)
{
if ((state == 4 && ValueIndex < b_len) || (state == 5 && ValueIndex < p_len))
{
LCD.setTextColor(ILI9341_WHITE);
ID_Value[ValueIndex] = key;
LCD.setCursor((ValueIndex * 18), 100);
LCD.println(key);
ValueIndex++;
}
isfull = (state == 4 && ValueIndex >= b_len) || (state == 5 && ValueIndex >= p_len);
if (isfull)
{
LCD.setTextColor(ILI9341_WHITE);
LCD.setCursor(20, 140);
LCD.println("Please press '#'");
}
}
void eraseChar()
{
if (ValueIndex > 0)
{
LCD.setTextColor(ILI9341_WHITE);
ValueIndex--;
ID_Value[ValueIndex] = '\0';
LCD.fillScreen(ILI9341_BLACK);
LCD.setCursor(0, 20);
string msgVal = state == 4 ? "BIO ID" : state == 5 ? "Post ID" : "";
string msg = "Please enter the " + msgVal + "\n";
LCD.println(msg.c_str());
LCD.setCursor(0, 100);
LCD.println(ID_Value);
}
}
void IDEntery(uint8_t _st)
{
state = _st;
char key = customKeypad.getKey();
switch (key)
{
case '#':
CorrectID();
break;
case '*':
eraseChar();
break;
default:
if (isDigit(key))
{
nextChar(key);
}
break;
}
}
void BioIDRequest()
{
memset(ID_Value, '\0', sizeof(ID_Value));
ValueIndex = 0;
state = 4;
LCD.fillScreen(ILI9341_BLACK);
LCD.setCursor(0, 10);
LCD.println("Please enter the ID\n");
}
void startVoting()
{
char key = customKeypad.getKey();
if (key != '\0')
{
switch (key)
{
case '#':
BioIDRequest();
break;
case '*':
StartMessage();
// getKeyValue();
break;
default:
LCD.fillScreen(ILI9341_BLACK);
LCD.setCursor(0, 20);
LCD.println("# - Start / Enter\n");
LCD.println("* - Cancel");
break;
}
}
}
int getPressedVoteBtn()
{
Serial.println("getPressedVoteBtn");
for (int i = 0; i < numButtons; i++) {
int currentState = digitalRead(buttonPins[i]);
if (currentState == LOW)
{
while (digitalRead(buttonPins[i]) == LOW);
return i;
}
}
return -99;
}
int GetVote()
{
Serial.println("GetVote");
int pressedVote = getPressedVoteBtn(); //get pin number
Serial.print(pressedVote);Serial.println("Pressed");
if (pressedVote != -99 && lastpressed != pressedVote && !isvoted)
{
Serial.print("Vote button ");
Serial.print(pressedVote);
Serial.println(" pressed");
lastpressed = pressedVote;
int voteindex = buttonPins[pressedVote];
isvoted = true;
//get candidate id using mapp
for (const auto& pair : CandidateButtonMap)
{
if(pair.second == voteindex)
{
Serial.println("Correct user");
string canid = pair.first;
for (JsonPair detail : Candidates.as<JsonObject>())
{
const char* Candt = detail.value()["CandicateID"];
std::string CandicateID(Candt);
if(CandicateID == canid)
{
LCD.fillScreen(ILI9341_BLACK);
LCD.setCursor(0, 20);
const char* CandidateName = detail.value()["CandidateName"] ;
LCD.println(CandidateName);
LCD.println(" Selected..\n\n");
delay(2000);
LCD.println("Please wait...\n");
if(StoreVote(CandicateID))
{
LCD.fillScreen(ILI9341_BLACK);
LCD.setCursor(0, 29);
LCD.println("Thanks for voting...\n ");
delay(10000);
state = 2;
//getKeyValue();
isvoted = false;
}
else
{
isvoted = false;
LCD.fillScreen(ILI9341_BLACK);
LCD.setCursor(0, 20);
LCD.println("Something went wrong..\n");
LCD.println("Please try again..\n");
int time = 0;
LCD.setCursor(0, 100);
LCD.print("Wait.");
while(time < 10)
{
LCD.print(" .");
delay((1000));
time++;
}
state = 1;
}
}
}
}
}
}
isvoted = false;
return 0;
}
void printCandidate()
{
const char* name = SelectedVoterName; // SelectedVoter["Name"];
LCD.fillScreen(ILI9341_BLACK);
LCD.setCursor(0, 20);
LCD.print("Hi! ");
LCD.print(name);
LCD.println(" ..");
LCD.println("Please Select your votes..\n");
for (JsonPair detail : Candidates.as<JsonObject>())
{
const char* CandicateID = detail.value()["CandicateID"];
const char* CandidateName = detail.value()["CandidateName"];
LCD.print(CandicateID);
LCD.print(" : ");
LCD.println(CandidateName);
}
}
void awaitCandidateSelection()
{
Serial.println("awaitCandidateSelection");
if(isPrint)
{
isPrint = false;
printCandidate();
}
int voteRes = GetVote();
if (voteRes == 0)
{
//state = 3;
// start again
}
}
void getKeyValue()
{
{
switch (state)
{
case 0:
startVoting(); // done
break;
case 1:
BioIDRequest();
break;
case 2:
fnCollectData();
StartMessage();
break;
case 3:
StartMessage();
break;
case 4:
IDEntery(4);
break;
case 5:
awaitCandidateSelection();
break;
}
}
}
void StoreVote1()
{
GetVoteHistory();
std::map<string, string> VotesMap;
string ElectID = "E1";
string Candt = "C4";
string SelectedVoterID = "F1";
int voteCount = getVotersCount() + 1;
string count = "V" + std::to_string(voteCount);
VotesMap["VoteID"] = count;
VotesMap["CandiID"] = Candt;
VotesMap["ElectID"] = ElectID;
VotesMap["FingID"] = SelectedVoterID;
string path = defaultPath + "/Voting/" + count;
Serial.print(" Path //");
Serial.println(path.c_str());
for (const auto& pair : VotesMap)
{
string newpath = path + "/" + pair.first;
Serial.print(pair.first.c_str());
Serial.print(" : ");
Serial.print(pair.second.c_str());
firebase.setString(newpath.c_str(), pair.second.c_str());
}
}
void setup()
{
Serial.begin(9600);
InitDisplay();
InitRTC();
InitPushBtn();
InitFirebase();
fnCollectData();
ElectID = "E1";
//StoreVote1();
StartMessage();
}
void loop()
{
getKeyValue();
}